1use crate::models::Tag;
2use crate::parser::utils::parse_str_as_date;
3use chrono::NaiveDate;
4use lazy_static::lazy_static;
5use regex::Regex;
6use std::cell::RefCell;
7
8use super::HasName;
9#[derive(Debug, Clone)]
10pub struct Comment {
11 pub comment: String,
12 calculated_tags: RefCell<bool>,
13 tags: RefCell<Vec<Tag>>,
14 calculated_payee: RefCell<bool>,
15 payee: RefCell<Option<String>>,
16}
17
18impl From<String> for Comment {
19 fn from(comment: String) -> Self {
20 Comment {
21 comment,
22 calculated_tags: RefCell::new(false),
23 tags: RefCell::new(vec![]),
24 calculated_payee: RefCell::new(false),
25 payee: RefCell::new(None),
26 }
27 }
28}
29
30impl From<&str> for Comment {
31 fn from(comment: &str) -> Self {
32 Comment::from(comment.to_string())
33 }
34}
35
36impl Comment {
37 pub fn get_tags(&self) -> Vec<Tag> {
38 lazy_static! {
39 static ref RE_FLAGS: Regex = Regex::new(r"(:.+:) *$").unwrap();
41 static ref RE_VALUE: Regex = Regex::new(" *(.*): *(.*) *$").unwrap();
43 }
44 let calculated_tags = *self.calculated_tags.borrow_mut();
45 let tags = {
46 if !calculated_tags {
47 self.calculated_tags.replace(true);
48 self.tags
49 .borrow_mut()
50 .append(&mut match RE_FLAGS.is_match(&self.comment) {
51 true => {
52 let value = RE_FLAGS
53 .captures(&self.comment)
54 .unwrap()
55 .iter()
56 .nth(1)
57 .unwrap()
58 .unwrap()
59 .as_str();
60 let mut tags: Vec<Tag> = value
61 .split(':')
62 .map(|x| Tag {
63 name: x.into(),
64 check: vec![],
65 assert: vec![],
66 value: None,
67 })
68 .collect();
69 tags.pop();
70 tags.remove(0);
71 tags
72 }
73 false => match RE_VALUE.is_match(&self.comment) {
74 true => {
75 let re_captures = RE_VALUE.captures(&self.comment).unwrap();
76 let mut captures = re_captures.iter();
77 captures.next();
78 let name: String =
79 captures.next().unwrap().unwrap().as_str().to_string();
80 if name.contains(':') {
81 vec![]
82 } else {
83 vec![Tag {
84 name,
85 check: vec![],
86 assert: vec![],
87 value: Some(
88 captures
89 .next()
90 .unwrap()
91 .unwrap()
92 .as_str()
93 .trim()
94 .to_string(),
95 ),
96 }]
97 }
98 }
99 false => vec![],
100 },
101 });
102 }
103 self.tags.borrow().clone()
104 };
105 tags
106 }
107
108 pub fn get_payee_str(&self) -> Option<String> {
109 let calculated_payee = *self.calculated_payee.borrow_mut();
110 if calculated_payee {
111 return self.payee.borrow().clone();
112 }
113 self.calculated_payee.replace(true);
114 for tag in self.get_tags().iter() {
115 if tag.value.is_none() | (tag.get_name().to_lowercase() != "payee") {
116 continue;
117 }
118 self.payee.replace(tag.value.clone());
119 return tag.value.clone();
120 }
121 None
122 }
123
124 pub fn get_date(&self) -> Option<NaiveDate> {
128 lazy_static! {
129 static ref RE_VALUE: Regex = Regex::new(r" *\[=(\d{4}.\d{2}.\d{2})\] *$").unwrap();
131 }
132 match RE_VALUE.is_match(&self.comment) {
133 true => Some(parse_str_as_date(
134 self.comment.as_str().trim().split_at(2).1,
135 )),
136 false => None,
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::models::HasName;
145 #[test]
146 fn multi_tag() {
147 let comment = Comment::from(":tag_1:tag_2:tag_3:");
148 let tags = comment.get_tags();
149 assert_eq!(tags.len(), 3, "There should be three tags");
150 assert_eq!(tags[0].get_name(), "tag_1");
151 assert_eq!(tags[1].get_name(), "tag_2");
152 assert_eq!(tags[2].get_name(), "tag_3");
153 }
154 #[test]
155 fn no_tag() {
156 let comment = Comment::from(":tag_1:tag_2:tag_3: this is not valid");
157 let tags = comment.get_tags();
158 assert_eq!(tags.len(), 0, "There should no tags");
159 }
160 #[test]
161 fn not_a_tag() {
162 let comment = Comment::from("not a tag whatsoever");
163 let tags = comment.get_tags();
164 assert_eq!(tags.len(), 0, "There should no tags");
165 }
166 #[test]
167 fn tag_value() {
168 let comment = Comment::from("tag: value");
169 let tags = comment.get_tags();
170 assert_eq!(tags.len(), 1, "There should be one tag");
171 let tag = tags[0].clone();
172 assert_eq!(tag.get_name(), "tag");
173 assert_eq!(tag.value.unwrap(), "value".to_string());
174 }
175 #[test]
176 fn tag_value_spaces() {
177 let comment = Comment::from("tag: value with spaces");
178 let tags = comment.get_tags();
179 assert_eq!(tags.len(), 1, "There should be one tag");
180 let tag = tags[0].clone();
181 assert_eq!(tag.get_name(), "tag");
182 assert_eq!(tag.value.unwrap(), "value with spaces".to_string());
183 }
184 #[test]
185 fn date_in_comment() {
186 let comment = Comment::from(" [=2021/03/02] ");
187 let date = comment.get_date().unwrap();
188 assert_eq!(date, NaiveDate::from_ymd(2021, 3, 2));
189 }
190 #[test]
191 fn payee_in_comment() {
192 let comment = Comment::from(" payee: claudio ");
193 let payee = comment.get_payee_str().unwrap();
194 assert_eq!(payee, "claudio".to_string());
195 }
196}