1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//! Module dedicated to Maildir email folders.
//!
//! This module contains folder-related mapping functions from the
//! [maildirpp] crate types.

use log::{debug, trace};
use maildirpp::{Maildir, Submaildirs};
use rayon::prelude::*;
use std::ffi::OsStr;

use crate::{
    account::config::AccountConfig,
    folder::{Folder, Folders},
    maildir,
};

impl Folder {
    /// Parse a folder from a maildir instance.
    ///
    /// Returns [`None`] in case the folder name is too short (does
    /// not start by a dot) or is equal to `notmuch` (which should not
    /// be treated as a maildir folder).
    pub fn from_maildir(config: &AccountConfig, mdir: Maildir) -> Option<Self> {
        let folder = mdir
            .path()
            .file_name()
            .and_then(OsStr::to_str)
            .filter(|folder| folder.len() >= 2)
            .map(|folder| &folder[1..]);

        match folder {
            None => {
                debug!("cannot parse folder from maildir: invalid subdirectory name");
                None
            }
            Some("notmuch") => {
                debug!("skipping folder .notmuch");
                None
            }
            Some(name) => {
                let name = maildir::decode_folder(name);
                let kind = config
                    .find_folder_kind_from_alias(&name)
                    .or_else(|| name.parse().ok());
                let desc = mdir.path().to_owned().to_string_lossy().to_string();

                let folder = Folder { kind, name, desc };
                trace!("parsed maildir folder: {folder:#?}");
                Some(folder)
            }
        }
    }
}

impl Folders {
    /// Parse folders from submaildirs.
    ///
    /// Folders are parsed in parallel, using [`rayon`]. Only parses
    /// direct submaildirs (no recursion).
    pub fn from_submaildirs(config: &AccountConfig, submdirs: Submaildirs) -> Self {
        Folders::from_iter(
            submdirs
                .collect::<Vec<_>>()
                .into_par_iter()
                .filter_map(|mdir| match mdir {
                    Ok(mdir) => Folder::from_maildir(config, mdir),
                    Err(err) => {
                        debug!("cannot parse submaildir: {err}");
                        debug!("{err:?}");
                        None
                    }
                })
                .collect::<Vec<_>>(),
        )
    }
}