Skip to main content

io_maildir/maildir/
types.rs

1//! Maildir directory structure.
2
3use core::{
4    fmt,
5    hash::{Hash, Hasher},
6    str::FromStr,
7};
8
9use alloc::string::String;
10
11use thiserror::Error;
12
13use crate::path::FsPath;
14
15/// Failure causes when validating a Maildir on disk.
16#[derive(Clone, Debug, Error)]
17pub enum MaildirError {
18    #[error("path {0} is not a directory")]
19    NotDir(FsPath),
20
21    #[error("missing {0}/ subdirectory at Maildir {1}")]
22    MissingSubdir(&'static str, FsPath),
23
24    #[error("invalid Maildir subdir {0:?}: expected cur, new or tmp")]
25    InvalidSubdir(String),
26}
27
28pub const CUR: &str = "cur";
29pub const NEW: &str = "new";
30pub const TMP: &str = "tmp";
31
32/// One of the three Maildir subdirectories: cur, new, tmp.
33#[derive(Clone, Debug, Eq, PartialEq)]
34pub enum MaildirSubdir {
35    Cur,
36    New,
37    Tmp,
38}
39
40impl FromStr for MaildirSubdir {
41    type Err = MaildirError;
42
43    fn from_str(s: &str) -> Result<Self, Self::Err> {
44        match s {
45            CUR => Ok(Self::Cur),
46            NEW => Ok(Self::New),
47            TMP => Ok(Self::Tmp),
48            _ => Err(MaildirError::InvalidSubdir(s.into())),
49        }
50    }
51}
52
53impl fmt::Display for MaildirSubdir {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            Self::Cur => write!(f, "{CUR}"),
57            Self::New => write!(f, "{NEW}"),
58            Self::Tmp => write!(f, "{TMP}"),
59        }
60    }
61}
62
63/// A Maildir root on the filesystem (with cur/new/tmp subdirs).
64#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
65pub struct Maildir {
66    root: FsPath,
67}
68
69impl Maildir {
70    /// Wraps `root` without checking that the subdirectories exist.
71    pub fn from_path(root: impl Into<FsPath>) -> Self {
72        Self { root: root.into() }
73    }
74
75    pub fn path(&self) -> &FsPath {
76        &self.root
77    }
78
79    pub fn name(&self) -> Option<&str> {
80        self.root.file_name()
81    }
82
83    pub fn subdir(&self, subdir: &MaildirSubdir) -> FsPath {
84        match subdir {
85            MaildirSubdir::Cur => self.cur(),
86            MaildirSubdir::New => self.new(),
87            MaildirSubdir::Tmp => self.tmp(),
88        }
89    }
90
91    pub fn cur(&self) -> FsPath {
92        self.root.join(CUR)
93    }
94
95    pub fn new(&self) -> FsPath {
96        self.root.join(NEW)
97    }
98
99    pub fn tmp(&self) -> FsPath {
100        self.root.join(TMP)
101    }
102}
103
104impl Hash for Maildir {
105    fn hash<H: Hasher>(&self, state: &mut H) {
106        self.root.hash(state);
107    }
108}
109
110impl AsRef<FsPath> for Maildir {
111    fn as_ref(&self) -> &FsPath {
112        &self.root
113    }
114}
115
116impl From<FsPath> for Maildir {
117    fn from(root: FsPath) -> Self {
118        Self { root }
119    }
120}
121
122impl From<String> for Maildir {
123    fn from(root: String) -> Self {
124        Self { root: root.into() }
125    }
126}
127
128impl From<&str> for Maildir {
129    fn from(root: &str) -> Self {
130        Self { root: root.into() }
131    }
132}