diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..0c2fa32 --- /dev/null +++ b/src/model.rs @@ -0,0 +1,64 @@ +use chrono::{DateTime, Utc}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Priority { + Low, + Medium, + High, + Critical, +} + +impl Default for Priority { + fn default() -> Self { + Self::Medium + } +} + +impl std::fmt::Display for Priority { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Low => write!(f, "low"), + Self::Medium => write!(f, "med"), + Self::High => write!(f, "high"), + Self::Critical => write!(f, "CRIT"), + } + } +} + +impl std::str::FromStr for Priority { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "low" | "l" | "1" => Ok(Self::Low), + "medium" | "med" | "m" | "2" => Ok(Self::Medium), + "high" | "hi" | "h" | "3" => Ok(Self::High), + "critical" | "crit" | "c" | "4" => Ok(Self::Critical), + _ => anyhow::bail!("invalid priority: {s} (use low/medium/high/critical)"), + } + } +} + +#[derive(Debug, Clone)] +pub struct Todo { + pub id: i64, + pub text: String, + pub priority: Priority, + pub project: Option, + pub tags: Vec, + pub created_at: DateTime, + pub completed_at: Option>, +} + +impl Todo { + pub fn is_done(&self) -> bool { + self.completed_at.is_some() + } + + /// short project name (last path component) + pub fn project_short(&self) -> Option<&str> { + self.project + .as_deref() + .and_then(|p| p.rsplit('/').next()) + } +}