use crate::filters::SortBy;
use crate::{Error, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub display: DisplayConfig,
#[serde(default)]
pub filters: FilterConfig,
#[serde(default)]
pub performance: PerformanceConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisplayConfig {
#[serde(default)]
pub unicode: bool,
#[serde(default = "default_true")]
pub show_size: bool,
#[serde(default)]
pub show_lines: bool,
#[serde(default)]
pub dir_sizes: bool,
#[serde(default = "default_true")]
pub total_size: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FilterConfig {
#[serde(default)]
pub show_hidden: bool,
#[serde(default)]
pub gitignore: bool,
#[serde(default)]
pub max_depth: Option<usize>,
#[serde(default)]
pub max_dirs: Option<usize>,
#[serde(default)]
pub max_files: Option<usize>,
#[serde(default)]
pub sort_by: Option<SortBy>,
#[serde(default)]
pub reverse_sort: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
#[serde(default)]
pub threads: usize,
#[serde(default = "default_max_file_size")]
pub max_file_size: u64,
}
impl Default for DisplayConfig {
fn default() -> Self {
Self {
unicode: true,
show_size: true,
show_lines: false,
dir_sizes: false,
total_size: true,
}
}
}
impl Default for PerformanceConfig {
fn default() -> Self {
Self {
threads: 0,
max_file_size: 1_073_741_824, }
}
}
impl Config {
pub fn load() -> Result<Self> {
let config_path = get_config_path()?;
if !config_path.exists() {
log::debug!("No config file found at {:?}, using defaults", config_path);
return Ok(Self::default());
}
log::debug!("Loading config from {:?}", config_path);
let content = fs::read_to_string(&config_path)
.map_err(|e| Error::config(format!("Failed to read config file: {}", e)))?;
let config: Config = toml::from_str(&content)?;
Ok(config)
}
pub fn save(&self) -> Result<()> {
let config_path = get_config_path()?;
if let Some(parent) = config_path.parent() {
fs::create_dir_all(parent)
.map_err(|e| Error::config(format!("Failed to create config directory: {}", e)))?;
}
let content = toml::to_string_pretty(self)
.map_err(|e| Error::config(format!("Failed to serialize config: {}", e)))?;
fs::write(&config_path, content)
.map_err(|e| Error::config(format!("Failed to write config file: {}", e)))?;
Ok(())
}
}
fn get_config_path() -> Result<PathBuf> {
let home =
dirs::home_dir().ok_or_else(|| Error::config("Could not determine home directory"))?;
Ok(home.join(".maram.toml"))
}
fn default_true() -> bool {
true
}
fn default_max_file_size() -> u64 {
1_073_741_824 }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Config::default();
assert!(config.display.show_size);
assert!(config.display.unicode);
assert_eq!(config.performance.max_file_size, 1_073_741_824);
}
#[test]
fn test_config_serialization() {
let config = Config::default();
let toml_str = toml::to_string(&config).unwrap();
let parsed: Config = toml::from_str(&toml_str).unwrap();
assert_eq!(config.display.show_size, parsed.display.show_size);
assert_eq!(config.filters.gitignore, parsed.filters.gitignore);
}
}