1use std::marker::PhantomData;
10use std::sync::Arc;
11
12use key_paths_core::RefKpTrait;
13use parking_lot::{Mutex, RwLock};
14
15use crate::runtime::state_access::{StateRead, StateWrite};
16
17#[derive(Clone)]
19pub struct StateBinding<S: 'static> {
20 state: Arc<Mutex<S>>,
21}
22
23impl<S: 'static> StateBinding<S> {
24 pub(crate) fn new(state: Arc<Mutex<S>>) -> Self {
25 Self { state }
26 }
27
28 pub fn with<R>(&self, f: impl FnOnce(&S) -> R) -> R {
30 f(&*self.state.lock())
31 }
32
33 pub fn with_mut<R>(&self, f: impl FnOnce(&mut S) -> R) -> R {
35 f(&mut *self.state.lock())
36 }
37
38 pub fn project<V, SK>(&self, kp: SK) -> ProjectedBinding<S, V, SK>
40 where
41 SK: RefKpTrait<S, V>,
42 {
43 ProjectedBinding {
44 state: Arc::clone(&self.state),
45 kp,
46 _marker: PhantomData,
47 }
48 }
49}
50
51pub struct ProjectedBinding<Root: 'static, Focus: 'static, SK> {
53 state: Arc<Mutex<Root>>,
54 kp: SK,
55 _marker: PhantomData<Focus>,
56}
57
58impl<Root, Focus, SK> Clone for ProjectedBinding<Root, Focus, SK>
59where
60 Root: 'static,
61 Focus: 'static,
62 SK: RefKpTrait<Root, Focus> + Clone,
63{
64 fn clone(&self) -> Self {
65 Self {
66 state: Arc::clone(&self.state),
67 kp: self.kp.clone(),
68 _marker: PhantomData,
69 }
70 }
71}
72
73impl<Root, Focus, SK> ProjectedBinding<Root, Focus, SK>
74where
75 Root: 'static,
76 Focus: 'static,
77 SK: RefKpTrait<Root, Focus>,
78{
79 pub fn with<R>(&self, f: impl FnOnce(&Focus) -> R) -> Option<R> {
81 let guard = self.state.lock();
82 let focused = self.kp.focus(&*guard)?;
83 Some(f(focused))
84 }
85
86 pub fn with_mut<R>(&self, f: impl FnOnce(&mut Focus) -> R) -> Option<R> {
88 let mut guard = self.state.lock();
89 let focused = self.kp.focus_mut(&mut *guard)?;
90 Some(f(focused))
91 }
92
93 pub fn project<V, SubSK>(self, sub_kp: SubSK) -> ComposedBinding<Root, Focus, V, SK, SubSK>
95 where
96 SubSK: RefKpTrait<Focus, V>,
97 SK: Clone,
98 {
99 ComposedBinding {
100 state: self.state,
101 parent_kp: self.kp,
102 sub_kp,
103 _marker: PhantomData,
104 }
105 }
106}
107
108pub struct ComposedBinding<Root: 'static, Mid: 'static, Focus: 'static, ParentKP, SubKP> {
110 state: Arc<Mutex<Root>>,
111 parent_kp: ParentKP,
112 sub_kp: SubKP,
113 _marker: PhantomData<(Mid, Focus)>,
114}
115
116impl<Root, Mid, Focus, ParentKP, SubKP> ComposedBinding<Root, Mid, Focus, ParentKP, SubKP>
117where
118 Root: 'static,
119 Mid: 'static,
120 Focus: 'static,
121 ParentKP: RefKpTrait<Root, Mid>,
122 SubKP: RefKpTrait<Mid, Focus>,
123{
124 pub fn with<R>(&self, f: impl FnOnce(&Focus) -> R) -> Option<R> {
126 let guard = self.state.lock();
127 let mid = self.parent_kp.focus(&*guard)?;
128 let focused = self.sub_kp.focus(mid)?;
129 Some(f(focused))
130 }
131
132 pub fn with_mut<R>(&self, f: impl FnOnce(&mut Focus) -> R) -> Option<R> {
134 let mut guard = self.state.lock();
135 let mid = self.parent_kp.focus_mut(&mut *guard)?;
136 let focused = self.sub_kp.focus_mut(mid)?;
137 Some(f(focused))
138 }
139}
140
141#[derive(Clone)]
145pub struct ReadStateBinding<S: 'static> {
146 state: Arc<RwLock<S>>,
147}
148
149impl<S: 'static> ReadStateBinding<S> {
150 pub(crate) fn new(state: Arc<RwLock<S>>) -> Self {
151 Self { state }
152 }
153
154 pub fn with_read<R>(&self, f: impl FnOnce(&S) -> R) -> R {
155 self.state.with_read(f)
156 }
157
158 pub fn project<V, SK>(&self, kp: SK) -> ReadProjectedBinding<S, V, SK>
159 where
160 SK: RefKpTrait<S, V>,
161 {
162 ReadProjectedBinding {
163 state: Arc::clone(&self.state),
164 kp,
165 _marker: PhantomData,
166 }
167 }
168}
169
170#[derive(Clone)]
172pub struct RwStateBinding<S: 'static> {
173 state: Arc<RwLock<S>>,
174}
175
176impl<S: 'static> RwStateBinding<S> {
177 pub(crate) fn new(state: Arc<RwLock<S>>) -> Self {
178 Self { state }
179 }
180
181 pub fn with_read<R>(&self, f: impl FnOnce(&S) -> R) -> R {
182 self.state.with_read(f)
183 }
184
185 pub fn with_write<R>(&self, f: impl FnOnce(&mut S) -> R) -> R {
186 self.state.with_write(f)
187 }
188
189 pub fn read_store(&self) -> ReadStateBinding<S> {
190 ReadStateBinding::new(Arc::clone(&self.state))
191 }
192
193 pub fn project<V, SK>(&self, kp: SK) -> RwProjectedBinding<S, V, SK>
194 where
195 SK: RefKpTrait<S, V>,
196 {
197 RwProjectedBinding {
198 state: Arc::clone(&self.state),
199 kp,
200 _marker: PhantomData,
201 }
202 }
203}
204
205pub struct ReadProjectedBinding<Root: 'static, Focus: 'static, SK> {
207 state: Arc<RwLock<Root>>,
208 kp: SK,
209 _marker: PhantomData<Focus>,
210}
211
212impl<Root, Focus, SK> Clone for ReadProjectedBinding<Root, Focus, SK>
213where
214 Root: 'static,
215 Focus: 'static,
216 SK: RefKpTrait<Root, Focus> + Clone,
217{
218 fn clone(&self) -> Self {
219 Self {
220 state: Arc::clone(&self.state),
221 kp: self.kp.clone(),
222 _marker: PhantomData,
223 }
224 }
225}
226
227impl<Root, Focus, SK> ReadProjectedBinding<Root, Focus, SK>
228where
229 Root: 'static,
230 Focus: 'static,
231 SK: RefKpTrait<Root, Focus>,
232{
233 pub fn with_read<R>(&self, f: impl FnOnce(&Focus) -> R) -> Option<R> {
234 let guard = self.state.read();
235 let focused = self.kp.focus(&*guard)?;
236 Some(f(focused))
237 }
238}
239
240pub struct RwProjectedBinding<Root: 'static, Focus: 'static, SK> {
242 state: Arc<RwLock<Root>>,
243 kp: SK,
244 _marker: PhantomData<Focus>,
245}
246
247impl<Root, Focus, SK> Clone for RwProjectedBinding<Root, Focus, SK>
248where
249 Root: 'static,
250 Focus: 'static,
251 SK: RefKpTrait<Root, Focus> + Clone,
252{
253 fn clone(&self) -> Self {
254 Self {
255 state: Arc::clone(&self.state),
256 kp: self.kp.clone(),
257 _marker: PhantomData,
258 }
259 }
260}
261
262impl<Root, Focus, SK> RwProjectedBinding<Root, Focus, SK>
263where
264 Root: 'static,
265 Focus: 'static,
266 SK: RefKpTrait<Root, Focus>,
267{
268 pub fn with_read<R>(&self, f: impl FnOnce(&Focus) -> R) -> Option<R> {
269 let guard = self.state.read();
270 let focused = self.kp.focus(&*guard)?;
271 Some(f(focused))
272 }
273
274 pub fn with_write<R>(&self, f: impl FnOnce(&mut Focus) -> R) -> Option<R> {
275 let mut guard = self.state.write();
276 let focused = self.kp.focus_mut(&mut *guard)?;
277 Some(f(focused))
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use key_paths_derive::{FieldDiff, Kp};
285 use rust_key_paths::Kp as KpPath;
286
287 #[derive(Debug, Kp, Clone, Hash, FieldDiff, PartialEq)]
288 struct Episode {
289 current_position: i32,
290 is_favorite: bool,
291 }
292
293 fn position_kp() -> KpPath<
294 Episode,
295 i32,
296 &'static Episode,
297 &'static i32,
298 &'static mut Episode,
299 &'static mut i32,
300 for<'b> fn(&'b Episode) -> Option<&'b i32>,
301 for<'b> fn(&'b mut Episode) -> Option<&'b mut i32>,
302 > {
303 fn get(e: &Episode) -> Option<&i32> {
304 Some(&e.current_position)
305 }
306 fn get_mut(e: &mut Episode) -> Option<&mut i32> {
307 Some(&mut e.current_position)
308 }
309 KpPath::new(get, get_mut)
310 }
311
312 fn favorite_kp() -> KpPath<
313 Episode,
314 bool,
315 &'static Episode,
316 &'static bool,
317 &'static mut Episode,
318 &'static mut bool,
319 for<'b> fn(&'b Episode) -> Option<&'b bool>,
320 for<'b> fn(&'b mut Episode) -> Option<&'b mut bool>,
321 > {
322 fn get(e: &Episode) -> Option<&bool> {
323 Some(&e.is_favorite)
324 }
325 fn get_mut(e: &mut Episode) -> Option<&mut bool> {
326 Some(&mut e.is_favorite)
327 }
328 KpPath::new(get, get_mut)
329 }
330
331 #[test]
332 fn binding_projects_fields_without_cloning_root() {
333 let state = Arc::new(Mutex::new(Episode {
334 current_position: 1,
335 is_favorite: false,
336 }));
337 let binding = StateBinding::new(state);
338
339 let position = binding.project(position_kp());
340 assert_eq!(position.with(|p| *p), Some(1));
341
342 position.with_mut(|p| *p = 2);
343 assert_eq!(binding.with(|e| e.current_position), 2);
344
345 let favorite = binding.project(favorite_kp());
346 favorite.with_mut(|f| *f = true);
347 assert!(binding.with(|e| e.is_favorite));
348 }
349
350 #[test]
351 fn projected_binding_returns_none_when_keypath_misses() {
352 #[derive(Kp, Clone, Hash, FieldDiff, PartialEq, Debug)]
353 struct Root {
354 child: Option<Episode>,
355 }
356
357 fn child_kp() -> KpPath<
358 Root,
359 Episode,
360 &'static Root,
361 &'static Episode,
362 &'static mut Root,
363 &'static mut Episode,
364 for<'b> fn(&'b Root) -> Option<&'b Episode>,
365 for<'b> fn(&'b mut Root) -> Option<&'b mut Episode>,
366 > {
367 fn get(r: &Root) -> Option<&Episode> {
368 r.child.as_ref()
369 }
370 fn get_mut(r: &mut Root) -> Option<&mut Episode> {
371 r.child.as_mut()
372 }
373 KpPath::new(get, get_mut)
374 }
375
376 let binding = StateBinding::new(Arc::new(Mutex::new(Root { child: None })));
377 let child = binding.project(child_kp());
378 assert!(child.with(|_| ()).is_none());
379 assert!(child.with_mut(|_| ()).is_none());
380 }
381}