use {
super::repo,
anyhow::{anyhow, Context, Result},
once_cell::sync::Lazy,
resolve_path::PathResolveExt,
serde::Deserialize,
std::{collections::HashMap, fs::read_to_string, path::PathBuf},
};
const CONFIG_FILENAME: &str = ".commit-emoji.toml"; const GLOBAL_CONFIG: &str = "~/.config/commit-emoji/config.toml";
#[derive(Clone)]
pub struct Config {
pub breaking: bool,
pub commit_types: HashMap<String, String>, pub replacements: HashMap<String, String>,
}
fn map_file_config_to_config(from_file: &FileConfig, from_default: &Config) -> Config {
let mut out_config = from_default.clone();
if let Some(breaking) = from_file.breaking {
out_config.breaking = breaking;
}
if let Some(ref commit_types) = from_file.commit_types {
for t in commit_types {
out_config
.commit_types
.entry(t.name.clone())
.and_modify(|e| *e = t.emoji.clone())
.or_insert(t.emoji.clone());
}
}
if let Some(ref replacements) = from_file.replacements {
for r in replacements {
out_config
.replacements
.entry(r.from.clone())
.and_modify(|e| *e = r.to.clone())
.or_insert(r.to.clone());
}
}
out_config
}
#[derive(Deserialize)]
struct FileConfig {
pub breaking: Option<bool>,
pub commit_types: Option<Vec<EmojiMap>>,
pub replacements: Option<Vec<ReplacementMap>>,
}
#[derive(Deserialize)]
struct EmojiMap {
pub name: String,
pub emoji: String,
#[allow(dead_code)]
pub description: Option<String>,
}
#[derive(Deserialize)]
struct ReplacementMap {
pub from: String,
pub to: String,
}
fn config_from_file(path: &PathBuf) -> Result<FileConfig> {
if path.exists() {
let config_str =
read_to_string(&path).context(format!("Failed to read config file {:?}", &path))?;
let config: FileConfig = toml::from_str(&config_str)
.context(format!("Failed to parse config file {:?}", &path))?;
return Ok(config);
}
return Err(anyhow!("Failed to read config from {}", path.display()));
}
pub fn config() -> Result<Config> {
let mut path = repo::get_absolute_path()?;
path.pop();
path.push(CONFIG_FILENAME);
let file_config = config_from_file(&path)
.or_else(|_| config_from_file(&PathBuf::from(GLOBAL_CONFIG.resolve())))
.or_else(|_| {
Ok::<FileConfig, anyhow::Error>(FileConfig {
breaking: None,
commit_types: None,
replacements: None,
})
})
.unwrap();
Ok(map_file_config_to_config(&file_config, &*DEFAULT_CONFIG))
}
static DEFAULT_CONFIG: Lazy<Config> = Lazy::new(|| Config {
breaking: true,
commit_types: HashMap::from([
("feat".into(), "โจ".into()),
("fix".into(), "๐".into()),
("docs".into(), "๐".into()),
("style".into(), "๐".into()),
("refactor".into(), "๐ฆ".into()),
("perf".into(), "๐".into()),
("test".into(), "๐จ".into()),
("build".into(), "๐ ".into()),
("ci".into(), "โ๏ธ".into()),
("chore".into(), "๐งน".into()),
("revert".into(), "๐".into()),
("deprecated".into(), "โ๏ธ".into()),
("removed".into(), "โ".into()),
("security".into(), "๐".into()),
("merge".into(), "โ๏ธ".into()),
("breaking".into(), "๐".into()),
]),
replacements: HashMap::from([
("initial".into(), "feat: ๐ Initial commit".into()),
("deps".into(), "fix: โซ Update dependencies".into()),
("peerdeps".into(), "fix: โฌ๏ธ Update peer dependencies".into()),
(
"devdeps".into(),
"chore: ๐ผ Update development dependencies".into(),
),
(
"metadata".into(),
"fix: ๐ฆ Update metadata (Cargo.toml)".into(),
),
("typos".into(), "chore: โป๏ธ Fix typos".into()),
]),
});