1use std::fmt;
2
3use crate::date::DateCompound;
4use crate::description::Description;
5use crate::parse::{Parse, Parser};
6use crate::priority::Priority;
7use crate::state::State;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Task {
13 pub state: State,
15
16 #[cfg_attr(
18 feature = "serde",
19 serde(skip_serializing_if = "Option::is_none", default)
20 )]
21 pub priority: Option<Priority>,
22
23 #[cfg_attr(
25 feature = "serde",
26 serde(flatten, skip_serializing_if = "Option::is_none", default)
27 )]
28 pub date_compound: Option<DateCompound>,
29
30 pub description: Description,
32}
33
34impl Task {
35 pub fn build() -> TaskBuilder {
37 TaskBuilder::default()
38 }
39
40 pub const fn state(&self) -> &State {
42 &self.state
43 }
44
45 pub const fn priority(&self) -> Option<&Priority> {
47 self.priority.as_ref()
48 }
49
50 pub const fn date_compound(&self) -> Option<&DateCompound> {
52 self.date_compound.as_ref()
53 }
54
55 pub const fn description(&self) -> &Description {
57 &self.description
58 }
59}
60
61impl fmt::Display for Task {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 let mut s: Vec<String> = Vec::with_capacity(4);
64
65 if self.state != State::Open {
66 s.push(self.state.to_string());
67 }
68
69 if let Some(priority) = self.priority {
70 s.push(priority.to_string());
71 }
72
73 if let Some(date_compound) = self.date_compound {
74 s.push(date_compound.to_string());
75 }
76
77 s.push(self.description.to_string());
78
79 f.write_str(&s.join(" "))
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub struct ParseTaskError;
87
88impl fmt::Display for ParseTaskError {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 f.write_str("failed to parse task")
91 }
92}
93
94impl std::error::Error for ParseTaskError {}
95
96impl Parse for Task {
97 type Error = ParseTaskError;
98
99 fn parse(parser: &mut Parser<'_>) -> Result<Self, Self::Error> {
100 macro_rules! try_parse {
101 ( $parser:ident : $ty:ty ) => {{
102 let mut p_copy = *parser;
103
104 if let Some(ty) = <$ty>::parse_opt(&mut p_copy) {
105 if p_copy.is_eof() || p_copy.expect_whitespace().is_some()
106 {
107 *parser = p_copy;
108 Some(ty)
109 } else {
110 None
111 }
112 } else {
113 None
114 }
115 }};
116 }
117
118 let state = try_parse!(parser: State).unwrap_or_default();
119 let priority = try_parse!(parser: Priority);
120 let date_compound = try_parse!(parser: DateCompound);
121
122 let description =
123 Description::parse(parser).map_err(|_| ParseTaskError)?;
124
125 let task = Self { state, priority, date_compound, description };
126
127 Ok(task)
128 }
129}
130
131impl std::str::FromStr for Task {
132 type Err = ParseTaskError;
133
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
135 let mut parser = Parser::new(s.as_bytes());
136 Self::parse(&mut parser)
137 }
138}
139
140#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
145pub struct TaskBuilder {
146 state: Option<State>,
147 priority: Option<Priority>,
148 date_compound: Option<DateCompound>,
149}
150
151impl TaskBuilder {
152 pub fn new() -> Self {
154 let (state, priority, date_compound) = <_>::default();
155
156 Self { state, priority, date_compound }
157 }
158
159 pub fn state(&mut self, state: State) -> &mut Self {
161 self.state = Some(state);
162 self
163 }
164
165 pub fn priority<P>(&mut self, priority: P) -> &mut Self
167 where
168 P: Into<Priority>,
169 {
170 self.priority = Some(priority.into());
171 self
172 }
173
174 pub fn date_compound<D>(&mut self, date_compound: D) -> &mut Self
176 where
177 D: Into<DateCompound>,
178 {
179 self.date_compound = Some(date_compound.into());
180 self
181 }
182
183 pub fn build<D>(&mut self, description: D) -> Task
189 where
190 D: Into<Description>,
191 {
192 Task {
193 state: self.state.unwrap_or_default(),
194 priority: self.priority,
195 date_compound: self.date_compound,
196 description: description.into(),
197 }
198 }
199}