use core::fmt;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ConformanceClass {
A,
B,
C,
}
impl ConformanceClass {
pub const TOKEN_A: &'static str = "gbp/class-a";
pub const TOKEN_B: &'static str = "gbp/class-b";
pub const TOKEN_C: &'static str = "gbp/class-c";
pub fn tokens(self) -> &'static [&'static str] {
match self {
Self::A => &[Self::TOKEN_A],
Self::B => &[Self::TOKEN_A, Self::TOKEN_B],
Self::C => &[Self::TOKEN_A, Self::TOKEN_B, Self::TOKEN_C],
}
}
pub fn from_tokens<'a>(tokens: impl IntoIterator<Item = &'a str>) -> Option<Self> {
let mut has_a = false;
let mut has_b = false;
let mut has_c = false;
for t in tokens {
match t {
Self::TOKEN_A => has_a = true,
Self::TOKEN_B => has_b = true,
Self::TOKEN_C => has_c = true,
_ => {}
}
}
if has_a && has_b && has_c {
Some(Self::C)
} else if has_a && has_b {
Some(Self::B)
} else if has_a {
Some(Self::A)
} else {
None
}
}
}
impl fmt::Display for ConformanceClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::A => "Class A (GBP+GSP)",
Self::B => "Class B (GBP+GSP+GTP)",
Self::C => "Class C (GBP+GSP+GTP+GAP)",
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tokens_are_cumulative() {
assert_eq!(ConformanceClass::A.tokens(), &["gbp/class-a"]);
assert_eq!(
ConformanceClass::B.tokens(),
&["gbp/class-a", "gbp/class-b"]
);
assert_eq!(
ConformanceClass::C.tokens(),
&["gbp/class-a", "gbp/class-b", "gbp/class-c"]
);
}
#[test]
fn from_tokens_detects_class() {
assert_eq!(
ConformanceClass::from_tokens(["gbp/class-a"]),
Some(ConformanceClass::A)
);
assert_eq!(
ConformanceClass::from_tokens(["gbp/class-a", "gbp/class-b"]),
Some(ConformanceClass::B)
);
assert_eq!(
ConformanceClass::from_tokens(["gbp/class-a", "gbp/class-b", "gbp/class-c"]),
Some(ConformanceClass::C)
);
assert_eq!(ConformanceClass::from_tokens(["something-else"]), None);
}
#[test]
fn class_ordering() {
assert!(ConformanceClass::A < ConformanceClass::B);
assert!(ConformanceClass::B < ConformanceClass::C);
}
}