windjammer_runtime/
fixtures.rs1use std::any::{Any, TypeId};
6use std::collections::HashMap;
7use std::sync::{Arc, Mutex};
8
9type FixtureFactory = Box<dyn Fn() -> Box<dyn Any + Send> + Send + Sync>;
10
11static FIXTURE_REGISTRY: Mutex<Option<FixtureRegistry>> = Mutex::new(None);
13
14pub struct FixtureRegistry {
16 factories: HashMap<String, FixtureFactory>,
17 type_ids: HashMap<String, TypeId>,
18}
19
20impl FixtureRegistry {
21 pub fn new() -> Self {
22 Self {
23 factories: HashMap::new(),
24 type_ids: HashMap::new(),
25 }
26 }
27
28 pub fn register<T: 'static + Send>(
30 &mut self,
31 name: &str,
32 factory: impl Fn() -> T + 'static + Send + Sync,
33 ) {
34 self.factories
35 .insert(name.to_string(), Box::new(move || Box::new(factory())));
36 self.type_ids.insert(name.to_string(), TypeId::of::<T>());
37 }
38
39 pub fn get<T: 'static>(&self, name: &str) -> Option<T> {
41 let type_id = self.type_ids.get(name)?;
42 if *type_id != TypeId::of::<T>() {
43 return None;
44 }
45
46 let factory = self.factories.get(name)?;
47 let boxed = factory();
48 boxed.downcast::<T>().ok().map(|b| *b)
49 }
50}
51
52impl Default for FixtureRegistry {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58pub fn global_registry() -> Arc<Mutex<FixtureRegistry>> {
60 let mut guard = FIXTURE_REGISTRY.lock().unwrap();
61 if guard.is_none() {
62 *guard = Some(FixtureRegistry::new());
63 }
64 drop(guard);
65
66 Arc::new(Mutex::new(FixtureRegistry::new()))
68}
69
70pub fn register_fixture<T: 'static + Send>(
72 name: &str,
73 factory: impl Fn() -> T + 'static + Send + Sync,
74) {
75 let registry = global_registry();
76 let mut guard = registry.lock().unwrap();
77 guard.register(name, factory);
78}
79
80pub fn use_fixture<T: 'static>(name: &str) -> Option<T> {
82 let registry = global_registry();
83 let guard = registry.lock().unwrap();
84 guard.get(name)
85}
86
87pub struct FixtureScope<T> {
89 value: Option<T>,
90}
91
92impl<T> FixtureScope<T> {
93 pub fn new(value: T) -> Self {
94 Self { value: Some(value) }
95 }
96
97 pub fn get(&self) -> &T {
98 self.value.as_ref().unwrap()
99 }
100
101 pub fn get_mut(&mut self) -> &mut T {
102 self.value.as_mut().unwrap()
103 }
104}
105
106impl<T> Drop for FixtureScope<T> {
107 fn drop(&mut self) {
108 self.value.take();
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[derive(Debug, Clone, PartialEq)]
118 struct TestData {
119 value: i32,
120 }
121
122 #[test]
123 fn test_fixture_registry() {
124 let mut registry = FixtureRegistry::new();
125
126 registry.register("test_data", || TestData { value: 42 });
127
128 let data: Option<TestData> = registry.get("test_data");
129 assert!(data.is_some());
130 assert_eq!(data.unwrap().value, 42);
131 }
132
133 #[test]
134 fn test_fixture_wrong_type() {
135 let mut registry = FixtureRegistry::new();
136
137 registry.register("test_data", || TestData { value: 42 });
138
139 let data: Option<String> = registry.get("test_data");
141 assert!(data.is_none());
142 }
143
144 #[test]
145 fn test_fixture_not_found() {
146 let registry = FixtureRegistry::new();
147
148 let data: Option<TestData> = registry.get("nonexistent");
149 assert!(data.is_none());
150 }
151
152 #[test]
153 fn test_fixture_scope() {
154 let scope = FixtureScope::new(TestData { value: 42 });
155 assert_eq!(scope.get().value, 42);
156
157 }
159
160 #[test]
161 fn test_fixture_scope_mut() {
162 let mut scope = FixtureScope::new(TestData { value: 42 });
163 scope.get_mut().value = 100;
164 assert_eq!(scope.get().value, 100);
165 }
166}