gestura_core/checkpoints/
store.rs1use std::{fs, path::PathBuf};
4
5use super::types::{Checkpoint, CheckpointError, CheckpointId, CheckpointMetadata};
6
7pub fn default_checkpoints_dir() -> PathBuf {
11 crate::config::AppConfig::data_dir().join("checkpoints")
12}
13
14#[derive(Debug, Clone)]
16pub struct FileCheckpointStore {
17 dir: PathBuf,
18}
19
20impl FileCheckpointStore {
21 pub fn new(dir: PathBuf) -> Self {
23 Self { dir }
24 }
25
26 pub fn new_default() -> Self {
28 Self::new(default_checkpoints_dir())
29 }
30
31 fn ensure_dir(&self) -> Result<(), CheckpointError> {
33 fs::create_dir_all(&self.dir)?;
34 Ok(())
35 }
36
37 fn path_for(&self, id: &CheckpointId) -> PathBuf {
39 self.dir.join(format!("{id}.json"))
40 }
41
42 pub fn save(&self, checkpoint: &Checkpoint) -> Result<(), CheckpointError> {
44 self.ensure_dir()?;
45 let path = self.path_for(&checkpoint.metadata.id);
46 let bytes = serde_json::to_vec_pretty(checkpoint)?;
47 fs::write(path, bytes)?;
48 Ok(())
49 }
50
51 pub fn load(&self, id: &CheckpointId) -> Result<Checkpoint, CheckpointError> {
53 self.ensure_dir()?;
54 let path = self.path_for(id);
55 let bytes = fs::read(&path).map_err(|e| {
56 if e.kind() == std::io::ErrorKind::NotFound {
57 CheckpointError::NotFound(*id)
58 } else {
59 CheckpointError::Io(e)
60 }
61 })?;
62 Ok(serde_json::from_slice(&bytes)?)
63 }
64
65 pub fn delete(&self, id: &CheckpointId) -> Result<(), CheckpointError> {
67 self.ensure_dir()?;
68 let path = self.path_for(id);
69 fs::remove_file(&path).map_err(|e| {
70 if e.kind() == std::io::ErrorKind::NotFound {
71 CheckpointError::NotFound(*id)
72 } else {
73 CheckpointError::Io(e)
74 }
75 })?;
76 Ok(())
77 }
78
79 pub fn list_metadata(&self) -> Result<Vec<CheckpointMetadata>, CheckpointError> {
81 self.ensure_dir()?;
82 let mut out = Vec::new();
83 for entry in fs::read_dir(&self.dir)? {
84 let entry = entry?;
85 let path = entry.path();
86 if path.extension().and_then(|e| e.to_str()) != Some("json") {
87 continue;
88 }
89 let bytes = fs::read(&path)?;
90 let ckpt: Checkpoint = serde_json::from_slice(&bytes)?;
91 out.push(ckpt.metadata);
92 }
93 Ok(out)
94 }
95}