Skip to main content

amethystate_core/primitives/
field_core.rs

1use crate::change::Change;
2use crate::primitives::signal::{Signal, SignalSubscription, held};
3use serde::Serialize;
4use serde::de::DeserializeOwned;
5use std::sync::{Arc, Mutex};
6use uuid::Uuid;
7
8pub trait FieldValue: DeserializeOwned + Serialize + Clone + Send + Sync + 'static {}
9impl<T: DeserializeOwned + Serialize + Clone + Send + Sync + 'static> FieldValue for T {}
10
11/// A rule a field was declared with: it puts the value right where it has to.
12pub type DeclaredRule<T> = Arc<dyn Fn(&mut T) + Send + Sync + 'static>;
13
14pub struct FieldCore<T> {
15    pub signal: Signal<T>,
16    pub rules: Arc<Mutex<Vec<DeclaredRule<T>>>>,
17}
18
19impl<T> Clone for FieldCore<T> {
20    fn clone(&self) -> Self {
21        Self {
22            signal: self.signal.clone(),
23            rules: self.rules.clone(),
24        }
25    }
26}
27
28impl<T: Clone + 'static> FieldCore<T> {
29    pub fn new_with_signal(initial: Signal<T>) -> Self {
30        Self {
31            signal: initial,
32            rules: Arc::new(Mutex::new(Vec::new())),
33        }
34    }
35
36    pub fn new(initial: T) -> Self {
37        Self::new_with_signal(Signal::new(initial))
38    }
39
40    /// Adds a rule every write goes through, for as long as the field lives:
41    /// there is no handle to take it off.
42    pub fn rule<F>(&self, rule: F)
43    where
44        F: Fn(&mut T) + Send + Sync + 'static,
45    {
46        held(&self.rules).push(Arc::new(rule));
47    }
48
49    /// Puts `value` through the declared rules, in the order they were added.
50    pub fn ruled(&self, value: &mut T) {
51        let rules = held(&self.rules).clone();
52        for rule in rules {
53            rule(value);
54        }
55    }
56
57    pub fn get(&self) -> T {
58        self.signal.get()
59    }
60
61    #[track_caller]
62    pub fn subscribe<F>(&self, callback: F) -> SignalSubscription
63    where
64        F: for<'a> Fn(&'a T) + Send + Sync + 'static,
65    {
66        self.signal.subscribe(callback)
67    }
68
69    #[track_caller]
70    pub fn subscribe_with_source<F>(&self, callback: F) -> SignalSubscription
71    where
72        F: for<'a> Fn(&'a T, Option<Uuid>) + Send + Sync + 'static,
73    {
74        self.signal.subscribe_with_source(callback)
75    }
76
77    /// The change a write of `value` makes, with the declared rules applied.
78    pub fn change(&self, value: T, source: Option<Uuid>) -> Change<T> {
79        let mut change = Change {
80            source,
81            old_value: self.get(),
82            new_value: value,
83        };
84        self.ruled(&mut change.new_value);
85        change
86    }
87}