Skip to main content

ferroui_base/rendering/
owned_disposable.rs

1use std::rc::Rc;
2
3use crate::platform::{IPlatformGraphicsContext, IPlatformRenderInterfaceContext};
4use crate::reactive::IDisposable;
5
6/// A value that [`OwnedDisposable`] can dispose.
7pub trait OwnedDisposableValue {
8    fn dispose_owned(&self);
9}
10
11impl OwnedDisposableValue for dyn IDisposable {
12    fn dispose_owned(&self) {
13        self.dispose();
14    }
15}
16
17impl OwnedDisposableValue for dyn IPlatformGraphicsContext {
18    fn dispose_owned(&self) {
19        self.dispose();
20    }
21}
22
23impl OwnedDisposableValue for dyn IPlatformRenderInterfaceContext {
24    fn dispose_owned(&self) {
25        self.dispose();
26    }
27}
28
29/// A disposable value that is only disposed with its holder when the holder
30/// owns it.
31pub struct OwnedDisposable<T: ?Sized + OwnedDisposableValue> {
32    owns: bool,
33    value: Option<Rc<T>>,
34}
35
36impl<T: ?Sized + OwnedDisposableValue> OwnedDisposable<T> {
37    pub fn new(value: Rc<T>, owns: bool) -> Self {
38        Self { owns, value: Some(value) }
39    }
40
41    /// The held value.
42    ///
43    /// # Panics
44    /// Panics when the holder has been disposed.
45    pub fn value(&self) -> Rc<T> {
46        match &self.value {
47            Some(value) => value.clone(),
48            None => panic!("Cannot access a disposed object: 'OwnedDisposable'."),
49        }
50    }
51
52    /// Releases the value, disposing it when it is owned.
53    pub fn dispose(&mut self) {
54        if let Some(value) = self.value.take() {
55            if self.owns {
56                value.dispose_owned();
57            }
58        }
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::reactive::Disposable;
66    use std::cell::Cell;
67
68    fn counting() -> (Rc<Cell<i32>>, Rc<dyn IDisposable>) {
69        let count = Rc::new(Cell::new(0));
70        let c = count.clone();
71        // Counts every call, unlike `Disposable::create`.
72        struct Counting(Rc<Cell<i32>>);
73        impl IDisposable for Counting {
74            fn dispose(&self) {
75                self.0.set(self.0.get() + 1);
76            }
77        }
78        (count, Rc::new(Counting(c)))
79    }
80
81    #[test]
82    fn disposes_an_owned_value_once() {
83        let (count, value) = counting();
84        let mut owned = OwnedDisposable::new(value.clone(), true);
85        assert!(Rc::ptr_eq(&owned.value(), &value));
86        owned.dispose();
87        owned.dispose();
88        assert_eq!(count.get(), 1);
89    }
90
91    #[test]
92    fn does_not_dispose_a_value_it_does_not_own() {
93        let (count, value) = counting();
94        let mut owned = OwnedDisposable::new(value, false);
95        owned.dispose();
96        assert_eq!(count.get(), 0);
97    }
98
99    #[test]
100    #[should_panic(expected = "OwnedDisposable")]
101    fn value_panics_after_dispose() {
102        let mut owned = OwnedDisposable::new(Disposable::empty(), false);
103        owned.dispose();
104        owned.value();
105    }
106}