use anyhow::{Context, Result, anyhow};
use serde::Deserialize;
use std::path::PathBuf;
#[derive(Debug, Default, Deserialize)]
#[serde(default)]
pub struct Config {
pub keys: KeysConfig,
}
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct KeysConfig {
#[serde(default = "default_esc_navigates_back")]
pub esc_navigates_back: bool,
}
fn default_esc_navigates_back() -> bool {
true
}
impl Default for KeysConfig {
fn default() -> Self {
Self {
esc_navigates_back: default_esc_navigates_back(),
}
}
}
impl Config {
pub fn load() -> Result<Self> {
let Ok(path) = Self::path() else {
log::info!("Configuration path couldn't be determined. Using defaults.");
return Ok(Config::default());
};
let contents = match std::fs::read_to_string(&path) {
Ok(c) => c,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
log::info!(
"Configuration not loaded from {}: file not found. Using defaults.",
path.display()
);
return Ok(Config::default());
}
Err(e) => {
return Err(e)
.with_context(|| format!("Failed to read config at {}", path.display()));
}
};
toml::from_str(&contents)
.with_context(|| format!("Failed to parse config at {}", path.display()))
}
pub fn default_file_content() -> &'static str {
concat!(
"# dua-cli configuration\n",
"#\n",
"[keys]\n",
"# If true, pressing <Esc> in the main pane ascends to the parent directory.\n",
"# If false, <Esc> follows the default quit behavior.\n",
"esc_navigates_back = true\n",
)
}
pub fn path() -> Result<PathBuf> {
let config_dir = dirs::config_dir()
.ok_or_else(|| anyhow!("platform config directory is unavailable"))?;
Ok(config_dir.join("dua-cli").join("config.toml"))
}
}