1use crate::{FlagStatus, Message};
3
4pub struct UserContext<'a> {
5 pub my_addresses: &'a [String],
6 pub trusted_senders: &'a [String],
7}
8
9#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
10pub struct ItemFlags {
11 pub to_me: bool,
12 pub trusted: bool,
13 pub question: bool,
14 pub attachments: bool,
15 pub flagged: bool,
16 pub unread: bool,
17}
18
19impl ItemFlags {
20 pub fn labels(&self) -> Vec<&'static str> {
21 let mut v = Vec::new();
22 if self.to_me {
23 v.push("to-me");
24 }
25 if self.trusted {
26 v.push("trusted");
27 }
28 if self.question {
29 v.push("question");
30 }
31 if self.attachments {
32 v.push("attachments");
33 }
34 if self.flagged {
35 v.push("flagged");
36 }
37 if self.unread {
38 v.push("unread");
39 }
40 v
41 }
42}
43
44fn eq_ci(a: &str, b: &str) -> bool {
45 a.eq_ignore_ascii_case(b)
46}
47
48pub fn compute_flags(m: &Message, ctx: &UserContext) -> ItemFlags {
49 let to_me =
50 m.to.iter()
51 .any(|r| ctx.my_addresses.iter().any(|mine| eq_ci(&r.address, mine)));
52 let trusted = ctx
53 .trusted_senders
54 .iter()
55 .any(|t| eq_ci(t, &m.from.address));
56 let question = m.preview.contains('?');
57 ItemFlags {
58 to_me,
59 trusted,
60 question,
61 attachments: m.has_attachments,
62 flagged: m.flag_status == FlagStatus::Flagged,
63 unread: !m.is_read,
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::{BodyContentType, FlagStatus, Message, MessageFrom};
71 use chrono::Utc;
72
73 fn msg(from: &str, to: &[&str], cc: &[&str], preview: &str) -> Message {
74 let who = |a: &str| MessageFrom {
75 name: String::new(),
76 address: a.to_string(),
77 };
78 Message {
79 account: "me@example.com".into(),
80 id: "1".into(),
81 conversation_id: String::new(),
82 from: who(from),
83 subject: "s".into(),
84 received_at: Utc::now(),
85 is_read: false,
86 preview: preview.into(),
87 flag_status: FlagStatus::NotFlagged,
88 has_attachments: false,
89 body: String::new(),
90 body_content_type: BodyContentType::Text,
91 to: to.iter().map(|a| who(a)).collect(),
92 cc: cc.iter().map(|a| who(a)).collect(),
93 is_invite: false,
94 }
95 }
96
97 #[test]
98 fn to_me_requires_to_not_cc() {
99 let mine = vec!["me@example.com".to_string()];
100 let ctx = UserContext {
101 my_addresses: &mine,
102 trusted_senders: &[],
103 };
104 assert!(compute_flags(&msg("a@x", &["Me@Example.com"], &[], ""), &ctx).to_me);
105 assert!(!compute_flags(&msg("a@x", &["b@x"], &["me@example.com"], ""), &ctx).to_me);
106 }
107
108 #[test]
109 fn trusted_and_question() {
110 let mine = vec!["me@example.com".to_string()];
111 let trusted = vec!["Anna@Example.com".to_string()];
112 let ctx = UserContext {
113 my_addresses: &mine,
114 trusted_senders: &trusted,
115 };
116 let f = compute_flags(&msg("anna@example.com", &[], &[], "Can you come?"), &ctx);
117 assert!(f.trusted && f.question);
118 assert_eq!(f.labels(), vec!["trusted", "question", "unread"]);
119 }
120}