1use std::fmt;
4
5#[derive(Debug, Copy, Clone, PartialEq, Eq)]
7pub enum TaskStatus {
8 ToDo,
10
11 Started,
13
14 Blocked,
16
17 Done,
19}
20
21impl TaskStatus {
22 pub fn display_name(&self) -> &str {
24 match self {
25 TaskStatus::ToDo => "To Do",
26 TaskStatus::Started => "In Progress",
27 TaskStatus::Blocked => "Blocked",
28 TaskStatus::Done => "Done",
29 }
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct Task {
36 status: TaskStatus,
37 content: String,
38}
39
40impl Task {
41 pub fn new(status: TaskStatus, content: &str) -> Task {
43 Task {
44 status,
45 content: content.to_string(),
46 }
47 }
48
49 pub fn from_string(s: &str) -> Option<Task> {
55 let parse_content = |s: &str| s[1..].trim().to_string();
56 if s.starts_with("*") {
57 Some(Task {
58 status: TaskStatus::ToDo,
59 content: parse_content(s),
60 })
61 } else if s.starts_with("^") {
62 Some(Task {
63 status: TaskStatus::Started,
64 content: parse_content(s),
65 })
66 } else if s.starts_with("+") {
67 Some(Task {
68 status: TaskStatus::Done,
69 content: parse_content(s),
70 })
71 } else if s.starts_with("-") {
72 Some(Task {
73 status: TaskStatus::Blocked,
74 content: parse_content(s),
75 })
76 } else {
77 None
78 }
79 }
80
81 pub fn status(&self) -> TaskStatus {
83 self.status
84 }
85
86 pub fn content(&self) -> &str {
88 &self.content
89 }
90}
91
92impl fmt::Display for Task {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 match self.status {
95 TaskStatus::ToDo => write!(f, "* ")?,
96 TaskStatus::Started => write!(f, "^ ")?,
97 TaskStatus::Done => write!(f, "+ ")?,
98 TaskStatus::Blocked => write!(f, "- ")?,
99 };
100 write!(f, "{}", self.content)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_parse_todo() {
110 let t = Task::from_string("* INCOMPLETE").expect("Could not parse todo task");
111 assert_eq!(t.status(), TaskStatus::ToDo);
112 assert_eq!(t.content(), "INCOMPLETE");
113 }
114
115 #[test]
116 fn test_parse_started() {
117 let t = Task::from_string("^ STARTED").expect("Could not parse started task");
118 assert_eq!(t.status(), TaskStatus::Started);
119 assert_eq!(t.content(), "STARTED");
120 }
121
122 #[test]
123 fn test_parse_done() {
124 let t = Task::from_string("+ Done").expect("Could not parse done task");
125 assert_eq!(t.status(), TaskStatus::Done);
126 assert_eq!(t.content(), "Done");
127 }
128
129 #[test]
130 fn test_parse_blocked() {
131 let t = Task::from_string("- Blocked").expect("Could not parse blocked task");
132 assert_eq!(t.status(), TaskStatus::Blocked);
133 assert_eq!(t.content(), "Blocked");
134 }
135
136 #[test]
137 fn test_parse_ignore() {
138 let t = Task::from_string("Comment");
139 assert!(t.is_none());
140 }
141
142 #[test]
143 fn test_parse_ignore_leading_whitespace() {
144 let t = Task::from_string(" * COMMENT");
145 assert!(t.is_none());
146 }
147
148 #[test]
149 fn test_trim_whitespace() {
150 let t = Task::from_string("+ done \n").expect("Could not parse task");
151 assert_eq!(t.status(), TaskStatus::Done);
152 assert_eq!(t.content(), "done");
153 }
154
155 #[test]
156 fn test_fmt_todo() {
157 let t = Task::new(TaskStatus::ToDo, "INCOMPLETE");
158 let s = format!("{}", t);
159 assert_eq!(s, "* INCOMPLETE");
160 }
161
162 #[test]
163 fn test_fmt_started() {
164 let t = Task::new(TaskStatus::Started, "STARTED");
165 let s = format!("{}", t);
166 assert_eq!(s, "^ STARTED");
167 }
168
169 #[test]
170 fn test_fmt_done() {
171 let t = Task::new(TaskStatus::Done, "DONE");
172 let s = format!("{}", t);
173 assert_eq!(s, "+ DONE");
174 }
175
176 #[test]
177 fn test_fmt_blocked() {
178 let t = Task::new(TaskStatus::Blocked, "BLOCKED");
179 let s = format!("{}", t);
180 assert_eq!(s, "- BLOCKED");
181 }
182}