Skip to main content

telar_services_core/
lib.rs

1pub mod paths;
2#[cfg(feature = "di")]
3mod registry;
4#[cfg(feature = "di")]
5mod scope;
6
7pub use paths::AppPathsProvider;
8#[cfg(feature = "di")]
9pub use registry::ServiceError;
10#[cfg(feature = "di")]
11pub use scope::{Scope, ServiceContext, ServiceGuard, provide, try_inject, with_service};
12
13#[cfg(all(test, feature = "di"))]
14mod tests {
15    use std::cell::RefCell;
16    use std::rc::Rc;
17
18    use super::*;
19    use crate::registry::ServiceRegistry;
20
21    #[test]
22    fn registry_insert_get() {
23        let mut reg = ServiceRegistry::new();
24        reg.insert(42u32).unwrap();
25        assert_eq!(reg.get::<u32>(), Some(&42));
26    }
27
28    #[test]
29    fn registry_insert_duplicate_returns_error() {
30        let mut reg = ServiceRegistry::new();
31        assert!(reg.insert(1u32).is_ok());
32        assert_eq!(reg.insert(2u32), Err(ServiceError::AlreadyRegistered));
33        assert_eq!(reg.get::<u32>(), Some(&1));
34    }
35
36    #[test]
37    fn registry_missing_type() {
38        let reg = ServiceRegistry::new();
39        assert_eq!(reg.get::<u32>(), None);
40    }
41
42    #[test]
43    fn scope_provide_inject() {
44        Scope::with(|| {
45            provide(99u32).unwrap();
46            assert_eq!(try_inject::<u32>(), Some(99));
47        });
48    }
49
50    #[test]
51    fn scope_child_inherits_parent() {
52        Scope::with(|| {
53            provide(String::from("from-parent")).unwrap();
54            Scope::with(|| {
55                assert_eq!(try_inject::<String>(), Some(String::from("from-parent")));
56            });
57        });
58    }
59
60    #[test]
61    fn scope_child_shadows_parent() {
62        Scope::with(|| {
63            provide(1u32).unwrap();
64            Scope::with(|| {
65                provide(2u32).unwrap();
66                assert_eq!(try_inject::<u32>(), Some(2));
67            });
68        });
69    }
70
71    #[test]
72    fn scope_drop_restores_parent() {
73        Scope::with(|| {
74            provide(String::from("parent")).unwrap();
75            Scope::with(|| {
76                provide(String::from("child")).unwrap();
77                assert_eq!(try_inject::<String>().as_deref(), Some("child"));
78            });
79            assert_eq!(try_inject::<String>().as_deref(), Some("parent"));
80        });
81    }
82
83    #[test]
84    fn with_service_non_clone() {
85        Scope::with(|| {
86            provide(Rc::new(RefCell::new(vec![1, 2, 3]))).unwrap();
87            let len = with_service(|v: &Rc<RefCell<Vec<i32>>>| v.borrow().len());
88            assert_eq!(len, Some(3));
89        });
90    }
91
92    #[test]
93    fn try_inject_missing_returns_none() {
94        Scope::with(|| {
95            assert_eq!(try_inject::<u64>(), None);
96        });
97    }
98
99    #[test]
100    fn scope_with_provides_service() {
101        Scope::with(|| {
102            provide(42u32).unwrap();
103            assert_eq!(try_inject::<u32>(), Some(42));
104        });
105        assert_eq!(try_inject::<u32>(), None);
106    }
107
108    #[test]
109    fn scope_with_cleans_up_after_closure_returns() {
110        Scope::with(|| {
111            provide(String::from("ephemeral")).unwrap();
112            assert!(try_inject::<String>().is_some());
113        });
114        assert_eq!(
115            try_inject::<String>(),
116            None,
117            "service must not be visible after Scope::with returns"
118        );
119    }
120
121    #[test]
122    fn scope_with_nested() {
123        Scope::with(|| {
124            provide(String::from("outer")).unwrap();
125            Scope::with(|| {
126                provide(String::from("inner")).unwrap();
127                assert_eq!(try_inject::<String>().as_deref(), Some("inner"));
128            });
129            assert_eq!(try_inject::<String>().as_deref(), Some("outer"));
130        });
131    }
132}