use super::types::RawField;
use nom::{
branch::alt,
bytes::complete::{tag, take_until, take_while},
character::complete::{char, line_ending, not_line_ending},
combinator::all_consuming,
multi::{many0, many1},
sequence::{delimited, tuple},
IResult,
};
pub fn body_parser(i: &str) -> IResult<&str, Vec<Option<RawField>>> {
all_consuming(alt((signed_parser, unsigned_parser)))(i)
}
fn signed_parser(i: &str) -> IResult<&str, Vec<Option<RawField>>> {
delimited(sign_header_parser, unsigned_parser, sign_footer_parser)(i)
}
fn cleartext_header_parser(i: &str) -> IResult<&str, &str> {
tag("-----BEGIN PGP SIGNED MESSAGE-----\n")(i)
}
fn hash_armor_header_parser(i: &str) -> IResult<&str, &str> {
delimited(tag("Hash: "), not_line_ending, line_ending)(i)
}
fn hash_armor_headers_parser(i: &str) -> IResult<&str, Vec<&str>> {
many0(hash_armor_header_parser)(i)
}
fn sign_header_parser(i: &str) -> IResult<&str, Vec<&str>> {
delimited(cleartext_header_parser, hash_armor_headers_parser, line_ending)(i)
}
fn armor_header_parser(i: &str) -> IResult<&str, &str> {
tag("-----BEGIN PGP SIGNATURE-----\n")(i)
}
fn signature_parser(i: &str) -> IResult<&str, &str> {
take_until("-----END PGP SIGNATURE-----\n")(i)
}
fn armor_tail_parser(i: &str) -> IResult<&str, &str> {
tag("-----END PGP SIGNATURE-----\n")(i)
}
fn sign_footer_parser(i: &str) -> IResult<&str, &str> {
delimited(armor_header_parser, signature_parser, armor_tail_parser)(i)
}
fn unsigned_parser(i: &str) -> IResult<&str, Vec<Option<RawField>>> {
many1(line_parser)(i)
}
fn line_parser(i: &str) -> IResult<&str, Option<RawField>> {
alt((field_parser, comment_parser, eol_parser))(i)
}
fn is_field_name_char(c: char) -> bool {
c.is_ascii() && !c.is_control() && (c != ':') && (c != ' ')
}
fn field_parser(i: &str) -> IResult<&str, Option<RawField>> {
let (i, (name, _, value, _)) = tuple((take_while(is_field_name_char), tag(": "), not_line_ending, line_ending))(i)?;
Ok((i, Some(RawField { name, value })))
}
fn comment_parser(i: &str) -> IResult<&str, Option<RawField>> {
let (i, _) = tuple((char('#'), not_line_ending, line_ending))(i)?;
Ok((i, None))
}
fn eol_parser(i: &str) -> IResult<&str, Option<RawField>> {
let (i, _) = line_ending(i)?;
Ok((i, None))
}