#[non_exhaustive]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Flag {
Seen,
Answered,
Flagged,
Deleted,
Draft,
Recent,
Wildcard,
Custom(String),
}
impl Flag {
pub fn as_imap_str(&self) -> &str {
match self {
Self::Seen => "\\Seen",
Self::Answered => "\\Answered",
Self::Flagged => "\\Flagged",
Self::Deleted => "\\Deleted",
Self::Draft => "\\Draft",
Self::Recent => "\\Recent",
Self::Wildcard => "\\*",
Self::Custom(s) => s,
}
}
pub fn from_imap_str(s: &str) -> Self {
let lower = s.to_ascii_lowercase();
match lower.as_str() {
"\\seen" => Self::Seen,
"\\answered" => Self::Answered,
"\\flagged" => Self::Flagged,
"\\deleted" => Self::Deleted,
"\\draft" => Self::Draft,
"\\recent" => Self::Recent,
"\\*" => Self::Wildcard,
_ => Self::Custom(s.to_owned()),
}
}
}
impl std::fmt::Display for Flag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_imap_str())
}
}
impl PartialEq for Flag {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Seen, Self::Seen)
| (Self::Answered, Self::Answered)
| (Self::Flagged, Self::Flagged)
| (Self::Deleted, Self::Deleted)
| (Self::Draft, Self::Draft)
| (Self::Recent, Self::Recent)
| (Self::Wildcard, Self::Wildcard) => true,
(Self::Custom(a), Self::Custom(b)) => a.eq_ignore_ascii_case(b),
(Self::Custom(s), known) | (known, Self::Custom(s)) => {
s.eq_ignore_ascii_case(known.as_imap_str())
}
_ => false,
}
}
}
impl Eq for Flag {}
impl std::hash::Hash for Flag {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
for byte in self.as_imap_str().as_bytes() {
byte.to_ascii_lowercase().hash(state);
}
}
}
impl From<String> for Flag {
fn from(s: String) -> Self {
Self::from_imap_str(&s)
}
}
impl From<&str> for Flag {
fn from(s: &str) -> Self {
Self::from_imap_str(s)
}
}
#[cfg(test)]
#[path = "flag_tests.rs"]
mod tests;