ferroui_base/platform/
i_scoped_resource.rs1use std::cell::{Cell, RefCell};
2use std::rc::Rc;
3
4pub trait IScopedResource<T> {
6 fn value(&self) -> T;
10
11 fn dispose(&self);
13}
14
15pub struct ScopedResource<T> {
17 disposed: Cell<bool>,
18 value: RefCell<Option<T>>,
19 dispose: RefCell<Option<Box<dyn FnOnce()>>>,
20}
21
22impl<T: Clone + 'static> ScopedResource<T> {
23 fn new(value: T, dispose: Box<dyn FnOnce()>) -> Self {
24 Self { disposed: Cell::new(false), value: RefCell::new(Some(value)), dispose: RefCell::new(Some(dispose)) }
25 }
26
27 pub fn create(value: T, dispose: impl FnOnce() + 'static) -> Rc<dyn IScopedResource<T>> {
28 Rc::new(Self::new(value, Box::new(dispose)))
29 }
30}
31
32impl<T: Clone + 'static> IScopedResource<T> for ScopedResource<T> {
33 fn dispose(&self) {
34 if !self.disposed.replace(true) {
35 let disp = self.dispose.borrow_mut().take();
36 let value = self.value.borrow_mut().take();
37 drop(value);
38 if let Some(disp) = disp {
39 disp();
40 }
41 }
42 }
43
44 fn value(&self) -> T {
45 if self.disposed.get() {
46 panic!("Cannot access a disposed object: ScopedResource");
47 }
48 match self.value.borrow().as_ref() {
49 Some(value) => value.clone(),
50 None => panic!("Cannot access a disposed object: ScopedResource"),
51 }
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
59
60 #[test]
61 fn dispose_runs_the_action_once() {
62 let count = Rc::new(Cell::new(0));
63 let c = count.clone();
64 let resource = ScopedResource::create(5, move || c.set(c.get() + 1));
65
66 assert_eq!(5, resource.value());
67 assert_eq!(0, count.get());
68
69 resource.dispose();
70 resource.dispose();
71 assert_eq!(1, count.get());
72 }
73
74 #[test]
75 #[should_panic(expected = "Cannot access a disposed object")]
76 fn value_of_a_disposed_resource_throws() {
77 let resource = ScopedResource::create(5, || {});
78 resource.dispose();
79 resource.value();
80 }
81}