use std::collections::HashMap; use serde::ser::{Serialize, SerializeMap, Serializer}; use serde_json::{Map, Value}; use super::interface::SessionState; const SESSION_STATE_FORMAT_VERSION: u8 = 1; #[derive(Debug)] struct StoredSessionStateRef<'a> { state: &'a SessionState, } impl Serialize for StoredSessionStateRef<'_> { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut map = serializer.serialize_map(Some(2))?; map.serialize_entry("v", &SESSION_STATE_FORMAT_VERSION)?; map.serialize_entry("state", self.state)?; map.end() } } pub(crate) fn serialize_session_state( session_state: &SessionState, ) -> Result { let stored = StoredSessionStateRef { state: session_state, }; serde_json::to_string(&stored).map_err(anyhow::Error::new) } pub(crate) fn deserialize_session_state(value: &str) -> Result { let value = serde_json::from_str::(value)?; let Value::Object(mut obj) = value else { anyhow::bail!("Session state is not a JSON object"); }; if matches!(obj.get("state"), Some(Value::Object(_))) { if let Some(Value::Number(v)) = obj.get("v") { let v = v .as_u64() .ok_or_else(|| anyhow::anyhow!("Invalid session state format version"))?; let v = u8::try_from(v) .map_err(|_| anyhow::anyhow!("Invalid session state format version"))?; anyhow::ensure!( v == SESSION_STATE_FORMAT_VERSION, "Unsupported session state format version: {}", v ); let Some(Value::Object(state)) = obj.remove("state") else { unreachable!("`state` was checked to be an object above"); }; return Ok(state); } } if obj.values().all(Value::is_string) { let legacy: HashMap = serde_json::from_value(Value::Object(obj))?; let mut migrated: Map = Map::new(); for (key, json_encoded) in legacy { migrated.insert(key, serde_json::from_str::(&json_encoded)?); } return Ok(migrated); } Ok(obj) }