email-parser 0.5.0

The fastest and lightest email parsing Rust library. Supports MIME.
Documentation
use crate::prelude::*;
use std::borrow::Cow;

#[inline]
pub fn fws(input: &[u8]) -> Res<Cow<str>> {
    let (input, before) = optional(input, |input| {
        pair(
            input,
            |input| take_while(input, is_wsp),
            |input| tag(input, b"\r\n"),
        )
    });
    let (input, after) = take_while1(input, is_wsp)?;

    if let Some((before, _crlf)) = before {
        let mut before = Cow::Borrowed(before);
        add_str(&mut before, after);
        Ok((input, before))
    } else {
        Ok((input, Cow::Borrowed(after)))
    }
}

#[inline]
pub fn ccontent(input: &[u8]) -> Res<Cow<str>> {
    match_parsers(
        input,
        &mut [
            (|i| {
                let (input, value) = take_while1(i, is_ctext)?;
                Ok((input, Cow::Borrowed(value)))
            }) as fn(input: &[u8]) -> Res<Cow<str>>,
            quoted_pair,
            comment,
        ][..],
    )
}

#[inline]
pub fn comment(input: &[u8]) -> Res<Cow<str>> {
    let (input, ()) = tag(input, b"(")?;

    let (input, _) = ignore_many(input, |input| {
        pair(input, |i| Ok(optional(i, fws)), ccontent)
    })?;

    let (input, _) = optional(input, fws);
    let (input, ()) = tag(input, b")")?;

    Ok((input, empty_string()))
}

#[inline]
pub fn cfws(input: &[u8]) -> Res<Cow<str>> {
    fn real_cfws(mut input: &[u8]) -> Res<Cow<str>> {
        let mut output = empty_string();

        let (new_input, folding_wsp) = optional(input, fws);
        if let Ok((new_input, _comment)) = comment(new_input) {
            input = new_input;
            if let Some(s) = folding_wsp {
                add_string(&mut output, s);
            }
        } else {
            return Err(Error::Unknown("Expected at least one comment"));
        }

        loop {
            let (new_input, folding_wsp) = optional(input, fws);

            if let Ok((new_input, _comment)) = comment(new_input) {
                input = new_input;
                if let Some(s) = folding_wsp {
                    add_string(&mut output, s);
                }
            } else {
                break;
            }
        }

        let (input, fws) = optional(input, fws);
        if let Some(s) = fws {
            add_string(&mut output, s);
        }

        Ok((input, output))
    }

    match_parsers(input, &mut [real_cfws, fws][..])
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_fws() {
        assert_eq!(fws(b"   test").unwrap().1, "   ");
        assert_eq!(fws(b" test").unwrap().1, " ");
        assert_eq!(fws(b"   \r\n  test").unwrap().1, "     ");

        assert!(fws(b"  \r\ntest").is_err());
        assert!(fws(b"\r\ntest").is_err());
        assert!(fws(b"test").is_err());
    }

    #[test]
    fn test_ccontent() {
        assert_eq!(ccontent(b"abcde").unwrap().1, "abcde");
        assert_eq!(ccontent(b"ab)cde").unwrap().1, "ab");
    }

    #[test]
    fn test_comment() {
        assert_eq!(comment(b"(this is a comment)").unwrap().0.len(), 0);
        assert_eq!(comment(b"(a comment) and a value").unwrap().0.len(), 12);
        assert_eq!(
            comment(b"(this is a comment (and another comment)) and a value")
                .unwrap()
                .0
                .len(),
            12
        );

        assert!(comment(b"a value").is_err());
        assert!(comment(b"(unclosed comment").is_err());
    }

    #[test]
    fn test_cfws() {
        assert_eq!(
            cfws(b"  (this is a comment)\r\n (this is a second comment)  value")
                .unwrap()
                .1,
            "     "
        );

        assert_eq!(
            cfws(b"  (this is a comment)\r\n (this is a second comment)\r\n  value")
                .unwrap()
                .1,
            "     "
        );
    }
}