use std::{
cmp::Ordering,
fmt::{Display, Formatter, Result as FmtResult},
str::FromStr,
};
use eyre::{Error, Result, eyre};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TaskStatus {
Done,
InProgress,
#[default]
Todo,
}
impl Display for TaskStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Done => write!(f, "done"),
Self::InProgress => write!(f, "in_progress"),
Self::Todo => write!(f, "todo"),
}
}
}
impl FromStr for TaskStatus {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().as_str() {
"done" => Ok(Self::Done),
"in_progress" => Ok(Self::InProgress),
"todo" => Ok(Self::Todo),
_ => Err(eyre!(format!("invalid task status: {}", s))),
}
}
}
impl Ord for TaskStatus {
fn cmp(&self, other: &Self) -> Ordering {
let self_priority = match self {
TaskStatus::InProgress => 0,
TaskStatus::Todo => 1,
TaskStatus::Done => 2,
};
let other_priority = match other {
TaskStatus::InProgress => 0,
TaskStatus::Todo => 1,
TaskStatus::Done => 2,
};
self_priority.cmp(&other_priority)
}
}
impl PartialOrd for TaskStatus {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(test)]
mod tests {
use super::*;
mod display {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_displays_correctly() {
assert_eq!(TaskStatus::Done.to_string(), "done");
assert_eq!(TaskStatus::InProgress.to_string(), "in_progress");
assert_eq!(TaskStatus::Todo.to_string(), "todo");
}
}
mod from_str {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn it_parses_correctly() {
assert_eq!(TaskStatus::from_str("done").unwrap(), TaskStatus::Done);
assert_eq!(
TaskStatus::from_str("in_progress").unwrap(),
TaskStatus::InProgress
);
assert_eq!(TaskStatus::from_str("todo").unwrap(), TaskStatus::Todo);
}
#[test]
fn it_errors_on_invalid_input() {
assert!(TaskStatus::from_str("invalid").is_err());
}
#[test]
fn it_parses_case_insensitively() {
for str in &["TODO", "Todo", "ToDo"] {
assert_eq!(TaskStatus::from_str(str).unwrap(), TaskStatus::Todo);
}
}
}
mod ordering {
use super::*;
#[test]
fn it_orders_correctly() {
assert!(TaskStatus::InProgress < TaskStatus::Todo);
assert!(TaskStatus::Todo < TaskStatus::Done);
assert!(TaskStatus::Done > TaskStatus::InProgress);
}
}
}