Skip to main content

attune_core/
field.rs

1use crossbeam_channel::{Receiver, unbounded};
2use serde::{Serialize, de::DeserializeOwned};
3use std::thread;
4
5use crate::{ChangeEvent, SettingsError, SettingsHandle, StoredValue};
6
7type FieldGetter<T, V> = Box<dyn for<'a> Fn(&'a T) -> &'a V + Send + Sync + 'static>;
8type FieldMutator<T, V> = Box<dyn Fn(&mut T, V) + Send + Sync + 'static>;
9
10/// Read-only access to a generated settings field.
11pub struct ReadOnlyField<T, V> {
12    handle: SettingsHandle<T>,
13    getter: FieldGetter<T, V>,
14}
15
16impl<T, V> ReadOnlyField<T, V>
17where
18    T: Clone + Send + Sync + 'static,
19    V: Clone,
20{
21    /// Creates a read-only field handle.
22    pub fn new(handle: SettingsHandle<T>, getter: FieldGetter<T, V>) -> Self {
23        Self { handle, getter }
24    }
25
26    /// Returns the field value from the current settings snapshot.
27    pub fn get(&self) -> V {
28        let snapshot = self.handle.snapshot();
29        (self.getter)(snapshot.as_ref()).clone()
30    }
31}
32
33/// Read, write, and subscribe access to a generated persisted settings field.
34pub struct PersistedField<T, V> {
35    handle: SettingsHandle<T>,
36    key: &'static str,
37    getter: FieldGetter<T, V>,
38    mutator: FieldMutator<T, V>,
39}
40
41impl<T, V> PersistedField<T, V>
42where
43    T: Clone + Send + Sync + 'static,
44    V: Clone,
45{
46    /// Creates a persisted field handle.
47    pub fn new(
48        handle: SettingsHandle<T>,
49        key: &'static str,
50        getter: FieldGetter<T, V>,
51        mutator: FieldMutator<T, V>,
52    ) -> Self {
53        Self {
54            handle,
55            key,
56            getter,
57            mutator,
58        }
59    }
60
61    /// Returns the field value from the current settings snapshot.
62    pub fn get(&self) -> V {
63        let snapshot = self.handle.snapshot();
64        (self.getter)(snapshot.as_ref()).clone()
65    }
66}
67
68impl<T, V> PersistedField<T, V>
69where
70    T: Clone + Send + Sync + 'static,
71    V: Clone + Serialize,
72{
73    /// Persists a new field value and updates the in-memory snapshot.
74    pub fn set(&self, value: V) -> Result<(), SettingsError> {
75        let old_value = Some(StoredValue::encode(&self.get())?);
76        let stored = StoredValue::encode(&value)?;
77        let mutator = &self.mutator;
78
79        self.handle
80            .write_field(self.key, old_value, stored, |next| {
81                mutator(next, value);
82            })
83    }
84}
85
86impl<T, V> PersistedField<T, V>
87where
88    T: Clone + Send + Sync + 'static,
89    V: DeserializeOwned + Send + 'static,
90{
91    /// Subscribes to typed changes for this field.
92    ///
93    /// The returned channel receives successfully decoded local and external
94    /// `Set` events for this field. Deletes, deserialize failures, other keys,
95    /// and values that cannot be decoded as `V` are ignored.
96    pub fn on_change(&self) -> Receiver<V> {
97        let source = self.handle.on_change();
98        let (tx, rx) = unbounded();
99        let key = self.key.to_string();
100
101        thread::spawn(move || {
102            while let Ok(event) = source.recv() {
103                let ChangeEvent::Set {
104                    key: event_key,
105                    new_value,
106                    ..
107                } = event
108                else {
109                    continue;
110                };
111
112                if event_key != key {
113                    continue;
114                }
115
116                let Ok(decoded) = new_value.decode::<V>() else {
117                    continue;
118                };
119
120                if tx.send(decoded).is_err() {
121                    return;
122                }
123            }
124        });
125
126        rx
127    }
128}