use crate::vm::{Callable, Func};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt;
use object_query::Query;
use serde::Deserialize;
#[derive(Debug)]
pub enum MatchError {
Nlsd(nlsd::Error),
MismatchedStaticToken,
EmptyQuery,
UnknownQueryVar,
UnknownDataVar,
UnfilledVar,
UnexpectedEof,
ExpectedEof,
InvalidCtx,
}
pub struct Matcher<'a> {
src: &'a str,
}
impl<'a> Matcher<'a> {
pub fn new(src: &'a str) -> Self {
Self { src }
}
pub fn next_static(&mut self) -> Result<&'a str, MatchError> {
if let Ok((_, tok, rest)) = nl_parser::parse_token(self.src) {
self.src = rest;
Ok(tok)
} else {
Err(MatchError::UnexpectedEof)
}
}
pub fn next_query(&mut self) -> Result<Vec<Query<'a>>, MatchError> {
let mut nloq_de = nloq::Deserializer::from_str(self.src);
let query = nloq_de.query();
if query.is_empty() {
Err(MatchError::EmptyQuery)
} else {
self.src = nloq_de.rest();
Ok(query)
}
}
pub fn next_query_owned(&mut self) -> Result<Vec<Query<'static>>, MatchError> {
Ok(self
.next_query()?
.into_iter()
.map(|q| q.to_owned())
.collect())
}
pub fn next_data<T>(&mut self) -> Result<T, MatchError>
where
T: Deserialize<'a>,
{
let mut nlsd_de = nlsd::Deserializer::from_str(self.src);
let out = T::deserialize(&mut nlsd_de)?;
self.src = nlsd_de.rest();
Ok(out)
}
pub fn is_empty(&self) -> bool {
self.src.trim_start().is_empty()
}
}
pub trait Match<'a, C>: Sized {
fn match_str(ctx: &mut C, string: &'a str) -> Result<Self, MatchError>;
}
pub type FuncMatcher<'a, C> = fn(&mut C, &'a str) -> Result<Func<'a>, MatchError>;
pub trait MatchFunc<'a, C>: 'a + Match<'a, C> + Callable {
fn match_func(ctx: &mut C, string: &'a str) -> Result<Func<'a>, MatchError> {
Self::match_str(ctx, string).map(|this| Box::new(this) as Box<dyn Callable>)
}
}
impl<'a, C, T> MatchFunc<'a, C> for T where T: 'a + Match<'a, C> + Callable {}
impl From<nlsd::Error> for MatchError {
fn from(err: nlsd::Error) -> Self {
Self::Nlsd(err)
}
}
impl fmt::Display for MatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Nlsd(err) => f.write_fmt(format_args!("NLSD err: {}", err)),
Self::MismatchedStaticToken => f.write_str("mismatched static token"),
Self::EmptyQuery => f.write_str("empty NLOQ query"),
Self::UnknownQueryVar => f.write_str("mismatched query variable name"),
Self::UnknownDataVar => f.write_str("mismatched data variable name"),
Self::UnfilledVar => f.write_str("variable not set"),
Self::UnexpectedEof => f.write_str("unexpected end of file"),
Self::ExpectedEof => f.write_str("clause has extra tokens"),
Self::InvalidCtx => f.write_str("context error when parsing"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for MatchError {}