Skip to main content

protonmail_client/
folder.rs

1//! IMAP folder types
2//!
3//! Provides a strongly-typed enum for IMAP folders instead of raw
4//! strings. Well-known folders like INBOX, Sent, and Trash have
5//! dedicated constructors. User-defined folders use the `Custom`
6//! variant.
7
8use std::fmt;
9
10/// An IMAP mailbox folder.
11///
12/// Well-known folders have dedicated variants that map to their
13/// standard IMAP names. For user-created folders, use
14/// [`Folder::custom`].
15///
16/// # Examples
17///
18/// ```
19/// use protonmail_client::Folder;
20///
21/// let inbox = Folder::Inbox;
22/// assert_eq!(inbox.as_str(), "INBOX");
23///
24/// let custom = Folder::custom("My Projects");
25/// assert_eq!(custom.as_str(), "My Projects");
26/// ```
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
28pub enum Folder {
29    /// The INBOX folder (RFC 3501 required, case-insensitive).
30    Inbox,
31    /// Sent messages.
32    Sent,
33    /// Draft messages.
34    Drafts,
35    /// Deleted messages.
36    Trash,
37    /// Spam / junk messages.
38    Spam,
39    /// Archived messages.
40    Archive,
41    /// A user-defined or server-specific folder.
42    Custom(String),
43}
44
45impl Folder {
46    /// Create a folder for a user-defined or non-standard mailbox.
47    #[must_use]
48    pub fn custom(name: impl Into<String>) -> Self {
49        Self::Custom(name.into())
50    }
51
52    /// The IMAP folder name as a string slice.
53    #[must_use]
54    pub fn as_str(&self) -> &str {
55        match self {
56            Self::Inbox => "INBOX",
57            Self::Sent => "Sent",
58            Self::Drafts => "Drafts",
59            Self::Trash => "Trash",
60            Self::Spam => "Spam",
61            Self::Archive => "Archive",
62            Self::Custom(name) => name,
63        }
64    }
65}
66
67impl fmt::Display for Folder {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        f.write_str(self.as_str())
70    }
71}
72
73impl From<&str> for Folder {
74    fn from(s: &str) -> Self {
75        if s.eq_ignore_ascii_case("inbox") {
76            Self::Inbox
77        } else {
78            match s {
79                "Sent" => Self::Sent,
80                "Drafts" => Self::Drafts,
81                "Trash" => Self::Trash,
82                "Spam" => Self::Spam,
83                "Archive" => Self::Archive,
84                other => Self::Custom(other.to_string()),
85            }
86        }
87    }
88}
89
90impl From<String> for Folder {
91    fn from(s: String) -> Self {
92        Self::from(s.as_str())
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn inbox_name() {
102        assert_eq!(Folder::Inbox.as_str(), "INBOX");
103    }
104
105    #[test]
106    fn custom_name() {
107        let f = Folder::custom("Work");
108        assert_eq!(f.as_str(), "Work");
109    }
110
111    #[test]
112    fn from_str_inbox_case_insensitive() {
113        assert_eq!(Folder::from("inbox"), Folder::Inbox);
114        assert_eq!(Folder::from("INBOX"), Folder::Inbox);
115        assert_eq!(Folder::from("Inbox"), Folder::Inbox);
116    }
117
118    #[test]
119    fn from_str_known_folders() {
120        assert_eq!(Folder::from("Sent"), Folder::Sent);
121        assert_eq!(Folder::from("Drafts"), Folder::Drafts);
122        assert_eq!(Folder::from("Trash"), Folder::Trash);
123        assert_eq!(Folder::from("Spam"), Folder::Spam);
124        assert_eq!(Folder::from("Archive"), Folder::Archive);
125    }
126
127    #[test]
128    fn from_str_unknown_becomes_custom() {
129        assert_eq!(
130            Folder::from("My Stuff"),
131            Folder::Custom("My Stuff".to_string())
132        );
133    }
134
135    #[test]
136    fn display_matches_as_str() {
137        assert_eq!(format!("{}", Folder::Inbox), "INBOX");
138        assert_eq!(format!("{}", Folder::custom("Notes")), "Notes");
139    }
140}