Skip to main content

pitchfork_cli/
state_file.rs

1use crate::daemon::Daemon;
2use crate::error::FileError;
3use crate::{Result, env};
4use once_cell::sync::Lazy;
5use std::collections::{BTreeMap, BTreeSet};
6use std::fmt::Debug;
7use std::path::{Path, PathBuf};
8
9#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
10pub struct StateFile {
11    pub daemons: BTreeMap<String, Daemon>,
12    pub disabled: BTreeSet<String>,
13    pub shell_dirs: BTreeMap<String, PathBuf>,
14    #[serde(skip)]
15    pub(crate) path: PathBuf,
16}
17
18impl StateFile {
19    pub fn new(path: PathBuf) -> Self {
20        Self {
21            daemons: Default::default(),
22            disabled: Default::default(),
23            shell_dirs: Default::default(),
24            path,
25        }
26    }
27
28    pub fn get() -> &'static Self {
29        static STATE_FILE: Lazy<StateFile> = Lazy::new(|| {
30            let path = &*env::PITCHFORK_STATE_FILE;
31            StateFile::read(path).unwrap_or_else(|e| {
32                warn!("Could not read state file: {e}, starting fresh");
33                StateFile::new(path.to_path_buf())
34            })
35        });
36        &STATE_FILE
37    }
38
39    pub fn read<P: AsRef<Path>>(path: P) -> Result<Self> {
40        let path = path.as_ref();
41        if !path.exists() {
42            return Ok(Self::new(path.to_path_buf()));
43        }
44        let _lock = xx::fslock::get(path, false)?;
45        let raw = xx::file::read_to_string(path).unwrap_or_else(|e| {
46            warn!("Error reading state file {path:?}: {e}");
47            String::new()
48        });
49        let mut state_file: Self = toml::from_str(&raw).unwrap_or_else(|e| {
50            warn!("Error parsing state file {path:?}: {e}");
51            Self::new(path.to_path_buf())
52        });
53        state_file.path = path.to_path_buf();
54        for (name, daemon) in state_file.daemons.iter_mut() {
55            daemon.id = name.clone();
56        }
57        Ok(state_file)
58    }
59
60    pub fn write(&self) -> Result<()> {
61        let _lock = xx::fslock::get(&self.path, false)?;
62        let raw = toml::to_string(self).map_err(|e| FileError::SerializeError {
63            path: self.path.clone(),
64            source: e,
65        })?;
66        xx::file::write(&self.path, &raw).map_err(|e| FileError::WriteError {
67            path: self.path.clone(),
68            details: Some(e.to_string()),
69        })?;
70        Ok(())
71    }
72}