1use crate::paths::InstallScope;
2use anyhow::{Context, Result};
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5use std::fs;
6use std::path::{Path, PathBuf};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct InstalledApp {
10 pub id: String,
11 pub name: String,
12 pub version: Option<String>,
13 pub scope: InstallScope,
14 pub install_dir: PathBuf,
15 pub command_name: String,
16 pub command_path: PathBuf,
17 pub desktop_path: PathBuf,
18 pub icon_paths: Vec<PathBuf>,
19 pub source_archive: Option<PathBuf>,
20 pub source_sha256: Option<String>,
21}
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
24pub struct StateDb {
25 pub apps: BTreeMap<String, InstalledApp>,
26}
27
28pub fn load_state(path: &Path) -> Result<StateDb> {
29 if !path.exists() {
30 return Ok(StateDb::default());
31 }
32 let text = fs::read_to_string(path).with_context(|| format!("failed to read state DB: {}", path.display()))?;
33 let db = serde_json::from_str(&text).with_context(|| format!("failed to parse state DB: {}", path.display()))?;
34 Ok(db)
35}
36
37pub fn save_state(path: &Path, db: &StateDb) -> Result<()> {
38 if let Some(parent) = path.parent() {
39 fs::create_dir_all(parent).with_context(|| format!("failed to create state dir: {}", parent.display()))?;
40 }
41 let text = serde_json::to_string_pretty(db)?;
42 fs::write(path, text).with_context(|| format!("failed to write state DB: {}", path.display()))?;
43 Ok(())
44}