1use crate::Platform;
2use origin_domain::{AppError, Result};
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::fmt;
6use std::sync::Arc;
7
8pub trait ApplicationModule: fmt::Debug + Send + Sync + 'static {
14 fn id(&self) -> &'static str;
16
17 fn register(&self, registry: &mut ModuleRegistry) -> Result<()>;
19}
20
21#[derive(Default)]
23pub struct ModuleRegistry {
24 platform: Option<Platform>,
25 services: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
26 module_ids: Vec<&'static str>,
27}
28
29impl fmt::Debug for ModuleRegistry {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 f.debug_struct("ModuleRegistry")
32 .field("modules", &self.module_ids)
33 .field("services", &self.services.len())
34 .finish()
35 }
36}
37
38impl ModuleRegistry {
39 pub(crate) fn new(platform: Platform) -> Self {
40 Self {
41 platform: Some(platform),
42 services: HashMap::new(),
43 module_ids: Vec::new(),
44 }
45 }
46
47 pub fn platform(&self) -> &Platform {
49 self.platform
50 .as_ref()
51 .expect("registry is always constructed with a platform")
52 }
53
54 pub fn provide<T: Send + Sync + 'static>(&mut self, service: Arc<T>) {
59 self.services.insert(TypeId::of::<T>(), Box::new(service));
60 }
61
62 pub fn service<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
63 self.services
64 .get(&TypeId::of::<T>())
65 .and_then(|entry| entry.downcast_ref::<Arc<T>>())
66 .cloned()
67 }
68
69 pub fn require<T: Send + Sync + 'static>(&self) -> Result<Arc<T>> {
71 self.service::<T>().ok_or_else(|| {
72 AppError::configuration(format!(
73 "no module provided the service `{}`",
74 std::any::type_name::<T>()
75 ))
76 })
77 }
78
79 pub(crate) fn record_module(&mut self, id: &'static str) {
80 self.module_ids.push(id);
81 }
82
83 pub(crate) fn module_ids(&self) -> &[&'static str] {
84 &self.module_ids
85 }
86}