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, PartialEq, Eq)]
36pub struct InvalidTaskSource(String);
37
38impl fmt::Display for InvalidTaskSource {
39    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(
41            formatter,
42            "error invalid-task-source input={} choices={}",
43            self.0,
44            TASK_SOURCES.join(",")
45        )
46    }
47}
48
49impl std::error::Error for InvalidTaskSource {}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
52pub enum TaskSource {
53    Cli,
54    Tui,
55    Api,
56    #[default]
57    Unknown,
58}
59
60impl TaskSource {
61    pub const fn as_str(self) -> &'static str {
62        match self {
63            Self::Cli => "cli",
64            Self::Tui => "tui",
65            Self::Api => "api",
66            Self::Unknown => "unknown",
67        }
68    }
69
70    pub fn parse(value: &str) -> Result<Self, InvalidTaskSource> {
71        match value {
72            "cli" => Ok(Self::Cli),
73            "tui" => Ok(Self::Tui),
74            "api" => Ok(Self::Api),
75            "unknown" => Ok(Self::Unknown),
76            _ => Err(InvalidTaskSource(value.to_string())),
77        }
78    }
79}
80
81impl fmt::Display for TaskSource {
82    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
83        formatter.write_str(self.as_str())
84    }
85}
86
87impl TryFrom<&str> for TaskSource {
88    type Error = InvalidTaskSource;
89
90    fn try_from(value: &str) -> Result<Self, Self::Error> {
91        Self::parse(value)
92    }
93}
94
95pub const TASK_SOURCES: &[&str] = &[
96    TaskSource::Cli.as_str(),
97    TaskSource::Tui.as_str(),
98    TaskSource::Api.as_str(),
99    TaskSource::Unknown.as_str(),
100];
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103pub enum TaskStatus {
104    Inbox,
105    Backlog,
106    Todo,
107    Active,
108    Done,
109    Canceled,
110}
111
112impl TaskStatus {
113    #[allow(dead_code)]
114    pub const OPEN: [Self; 4] = [Self::Inbox, Self::Backlog, Self::Todo, Self::Active];
115    #[allow(dead_code)]
116    pub const TERMINAL: [Self; 2] = [Self::Done, Self::Canceled];
117
118    pub const fn is_open(self) -> bool {
119        matches!(
120            self,
121            Self::Inbox | Self::Backlog | Self::Todo | Self::Active
122        )
123    }
124
125    #[allow(dead_code)]
126    pub const fn is_terminal(self) -> bool {
127        matches!(self, Self::Done | Self::Canceled)
128    }
129
130    pub const fn as_str(self) -> &'static str {
131        match self {
132            Self::Inbox => "inbox",
133            Self::Backlog => "backlog",
134            Self::Todo => "todo",
135            Self::Active => "active",
136            Self::Done => "done",
137            Self::Canceled => "canceled",
138        }
139    }
140
141    pub fn parse(value: &str) -> Result<Self, InvalidTaskStatus> {
142        match value {
143            "inbox" => Ok(Self::Inbox),
144            "backlog" => Ok(Self::Backlog),
145            "todo" => Ok(Self::Todo),
146            "active" => Ok(Self::Active),
147            "done" => Ok(Self::Done),
148            "canceled" => Ok(Self::Canceled),
149            _ => Err(InvalidTaskStatus(value.to_string())),
150        }
151    }
152}
153
154impl std::fmt::Display for TaskStatus {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        f.write_str(self.as_str())
157    }
158}
159
160impl TryFrom<&str> for TaskStatus {
161    type Error = InvalidTaskStatus;
162
163    fn try_from(value: &str) -> Result<Self, Self::Error> {
164        Self::parse(value)
165    }
166}
167
168pub const STATUSES: &[&str] = &[
169    TaskStatus::Inbox.as_str(),
170    TaskStatus::Backlog.as_str(),
171    TaskStatus::Todo.as_str(),
172    TaskStatus::Active.as_str(),
173    TaskStatus::Done.as_str(),
174    TaskStatus::Canceled.as_str(),
175];
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
178pub enum TaskPriority {
179    None,
180    Low,
181    Medium,
182    High,
183    Urgent,
184}
185
186impl TaskPriority {
187    pub const ALL: [Self; 5] = [
188        Self::None,
189        Self::Low,
190        Self::Medium,
191        Self::High,
192        Self::Urgent,
193    ];
194
195    pub const fn as_str(self) -> &'static str {
196        match self {
197            Self::None => "none",
198            Self::Low => "low",
199            Self::Medium => "medium",
200            Self::High => "high",
201            Self::Urgent => "urgent",
202        }
203    }
204
205    pub fn parse(value: &str) -> Result<Self, InvalidTaskPriority> {
206        match value {
207            "none" => Ok(Self::None),
208            "low" => Ok(Self::Low),
209            "medium" => Ok(Self::Medium),
210            "high" => Ok(Self::High),
211            "urgent" => Ok(Self::Urgent),
212            _ => Err(InvalidTaskPriority(value.to_string())),
213        }
214    }
215}
216
217impl std::fmt::Display for TaskPriority {
218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219        f.write_str(self.as_str())
220    }
221}
222
223impl TryFrom<&str> for TaskPriority {
224    type Error = InvalidTaskPriority;
225
226    fn try_from(value: &str) -> Result<Self, Self::Error> {
227        Self::parse(value)
228    }
229}
230
231pub const PRIORITIES: &[&str] = &[
232    TaskPriority::None.as_str(),
233    TaskPriority::Low.as_str(),
234    TaskPriority::Medium.as_str(),
235    TaskPriority::High.as_str(),
236    TaskPriority::Urgent.as_str(),
237];
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    #[test]
244    fn status_and_priority_parse_display_and_reject_invalid_values() {
245        assert_eq!(TaskSource::parse("tui").unwrap(), TaskSource::Tui);
246        assert_eq!(TaskSource::Api.as_str(), "api");
247        assert_eq!(TaskSource::Unknown.to_string(), "unknown");
248        assert_eq!(
249            TaskSource::parse("agent").unwrap_err().to_string(),
250            "error invalid-task-source input=agent choices=cli,tui,api,unknown"
251        );
252
253        assert_eq!(TaskStatus::parse("active").unwrap(), TaskStatus::Active);
254        assert_eq!(TaskStatus::Active.as_str(), "active");
255        assert_eq!(TaskStatus::Active.to_string(), "active");
256        assert_eq!(
257            TaskStatus::parse("blocked").unwrap_err().to_string(),
258            "error invalid-status input=blocked choices=inbox,backlog,todo,active,done,canceled"
259        );
260
261        assert_eq!(TaskPriority::parse("urgent").unwrap(), TaskPriority::Urgent);
262        assert_eq!(TaskPriority::Urgent.as_str(), "urgent");
263        assert_eq!(TaskPriority::Urgent.to_string(), "urgent");
264        assert_eq!(
265            TaskPriority::parse("soon").unwrap_err().to_string(),
266            "error invalid-priority input=soon choices=none,low,medium,high,urgent"
267        );
268    }
269}