dill/
specs.rs

1use std::marker::PhantomData;
2use std::sync::Arc;
3
4use crate::{Catalog, InjectionError};
5
6////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
7// DependencySpec
8////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
10/// Specifies a particular way of resolving a dependency using the [`Catalog`]
11pub trait DependencySpec {
12    type ReturnType;
13    // Resolve and create instances
14    fn get(cat: &Catalog) -> Result<Self::ReturnType, InjectionError>;
15    // Only resolve builders without instantiating and report errors
16    fn check(cat: &Catalog) -> Result<(), InjectionError>;
17}
18
19////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
20// OneOf
21////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
22
23/// Builds a single instance of type implementing specific interface. Will
24/// return an error if no implementations or multiple implementations were
25/// found.
26pub struct OneOf<Iface>
27where
28    Iface: 'static + ?Sized + Send + Sync,
29{
30    _dummy: PhantomData<Iface>,
31}
32
33impl<Iface> DependencySpec for OneOf<Iface>
34where
35    Iface: 'static + ?Sized + Send + Sync,
36{
37    type ReturnType = Arc<Iface>;
38
39    default fn get(cat: &Catalog) -> Result<Self::ReturnType, InjectionError> {
40        let mut builders = cat.builders_for::<Iface>();
41        if let Some(first) = builders.next() {
42            if builders.next().is_some() {
43                Err(InjectionError::ambiguous::<Iface>())
44            } else {
45                first.get(cat)
46            }
47        } else {
48            Err(InjectionError::unregistered::<Iface>())
49        }
50    }
51
52    default fn check(cat: &Catalog) -> Result<(), InjectionError> {
53        let mut builders = cat.builders_for::<Iface>();
54        if builders.next().is_some() {
55            if builders.next().is_some() {
56                Err(InjectionError::ambiguous::<Iface>())
57            } else {
58                Ok(())
59            }
60        } else {
61            Err(InjectionError::unregistered::<Iface>())
62        }
63    }
64}
65
66impl DependencySpec for OneOf<Catalog> {
67    fn get(cat: &Catalog) -> Result<Self::ReturnType, InjectionError> {
68        // TODO: Avoid wrapping in Arc?
69        Ok(Arc::new(cat.clone()))
70    }
71
72    fn check(_: &Catalog) -> Result<(), InjectionError> {
73        Ok(())
74    }
75}
76
77////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
78// AllOf
79////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
80
81/// Builds all instances that implement a specific interface, returning a
82/// [`Vec`].
83pub struct AllOf<Iface>
84where
85    Iface: 'static + ?Sized,
86{
87    _dummy: PhantomData<Iface>,
88}
89
90impl<Iface> DependencySpec for AllOf<Iface>
91where
92    Iface: 'static + ?Sized,
93{
94    type ReturnType = Vec<Arc<Iface>>;
95
96    fn get(cat: &Catalog) -> Result<Self::ReturnType, InjectionError> {
97        cat.builders_for::<Iface>().map(|b| b.get(cat)).collect()
98    }
99
100    fn check(_cat: &Catalog) -> Result<(), InjectionError> {
101        Ok(())
102    }
103}
104
105////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
106// Maybe
107////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
108
109/// Returns `None` if an optional dependency is not registered
110pub struct Maybe<Inner: DependencySpec> {
111    _dummy: PhantomData<Inner>,
112}
113
114impl<Inner: DependencySpec> DependencySpec for Maybe<Inner> {
115    type ReturnType = Option<Inner::ReturnType>;
116
117    fn get(cat: &Catalog) -> Result<Self::ReturnType, InjectionError> {
118        match Inner::get(cat) {
119            Ok(v) => Ok(Some(v)),
120            Err(InjectionError::Unregistered(_)) => Ok(None),
121            Err(err) => Err(err),
122        }
123    }
124
125    fn check(cat: &Catalog) -> Result<(), InjectionError> {
126        match Inner::check(cat) {
127            Ok(()) => Ok(()),
128            Err(InjectionError::Unregistered(_)) => Ok(()),
129            Err(err) => Err(err),
130        }
131    }
132}
133
134////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
135// Lazy
136////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
137
138/// Delays the instantiation of a component until explicitly requested.
139///
140/// See [`crate::lazy::Lazy`] documentation for details.
141pub struct Lazy<Inner: DependencySpec> {
142    _dummy: PhantomData<Inner>,
143}
144
145impl<Inner: DependencySpec> DependencySpec for Lazy<Inner> {
146    type ReturnType = crate::lazy::Lazy<Inner::ReturnType>;
147
148    #[cfg(not(feature = "tokio"))]
149    fn get(cat: &Catalog) -> Result<Self::ReturnType, InjectionError> {
150        let cat = cat.clone();
151        Ok(crate::lazy::Lazy::new(move || Inner::get(&cat)))
152    }
153
154    #[cfg(feature = "tokio")]
155    fn get(cat: &Catalog) -> Result<Self::ReturnType, InjectionError> {
156        // Lazy<T> will store the clone of a catalog it was initially created with
157        // It will however first attempt to resolve a current catalog if scope feature
158        // is used and only use the former as a fallback.
159        let fallback_cat = cat.clone();
160        Ok(crate::lazy::Lazy::new(move || match crate::CURRENT_CATALOG
161            .try_with(|cat| Inner::get(cat))
162        {
163            Ok(v) => v,
164            Err(_) => Inner::get(&fallback_cat),
165        }))
166    }
167
168    fn check(cat: &Catalog) -> Result<(), InjectionError> {
169        Inner::check(cat)
170    }
171}