fret_runtime/model/
handle.rs1use std::{
2 any::Any,
3 marker::PhantomData,
4 panic::{AssertUnwindSafe, Location, catch_unwind, resume_unwind},
5 rc::{Rc, Weak},
6};
7
8use super::ModelId;
9use super::error::ModelUpdateError;
10use super::host::{ModelCx, ModelHost};
11use super::store::{ModelStore, ModelStoreInner};
12
13pub struct Model<T> {
20 store: ModelStore,
21 id: ModelId,
22 _phantom: PhantomData<fn() -> T>,
23}
24
25impl<T> Model<T> {
26 pub(super) fn from_store_id(store: ModelStore, id: ModelId) -> Self {
27 Self {
28 store,
29 id,
30 _phantom: PhantomData,
31 }
32 }
33
34 pub fn id(&self) -> ModelId {
35 self.id
36 }
37
38 pub fn downgrade(&self) -> WeakModel<T> {
40 WeakModel {
41 store: Rc::downgrade(&self.store.inner),
42 id: self.id,
43 _phantom: PhantomData,
44 }
45 }
46
47 pub fn read<H: ModelHost, R>(
52 &self,
53 host: &mut H,
54 f: impl FnOnce(&mut H, &T) -> R,
55 ) -> Result<R, ModelUpdateError>
56 where
57 T: Any,
58 {
59 host.read(self, f)
60 }
61
62 #[track_caller]
67 pub fn update<H: ModelHost, R>(
68 &self,
69 host: &mut H,
70 f: impl FnOnce(&mut T, &mut ModelCx<'_, H>) -> R,
71 ) -> Result<R, ModelUpdateError>
72 where
73 T: Any,
74 {
75 let changed_at = Location::caller();
76
77 let mut lease = host.models_mut().lease(self)?;
78 let result = if cfg!(panic = "unwind") {
79 catch_unwind(AssertUnwindSafe(|| {
80 let mut cx = ModelCx { host };
81 f(lease.value_mut(), &mut cx)
82 }))
83 } else {
84 Ok({
85 let mut cx = ModelCx { host };
86 f(lease.value_mut(), &mut cx)
87 })
88 };
89
90 match result {
91 Ok(value) => {
92 lease.mark_dirty();
93 host.models_mut()
94 .end_lease_with_changed_at(&mut lease, changed_at);
95 Ok(value)
96 }
97 Err(panic) => {
98 host.models_mut().end_lease(&mut lease);
99 resume_unwind(panic)
100 }
101 }
102 }
103
104 pub fn revision<H: ModelHost>(&self, host: &H) -> Option<u64>
105 where
106 T: Any,
107 {
108 host.model_revision(self)
109 }
110
111 #[track_caller]
116 pub fn notify<H: ModelHost>(&self, host: &mut H) -> Result<(), ModelUpdateError>
117 where
118 T: Any,
119 {
120 host.models_mut()
121 .notify_with_changed_at(self, Location::caller())
122 }
123
124 pub fn read_ref<H: ModelHost, R>(
125 &self,
126 host: &H,
127 f: impl FnOnce(&T) -> R,
128 ) -> Result<R, ModelUpdateError>
129 where
130 T: Any,
131 {
132 host.models().read(self, f)
133 }
134}
135
136impl<T> std::fmt::Debug for Model<T> {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 f.debug_struct("Model").field("id", &self.id).finish()
139 }
140}
141
142impl<T> PartialEq for Model<T> {
143 fn eq(&self, other: &Self) -> bool {
144 Rc::ptr_eq(&self.store.inner, &other.store.inner) && self.id == other.id
145 }
146}
147
148impl<T> Eq for Model<T> {}
149
150impl<T> std::hash::Hash for Model<T> {
151 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
152 (Rc::as_ptr(&self.store.inner) as usize).hash(state);
153 self.id.hash(state);
154 }
155}
156
157impl<T> Clone for Model<T> {
158 fn clone(&self) -> Self {
159 self.store.inc_strong(self.id);
160 Self {
161 store: self.store.clone(),
162 id: self.id,
163 _phantom: PhantomData,
164 }
165 }
166}
167
168impl<T> Drop for Model<T> {
169 fn drop(&mut self) {
170 self.store.dec_strong(self.id);
171 }
172}
173
174pub struct WeakModel<T> {
175 store: Weak<ModelStoreInner>,
176 id: ModelId,
177 _phantom: PhantomData<fn() -> T>,
178}
179
180impl<T> std::fmt::Debug for WeakModel<T> {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 f.debug_struct("WeakModel").field("id", &self.id).finish()
183 }
184}
185
186impl<T> PartialEq for WeakModel<T> {
187 fn eq(&self, other: &Self) -> bool {
188 Weak::ptr_eq(&self.store, &other.store) && self.id == other.id
189 }
190}
191
192impl<T> Eq for WeakModel<T> {}
193
194impl<T> std::hash::Hash for WeakModel<T> {
195 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
196 (Weak::as_ptr(&self.store) as usize).hash(state);
197 self.id.hash(state);
198 }
199}
200
201impl<T> Clone for WeakModel<T> {
202 fn clone(&self) -> Self {
203 Self {
204 store: self.store.clone(),
205 id: self.id,
206 _phantom: PhantomData,
207 }
208 }
209}
210
211impl<T> WeakModel<T> {
212 pub fn id(&self) -> ModelId {
213 self.id
214 }
215
216 pub fn upgrade(&self) -> Option<Model<T>> {
217 let store = ModelStore {
218 inner: self.store.upgrade()?,
219 _not_send: PhantomData,
220 };
221 store.upgrade_strong(self.id).then(|| Model {
222 store,
223 id: self.id,
224 _phantom: PhantomData,
225 })
226 }
227}