use crate::error::Error;
use crate::parse::{parse_many_into, MapOrNot};
use crate::types::UnsolicitedResponse;
use imap_proto::{Capability, Response};
use ouroboros::self_referencing;
use std::collections::hash_set::Iter;
use std::collections::HashSet;
use std::sync::mpsc;
const IMAP4REV1_CAPABILITY: &str = "IMAP4rev1";
const AUTH_CAPABILITY_PREFIX: &str = "AUTH=";
#[self_referencing]
pub struct Capabilities {
data: Vec<u8>,
#[borrows(data)]
#[covariant]
pub(crate) capabilities: HashSet<Capability<'this>>,
}
impl Capabilities {
pub(crate) fn parse(
owned: Vec<u8>,
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
) -> Result<Self, Error> {
CapabilitiesTryBuilder {
data: owned,
capabilities_builder: |input| {
let mut caps = HashSet::new();
parse_many_into(input, &mut caps, unsolicited, |response| match response {
Response::Capabilities(c) => Ok(MapOrNot::MapVec(c)),
resp => Ok(MapOrNot::Not(resp)),
})?;
Ok(caps)
},
}
.try_build()
}
pub fn has(&self, cap: &Capability<'_>) -> bool {
self.borrow_capabilities().contains(cap)
}
pub fn has_str<S: AsRef<str>>(&self, cap: S) -> bool {
let s = cap.as_ref();
if s.eq_ignore_ascii_case(IMAP4REV1_CAPABILITY) {
return self.has(&Capability::Imap4rev1);
}
if s.len() > AUTH_CAPABILITY_PREFIX.len() {
let (pre, val) = s.split_at(AUTH_CAPABILITY_PREFIX.len());
if pre.eq_ignore_ascii_case(AUTH_CAPABILITY_PREFIX) {
return self.has(&Capability::Auth(val.into()));
}
}
self.has(&Capability::Atom(s.into()))
}
pub fn iter(&self) -> Iter<'_, Capability<'_>> {
self.borrow_capabilities().iter()
}
pub fn len(&self) -> usize {
self.borrow_capabilities().len()
}
pub fn is_empty(&self) -> bool {
self.borrow_capabilities().is_empty()
}
}