tdtxt/
task.rs

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/// Represents the whole task.
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Task {
13	/// Optional state of the task.
14	pub state: State,
15
16	/// Optional priority of the task.
17	#[cfg_attr(
18		feature = "serde",
19		serde(skip_serializing_if = "Option::is_none", default)
20	)]
21	pub priority: Option<Priority>,
22
23	/// Optional associated special dates for the task.
24	#[cfg_attr(
25		feature = "serde",
26		serde(flatten, skip_serializing_if = "Option::is_none", default)
27	)]
28	pub date_compound: Option<DateCompound>,
29
30	/// Description of the task.
31	pub description: Description,
32}
33
34impl Task {
35	/// Creates a new builder for a task.
36	pub fn build() -> TaskBuilder {
37		TaskBuilder::default()
38	}
39
40	/// Returns the state of the task.
41	pub const fn state(&self) -> &State {
42		&self.state
43	}
44
45	/// Returns the priority of the task.
46	pub const fn priority(&self) -> Option<&Priority> {
47		self.priority.as_ref()
48	}
49
50	/// Returns the date compound of the task.
51	pub const fn date_compound(&self) -> Option<&DateCompound> {
52		self.date_compound.as_ref()
53	}
54
55	/// Returns the description of the task.
56	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/// This struct represents errors which may occur during the parsing of a
84/// [`Task`].
85#[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/// A builder for a task.
141///
142/// All components implement `Copy`, meaning the builder can be used to build
143/// multiple tasks without being consumed.
144#[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	/// Creates a new instance of the builder.
153	pub fn new() -> Self {
154		let (state, priority, date_compound) = <_>::default();
155
156		Self { state, priority, date_compound }
157	}
158
159	/// Sets the state for the task.
160	pub fn state(&mut self, state: State) -> &mut Self {
161		self.state = Some(state);
162		self
163	}
164
165	/// Sets the priority for the task.
166	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	/// Sets the date compound for the task.
175	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	/// Creates a task from the builder.
184	///
185	/// # Notes
186	///
187	/// If no priority was set it will use the default implementation for it.
188	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}