You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
1.9 KiB
Rust

use std::collections::HashMap;
use crate::parse::{spanned, ParseExtensions, Spanned};
#[derive(Debug)]
pub enum TagDispatchParseErrorBase {
MissingTag,
MissingParserFor(String),
}
#[derive(Debug)]
pub enum TagDispatchParseError<E> {
Base(Spanned<TagDispatchParseErrorBase>),
User(E),
}
impl<E> From<Spanned<TagDispatchParseErrorBase>> for TagDispatchParseError<E> {
fn from(o: Spanned<TagDispatchParseErrorBase>) -> Self {
TagDispatchParseError::Base(o)
}
}
pub struct TagDispatchParser<'a, R, E> {
map: HashMap<
&'static str,
Box<dyn ParseExtensions<'a, Output = R, Error = E>>,
>,
}
impl<'a, R, E> TagDispatchParser<'a, R, E> {
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
pub fn add(
&mut self,
k: &'static str,
v: Box<dyn ParseExtensions<'a, Output = R, Error = E>>,
) {
self.map.insert(k, v);
}
}
impl<'a, R, E, const N: usize>
From<
[(
&'static str,
Box<dyn ParseExtensions<'a, Output = R, Error = E>>,
); N],
> for TagDispatchParser<'a, R, E>
{
fn from(
p: [(
&'static str,
Box<dyn ParseExtensions<'a, Output = R, Error = E>>,
); N],
) -> Self {
Self {
map: p.into_iter().collect(),
}
}
}
impl<'a, R, E> ParseExtensions<'a> for TagDispatchParser<'a, R, E> {
type Output = R;
type Error = TagDispatchParseError<E>;
fn parse(
&self,
content: &'a str,
span: crate::parse::Span,
) -> core::result::Result<Self::Output, Self::Error> {
let content = content.trim();
let tag =
content.split(char::is_whitespace).next().ok_or_else(|| {
spanned(
span.start,
span.end,
TagDispatchParseErrorBase::MissingTag,
)
})?;
let handler = self.map.get(tag).ok_or_else(|| {
spanned(
span.start,
span.end,
TagDispatchParseErrorBase::MissingParserFor(tag.into()),
)
})?;
let content = content.trim_start_matches(tag);
handler
.parse(content, crate::parse::span(span.start, span.end))
.map_err(|e| TagDispatchParseError::User(e))
}
}