use std::borrow::Cow;
use nom::{
bytes::streaming::tag_no_case,
character::complete::{space0, space1},
combinator::map,
multi::separated_list0,
sequence::{preceded, separated_pair},
IResult, Parser,
};
use crate::core::{astring_utf8, to_owned_cow};
use crate::rfc3501::mailbox;
use crate::Response;
#[derive(Debug, Eq, PartialEq)]
pub struct Acl<'a> {
pub mailbox: Cow<'a, str>,
pub acls: Vec<AclEntry<'a>>,
}
impl<'a> Acl<'a> {
pub fn into_owned(self) -> Acl<'static> {
Acl {
mailbox: to_owned_cow(self.mailbox),
acls: self.acls.into_iter().map(AclEntry::into_owned).collect(),
}
}
}
pub(crate) fn acl(i: &[u8]) -> IResult<&[u8], Response<'_>> {
let (rest, (_, _, mailbox, acls)) = (tag_no_case("ACL"), space1, mailbox, acl_list).parse(i)?;
Ok((rest, Response::Acl(Acl { mailbox, acls })))
}
fn acl_list(i: &[u8]) -> IResult<&[u8], Vec<AclEntry<'_>>> {
preceded(space0, separated_list0(space1, AclEntry::parse)).parse(i)
}
#[derive(Debug, Eq, PartialEq)]
pub struct AclEntry<'a> {
pub identifier: Cow<'a, str>,
pub rights: Vec<AclRight>,
}
impl<'a> AclEntry<'a> {
fn parse(i: &'a [u8]) -> IResult<&'a [u8], Self> {
let (rest, (identifier, rights)) = separated_pair(
astring_utf8,
space1,
map(astring_utf8, |s| map_text_to_rights(&s)),
)
.parse(i)?;
Ok((rest, AclEntry { identifier, rights }))
}
pub fn into_owned(self) -> AclEntry<'static> {
AclEntry {
identifier: to_owned_cow(self.identifier),
rights: self.rights,
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct ListRights<'a> {
pub mailbox: Cow<'a, str>,
pub identifier: Cow<'a, str>,
pub required: Vec<AclRight>,
pub optional: Vec<AclRight>,
}
impl<'a> ListRights<'a> {
pub fn into_owned(self) -> ListRights<'static> {
ListRights {
mailbox: to_owned_cow(self.mailbox),
identifier: to_owned_cow(self.identifier),
required: self.required,
optional: self.optional,
}
}
}
pub(crate) fn list_rights(i: &[u8]) -> IResult<&[u8], Response<'_>> {
let (rest, (_, _, mailbox, _, identifier, _, required, optional)) = (
tag_no_case("LISTRIGHTS"),
space1,
mailbox,
space1,
astring_utf8,
space1,
map(astring_utf8, |s| map_text_to_rights(&s)),
list_rights_optional,
)
.parse(i)?;
Ok((
rest,
Response::ListRights(ListRights {
mailbox,
identifier,
required,
optional,
}),
))
}
fn list_rights_optional(i: &[u8]) -> IResult<&[u8], Vec<AclRight>> {
let (rest, items) = preceded(space0, separated_list0(space1, astring_utf8)).parse(i)?;
Ok((
rest,
items
.into_iter()
.flat_map(|s| s.chars().map(AclRight::from).collect::<Vec<_>>())
.collect(),
))
}
#[derive(Debug, Eq, PartialEq)]
pub struct MyRights<'a> {
pub mailbox: Cow<'a, str>,
pub rights: Vec<AclRight>,
}
impl<'a> MyRights<'a> {
pub fn into_owned(self) -> MyRights<'static> {
MyRights {
mailbox: to_owned_cow(self.mailbox),
rights: self.rights,
}
}
}
pub(crate) fn my_rights(i: &[u8]) -> IResult<&[u8], Response<'_>> {
let (rest, (_, _, mailbox, _, rights)) = (
tag_no_case("MYRIGHTS"),
space1,
mailbox,
space1,
map(astring_utf8, |s| map_text_to_rights(&s)),
)
.parse(i)?;
Ok((rest, Response::MyRights(MyRights { mailbox, rights })))
}
fn map_text_to_rights(i: &str) -> Vec<AclRight> {
i.chars().map(|c| c.into()).collect()
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum AclRight {
Lookup,
Read,
Seen,
Write,
Insert,
Post,
CreateMailbox,
DeleteMailbox,
DeleteMessage,
Expunge,
Administer,
Annotation,
OldCreate,
OldDelete,
Custom(char),
}
impl From<char> for AclRight {
fn from(c: char) -> Self {
match c {
'l' => AclRight::Lookup,
'r' => AclRight::Read,
's' => AclRight::Seen,
'w' => AclRight::Write,
'i' => AclRight::Insert,
'p' => AclRight::Post,
'k' => AclRight::CreateMailbox,
'x' => AclRight::DeleteMailbox,
't' => AclRight::DeleteMessage,
'e' => AclRight::Expunge,
'a' => AclRight::Administer,
'n' => AclRight::Annotation,
'c' => AclRight::OldCreate,
'd' => AclRight::OldDelete,
_ => AclRight::Custom(c),
}
}
}
impl From<AclRight> for char {
fn from(right: AclRight) -> Self {
match right {
AclRight::Lookup => 'l',
AclRight::Read => 'r',
AclRight::Seen => 's',
AclRight::Write => 'w',
AclRight::Insert => 'i',
AclRight::Post => 'p',
AclRight::CreateMailbox => 'k',
AclRight::DeleteMailbox => 'x',
AclRight::DeleteMessage => 't',
AclRight::Expunge => 'e',
AclRight::Administer => 'a',
AclRight::Annotation => 'n',
AclRight::OldCreate => 'c',
AclRight::OldDelete => 'd',
AclRight::Custom(c) => c,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_char_to_acl_right() {
assert_eq!(Into::<AclRight>::into('l'), AclRight::Lookup);
assert_eq!(Into::<AclRight>::into('c'), AclRight::OldCreate);
assert_eq!(Into::<AclRight>::into('k'), AclRight::CreateMailbox);
assert_eq!(Into::<AclRight>::into('0'), AclRight::Custom('0'));
}
#[test]
fn test_acl_right_to_char() {
assert_eq!(Into::<char>::into(AclRight::Lookup), 'l');
assert_eq!(Into::<char>::into(AclRight::OldCreate), 'c');
assert_eq!(Into::<char>::into(AclRight::CreateMailbox), 'k');
assert_eq!(Into::<char>::into(AclRight::Custom('0')), '0');
}
}