Skip to main content

diode/
inject.rs

1use std::ops::{Deref, DerefMut};
2
3use crate::{
4    AppContext, AppError, ComponentMut, ComponentRef, Dependencies, Service, ServiceDependencyExt,
5};
6
7/// Trait for extracting owned values from the application context.
8pub trait Extract<T> {
9    fn extract(ctx: &AppContext) -> Result<T, AppError>;
10
11    fn dependencies() -> Dependencies {
12        Dependencies::new()
13    }
14}
15
16/// Trait for extracting borrowed references from the application context.
17///
18/// The associated type `Ref` allows each implementation to choose its
19/// return type — `ComponentRef<T>` for stored components, `&T` for
20/// values available directly (e.g. `AppContext` itself).
21pub trait ExtractRef<T> {
22    type Ref<'a>: Deref<Target = T> + 'a;
23
24    fn extract_ref(ctx: &AppContext) -> Result<Self::Ref<'_>, AppError>;
25
26    fn dependencies() -> Dependencies {
27        Dependencies::new()
28    }
29}
30
31/// Trait for extracting mutable references from the application context.
32///
33/// Similar to [`ExtractRef`], but provides `DerefMut` access to the component.
34///
35/// # Deadlock warning
36///
37/// The returned guard holds a write lock on the underlying storage shard.
38/// Combining a `&mut` inject parameter with other `&` or `&mut` inject
39/// parameters in the same `#[factory]` method may deadlock if the components
40/// are stored in the same shard. The `#[service]` macro rejects such
41/// combinations at compile time.
42///
43/// If you need multiple references where at least one is mutable, inject
44/// `&AppContext` and manage guards manually:
45///
46/// ```rust,ignore
47/// #[factory]
48/// fn new(#[inject(AppContext)] ctx: &AppContext) -> Arc<Self> {
49///     {
50///         let mut registry = ctx.get_component_mut::<Registry>().unwrap();
51///         registry.register("my_service");
52///     } // guard dropped before next acquisition
53///     let config = ctx.get_component_ref::<Config>().unwrap();
54///     Arc::new(Self { /* ... */ })
55/// }
56/// ```
57pub trait ExtractMut<T>: ExtractRef<T> {
58    type RefMut<'a>: DerefMut<Target = T> + 'a;
59
60    fn extract_mut(ctx: &AppContext) -> Result<Self::RefMut<'_>, AppError>;
61}
62
63impl<T, S> Extract<T> for S
64where
65    T: Clone + Send + Sync + 'static,
66    S: Service<Handle = T> + 'static,
67{
68    fn extract(ctx: &AppContext) -> Result<T, AppError> {
69        ctx.get_component::<T>().ok_or(AppError::MissingComponent(std::any::type_name::<T>()))
70    }
71
72    fn dependencies() -> Dependencies {
73        Dependencies::new().service::<S>()
74    }
75}
76
77impl<T, S> ExtractRef<T> for S
78where
79    T: Send + Sync + 'static,
80    S: Service<Handle = T> + 'static,
81{
82    type Ref<'a> = ComponentRef<'a, T>;
83
84    fn extract_ref(ctx: &AppContext) -> Result<Self::Ref<'_>, AppError> {
85        ctx.get_component_ref::<T>().ok_or(AppError::MissingComponent(std::any::type_name::<T>()))
86    }
87
88    fn dependencies() -> Dependencies {
89        Dependencies::new().service::<S>()
90    }
91}
92
93impl ExtractRef<AppContext> for AppContext {
94    type Ref<'a> = &'a AppContext;
95
96    fn extract_ref(ctx: &AppContext) -> Result<&AppContext, AppError> {
97        Ok(ctx)
98    }
99}
100
101/// Generic extractor for any component stored in the application.
102pub struct Component;
103
104impl<T> Extract<T> for Component
105where
106    T: Clone + Send + Sync + 'static,
107{
108    fn extract(ctx: &AppContext) -> Result<T, AppError> {
109        ctx.get_component::<T>().ok_or(AppError::MissingComponent(std::any::type_name::<T>()))
110    }
111}
112
113impl<T> ExtractRef<T> for Component
114where
115    T: Send + Sync + 'static,
116{
117    type Ref<'a> = ComponentRef<'a, T>;
118
119    fn extract_ref(ctx: &AppContext) -> Result<Self::Ref<'_>, AppError> {
120        ctx.get_component_ref::<T>()
121            .ok_or(AppError::MissingComponent(std::any::type_name::<T>()))
122    }
123}
124
125impl<T> ExtractMut<T> for Component
126where
127    T: Send + Sync + 'static,
128{
129    type RefMut<'a> = ComponentMut<'a, T>;
130
131    fn extract_mut(ctx: &AppContext) -> Result<Self::RefMut<'_>, AppError> {
132        ctx.get_component_mut::<T>()
133            .ok_or(AppError::MissingComponent(std::any::type_name::<T>()))
134    }
135}