qualia_client_core/
canvas_state.rs1use std::fs;
4use std::path::{Path, PathBuf};
5
6use serde::{Deserialize, Serialize};
7
8pub const STATE_FILE: &str = "wellfair/canvas_state.json";
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub struct CanvasNavigationState {
12 pub active_world_id: String,
13 pub temporal_t: u64,
15 #[serde(default = "default_ramp")]
17 pub ramp_secs: u64,
18}
19
20fn default_ramp() -> u64 {
21 86_400 }
23
24impl Default for CanvasNavigationState {
25 fn default() -> Self {
26 Self {
27 active_world_id: "q42:world:demo-offline".to_string(),
28 temporal_t: 1_750_000_000,
29 ramp_secs: default_ramp(),
30 }
31 }
32}
33
34pub fn load(storage_root: impl AsRef<Path>) -> CanvasNavigationState {
35 let path = storage_root.as_ref().join(STATE_FILE);
36 match fs::read(&path) {
37 Ok(bytes) => serde_json::from_slice(&bytes).unwrap_or_default(),
38 Err(_) => CanvasNavigationState::default(),
39 }
40}
41
42pub fn save(storage_root: impl AsRef<Path>, state: &CanvasNavigationState) -> std::io::Result<()> {
43 let path: PathBuf = storage_root.as_ref().join(STATE_FILE);
44 if let Some(parent) = path.parent() {
45 fs::create_dir_all(parent)?;
46 }
47 let tmp = path.with_extension("json.tmp");
48 let json =
49 serde_json::to_vec_pretty(state).map_err(|e| std::io::Error::other(e.to_string()))?;
50 fs::write(&tmp, &json)?;
51 fs::rename(&tmp, &path)?;
52 Ok(())
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn roundtrip() {
61 let dir = std::env::temp_dir().join(format!("chora-state-{}", std::process::id()));
62 let _ = fs::remove_dir_all(&dir);
63 let mut s = CanvasNavigationState::default();
64 s.temporal_t = 999;
65 save(&dir, &s).unwrap();
66 assert_eq!(load(&dir).temporal_t, 999);
67 let _ = fs::remove_dir_all(&dir);
68 }
69}