use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum QueueItemLifecycle {
Live,
Struck,
}
impl QueueItemLifecycle {
pub fn rank(self) -> u8 {
match self {
QueueItemLifecycle::Live => 0,
QueueItemLifecycle::Struck => 1,
}
}
pub fn join(self, other: QueueItemLifecycle) -> QueueItemLifecycle {
if other.rank() > self.rank() {
other
} else {
self
}
}
pub fn classify(item: &str) -> QueueItemLifecycle {
let first = item.lines().next().unwrap_or("");
if first.contains("~~") {
QueueItemLifecycle::Struck
} else {
QueueItemLifecycle::Live
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rank_orders_live_before_struck() {
assert!(QueueItemLifecycle::Live.rank() < QueueItemLifecycle::Struck.rank());
assert!(QueueItemLifecycle::Live < QueueItemLifecycle::Struck);
}
#[test]
fn join_is_monotonic_max() {
use QueueItemLifecycle::*;
assert_eq!(Live.join(Live), Live);
assert_eq!(Live.join(Struck), Struck);
assert_eq!(Struck.join(Live), Struck, "struck never regresses to live");
assert_eq!(Struck.join(Struck), Struck);
}
#[test]
fn join_is_idempotent_and_commutative() {
use QueueItemLifecycle::*;
for &a in &[Live, Struck] {
assert_eq!(a.join(a), a, "idempotent");
for &b in &[Live, Struck] {
assert_eq!(a.join(b), b.join(a), "commutative");
}
}
}
#[test]
fn classify_detects_strike_on_head_line() {
assert_eq!(
QueueItemLifecycle::classify("- do [#abcd] fix"),
QueueItemLifecycle::Live
);
assert_eq!(
QueueItemLifecycle::classify("- ~~do [#abcd] fix~~"),
QueueItemLifecycle::Struck
);
assert_eq!(
QueueItemLifecycle::classify("- ~~do [#abcd] fix~~\n note line"),
QueueItemLifecycle::Struck
);
assert_eq!(
QueueItemLifecycle::classify("- do [#abcd] fix\n ~~old note~~"),
QueueItemLifecycle::Live
);
}
}