Skip to main content

cognis_graph/
state.rs

1//! Per-graph typed state contract.
2
3/// Trait implemented by structs that hold graph state. The `Update` type
4/// is generated by `#[derive(GraphState)]` and represents a partial delta;
5/// `apply` merges a delta into self via per-field reducers.
6pub trait GraphState: Sized + Send + Sync + 'static {
7    /// Per-field-reducer-aware delta type.
8    type Update: Default + Send + 'static;
9
10    /// Apply a delta to self.
11    fn apply(&mut self, update: Self::Update);
12
13    /// Reset any `#[reducer(ephemeral)]` fields to `Default::default()`.
14    /// The engine calls this at the start of every superstep, before
15    /// running tasks — so ephemeral fields hold only writes from the
16    /// current step. Default impl is a no-op for state types with no
17    /// ephemeral fields.
18    fn reset_ephemeral(&mut self) {}
19}
20
21/// Deep-merge `source` into `target` for `serde_json::Value`. Used by
22/// `#[derive(GraphState)]`-generated code when a field is annotated
23/// `#[reducer(merge)]`.
24///
25/// Merge rules:
26/// - Both objects: keys union; for shared keys, recurse.
27/// - Both arrays: target is replaced by source. (Use `#[reducer(append)]`
28///   for `Vec<T>` if you want concatenation.)
29/// - Anything else: source replaces target.
30#[doc(hidden)]
31pub fn __merge_json(target: &mut serde_json::Value, source: serde_json::Value) {
32    use serde_json::Value;
33    match (target, source) {
34        (Value::Object(t_map), Value::Object(s_map)) => {
35            for (k, v) in s_map {
36                __merge_json(t_map.entry(k).or_insert(Value::Null), v);
37            }
38        }
39        (slot, source) => {
40            *slot = source;
41        }
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::__merge_json;
48    use serde_json::json;
49
50    #[test]
51    fn merge_objects_keys_union() {
52        let mut t = json!({"a": 1, "b": 2});
53        __merge_json(&mut t, json!({"b": 20, "c": 3}));
54        assert_eq!(t, json!({"a": 1, "b": 20, "c": 3}));
55    }
56
57    #[test]
58    fn merge_nested_objects() {
59        let mut t = json!({"x": {"a": 1, "b": 2}});
60        __merge_json(&mut t, json!({"x": {"b": 20, "c": 3}}));
61        assert_eq!(t, json!({"x": {"a": 1, "b": 20, "c": 3}}));
62    }
63
64    #[test]
65    fn merge_arrays_replace() {
66        let mut t = json!([1, 2, 3]);
67        __merge_json(&mut t, json!([9]));
68        assert_eq!(t, json!([9]));
69    }
70
71    #[test]
72    fn merge_scalar_replace() {
73        let mut t = json!(1);
74        __merge_json(&mut t, json!("hello"));
75        assert_eq!(t, json!("hello"));
76    }
77}