1use std::collections::BTreeMap;
2
3#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Simple {
6 pub subject: String,
7 #[cfg_attr(feature = "serde", serde(default))]
8 pub priority: crate::Priority,
9 pub create_date: Option<crate::Date>,
10 pub finish_date: Option<crate::Date>,
11 #[cfg_attr(feature = "serde", serde(default))]
12 pub finished: bool,
13 pub threshold_date: Option<crate::Date>,
14 pub due_date: Option<crate::Date>,
15 #[cfg_attr(feature = "serde", serde(default))]
16 pub contexts: Vec<String>,
17 #[cfg_attr(feature = "serde", serde(default))]
18 pub projects: Vec<String>,
19 #[cfg_attr(feature = "serde", serde(default))]
20 pub hashtags: Vec<String>,
21 #[cfg_attr(feature = "serde", serde(default))]
22 pub tags: BTreeMap<String, String>,
23}
24
25impl Simple {
26 pub fn complete(&mut self) {
27 let today = chrono::Local::now().date_naive();
28
29 self.finished = true;
30 if self.create_date.is_some() {
31 self.finish_date = Some(today);
32 }
33 }
34
35 pub fn uncomplete(&mut self) {
36 self.finished = false;
37 self.finish_date = None;
38 }
39}
40
41impl Default for Simple {
42 fn default() -> Self {
43 Self {
44 subject: String::new(),
45 priority: crate::Priority::lowest(),
46 create_date: None,
47 finish_date: None,
48 finished: false,
49 threshold_date: None,
50 due_date: None,
51 contexts: Vec::new(),
52 projects: Vec::new(),
53 hashtags: Vec::new(),
54 tags: BTreeMap::new(),
55 }
56 }
57}
58
59impl std::str::FromStr for Simple {
60 type Err = std::convert::Infallible;
61
62 fn from_str(s: &str) -> Result<Simple, Self::Err> {
63 Ok(crate::parser::task(s))
64 }
65}
66
67impl From<String> for Simple {
68 fn from(value: String) -> Self {
69 value.parse().unwrap()
70 }
71}
72
73impl std::fmt::Display for Simple {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 if self.finished {
76 f.write_str("x ")?;
77 }
78
79 if !self.priority.is_lowest() {
80 f.write_str(&format!("({}) ", self.priority))?;
81 }
82
83 if let Some(finish_date) = self.finish_date {
84 f.write_str(&format!("{} ", finish_date.format("%Y-%m-%d")))?;
85 }
86
87 if let Some(create_date) = self.create_date {
88 f.write_str(&format!("{} ", create_date.format("%Y-%m-%d")))?;
89 }
90
91 f.write_str(&self.subject)?;
92
93 if let Some(due_date) = self.due_date {
94 f.write_str(&format!(" due:{}", due_date.format("%Y-%m-%d")))?;
95 }
96
97 if let Some(threshold_date) = self.threshold_date {
98 f.write_str(&format!(" t:{}", threshold_date.format("%Y-%m-%d")))?;
99 }
100
101 for context in &self.contexts {
102 let tag = format!("{}{context}", super::Tag::Context);
103
104 if !self.subject.contains(&tag) {
105 write!(f, " {tag}")?;
106 }
107 }
108
109 for project in &self.projects {
110 let tag = format!("{}{project}", super::Tag::Project);
111
112 if !self.subject.contains(&tag) {
113 write!(f, " {tag}")?;
114 }
115 }
116
117 for hashtags in &self.hashtags {
118 let tag = format!("{}{hashtags}", super::Tag::Hashtag);
119
120 if !self.subject.contains(&tag) {
121 write!(f, " {tag}")?;
122 }
123 }
124
125 for (key, value) in &self.tags {
126 f.write_str(&format!(" {key}:{value}"))?;
127 }
128
129 Ok(())
130 }
131}
132
133impl std::cmp::PartialOrd for Simple {
134 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
135 Some(self.cmp(other))
136 }
137}
138
139impl std::cmp::Ord for Simple {
140 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
141 if self.priority != other.priority {
142 return self.priority.cmp(&other.priority);
143 }
144
145 if self.due_date != other.due_date {
146 return self.due_date.cmp(&other.due_date);
147 }
148
149 if self.subject != other.subject {
150 return self.subject.cmp(&other.subject);
151 }
152
153 std::cmp::Ordering::Equal
154 }
155}
156
157impl AsRef<Simple> for Simple {
158 fn as_ref(&self) -> &Simple {
159 self
160 }
161}