Skip to main content

yuki_cli/
config.rs

1use std::collections::BTreeMap;
2use std::path::{Path, PathBuf};
3
4use serde::{Deserialize, Serialize};
5
6use crate::error::YukiError;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct AdminEntry {
10    pub domain_id: String,
11    pub admin_id: String,
12}
13
14#[derive(Debug, Serialize, Deserialize)]
15pub struct Config {
16    pub api_key: String,
17    pub default_admin: String,
18    pub administrations: BTreeMap<String, AdminEntry>,
19    /// Counterparty name patterns to ignore in `check unmatched`.
20    /// Matched case-insensitively as substrings against the counterparty name.
21    #[serde(default)]
22    pub unmatched_ignore: Vec<String>,
23}
24
25impl Config {
26    pub fn default_path() -> PathBuf {
27        #[cfg(unix)]
28        {
29            let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
30            PathBuf::from(home).join(".config/yuki/config.toml")
31        }
32        #[cfg(not(unix))]
33        {
34            directories::ProjectDirs::from("nl", "yukiworks", "yuki")
35                .map(|d| d.config_dir().join("config.toml"))
36                .unwrap_or_else(|| PathBuf::from("config.toml"))
37        }
38    }
39
40    pub fn load() -> Result<Self, YukiError> {
41        Self::load_from(&Self::default_path())
42    }
43
44    pub fn load_from(path: &Path) -> Result<Self, YukiError> {
45        let content = std::fs::read_to_string(path)
46            .map_err(|e| YukiError::Config(format!("failed to read {}: {e}", path.display())))?;
47        toml::from_str(&content).map_err(|e| YukiError::Config(format!("invalid config: {e}")))
48    }
49
50    pub fn save_to(&self, path: &Path) -> Result<(), YukiError> {
51        if let Some(parent) = path.parent() {
52            std::fs::create_dir_all(parent)
53                .map_err(|e| YukiError::Config(format!("cannot create config dir: {e}")))?;
54        }
55        let content = toml::to_string_pretty(self)
56            .map_err(|e| YukiError::Config(format!("serialize error: {e}")))?;
57        std::fs::write(path, content)
58            .map_err(|e| YukiError::Config(format!("failed to write {}: {e}", path.display())))
59    }
60
61    pub fn resolve_admin(&self, override_name: Option<&str>) -> Result<AdminEntry, YukiError> {
62        let name = override_name.unwrap_or(&self.default_admin);
63        self.administrations
64            .get(name)
65            .cloned()
66            .ok_or_else(|| YukiError::Config(format!("unknown administration: {name}")))
67    }
68}