doing_ops/
extract_note.rs1pub fn extract_note(title: &str) -> (String, Option<String>) {
16 let trimmed = title.trim();
17
18 if !trimmed.ends_with(')') {
19 return (trimmed.to_string(), None);
20 }
21
22 let open_pos = match trimmed.find('(') {
24 Some(pos) => pos,
25 None => return (trimmed.to_string(), None),
26 };
27
28 let note_content = trimmed[open_pos + 1..trimmed.len() - 1].trim();
29
30 if note_content.is_empty() {
32 return (trimmed.to_string(), None);
33 }
34
35 let title_part = trimmed[..open_pos].trim();
36
37 if title_part.ends_with(|c: char| c.is_alphanumeric() || c == '_')
39 && title_part.contains('@')
40 && title_part
41 .rfind('@')
42 .map(|at| !title_part[at..].contains(' '))
43 .unwrap_or(false)
44 {
45 return (trimmed.to_string(), None);
46 }
47
48 (title_part.to_string(), Some(note_content.to_string()))
49}
50
51#[cfg(test)]
52mod test {
53 use super::*;
54
55 mod extract_note {
56 use pretty_assertions::assert_eq;
57
58 use super::*;
59
60 #[test]
61 fn it_combines_with_existing_note() {
62 let (title, note) = extract_note("Task (extra context)");
63
64 assert_eq!(title, "Task");
65 assert_eq!(note.unwrap(), "extra context");
66 }
67
68 #[test]
69 fn it_extracts_trailing_parenthetical() {
70 let (title, note) = extract_note("Working on project (some context)");
71
72 assert_eq!(title, "Working on project");
73 assert_eq!(note.unwrap(), "some context");
74 }
75
76 #[test]
77 fn it_handles_nested_parens() {
78 let (title, note) = extract_note("Task (note with (nested) parens)");
79
80 assert_eq!(title, "Task");
81 assert_eq!(note.unwrap(), "note with (nested) parens");
82 }
83
84 #[test]
85 fn it_ignores_tag_values() {
86 let (title, note) = extract_note("Working on @project(myapp)");
87
88 assert_eq!(title, "Working on @project(myapp)");
89 assert!(note.is_none());
90 }
91
92 #[test]
93 fn it_ignores_empty_parenthetical() {
94 let (title, note) = extract_note("Task ()");
95
96 assert_eq!(title, "Task ()");
97 assert!(note.is_none());
98 }
99
100 #[test]
101 fn it_ignores_non_trailing_parenthetical() {
102 let (title, note) = extract_note("Foo (bar) baz");
103
104 assert_eq!(title, "Foo (bar) baz");
105 assert!(note.is_none());
106 }
107
108 #[test]
109 fn it_returns_none_for_no_parenthetical() {
110 let (title, note) = extract_note("Just a title");
111
112 assert_eq!(title, "Just a title");
113 assert!(note.is_none());
114 }
115 }
116}