use crate::Platform;
use origin_domain::{AppError, Result};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
pub trait ApplicationModule: fmt::Debug + Send + Sync + 'static {
fn id(&self) -> &'static str;
fn register(&self, registry: &mut ModuleRegistry) -> Result<()>;
}
#[derive(Default)]
pub struct ModuleRegistry {
platform: Option<Platform>,
services: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
module_ids: Vec<&'static str>,
}
impl fmt::Debug for ModuleRegistry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModuleRegistry")
.field("modules", &self.module_ids)
.field("services", &self.services.len())
.finish()
}
}
impl ModuleRegistry {
pub(crate) fn new(platform: Platform) -> Self {
Self {
platform: Some(platform),
services: HashMap::new(),
module_ids: Vec::new(),
}
}
pub fn platform(&self) -> &Platform {
self.platform
.as_ref()
.expect("registry is always constructed with a platform")
}
pub fn provide<T: Send + Sync + 'static>(&mut self, service: Arc<T>) {
self.services.insert(TypeId::of::<T>(), Box::new(service));
}
pub fn service<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.services
.get(&TypeId::of::<T>())
.and_then(|entry| entry.downcast_ref::<Arc<T>>())
.cloned()
}
pub fn require<T: Send + Sync + 'static>(&self) -> Result<Arc<T>> {
self.service::<T>().ok_or_else(|| {
AppError::configuration(format!(
"no module provided the service `{}`",
std::any::type_name::<T>()
))
})
}
pub(crate) fn record_module(&mut self, id: &'static str) {
self.module_ids.push(id);
}
pub(crate) fn module_ids(&self) -> &[&'static str] {
&self.module_ids
}
}