use std::any::{Any, TypeId};
use std::collections::BTreeMap;
use std::fmt; use std::sync::Arc;
use std::time::SystemTime;
#[derive(Debug, Clone)] pub struct RsServiceEntryMetadata {
pub type_id_repr: String, pub type_name: &'static str, }
#[derive(Debug, Clone)]
pub struct RsContextMeta { pub registered_services: Vec<RsServiceEntryMetadata>,
pub creation_timestamp: SystemTime, }
struct StoredServiceInfo {
container: ContainerStruct, type_name: &'static str,
}
#[cfg(not(feature = "tokio"))]
use std::sync::Mutex;
#[cfg(feature = "tokio")]
use tokio::sync::Mutex;
#[cfg(feature = "tokio")]
use std::{
pin::Pin,
future::Future
};
#[derive(Debug)]
pub struct RsServiceError(String);
impl fmt::Display for RsServiceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RsService Error: {}", self.0)
}
}
impl std::error::Error for RsServiceError {
}
type ContainerStruct = Box<dyn Any>;
type MapForContainer = BTreeMap<TypeId, StoredServiceInfo>;
#[cfg(not(feature = "tokio"))]
pub trait RSContextService: Any {
fn on_register_crate_instance() -> Self where Self: Sized;
fn on_service_created(&mut self, builder: &RSContextBuilder) -> Result<(), RsServiceError>;
fn on_all_services_built(&self, context: &RSContext) -> Result<(), RsServiceError>;
}
#[cfg(feature = "tokio")]
pub type AsyncHooksResult = Result<(), RsServiceError>;
#[cfg(feature = "tokio")]
pub trait RSContextService: Any {
fn on_register_crate_instance() -> impl Future<Output = Self> where Self: Sized;
fn on_service_created(&mut self, builder: &RSContextBuilder) -> impl Future<Output = AsyncHooksResult>;
fn on_all_services_built(&self, context: &RSContext) -> impl Future<Output = AsyncHooksResult>;
}
#[cfg(not(feature = "tokio"))]
type AfterBuildHook = Box<
dyn FnOnce(&RSContext) ->
Result<(), RsServiceError>
>;
#[cfg(feature = "tokio")]
type AfterAsyncBuildHook = Box<
dyn Fn(Arc<RSContext>) -> Pin<Box<dyn Future<Output = Result<(), RsServiceError>> >>
>;
#[cfg(not(feature = "tokio"))]
pub struct RSContextBuilder {
pending_services: MapForContainer,
after_build_hooks: Vec<AfterBuildHook>,
}
#[cfg(feature = "tokio")]
pub struct RSContextBuilder {
pending_services: MapForContainer,
after_build_async_hooks: Vec<AfterAsyncBuildHook>,
}
impl RSContextBuilder {
#[cfg(feature = "tokio")]
pub fn new() -> Self {
RSContextBuilder {
pending_services: BTreeMap::new(),
after_build_async_hooks: Vec::new(),
}
}
#[cfg(feature = "tokio")]
pub async fn register<T>(mut self) -> Self
where
T: RSContextService,
{
let type_id = TypeId::of::<T>();
if self.pending_services.contains_key(&type_id) {
panic!("Service type {:?} already registered.", std::any::type_name::<T>());
}
let mut instance = T::on_register_crate_instance().await;
instance.on_service_created(&self)
.await
.map_err(|e| RsServiceError(format!("on_service_created hook failed for {}: {}", std::any::type_name::<T>(), e)))
.expect("on_service_created hook failed");
let service_arc_mutex: Arc<Mutex<T>> = Arc::new(Mutex::new(instance));
let type_name_str: &'static str = std::any::type_name::<T>(); let service_info = StoredServiceInfo {
container: Box::new(service_arc_mutex) as ContainerStruct,
type_name: type_name_str,
};
self.pending_services.insert(
type_id,service_info
);
{
let hook = Box::new(move |ctx: Arc<RSContext>| {
let arc_mutex = ctx.call::<T>().expect("Service not found");
Box::pin(async move {
let guard = arc_mutex.lock().await;
guard.on_all_services_built(&ctx).await
}) as Pin<Box<dyn Future<Output = Result<(), RsServiceError>>>>
});
self.after_build_async_hooks.push(hook);
}
self
}
#[cfg(feature = "tokio")]
pub async fn build(self) -> Result<RSContext, RsServiceError> {
let mut metadata_entries: Vec<RsServiceEntryMetadata> = Vec::new();
for (type_id, stored_info) in &self.pending_services {
metadata_entries.push(RsServiceEntryMetadata {
type_id_repr: format!("{:?}", type_id),
type_name: stored_info.type_name,
});
}
let context_metadata = RsContextMeta {
registered_services: metadata_entries,
creation_timestamp: SystemTime::now(),
};
let context = RSContext {
service_map: self.pending_services,
metadata: context_metadata
};
let arc_context = Arc::new(context);
for async_hook in self.after_build_async_hooks {
let fut = async_hook(Arc::clone(&arc_context)).await;
fut.map_err(|e| RsServiceError(format!("Async after_build hook failed: {}", e)))?;
}
match Arc::try_unwrap(arc_context) {
Ok(context) => Ok(context),
Err(_) => Err(RsServiceError("Failed to unwrap Arc<RSContext> in build()".to_string())),
}
}
#[cfg(not(feature = "tokio"))]
pub fn new() -> Self {
RSContextBuilder {
pending_services: BTreeMap::new(),
after_build_hooks: Vec::new(),
}
}
#[cfg(not(feature = "tokio"))]
pub fn register<T>(mut self) -> Self
where
T: RSContextService, {
let type_id = TypeId::of::<T>();
if self.pending_services.contains_key(&type_id) {
panic!("Service type {:?} already registered.", std::any::type_name::<T>());
}
let mut instance = T::on_register_crate_instance();
instance.on_service_created(&self)
.map_err(|e| RsServiceError(format!("on_service_created hook failed for {}: {}", std::any::type_name::<T>(), e)))
.expect("on_service_created hook failed");
let service_arc_mutex: Arc<Mutex<T>> = Arc::new(Mutex::new(instance));
let type_name_str: &'static str = std::any::type_name::<T>();
let service_info = StoredServiceInfo {
container: Box::new(service_arc_mutex) as ContainerStruct,
type_name: type_name_str,
};
self.pending_services.insert(type_id, service_info);
let hook = Box::new(move |ctx: &RSContext| {
let arc_mutex = ctx.call::<T>().expect("Service not found");
let guard = arc_mutex.lock().unwrap(); guard.on_all_services_built(ctx)
});
self.after_build_hooks.push(hook);
self
}
#[cfg(not(feature = "tokio"))]
pub fn build(self) -> Result<RSContext, RsServiceError> {
let mut metadata_entries: Vec<RsServiceEntryMetadata> = Vec::new();
for (type_id, stored_info) in &self.pending_services {
metadata_entries.push(RsServiceEntryMetadata {
type_id_repr: format!("{:?}", type_id),
type_name: stored_info.type_name,
});
}
let context_metadata = RsContextMeta {
registered_services: metadata_entries,
creation_timestamp: SystemTime::now(),
};
let context = RSContext {
service_map: self.pending_services, metadata: context_metadata,
};
for hook_fn in self.after_build_hooks {
hook_fn(&context)?;
}
Ok(context)
}
}
pub struct RSContext {
service_map: MapForContainer,
pub metadata: RsContextMeta,
}
impl RSContext {
pub fn call<T>(&self) -> Option<Arc<Mutex<T>>>
where
T: RSContextService, {
self.service_map
.get(&TypeId::of::<T>())
.and_then(|boxed_val| {
boxed_val.container.downcast_ref::<Arc<Mutex<T>>>()
})
.cloned()
}
pub fn is_registered<T: RSContextService>(&self) -> bool {
self.service_map.contains_key(&TypeId::of::<T>())
}
pub fn get_metadata(&self) -> &RsContextMeta {
&self.metadata
}
}