1#![cfg_attr(not(feature = "global"), allow(unused_imports, unused_variables))]
2
3use crate::{Database, DatabaseRc, WeakDatabaseRc};
4use std::sync::Mutex;
5
6#[cfg(feature = "global")]
7lazy_static::lazy_static! {
8 static ref DATABASE: Mutex<Option<DatabaseRc>> = Mutex::new(None);
9 static ref WITH_LOCK: Mutex<()> = Mutex::new(());
10}
11
12pub fn with_db_from_rc<F: FnMut() -> R, R>(database: DatabaseRc, mut f: F) -> R {
16 #[cfg(feature = "global")]
17 let _lock = WITH_LOCK.lock().unwrap();
18
19 set_db_from_rc(database);
20 let result = f();
21 destroy_db();
22 result
23}
24
25#[inline]
29pub fn with_db_from_box<F: FnMut() -> R, R>(database: Box<dyn Database>, f: F) -> R {
30 with_db_from_rc(DatabaseRc::new(database), f)
31}
32
33#[inline]
37pub fn with_db<D: Database + 'static, F: FnMut() -> R, R>(database: D, f: F) -> R {
38 with_db_from_box(Box::new(database), f)
39}
40
41#[inline]
44pub fn db() -> WeakDatabaseRc {
45 #[cfg(feature = "global")]
46 let x = match DATABASE.lock().unwrap().as_ref() {
47 Some(x) => DatabaseRc::downgrade(x),
48 None => WeakDatabaseRc::new(),
49 };
50
51 #[cfg(not(feature = "global"))]
52 let x = WeakDatabaseRc::new();
53
54 x
55}
56
57#[inline]
59pub fn set_db<D: Database + 'static>(database: D) -> WeakDatabaseRc {
60 set_db_from_box(Box::new(database))
61}
62
63#[inline]
65pub fn set_db_from_box(database: Box<dyn Database>) -> WeakDatabaseRc {
66 set_db_from_rc(DatabaseRc::new(database))
67}
68
69#[inline]
72pub fn set_db_from_rc(database_rc: DatabaseRc) -> WeakDatabaseRc {
73 #[cfg(feature = "global")]
74 DATABASE.lock().unwrap().replace(database_rc);
75 db()
76}
77
78#[inline]
80pub fn has_db() -> bool {
81 #[cfg(feature = "global")]
82 let x = DATABASE.lock().unwrap().is_some();
83
84 #[cfg(not(feature = "global"))]
85 let x = false;
86
87 x
88}
89
90#[inline]
92pub fn destroy_db() {
93 #[cfg(feature = "global")]
94 DATABASE.lock().unwrap().take();
95}
96
97#[cfg(all(test, feature = "global"))]
98mod tests {
99 use super::*;
100 use crate::{DatabaseResult, Ent, Id, Query};
101
102 fn reset_db_state() {
104 DATABASE.lock().unwrap().take();
105 }
106
107 #[test]
113 fn test_runner() {
114 fn db_should_return_empty_weak_ref_if_database_not_set() {
115 reset_db_state();
116
117 assert!(
118 WeakDatabaseRc::ptr_eq(&db(), &WeakDatabaseRc::new()),
119 "Returned weak reference unexpectedly pointing to database"
120 );
121 }
122 db_should_return_empty_weak_ref_if_database_not_set();
123
124 fn db_should_return_weak_ref_for_active_database_if_set() {
125 reset_db_state();
126
127 set_db(TestDatabase);
128 assert!(
129 !WeakDatabaseRc::ptr_eq(&db(), &WeakDatabaseRc::new()),
130 "Returned weak reference not pointing to database"
131 );
132 }
133 db_should_return_weak_ref_for_active_database_if_set();
134
135 fn set_db_should_update_the_global_database_with_the_given_instance() {
136 reset_db_state();
137
138 assert!(
139 !WeakDatabaseRc::ptr_eq(&set_db(TestDatabase), &WeakDatabaseRc::new()),
140 "Returned weak reference not pointing to database"
141 );
142
143 assert!(DATABASE.lock().unwrap().is_some());
144 }
145 set_db_should_update_the_global_database_with_the_given_instance();
146
147 fn set_db_from_box_should_update_the_global_database_with_the_given_instance() {
148 reset_db_state();
149
150 assert!(
151 !WeakDatabaseRc::ptr_eq(
152 &set_db_from_box(Box::new(TestDatabase)),
153 &WeakDatabaseRc::new()
154 ),
155 "Returned weak reference not pointing to database"
156 );
157
158 assert!(DATABASE.lock().unwrap().is_some());
159 }
160 set_db_from_box_should_update_the_global_database_with_the_given_instance();
161
162 fn set_db_from_rc_should_update_the_global_database_with_the_given_instance() {
163 reset_db_state();
164
165 assert!(
166 !WeakDatabaseRc::ptr_eq(
167 &set_db_from_rc(DatabaseRc::new(Box::new(TestDatabase))),
168 &WeakDatabaseRc::new()
169 ),
170 "Returned weak reference not pointing to database"
171 );
172
173 assert!(DATABASE.lock().unwrap().is_some());
174 }
175 set_db_from_rc_should_update_the_global_database_with_the_given_instance();
176
177 fn has_db_should_return_false_if_database_not_set() {
178 reset_db_state();
179
180 assert!(!has_db(), "Unexpectedly reported having database");
181 }
182 has_db_should_return_false_if_database_not_set();
183
184 fn has_db_should_return_false_if_database_destroyed() {
185 reset_db_state();
186
187 DATABASE
188 .lock()
189 .unwrap()
190 .replace(DatabaseRc::new(Box::new(TestDatabase)));
191 destroy_db();
192
193 assert!(!has_db(), "Unexpectedly reported having database");
194 }
195 has_db_should_return_false_if_database_destroyed();
196
197 fn has_db_should_return_true_if_database_set() {
198 reset_db_state();
199
200 set_db(TestDatabase);
201 assert!(has_db(), "Unexpectedly reported NOT having database");
202 }
203 has_db_should_return_true_if_database_set();
204
205 fn destroy_db_should_remove_global_database_if_set() {
206 reset_db_state();
207
208 DATABASE
209 .lock()
210 .unwrap()
211 .replace(DatabaseRc::new(Box::new(TestDatabase)));
212
213 destroy_db();
214 assert!(
215 DATABASE.lock().unwrap().is_none(),
216 "Database was not destroyed"
217 );
218 }
219 destroy_db_should_remove_global_database_if_set();
220
221 fn destroy_db_should_do_nothing_if_global_database_is_not_set() {
222 reset_db_state();
223
224 destroy_db();
225 assert!(
226 DATABASE.lock().unwrap().is_none(),
227 "Database was not destroyed"
228 );
229 }
230 destroy_db_should_do_nothing_if_global_database_is_not_set();
231 }
232
233 struct TestDatabase;
236
237 impl Database for TestDatabase {
238 fn get(&self, _id: Id) -> DatabaseResult<Option<Box<dyn Ent>>> {
239 unimplemented!()
240 }
241
242 fn remove(&self, _id: Id) -> DatabaseResult<bool> {
243 unimplemented!()
244 }
245
246 fn insert(&self, _ent: Box<dyn Ent>) -> DatabaseResult<Id> {
247 unimplemented!()
248 }
249
250 fn get_all(&self, _ids: Vec<Id>) -> DatabaseResult<Vec<Box<dyn Ent>>> {
251 unimplemented!()
252 }
253
254 fn find_all(&self, _query: Query) -> DatabaseResult<Vec<Box<dyn Ent>>> {
255 unimplemented!()
256 }
257 }
258}