Skip to main content

truce_core/
custom_state.rs

1//! Custom state serialization for plugin-specific persistent data.
2//!
3//! Use `#[derive(State)]` on a struct to auto-generate binary serialization:
4//!
5//! ```ignore
6//! #[derive(State, Default)]
7//! pub struct MyState {
8//!     pub instance_name: String,
9//!     pub view_mode: u8,
10//!     pub selected_ids: Vec<u32>,
11//! }
12//! ```
13//!
14//! Then use it in your plugin's `save_state`/`load_state`:
15//!
16//! ```ignore
17//! fn save_state(&self) -> Vec<u8> {
18//!     self.persistent.serialize()
19//! }
20//! fn load_state(&mut self, data: &[u8]) -> Result<(), StateLoadError> {
21//!     match MyState::deserialize(data) {
22//!         Some(s) => { self.persistent = s; Ok(()) }
23//!         None => Err(StateLoadError::Malformed("MyState")),
24//!     }
25//! }
26//! ```
27
28/// Cursor for reading binary state data.
29pub struct StateCursor<'a> {
30    data: &'a [u8],
31    pos: usize,
32}
33
34impl<'a> StateCursor<'a> {
35    #[must_use]
36    pub fn new(data: &'a [u8]) -> Self {
37        Self { data, pos: 0 }
38    }
39
40    #[must_use]
41    pub fn remaining(&self) -> usize {
42        self.data.len().saturating_sub(self.pos)
43    }
44
45    pub fn read_bytes(&mut self, n: usize) -> Option<&'a [u8]> {
46        if self.pos + n > self.data.len() {
47            return None;
48        }
49        let slice = &self.data[self.pos..self.pos + n];
50        self.pos += n;
51        Some(slice)
52    }
53
54    /// Skip the next field (reads its encoded size and advances past it).
55    /// Returns false if the data is malformed.
56    ///
57    /// # Panics
58    ///
59    /// Does not panic - the `expect` inside is unreachable because
60    /// `read_bytes(4)` only returns `Some` when the slice is exactly
61    /// 4 bytes long.
62    pub fn skip_field(&mut self) -> bool {
63        // Fields are prefixed with a u32 byte length by the derive macro.
64        if let Some(bytes) = self.read_bytes(4) {
65            // `read_bytes(4)` returns `Some(slice of length 4)` or
66            // `None`, so the `try_into::<[u8; 4]>()` here cannot fail.
67            // The `expect` documents that invariant for readers.
68            let len = u32::from_le_bytes(
69                bytes
70                    .try_into()
71                    .expect("read_bytes(4) returned a slice of unexpected length"),
72            ) as usize;
73            if self.pos + len <= self.data.len() {
74                self.pos += len;
75                return true;
76            }
77        }
78        false
79    }
80}
81
82/// Trait for types that can be serialized as a single state field.
83///
84/// Implemented for primitives, `String`, `Vec<T>`, and `Option<T>`.
85pub trait StateField: Sized {
86    fn write_field(&self, buf: &mut Vec<u8>);
87    fn read_field(cursor: &mut StateCursor) -> Option<Self>;
88}
89
90/// Trait for custom plugin state structs.
91///
92/// Derive with `#[derive(State)]`. The struct must also implement `Default`
93/// so missing fields can be filled with defaults when loading old state.
94pub trait State: Sized + Default {
95    /// Serialize into `buf`. Clears `buf` first, then reuses its
96    /// capacity - calling this repeatedly with the same buffer is
97    /// allocation-free once warmed, so it is the form to use on the
98    /// audio thread (e.g. `PluginLogic::snapshot_into`).
99    fn serialize_into(&self, buf: &mut Vec<u8>);
100
101    /// Serialize to a fresh `Vec`. Convenience wrapper over
102    /// [`Self::serialize_into`]; allocates, so prefer `serialize_into`
103    /// on the real-time path.
104    #[must_use]
105    fn serialize(&self) -> Vec<u8> {
106        let mut buf = Vec::new();
107        self.serialize_into(&mut buf);
108        buf
109    }
110
111    fn deserialize(data: &[u8]) -> Option<Self>;
112}
113
114// ---------------------------------------------------------------------------
115// StateField implementations for primitives
116// ---------------------------------------------------------------------------
117
118macro_rules! impl_state_field_int {
119    ($($ty:ty),*) => {
120        $(
121            impl StateField for $ty {
122                fn write_field(&self, buf: &mut Vec<u8>) {
123                    buf.extend_from_slice(&self.to_le_bytes());
124                }
125                fn read_field(cursor: &mut StateCursor) -> Option<Self> {
126                    let bytes = cursor.read_bytes(std::mem::size_of::<Self>())?;
127                    Some(Self::from_le_bytes(bytes.try_into().ok()?))
128                }
129            }
130        )*
131    };
132}
133
134impl_state_field_int!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
135
136impl StateField for bool {
137    fn write_field(&self, buf: &mut Vec<u8>) {
138        buf.push(u8::from(*self));
139    }
140    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
141        let b = cursor.read_bytes(1)?;
142        Some(b[0] != 0)
143    }
144}
145
146impl StateField for String {
147    fn write_field(&self, buf: &mut Vec<u8>) {
148        let bytes = self.as_bytes();
149        crate::cast::len_u32(bytes.len()).write_field(buf);
150        buf.extend_from_slice(bytes);
151    }
152    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
153        let len = u32::read_field(cursor)? as usize;
154        let bytes = cursor.read_bytes(len)?;
155        String::from_utf8(bytes.to_vec()).ok()
156    }
157}
158
159impl<T: StateField> StateField for Vec<T> {
160    fn write_field(&self, buf: &mut Vec<u8>) {
161        crate::cast::len_u32(self.len()).write_field(buf);
162        for item in self {
163            item.write_field(buf);
164        }
165    }
166    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
167        let len = u32::read_field(cursor)? as usize;
168        let mut vec = Vec::with_capacity(len.min(1024));
169        for _ in 0..len {
170            vec.push(T::read_field(cursor)?);
171        }
172        Some(vec)
173    }
174}
175
176impl<T: StateField> StateField for Option<T> {
177    fn write_field(&self, buf: &mut Vec<u8>) {
178        match self {
179            Some(val) => {
180                1u8.write_field(buf);
181                val.write_field(buf);
182            }
183            None => {
184                0u8.write_field(buf);
185            }
186        }
187    }
188    fn read_field(cursor: &mut StateCursor) -> Option<Self> {
189        let tag = u8::read_field(cursor)?;
190        if tag == 0 {
191            Some(None)
192        } else {
193            Some(Some(T::read_field(cursor)?))
194        }
195    }
196}
197
198// ---------------------------------------------------------------------------
199// StateBinding - typed wrapper for editor state access
200// ---------------------------------------------------------------------------
201
202use crate::editor::PluginContext;
203use std::sync::Arc;
204
205/// Typed state binding for editors.
206///
207/// Wraps the `get_state`/`set_state` closures from `PluginContext` with
208/// typed serialization. Caches the deserialized state to avoid repeated
209/// deserialization each frame.
210///
211/// ```ignore
212/// struct MyEditor {
213///     state: StateBinding<PersistentState>,
214/// }
215///
216/// // In open():
217/// self.state = StateBinding::new(&context);
218///
219/// // In state_changed():
220/// self.state.sync();
221///
222/// // Reading:
223/// let name = &self.state.get().instance_name;
224///
225/// // Writing:
226/// self.state.update(|s| s.instance_name = new_name);
227/// ```
228pub struct StateBinding<T: State> {
229    cached: T,
230    get_state: Arc<dyn Fn() -> Vec<u8> + Send + Sync>,
231    set_state: Arc<dyn Fn(Vec<u8>) + Send + Sync>,
232}
233
234impl<T: State> StateBinding<T> {
235    /// Create a new binding from a [`PluginContext`]. Generic over the
236    /// context's `<P>` since `StateBinding` cares only about the
237    /// `get_state` / `set_state` channel on the underlying
238    /// `EditorBridge`, never about parameter typing.
239    #[must_use]
240    pub fn new<P: ?Sized>(context: &PluginContext<P>) -> Self {
241        let bridge_for_get = Arc::clone(context.bridge());
242        let bridge_for_set = Arc::clone(context.bridge());
243        let mut binding = Self {
244            cached: T::default(),
245            get_state: Arc::new(move || bridge_for_get.get_state()),
246            set_state: Arc::new(move |data| bridge_for_set.set_state(data)),
247        };
248        binding.sync();
249        binding
250    }
251
252    /// Re-read state from the plugin. Call this from `state_changed()`.
253    pub fn sync(&mut self) {
254        let data = (self.get_state)();
255        if !data.is_empty()
256            && let Some(s) = T::deserialize(&data)
257        {
258            self.cached = s;
259        }
260    }
261
262    /// Get the current cached state.
263    pub fn get(&self) -> &T {
264        &self.cached
265    }
266
267    /// Modify state and write it back to the plugin.
268    pub fn update(&mut self, f: impl FnOnce(&mut T)) {
269        f(&mut self.cached);
270        let data = self.cached.serialize();
271        (self.set_state)(data);
272    }
273}
274
275impl<T: State> Default for StateBinding<T> {
276    /// Construct an **unwired** binding: `get()` returns `T::default()`
277    /// and `update()` *silently discards* the new state. Only useful
278    /// as a placeholder before the editor is opened; replace with
279    /// [`StateBinding::new(&context)`](StateBinding::new) inside
280    /// `Editor::open` once a [`PluginContext`] is available. If you
281    /// see writes vanishing, check that the binding has been wired up
282    /// before you call `update`.
283    fn default() -> Self {
284        Self {
285            cached: T::default(),
286            get_state: Arc::new(Vec::new),
287            set_state: Arc::new(|_| {}),
288        }
289    }
290}
291
292// ---------------------------------------------------------------------------
293// Tests
294// ---------------------------------------------------------------------------
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn primitives_round_trip() {
302        let mut buf = Vec::new();
303        42u32.write_field(&mut buf);
304        2.5f64.write_field(&mut buf);
305        true.write_field(&mut buf);
306
307        let mut cursor = StateCursor::new(&buf);
308        assert_eq!(u32::read_field(&mut cursor), Some(42));
309        assert_eq!(f64::read_field(&mut cursor), Some(2.5));
310        assert_eq!(bool::read_field(&mut cursor), Some(true));
311    }
312
313    #[test]
314    fn string_round_trip() {
315        let mut buf = Vec::new();
316        "hello world".to_string().write_field(&mut buf);
317
318        let mut cursor = StateCursor::new(&buf);
319        assert_eq!(
320            String::read_field(&mut cursor),
321            Some("hello world".to_string())
322        );
323    }
324
325    #[test]
326    fn vec_round_trip() {
327        let mut buf = Vec::new();
328        vec![1u32, 2, 3].write_field(&mut buf);
329
330        let mut cursor = StateCursor::new(&buf);
331        assert_eq!(Vec::<u32>::read_field(&mut cursor), Some(vec![1, 2, 3]));
332    }
333
334    #[test]
335    fn option_round_trip() {
336        let mut buf = Vec::new();
337        Some(42u32).write_field(&mut buf);
338        None::<u32>.write_field(&mut buf);
339
340        let mut cursor = StateCursor::new(&buf);
341        assert_eq!(Option::<u32>::read_field(&mut cursor), Some(Some(42)));
342        assert_eq!(Option::<u32>::read_field(&mut cursor), Some(None));
343    }
344
345    #[test]
346    fn nested_vec_string() {
347        let mut buf = Vec::new();
348        let v = vec!["foo".to_string(), "bar".to_string()];
349        v.write_field(&mut buf);
350
351        let mut cursor = StateCursor::new(&buf);
352        assert_eq!(Vec::<String>::read_field(&mut cursor), Some(v));
353    }
354}