Skip to main content

aven_core/
choices.rs

1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct InvalidTaskStatus(String);
5
6impl fmt::Display for InvalidTaskStatus {
7    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
8        write!(
9            formatter,
10            "error invalid-status input={} choices={}",
11            self.0,
12            STATUSES.join(",")
13        )
14    }
15}
16
17impl std::error::Error for InvalidTaskStatus {}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct InvalidTaskPriority(String);
21
22impl fmt::Display for InvalidTaskPriority {
23    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
24        write!(
25            formatter,
26            "error invalid-priority input={} choices={}",
27            self.0,
28            PRIORITIES.join(",")
29        )
30    }
31}
32
33impl std::error::Error for InvalidTaskPriority {}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub enum TaskStatus {
37    Inbox,
38    Backlog,
39    Todo,
40    Active,
41    Done,
42    Canceled,
43}
44
45impl TaskStatus {
46    #[allow(dead_code)]
47    pub const OPEN: [Self; 4] = [Self::Inbox, Self::Backlog, Self::Todo, Self::Active];
48    #[allow(dead_code)]
49    pub const TERMINAL: [Self; 2] = [Self::Done, Self::Canceled];
50
51    pub const fn is_open(self) -> bool {
52        matches!(
53            self,
54            Self::Inbox | Self::Backlog | Self::Todo | Self::Active
55        )
56    }
57
58    #[allow(dead_code)]
59    pub const fn is_terminal(self) -> bool {
60        matches!(self, Self::Done | Self::Canceled)
61    }
62
63    pub const fn as_str(self) -> &'static str {
64        match self {
65            Self::Inbox => "inbox",
66            Self::Backlog => "backlog",
67            Self::Todo => "todo",
68            Self::Active => "active",
69            Self::Done => "done",
70            Self::Canceled => "canceled",
71        }
72    }
73
74    pub fn parse(value: &str) -> Result<Self, InvalidTaskStatus> {
75        match value {
76            "inbox" => Ok(Self::Inbox),
77            "backlog" => Ok(Self::Backlog),
78            "todo" => Ok(Self::Todo),
79            "active" => Ok(Self::Active),
80            "done" => Ok(Self::Done),
81            "canceled" => Ok(Self::Canceled),
82            _ => Err(InvalidTaskStatus(value.to_string())),
83        }
84    }
85}
86
87impl std::fmt::Display for TaskStatus {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        f.write_str(self.as_str())
90    }
91}
92
93impl TryFrom<&str> for TaskStatus {
94    type Error = InvalidTaskStatus;
95
96    fn try_from(value: &str) -> Result<Self, Self::Error> {
97        Self::parse(value)
98    }
99}
100
101pub const STATUSES: &[&str] = &[
102    TaskStatus::Inbox.as_str(),
103    TaskStatus::Backlog.as_str(),
104    TaskStatus::Todo.as_str(),
105    TaskStatus::Active.as_str(),
106    TaskStatus::Done.as_str(),
107    TaskStatus::Canceled.as_str(),
108];
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub enum TaskPriority {
112    None,
113    Low,
114    Medium,
115    High,
116    Urgent,
117}
118
119impl TaskPriority {
120    pub const ALL: [Self; 5] = [
121        Self::None,
122        Self::Low,
123        Self::Medium,
124        Self::High,
125        Self::Urgent,
126    ];
127
128    pub const fn as_str(self) -> &'static str {
129        match self {
130            Self::None => "none",
131            Self::Low => "low",
132            Self::Medium => "medium",
133            Self::High => "high",
134            Self::Urgent => "urgent",
135        }
136    }
137
138    pub fn parse(value: &str) -> Result<Self, InvalidTaskPriority> {
139        match value {
140            "none" => Ok(Self::None),
141            "low" => Ok(Self::Low),
142            "medium" => Ok(Self::Medium),
143            "high" => Ok(Self::High),
144            "urgent" => Ok(Self::Urgent),
145            _ => Err(InvalidTaskPriority(value.to_string())),
146        }
147    }
148}
149
150impl std::fmt::Display for TaskPriority {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        f.write_str(self.as_str())
153    }
154}
155
156impl TryFrom<&str> for TaskPriority {
157    type Error = InvalidTaskPriority;
158
159    fn try_from(value: &str) -> Result<Self, Self::Error> {
160        Self::parse(value)
161    }
162}
163
164pub const PRIORITIES: &[&str] = &[
165    TaskPriority::None.as_str(),
166    TaskPriority::Low.as_str(),
167    TaskPriority::Medium.as_str(),
168    TaskPriority::High.as_str(),
169    TaskPriority::Urgent.as_str(),
170];
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn status_and_priority_parse_display_and_reject_invalid_values() {
178        assert_eq!(TaskStatus::parse("active").unwrap(), TaskStatus::Active);
179        assert_eq!(TaskStatus::Active.as_str(), "active");
180        assert_eq!(TaskStatus::Active.to_string(), "active");
181        assert_eq!(
182            TaskStatus::parse("blocked").unwrap_err().to_string(),
183            "error invalid-status input=blocked choices=inbox,backlog,todo,active,done,canceled"
184        );
185
186        assert_eq!(TaskPriority::parse("urgent").unwrap(), TaskPriority::Urgent);
187        assert_eq!(TaskPriority::Urgent.as_str(), "urgent");
188        assert_eq!(TaskPriority::Urgent.to_string(), "urgent");
189        assert_eq!(
190            TaskPriority::parse("soon").unwrap_err().to_string(),
191            "error invalid-priority input=soon choices=none,low,medium,high,urgent"
192        );
193    }
194}