use anyhow::{Context, Result};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
pub fn config_dir() -> Result<PathBuf> {
let dir = dirs::config_dir()
.context("could not determine user config directory")?
.join("ddctui");
fs::create_dir_all(&dir)
.with_context(|| format!("failed to create config directory {}", dir.display()))?;
Ok(dir)
}
#[derive(Debug, Default)]
pub struct Favorites {
path: PathBuf,
entries: HashMap<String, Vec<u8>>,
}
impl Favorites {
pub fn load(dir: &Path) -> Result<Self> {
let path = dir.join("favorites.json");
let entries = match fs::read_to_string(&path) {
Ok(contents) => serde_json::from_str(&contents)
.with_context(|| format!("failed to parse {}", path.display()))?,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => HashMap::new(),
Err(e) => return Err(e).with_context(|| format!("failed to read {}", path.display())),
};
Ok(Self { path, entries })
}
pub fn is_favorite(&self, monitor_key: &str, feature_code: u8) -> bool {
self.entries
.get(monitor_key)
.is_some_and(|codes| codes.contains(&feature_code))
}
pub fn toggle(&mut self, monitor_key: &str, feature_code: u8) {
let codes = self.entries.entry(monitor_key.to_string()).or_default();
if let Some(pos) = codes.iter().position(|&c| c == feature_code) {
codes.remove(pos);
if codes.is_empty() {
self.entries.remove(monitor_key);
}
} else {
codes.push(feature_code);
}
}
pub fn save(&self) -> Result<()> {
let contents = serde_json::to_string_pretty(&self.entries)?;
fs::write(&self.path, contents)
.with_context(|| format!("failed to write {}", self.path.display()))
}
}