use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub display: DisplayConfig,
pub general: GeneralConfig,
pub search: SearchConfig,
pub ui: UiConfig,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum AliasExpansion {
#[default]
Name,
Script,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct GeneralConfig {
pub shell_files: Vec<String>,
pub alias_expansion: AliasExpansion,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchConfig {
pub case_matching: CaseMatching,
pub normalize: bool,
pub enable_regex: bool,
pub substring_matching: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CaseMatching {
Ignore,
Smart,
Respect,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiConfig {
pub theme: String,
pub keybind_mode: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisplayConfig {
pub show_type_badges: bool,
pub syntax_highlighting: bool,
pub parse_comments: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
display: DisplayConfig {
parse_comments: true,
show_type_badges: true,
syntax_highlighting: true,
},
general: GeneralConfig::default(),
search: SearchConfig {
case_matching: CaseMatching::Smart,
enable_regex: true,
normalize: true,
substring_matching: true,
},
ui: UiConfig {
keybind_mode: "vim".to_string(),
theme: "default".to_string(),
},
}
}
}
pub fn get_config_path() -> Result<PathBuf> {
let home = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.map_err(|_| anyhow::anyhow!("HOME or USERPROFILE environment variable not set"))?;
let config_dir = PathBuf::from(home).join(".config").join("alf");
Ok(config_dir.join("config.toml"))
}
pub fn load_config() -> Result<Config> {
let path = get_config_path()?;
let content = fs::read_to_string(&path)?;
let config: Config = toml::from_str(&content)?;
Ok(config)
}
pub fn save_config(config: &Config) -> Result<()> {
let path = get_config_path()?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let content = toml::to_string_pretty(config)?;
fs::write(&path, content)?;
Ok(())
}
pub fn is_first_run() -> Result<bool> {
let path = get_config_path()?;
Ok(!path.exists())
}
#[cfg(test)]
mod config_tests;