use std::{cmp, fmt};
use clap::{Parser, ValueEnum};
use colored::Colorize;
use crate::Error;
use super::TopType;
#[derive(Debug, PartialEq, Eq, Clone, ValueEnum, Default, Parser)]
pub enum Hierarchy {
#[default]
Other,
Fix,
Feature,
Breaking,
}
impl Ord for Hierarchy {
fn cmp(&self, other: &Self) -> cmp::Ordering {
match (self, other) {
(Hierarchy::Breaking, Hierarchy::Breaking)
| (Hierarchy::Feature, Hierarchy::Feature)
| (Hierarchy::Fix, Hierarchy::Fix)
| (Hierarchy::Other, Hierarchy::Other) => cmp::Ordering::Equal,
(Hierarchy::Other, _) => cmp::Ordering::Less,
(Hierarchy::Breaking, _) => cmp::Ordering::Greater,
(Hierarchy::Fix, Hierarchy::Other) => cmp::Ordering::Greater,
(Hierarchy::Fix, Hierarchy::Feature) | (Hierarchy::Fix, Hierarchy::Breaking) => {
cmp::Ordering::Less
}
(Hierarchy::Feature, Hierarchy::Other) | (Hierarchy::Feature, Hierarchy::Fix) => {
cmp::Ordering::Greater
}
(Hierarchy::Feature, Hierarchy::Breaking) => cmp::Ordering::Less,
}
}
}
impl PartialOrd for Hierarchy {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Hierarchy {
#[allow(missing_docs)]
pub fn parse(s: &str) -> Result<Hierarchy, Error> {
Ok(match s.to_lowercase().as_str() {
"breaking" => Hierarchy::Breaking,
"feat" => Hierarchy::Feature,
"fix" => Hierarchy::Fix,
"revert" => Hierarchy::Fix,
_ => Hierarchy::Other,
})
}
}
impl fmt::Display for Hierarchy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Hierarchy::Breaking => write!(f, "{}", "[Major]".red()),
Hierarchy::Feature => write!(f, "{}", "[Minor]".yellow()),
Hierarchy::Fix => write!(f, "{}", "[Patch]".green()),
Hierarchy::Other => write!(f, "{}", "[Patch]".white()),
}
}
}
impl From<&TopType> for Hierarchy {
fn from(t: &TopType) -> Self {
match t {
TopType::Breaking => Hierarchy::Breaking,
TopType::Feature => Hierarchy::Feature,
TopType::Fix => Hierarchy::Fix,
TopType::Other => Hierarchy::Other,
TopType::None => Hierarchy::Other,
}
}
}
impl AsRef<Hierarchy> for Hierarchy {
fn as_ref(&self) -> &Hierarchy {
self
}
}
#[cfg(test)]
mod test {
use super::Hierarchy;
use colored::Colorize;
use rstest::rstest;
fn level_hierarchy_example_lowest() -> Hierarchy {
Hierarchy::Other
}
fn level_hierarchy_example_highest() -> Hierarchy {
Hierarchy::Breaking
}
fn level_hierarchy_example_mid() -> Hierarchy {
Hierarchy::Feature
}
#[test]
fn test_partial_eq() {
let one = level_hierarchy_example_highest();
let two = level_hierarchy_example_highest();
assert_eq!(one, two);
}
#[test]
fn test_eq() {
let one = level_hierarchy_example_lowest();
let two = level_hierarchy_example_lowest();
assert!(one == two);
}
#[test]
fn test_partial_ord() {
let one = level_hierarchy_example_highest();
let two = level_hierarchy_example_mid();
assert!(one > two);
}
#[test]
fn test_ord() {
let one = level_hierarchy_example_lowest();
let two = level_hierarchy_example_mid();
assert_eq!(two.cmp(&one), std::cmp::Ordering::Greater);
}
#[rstest]
#[case::breaking(Hierarchy::Breaking, format!("{}","[Major]".red()))]
#[case::non_production(Hierarchy::Feature, format!("{}","[Minor]".yellow()))]
#[case::production(Hierarchy::Fix, format!("{}","[Patch]".green()))]
#[case::release(Hierarchy::Other, format!("{}","[Patch]".white()))]
fn display_value(#[case] test: Hierarchy, #[case] expected: String) {
assert_eq!(expected, test.to_string());
}
#[rstest]
#[case::feature("feat", Hierarchy::Feature)]
#[case::fix("fix", Hierarchy::Fix)]
#[case::revert("revert", Hierarchy::Fix)]
#[case::docs("docs", Hierarchy::Other)]
#[case::style("style", Hierarchy::Other)]
#[case::refactor("refactor", Hierarchy::Other)]
#[case::perf("perf", Hierarchy::Other)]
#[case::test("test", Hierarchy::Other)]
#[case::chore("chore", Hierarchy::Other)]
#[case::breaking("breaking", Hierarchy::Breaking)]
#[case::build("build", Hierarchy::Other)]
#[case::ci("ci", Hierarchy::Other)]
fn parse_conventional_commit_label_to_level_hierarchy(
#[case] label: &str,
#[case] expected: Hierarchy,
) {
let test_level = Hierarchy::parse(label).unwrap();
assert_eq!(expected, test_level);
}
}