use std::borrow::Cow;
use nom::{
bytes::complete::tag,
character::complete::{char, satisfy},
combinator::{opt, recognize},
multi::{many0, many1, separated_list1},
IResult,
};
pub trait Parse<'a>: Sized {
fn parse(_input: &'a str) -> IResult<&'a str, Self> {
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: &'a str) -> IResult<&'a str, char> {
satisfy(Self::is_char)(input)
}
fn is_whitespace(c: char) -> bool {
matches!(c, ' ' | '\t' | '\r' | '\n')
}
fn parse_multispace1(input: &'a str) -> IResult<&'a str, ()> {
let (input, _) = many1(satisfy(Self::is_whitespace))(input)?;
Ok((input, ()))
}
fn parse_multispace0(input: &'a str) -> IResult<&'a 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: &'a str) -> IResult<&'a str, char> {
satisfy(Self::is_name_char)(input)
}
fn parse_name_start_char(input: &'a str) -> IResult<&'a str, char> {
satisfy(Self::is_name_start_char)(input)
}
fn parse_nmtoken(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, result) = recognize(many1(Self::parse_name_char))(input)?;
Ok((input, Cow::Borrowed(result)))
}
fn parse_nmtokens(input: &'a str) -> IResult<&'a str, Vec<Cow<'a, str>>> {
separated_list1(char(' '), Self::parse_nmtoken)(input)
}
fn parse_name(input: &'a str) -> IResult<&'a str, Cow<'a, str>> {
let (input, start_char) = Self::parse_name_start_char(input)?;
let (input, rest_chars) = opt(Self::parse_nmtoken)(input)?;
let mut name = start_char.to_string();
if let Some(rest) = rest_chars {
name.push_str(&rest);
}
Ok((input, Cow::Owned(name)))
}
fn parse_names(input: &'a str) -> IResult<&'a str, Vec<Cow<'a, str>>> {
separated_list1(char(' '), Self::parse_name)(input)
}
fn parse_eq(input: &'a str) -> IResult<&'a str, ()> {
let (input, _) = Self::parse_multispace0(input)?;
let (input, _) = tag("=")(input)?;
let (input, _) = Self::parse_multispace0(input)?;
Ok((input, ()))
}
}