91 lines
2.9 KiB
Rust
91 lines
2.9 KiB
Rust
use std::any::{Any, TypeId, type_name};
|
|
use std::fmt;
|
|
use std::num::NonZero;
|
|
use std::rc::Rc;
|
|
|
|
use memo_map::MemoMap;
|
|
use orchid_base::builtin::Spawner;
|
|
use orchid_base::interner::Interner;
|
|
use orchid_base::logging::Logger;
|
|
use orchid_base::reqnot::ReqNot;
|
|
use task_local::task_local;
|
|
|
|
use crate::api;
|
|
use crate::system_ctor::CtedObj;
|
|
|
|
#[derive(Clone)]
|
|
pub struct SysCtx(Rc<MemoMap<TypeId, Box<dyn Any>>>);
|
|
impl SysCtx {
|
|
pub fn new(
|
|
id: api::SysId,
|
|
i: Interner,
|
|
reqnot: ReqNot<api::ExtMsgSet>,
|
|
spawner: Spawner,
|
|
logger: Logger,
|
|
cted: CtedObj,
|
|
) -> Self {
|
|
let this = Self(Rc::new(MemoMap::new()));
|
|
this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted);
|
|
this
|
|
}
|
|
pub fn add<T: SysCtxEntry>(&self, t: T) -> &Self {
|
|
assert!(self.0.insert(TypeId::of::<T>(), Box::new(t)), "Key already exists");
|
|
self
|
|
}
|
|
pub fn get_or_insert<T: SysCtxEntry>(&self, f: impl FnOnce() -> T) -> &T {
|
|
(self.0.get_or_insert_owned(TypeId::of::<T>(), || Box::new(f())).downcast_ref())
|
|
.expect("Keyed by TypeId")
|
|
}
|
|
pub fn get_or_default<T: SysCtxEntry + Default>(&self) -> &T { self.get_or_insert(T::default) }
|
|
pub fn try_get<T: SysCtxEntry>(&self) -> Option<&T> {
|
|
Some(self.0.get(&TypeId::of::<T>())?.downcast_ref().expect("Keyed by TypeId"))
|
|
}
|
|
pub fn get<T: SysCtxEntry>(&self) -> &T {
|
|
self.try_get().unwrap_or_else(|| panic!("Context {} missing", type_name::<T>()))
|
|
}
|
|
/// Shorthand to get the messaging link
|
|
pub fn reqnot(&self) -> &ReqNot<api::ExtMsgSet> { self.get::<ReqNot<api::ExtMsgSet>>() }
|
|
/// Shorthand to get the system ID
|
|
pub fn sys_id(&self) -> api::SysId { *self.get::<api::SysId>() }
|
|
/// Spawn a task that will eventually be executed asynchronously
|
|
pub fn spawn(&self, f: impl Future<Output = ()> + 'static) {
|
|
(self.get::<Spawner>())(Box::pin(CTX.scope(self.clone(), f)))
|
|
}
|
|
/// Shorthand to get the logger
|
|
pub fn logger(&self) -> &Logger { self.get::<Logger>() }
|
|
/// Shorthand to get the constructed system object
|
|
pub fn cted(&self) -> &CtedObj { self.get::<CtedObj>() }
|
|
}
|
|
impl fmt::Debug for SysCtx {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "SysCtx({:?})", self.sys_id())
|
|
}
|
|
}
|
|
pub trait SysCtxEntry: 'static + Sized {}
|
|
impl SysCtxEntry for api::SysId {}
|
|
impl SysCtxEntry for ReqNot<api::ExtMsgSet> {}
|
|
impl SysCtxEntry for Spawner {}
|
|
impl SysCtxEntry for CtedObj {}
|
|
impl SysCtxEntry for Logger {}
|
|
impl SysCtxEntry for Interner {}
|
|
|
|
task_local! {
|
|
static CTX: SysCtx;
|
|
}
|
|
|
|
pub async fn with_ctx<F: Future>(ctx: SysCtx, f: F) -> F::Output { CTX.scope(ctx, f).await }
|
|
pub fn ctx() -> SysCtx { CTX.get() }
|
|
|
|
/// Shorthand to get the [Interner] instance
|
|
pub fn i() -> Interner { ctx().get::<Interner>().clone() }
|
|
|
|
pub fn mock_ctx() -> SysCtx {
|
|
let ctx = SysCtx(Rc::default());
|
|
ctx
|
|
.add(Logger::new(api::LogStrategy::StdErr))
|
|
.add(Interner::new_master())
|
|
.add::<Spawner>(Rc::new(|_| panic!("Cannot fork in test environment")))
|
|
.add(api::SysId(NonZero::<u16>::MIN));
|
|
ctx
|
|
}
|