diff --git a/melib/src/email/list_management.rs b/melib/src/email/list_management.rs index c797395c..64a8d248 100644 --- a/melib/src/email/list_management.rs +++ b/melib/src/email/list_management.rs @@ -48,7 +48,7 @@ impl<'a> From<&'a [u8]> for ListAction<'a> { impl<'a> ListAction<'a> { pub fn parse_options_list(input: &'a [u8]) -> Option; 4]>> { - parser::generic::angle_bracket_delimeted_list(input) + parser::mailing_lists::rfc_2369_list_headers_action_list(input) .map(|(_, mut vec)| { /* Prefer email options first, since this _is_ a mail client after all and it's * more automated */ diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs index 089493ad..634db9b0 100644 --- a/melib/src/email/parser.rs +++ b/melib/src/email/parser.rs @@ -41,8 +41,8 @@ macro_rules! to_str { } #[derive(Eq, PartialEq)] pub struct ParsingError { - input: I, - error: Cow<'static, str>, + pub input: I, + pub error: Cow<'static, str>, } impl core::fmt::Debug for ParsingError<&'_ [u8]> { @@ -269,13 +269,6 @@ pub fn mail(input: &[u8]) -> Result<(Vec<(&[u8], &[u8])>, &[u8])> { pub mod generic { use super::*; - pub fn angle_bracket_delimeted_list(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { - separated_nonempty_list(is_a(","), delimited(tag("<"), take_until(">"), tag(">")))( - input.rtrim(), - ) - // separated_nonempty_list!(complete!(is_a!(",")), ws!(complete!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))))))); - } - pub fn date(input: &[u8]) -> Result { let (_, mut parsed_result) = encodings::phrase(&eat_comments(input), false)?; if let Some(pos) = parsed_result.find(b"-0000") { @@ -839,6 +832,69 @@ pub mod generic { } } +pub mod mailing_lists { + //! Mailing lists headers. + //! + //! Implemented RFCs: + //! + //! - [RFC2369 "The Use of URLs as Meta-Syntax for Core Mail List Commands and their Transport through Message Header Fields"](https://tools.ietf.org/html/rfc2369) + use super::*; + use generic::cfws; + + ///Parse the value of headers defined in RFC2369 "The Use of URLs as Meta-Syntax for Core + ///Mail List Commands and their Transport through Message Header Fields" + pub fn rfc_2369_list_headers_action_list(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + let (input, _) = opt(cfws)(input)?; + let (input, ret) = alt(( + separated_nonempty_list( + delimited( + map(opt(cfws), |_| ()), + map(is_a(", "), |_| ()), + map(opt(cfws), |_| ()), + ), + delimited(tag("<"), take_until(">"), tag(">")), + ), + map(delimited(tag("<"), take_until(">"), tag(">")), |el| { + vec![el] + }), + map( + delimited( + map(opt(cfws), |_| ()), + map(tag("NO"), |_| ()), + map(opt(cfws), |_| ()), + ), + |_| vec![], + ), + ))(input)?; + let (input, _) = opt(cfws)(input)?; + Ok((input, ret)) + } + + #[test] + fn test_parser_rfc_2369_list() { + let s = r#"List-Help: (List Instructions) +List-Help: +List-Help: (Info about the list) +List-Help: , +List-Help: (FTP), + +List-Post: +List-Post: (Postings are Moderated) +List-Post: +List-Post: NO (posting not allowed on this list) +List-Archive: +List-Archive: +List-Archive: (Web Archive) +"#; + let (rest, headers) = headers::headers(s.as_bytes()).unwrap(); + assert!(rest.is_empty()); + for (h, v) in headers { + let (rest, action_list) = rfc_2369_list_headers_action_list(v).unwrap(); + assert!(rest.is_empty()); + } + } +} + pub mod headers { use super::*;