use std::path::PathBuf;
use serde::Deserialize;
use crate::error::{Error, Result};
#[derive(Debug, Clone, Deserialize, Default, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub editor: Option<String>,
pub default_priority: Option<String>,
pub filename_format: Option<String>,
pub warn_invalid_files: Option<bool>,
}
pub fn base_dir() -> Result<PathBuf> {
match std::env::var("TASKS_CLI_HOME") {
Ok(dir) if !dir.is_empty() => return Ok(PathBuf::from(dir)),
_ => {}
}
Ok(dirs::home_dir().ok_or(Error::NoHomeDir)?.join(".tasks-cli"))
}
impl Config {
pub fn load() -> Result<Self> {
Self::load_from(&base_dir()?.join("config.toml"))
}
pub fn load_from(path: &std::path::Path) -> Result<Self> {
if !path.exists() {
return Ok(Self::default());
}
let content = std::fs::read_to_string(path)?;
Self::parse(&content)
}
pub fn parse(content: &str) -> Result<Self> {
Ok(toml::from_str(content)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_when_empty() {
let config = Config::parse("").unwrap();
assert_eq!(config, Config::default());
assert!(config.editor.is_none());
}
#[test]
fn user_overrides() {
let config = Config::parse("editor = \"vim\"\ndefault_priority = \"high\"").unwrap();
assert_eq!(config.editor.as_deref(), Some("vim"));
assert_eq!(config.default_priority.as_deref(), Some("high"));
}
#[test]
fn rejects_unknown_fields() {
assert!(Config::parse("nonsense = 1").is_err());
}
#[test]
fn missing_file_yields_defaults() {
let config = Config::load_from(std::path::Path::new("/nonexistent/config.toml")).unwrap();
assert_eq!(config, Config::default());
}
}