use crate::{transcode::Decode, IResult, Name};
use nom::{
bytes::complete::tag,
character::complete::{char, satisfy},
combinator::{map, opt, recognize},
multi::{many0, many1, separated_list1},
sequence::tuple,
Offset,
};
type CaptureSpan<'a, O> = Box<dyn FnMut(&'a str) -> IResult<&'a str, (&'a str, O)> + 'a>;
pub trait Parse<'a>: Sized {
type Args;
type Output; fn parse(_input: &'a str, _args: Self::Args) -> Self::Output {
unimplemented!()
}
fn is_char(c: char) -> bool {
matches!(c, '\u{9}' | '\u{A}' | '\u{D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..='\u{10FFFF}')
}
fn parse_char(input: &str) -> IResult<&str, char> {
satisfy(Self::is_char)(input)
}
fn is_whitespace(c: char) -> bool {
matches!(c, ' ' | '\t' | '\r' | '\n')
}
fn parse_multispace1(input: &str) -> IResult<&str, ()> {
let (input, _) = many1(satisfy(Self::is_whitespace))(input)?;
Ok((input, ()))
}
fn parse_multispace0(input: &str) -> IResult<&str, ()> {
let (input, _) = many0(satisfy(Self::is_whitespace))(input)?;
Ok((input, ()))
}
fn is_name_start_char(c: char) -> bool {
matches!(c, ':' | 'A'..='Z' | '_' | 'a'..='z' |
'\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
'\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' | '\u{200C}'..='\u{200D}' |
'\u{2070}'..='\u{218F}' | '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
'\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' | '\u{10000}'..='\u{EFFFF}')
}
fn is_name_char(c: char) -> bool {
Self::is_name_start_char(c)
|| matches!(c, '-' | '.' | '0'..='9' | '\u{B7}' |
'\u{0300}'..='\u{036F}' | '\u{203F}'..='\u{2040}')
}
fn parse_name_char(input: &str) -> IResult<&str, char> {
satisfy(Self::is_name_char)(input)
}
fn parse_name_start_char(input: &str) -> IResult<&str, char> {
satisfy(Self::is_name_start_char)(input)
}
fn parse_nmtoken(input: &str) -> IResult<&str, String> {
let (input, result) = recognize(many1(Self::parse_name_char))(input)?;
Ok((input, result.to_string()))
}
fn parse_nmtokens(input: &str) -> IResult<&str, Vec<String>> {
separated_list1(char(' '), Self::parse_nmtoken)(input)
}
fn parse_name(input: &str) -> IResult<&str, Name> {
map(
tuple((Self::parse_name_start_char, opt(Self::parse_nmtoken))),
|(start_char, rest_chars)| {
let mut name = start_char.to_string();
if let Some(rest) = rest_chars {
name.push_str(&rest);
}
let name_clone = name.clone();
let local_part = match name_clone.decode() {
Ok(decoded) => decoded.into_owned(),
Err(_) => name,
};
Name {
prefix: None,
local_part,
}
},
)(input)
}
fn parse_names(input: &str) -> IResult<&str, Vec<Name>> {
separated_list1(char(' '), Self::parse_name)(input)
}
fn parse_eq(input: &str) -> IResult<&str, ()> {
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag("=")(input)?;
let (input, _) = Self::parse_multispace0(input)?;
Ok((input, ()))
}
fn capture_span<O, F>(mut f: F) -> CaptureSpan<'a, O>
where
F: FnMut(&'a str) -> IResult<&'a str, O> + 'a,
{
Box::new(move |input: &'a str| {
let (remaining, result) = f(input)?;
let offset = input.offset(remaining);
Ok((remaining, (&input[..offset], result)))
})
}
}