1use std::collections::HashMap;
10
11use crate::error::{CoreError, CoreResult};
12use crate::migration::MigrationSet;
13use crate::registry::Registry;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub struct ModuleId(&'static str);
20
21impl ModuleId {
22 pub const fn new(code: &'static str) -> Self {
23 Self(code)
24 }
25 pub const fn as_str(&self) -> &'static str {
26 self.0
27 }
28}
29
30impl std::fmt::Display for ModuleId {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.write_str(self.0)
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
40pub struct Capability(&'static str);
41
42impl Capability {
43 pub const fn new(name: &'static str) -> Self {
44 Self(name)
45 }
46 pub const fn as_str(&self) -> &'static str {
47 self.0
48 }
49}
50
51pub trait Module: Send + Sync + 'static {
53 fn id(&self) -> ModuleId;
58
59 fn migrations(&self) -> MigrationSet {
62 MigrationSet::new(self.id().as_str(), Vec::new())
63 }
64
65 fn requires(&self) -> &'static [ModuleId] {
69 &[]
70 }
71
72 fn requires_db_capabilities(&self) -> &'static [Capability] {
76 &[]
77 }
78
79 fn optional_db_capabilities(&self) -> &'static [Capability] {
83 &[]
84 }
85
86 fn register(&self, registry: &mut Registry) {
91 let _ = registry;
92 }
93
94 fn admin_base(&self) -> Option<&'static str> {
102 None
103 }
104
105 fn catalogs(&self) -> &'static [(&'static str, &'static str)] {
110 &[]
111 }
112}
113
114#[derive(Default)]
116pub struct ModuleRegistry {
117 modules: Vec<Box<dyn Module>>,
118}
119
120impl ModuleRegistry {
121 pub fn new() -> Self {
122 Self::default()
123 }
124
125 pub fn register(&mut self, module: impl Module) -> &mut Self {
128 self.modules.push(Box::new(module));
129 self
130 }
131
132 pub fn register_boxed(&mut self, module: Box<dyn Module>) -> &mut Self {
135 self.modules.push(module);
136 self
137 }
138
139 pub fn has(&self, id: ModuleId) -> bool {
141 self.modules.iter().any(|m| m.id() == id)
142 }
143
144 pub fn get(&self, id: ModuleId) -> Option<&dyn Module> {
146 self.modules
147 .iter()
148 .map(|m| m.as_ref())
149 .find(|m| m.id() == id)
150 }
151
152 pub fn iter(&self) -> impl Iterator<Item = &dyn Module> + '_ {
153 self.modules.iter().map(|m| m.as_ref())
154 }
155
156 pub fn len(&self) -> usize {
157 self.modules.len()
158 }
159
160 pub fn is_empty(&self) -> bool {
161 self.modules.is_empty()
162 }
163
164 pub fn ordered(&self) -> CoreResult<Vec<&dyn Module>> {
169 let mut by_id: HashMap<ModuleId, &dyn Module> = HashMap::with_capacity(self.modules.len());
170 for m in &self.modules {
171 if by_id.insert(m.id(), m.as_ref()).is_some() {
172 return Err(CoreError::DuplicateModule {
173 module: m.id().to_string(),
174 });
175 }
176 }
177 for m in &self.modules {
178 for dep in m.requires() {
179 if !by_id.contains_key(dep) {
180 return Err(CoreError::UnknownModuleDependency {
181 module: m.id().to_string(),
182 dependency: dep.to_string(),
183 });
184 }
185 }
186 }
187
188 let mut state: HashMap<ModuleId, Visit> = HashMap::with_capacity(self.modules.len());
189 let mut order: Vec<ModuleId> = Vec::with_capacity(self.modules.len());
190 for m in &self.modules {
191 visit(m.id(), &by_id, &mut state, &mut order)?;
192 }
193 Ok(order.into_iter().map(|id| by_id[&id]).collect())
194 }
195
196 pub fn ordered_migration_sets(&self) -> CoreResult<Vec<MigrationSet>> {
199 Ok(self
200 .ordered()?
201 .into_iter()
202 .map(|m| m.migrations())
203 .collect())
204 }
205}
206
207#[derive(Clone, Copy)]
208enum Visit {
209 Active,
210 Done,
211}
212
213fn visit(
216 id: ModuleId,
217 by_id: &HashMap<ModuleId, &dyn Module>,
218 state: &mut HashMap<ModuleId, Visit>,
219 order: &mut Vec<ModuleId>,
220) -> CoreResult<()> {
221 match state.get(&id) {
222 Some(Visit::Done) => return Ok(()),
223 Some(Visit::Active) => {
224 return Err(CoreError::ModuleDependencyCycle {
225 module: id.to_string(),
226 })
227 }
228 None => {}
229 }
230 state.insert(id, Visit::Active);
231 for dep in by_id[&id].requires() {
232 visit(*dep, by_id, state, order)?;
233 }
234 state.insert(id, Visit::Done);
235 order.push(id);
236 Ok(())
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 struct Mod(ModuleId, &'static [ModuleId]);
245
246 impl Module for Mod {
247 fn id(&self) -> ModuleId {
248 self.0
249 }
250 fn requires(&self) -> &'static [ModuleId] {
251 self.1
252 }
253 }
254
255 const BLOG: ModuleId = ModuleId::new("acme.blog");
256 const USER: ModuleId = ModuleId::new("acme.user");
257 const MEDIA: ModuleId = ModuleId::new("acme.media");
258
259 fn order_of(reg: &ModuleRegistry) -> Vec<&'static str> {
260 reg.ordered()
261 .unwrap()
262 .into_iter()
263 .map(|m| m.id().as_str())
264 .collect()
265 }
266
267 #[test]
268 fn dependencies_come_before_dependents() {
269 let mut reg = ModuleRegistry::new();
270 reg.register(Mod(BLOG, &[USER, MEDIA]))
272 .register(Mod(MEDIA, &[]))
273 .register(Mod(USER, &[]));
274 let order = order_of(®);
275 let pos = |id: &str| order.iter().position(|x| *x == id).unwrap();
276 assert!(pos("acme.user") < pos("acme.blog"));
277 assert!(pos("acme.media") < pos("acme.blog"));
278 }
279
280 #[test]
281 fn registration_order_breaks_ties() {
282 let mut reg = ModuleRegistry::new();
283 reg.register(Mod(ModuleId::new("a"), &[]))
284 .register(Mod(ModuleId::new("b"), &[]))
285 .register(Mod(ModuleId::new("c"), &[]));
286 assert_eq!(order_of(®), ["a", "b", "c"]);
287 }
288
289 #[test]
290 fn has_and_get_find_by_id() {
291 let mut reg = ModuleRegistry::new();
292 reg.register(Mod(MEDIA, &[]));
293 assert!(reg.has(MEDIA));
294 assert!(!reg.has(USER));
295 assert_eq!(reg.get(MEDIA).unwrap().id(), MEDIA);
296 assert!(reg.get(ModuleId::new("missing")).is_none());
297 }
298
299 #[test]
300 fn unknown_dependency_errors() {
301 let mut reg = ModuleRegistry::new();
302 reg.register(Mod(BLOG, &[USER]));
303 assert!(matches!(
304 reg.ordered(),
305 Err(CoreError::UnknownModuleDependency { .. })
306 ));
307 }
308
309 #[test]
310 fn cycle_errors() {
311 const A_REQS: &[ModuleId] = &[ModuleId::new("acme.b")];
314 const B_REQS: &[ModuleId] = &[ModuleId::new("acme.a")];
315 let mut reg = ModuleRegistry::new();
316 reg.register(Mod(ModuleId::new("acme.a"), A_REQS))
317 .register(Mod(ModuleId::new("acme.b"), B_REQS));
318 assert!(matches!(
319 reg.ordered(),
320 Err(CoreError::ModuleDependencyCycle { .. })
321 ));
322 }
323
324 #[test]
325 fn duplicate_id_errors() {
326 let mut reg = ModuleRegistry::new();
327 reg.register(Mod(ModuleId::new("a"), &[]))
328 .register(Mod(ModuleId::new("a"), &[]));
329 assert!(matches!(
330 reg.ordered(),
331 Err(CoreError::DuplicateModule { .. })
332 ));
333 }
334}