imap-proto 0.17.0

IMAP protocol parser and data structures
Documentation
use nom::{
    branch::alt,
    bytes::streaming::{tag, tag_no_case},
    character::streaming::char,
    combinator::{map, opt},
    multi::many0,
    sequence::{delimited, preceded},
    IResult, Parser,
};
use std::borrow::Cow;

use crate::core::*;
use crate::rfc3501::AttributeValue;

pub fn section_part(i: &[u8]) -> IResult<&[u8], Vec<u32>> {
    let (i, (part, mut rest)) = (number, many0(preceded(char('.'), number))).parse(i)?;
    rest.insert(0, part);
    Ok((i, rest))
}

pub fn section_msgtext(i: &[u8]) -> IResult<&[u8], MessageSection> {
    alt((
        map(
            (
                tag_no_case("HEADER.FIELDS"),
                opt(tag_no_case(".NOT")),
                tag(" "),
                parenthesized_list(astring),
            ),
            |_| MessageSection::Header,
        ),
        map(tag_no_case("HEADER"), |_| MessageSection::Header),
        map(tag_no_case("TEXT"), |_| MessageSection::Text),
    ))
    .parse(i)
}

#[derive(Debug, Eq, PartialEq)]
pub enum MessageSection {
    Header,
    Mime,
    Text,
}

impl MessageSection {
    pub fn parse(i: &[u8]) -> IResult<&[u8], Self> {
        alt((
            section_msgtext,
            map(tag_no_case("MIME"), |_| MessageSection::Mime),
        ))
        .parse(i)
    }
}

#[derive(Debug, Eq, PartialEq)]
pub enum SectionPath {
    Full(MessageSection),
    Part(Vec<u32>, Option<MessageSection>),
}

impl SectionPath {
    pub fn parse(i: &[u8]) -> IResult<&[u8], Self> {
        alt((
            map(section_msgtext, SectionPath::Full),
            map(
                (
                    section_part,
                    opt(preceded(char('.'), MessageSection::parse)),
                ),
                |(part, text)| SectionPath::Part(part, text),
            ),
        ))
        .parse(i)
    }
}

pub fn section(i: &[u8]) -> IResult<&[u8], Option<SectionPath>> {
    delimited(char('['), opt(SectionPath::parse), char(']')).parse(i)
}

pub fn msg_att_body_section(i: &[u8]) -> IResult<&[u8], AttributeValue<'_>> {
    map(
        (
            tag_no_case("BODY"),
            section,
            opt(delimited(char('<'), number, char('>'))),
            tag(" "),
            nstring,
        ),
        |(_, section, index, _, data)| AttributeValue::BodySection {
            section,
            index,
            data: data.map(Cow::Borrowed),
        },
    )
    .parse(i)
}