use crate::{FromScanf, advanced::*};
use regex_syntax::hir::{Hir, Look};
pub struct Parser<'input, T> {
regex: regex_automata::meta::Regex,
captures: regex_automata::util::captures::Captures,
match_tree_template: MatchTreeTemplate,
parse_fn: Box<dyn FnMut(Match<'_, 'input>) -> Option<T>>,
}
impl<'input, T> Parser<'input, T> {
pub fn new() -> Self
where
T: FromScanf<'input>,
{
Self::with_options(Default::default())
}
pub fn with_options(options: FormatOptions) -> Self
where
T: FromScanf<'input>,
{
let matcher = T::get_matcher(&options);
let parse_fn = move |match_tree: Match<'_, 'input>| T::from_match(match_tree, &options);
Self::from_matcher(matcher, parse_fn)
}
pub fn from_matcher(
matcher: Matcher,
parse_fn: impl FnMut(Match<'_, 'input>) -> Option<T> + 'static,
) -> Self {
let mut capture_index = 0;
let (capture, match_tree_template) = matcher.compile(&mut capture_index);
let hir = *capture.sub;
capture_index -= 1;
let hir = Hir::concat(vec![Hir::look(Look::Start), hir, Hir::look(Look::End)]);
if hir.properties().explicit_captures_len() != capture_index {
panic!(
"sscanf: Internal Error: Matcher has mismatched number of capture groups! Expected {capture_index}, got {}",
hir.properties().explicit_captures_len()
);
}
let regex = regex_automata::meta::Regex::builder()
.build_from_hir(&hir)
.expect("sscanf: Failed to compile regex from Matcher");
let captures = regex.create_captures();
Self {
regex,
captures,
match_tree_template,
parse_fn: Box::new(parse_fn),
}
}
pub fn parse(&mut self, input: &'input str) -> Option<T> {
self.regex.captures(input, &mut self.captures);
let match_tree = Match::new(
&self.match_tree_template,
&self.captures,
input,
self.captures.get_group(0)?,
Context::Root.into(),
);
(self.parse_fn)(match_tree)
}
}
impl<'input, T: FromScanf<'input>> Default for Parser<'input, T> {
fn default() -> Self {
Self::new()
}
}
impl<T> std::fmt::Debug for Parser<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(&format!("Parser<{}>", std::any::type_name::<T>()))
.field("regex", &self.regex)
.field("match_tree_template", &self.match_tree_template)
.finish()
}
}