Merge pull request #225 from CrispinStichart/add-gmail-labels

Add support for Gmail Labels
This commit is contained in:
Jon Gjengset 2022-04-14 18:34:56 -07:00 committed by GitHub
commit 0217125962
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 18 additions and 85 deletions

View file

@ -22,7 +22,7 @@ native-tls = { version = "0.2.2", optional = true }
rustls-connector = { version = "0.16.1", optional = true }
regex = "1.0"
bufstream = "0.1.3"
imap-proto = "0.15.0"
imap-proto = "0.16.0"
nom = { version = "7.1.0", default-features = false }
base64 = "0.13"
chrono = { version = "0.4", default-features = false, features = ["std"]}

View file

@ -417,7 +417,7 @@ mod tests {
let first = names.get(0).unwrap();
assert_eq!(
first.attributes(),
&[NameAttribute::from("\\HasNoChildren")]
&[NameAttribute::Extension(Cow::Borrowed("\\HasNoChildren"))]
);
assert_eq!(first.delimiter(), Some("."));
assert_eq!(first.name(), "INBOX");
@ -507,7 +507,7 @@ mod tests {
let first = names.get(0).unwrap();
assert_eq!(
first.attributes(),
&[NameAttribute::from("\\HasNoChildren")]
&[NameAttribute::Extension(Cow::Borrowed("\\HasNoChildren"))]
);
assert_eq!(first.delimiter(), Some("."));
assert_eq!(first.name(), "INBOX");

View file

@ -219,6 +219,17 @@ impl<'a> Fetch<'a> {
})
}
/// Extract the `X-GM-LABELS` of a `FETCH` response
///
/// This is a Gmail-specific extension. See their
/// [developer's page](https://developers.google.com/gmail/imap/imap-extensions) for details.
pub fn gmail_labels(&'a self) -> Option<impl Iterator<Item = &'a str>> {
self.fetch.iter().find_map(|av| match av {
AttributeValue::GmailLabels(labels) => Some(labels.iter().map(|cow| cow.as_ref())),
_ => None,
})
}
/// Get an owned copy of the [`Fetch`].
pub fn into_owned(self) -> Fetch<'static> {
Fetch {

View file

@ -113,7 +113,7 @@ mod mailbox;
pub use self::mailbox::Mailbox;
mod name;
pub use self::name::{Name, NameAttribute, Names};
pub use self::name::{Name, Names};
mod capabilities;
pub use self::capabilities::Capabilities;

View file

@ -1,7 +1,7 @@
use crate::error::Error;
use crate::parse::{parse_many_into, MapOrNot};
use crate::types::UnsolicitedResponse;
use imap_proto::{MailboxDatum, Response};
use imap_proto::{MailboxDatum, NameAttribute, Response};
use ouroboros::self_referencing;
use std::borrow::Cow;
use std::slice::Iter;
@ -28,11 +28,11 @@ impl Names {
let mut names = Vec::new();
parse_many_into(input, &mut names, unsolicited, |response| match response {
Response::MailboxData(MailboxDatum::List {
flags,
name_attributes,
delimiter,
name,
}) => Ok(MapOrNot::Map(Name {
attributes: flags.into_iter().map(NameAttribute::from).collect(),
attributes: name_attributes,
delimiter,
name,
})),
@ -73,84 +73,6 @@ pub struct Name<'a> {
pub(crate) name: Cow<'a, str>,
}
/// An attribute set for an IMAP name.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum NameAttribute<'a> {
/// It is not possible for any child levels of hierarchy to exist
/// under this name; no child levels exist now and none can be
/// created in the future.
NoInferiors,
/// It is not possible to use this name as a selectable mailbox.
NoSelect,
/// The mailbox has been marked "interesting" by the server; the
/// mailbox probably contains messages that have been added since
/// the last time the mailbox was selected.
Marked,
/// The mailbox does not contain any additional messages since the
/// last time the mailbox was selected.
Unmarked,
/// A non-standard user- or server-defined name attribute.
Custom(Cow<'a, str>),
}
impl NameAttribute<'static> {
fn system(s: &str) -> Option<Self> {
match s {
"\\Noinferiors" => Some(NameAttribute::NoInferiors),
"\\Noselect" => Some(NameAttribute::NoSelect),
"\\Marked" => Some(NameAttribute::Marked),
"\\Unmarked" => Some(NameAttribute::Unmarked),
_ => None,
}
}
}
impl<'a> NameAttribute<'a> {
fn into_owned(self) -> NameAttribute<'static> {
match self {
NameAttribute::NoInferiors => NameAttribute::NoInferiors,
NameAttribute::NoSelect => NameAttribute::NoSelect,
NameAttribute::Marked => NameAttribute::Marked,
NameAttribute::Unmarked => NameAttribute::Unmarked,
NameAttribute::Custom(cow) => NameAttribute::Custom(Cow::Owned(cow.into_owned())),
}
}
}
impl<'a> From<String> for NameAttribute<'a> {
fn from(s: String) -> Self {
if let Some(f) = NameAttribute::system(&s) {
f
} else {
NameAttribute::Custom(Cow::Owned(s))
}
}
}
impl<'a> From<Cow<'a, str>> for NameAttribute<'a> {
fn from(s: Cow<'a, str>) -> Self {
if let Some(f) = NameAttribute::system(&*s) {
f
} else {
NameAttribute::Custom(s)
}
}
}
impl<'a> From<&'a str> for NameAttribute<'a> {
fn from(s: &'a str) -> Self {
if let Some(f) = NameAttribute::system(s) {
f
} else {
NameAttribute::Custom(Cow::Borrowed(s))
}
}
}
impl<'a> Name<'a> {
/// Attributes of this name.
pub fn attributes(&self) -> &[NameAttribute<'a>] {