aoc_parse/parsers/
regex.rs

1//! Parsers using Regex.
2
3use std::{
4    any::{self, Any},
5    fmt::Display,
6};
7
8use regex::Regex;
9
10use crate::{parsers::BasicParseIter, ParseContext, Parser, Reported, Result};
11
12/// This parser matches using a regex, then converts the value to a Rust value
13/// using the given `parse_fn`.
14///
15/// Each time this parser is used, either it finds a regex match and `parse_fn`
16/// succeeds, or it fails to match. For example, if `regex` is `/\w+/` and the
17/// input is "hello world", if the RegexParser is invoked at start offset 0, it
18/// either matches `"hello"` or doesn't match at all. It never falls back on
19/// `"hell"` or `"h"`, even though those are both valid (shorter) regex
20/// matches.
21///
22/// This means `sequence(/[a-z]+/, 'a')` will not match `"cba"` (or any other
23/// string) because the regex matches all lowercase letters, leaving nothing
24/// for the next pattern `'a'` to match.
25pub struct RegexParser<T, E> {
26    pub(crate) regex: fn() -> &'static Regex,
27    pub(crate) parse_fn: fn(&str) -> Result<T, E>,
28}
29
30// Manual Clone impl because `#[derive(Clone)]` is buggy in this case.
31impl<T, E> Clone for RegexParser<T, E> {
32    fn clone(&self) -> Self {
33        RegexParser {
34            regex: self.regex,
35            parse_fn: self.parse_fn,
36        }
37    }
38}
39
40impl<T, E> Copy for RegexParser<T, E> {}
41
42impl<T, E> Parser for RegexParser<T, E>
43where
44    T: Any + Clone,
45    E: Display,
46{
47    type Output = T;
48    type RawOutput = (T,);
49    type Iter<'parse> = BasicParseIter<T>
50    where
51        E: 'parse;
52
53    fn parse_iter<'parse>(
54        &'parse self,
55        context: &mut ParseContext<'parse>,
56        start: usize,
57    ) -> Result<Self::Iter<'parse>, Reported> {
58        match (self.regex)().find(&context.source()[start..]) {
59            None => Err(context.error_expected(start, any::type_name::<T>())),
60            Some(m) => match (self.parse_fn)(m.as_str()) {
61                Ok(value) => Ok(BasicParseIter {
62                    end: start + m.end(),
63                    value,
64                }),
65                Err(err) => Err(context.error_from_str_failed(
66                    start,
67                    start + m.end(),
68                    any::type_name::<T>(),
69                    format!("{err}"),
70                )),
71            },
72        }
73    }
74}