use anyhow::{Result, bail};
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::{env, fs};
#[derive(Debug, Serialize)]
pub struct Config {
pub paths: Vec<PathBuf>,
pub display_mode: DisplayMode,
pub color_mode: ColorMode,
}
impl Config {
pub fn try_config() -> Result<Self> {
let config_dir = user_dirs::config_dir()?;
let home_dir = user_dirs::home_dir()?;
let paths = [
config_dir.join("gfold.toml"),
config_dir.join("gfold").join("config.toml"),
home_dir.join(".config").join("gfold.toml"),
];
let path = match paths.into_iter().find(|p| p.exists()) {
Some(path) => path,
None => return Self::try_config_default(),
};
let contents = fs::read_to_string(path)?;
let entry_config = if contents.is_empty() {
EntryConfig::default()
} else {
toml::from_str(&contents)?
};
Self::from_entry_config(&entry_config)
}
pub fn try_config_default() -> Result<Self> {
Self::from_entry_config(&EntryConfig::default())
}
pub fn print(self) -> Result<(), toml::ser::Error> {
print!("{}", toml::to_string_pretty(&self)?);
Ok(())
}
fn from_entry_config(entry_config: &EntryConfig) -> Result<Self> {
if entry_config.path.is_some() && entry_config.paths.is_some() {
bail!("Cannot have both `path` and `paths` in config");
}
Ok(Config {
paths: if let Some(paths) = &entry_config.paths {
paths
.iter()
.map(|p| normalize_path(p))
.collect::<Result<Vec<PathBuf>, _>>()?
} else if let Some(path) = &entry_config.path {
eprintln!(
"WARNING: the `path` configuration option is deprecated. Use `paths` instead."
);
vec![normalize_path(path)?]
} else {
vec![env::current_dir()?.canonicalize()?]
},
display_mode: match &entry_config.display_mode {
Some(display_mode) => *display_mode,
None => DisplayMode::Standard,
},
color_mode: match &entry_config.color_mode {
Some(color_mode) => *color_mode,
None => ColorMode::Always,
},
})
}
}
fn normalize_path(path: &Path) -> Result<PathBuf> {
Ok(match path
.strip_prefix("~")
.or_else(|_| path.strip_prefix("$HOME"))
{
Ok(stripped) => user_dirs::home_dir()?.join(stripped),
Err(_) => path.to_path_buf(),
}
.canonicalize()?)
}
#[derive(Deserialize, Default)]
struct EntryConfig {
pub path: Option<PathBuf>,
pub paths: Option<Vec<PathBuf>>,
pub display_mode: Option<DisplayMode>,
pub color_mode: Option<ColorMode>,
}
#[remain::sorted]
#[derive(Debug, Serialize, Deserialize, Clone, Copy, ValueEnum)]
pub enum DisplayMode {
Classic,
Json,
Standard,
StandardAlphabetical,
}
#[remain::sorted]
#[derive(Debug, Serialize, Deserialize, Clone, Copy, ValueEnum)]
pub enum ColorMode {
Always,
Compatibility,
Never,
}