1use rusmes_proto::{MessageId, Username};
4use serde::{Deserialize, Serialize};
5use std::collections::HashSet;
6use std::fmt;
7use uuid::Uuid;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct MailboxId(Uuid);
12
13impl MailboxId {
14 pub fn new() -> Self {
16 Self(Uuid::new_v4())
17 }
18
19 pub fn from_uuid(uuid: Uuid) -> Self {
21 Self(uuid)
22 }
23
24 pub fn as_uuid(&self) -> &Uuid {
26 &self.0
27 }
28}
29
30impl Default for MailboxId {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl std::fmt::Display for MailboxId {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 write!(f, "{}", self.0)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
44pub struct MailboxPath {
45 user: Username,
46 path: Vec<String>,
47}
48
49impl MailboxPath {
50 pub fn new(user: Username, path: Vec<String>) -> Self {
52 Self { user, path }
53 }
54
55 pub fn user(&self) -> &Username {
57 &self.user
58 }
59
60 pub fn path(&self) -> &[String] {
62 &self.path
63 }
64
65 pub fn name(&self) -> Option<&str> {
67 self.path.last().map(|s| s.as_str())
68 }
69}
70
71impl std::fmt::Display for MailboxPath {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 write!(f, "{}/{}", self.user, self.path.join("/"))
74 }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct Mailbox {
80 id: MailboxId,
81 path: MailboxPath,
82 uid_validity: u32,
83 uid_next: u32,
84 special_use: Option<String>,
85}
86
87impl Mailbox {
88 pub fn new(path: MailboxPath) -> Self {
90 Self {
91 id: MailboxId::new(),
92 path,
93 uid_validity: 1, uid_next: 1,
95 special_use: None,
96 }
97 }
98
99 pub fn new_with_special_use(path: MailboxPath, special_use: String) -> Self {
101 Self {
102 id: MailboxId::new(),
103 path,
104 uid_validity: 1,
105 uid_next: 1,
106 special_use: Some(special_use),
107 }
108 }
109
110 pub fn id(&self) -> &MailboxId {
112 &self.id
113 }
114
115 pub fn path(&self) -> &MailboxPath {
117 &self.path
118 }
119
120 pub fn set_path(&mut self, path: MailboxPath) {
122 self.path = path;
123 }
124
125 pub fn uid_validity(&self) -> u32 {
127 self.uid_validity
128 }
129
130 pub fn uid_next(&self) -> u32 {
132 self.uid_next
133 }
134
135 pub fn special_use(&self) -> Option<&str> {
137 self.special_use.as_deref()
138 }
139
140 pub fn set_special_use(&mut self, special_use: Option<String>) {
142 self.special_use = special_use;
143 }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct MessageMetadata {
149 message_id: MessageId,
150 mailbox_id: MailboxId,
151 uid: u32,
152 flags: MessageFlags,
153 size: usize,
154}
155
156impl MessageMetadata {
157 #[allow(clippy::too_many_arguments)]
159 pub fn new(
160 message_id: MessageId,
161 mailbox_id: MailboxId,
162 uid: u32,
163 flags: MessageFlags,
164 size: usize,
165 ) -> Self {
166 Self {
167 message_id,
168 mailbox_id,
169 uid,
170 flags,
171 size,
172 }
173 }
174
175 pub fn message_id(&self) -> &MessageId {
177 &self.message_id
178 }
179
180 pub fn mailbox_id(&self) -> &MailboxId {
182 &self.mailbox_id
183 }
184
185 pub fn uid(&self) -> u32 {
187 self.uid
188 }
189
190 pub fn flags(&self) -> &MessageFlags {
192 &self.flags
193 }
194
195 pub fn size(&self) -> usize {
197 self.size
198 }
199}
200
201#[derive(Debug, Clone, Default, Serialize, Deserialize)]
203pub struct MessageFlags {
204 seen: bool,
205 answered: bool,
206 flagged: bool,
207 deleted: bool,
208 draft: bool,
209 recent: bool,
210 custom: HashSet<String>,
211}
212
213impl MessageFlags {
214 pub fn new() -> Self {
216 Self::default()
217 }
218
219 pub fn is_seen(&self) -> bool {
221 self.seen
222 }
223
224 pub fn set_seen(&mut self, value: bool) {
226 self.seen = value;
227 }
228
229 pub fn is_answered(&self) -> bool {
231 self.answered
232 }
233
234 pub fn set_answered(&mut self, value: bool) {
236 self.answered = value;
237 }
238
239 pub fn is_flagged(&self) -> bool {
241 self.flagged
242 }
243
244 pub fn set_flagged(&mut self, value: bool) {
246 self.flagged = value;
247 }
248
249 pub fn is_deleted(&self) -> bool {
251 self.deleted
252 }
253
254 pub fn set_deleted(&mut self, value: bool) {
256 self.deleted = value;
257 }
258
259 pub fn is_draft(&self) -> bool {
261 self.draft
262 }
263
264 pub fn set_draft(&mut self, value: bool) {
266 self.draft = value;
267 }
268
269 pub fn is_recent(&self) -> bool {
271 self.recent
272 }
273
274 pub fn set_recent(&mut self, value: bool) {
276 self.recent = value;
277 }
278
279 pub fn add_custom(&mut self, flag: String) {
281 self.custom.insert(flag);
282 }
283
284 pub fn remove_custom(&mut self, flag: &str) -> bool {
286 self.custom.remove(flag)
287 }
288
289 pub fn custom(&self) -> &HashSet<String> {
291 &self.custom
292 }
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
297pub enum SearchCriteria {
298 All,
299 Unseen,
300 Seen,
301 Flagged,
302 Unflagged,
303 Deleted,
304 Undeleted,
305 From(String),
306 To(String),
307 Subject(String),
308 Body(String),
309 And(Vec<SearchCriteria>),
310 Or(Vec<SearchCriteria>),
311 Not(Box<SearchCriteria>),
312}
313
314#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
316pub struct MailboxCounters {
317 pub exists: u32,
318 pub recent: u32,
319 pub unseen: u32,
320}
321
322#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
324pub struct Quota {
325 pub used: u64,
326 pub limit: u64,
327}
328
329impl Quota {
330 pub fn new(used: u64, limit: u64) -> Self {
332 Self { used, limit }
333 }
334
335 pub fn is_exceeded(&self) -> bool {
337 self.used >= self.limit
338 }
339
340 pub fn remaining(&self) -> u64 {
342 self.limit.saturating_sub(self.used)
343 }
344}
345
346#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
348pub struct SpecialUseAttributes {
349 attributes: HashSet<String>,
350}
351
352impl SpecialUseAttributes {
353 pub fn new() -> Self {
355 Self {
356 attributes: HashSet::new(),
357 }
358 }
359
360 pub fn single(attribute: String) -> Self {
362 let mut attrs = HashSet::new();
363 attrs.insert(attribute);
364 Self { attributes: attrs }
365 }
366
367 pub fn add(&mut self, attribute: String) {
369 self.attributes.insert(attribute);
370 }
371
372 pub fn remove(&mut self, attribute: &str) -> bool {
374 self.attributes.remove(attribute)
375 }
376
377 pub fn has_attribute(&self, attribute: &str) -> bool {
379 self.attributes.contains(attribute)
380 }
381
382 pub fn is_empty(&self) -> bool {
384 self.attributes.is_empty()
385 }
386
387 pub fn len(&self) -> usize {
389 self.attributes.len()
390 }
391
392 pub fn iter(&self) -> impl Iterator<Item = &String> {
394 self.attributes.iter()
395 }
396
397 pub fn to_vec(&self) -> Vec<String> {
399 self.attributes.iter().cloned().collect()
400 }
401
402 pub fn from_vec(attributes: Vec<String>) -> Self {
404 Self {
405 attributes: attributes.into_iter().collect(),
406 }
407 }
408
409 pub fn drafts() -> Self {
411 Self::single("\\Drafts".to_string())
412 }
413
414 pub fn sent() -> Self {
415 Self::single("\\Sent".to_string())
416 }
417
418 pub fn trash() -> Self {
419 Self::single("\\Trash".to_string())
420 }
421
422 pub fn junk() -> Self {
423 Self::single("\\Junk".to_string())
424 }
425
426 pub fn archive() -> Self {
427 Self::single("\\Archive".to_string())
428 }
429
430 pub fn all() -> Self {
431 Self::single("\\All".to_string())
432 }
433
434 pub fn flagged() -> Self {
435 Self::single("\\Flagged".to_string())
436 }
437}
438
439impl FromIterator<String> for SpecialUseAttributes {
440 fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
441 Self {
442 attributes: iter.into_iter().collect(),
443 }
444 }
445}
446
447impl fmt::Display for SpecialUseAttributes {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 let attrs: Vec<&String> = self.attributes.iter().collect();
450 write!(
451 f,
452 "{}",
453 attrs
454 .iter()
455 .map(|s| s.as_str())
456 .collect::<Vec<_>>()
457 .join(" ")
458 )
459 }
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465
466 #[test]
467 fn test_special_use_attributes_new() {
468 let attrs = SpecialUseAttributes::new();
469 assert!(attrs.is_empty());
470 assert_eq!(attrs.len(), 0);
471 }
472
473 #[test]
474 fn test_special_use_attributes_single() {
475 let attrs = SpecialUseAttributes::single("\\Drafts".to_string());
476 assert!(!attrs.is_empty());
477 assert_eq!(attrs.len(), 1);
478 assert!(attrs.has_attribute("\\Drafts"));
479 }
480
481 #[test]
482 fn test_special_use_attributes_add_remove() {
483 let mut attrs = SpecialUseAttributes::new();
484 attrs.add("\\Drafts".to_string());
485 assert!(attrs.has_attribute("\\Drafts"));
486 assert_eq!(attrs.len(), 1);
487
488 attrs.add("\\Sent".to_string());
489 assert!(attrs.has_attribute("\\Sent"));
490 assert_eq!(attrs.len(), 2);
491
492 assert!(attrs.remove("\\Drafts"));
493 assert!(!attrs.has_attribute("\\Drafts"));
494 assert_eq!(attrs.len(), 1);
495
496 assert!(!attrs.remove("\\Drafts"));
497 }
498
499 #[test]
500 fn test_special_use_attributes_from_vec() {
501 let vec = vec!["\\Drafts".to_string(), "\\Sent".to_string()];
502 let attrs = SpecialUseAttributes::from_vec(vec);
503 assert_eq!(attrs.len(), 2);
504 assert!(attrs.has_attribute("\\Drafts"));
505 assert!(attrs.has_attribute("\\Sent"));
506 }
507
508 #[test]
509 fn test_special_use_attributes_to_vec() {
510 let mut attrs = SpecialUseAttributes::new();
511 attrs.add("\\Drafts".to_string());
512 attrs.add("\\Sent".to_string());
513
514 let vec = attrs.to_vec();
515 assert_eq!(vec.len(), 2);
516 assert!(vec.contains(&"\\Drafts".to_string()));
517 assert!(vec.contains(&"\\Sent".to_string()));
518 }
519
520 #[test]
521 fn test_special_use_attributes_iter() {
522 let mut attrs = SpecialUseAttributes::new();
523 attrs.add("\\Drafts".to_string());
524 attrs.add("\\Sent".to_string());
525
526 let mut count = 0;
527 for _attr in attrs.iter() {
528 count += 1;
529 }
530 assert_eq!(count, 2);
531 }
532
533 #[test]
534 fn test_special_use_attributes_from_iter() {
535 let attrs: SpecialUseAttributes = vec!["\\Drafts".to_string(), "\\Sent".to_string()]
536 .into_iter()
537 .collect();
538 assert_eq!(attrs.len(), 2);
539 assert!(attrs.has_attribute("\\Drafts"));
540 assert!(attrs.has_attribute("\\Sent"));
541 }
542
543 #[test]
544 fn test_special_use_attributes_display() {
545 let mut attrs = SpecialUseAttributes::new();
546 attrs.add("\\Drafts".to_string());
547
548 let display = attrs.to_string();
549 assert!(display.contains("\\Drafts"));
550 }
551
552 #[test]
553 fn test_special_use_attributes_drafts() {
554 let attrs = SpecialUseAttributes::drafts();
555 assert!(attrs.has_attribute("\\Drafts"));
556 assert_eq!(attrs.len(), 1);
557 }
558
559 #[test]
560 fn test_special_use_attributes_sent() {
561 let attrs = SpecialUseAttributes::sent();
562 assert!(attrs.has_attribute("\\Sent"));
563 assert_eq!(attrs.len(), 1);
564 }
565
566 #[test]
567 fn test_special_use_attributes_trash() {
568 let attrs = SpecialUseAttributes::trash();
569 assert!(attrs.has_attribute("\\Trash"));
570 assert_eq!(attrs.len(), 1);
571 }
572
573 #[test]
574 fn test_special_use_attributes_junk() {
575 let attrs = SpecialUseAttributes::junk();
576 assert!(attrs.has_attribute("\\Junk"));
577 assert_eq!(attrs.len(), 1);
578 }
579
580 #[test]
581 fn test_special_use_attributes_archive() {
582 let attrs = SpecialUseAttributes::archive();
583 assert!(attrs.has_attribute("\\Archive"));
584 assert_eq!(attrs.len(), 1);
585 }
586
587 #[test]
588 fn test_special_use_attributes_all() {
589 let attrs = SpecialUseAttributes::all();
590 assert!(attrs.has_attribute("\\All"));
591 assert_eq!(attrs.len(), 1);
592 }
593
594 #[test]
595 fn test_special_use_attributes_flagged() {
596 let attrs = SpecialUseAttributes::flagged();
597 assert!(attrs.has_attribute("\\Flagged"));
598 assert_eq!(attrs.len(), 1);
599 }
600
601 #[test]
602 fn test_special_use_attributes_default() {
603 let attrs = SpecialUseAttributes::default();
604 assert!(attrs.is_empty());
605 }
606
607 #[test]
608 fn test_special_use_attributes_equality() {
609 let mut attrs1 = SpecialUseAttributes::new();
610 attrs1.add("\\Drafts".to_string());
611 attrs1.add("\\Sent".to_string());
612
613 let mut attrs2 = SpecialUseAttributes::new();
614 attrs2.add("\\Sent".to_string());
615 attrs2.add("\\Drafts".to_string());
616
617 assert_eq!(attrs1, attrs2);
618 }
619}