Skip to main content

caelix_core/
module.rs

1use crate::{
2    BoxFuture, Container, Controller, EventHandler, EventHandlerDef, Injectable, MessageHandlerDef,
3    MessageHandlerKind, Microservice, MicroserviceDef, RegisterableEventHandler, Result,
4    WebSocketGateway,
5};
6use std::{
7    any::{Any, TypeId},
8    collections::{HashMap, HashSet},
9    fmt::Debug,
10    future::Future,
11    sync::Arc,
12    time::Instant,
13};
14
15type ProviderValue = Arc<dyn Any + Send + Sync>;
16type BuildProviderFn =
17    Box<dyn for<'a> Fn(&'a Container) -> BoxFuture<'a, crate::Result<ProviderValue>> + Send + Sync>;
18type LifecycleFn =
19    Box<dyn for<'a> Fn(&'a ProviderValue) -> BoxFuture<'a, crate::Result<()>> + Send + Sync>;
20
21/// Metadata for one resolved provider dependency.
22#[derive(Clone, Copy)]
23/// Public Caelix type `ProviderDependency`.
24pub struct ProviderDependency {
25    type_id: TypeId,
26    type_name: &'static str,
27}
28
29impl ProviderDependency {
30    /// Runs the `of` public API operation.
31    pub fn of<T: Send + Sync + 'static>() -> Self {
32        Self {
33            type_id: TypeId::of::<T>(),
34            type_name: std::any::type_name::<T>(),
35        }
36    }
37
38    pub(crate) fn type_id(&self) -> TypeId {
39        self.type_id
40    }
41}
42
43/// Declares provider dependencies for manual `Injectable` implementations and factories.
44/// Builds the explicit dependency list required by a handwritten
45/// [`Injectable`](crate::Injectable) implementation.
46///
47/// Pass zero or more provider types, for example
48/// `provider_dependencies![UserRepository, Logger]`. The returned metadata is
49/// used during module visibility validation before the provider is constructed.
50#[macro_export]
51macro_rules! provider_dependencies {
52    ($($dependency:ty),* $(,)?) => {
53        vec![$($crate::ProviderDependency::of::<$dependency>()),*]
54    };
55}
56
57/// Public Caelix type `ProviderDef`.
58pub struct ProviderDef {
59    type_id: TypeId,
60    type_name: &'static str,
61    dependencies: Vec<ProviderDependency>,
62    build: BuildProviderFn,
63    init_fn: LifecycleFn,
64    bootstrap_fn: LifecycleFn,
65    shutdown_fn: LifecycleFn,
66}
67
68impl ProviderDef {
69    /// Runs the `of` public API operation.
70    pub fn of<T: Injectable>() -> Self {
71        Self {
72            type_id: TypeId::of::<T>(),
73            type_name: std::any::type_name::<T>(),
74            dependencies: T::dependencies(),
75            build: Box::new(|container| {
76                Box::pin(async move { Ok(Arc::new(T::create(container).await?) as ProviderValue) })
77            }),
78            init_fn: Box::new(|value| {
79                let value = downcast_provider::<T>(value);
80                Box::pin(async move { value?.on_module_init().await })
81            }),
82            bootstrap_fn: Box::new(|value| {
83                let value = downcast_provider::<T>(value);
84                Box::pin(async move { value?.on_bootstrap().await })
85            }),
86            shutdown_fn: Box::new(|value| {
87                let value = downcast_provider::<T>(value);
88                Box::pin(async move { value?.on_shutdown().await })
89            }),
90        }
91    }
92
93    /// Pre-built provider value for tests and other manual registration paths.
94    /// Lifecycle hooks are no-ops (`useValue` semantics).
95    pub fn instance<T: Send + Sync + 'static>(value: T) -> Self {
96        let value = Arc::new(value) as ProviderValue;
97        Self {
98            type_id: TypeId::of::<T>(),
99            type_name: std::any::type_name::<T>(),
100            dependencies: vec![],
101            build: Box::new(move |_| {
102                let value = value.clone();
103                Box::pin(async move { Ok(value) })
104            }),
105            init_fn: noop_lifecycle(),
106            bootstrap_fn: noop_lifecycle(),
107            shutdown_fn: noop_lifecycle(),
108        }
109    }
110
111    /// Runs the `async_factory` public API operation.
112    pub fn async_factory<T, Fut, E>(
113        dependencies: Vec<ProviderDependency>,
114        factory: impl Fn(Arc<Container>) -> Fut + Send + Sync + 'static,
115    ) -> Self
116    where
117        T: Send + Sync + 'static,
118        Fut: Future<Output = std::result::Result<T, E>> + Send + 'static,
119        E: Debug + Send + 'static,
120    {
121        Self {
122            type_id: TypeId::of::<T>(),
123            type_name: std::any::type_name::<T>(),
124            dependencies,
125            build: Box::new(move |container| {
126                let future = factory(Arc::new(container.clone()));
127                Box::pin(async move {
128                    let value = future.await.map_err(|err| {
129                        crate::exception::startup_error(format!(
130                            "async factory failed for {}: {:?}",
131                            std::any::type_name::<T>(),
132                            err
133                        ))
134                    })?;
135                    Ok(Arc::new(value) as ProviderValue)
136                })
137            }),
138            init_fn: noop_lifecycle(),
139            bootstrap_fn: noop_lifecycle(),
140            shutdown_fn: noop_lifecycle(),
141        }
142    }
143
144    /// Runs the `type_id` public API operation.
145    pub fn type_id(&self) -> TypeId {
146        self.type_id
147    }
148    /// Runs the `type_name` public API operation.
149    pub fn type_name(&self) -> &'static str {
150        self.type_name
151    }
152
153    fn assert_registered(&self, container: &Container) -> crate::Result<()> {
154        if container.contains_type_id(self.type_id) {
155            return Ok(());
156        }
157        Err(crate::exception::startup_error(format!(
158            "missing provider at startup: {} was declared by module metadata but was not registered",
159            self.type_name
160        )))
161    }
162
163    async fn run_lifecycle(
164        &self,
165        value: &ProviderValue,
166        hook: &'static str,
167        callback: &LifecycleFn,
168    ) -> crate::Result<()> {
169        callback(value).await.map_err(|err| {
170            crate::exception::startup_error(format!(
171                "{hook} failed for {}: {}: {}",
172                self.type_name, err.error, err.message
173            ))
174        })
175    }
176
177    async fn run_lifecycle_from_container(
178        &self,
179        container: &Container,
180        hook: &'static str,
181        callback: &LifecycleFn,
182    ) -> crate::Result<()> {
183        let value = container.resolve_erased(self.type_id).ok_or_else(|| crate::exception::startup_error(format!(
184            "missing provider during {hook}: {} was declared by module metadata but was not registered", self.type_name
185        )))?;
186        self.run_lifecycle(&value, hook, callback).await
187    }
188}
189
190/// Public Caelix type `ProviderOverrides`.
191pub struct ProviderOverrides {
192    defs: HashMap<TypeId, ProviderDef>,
193}
194impl ProviderOverrides {
195    /// Runs the `new` public API operation.
196    pub fn new() -> Self {
197        Self {
198            defs: HashMap::new(),
199        }
200    }
201    /// Runs the `insert_instance` public API operation.
202    pub fn insert_instance<T: Send + Sync + 'static>(mut self, value: T) -> Self {
203        self.defs
204            .insert(TypeId::of::<T>(), ProviderDef::instance(value));
205        self
206    }
207    /// Runs the `insert_factory` public API operation.
208    pub fn insert_factory<T, Fut, E>(
209        mut self,
210        dependencies: Vec<ProviderDependency>,
211        factory: impl Fn(Arc<Container>) -> Fut + Send + Sync + 'static,
212    ) -> Self
213    where
214        T: Send + Sync + 'static,
215        Fut: Future<Output = std::result::Result<T, E>> + Send + 'static,
216        E: Debug + Send + 'static,
217    {
218        self.defs.insert(
219            TypeId::of::<T>(),
220            ProviderDef::async_factory::<T, Fut, E>(dependencies, factory),
221        );
222        self
223    }
224    /// Runs the `insert` public API operation.
225    pub fn insert(mut self, def: ProviderDef) -> Self {
226        self.defs.insert(def.type_id, def);
227        self
228    }
229    pub(crate) fn into_inner(self) -> HashMap<TypeId, ProviderDef> {
230        self.defs
231    }
232}
233impl Default for ProviderOverrides {
234    fn default() -> Self {
235        Self::new()
236    }
237}
238
239fn downcast_provider<T: Send + Sync + 'static>(value: &ProviderValue) -> crate::Result<Arc<T>> {
240    value.clone().downcast::<T>().map_err(|_| {
241        crate::exception::startup_error(format!(
242            "type mismatch running lifecycle hook for {}",
243            std::any::type_name::<T>()
244        ))
245    })
246}
247fn noop_lifecycle() -> LifecycleFn {
248    Box::new(|_| Box::pin(async { Ok(()) }))
249}
250
251/// Public Caelix type `ControllerDef`.
252pub struct ControllerDef {
253    /// The `register_fn` value.
254    pub register_fn: fn(&mut dyn Any),
255    /// The `route_log_fn` value.
256    pub route_log_fn: fn(),
257    #[cfg(feature = "openapi")]
258    pub(crate) openapi_routes_fn: fn() -> &'static [crate::openapi::OpenApiRouteDef],
259    provider: ProviderDef,
260}
261impl ControllerDef {
262    /// Runs the `of` public API operation.
263    pub fn of<C: Controller + Injectable + 'static>() -> Self {
264        let mut provider = ProviderDef::of::<C>();
265        for dependency in C::route_dependencies() {
266            if !provider
267                .dependencies
268                .iter()
269                .any(|existing| existing.type_id == dependency.type_id)
270            {
271                provider.dependencies.push(dependency);
272            }
273        }
274        Self {
275            register_fn: |any| C::register_routes(any),
276            route_log_fn: || crate::log_controller_routes::<C>(),
277            #[cfg(feature = "openapi")]
278            openapi_routes_fn: || C::openapi_routes(),
279            provider,
280        }
281    }
282}
283
284/// Public Caelix type `GatewayDef`.
285pub struct GatewayDef {
286    /// The `path` value.
287    pub path: &'static str,
288    /// The `type_id` value.
289    pub type_id: TypeId,
290    provider: ProviderDef,
291    kind: GatewayKind,
292}
293enum GatewayKind {
294    WebSocket {
295        resolve_fn: fn(&Container) -> crate::Result<Arc<dyn WebSocketGateway>>,
296    },
297    SocketIo {
298        register_fn: fn(&Container, &dyn Any) -> Result<()>,
299    },
300}
301impl GatewayDef {
302    /// Runs the `websocket` public API operation.
303    pub fn websocket<G: WebSocketGateway>(path: &'static str) -> Self {
304        Self {
305            path,
306            type_id: TypeId::of::<G>(),
307            provider: ProviderDef::of::<G>(),
308            kind: GatewayKind::WebSocket {
309                resolve_fn: |c| Ok(c.resolve::<G>()? as Arc<dyn WebSocketGateway>),
310            },
311        }
312    }
313    #[doc(hidden)]
314    pub fn socket_io<G: Injectable>(
315        path: &'static str,
316        register_fn: fn(&Container, &dyn Any) -> crate::Result<()>,
317    ) -> Self {
318        Self {
319            path,
320            type_id: TypeId::of::<G>(),
321            provider: ProviderDef::of::<G>(),
322            kind: GatewayKind::SocketIo { register_fn },
323        }
324    }
325    /// Runs the `resolve` public API operation.
326    pub fn resolve(&self, container: &Container) -> Result<Arc<dyn WebSocketGateway>> {
327        match self.kind {
328            GatewayKind::WebSocket { resolve_fn } => resolve_fn(container),
329            GatewayKind::SocketIo { .. } => Err(crate::exception::startup_error(format!(
330                "Socket.IO gateway {} cannot be mounted by an RFC 6455 application",
331                self.path
332            ))),
333        }
334    }
335    #[doc(hidden)]
336    pub fn is_websocket(&self) -> bool {
337        matches!(self.kind, GatewayKind::WebSocket { .. })
338    }
339    #[doc(hidden)]
340    pub fn register_socket_io(&self, container: &Container, handle: &dyn Any) -> Result<()> {
341        match self.kind {
342            GatewayKind::WebSocket { .. } => Ok(()),
343            GatewayKind::SocketIo { register_fn } => register_fn(container, handle),
344        }
345    }
346}
347/// Public Caelix extension trait `Gateway`.
348pub trait Gateway: Injectable {
349    #[doc(hidden)]
350    fn definition() -> GatewayDef;
351}
352
353/// Public Caelix type `ModuleDef`.
354pub struct ModuleDef {
355    type_id: TypeId,
356    type_name: &'static str,
357    metadata_fn: fn() -> ModuleMetadata,
358    pub(crate) route_log_fn: fn(),
359}
360impl ModuleDef {
361    /// Runs the `of` public API operation.
362    pub fn of<M: Module + 'static>() -> Self {
363        Self {
364            type_id: TypeId::of::<M>(),
365            type_name: std::any::type_name::<M>(),
366            metadata_fn: M::register,
367            route_log_fn: || crate::log_module_routes::<M>(),
368        }
369    }
370}
371/// Public Caelix extension trait `Module`.
372pub trait Module {
373    /// Public Caelix API.
374    fn register() -> ModuleMetadata;
375}
376
377/// Public Caelix type `ModuleMetadata`.
378pub struct ModuleMetadata {
379    /// The `imports` value.
380    pub imports: Vec<ModuleDef>,
381    /// The `providers` value.
382    pub providers: Vec<ProviderDef>,
383    /// The `controllers` value.
384    pub controllers: Vec<ControllerDef>,
385    /// The `event_handlers` value.
386    pub event_handlers: Vec<EventHandlerDef>,
387    /// The `gateways` value.
388    pub gateways: Vec<GatewayDef>,
389    /// The dependency-injected microservice classes declared by this module.
390    pub microservices: Vec<MicroserviceDef>,
391    exports: Vec<ProviderDependency>,
392    global: bool,
393}
394impl ModuleMetadata {
395    /// Runs the `new` public API operation.
396    pub fn new() -> Self {
397        Self {
398            imports: vec![],
399            providers: vec![],
400            controllers: vec![],
401            event_handlers: vec![],
402            gateways: vec![],
403            microservices: vec![],
404            exports: vec![],
405            global: false,
406        }
407    }
408    /// Creates metadata for a global module. Only explicitly exported providers become global.
409    pub fn global() -> Self {
410        let mut metadata = Self::new();
411        metadata.global = true;
412        metadata
413    }
414    /// Runs the `import` public API operation.
415    pub fn import<M: Module + 'static>(mut self) -> Self {
416        self.imports.push(ModuleDef::of::<M>());
417        self
418    }
419    /// Runs the `provider` public API operation.
420    pub fn provider<T: Injectable>(mut self) -> Self {
421        self.providers.push(ProviderDef::of::<T>());
422        self
423    }
424    /// Runs the `provider_async_factory` public API operation.
425    pub fn provider_async_factory<T, Fut, E>(
426        mut self,
427        dependencies: Vec<ProviderDependency>,
428        factory: impl Fn(Arc<Container>) -> Fut + Send + Sync + 'static,
429    ) -> Self
430    where
431        T: Send + Sync + 'static,
432        Fut: Future<Output = std::result::Result<T, E>> + Send + 'static,
433        E: Debug + Send + 'static,
434    {
435        self.providers.push(ProviderDef::async_factory::<T, Fut, E>(
436            dependencies,
437            factory,
438        ));
439        self
440    }
441    /// Runs the `controller` public API operation.
442    pub fn controller<C: Controller + Injectable + 'static>(mut self) -> Self {
443        self.controllers.push(ControllerDef::of::<C>());
444        self
445    }
446    /// Runs the `gateway` public API operation.
447    pub fn gateway<G: Gateway>(mut self) -> Self {
448        self.gateways.push(G::definition());
449        self
450    }
451    /// Registers a dependency-injected microservice and all of its message handlers.
452    ///
453    /// A microservice is already a provider; do not additionally register it
454    /// with [`Self::provider`].
455    pub fn microservice<T: Microservice>(mut self) -> Self {
456        self.microservices.push(T::definition());
457        self
458    }
459    /// Runs the `event_handler` public API operation.
460    pub fn event_handler<H>(mut self) -> Self
461    where
462        H: RegisterableEventHandler + EventHandler<H::Event>,
463    {
464        self.event_handlers.push(EventHandlerDef::of::<H>());
465        self
466    }
467    /// Runs the `event_handler_for` public API operation.
468    pub fn event_handler_for<E, H>(mut self) -> Self
469    where
470        E: Clone + Send + Sync + 'static,
471        H: Injectable + EventHandler<E>,
472    {
473        self.event_handlers
474            .push(EventHandlerDef::for_event::<E, H>());
475        self
476    }
477    /// Makes a locally declared provider, or a direct import's export, available to importing modules.
478    pub fn export<T: Send + Sync + 'static>(mut self) -> Self {
479        self.exports.push(ProviderDependency::of::<T>());
480        self
481    }
482}
483impl Default for ModuleMetadata {
484    fn default() -> Self {
485        Self::new()
486    }
487}
488
489struct ModuleNode {
490    type_id: TypeId,
491    type_name: &'static str,
492    metadata: ModuleMetadata,
493    imports: Vec<usize>,
494}
495struct ModuleGraph {
496    nodes: Vec<ModuleNode>,
497}
498#[derive(Clone, Copy)]
499enum ProviderSlot {
500    Provider(usize),
501    Controller(usize),
502    Gateway(usize),
503    Microservice(usize),
504}
505#[derive(Clone, Copy)]
506struct ProviderRegistration {
507    module: usize,
508    slot: ProviderSlot,
509}
510
511impl ModuleGraph {
512    fn discover<M: Module + 'static>() -> crate::Result<Self> {
513        Self::discover_from(ModuleDef::of::<M>())
514    }
515    fn discover_from(root: ModuleDef) -> crate::Result<Self> {
516        fn visit(
517            def: ModuleDef,
518            graph: &mut Vec<ModuleNode>,
519            states: &mut HashMap<TypeId, u8>,
520            path: &mut Vec<&'static str>,
521        ) -> crate::Result<usize> {
522            match states.get(&def.type_id).copied() {
523                Some(2) => {
524                    return Ok(graph
525                        .iter()
526                        .position(|node| node.type_id == def.type_id)
527                        .expect("discovered module missing"));
528                }
529                Some(1) => {
530                    let mut cycle = path.clone();
531                    cycle.push(def.type_name);
532                    return Err(crate::exception::startup_error(format!(
533                        "circular module import: {}",
534                        cycle.join(" -> ")
535                    )));
536                }
537                _ => {}
538            }
539            states.insert(def.type_id, 1);
540            path.push(def.type_name);
541            let metadata = (def.metadata_fn)();
542            let index = graph.len();
543            graph.push(ModuleNode {
544                type_id: def.type_id,
545                type_name: def.type_name,
546                metadata,
547                imports: vec![],
548            });
549            let imports = std::mem::take(&mut graph[index].metadata.imports);
550            for import in imports {
551                let child = visit(import, graph, states, path)?;
552                graph[index].imports.push(child);
553            }
554            path.pop();
555            states.insert(def.type_id, 2);
556            Ok(index)
557        }
558        let mut nodes = vec![];
559        let mut states = HashMap::new();
560        let root_index = visit(root, &mut nodes, &mut states, &mut vec![])?;
561        // DFS creates parents before children; reverse for imported-first deterministic traversal.
562        let mut ordered = Vec::new();
563        let mut seen = HashSet::new();
564        fn order(
565            index: usize,
566            nodes: &Vec<ModuleNode>,
567            seen: &mut HashSet<usize>,
568            ordered: &mut Vec<usize>,
569        ) {
570            if !seen.insert(index) {
571                return;
572            }
573            for &child in &nodes[index].imports {
574                order(child, nodes, seen, ordered);
575            }
576            ordered.push(index);
577        }
578        order(root_index, &nodes, &mut seen, &mut ordered);
579        let mut remap = HashMap::new();
580        for (new, old) in ordered.iter().enumerate() {
581            remap.insert(*old, new);
582        }
583        let mut slots: Vec<Option<ModuleNode>> = nodes.into_iter().map(Some).collect();
584        let mut result = Vec::with_capacity(slots.len());
585        for old in ordered {
586            let mut node = slots[old]
587                .take()
588                .expect("module graph ordering repeated a node");
589            node.imports = node.imports.into_iter().map(|i| remap[&i]).collect();
590            result.push(node);
591        }
592        let _ = remap[&root_index];
593        Ok(Self { nodes: result })
594    }
595
596    fn definitions(&self) -> Vec<ProviderRegistration> {
597        let mut values = vec![];
598        for (module, node) in self.nodes.iter().enumerate() {
599            values.extend(
600                (0..node.metadata.providers.len()).map(|slot| ProviderRegistration {
601                    module,
602                    slot: ProviderSlot::Provider(slot),
603                }),
604            );
605            values.extend(
606                (0..node.metadata.controllers.len()).map(|slot| ProviderRegistration {
607                    module,
608                    slot: ProviderSlot::Controller(slot),
609                }),
610            );
611            for slot in 0..node.metadata.gateways.len() {
612                values.push(ProviderRegistration {
613                    module,
614                    slot: ProviderSlot::Gateway(slot),
615                });
616            }
617            values.extend((0..node.metadata.microservices.len()).map(|slot| {
618                ProviderRegistration {
619                    module,
620                    slot: ProviderSlot::Microservice(slot),
621                }
622            }));
623        }
624        values
625    }
626    fn def(&self, registration: ProviderRegistration) -> &ProviderDef {
627        match registration.slot {
628            ProviderSlot::Provider(index) => {
629                &self.nodes[registration.module].metadata.providers[index]
630            }
631            ProviderSlot::Controller(index) => {
632                &self.nodes[registration.module].metadata.controllers[index].provider
633            }
634            ProviderSlot::Gateway(index) => {
635                &self.nodes[registration.module].metadata.gateways[index].provider
636            }
637            ProviderSlot::Microservice(index) => {
638                &self.nodes[registration.module].metadata.microservices[index].provider
639            }
640        }
641    }
642    fn local_types(&self, module: usize) -> HashSet<TypeId> {
643        self.definitions()
644            .into_iter()
645            .filter(|r| r.module == module)
646            .map(|r| self.def(r).type_id)
647            .collect()
648    }
649    /// Validates the module graph and returns providers in construction order.
650    ///
651    /// Passing `None` performs metadata-only validation. A container is only
652    /// needed during application startup, when provider overrides and values
653    /// registered by application setup may satisfy declarations.
654    fn preflight(&self, container: Option<&Container>) -> crate::Result<Vec<ProviderRegistration>> {
655        let definitions = self.definitions();
656        let mut by_type = HashMap::new();
657        for registration in &definitions {
658            let def = self.def(*registration);
659            if let Some(existing) = by_type.insert(def.type_id, *registration) {
660                if !container.is_some_and(|container| {
661                    container.has_pending_override(def.type_id)
662                        || container.was_overridden(def.type_id)
663                }) {
664                    return Err(crate::exception::startup_error(format!(
665                        "duplicate provider registration for {} in {} and {}",
666                        def.type_name,
667                        self.nodes[existing.module].type_name,
668                        self.nodes[registration.module].type_name
669                    )));
670                }
671            }
672        }
673        let locals: Vec<HashSet<TypeId>> =
674            (0..self.nodes.len()).map(|i| self.local_types(i)).collect();
675        let mut exports = vec![HashSet::new(); self.nodes.len()];
676        for index in 0..self.nodes.len() {
677            let node = &self.nodes[index];
678            for export in &node.metadata.exports {
679                let imported = node
680                    .imports
681                    .iter()
682                    .any(|&child| exports[child].contains(&export.type_id));
683                if !locals[index].contains(&export.type_id) && !imported {
684                    return Err(crate::exception::startup_error(format!(
685                        "module {} cannot export {}: it is neither declared locally nor exported by a direct import",
686                        node.type_name, export.type_name
687                    )));
688                }
689                exports[index].insert(export.type_id);
690            }
691        }
692        let global_exports: HashSet<TypeId> = self
693            .nodes
694            .iter()
695            .enumerate()
696            .filter(|(_, node)| node.metadata.global)
697            .flat_map(|(index, _)| exports[index].iter().copied())
698            .collect();
699        let visible = |module: usize, type_id: TypeId| {
700            locals[module].contains(&type_id)
701                || global_exports.contains(&type_id)
702                || self.nodes[module]
703                    .imports
704                    .iter()
705                    .any(|&child| exports[child].contains(&type_id))
706        };
707        for registration in &definitions {
708            let production = self.def(*registration);
709            let effective = container
710                .and_then(|container| container.pending_override(production.type_id))
711                .unwrap_or(production);
712            for dependency in &effective.dependencies {
713                if dependency.type_id == TypeId::of::<crate::Logger>() {
714                    continue;
715                }
716                if by_type.contains_key(&dependency.type_id) {
717                    if !visible(registration.module, dependency.type_id) {
718                        return Err(crate::exception::startup_error(format!(
719                            "{} depends on {} but it is not visible in module {}; import and export its module",
720                            effective.type_name,
721                            dependency.type_name,
722                            self.nodes[registration.module].type_name
723                        )));
724                    }
725                } else if !container
726                    .is_some_and(|container| container.contains_type_id(dependency.type_id))
727                {
728                    return Err(crate::exception::startup_error(format!(
729                        "missing provider at startup: {} depends on {} but no provider is registered",
730                        effective.type_name, dependency.type_name
731                    )));
732                }
733            }
734        }
735        for (module, node) in self.nodes.iter().enumerate() {
736            for handler in &node.metadata.event_handlers {
737                handler.assert_registered_or_declared(&locals[module])?;
738                if !visible(module, TypeId::of::<crate::EventBus>()) {
739                    return Err(crate::exception::startup_error(format!(
740                        "no provider registered for EventBus in module {}; import EventModule",
741                        node.type_name
742                    )));
743                }
744            }
745        }
746        let mut command_patterns: HashMap<&'static str, usize> = HashMap::new();
747        let mut event_patterns: HashMap<&'static str, usize> = HashMap::new();
748        for (module, node) in self.nodes.iter().enumerate() {
749            for microservice in &node.metadata.microservices {
750                for handler in microservice.handlers() {
751                    if !valid_microservice_pattern(
752                        handler.pattern,
753                        handler.kind == MessageHandlerKind::Command,
754                    ) {
755                        return Err(crate::exception::startup_error(format!(
756                            "invalid microservice subject `{}` in module {}",
757                            handler.pattern, node.type_name
758                        )));
759                    }
760                    let (other, existing) = if handler.kind == MessageHandlerKind::Command {
761                        (&event_patterns, command_patterns.get(handler.pattern))
762                    } else {
763                        (&command_patterns, event_patterns.get(handler.pattern))
764                    };
765                    if let Some(other_module) = other.get(handler.pattern) {
766                        return Err(crate::exception::startup_error(format!(
767                            "command and event handlers cannot share subject `{}` in {} and {}",
768                            handler.pattern, self.nodes[*other_module].type_name, node.type_name
769                        )));
770                    }
771                    if let Some(existing) = existing {
772                        return Err(crate::exception::startup_error(format!(
773                            "duplicate {:?} handler subject `{}` in {} and {}",
774                            handler.kind,
775                            handler.pattern,
776                            self.nodes[*existing].type_name,
777                            node.type_name
778                        )));
779                    }
780                    if handler.kind == MessageHandlerKind::Command {
781                        command_patterns.insert(handler.pattern, module);
782                    } else {
783                        event_patterns.insert(handler.pattern, module);
784                    }
785                }
786            }
787        }
788        for command in command_patterns.keys() {
789            for event in event_patterns.keys() {
790                if microservice_patterns_intersect(command, event) {
791                    return Err(crate::exception::startup_error(format!(
792                        "command subject `{command}` overlaps event subject `{event}`; command and event transports must be disjoint"
793                    )));
794                }
795            }
796        }
797        let event_subjects: Vec<_> = event_patterns.keys().copied().collect();
798        for (index, first) in event_subjects.iter().enumerate() {
799            for second in event_subjects.iter().skip(index + 1) {
800                if microservice_patterns_intersect(first, second) {
801                    return Err(crate::exception::startup_error(format!(
802                        "event subjects `{first}` and `{second}` overlap; each event delivery must have one owner"
803                    )));
804                }
805            }
806        }
807        let mut sorted = vec![];
808        let mut states = HashMap::new();
809        let mut stack = vec![];
810        fn schedule(
811            reg: ProviderRegistration,
812            graph: &ModuleGraph,
813            by_type: &HashMap<TypeId, ProviderRegistration>,
814            container: Option<&Container>,
815            states: &mut HashMap<TypeId, u8>,
816            stack: &mut Vec<&'static str>,
817            sorted: &mut Vec<ProviderRegistration>,
818        ) -> crate::Result<()> {
819            let def = graph.def(reg);
820            match states.get(&def.type_id).copied() {
821                Some(2) => return Ok(()),
822                Some(1) => {
823                    let mut cycle = stack.clone();
824                    cycle.push(def.type_name);
825                    return Err(crate::exception::startup_error(format!(
826                        "provider dependency cycle: {}",
827                        cycle.join(" -> ")
828                    )));
829                }
830                _ => {}
831            }
832            states.insert(def.type_id, 1);
833            stack.push(def.type_name);
834            let effective = container
835                .and_then(|container| container.pending_override(def.type_id))
836                .unwrap_or(def);
837            for dep in &effective.dependencies {
838                if let Some(next) = by_type.get(&dep.type_id) {
839                    schedule(*next, graph, by_type, container, states, stack, sorted)?;
840                }
841            }
842            stack.pop();
843            states.insert(def.type_id, 2);
844            sorted.push(reg);
845            Ok(())
846        }
847        for registration in definitions {
848            schedule(
849                registration,
850                self,
851                &by_type,
852                container,
853                &mut states,
854                &mut stack,
855                &mut sorted,
856            )?;
857        }
858        Ok(sorted)
859    }
860    fn def_by_type(&self, type_id: TypeId) -> Option<&ProviderDef> {
861        self.definitions().into_iter().find_map(|r| {
862            let def = self.def(r);
863            (def.type_id == type_id).then_some(def)
864        })
865    }
866}
867
868async fn initialize_graph(graph: &ModuleGraph, container: &mut Container) -> crate::Result<()> {
869    let order = graph.preflight(Some(container))?;
870    for registration in order {
871        let declared = graph.def(registration);
872        container.mark_provider_declared(declared.type_id);
873        if container.contains_type_id(declared.type_id)
874            || container.was_overridden(declared.type_id)
875        {
876            continue;
877        }
878        let start = Instant::now();
879        let override_def = container.take_pending_override(declared.type_id);
880        if override_def.is_some() {
881            container.mark_override_applied(declared.type_id);
882        }
883        let effective = override_def.as_ref().unwrap_or(declared);
884        let scoped_container =
885            container.scoped_for_provider(effective.type_name, &effective.dependencies);
886        let value = match (effective.build)(&scoped_container).await {
887            Ok(value) => value,
888            Err(error) => {
889                rollback_graph(graph, container).await;
890                return Err(error);
891            }
892        };
893        container.register_erased(effective.type_id, value.clone());
894        if let Err(error) = effective
895            .run_lifecycle(&value, "on_module_init", &effective.init_fn)
896            .await
897        {
898            rollback_graph(graph, container).await;
899            return Err(error);
900        }
901        if override_def.is_none() {
902            container.record_initialized_provider(declared.type_id);
903        }
904        crate::log_provider_initialized(effective.type_name, start.elapsed());
905    }
906    for node in &graph.nodes {
907        for handler in &node.metadata.event_handlers {
908            if let Err(error) = handler.register(container) {
909                rollback_graph(graph, container).await;
910                return Err(error);
911            }
912        }
913        crate::log_module_initialized(node.type_name, std::time::Duration::ZERO);
914    }
915    Ok(())
916}
917
918async fn bootstrap_graph(graph: &ModuleGraph, container: &Container) -> crate::Result<()> {
919    let order = graph.preflight(Some(container))?;
920    let initialized: HashSet<TypeId> = container.initialized_provider_types().into_iter().collect();
921    for registration in order {
922        let def = graph.def(registration);
923        if initialized.contains(&def.type_id)
924            && !container.was_overridden(def.type_id)
925            && container.begin_provider_bootstrap(def.type_id)
926        {
927            if let Err(error) = def
928                .run_lifecycle_from_container(container, "on_bootstrap", &def.bootstrap_fn)
929                .await
930            {
931                rollback_graph(graph, container).await;
932                return Err(error);
933            }
934        }
935    }
936    Ok(())
937}
938
939async fn rollback_graph(graph: &ModuleGraph, container: &Container) {
940    for type_id in container.take_initialized_providers().into_iter().rev() {
941        if let Some(def) = graph.def_by_type(type_id) {
942            let _ = def
943                .run_lifecycle_from_container(container, "on_shutdown", &def.shutdown_fn)
944                .await;
945        }
946    }
947}
948
949/// Runs the `register_module` public API operation.
950pub async fn register_module<M: Module + 'static>(container: &mut Container) -> Result<()> {
951    let graph = ModuleGraph::discover::<M>()?;
952    initialize_graph(&graph, container).await
953}
954/// Runs the `bootstrap_module` public API operation.
955pub async fn bootstrap_module<M: Module + 'static>(container: &Container) -> Result<()> {
956    let graph = ModuleGraph::discover::<M>()?;
957    bootstrap_graph(&graph, container).await
958}
959/// Runs the `shutdown_module` public API operation.
960pub async fn shutdown_module<M: Module + 'static>(container: &Container) -> Result<()> {
961    let graph = ModuleGraph::discover::<M>()?;
962    let mut first = None;
963    for type_id in container.take_initialized_providers().into_iter().rev() {
964        if let Some(def) = graph.def_by_type(type_id) {
965            if let Err(error) = def
966                .run_lifecycle_from_container(container, "on_shutdown", &def.shutdown_fn)
967                .await
968            {
969                if first.is_none() {
970                    first = Some(error);
971                }
972            }
973        }
974    }
975    first.map_or(Ok(()), Err)
976}
977
978/// Runs the `build_container` public API operation.
979pub async fn build_container<M: Module + 'static>() -> Result<Container> {
980    build_container_with_overrides::<M>(ProviderOverrides::new()).await
981}
982#[doc(hidden)]
983pub async fn build_container_with_setup<M: Module + 'static>(
984    setup: impl FnOnce(&mut Container),
985) -> Result<Container> {
986    crate::log_application_starting();
987    let mut container = Container::new();
988    setup(&mut container);
989    if let Err(error) = register_module::<M>(&mut container).await {
990        return Err(error);
991    }
992    if let Err(error) =
993        validate_module_providers::<M>(&container).and_then(|_| validate_gateway_paths::<M>())
994    {
995        let graph = ModuleGraph::discover::<M>()?;
996        rollback_graph(&graph, &container).await;
997        return Err(error);
998    }
999    if let Err(error) = bootstrap_module::<M>(&container).await {
1000        return Err(error);
1001    }
1002    Ok(container)
1003}
1004/// Runs the `build_container_with_overrides` public API operation.
1005pub async fn build_container_with_overrides<M: Module + 'static>(
1006    overrides: ProviderOverrides,
1007) -> crate::Result<Container> {
1008    crate::log_application_starting();
1009    let mut container = Container::new();
1010    container.seed_overrides(overrides);
1011    if let Err(error) = register_module::<M>(&mut container).await {
1012        return Err(error);
1013    }
1014    if let Err(error) = container
1015        .assert_no_unused_overrides()
1016        .and_then(|_| validate_module_providers::<M>(&container))
1017        .and_then(|_| validate_gateway_paths::<M>())
1018    {
1019        let graph = ModuleGraph::discover::<M>()?;
1020        rollback_graph(&graph, &container).await;
1021        return Err(error);
1022    }
1023    if let Err(error) = bootstrap_module::<M>(&container).await {
1024        return Err(error);
1025    }
1026    Ok(container)
1027}
1028
1029/// Collects the transport-neutral handlers declared by a module graph.
1030///
1031/// Transport adapters call this after constructing the shared container. The
1032/// same graph validation used during startup rejects duplicate ownership before
1033/// any subscriptions are started.
1034pub fn collect_module_message_handlers<M: Module + 'static>() -> Result<Vec<MessageHandlerDef>> {
1035    collect_module_message_handlers_with_container::<M>(None)
1036}
1037
1038/// Collects message handlers while validating dependencies against an existing
1039/// application container. Transport adapters use this when they register
1040/// transport-owned values before module construction.
1041pub fn collect_module_message_handlers_with_container<M: Module + 'static>(
1042    container: Option<&Container>,
1043) -> Result<Vec<MessageHandlerDef>> {
1044    let graph = ModuleGraph::discover::<M>()?;
1045    graph.preflight(container)?;
1046    let mut handlers = Vec::new();
1047    for node in &graph.nodes {
1048        for microservice in &node.metadata.microservices {
1049            handlers.extend(microservice.handlers());
1050        }
1051    }
1052    Ok(handlers)
1053}
1054
1055fn validate_gateway_paths_in_graph(graph: &ModuleGraph) -> crate::Result<()> {
1056    let mut paths = HashSet::new();
1057    for node in &graph.nodes {
1058        for gateway in &node.metadata.gateways {
1059            if !gateway.path.starts_with('/') {
1060                return Err(crate::exception::startup_error(format!(
1061                    "websocket gateway path must start with '/': {}",
1062                    gateway.path
1063                )));
1064            }
1065            if !paths.insert((gateway.path, gateway.is_websocket())) {
1066                return Err(crate::exception::startup_error(format!(
1067                    "duplicate gateway path: {}",
1068                    gateway.path
1069                )));
1070            }
1071        }
1072    }
1073    Ok(())
1074}
1075
1076fn valid_microservice_pattern(pattern: &str, command: bool) -> bool {
1077    if pattern.is_empty() || pattern.chars().any(char::is_whitespace) {
1078        return false;
1079    }
1080    let tokens: Vec<_> = pattern.split('.').collect();
1081    tokens.iter().enumerate().all(|(index, token)| {
1082        !token.is_empty()
1083            && (!token.contains('*') || (!command && *token == "*"))
1084            && (!token.contains('>') || (!command && *token == ">" && index + 1 == tokens.len()))
1085    })
1086}
1087
1088fn microservice_patterns_intersect(first: &str, second: &str) -> bool {
1089    let first: Vec<_> = first.split('.').collect();
1090    let second: Vec<_> = second.split('.').collect();
1091    let mut index = 0;
1092    loop {
1093        let left = first.get(index).copied();
1094        let right = second.get(index).copied();
1095        match (left, right) {
1096            (None, None) => return true,
1097            (Some(">"), Some(_)) | (Some(_), Some(">")) => return true,
1098            (Some(left), Some(right)) if left == "*" || right == "*" || left == right => {
1099                index += 1;
1100            }
1101            _ => return false,
1102        }
1103    }
1104}
1105
1106#[cfg(test)]
1107mod microservice_pattern_tests {
1108    use super::microservice_patterns_intersect;
1109
1110    #[test]
1111    fn terminal_wildcards_do_not_overlap_their_empty_prefix() {
1112        assert!(!microservice_patterns_intersect("orders", "orders.>"));
1113        assert!(microservice_patterns_intersect(
1114            "orders.created",
1115            "orders.>"
1116        ));
1117    }
1118}
1119
1120fn validate_gateway_paths<M: Module + 'static>() -> crate::Result<()> {
1121    validate_gateway_paths_in_graph(&ModuleGraph::discover::<M>()?)
1122}
1123
1124/// Validates a module's metadata without constructing providers or starting an application.
1125///
1126/// This checks module imports, provider declarations and dependencies, exports,
1127/// event-handler declarations, and WebSocket gateway paths. It intentionally
1128/// does not invoke provider constructors or factories, lifecycle hooks, external
1129/// services, or network listeners.
1130pub fn validate_module<M: Module + 'static>() -> Result<()> {
1131    let graph = ModuleGraph::discover::<M>()?;
1132    graph.preflight(None)?;
1133    validate_gateway_paths_in_graph(&graph)
1134}
1135
1136/// Runs the `validate_module_providers` public API operation.
1137pub fn validate_module_providers<M: Module + 'static>(container: &Container) -> Result<()> {
1138    let graph = ModuleGraph::discover::<M>()?;
1139    graph.preflight(Some(container))?;
1140    for registration in graph.definitions() {
1141        graph.def(registration).assert_registered(container)?;
1142    }
1143    Ok(())
1144}
1145
1146/// Runs the `register_module_controllers` public API operation.
1147pub fn register_module_controllers<M: Module + 'static>(any: &mut dyn Any) {
1148    if let Ok(graph) = ModuleGraph::discover::<M>() {
1149        for node in graph.nodes {
1150            for controller in &node.metadata.controllers {
1151                (controller.register_fn)(any);
1152            }
1153        }
1154    }
1155}
1156#[cfg(feature = "openapi")]
1157#[doc(hidden)]
1158pub fn visit_module_openapi_routes<M: Module + 'static>(
1159    visitor: &mut impl FnMut(&crate::openapi::OpenApiRouteDef),
1160) {
1161    if let Ok(graph) = ModuleGraph::discover::<M>() {
1162        for node in graph.nodes {
1163            for controller in &node.metadata.controllers {
1164                for route in (controller.openapi_routes_fn)() {
1165                    visitor(route);
1166                }
1167            }
1168        }
1169    }
1170}
1171/// Runs the `visit_module_gateways` public API operation.
1172pub fn visit_module_gateways<M: Module + 'static>(visitor: &mut impl FnMut(&GatewayDef)) {
1173    if let Ok(graph) = ModuleGraph::discover::<M>() {
1174        let mut seen = HashSet::new();
1175        for node in graph.nodes {
1176            for gateway in &node.metadata.gateways {
1177                if seen.insert(gateway.type_id) {
1178                    visitor(gateway);
1179                }
1180            }
1181        }
1182    }
1183}