Skip to main content

vtcode_core/pods/
store.rs

1use crate::pods::catalog::PodCatalog;
2use crate::pods::state::PodsState;
3use anyhow::{Context, Result, anyhow};
4use std::path::{Path, PathBuf};
5use vtcode_commons::fs::{ensure_dir_exists, read_json_file, write_json_file};
6
7/// Persisted pod storage rooted in `~/.vtcode/pods`.
8#[derive(Debug, Clone)]
9pub struct PodsStore {
10    base_dir: PathBuf,
11}
12
13impl PodsStore {
14    pub fn new(base_dir: impl Into<PathBuf>) -> Self {
15        Self {
16            base_dir: base_dir.into(),
17        }
18    }
19
20    pub fn default_store() -> Result<Self> {
21        let home = dirs::home_dir().ok_or_else(|| anyhow!("failed to resolve home directory"))?;
22        Ok(Self::new(home.join(".vtcode").join("pods")))
23    }
24
25    pub fn base_dir(&self) -> &Path {
26        &self.base_dir
27    }
28
29    pub fn state_path(&self) -> PathBuf {
30        self.base_dir.join("state.json")
31    }
32
33    pub fn catalog_path(&self) -> PathBuf {
34        self.base_dir.join("catalog.json")
35    }
36
37    pub async fn ensure_initialized(&self) -> Result<()> {
38        ensure_dir_exists(&self.base_dir).await?;
39
40        if !tokio::fs::try_exists(&self.catalog_path())
41            .await
42            .unwrap_or(false)
43        {
44            self.save_catalog(&PodCatalog::embedded_default()).await?;
45        }
46
47        if !tokio::fs::try_exists(&self.state_path())
48            .await
49            .unwrap_or(false)
50        {
51            self.save_state(&PodsState::default()).await?;
52        }
53
54        Ok(())
55    }
56
57    pub async fn load_state(&self) -> Result<PodsState> {
58        self.ensure_initialized().await?;
59        read_json_file(&self.state_path()).await.with_context(|| {
60            format!(
61                "failed to read pod state at {}",
62                self.state_path().display()
63            )
64        })
65    }
66
67    pub async fn save_state(&self, state: &PodsState) -> Result<()> {
68        ensure_dir_exists(&self.base_dir).await?;
69        write_json_file(&self.state_path(), state)
70            .await
71            .with_context(|| {
72                format!(
73                    "failed to write pod state at {}",
74                    self.state_path().display()
75                )
76            })
77    }
78
79    pub async fn load_catalog(&self) -> Result<PodCatalog> {
80        self.ensure_initialized().await?;
81        read_json_file(&self.catalog_path()).await.with_context(|| {
82            format!(
83                "failed to read pod catalog at {}",
84                self.catalog_path().display()
85            )
86        })
87    }
88
89    pub async fn save_catalog(&self, catalog: &PodCatalog) -> Result<()> {
90        ensure_dir_exists(&self.base_dir).await?;
91        write_json_file(&self.catalog_path(), catalog)
92            .await
93            .with_context(|| {
94                format!(
95                    "failed to write pod catalog at {}",
96                    self.catalog_path().display()
97                )
98            })
99    }
100}