1use chrono::{Duration, Local, NaiveDateTime};
8
9use crate::entry::{EventMetaView, TaskMetaView};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum EntryFlag {
16 Overdue,
18 New,
19 Updated,
20 Event,
22 EventClosed,
24 Done,
25 Cancelled,
26 InProgress,
27 Archived,
28 Open,
29 Note,
30}
31
32impl EntryFlag {
33 pub fn as_str(self) -> &'static str {
34 match self {
35 Self::Overdue => "overdue",
36 Self::New => "new",
37 Self::Updated => "updated",
38 Self::Event => "event",
39 Self::EventClosed => "event_closed",
40 Self::Done => "done",
41 Self::Cancelled => "cancelled",
42 Self::InProgress => "in_progress",
43 Self::Archived => "archived",
44 Self::Open => "open",
45 Self::Note => "note",
46 }
47 }
48
49 pub fn to_emoji(self) -> &'static str {
50 match self {
51 Self::Overdue => "⏰",
52 Self::New => "🆕",
53 Self::Updated => "✏️",
54 Self::Event => "📅",
55 Self::EventClosed => "🗓️",
56 Self::Done => "✅",
57 Self::Cancelled => "❌",
58 Self::InProgress => "🔄",
59 Self::Archived => "📦",
60 Self::Open => "⬜",
61 Self::Note => "📝",
62 }
63 }
64
65 pub fn to_nerd(self) -> &'static str {
66 match self {
67 Self::Overdue => "",
68 Self::New => "",
69 Self::Updated => "",
70 Self::Event => "",
71 Self::EventClosed => "",
72 Self::Done => "",
73 Self::Cancelled => "",
74 Self::InProgress => "",
75 Self::Archived => "",
76 Self::Open => "",
77 Self::Note => "",
78 }
79 }
80
81 pub fn to_initial(self) -> char {
82 match self {
83 Self::Overdue => '!',
84 Self::New => '+',
85 Self::Updated => '~',
86 Self::Event => 'E',
87 Self::EventClosed => 'e',
88 Self::Done => 'D',
89 Self::Cancelled => 'C',
90 Self::InProgress => 'I',
91 Self::Archived => 'A',
92 Self::Open => 'O',
93 Self::Note => 'N',
94 }
95 }
96}
97
98impl serde::Serialize for EntryFlag {
99 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
100 s.serialize_str(self.as_str())
101 }
102}
103
104pub fn task_status_label(status: &str) -> &'static str {
109 match status {
110 "done" | "completed" => "done",
111 "cancelled" | "canceled" => "cancelled",
112 "in_progress" | "wip" => "in_progress",
113 "archived" => "archived",
114 _ => "open",
115 }
116}
117
118pub fn entry_flags(
123 task: Option<&TaskMetaView>,
124 event: Option<&EventMetaView>,
125 created_at: NaiveDateTime,
126 updated_at: NaiveDateTime,
127) -> Vec<EntryFlag> {
128 let mut flags = Vec::new();
129
130 let now = Local::now().naive_local();
131
132 let is_overdue = task.map_or(false, |t| {
134 t.due.map_or(false, |due| due < now) && t.closed_at.is_none()
135 });
136 if is_overdue {
137 flags.push(EntryFlag::Overdue);
138 } else {
139 let threshold = now - Duration::hours(24);
140 if created_at >= threshold {
141 flags.push(EntryFlag::New);
142 } else if updated_at >= threshold {
143 flags.push(EntryFlag::Updated);
144 }
145 }
146
147 if let Some(ev) = event {
149 if ev.end < now {
150 flags.push(EntryFlag::EventClosed);
151 } else {
152 flags.push(EntryFlag::Event);
153 }
154 } else if let Some(task) = task {
155 let flag = match task_status_label(&task.status) {
156 "done" => EntryFlag::Done,
157 "cancelled" => EntryFlag::Cancelled,
158 "in_progress" => EntryFlag::InProgress,
159 "archived" => EntryFlag::Archived,
160 _ => EntryFlag::Open,
161 };
162 flags.push(flag);
163 } else {
164 flags.push(EntryFlag::Note);
165 }
166
167 flags
168}