1use crate::{LinkedBinding, Module, ModuleManifest};
2use platform_core::{AppContext, AppResult, Migration};
3use std::any::{Any, TypeId};
4use std::sync::Arc;
5
6#[derive(Clone)]
7pub struct HostContribution {
8 type_id: TypeId,
9 value: Arc<dyn Any + Send + Sync>,
10}
11
12impl std::fmt::Debug for HostContribution {
13 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 formatter
15 .debug_struct("HostContribution")
16 .field("type_id", &self.type_id)
17 .finish_non_exhaustive()
18 }
19}
20
21impl HostContribution {
22 pub fn typed<T>(value: T) -> Self
23 where
24 T: Send + Sync + 'static,
25 {
26 Self {
27 type_id: TypeId::of::<T>(),
28 value: Arc::new(value),
29 }
30 }
31
32 pub fn get<T>(&self) -> Option<&T>
33 where
34 T: Send + Sync + 'static,
35 {
36 (self.type_id == TypeId::of::<T>())
37 .then(|| self.value.downcast_ref::<T>())
38 .flatten()
39 }
40}
41
42#[derive(Debug, Clone)]
43pub struct HostLinkedModule {
44 pub module_name: &'static str,
45 pub manifest: fn() -> ModuleManifest,
46 pub load: Option<fn(&AppContext) -> Module>,
47 try_load: Option<fn(&AppContext) -> AppResult<Module>>,
48 pub http_binding: Option<fn() -> LinkedBinding>,
49 pub migrations: &'static [Migration],
50 contributions: Vec<HostContribution>,
51}
52
53impl HostLinkedModule {
54 #[must_use]
55 pub fn manifest_only(
56 module_name: &'static str,
57 manifest: fn() -> ModuleManifest,
58 migrations: &'static [Migration],
59 ) -> Self {
60 Self {
61 module_name,
62 manifest,
63 load: None,
64 try_load: None,
65 http_binding: None,
66 migrations,
67 contributions: Vec::new(),
68 }
69 }
70
71 #[must_use]
72 pub fn linked(
73 module_name: &'static str,
74 manifest: fn() -> ModuleManifest,
75 load: fn(&AppContext) -> Module,
76 migrations: &'static [Migration],
77 ) -> Self {
78 Self {
79 module_name,
80 manifest,
81 load: Some(load),
82 try_load: None,
83 http_binding: None,
84 migrations,
85 contributions: Vec::new(),
86 }
87 }
88
89 #[must_use]
92 pub fn try_linked(
93 module_name: &'static str,
94 manifest: fn() -> ModuleManifest,
95 load: fn(&AppContext) -> AppResult<Module>,
96 migrations: &'static [Migration],
97 ) -> Self {
98 Self {
99 module_name,
100 manifest,
101 load: None,
102 try_load: Some(load),
103 http_binding: None,
104 migrations,
105 contributions: Vec::new(),
106 }
107 }
108
109 #[doc(hidden)]
110 pub fn try_load_module(&self, context: &AppContext) -> AppResult<Module> {
111 if let Some(load) = self.try_load {
112 return load(context);
113 }
114 Ok(match self.load {
115 Some(load) => load(context),
116 None => Module::linked((self.manifest)(), LinkedBinding::builder().build()),
117 })
118 }
119
120 #[must_use]
121 pub fn with_http_binding(mut self, http_binding: fn() -> LinkedBinding) -> Self {
122 self.http_binding = Some(http_binding);
123 self
124 }
125
126 #[must_use]
127 pub fn with_contribution<T>(mut self, contribution: T) -> Self
128 where
129 T: Send + Sync + 'static,
130 {
131 self.contributions
132 .push(HostContribution::typed(contribution));
133 self
134 }
135
136 pub fn contributions<T>(&self) -> impl Iterator<Item = &T>
137 where
138 T: Send + Sync + 'static,
139 {
140 self.contributions
141 .iter()
142 .filter_map(HostContribution::get::<T>)
143 }
144}