Skip to main content

worktree_io/config/
ops.rs

1use anyhow::{Context, Result};
2use std::path::PathBuf;
3
4use super::Config;
5
6impl Config {
7    /// Return the path to the config file (`~/.config/worktree/config.toml`).
8    ///
9    /// # Errors
10    ///
11    /// Returns an error if the home directory cannot be determined.
12    pub fn path() -> Result<PathBuf> {
13        let home = dirs::home_dir().context("Could not determine home directory")?;
14        Ok(home.join(".config").join("worktree").join("config.toml"))
15    }
16
17    /// Load config from disk, returning `Default` if the file does not yet exist.
18    ///
19    /// # Errors
20    ///
21    /// Returns an error if the file cannot be read or parsed.
22    pub fn load() -> Result<Self> {
23        let path = Self::path()?;
24        if !path.exists() {
25            return Ok(Self::default());
26        }
27        // LLVM_COV_EXCL_START
28        let content = std::fs::read_to_string(&path)
29            .with_context(|| format!("Failed to read config from {}", path.display()))?;
30        let config: Self = toml::from_str(&content)
31            .with_context(|| format!("Failed to parse config at {}", path.display()))?;
32        Ok(config)
33        // LLVM_COV_EXCL_STOP
34    }
35
36    /// Persist the current config to disk.
37    ///
38    /// # Errors
39    ///
40    /// Returns an error if the config directory cannot be created or the file
41    /// cannot be written.
42    pub fn save(&self) -> Result<()> {
43        // LLVM_COV_EXCL_LINE
44        // LLVM_COV_EXCL_START
45        let path = Self::path()?;
46        if let Some(parent) = path.parent() {
47            std::fs::create_dir_all(parent)
48                .with_context(|| format!("Failed to create config dir {}", parent.display()))?;
49        }
50        let content = self.to_toml_with_comments();
51        std::fs::write(&path, content)
52            .with_context(|| format!("Failed to write config to {}", path.display()))?;
53        Ok(())
54        // LLVM_COV_EXCL_STOP
55    }
56
57    /// Get a config value by dot-separated key path
58    ///
59    /// # Errors
60    ///
61    /// Returns an error if `key` is not a recognised config key.
62    pub fn get_value(&self, key: &str) -> Result<String> {
63        match key {
64            "editor.command" => Ok(self.editor.command.clone().unwrap_or_default()),
65            "open.editor" => Ok(self.open.editor.to_string()),
66            _ => anyhow::bail!("Unknown config key: {key}"),
67        }
68    }
69
70    /// Set a config value by dot-separated key path
71    ///
72    /// # Errors
73    ///
74    /// Returns an error if `key` is not a recognised config key or if the
75    /// value cannot be parsed (e.g. a non-boolean for `open.editor`).
76    pub fn set_value(&mut self, key: &str, value: &str) -> Result<()> {
77        match key {
78            "editor.command" => {
79                self.editor.command = if value.is_empty() {
80                    None
81                } else {
82                    Some(value.to_string())
83                };
84            }
85            "open.editor" => {
86                self.open.editor = value
87                    .parse::<bool>()
88                    .with_context(|| format!("Invalid boolean value: {value}"))?;
89            }
90            _ => anyhow::bail!("Unknown config key: {key}"),
91        }
92        Ok(())
93    }
94}