Skip to main content

terrazzo_fixture/
lib.rs

1use std::sync::Arc;
2use std::sync::Mutex;
3use std::sync::OnceLock;
4use std::sync::Weak;
5
6#[derive(Default)]
7pub struct Fixture<T> {
8    once: OnceLock<FixtureState<T>>,
9    lock: Mutex<()>,
10}
11
12impl<T> Fixture<T> {
13    pub const fn new() -> Self {
14        Self {
15            once: OnceLock::new(),
16            lock: Mutex::new(()),
17        }
18    }
19
20    pub fn get_or_init(&self, init: impl Fn() -> T + Send + Sync + 'static) -> Arc<T> {
21        self.once
22            .get_or_init(|| FixtureState::new(init))
23            .get_or_init()
24    }
25
26    pub fn get(&self) -> Arc<T> {
27        self.once.get().expect("Fixture::get").get()
28    }
29
30    pub fn lock(&self) -> impl Drop {
31        self.lock.lock().unwrap()
32    }
33}
34
35struct FixtureState<T> {
36    init: Box<dyn Fn() -> T + Send + Sync>,
37    current: Mutex<Weak<T>>,
38}
39
40impl<T> FixtureState<T> {
41    fn new(init: impl Fn() -> T + Send + Sync + 'static) -> Self {
42        Self {
43            init: Box::new(init),
44            current: Default::default(),
45        }
46    }
47
48    fn get_or_init(&self) -> Arc<T> {
49        let mut current = self.current.lock().expect("FixtureState::current::lock");
50        if let Some(value) = current.upgrade() {
51            return value;
52        }
53        let value = Arc::new((self.init)());
54        *current = Arc::downgrade(&value);
55        return value;
56    }
57
58    fn get(&self) -> Arc<T> {
59        self.current
60            .lock()
61            .expect("FixtureState::current::lock")
62            .upgrade()
63            .expect("FixtureState::get")
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use std::sync::Arc;
70    use std::sync::atomic::AtomicI32;
71    use std::sync::atomic::Ordering::SeqCst;
72
73    use super::Fixture;
74
75    #[test]
76    fn test_fixture() {
77        let a = fixture();
78        assert_eq!(1, *a);
79        let b = fixture();
80        assert_eq!(1, *a);
81        drop(a);
82        drop(b);
83        let c = fixture();
84        assert_eq!(2, *c);
85    }
86
87    fn fixture() -> Arc<i32> {
88        static FIXTURE: Fixture<i32> = Fixture::new();
89        static NEXT: AtomicI32 = AtomicI32::new(1);
90        FIXTURE.get_or_init(|| NEXT.fetch_add(1, SeqCst))
91    }
92}