unidok-parser 0.2.0

Parser for the Unidok document format
Documentation
use detached_str::StrSlice;
use unidok_repr::ast::html::AttrAst;

use crate::utils::{ParseWs, QuotedString, Until};
use crate::{Indents, Input, Parse};

pub(crate) struct ParseAttribute<'a> {
    ind: Indents<'a>,
}

impl Parse for ParseAttribute<'_> {
    type Output = AttrAst;

    fn parse(&mut self, input: &mut Input) -> Option<Self::Output> {
        let mut input = input.start();

        let key = input.parse(ParseAttrName)?;
        input.parse_i(ParseWs(self.ind));

        let value = if input.parse('=').is_some() {
            input.parse_i(ParseWs(self.ind));

            let value = input.parse(QuotedString(self.ind)).or_else(|| {
                input.parse(ParseAttrName).map(|s| s.to_str(&input.text).to_string())
            })?;

            Some(value)
        } else {
            None
        };

        input.apply();
        Some(AttrAst { key, value })
    }
}

pub(crate) struct ParseAttributes<'a> {
    pub ind: Indents<'a>,
}

impl Parse for ParseAttributes<'_> {
    type Output = Vec<AttrAst>;

    fn parse(&mut self, input: &mut Input) -> Option<Self::Output> {
        let mut attrs = vec![];

        while !matches!(input.peek_char(), Some('>' | '/')) {
            attrs.push(input.parse(ParseAttribute { ind: self.ind })?);
        }

        Some(attrs)
    }
}

struct ParseAttrName;

impl Parse for ParseAttrName {
    type Output = StrSlice;

    fn parse(&mut self, input: &mut Input) -> Option<Self::Output> {
        let s = input.parse_i(Until(|c| {
            matches!(c, ' ' | '\t' | '\r' | '\n' | '"' | '\'' | '>' | '<' | '=')
        }));
        if s.is_empty() {
            None
        } else {
            Some(s)
        }
    }
}