io_email/m2dir/
convert.rs1use alloc::{
5 string::{String, ToString},
6 vec::Vec,
7};
8use std::path::PathBuf;
9
10use chrono::DateTime;
11use io_m2dir::{
12 entry::types::M2dirEntry,
13 flag::types::M2dirFlags,
14 m2dir::types::M2dir,
15 path::M2dirPath,
16 store::{M2dirStore, M2dirStoreError},
17};
18use mail_parser::{Address as MailParserAddress, Message as ParsedMessage};
19use thiserror::Error;
20
21use crate::{
22 address::Address,
23 envelope::types::{Envelope, normalize_message_id},
24 flag::types::Flag,
25};
26
27#[derive(Debug, Error)]
29pub enum InvalidMailboxName {
30 #[error(transparent)]
31 Store(#[from] M2dirStoreError),
32}
33
34pub(crate) fn store_from_root(root: impl Into<PathBuf>) -> M2dirStore {
36 let path: M2dirPath = root.into().into();
37 M2dirStore::from_path(path)
38}
39
40pub(crate) fn resolve_mailbox(
43 root: impl Into<PathBuf>,
44 name: &str,
45) -> Result<M2dir, InvalidMailboxName> {
46 let store = store_from_root(root);
47 let path = store.resolve_folder_path(name)?;
48 Ok(M2dir::from_path(path))
49}
50
51pub(crate) fn flag_from_meta_line(line: &str) -> Flag {
53 Flag::from_raw(line.trim())
54}
55
56pub(crate) fn flag_to_meta_line(flag: &Flag) -> String {
58 flag.raw().to_string()
59}
60
61pub(crate) fn flags_to_m2dir(flags: &[Flag]) -> M2dirFlags {
63 flags.iter().map(flag_to_meta_line).collect()
64}
65
66pub fn envelope_from(
69 entry: &M2dirEntry,
70 meta: &M2dirFlags,
71 parsed: &ParsedMessage<'_>,
72) -> Envelope {
73 let id = entry.id().to_string();
74 let flags = meta.iter().map(flag_from_meta_line).collect();
75 let subject = parsed.subject().unwrap_or_default().to_string();
76 let from = parsed.from().map(addresses_from).unwrap_or_default();
77 let to = parsed.to().map(addresses_from).unwrap_or_default();
78 let date = parsed
79 .date()
80 .and_then(|d| DateTime::parse_from_rfc3339(&d.to_rfc3339()).ok());
81 let size = parsed.raw_message().len() as u64;
82 let message_id = parsed.message_id().and_then(normalize_message_id);
83 Envelope {
84 id,
85 message_id,
86 flags,
87 subject,
88 from,
89 to,
90 date,
91 size,
92 has_attachment: None,
93 }
94}
95
96pub(crate) fn paginate<T>(items: Vec<T>, page: Option<u32>, page_size: Option<u32>) -> Vec<T> {
99 let Some(size) = page_size else {
100 return items;
101 };
102 if size == 0 {
103 return Vec::new();
104 }
105 let page = page.unwrap_or(1).max(1);
106 let skip = ((page - 1) as usize).saturating_mul(size as usize);
107 if skip >= items.len() {
108 return Vec::new();
109 }
110 items.into_iter().skip(skip).take(size as usize).collect()
111}
112
113fn addresses_from(addrs: &MailParserAddress<'_>) -> Vec<Address> {
114 addrs
115 .clone()
116 .into_list()
117 .into_iter()
118 .filter_map(|a| {
119 let email = a.address?.into_owned();
120 if email.is_empty() {
121 return None;
122 }
123 let name = a.name.map(|s| s.into_owned());
124 Some(Address { name, email })
125 })
126 .collect()
127}