telar_services_core/
lib.rs1pub 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}