Skip to main content

trait_kit/kit/
builder.rs

1// Copyright © 2026 Kirky.X. All rights reserved.
2
3//! KitModuleBuilder — build and register module capabilities.
4
5use std::marker::PhantomData;
6use std::sync::Arc;
7
8use crate::core::builder::ModuleBuilder;
9use crate::core::capability::CapabilityKey;
10use crate::core::module::Module;
11
12use crate::kit::error::KitError;
13use crate::kit::kit::Kit;
14
15/// Trait for converting a builder into a KitModuleBuilder.
16///
17/// Implemented automatically for all types that implement `ModuleBuilder<M>`.
18pub trait IntoKitModuleBuilder<M: Module> {
19    /// The builder type that gets wrapped.
20    type Builder: ModuleBuilder<M>;
21
22    /// Attach a Kit reference to the builder.
23    ///
24    /// Returns a `KitModuleBuilder` that can call `.provide::<K>()`.
25    fn kit(self, kit: &Kit) -> KitModuleBuilder<M, Self::Builder>;
26}
27
28/// Blanket implementation for all ModuleBuilder types.
29impl<M, B> IntoKitModuleBuilder<M> for B
30where
31    M: Module<Builder = B>,
32    B: ModuleBuilder<M>,
33{
34    type Builder = B;
35
36    fn kit(self, kit: &Kit) -> KitModuleBuilder<M, Self::Builder> {
37        KitModuleBuilder::new(self, kit.clone())
38    }
39}
40
41/// A builder wrapper that can build and register a module's capability.
42///
43/// Created by calling `.kit(&kit)` on any standard builder.
44///
45/// Does NOT implement `WithConfig` or `WithRequirements` — those must be
46/// called before `.kit()`.
47pub struct KitModuleBuilder<M: Module, B> {
48    builder: B,
49    kit: Kit,
50    _phantom: PhantomData<M>,
51}
52
53impl<M: Module, B: ModuleBuilder<M>> KitModuleBuilder<M, B> {
54    /// Create a new KitModuleBuilder.
55    pub fn new(builder: B, kit: Kit) -> Self {
56        KitModuleBuilder {
57            builder,
58            kit,
59            _phantom: PhantomData,
60        }
61    }
62
63    /// Build the module and register its capability.
64    ///
65    /// Returns the built capability on success.
66    /// On failure, returns `KitError::BuildFailed` or `KitError::DuplicateCapability`.
67    ///
68    /// # Type Constraint
69    ///
70    /// `M::Capability` must be convertible to `Arc<K::Capability>` for the provided key `K`.
71    /// Typically, `M::Capability = Arc<dyn Trait + Send + Sync>` and `K::Capability = dyn Trait`.
72    ///
73    /// # Failure Behavior
74    ///
75    /// If build succeeds but registration fails (key already exists),
76    /// the built capability is discarded and `DuplicateCapability` is returned.
77    pub fn provide<K>(self) -> Result<Arc<K::Capability>, KitError>
78    where
79        K: CapabilityKey,
80        M: Module<Capability = Arc<K::Capability>>,
81    {
82        let module_name = M::NAME;
83
84        // Build the capability
85        let capability: Arc<K::Capability> =
86            self.builder.build().map_err(|e| KitError::BuildFailed {
87                module: module_name,
88                source: Box::new(e),
89            })?;
90
91        // Register in Kit
92        self.kit.provide::<K>(Arc::clone(&capability))?;
93
94        // Return the registered capability
95        Ok(capability)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::core::builder::ModuleBuilder;
103    use crate::core::capability::CapabilityKey;
104    use crate::core::module::Module;
105    use std::fmt;
106
107    #[derive(Debug)]
108    struct TestError(&'static str);
109
110    impl fmt::Display for TestError {
111        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112            write!(f, "{}", self.0)
113        }
114    }
115
116    impl std::error::Error for TestError {}
117
118    struct TestCapKey;
119    impl CapabilityKey for TestCapKey {
120        type Capability = dyn Send + Sync;
121        const NAME: &'static str = "test_cap";
122    }
123
124    struct TestModule;
125    impl Module for TestModule {
126        const NAME: &'static str = "test_module";
127        type Config = ();
128        type Requirements = ();
129        type Capability = Arc<dyn Send + Sync>;
130        type Error = TestError;
131        type Builder = TestBuilder;
132    }
133
134    struct TestBuilder;
135    impl ModuleBuilder<TestModule> for TestBuilder {
136        fn build(self) -> Result<Arc<dyn Send + Sync>, TestError> {
137            Ok(Arc::new(42i32) as Arc<dyn Send + Sync>)
138        }
139    }
140
141    struct FailingModule;
142    impl Module for FailingModule {
143        const NAME: &'static str = "failing_module";
144        type Config = ();
145        type Requirements = ();
146        type Capability = Arc<dyn Send + Sync>;
147        type Error = TestError;
148        type Builder = FailingBuilder;
149    }
150
151    struct FailingBuilder;
152    impl ModuleBuilder<FailingModule> for FailingBuilder {
153        fn build(self) -> Result<Arc<dyn Send + Sync>, TestError> {
154            Err(TestError("build failed"))
155        }
156    }
157
158    #[test]
159    fn kit_creates_kit_module_builder() {
160        let kit = Kit::new();
161        let kmb = TestBuilder.kit(&kit);
162        // Verify it is a KitModuleBuilder by calling provide
163        let result = kmb.provide::<TestCapKey>();
164        assert!(result.is_ok());
165        assert!(kit.contains::<TestCapKey>());
166    }
167
168    #[test]
169    fn provide_build_failure() {
170        let kit = Kit::new();
171        let result = FailingBuilder.kit(&kit).provide::<TestCapKey>();
172        assert!(result.is_err(), "build should fail");
173        let err = result.err().unwrap();
174        assert!(err.to_string().contains("failed to build module"));
175        assert!(err.to_string().contains("failing_module"));
176    }
177
178    #[test]
179    fn provide_duplicate_failure() {
180        let kit = Kit::new();
181        TestBuilder.kit(&kit).provide::<TestCapKey>().unwrap();
182        let result = TestBuilder.kit(&kit).provide::<TestCapKey>();
183        assert!(result.is_err(), "duplicate should fail");
184        let err = result.err().unwrap();
185        assert!(err.to_string().contains("already exists"));
186        assert!(err.to_string().contains("test_cap"));
187    }
188
189    #[test]
190    fn test_error_display() {
191        let err = TestError("oops");
192        assert_eq!(err.to_string(), "oops");
193    }
194}