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