1#![allow(clippy::should_implement_trait, clippy::new_ret_no_self)]
3use std::any::{Any, TypeId};
4use std::cell::{RefCell, UnsafeCell};
5use std::sync::{Arc, atomic::AtomicUsize, atomic::Ordering};
6use std::{fmt, hash::Hash, hash::Hasher, marker::PhantomData, mem, ops, ptr, rc};
7
8type Key = (usize, TypeId);
9type HashMap<K, V> = std::collections::HashMap<K, V, foldhash::fast::RandomState>;
10
11thread_local! {
12 static DEFAULT_CFG: Arc<Storage> = {
13 let mut st = Arc::new(Storage::new("--", "", false, CfgContext(ptr::null())));
14 let p = Arc::as_ptr(&st);
15 Arc::get_mut(&mut st).unwrap().ctx.update(p);
16 st
17 };
18 static MAPPING: RefCell<HashMap<Key, Arc<dyn Any + Send + Sync>>> = {
19 RefCell::new(HashMap::default())
20 };
21}
22static IDX: AtomicUsize = AtomicUsize::new(0);
23const KIND_ARC: usize = 1;
24const KIND_UNMASK: usize = !KIND_ARC;
25
26pub trait Configuration: Default + Send + Sync + fmt::Debug + 'static {
28 const NAME: &'static str;
30
31 fn ctx(&self) -> &CfgContext;
33
34 fn set_ctx(&mut self, ctx: CfgContext);
36}
37
38#[derive(Debug)]
39struct Storage {
40 id: usize,
41 tag: &'static str,
42 service: &'static str,
43 ctx: CfgContext,
44 building: bool,
45 data: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
46}
47
48impl Storage {
49 fn new(tag: &'static str, service: &'static str, building: bool, ctx: CfgContext) -> Self {
50 let id = IDX.fetch_add(1, Ordering::SeqCst);
51 Storage {
52 id,
53 ctx,
54 tag,
55 service,
56 building,
57 data: HashMap::default(),
58 }
59 }
60}
61
62#[derive(Debug)]
63pub struct CfgContext(*const Storage);
65
66unsafe impl Send for CfgContext {}
67unsafe impl Sync for CfgContext {}
68
69impl CfgContext {
70 fn update(&mut self, new_p: *const Storage) {
71 self.0 = new_p;
72 }
73
74 pub fn id(&self) -> usize {
76 self.get_ref().id
77 }
78
79 #[inline]
80 pub fn tag(&self) -> &'static str {
82 self.get_ref().tag
83 }
84
85 pub fn service(&self) -> &'static str {
87 self.get_ref().service
88 }
89
90 pub fn get<T>(&self) -> Cfg<T>
92 where
93 T: Configuration,
94 {
95 let inner: Arc<Storage> = unsafe { Arc::from_raw(self.0) };
96 let cfg = get(&inner);
97 mem::forget(inner);
98 cfg
99 }
100
101 pub fn shared(&self) -> SharedCfg {
103 let inner: Arc<Storage> = unsafe { Arc::from_raw(self.0) };
104 let shared = SharedCfg(inner.clone());
105 mem::forget(inner);
106 shared
107 }
108
109 fn get_ref(&self) -> &Storage {
110 unsafe { self.0.as_ref().unwrap() }
111 }
112}
113
114impl Default for CfgContext {
115 #[inline]
116 fn default() -> Self {
117 CfgContext(DEFAULT_CFG.with(Arc::as_ptr))
118 }
119}
120
121#[derive(Debug)]
122pub struct Cfg<T: Configuration>(UnsafeCell<*const T>, PhantomData<rc::Rc<T>>);
124
125impl<T: Configuration> Cfg<T> {
126 fn new(ptr: *const T) -> Self {
127 Self(UnsafeCell::new(ptr), PhantomData)
128 }
129
130 #[inline]
131 pub fn id(&self) -> usize {
133 self.get_ref().ctx().id()
134 }
135
136 #[inline]
137 pub fn tag(&self) -> &'static str {
139 self.get_ref().ctx().tag()
140 }
141
142 pub fn service(&self) -> &'static str {
144 self.get_ref().ctx().service()
145 }
146
147 pub fn shared(&self) -> SharedCfg {
149 self.get_ref().ctx().shared()
150 }
151
152 pub fn get_shared<U>(&self) -> Cfg<U>
154 where
155 U: Configuration,
156 {
157 self.get_ref().ctx().get::<U>()
158 }
159
160 fn get_ref(&self) -> &T {
161 unsafe {
162 (*self.0.get())
163 .map_addr(|addr| addr & KIND_UNMASK)
164 .as_ref()
165 .unwrap()
166 }
167 }
168
169 #[allow(clippy::needless_pass_by_value)]
170 pub unsafe fn replace(&self, cfg: Cfg<T>) {
177 unsafe {
178 ptr::swap(self.0.get(), cfg.0.get());
179 }
180 }
181}
182
183impl<T: Configuration> Drop for Cfg<T> {
184 fn drop(&mut self) {
185 unsafe {
186 let addr = (*self.0.get()).map_addr(|addr| addr & KIND_UNMASK);
187 Arc::decrement_strong_count(addr.as_ref().unwrap().ctx().0);
188
189 if ((*self.0.get()).addr() & KIND_ARC) != 0 {
190 Arc::from_raw(addr);
191 }
192 }
193 }
194}
195
196impl<T: Configuration> Clone for Cfg<T> {
197 #[inline]
198 fn clone(&self) -> Self {
199 self.ctx().get()
200 }
201}
202
203impl<T: Configuration> From<T> for Cfg<T> {
204 #[inline]
205 fn from(cfg: T) -> Self {
206 SharedCfg::new("--").add(cfg).build().get()
207 }
208}
209
210impl<'a, T: Configuration> From<&'a T> for Cfg<T> {
211 #[inline]
212 fn from(cfg: &'a T) -> Self {
213 cfg.ctx().get()
214 }
215}
216
217impl<T: Configuration> ops::Deref for Cfg<T> {
218 type Target = T;
219
220 #[inline]
221 fn deref(&self) -> &T {
222 self.get_ref()
223 }
224}
225
226impl<T: Configuration> Default for Cfg<T> {
227 #[inline]
228 fn default() -> Self {
229 SharedCfg::default().get()
230 }
231}
232
233#[derive(Clone, Debug)]
234pub struct SharedCfg(Arc<Storage>);
236
237#[derive(Debug)]
238pub struct SharedCfgBuilder {
240 ctx: CfgContext,
241 storage: Arc<Storage>,
242}
243
244impl Eq for SharedCfg {}
245
246impl PartialEq for SharedCfg {
247 fn eq(&self, other: &Self) -> bool {
248 ptr::from_ref(self.0.as_ref()) == ptr::from_ref(other.0.as_ref())
249 }
250}
251
252impl Hash for SharedCfg {
253 fn hash<H: Hasher>(&self, state: &mut H) {
254 self.0.id.hash(state);
255 }
256}
257
258impl SharedCfg {
259 pub fn new(tag: &'static str) -> SharedCfgBuilder {
261 SharedCfgBuilder::new(tag)
262 }
263
264 #[inline]
265 pub fn id(&self) -> usize {
267 self.0.id
268 }
269
270 #[inline]
271 pub fn tag(&self) -> &'static str {
273 self.0.tag
274 }
275
276 pub fn service(&self) -> &'static str {
278 self.0.service
279 }
280
281 pub fn get<T>(&self) -> Cfg<T>
287 where
288 T: Configuration,
289 {
290 get(&self.0)
291 }
292}
293
294impl Default for SharedCfg {
295 #[inline]
296 fn default() -> Self {
297 Self(DEFAULT_CFG.with(Clone::clone))
298 }
299}
300
301impl<'a> From<&'a SharedCfg> for SharedCfg {
302 fn from(cfg: &'a SharedCfg) -> SharedCfg {
303 cfg.clone()
304 }
305}
306
307impl<T: Configuration> From<SharedCfg> for Cfg<T> {
308 #[inline]
309 fn from(cfg: SharedCfg) -> Self {
310 cfg.get()
311 }
312}
313
314impl<'a, T: Configuration> From<&'a SharedCfg> for Cfg<T> {
315 #[inline]
316 fn from(cfg: &'a SharedCfg) -> Self {
317 cfg.get()
318 }
319}
320
321impl<T: Configuration> From<SharedCfgBuilder> for Cfg<T> {
322 #[inline]
323 fn from(cfg: SharedCfgBuilder) -> Self {
324 cfg.build().get()
325 }
326}
327
328impl<T: Configuration> From<T> for SharedCfg {
329 #[inline]
330 fn from(cfg: T) -> Self {
331 SharedCfg::new("--").add(cfg).build()
332 }
333}
334
335impl<T: Configuration> From<Cfg<T>> for SharedCfg {
336 #[inline]
337 fn from(cfg: Cfg<T>) -> Self {
338 cfg.shared()
339 }
340}
341
342impl SharedCfgBuilder {
343 fn new(tag: &'static str) -> SharedCfgBuilder {
344 let mut storage = Arc::new(Storage::new(tag, tag, true, CfgContext::default()));
345 let ctx = CfgContext(Arc::as_ptr(&storage));
346 Arc::get_mut(&mut storage).unwrap().ctx.update(ctx.0);
347
348 SharedCfgBuilder { ctx, storage }
349 }
350
351 #[must_use]
352 pub fn service(mut self, name: &'static str) -> Self {
354 Arc::get_mut(&mut self.storage).unwrap().service = name;
355 self
356 }
357
358 #[must_use]
359 pub fn add<T: Configuration>(mut self, mut val: T) -> Self {
363 val.set_ctx(CfgContext(self.ctx.0));
364 Arc::get_mut(&mut self.storage)
365 .unwrap()
366 .data
367 .insert(TypeId::of::<T>(), Box::new(val));
368 self
369 }
370
371 #[must_use]
372 pub fn build(self) -> SharedCfg {
374 self.into()
375 }
376}
377
378impl From<SharedCfgBuilder> for SharedCfg {
379 fn from(mut cfg: SharedCfgBuilder) -> SharedCfg {
380 let st = Arc::get_mut(&mut cfg.storage).unwrap();
381 st.building = false;
382 SharedCfg(cfg.storage)
383 }
384}
385
386fn get<T>(st: &Arc<Storage>) -> Cfg<T>
387where
388 T: Configuration,
389{
390 assert!(
391 !st.building,
392 "{}: Cannot access shared config while building",
393 st.tag
394 );
395
396 mem::forget(st.clone());
399
400 let tp = TypeId::of::<T>();
401 if let Some(arc) = st.data.get(&tp) {
402 Cfg::new(arc.as_ref().downcast_ref::<T>().unwrap())
403 } else {
404 MAPPING.with(|store| {
405 let key = (st.id, tp);
406 if let Some(arc) = store.borrow().get(&key) {
407 Cfg::new(
408 Arc::into_raw(arc.clone())
409 .cast::<T>()
410 .map_addr(|addr| addr ^ KIND_ARC),
411 )
412 } else {
413 log::info!(
414 "{}: Configuration {:?} does not exist, using default",
415 st.tag,
416 T::NAME
417 );
418 let mut val = T::default();
419 val.set_ctx(CfgContext(st.ctx.0));
420 let arc = Arc::new(val);
421 store.borrow_mut().insert(key, arc.clone());
422 Cfg::new(
423 Arc::into_raw(arc)
424 .cast::<T>()
425 .map_addr(|addr| addr ^ KIND_ARC),
426 )
427 }
428 })
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 #[test]
437 #[allow(clippy::should_panic_without_expect)]
438 #[should_panic]
439 fn access_cfg_in_building_state() {
440 #[derive(Debug)]
441 struct TestCfg {
442 config: CfgContext,
443 }
444 impl TestCfg {
445 fn new() -> Self {
446 Self {
447 config: CfgContext::default(),
448 }
449 }
450 }
451 impl Default for TestCfg {
452 fn default() -> Self {
453 panic!()
454 }
455 }
456 impl Configuration for TestCfg {
457 const NAME: &str = "TEST";
458 fn ctx(&self) -> &CfgContext {
459 &self.config
460 }
461 fn set_ctx(&mut self, ctx: CfgContext) {
462 let _ = ctx.shared().get::<TestCfg>();
463 self.config = ctx;
464 }
465 }
466 let _ = TestCfg::new().ctx();
467 let _ = SharedCfg::new("TEST").add(TestCfg::new());
468 }
469
470 #[test]
471 fn shared_cfg() {
472 #[derive(Default, Debug)]
473 struct TestCfg {
474 config: CfgContext,
475 }
476 impl Configuration for TestCfg {
477 const NAME: &str = "TEST";
478 fn ctx(&self) -> &CfgContext {
479 &self.config
480 }
481 fn set_ctx(&mut self, ctx: CfgContext) {
482 self.config = ctx;
483 }
484 }
485
486 let cfg: SharedCfg = SharedCfg::new("TEST")
487 .add(TestCfg::default())
488 .service("SVC")
489 .into();
490
491 assert_eq!(cfg.tag(), "TEST");
492 assert_eq!(cfg.service(), "SVC");
493 let t = cfg.get::<TestCfg>();
494 assert_eq!(t.tag(), "TEST");
495 assert_eq!(t.service(), "SVC");
496 assert_eq!(t.shared(), cfg);
497 let t: Cfg<TestCfg> = Cfg::default();
498 assert_eq!(t.tag(), "--");
499 assert_eq!(t.service(), "");
500 assert_eq!(t.ctx().id(), t.id());
501
502 let t: Cfg<TestCfg> = t.ctx().get();
503 assert_eq!(t.tag(), "--");
504 assert_eq!(t.ctx().id(), t.id());
505
506 let cfg = SharedCfg::new("TEST2").build();
507 let cfg = SharedCfg::from(&cfg);
508 let t = cfg.get::<TestCfg>();
509 assert_eq!(t.tag(), "TEST2");
510 assert_eq!(t.id(), cfg.id());
511 drop(cfg);
512
513 let cfg2 = t.shared();
514 let t2 = cfg2.get::<TestCfg>();
515 assert_eq!(t2.tag(), "TEST2");
516 assert_eq!(t2.id(), cfg2.id());
517 unsafe { t2.replace(SharedCfg::from(SharedCfg::new("TEST3")).get::<TestCfg>()) };
518
519 let cfg2 = t2.shared();
520 let t3 = cfg2.get::<TestCfg>();
521 assert_eq!(t3.tag(), "TEST3");
522 assert_eq!(t3.id(), cfg2.id());
523
524 let t = SharedCfg::from(SharedCfg::new("TEST4").add(TestCfg::default())).get::<TestCfg>();
525 let cfg = t.shared();
526 assert_eq!(t.id(), cfg.id());
527 let t2 = t.clone();
528 assert_eq!(t2.id(), cfg.id());
529 assert_eq!(t2.tag(), "TEST4");
530
531 let t3 = t.ctx().get::<TestCfg>();
532 let t3 = Cfg::from(t3.get_ref());
533 let t3 = t3.get_shared::<TestCfg>();
534 assert_eq!(t3.id(), cfg.id());
535 assert_eq!(t3.tag(), "TEST4");
536
537 let t1 = SharedCfg::from(t3);
538 assert_eq!(t.id(), t1.id());
539 }
540
541 #[test]
542 fn cfg_conversions() {
543 use std::hash::BuildHasher;
544
545 #[derive(Default, Debug)]
546 struct TestCfg {
547 config: CfgContext,
548 val: usize,
549 }
550 impl Configuration for TestCfg {
551 const NAME: &str = "TEST";
552 fn ctx(&self) -> &CfgContext {
553 &self.config
554 }
555 fn set_ctx(&mut self, ctx: CfgContext) {
556 self.config = ctx;
557 }
558 }
559 let new = |val| TestCfg {
560 val,
561 ..Default::default()
562 };
563
564 let t = Cfg::from(new(1));
565 assert_eq!(t.val, 1);
566 assert_eq!(t.tag(), "--");
567
568 let shared = SharedCfg::from(new(2));
569 assert_eq!(shared.get::<TestCfg>().val, 2);
570 assert_eq!(Cfg::<TestCfg>::from(&shared).val, 2);
571 assert_eq!(Cfg::<TestCfg>::from(shared.clone()).val, 2);
572
573 let t: Cfg<TestCfg> = SharedCfg::new("T").add(new(3)).into();
574 assert_eq!(t.val, 3);
575 assert_eq!(t.tag(), "T");
576
577 let hasher = foldhash::fast::FixedState::default();
578 assert_eq!(hasher.hash_one(&shared), hasher.hash_one(shared.clone()));
579 assert_eq!(hasher.hash_one(&shared), hasher.hash_one(shared.id()));
580 }
581}