Skip to main content

fret_runtime/model/
host.rs

1use std::{
2    any::Any,
3    panic::{AssertUnwindSafe, Location, catch_unwind, resume_unwind},
4};
5
6use super::error::ModelUpdateError;
7use super::handle::Model;
8use super::store::ModelStore;
9
10/// Context passed to model update closures.
11///
12/// This provides access back to the host (often an `App`-like container) while a model lease is
13/// active, enabling updates that need to read/write other models or globals.
14pub struct ModelCx<'a, H: ModelHost + ?Sized> {
15    pub(super) host: &'a mut H,
16}
17
18impl<'a, H: ModelHost + ?Sized> ModelCx<'a, H> {
19    /// Returns the underlying host.
20    pub fn app(&mut self) -> &mut H {
21        self.host
22    }
23}
24
25/// Host surface required to read and update models stored in a [`ModelStore`].
26///
27/// This trait is intentionally portable and is implemented by higher-level containers like
28/// `fret-app::App`.
29pub trait ModelHost {
30    fn models(&self) -> &ModelStore;
31    fn models_mut(&mut self) -> &mut ModelStore;
32
33    fn read<T: Any, R>(
34        &mut self,
35        model: &Model<T>,
36        f: impl FnOnce(&mut Self, &T) -> R,
37    ) -> Result<R, ModelUpdateError> {
38        let mut lease = self.models_mut().lease(model)?;
39        let result = if cfg!(panic = "unwind") {
40            catch_unwind(AssertUnwindSafe(|| f(self, lease.value_ref())))
41        } else {
42            Ok(f(self, lease.value_ref()))
43        };
44
45        self.models_mut().end_lease(&mut lease);
46
47        match result {
48            Ok(value) => Ok(value),
49            Err(panic) => resume_unwind(panic),
50        }
51    }
52
53    fn model_revision<T: Any>(&self, model: &Model<T>) -> Option<u64> {
54        self.models().revision(model)
55    }
56
57    #[track_caller]
58    fn update_model<T: Any, R>(
59        &mut self,
60        model: &Model<T>,
61        f: impl FnOnce(&mut T, &mut ModelCx<'_, Self>) -> R,
62    ) -> Result<R, ModelUpdateError> {
63        let changed_at = Location::caller();
64
65        let mut lease = self.models_mut().lease(model)?;
66        let result = if cfg!(panic = "unwind") {
67            catch_unwind(AssertUnwindSafe(|| {
68                let mut cx = ModelCx { host: self };
69                f(lease.value_mut(), &mut cx)
70            }))
71        } else {
72            Ok({
73                let mut cx = ModelCx { host: self };
74                f(lease.value_mut(), &mut cx)
75            })
76        };
77
78        match result {
79            Ok(value) => {
80                lease.mark_dirty();
81                self.models_mut()
82                    .end_lease_with_changed_at(&mut lease, changed_at);
83                Ok(value)
84            }
85            Err(panic) => {
86                self.models_mut().end_lease(&mut lease);
87                resume_unwind(panic)
88            }
89        }
90    }
91}