use super::schema::ENUMS;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Variant {
pub enum_id: u32,
pub ordinal: i64,
}
impl Variant {
#[must_use]
pub fn name(self) -> Option<&'static str> {
let variants = ENUMS
.get(usize::try_from(self.enum_id).ok()?.checked_sub(1)?)?
.1;
usize::try_from(self.ordinal)
.ok()
.and_then(|i| variants.get(i))
.copied()
}
#[must_use]
pub fn enum_name(self) -> Option<&'static str> {
Some(
ENUMS
.get(usize::try_from(self.enum_id).ok()?.checked_sub(1)?)?
.0,
)
}
}
impl std::fmt::Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.name() {
Some(n) => f.write_str(n),
None => write!(f, "{}?{}", self.enum_name().unwrap_or("enum"), self.ordinal),
}
}
}
macro_rules! row_enum {
($(#[$outer:meta])* $name:ident = $id:expr, $($variant:ident => $spelling:literal),+ $(,)?) => {
$(#[$outer])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum $name { $($variant),+ }
impl $name {
pub const ENUM_ID: u32 = $id;
pub const ALL: &'static [Self] = &[$(Self::$variant),+];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self { $(Self::$variant => $spelling),+ }
}
#[must_use]
pub fn ordinal(self) -> u32 {
Self::ALL.iter().position(|v| *v == self)
.and_then(|i| u32::try_from(i).ok())
.unwrap_or(0)
}
#[must_use]
pub fn from_ordinal(ordinal: i64) -> Option<Self> {
usize::try_from(ordinal).ok().and_then(|i| Self::ALL.get(i)).copied()
}
#[must_use]
pub fn from_variant(v: Variant) -> Option<Self> {
(v.enum_id == Self::ENUM_ID).then(|| Self::from_ordinal(v.ordinal)).flatten()
}
#[must_use]
pub fn parse(name: &str) -> Option<Self> {
Self::ALL.iter().copied().find(|v| v.as_str() == name)
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
};
}
row_enum! {
Grade = 1,
None => "none",
Weak => "weak",
Moderate => "moderate",
Strong => "strong",
Identical => "identical",
}
row_enum! {
Channel = 2,
Copies => "copies",
Twins => "twins",
Shapes => "shapes",
Any => "any",
}
row_enum! {
Unit = 3,
File => "file",
Function => "function",
Match => "match",
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Polarity {
Distance,
Gap,
}
pub const DISTANCE_BANDS: &[(Grade, f64)] = &[
(Grade::Identical, 0.05),
(Grade::Strong, 0.25),
(Grade::Moderate, 0.50),
(Grade::Weak, 0.75),
];
pub const GAP_BANDS: &[(Grade, f64)] = &[
(Grade::Strong, 0.45),
(Grade::Moderate, 0.30),
(Grade::Weak, 0.15),
];
impl Channel {
#[must_use]
pub const fn metric(self) -> &'static str {
match self {
Self::Copies => "bytes",
Self::Twins => "echo",
Self::Shapes => "structure",
Self::Any => "fused",
}
}
#[must_use]
pub const fn polarity(self) -> Polarity {
match self {
Self::Twins => Polarity::Gap,
_ => Polarity::Distance,
}
}
#[must_use]
pub const fn admits(self) -> &'static str {
match self.polarity() {
Polarity::Gap => "--min-echo",
Polarity::Distance => "--max-distance",
}
}
#[must_use]
pub fn parse_any(name: &str) -> Option<Self> {
Self::parse(name).or_else(|| Self::ALL.iter().copied().find(|c| c.metric() == name))
}
}
impl Grade {
#[must_use]
pub fn band(score: f64, channel: Channel) -> Self {
match channel.polarity() {
Polarity::Distance => DISTANCE_BANDS
.iter()
.find(|(_, hi)| score <= *hi)
.map_or(Self::None, |(g, _)| *g),
Polarity::Gap => GAP_BANDS
.iter()
.find(|(_, lo)| score >= *lo)
.map_or(Self::None, |(g, _)| *g),
}
}
#[must_use]
pub fn admits(self, floor: Self) -> bool {
self >= floor
}
}