mod conventional;
mod gitmoji;
use clap::ValueEnum;
use color_eyre::eyre::{Result, WrapErr, bail};
#[derive(Debug, Clone, PartialEq, ValueEnum)]
pub enum Convention {
Conventional,
Gitmoji,
}
pub async fn resolve_convention(convention: Option<Convention>) -> Result<Convention> {
if let Some(c) = convention {
return Ok(c);
}
let num_commits_for_detection = 10;
let messages = crate::jj::fetch_commit_messages(num_commits_for_detection)
.await
.wrap_err("Error fetching commits")?;
detect_convention(&messages).wrap_err("Error detecting convention")
}
fn detect_convention(messages: &[String]) -> Result<Convention> {
let conventional_count = messages
.iter()
.filter(|m| conventional::is_conventional(m))
.count();
let gitmoji_count = messages.iter().filter(|m| gitmoji::is_gitmoji(m)).count();
match (conventional_count, gitmoji_count) {
(0, 0) => {
bail!("No commit adheres to a known convention (conventional commits or gitmoji).")
}
(c, g) if c == g => bail!(
"Cannot detect convention: tie between conventional commits ({c}) and gitmoji ({g})."
),
(c, g) if c > g => Ok(Convention::Conventional),
_ => Ok(Convention::Gitmoji),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_no_convention() {
let msgs = vec!["plain message".to_string(), "another plain".to_string()];
assert!(detect_convention(&msgs).is_err());
}
#[test]
fn test_detect_tie() {
let msgs = vec![
"feat: something".to_string(),
":sparkles: something".to_string(),
];
assert!(detect_convention(&msgs).is_err());
}
#[test]
fn test_detect_conventional_wins() {
let msgs = vec![
"feat: a".to_string(),
"fix: b".to_string(),
":sparkles: c".to_string(),
];
assert_eq!(detect_convention(&msgs).unwrap(), Convention::Conventional);
}
#[test]
fn test_detect_gitmoji_wins() {
let msgs = vec![
":sparkles: a".to_string(),
"🎉 b".to_string(),
"feat: c".to_string(),
];
assert_eq!(detect_convention(&msgs).unwrap(), Convention::Gitmoji);
}
}