1use std::any::{Any, TypeId};
11use std::cell::RefCell;
12use std::collections::HashMap;
13#[cfg(feature = "hot-reload")]
14use std::rc::Rc;
15
16use crate::core::error::KitError;
17use crate::core::meta::{AutoBuilder, BuildFn};
18
19#[cfg(feature = "encryption")]
20use super::config::EncryptedBlob;
21use super::graph::{DependencyGraph, GraphError, ModuleEntry};
22use super::typemap::TypeMap;
23
24#[cfg(feature = "encryption")]
27const KEY_DERIVATION_VERSION: &str = "v1";
28
29#[cfg(feature = "encryption")]
31fn derive_kit_field_key(
32 master_key: &[u8],
33 path: &'static str,
34 context: &'static str,
35) -> Result<[u8; 32], KitError> {
36 super::config::derive_field_key(master_key, path, KEY_DERIVATION_VERSION).map_err(|e| {
37 KitError::BuildFailed {
38 context,
39 source: Box::new(e),
40 }
41 })
42}
43
44pub struct Unbuilt;
46
47pub struct Ready;
49
50#[cfg(feature = "hot-reload")]
52type SubscriberMap = RefCell<HashMap<TypeId, Vec<Rc<dyn Fn()>>>>;
53
54#[cfg(feature = "encryption")]
56type EncryptedConfigMap = RefCell<HashMap<TypeId, EncryptedBlob>>;
57
58pub struct Kit<S = Unbuilt> {
60 builders: RefCell<HashMap<TypeId, BuildFn>>,
61 graph: DependencyGraph,
62 configs: TypeMap,
63 capabilities: TypeMap,
64 #[cfg(feature = "hot-reload")]
65 subscribers: SubscriberMap,
66 #[cfg(feature = "encryption")]
67 encrypted_configs: EncryptedConfigMap,
68 _state: std::marker::PhantomData<S>,
69}
70
71impl Kit {
72 #[must_use]
74 pub fn new() -> Self {
75 Kit {
76 builders: RefCell::new(HashMap::new()),
77 graph: DependencyGraph::new(),
78 configs: TypeMap::new(),
79 capabilities: TypeMap::new(),
80 #[cfg(feature = "hot-reload")]
81 subscribers: RefCell::new(HashMap::new()),
82 #[cfg(feature = "encryption")]
83 encrypted_configs: RefCell::new(HashMap::new()),
84 _state: std::marker::PhantomData,
85 }
86 }
87
88 pub fn register<M: AutoBuilder>(&mut self) -> Result<(), KitError> {
94 let entry = ModuleEntry {
95 type_id: TypeId::of::<M>(),
96 name: M::NAME,
97 dependencies: M::dependencies().iter().map(|(n, id)| (*n, *id)).collect(),
98 };
99
100 self.graph
101 .add(entry)
102 .map_err(|name| KitError::AlreadyRegistered { module: name })?;
103
104 let build_fn: BuildFn = Box::new(|kit| {
105 let capability = M::build(kit)
106 .map_err(|e| -> Box<dyn std::error::Error + Send + 'static> { Box::new(e) })?;
107 Ok(Box::new(capability) as Box<dyn Any>)
108 });
109
110 self.builders
111 .borrow_mut()
112 .insert(TypeId::of::<M>(), build_fn);
113 Ok(())
114 }
115
116 pub fn set_config<C: Clone + 'static>(&self, config: C) {
118 self.configs.insert(config);
119 }
120
121 #[cfg(feature = "confers")]
131 pub fn load_config<C: super::config::Configurable>(&self) -> Result<(), KitError> {
132 let config = C::load().map_err(|e| KitError::BuildFailed {
133 context: "load_config",
134 source: e,
135 })?;
136 self.set_config(config);
137 Ok(())
138 }
139
140 pub fn build(self) -> Result<Kit<Ready>, KitError> {
151 let sorted = match self.graph.validate() {
152 Ok(sorted) => sorted,
153 Err(GraphError::DependencyMissing { module, missing }) => {
154 return Err(KitError::DependencyMissing { module, missing });
155 }
156 Err(GraphError::CycleDetected { cycle }) => {
157 return Err(KitError::CycleDetected { cycle });
158 }
159 };
160
161 {
162 let kit_ref: &Self = &self;
163
164 for type_id in &sorted {
165 let build_fn = kit_ref.builders.borrow_mut().remove(type_id).ok_or(
166 KitError::MissingCapability {
167 key: kit_ref.module_name(*type_id),
168 },
169 )?;
170
171 let module_name = kit_ref.module_name(*type_id);
172
173 let result = (build_fn)(kit_ref);
174 match result {
175 Ok(boxed) => {
176 kit_ref.capabilities.insert_boxed(*type_id, boxed);
177 }
178 Err(e) => {
179 return Err(KitError::BuildFailed {
180 context: module_name,
181 source: e,
182 });
183 }
184 }
185 }
186 }
187
188 Ok(Kit {
189 builders: self.builders,
190 graph: self.graph,
191 configs: self.configs,
192 capabilities: self.capabilities,
193 #[cfg(feature = "hot-reload")]
194 subscribers: self.subscribers,
195 #[cfg(feature = "encryption")]
196 encrypted_configs: self.encrypted_configs,
197 _state: std::marker::PhantomData,
198 })
199 }
200
201 fn module_name(&self, type_id: TypeId) -> &'static str {
202 self.graph.name_of(type_id).unwrap_or("<unknown>")
203 }
204}
205
206impl<S> Kit<S> {
207 pub fn require<M: AutoBuilder>(&self) -> Result<M::Capability, KitError> {
216 let type_id = TypeId::of::<M>();
217 self.capabilities
218 .get_cloned_by_type_id::<M::Capability>(type_id)
219 .ok_or(KitError::MissingCapability { key: M::NAME })
220 }
221
222 pub fn config<C: Clone + 'static>(&self) -> Result<C, KitError> {
228 self.configs
229 .get_cloned::<C>()
230 .ok_or(KitError::MissingConfig {
231 key: std::any::type_name::<C>(),
232 })
233 }
234
235 #[cfg(feature = "hot-reload")]
244 pub fn subscribe<C: 'static>(&self, callback: impl Fn() + 'static) {
245 let callback: Rc<dyn Fn()> = Rc::new(callback);
246 self.subscribers
247 .borrow_mut()
248 .entry(TypeId::of::<C>())
249 .or_default()
250 .push(callback);
251 }
252
253 #[cfg(feature = "hot-reload")]
272 pub fn reload_config<C: super::config::Configurable>(&self) -> Result<(), KitError> {
273 let config = C::load().map_err(|e| KitError::BuildFailed {
274 context: "reload_config",
275 source: e,
276 })?;
277 self.configs.insert(config);
278 let callbacks: Vec<Rc<dyn Fn()>> = self
281 .subscribers
282 .borrow()
283 .get(&TypeId::of::<C>())
284 .cloned()
285 .unwrap_or_default();
286 for cb in &callbacks {
287 cb();
288 }
289 Ok(())
290 }
291}
292
293impl Kit {
294 #[cfg(feature = "encryption")]
310 pub fn set_encrypted<C>(&self, value: &C, master_key: &[u8]) -> Result<(), KitError>
311 where
312 C: super::config::ModuleConfig + serde::Serialize,
313 {
314 use super::config::XChaCha20Crypto;
315
316 let plaintext = serde_json::to_vec(value).map_err(|e| KitError::BuildFailed {
317 context: "set_encrypted",
318 source: Box::new(e),
319 })?;
320
321 let field_key = derive_kit_field_key(master_key, C::PATH, "set_encrypted")?;
322
323 let (nonce, ciphertext) = XChaCha20Crypto::new()
324 .encrypt(&plaintext, &field_key)
325 .map_err(|e| KitError::BuildFailed {
326 context: "set_encrypted",
327 source: Box::new(e),
328 })?;
329
330 self.encrypted_configs
331 .borrow_mut()
332 .insert(TypeId::of::<C>(), EncryptedBlob { nonce, ciphertext });
333 Ok(())
334 }
335
336 #[cfg(feature = "encryption")]
338 pub fn contains_encrypted<C: super::config::ModuleConfig>(&self) -> bool {
339 self.encrypted_configs
340 .borrow()
341 .contains_key(&TypeId::of::<C>())
342 }
343
344 #[cfg(feature = "confers-macros")]
356 pub fn load_config_or_default<C>(&self) -> Result<(), KitError>
357 where
358 C: super::config::Configurable + super::config::ModuleConfig,
359 {
360 let config = match C::load() {
361 Ok(value) => value,
362 Err(_) => C::default_value(),
363 };
364 self.set_config(config);
365 Ok(())
366 }
367}
368
369impl Kit<Ready> {
370 pub fn optional<M: AutoBuilder>(&self) -> Option<M::Capability> {
372 let type_id = TypeId::of::<M>();
373 self.capabilities
374 .get_cloned_by_type_id::<M::Capability>(type_id)
375 }
376
377 pub fn contains<M: AutoBuilder>(&self) -> bool {
379 self.capabilities.contains_by_type_id(TypeId::of::<M>())
380 }
381
382 pub fn contains_config<C: Clone + 'static>(&self) -> bool {
384 self.configs.contains::<C>()
385 }
386
387 #[cfg(feature = "encryption")]
400 pub fn get_encrypted<C>(&self, master_key: &[u8]) -> Result<C, KitError>
401 where
402 C: super::config::ModuleConfig + serde::de::DeserializeOwned,
403 {
404 use super::config::XChaCha20Crypto;
405
406 let blob = self
407 .encrypted_configs
408 .borrow()
409 .get(&TypeId::of::<C>())
410 .cloned()
411 .ok_or(KitError::MissingConfig {
412 key: std::any::type_name::<C>(),
413 })?;
414
415 let field_key = derive_kit_field_key(master_key, C::PATH, "get_encrypted")?;
416
417 let plaintext = XChaCha20Crypto::new()
418 .decrypt(&blob.nonce, &blob.ciphertext, &field_key)
419 .map_err(|e| KitError::BuildFailed {
420 context: "get_encrypted",
421 source: Box::new(e),
422 })?;
423
424 serde_json::from_slice(&plaintext).map_err(|e| KitError::BuildFailed {
425 context: "get_encrypted",
426 source: Box::new(e),
427 })
428 }
429}
430
431impl Default for Kit {
432 fn default() -> Self {
433 Self::new()
434 }
435}
436
437impl std::fmt::Debug for Kit {
438 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439 f.debug_struct("Kit<Unbuilt>")
440 .field("modules", &self.graph.entries().len())
441 .field("configs", &self.configs.len())
442 .finish()
443 }
444}
445
446impl std::fmt::Debug for Kit<Ready> {
447 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
448 f.debug_struct("Kit<Ready>")
449 .field("modules", &self.graph.entries().len())
450 .field("configs", &self.configs.len())
451 .finish()
452 }
453}