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 catalogs(&self) -> &'static [(&'static str, &'static str)] {
99 &[]
100 }
101}
102
103#[derive(Default)]
105pub struct ModuleRegistry {
106 modules: Vec<Box<dyn Module>>,
107}
108
109impl ModuleRegistry {
110 pub fn new() -> Self {
111 Self::default()
112 }
113
114 pub fn register(&mut self, module: impl Module) -> &mut Self {
117 self.modules.push(Box::new(module));
118 self
119 }
120
121 pub fn register_boxed(&mut self, module: Box<dyn Module>) -> &mut Self {
124 self.modules.push(module);
125 self
126 }
127
128 pub fn has(&self, id: ModuleId) -> bool {
130 self.modules.iter().any(|m| m.id() == id)
131 }
132
133 pub fn get(&self, id: ModuleId) -> Option<&dyn Module> {
135 self.modules
136 .iter()
137 .map(|m| m.as_ref())
138 .find(|m| m.id() == id)
139 }
140
141 pub fn iter(&self) -> impl Iterator<Item = &dyn Module> + '_ {
142 self.modules.iter().map(|m| m.as_ref())
143 }
144
145 pub fn len(&self) -> usize {
146 self.modules.len()
147 }
148
149 pub fn is_empty(&self) -> bool {
150 self.modules.is_empty()
151 }
152
153 pub fn ordered(&self) -> CoreResult<Vec<&dyn Module>> {
158 let mut by_id: HashMap<ModuleId, &dyn Module> = HashMap::with_capacity(self.modules.len());
159 for m in &self.modules {
160 if by_id.insert(m.id(), m.as_ref()).is_some() {
161 return Err(CoreError::DuplicateModule {
162 module: m.id().to_string(),
163 });
164 }
165 }
166 for m in &self.modules {
167 for dep in m.requires() {
168 if !by_id.contains_key(dep) {
169 return Err(CoreError::UnknownModuleDependency {
170 module: m.id().to_string(),
171 dependency: dep.to_string(),
172 });
173 }
174 }
175 }
176
177 let mut state: HashMap<ModuleId, Visit> = HashMap::with_capacity(self.modules.len());
178 let mut order: Vec<ModuleId> = Vec::with_capacity(self.modules.len());
179 for m in &self.modules {
180 visit(m.id(), &by_id, &mut state, &mut order)?;
181 }
182 Ok(order.into_iter().map(|id| by_id[&id]).collect())
183 }
184
185 pub fn ordered_migration_sets(&self) -> CoreResult<Vec<MigrationSet>> {
188 Ok(self
189 .ordered()?
190 .into_iter()
191 .map(|m| m.migrations())
192 .collect())
193 }
194}
195
196#[derive(Clone, Copy)]
197enum Visit {
198 Active,
199 Done,
200}
201
202fn visit(
205 id: ModuleId,
206 by_id: &HashMap<ModuleId, &dyn Module>,
207 state: &mut HashMap<ModuleId, Visit>,
208 order: &mut Vec<ModuleId>,
209) -> CoreResult<()> {
210 match state.get(&id) {
211 Some(Visit::Done) => return Ok(()),
212 Some(Visit::Active) => {
213 return Err(CoreError::ModuleDependencyCycle {
214 module: id.to_string(),
215 })
216 }
217 None => {}
218 }
219 state.insert(id, Visit::Active);
220 for dep in by_id[&id].requires() {
221 visit(*dep, by_id, state, order)?;
222 }
223 state.insert(id, Visit::Done);
224 order.push(id);
225 Ok(())
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 struct Mod(ModuleId, &'static [ModuleId]);
234
235 impl Module for Mod {
236 fn id(&self) -> ModuleId {
237 self.0
238 }
239 fn requires(&self) -> &'static [ModuleId] {
240 self.1
241 }
242 }
243
244 const BLOG: ModuleId = ModuleId::new("acme.blog");
245 const USER: ModuleId = ModuleId::new("acme.user");
246 const MEDIA: ModuleId = ModuleId::new("acme.media");
247
248 fn order_of(reg: &ModuleRegistry) -> Vec<&'static str> {
249 reg.ordered()
250 .unwrap()
251 .into_iter()
252 .map(|m| m.id().as_str())
253 .collect()
254 }
255
256 #[test]
257 fn dependencies_come_before_dependents() {
258 let mut reg = ModuleRegistry::new();
259 reg.register(Mod(BLOG, &[USER, MEDIA]))
261 .register(Mod(MEDIA, &[]))
262 .register(Mod(USER, &[]));
263 let order = order_of(®);
264 let pos = |id: &str| order.iter().position(|x| *x == id).unwrap();
265 assert!(pos("acme.user") < pos("acme.blog"));
266 assert!(pos("acme.media") < pos("acme.blog"));
267 }
268
269 #[test]
270 fn registration_order_breaks_ties() {
271 let mut reg = ModuleRegistry::new();
272 reg.register(Mod(ModuleId::new("a"), &[]))
273 .register(Mod(ModuleId::new("b"), &[]))
274 .register(Mod(ModuleId::new("c"), &[]));
275 assert_eq!(order_of(®), ["a", "b", "c"]);
276 }
277
278 #[test]
279 fn has_and_get_find_by_id() {
280 let mut reg = ModuleRegistry::new();
281 reg.register(Mod(MEDIA, &[]));
282 assert!(reg.has(MEDIA));
283 assert!(!reg.has(USER));
284 assert_eq!(reg.get(MEDIA).unwrap().id(), MEDIA);
285 assert!(reg.get(ModuleId::new("missing")).is_none());
286 }
287
288 #[test]
289 fn unknown_dependency_errors() {
290 let mut reg = ModuleRegistry::new();
291 reg.register(Mod(BLOG, &[USER]));
292 assert!(matches!(
293 reg.ordered(),
294 Err(CoreError::UnknownModuleDependency { .. })
295 ));
296 }
297
298 #[test]
299 fn cycle_errors() {
300 const A_REQS: &[ModuleId] = &[ModuleId::new("acme.b")];
303 const B_REQS: &[ModuleId] = &[ModuleId::new("acme.a")];
304 let mut reg = ModuleRegistry::new();
305 reg.register(Mod(ModuleId::new("acme.a"), A_REQS))
306 .register(Mod(ModuleId::new("acme.b"), B_REQS));
307 assert!(matches!(
308 reg.ordered(),
309 Err(CoreError::ModuleDependencyCycle { .. })
310 ));
311 }
312
313 #[test]
314 fn duplicate_id_errors() {
315 let mut reg = ModuleRegistry::new();
316 reg.register(Mod(ModuleId::new("a"), &[]))
317 .register(Mod(ModuleId::new("a"), &[]));
318 assert!(matches!(
319 reg.ordered(),
320 Err(CoreError::DuplicateModule { .. })
321 ));
322 }
323}