1use imap_client::imap_next::imap_types::{
2 core::{Atom, QuotedChar},
3 flag::FlagNameAttribute,
4 mailbox::Mailbox,
5};
6use tracing::debug;
7use utf7_imap::decode_utf7_imap as decode_utf7;
8
9use super::{Error, FolderKind, Result};
10use crate::{
11 account::config::AccountConfig,
12 folder::{Folder, Folders},
13};
14
15pub type ImapMailboxes = Vec<ImapMailbox>;
16
17impl Folders {
18 pub fn from_imap_mailboxes(config: &AccountConfig, mboxes: ImapMailboxes) -> Self {
19 mboxes
20 .into_iter()
21 .filter_map(|mbox| match Folder::try_from_imap_mailbox(config, &mbox) {
22 Ok(folder) => Some(folder),
23 Err(_err) => {
24 debug!("skipping IMAP mailbox {:?}: {_err}", mbox.0.clone());
25 None
26 }
27 })
28 .collect()
29 }
30}
31
32pub type ImapMailbox = (
33 Mailbox<'static>,
34 Option<QuotedChar>,
35 Vec<FlagNameAttribute<'static>>,
36);
37
38impl Folder {
39 fn try_from_imap_mailbox(
40 config: &AccountConfig,
41 (mbox, _delim, attrs): &ImapMailbox,
42 ) -> Result<Self> {
43 let mbox = match mbox {
44 Mailbox::Inbox => String::from("INBOX"),
45 Mailbox::Other(mbox) => String::from_utf8_lossy(mbox.as_ref()).to_string(),
46 };
47
48 if attrs.contains(&FlagNameAttribute::Noselect) {
51 return Err(Error::ParseImapFolderNotSelectableError(mbox.clone()));
52 }
53
54 let name = decode_utf7(mbox.into());
55
56 let kind = config
57 .find_folder_kind_from_alias(&name)
58 .or_else(|| find_folder_kind_from_imap_attrs(attrs.as_ref()))
59 .or_else(|| name.parse().ok());
60
61 let desc = attrs.iter().fold(String::default(), |mut desc, attr| {
62 if !desc.is_empty() {
63 desc.push_str(", ")
64 }
65 desc.push_str(&format!("{attr}"));
66 desc
67 });
68
69 Ok(Folder { kind, name, desc })
70 }
71}
72
73pub fn find_folder_kind_from_imap_attrs(attrs: &[FlagNameAttribute]) -> Option<FolderKind> {
74 attrs.iter().find_map(|attr| {
75 if attr == &FlagNameAttribute::from(Atom::try_from("Sent").unwrap()) {
76 Some(FolderKind::Sent)
77 } else if attr == &FlagNameAttribute::from(Atom::try_from("Drafts").unwrap()) {
78 Some(FolderKind::Drafts)
79 } else if attr == &FlagNameAttribute::from(Atom::try_from("Trash").unwrap()) {
80 Some(FolderKind::Trash)
81 } else {
82 None
83 }
84 })
85}