80 lines
2.2 KiB
Rust
80 lines
2.2 KiB
Rust
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
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 fn serialize_session_state(
|
|
session_state: &SessionState,
|
|
) -> Result<String, anyhow::Error> {
|
|
let stored = StoredSessionStateRef {
|
|
state: session_state,
|
|
};
|
|
|
|
serde_json::to_string(&stored).map_err(anyhow::Error::new)
|
|
}
|
|
|
|
pub fn deserialize_session_state(
|
|
value: &str,
|
|
) -> Result<SessionState, anyhow::Error> {
|
|
let value = serde_json::from_str::<Value>(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(_)))
|
|
&& 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<String, String> =
|
|
serde_json::from_value(Value::Object(obj))?;
|
|
let mut migrated: Map<String, Value> = Map::new();
|
|
for (key, json_encoded) in legacy {
|
|
migrated.insert(key, serde_json::from_str::<Value>(&json_encoded)?);
|
|
}
|
|
|
|
return Ok(migrated);
|
|
}
|
|
|
|
Ok(obj)
|
|
}
|