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(feature = "arc-swap")]
285#[derive(Clone)]
286pub struct SnapshotStateBinding<S: 'static> {
287 state: Arc<arc_swap::ArcSwap<S>>,
288}
289
290#[cfg(feature = "arc-swap")]
291impl<S: 'static> SnapshotStateBinding<S> {
292 pub(crate) fn new(state: Arc<arc_swap::ArcSwap<S>>) -> Self {
293 Self { state }
294 }
295
296 pub fn with_snapshot<R>(&self, f: impl FnOnce(&S) -> R) -> R {
297 f(&*self.state.load_full())
298 }
299
300 pub fn load(&self) -> Arc<S>
301 where
302 S: Clone,
303 {
304 self.state.load_full()
305 }
306
307 pub fn project<V, SK>(&self, kp: SK) -> SnapshotProjectedBinding<S, V, SK>
308 where
309 SK: RefKpTrait<S, V>,
310 {
311 SnapshotProjectedBinding {
312 state: Arc::clone(&self.state),
313 kp,
314 _marker: PhantomData,
315 }
316 }
317}
318
319#[cfg(feature = "arc-swap")]
321pub struct SnapshotProjectedBinding<Root: 'static, Focus: 'static, SK> {
322 state: Arc<arc_swap::ArcSwap<Root>>,
323 kp: SK,
324 _marker: PhantomData<Focus>,
325}
326
327#[cfg(feature = "arc-swap")]
328impl<Root, Focus, SK> Clone for SnapshotProjectedBinding<Root, Focus, SK>
329where
330 Root: 'static,
331 Focus: 'static,
332 SK: RefKpTrait<Root, Focus> + Clone,
333{
334 fn clone(&self) -> Self {
335 Self {
336 state: Arc::clone(&self.state),
337 kp: self.kp.clone(),
338 _marker: PhantomData,
339 }
340 }
341}
342
343#[cfg(feature = "arc-swap")]
344impl<Root, Focus, SK> SnapshotProjectedBinding<Root, Focus, SK>
345where
346 Root: 'static,
347 Focus: 'static,
348 SK: RefKpTrait<Root, Focus>,
349{
350 pub fn with_snapshot<R>(&self, f: impl FnOnce(&Focus) -> R) -> Option<R> {
351 let snapshot = self.state.load_full();
352 let focused = self.kp.focus(&*snapshot)?;
353 Some(f(focused))
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use key_paths_derive::{FieldDiff, Kp};
361 use rust_key_paths::Kp as KpPath;
362
363 #[derive(Debug, Kp, Clone, Hash, FieldDiff, PartialEq)]
364 struct Episode {
365 current_position: i32,
366 is_favorite: bool,
367 }
368
369 fn position_kp() -> KpPath<
370 Episode,
371 i32,
372 &'static Episode,
373 &'static i32,
374 &'static mut Episode,
375 &'static mut i32,
376 for<'b> fn(&'b Episode) -> Option<&'b i32>,
377 for<'b> fn(&'b mut Episode) -> Option<&'b mut i32>,
378 > {
379 fn get(e: &Episode) -> Option<&i32> {
380 Some(&e.current_position)
381 }
382 fn get_mut(e: &mut Episode) -> Option<&mut i32> {
383 Some(&mut e.current_position)
384 }
385 KpPath::new(get, get_mut)
386 }
387
388 fn favorite_kp() -> KpPath<
389 Episode,
390 bool,
391 &'static Episode,
392 &'static bool,
393 &'static mut Episode,
394 &'static mut bool,
395 for<'b> fn(&'b Episode) -> Option<&'b bool>,
396 for<'b> fn(&'b mut Episode) -> Option<&'b mut bool>,
397 > {
398 fn get(e: &Episode) -> Option<&bool> {
399 Some(&e.is_favorite)
400 }
401 fn get_mut(e: &mut Episode) -> Option<&mut bool> {
402 Some(&mut e.is_favorite)
403 }
404 KpPath::new(get, get_mut)
405 }
406
407 #[test]
408 fn binding_projects_fields_without_cloning_root() {
409 let state = Arc::new(Mutex::new(Episode {
410 current_position: 1,
411 is_favorite: false,
412 }));
413 let binding = StateBinding::new(state);
414
415 let position = binding.project(position_kp());
416 assert_eq!(position.with(|p| *p), Some(1));
417
418 position.with_mut(|p| *p = 2);
419 assert_eq!(binding.with(|e| e.current_position), 2);
420
421 let favorite = binding.project(favorite_kp());
422 favorite.with_mut(|f| *f = true);
423 assert!(binding.with(|e| e.is_favorite));
424 }
425
426 #[test]
427 fn projected_binding_returns_none_when_keypath_misses() {
428 #[derive(Kp, Clone, Hash, FieldDiff, PartialEq, Debug)]
429 struct Root {
430 child: Option<Episode>,
431 }
432
433 fn child_kp() -> KpPath<
434 Root,
435 Episode,
436 &'static Root,
437 &'static Episode,
438 &'static mut Root,
439 &'static mut Episode,
440 for<'b> fn(&'b Root) -> Option<&'b Episode>,
441 for<'b> fn(&'b mut Root) -> Option<&'b mut Episode>,
442 > {
443 fn get(r: &Root) -> Option<&Episode> {
444 r.child.as_ref()
445 }
446 fn get_mut(r: &mut Root) -> Option<&mut Episode> {
447 r.child.as_mut()
448 }
449 KpPath::new(get, get_mut)
450 }
451
452 let binding = StateBinding::new(Arc::new(Mutex::new(Root { child: None })));
453 let child = binding.project(child_kp());
454 assert!(child.with(|_| ()).is_none());
455 assert!(child.with_mut(|_| ()).is_none());
456 }
457}