ruice/
construct.rs

1use std::marker::PhantomData;
2use std::sync::Arc;
3
4use crate::{Resolve, ServiceContainer, Services};
5
6pub trait Construct<S = Self, C = ServiceContainer>: Send + Sync {
7    fn construct(container: &C) -> Option<S>;
8}
9
10pub struct Constructor<S> {
11    _phantom: PhantomData<fn() -> S>,
12}
13
14impl<S> Constructor<S> {
15    pub fn new() -> Self {
16        Self {
17            _phantom: PhantomData,
18        }
19    }
20}
21
22impl<S> Default for Constructor<S> {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl<S, C> Resolve<S, C> for Constructor<S>
29where
30    S: Construct<S, C>,
31{
32    fn resolve(&self, container: &C) -> Option<Arc<S>> {
33        Some(Arc::new(S::construct(container)?))
34    }
35}
36
37pub trait ConstructServices: Services {
38    fn construct<S>(&mut self)
39    where
40        S: Construct<S, Self> + 'static,
41    {
42        self.put(Constructor::<S>::new());
43    }
44}
45
46impl<C> ConstructServices for C where C: Services {}
47
48#[cfg(test)]
49mod tests {
50    use std::sync::Arc;
51
52    use crate::construct::{Construct, ConstructServices};
53    use crate::singleton::SingletonServices;
54    use crate::{ServiceContainer, Services};
55
56    struct Foo {
57        name: String,
58    }
59
60    struct Bar {
61        foo: Arc<Foo>,
62    }
63
64    impl Bar {
65        fn greet(&self) -> String {
66            format!("Hello, {}!", self.foo.name)
67        }
68    }
69
70    impl<C> Construct<Self, C> for Bar
71    where
72        C: Services,
73    {
74        fn construct(container: &C) -> Option<Self> {
75            Some(Self {
76                foo: container.get()?,
77            })
78        }
79    }
80
81    #[test]
82    fn construct() {
83        let mut container = ServiceContainer::default();
84
85        // Bar is constructed from services in the container.
86        // The instance will be initiated lazily, so we can set this earlier than Foo.
87        container.construct::<Bar>();
88
89        // Foo is shared as a singleton service.
90        container.singleton(Foo {
91            name: "Taro".to_string(),
92        });
93
94        let bar = container.get::<Bar>().unwrap();
95        assert_eq!("Hello, Taro!".to_string(), bar.greet());
96    }
97}