lazy_template/enclosed/
parser.rs

1use super::{ComponentParserInput, ParserConfig, Segment};
2use crate::{IntoSkipOrFatal, Parse};
3use derive_more::{Display, Error};
4use split_first_char::split_first_char;
5
6/// Parse a template string whose queries are placed between an opening bracket character and a closing bracket character,
7/// (such as [curly braces](crate::simple_curly_braces)).
8#[derive(Debug, Clone, Copy)]
9pub struct EnclosedTemplateParser<EscapeParser, QueryParser> {
10    pub config: ParserConfig,
11    pub escape_parser: EscapeParser,
12    pub query_parser: QueryParser,
13}
14
15pub type Parser<EscapeParser, QueryParser> = EnclosedTemplateParser<EscapeParser, QueryParser>;
16
17impl<EscapeParser, QueryParser> Parser<EscapeParser, QueryParser> {
18    /// Replace [`Parser::config`].
19    pub fn with_config(mut self, config: ParserConfig) -> Self {
20        self.config = config;
21        self
22    }
23
24    /// Replace [`Parser::escape_parser`].
25    pub fn with_escape_parser<NewEscapeParser>(
26        self,
27        escape_parser: NewEscapeParser,
28    ) -> Parser<NewEscapeParser, QueryParser> {
29        let Parser {
30            config,
31            query_parser,
32            escape_parser: _,
33        } = self;
34        Parser {
35            config,
36            escape_parser,
37            query_parser,
38        }
39    }
40
41    /// Replace [`Parser::query_parser`].
42    pub fn with_query_parser<NewQueryParser>(
43        self,
44        query_parser: NewQueryParser,
45    ) -> Parser<EscapeParser, NewQueryParser> {
46        let Parser {
47            config,
48            escape_parser,
49            query_parser: _,
50        } = self;
51        Parser {
52            config,
53            escape_parser,
54            query_parser,
55        }
56    }
57}
58
59impl Parser<(), ()> {
60    /// Create a builder of an [`EnclosedTemplateParser`] of templates whose queries should be placed between curly braces.
61    ///
62    /// The curly braces can be replaced by [replacing the config][Parser::with_config].
63    ///
64    /// The value returned from this function is not useful immediately. The [query parser][Parser::with_query_parser] and the
65    /// [escape parser][Parser::with_escape_parser] must be replaced first.
66    ///
67    /// **Usage example:**
68    ///
69    /// ```
70    /// # #[cfg(not(feature = "std"))] fn main() {}
71    /// # #[cfg(feature = "std")] fn main() {
72    /// # use pretty_assertions::assert_eq;
73    /// use lazy_template::{
74    ///     enclosed::{Parser, SimpleEscapeParser, SimpleQuery, SimpleQueryParser},
75    ///     IntoTemplateSystem,
76    /// };
77    /// let output = Parser::curly_braces()
78    ///     .with_escape_parser(SimpleEscapeParser)
79    ///     .with_query_parser(SimpleQueryParser)
80    ///     .into_template_system::<SimpleQuery>()
81    ///     .lazy_parse("{name} is a {age} years old {gender}")
82    ///     .to_string(|query| match query {
83    ///         "name" => Ok("Alice"),
84    ///         "age" => Ok("20"),
85    ///         "gender" => Ok("girl"),
86    ///         _ => Err(format!("Can't answer {query:?}")),
87    ///     })
88    ///     .unwrap();
89    /// assert_eq!(output, "Alice is a 20 years old girl");
90    /// # }
91    /// ```
92    pub fn curly_braces() -> Self {
93        Parser {
94            config: ParserConfig::curly_braces(),
95            escape_parser: (),
96            query_parser: (),
97        }
98    }
99}
100
101/// Error type of [`Parse`] on [`EnclosedTemplateParser`].
102#[derive(Debug, Display, Error, Clone, Copy)]
103pub enum ParseError<ParseEscapeError, ParseQueryError> {
104    #[display("Unexpected token {_0:?}")]
105    UnexpectedChar(#[error(not(source))] char),
106    #[display("Unexpected end of input")]
107    UnexpectedEndOfInput,
108    #[display("Fail to escape: {_0}")]
109    ParseEscape(ParseEscapeError),
110    #[display("Fail to parse query: {_0}")]
111    ParseQuery(ParseQueryError),
112}
113
114impl<'a, EscapeParser, QueryParser> Parse<'a> for Parser<EscapeParser, QueryParser>
115where
116    EscapeParser: Parse<'a, ComponentParserInput<'a>, Output = char>,
117    EscapeParser::Error: IntoSkipOrFatal,
118    QueryParser: Parse<'a, ComponentParserInput<'a>>,
119    QueryParser::Error: IntoSkipOrFatal,
120{
121    type Output = Segment<QueryParser::Output>;
122    type Error = ParseError<
123        <EscapeParser::Error as IntoSkipOrFatal>::Fatal,
124        <QueryParser::Error as IntoSkipOrFatal>::Fatal,
125    >;
126
127    fn parse(&self, input: &'a str) -> Result<(Self::Output, &'a str), Self::Error> {
128        let component_parser_input = ComponentParserInput {
129            text: input,
130            config: self.config,
131        };
132
133        let escape_pair = self
134            .escape_parser
135            .parse_as_component(component_parser_input)
136            .map_err(ParseError::ParseEscape)?;
137        if let Some((escaped, rest)) = escape_pair {
138            return Ok((Segment::Character(escaped), rest));
139        }
140
141        let query_pair = self
142            .query_parser
143            .parse_as_component(component_parser_input)
144            .map_err(ParseError::ParseQuery)?;
145        if let Some((query, rest)) = query_pair {
146            return Ok((Segment::Expression(query), rest));
147        }
148
149        let (head, tail) = split_first_char(input).ok_or(ParseError::UnexpectedEndOfInput)?;
150
151        if head == self.config.close_bracket {
152            return Err(ParseError::UnexpectedChar(head));
153        }
154
155        Ok((Segment::Character(head), tail))
156    }
157}