use crate::patch::{create_patch, Patch};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::fmt::Debug;
pub trait AgentState:
'static + Debug + Clone + Send + Sync + for<'de> Deserialize<'de> + Serialize + Default
{
}
pub trait FwdProps:
'static + Clone + Send + Sync + for<'de> Deserialize<'de> + Serialize + Default
{
}
impl AgentState for JsonValue {}
impl AgentState for () {}
impl FwdProps for JsonValue {}
impl FwdProps for () {}
pub fn diff_states(old: &JsonValue, new: &JsonValue) -> Option<Patch> {
let patch = create_patch(old, new);
if patch.0.is_empty() {
None
} else {
Some(patch)
}
}
#[derive(Debug, Clone)]
pub struct StateManager {
current: JsonValue,
version: u64,
}
impl StateManager {
pub fn new(initial: JsonValue) -> Self {
Self {
current: initial,
version: 0,
}
}
pub fn current(&self) -> &JsonValue {
&self.current
}
pub fn version(&self) -> u64 {
self.version
}
pub fn update(&mut self, new_state: JsonValue) -> Option<Patch> {
let patch = diff_states(&self.current, &new_state);
if patch.is_some() {
self.current = new_state;
self.version += 1;
}
patch
}
pub fn update_with<F>(&mut self, f: F) -> Option<Patch>
where
F: FnOnce(&mut JsonValue),
{
let old_state = self.current.clone();
f(&mut self.current);
let patch = diff_states(&old_state, &self.current);
if patch.is_some() {
self.version += 1;
}
patch
}
pub fn reset(&mut self, new_state: JsonValue) {
self.current = new_state;
self.version += 1;
}
pub fn snapshot(&self) -> JsonValue {
self.current.clone()
}
}
impl Default for StateManager {
fn default() -> Self {
Self::new(JsonValue::Object(serde_json::Map::new()))
}
}
#[derive(Debug, Clone)]
pub struct TypedStateManager<S: AgentState> {
current: S,
version: u64,
}
impl<S: AgentState + PartialEq> TypedStateManager<S> {
pub fn new(initial: S) -> Self {
Self {
current: initial,
version: 0,
}
}
pub fn current(&self) -> &S {
&self.current
}
pub fn version(&self) -> u64 {
self.version
}
pub fn update(&mut self, new_state: S) -> Option<Patch> {
if self.current == new_state {
return None;
}
let old_json = serde_json::to_value(&self.current).ok()?;
let new_json = serde_json::to_value(&new_state).ok()?;
let patch = diff_states(&old_json, &new_json);
self.current = new_state;
self.version += 1;
patch
}
pub fn reset(&mut self, new_state: S) {
self.current = new_state;
self.version += 1;
}
pub fn snapshot(&self) -> JsonValue {
serde_json::to_value(&self.current).unwrap_or(JsonValue::Null)
}
pub fn as_json(&self) -> JsonValue {
serde_json::to_value(&self.current).unwrap_or(JsonValue::Null)
}
}
impl<S: AgentState + PartialEq> Default for TypedStateManager<S> {
fn default() -> Self {
Self::new(S::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
struct TestState {
value: i32,
}
impl AgentState for TestState {}
#[derive(Clone, Default, Serialize, Deserialize)]
struct TestProps {
name: String,
}
impl FwdProps for TestProps {}
#[test]
fn test_json_value_implements_agent_state() {
fn requires_agent_state<T: AgentState>(_: T) {}
requires_agent_state(JsonValue::Null);
}
#[test]
fn test_unit_implements_agent_state() {
fn requires_agent_state<T: AgentState>(_: T) {}
requires_agent_state(());
}
#[test]
fn test_json_value_implements_fwd_props() {
fn requires_fwd_props<T: FwdProps>(_: T) {}
requires_fwd_props(JsonValue::Null);
}
#[test]
fn test_unit_implements_fwd_props() {
fn requires_fwd_props<T: FwdProps>(_: T) {}
requires_fwd_props(());
}
#[test]
fn test_custom_state_type() {
fn requires_agent_state<T: AgentState>(_: T) {}
requires_agent_state(TestState { value: 42 });
}
#[test]
fn test_custom_props_type() {
fn requires_fwd_props<T: FwdProps>(_: T) {}
requires_fwd_props(TestProps {
name: "test".to_string(),
});
}
#[test]
fn test_diff_states_with_changes() {
use serde_json::json;
let old = json!({"count": 0});
let new = json!({"count": 5});
let patch = diff_states(&old, &new);
assert!(patch.is_some());
}
#[test]
fn test_diff_states_no_changes() {
use serde_json::json;
let state = json!({"count": 0});
let patch = diff_states(&state, &state);
assert!(patch.is_none());
}
#[test]
fn test_state_manager_new() {
use serde_json::json;
let manager = StateManager::new(json!({"count": 0}));
assert_eq!(manager.current()["count"], 0);
assert_eq!(manager.version(), 0);
}
#[test]
fn test_state_manager_update_with_changes() {
use serde_json::json;
let mut manager = StateManager::new(json!({"count": 0}));
let delta = manager.update(json!({"count": 5}));
assert!(delta.is_some());
assert_eq!(manager.current()["count"], 5);
assert_eq!(manager.version(), 1);
}
#[test]
fn test_state_manager_update_no_changes() {
use serde_json::json;
let mut manager = StateManager::new(json!({"count": 0}));
let delta = manager.update(json!({"count": 0}));
assert!(delta.is_none());
assert_eq!(manager.version(), 0); }
#[test]
fn test_state_manager_update_with_closure() {
use serde_json::json;
let mut manager = StateManager::new(json!({"count": 0}));
let delta = manager.update_with(|state| {
state["count"] = json!(10);
});
assert!(delta.is_some());
assert_eq!(manager.current()["count"], 10);
assert_eq!(manager.version(), 1);
}
#[test]
fn test_state_manager_update_with_no_changes() {
use serde_json::json;
let mut manager = StateManager::new(json!({"count": 0}));
let delta = manager.update_with(|_state| {
});
assert!(delta.is_none());
assert_eq!(manager.version(), 0);
}
#[test]
fn test_state_manager_reset() {
use serde_json::json;
let mut manager = StateManager::new(json!({"count": 0}));
manager.reset(json!({"count": 100, "new_field": true}));
assert_eq!(manager.current()["count"], 100);
assert_eq!(manager.current()["new_field"], true);
assert_eq!(manager.version(), 1);
}
#[test]
fn test_state_manager_snapshot() {
use serde_json::json;
let manager = StateManager::new(json!({"count": 42}));
let snapshot = manager.snapshot();
assert_eq!(snapshot, json!({"count": 42}));
}
#[test]
fn test_state_manager_default() {
let manager = StateManager::default();
assert!(manager.current().is_object());
assert_eq!(manager.version(), 0);
}
#[test]
fn test_state_manager_multiple_updates() {
use serde_json::json;
let mut manager = StateManager::new(json!({"count": 0}));
manager.update(json!({"count": 1}));
manager.update(json!({"count": 2}));
manager.update(json!({"count": 3}));
assert_eq!(manager.current()["count"], 3);
assert_eq!(manager.version(), 3);
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
struct AppState {
count: u32,
name: String,
}
impl AgentState for AppState {}
#[test]
fn test_typed_state_manager_new() {
let manager = TypedStateManager::new(AppState {
count: 0,
name: "test".to_string(),
});
assert_eq!(manager.current().count, 0);
assert_eq!(manager.current().name, "test");
assert_eq!(manager.version(), 0);
}
#[test]
fn test_typed_state_manager_update() {
let mut manager = TypedStateManager::new(AppState {
count: 0,
name: "test".to_string(),
});
let delta = manager.update(AppState {
count: 5,
name: "test".to_string(),
});
assert!(delta.is_some());
assert_eq!(manager.current().count, 5);
assert_eq!(manager.version(), 1);
}
#[test]
fn test_typed_state_manager_update_no_changes() {
let mut manager = TypedStateManager::new(AppState {
count: 0,
name: "test".to_string(),
});
let delta = manager.update(AppState {
count: 0,
name: "test".to_string(),
});
assert!(delta.is_none());
assert_eq!(manager.version(), 0);
}
#[test]
fn test_typed_state_manager_reset() {
let mut manager = TypedStateManager::new(AppState {
count: 0,
name: "old".to_string(),
});
manager.reset(AppState {
count: 100,
name: "new".to_string(),
});
assert_eq!(manager.current().count, 100);
assert_eq!(manager.current().name, "new");
assert_eq!(manager.version(), 1);
}
#[test]
fn test_typed_state_manager_snapshot() {
let manager = TypedStateManager::new(AppState {
count: 42,
name: "test".to_string(),
});
let snapshot = manager.snapshot();
assert_eq!(snapshot["count"], 42);
assert_eq!(snapshot["name"], "test");
}
#[test]
fn test_typed_state_manager_as_json() {
let manager = TypedStateManager::new(AppState {
count: 10,
name: "hello".to_string(),
});
let json = manager.as_json();
assert_eq!(json["count"], 10);
assert_eq!(json["name"], "hello");
}
#[test]
fn test_typed_state_manager_default() {
let manager: TypedStateManager<AppState> = TypedStateManager::default();
assert_eq!(manager.current().count, 0);
assert_eq!(manager.current().name, "");
assert_eq!(manager.version(), 0);
}
}