lazy_template/
system.rs

1use crate::{
2    iter::{LazyParseIter, ParsedTemplate},
3    Parse, Template,
4};
5use core::marker::PhantomData;
6use pipe_trait::Pipe;
7
8#[derive(Debug, Clone, Copy)]
9pub struct TemplateSystem<Parser, Query> {
10    parser: Parser,
11    _query: PhantomData<Query>, // phantom Query is necessary to enable type inference later on
12}
13
14impl<Parser, Query> TemplateSystem<Parser, Query> {
15    pub fn new(parser: Parser) -> Self {
16        TemplateSystem {
17            parser,
18            _query: PhantomData,
19        }
20    }
21}
22
23impl<'a, Parser, Query> TemplateSystem<Parser, Query>
24where
25    Parser: Parse<'a>,
26{
27    /// Create a [`Template`] from a template string.
28    ///
29    /// ```
30    /// # #[cfg(not(feature = "std"))] fn main() {}
31    /// # #[cfg(feature = "std")] fn main() {
32    /// # use pretty_assertions::assert_eq;
33    /// use lazy_template::{Template, simple_curly_braces};
34    /// let system = simple_curly_braces();
35    /// let template: Template<_, _> = system.lazy_parse("{name} is a {age} years old {gender}");
36    /// let output = template
37    ///     .to_string(|query| match query {
38    ///         "name" => Ok("Alice"),
39    ///         "age" => Ok("20"),
40    ///         "gender" => Ok("girl"),
41    ///         _ => Err(format!("Can't answer {query:?}")),
42    ///     })
43    ///     .unwrap();
44    /// assert_eq!(output, "Alice is a 20 years old girl");
45    /// # }
46    /// ```
47    ///
48    /// [`Template`] only parses each segment just before it is needed, meaning that even a template with syntax error
49    /// can produce a partial output:
50    ///
51    /// ```
52    /// # #[cfg(not(feature = "std"))] fn main() {}
53    /// # #[cfg(feature = "std")] fn main() {
54    /// # use pretty_assertions::assert_eq;
55    /// let template_string = "{name} is a {age} years } old {gender})"; // incorrectly placed closing curly bracket
56    /// let mut output = String::new();
57    /// let error = lazy_template::simple_curly_braces()
58    ///     .lazy_parse(template_string)
59    ///     .write_to(&mut output, |query| match query {
60    ///         "name" => Ok("Alice"),
61    ///         "age" => Ok("20"),
62    ///         "gender" => Ok("girl"),
63    ///         _ => Err(format!("Can't answer {query:?}")),
64    ///     })
65    ///     .unwrap_err();
66    /// # assert_eq!(
67    /// #     error.to_string(),
68    /// #     "Fail to parse query: Unexpected token '}'"
69    /// # );
70    /// assert_eq!(output, "Alice is a 20 years "); // output is partially written
71    /// # }
72    /// ```
73    pub fn lazy_parse(&'a self, text: &'a str) -> Template<LazyParseIter<'a, Parser>, Query> {
74        LazyParseIter::new(text, &self.parser).pipe(Template::new)
75    }
76
77    /// Parse the template string ahead of time.
78    ///
79    /// The returned parsed template can be used multiple times with different responders to generate different outputs:
80    ///
81    /// ```
82    /// # #[cfg(not(feature = "std"))] fn main() {}
83    /// # #[cfg(feature = "std")] fn main() {
84    /// # use pretty_assertions::assert_eq;
85    /// let system = lazy_template::simple_curly_braces();
86    /// let parsed_template = system.eager_parse::<Vec<_>>("Hello, {name}!").unwrap();
87    /// let output = parsed_template
88    ///     .to_template()
89    ///     .to_string(|query| (query == "name").then_some("Alice").ok_or("Invalid query"))
90    ///     .unwrap();
91    /// assert_eq!(output, "Hello, Alice!");
92    /// let output = parsed_template
93    ///     .to_template()
94    ///     .to_string(|query| (query == "name").then_some("Bob").ok_or("Invalid query"))
95    ///     .unwrap();
96    /// assert_eq!(output, "Hello, Bob!");
97    /// # }
98    /// ```
99    ///
100    /// Unlike [`lazy_parse`](Self::lazy_parse), this function would fail if the template fails to parse (e.g. syntax error):
101    ///
102    /// ```
103    /// # #[cfg(not(feature = "std"))] fn main() {}
104    /// # #[cfg(feature = "std")] fn main() {
105    /// # use pretty_assertions::assert_eq;
106    /// let template_string = "Hello, {name!"; // missing a closing curly bracket
107    /// let error = lazy_template::simple_curly_braces()
108    ///     .eager_parse::<Vec<_>>(template_string)
109    ///     .unwrap_err();
110    /// # assert_eq!(
111    /// #     error.to_string(),
112    /// #     "Fail to parse query: Unexpected end of input",
113    /// # );
114    /// # }
115    /// ```
116    pub fn eager_parse<SegmentContainer>(
117        &'a self,
118        text: &'a str,
119    ) -> Result<ParsedTemplate<SegmentContainer, Query>, Parser::Error>
120    where
121        SegmentContainer: FromIterator<Parser::Output>,
122    {
123        LazyParseIter::new(text, &self.parser)
124            .collect::<Result<SegmentContainer, Parser::Error>>()
125            .map(ParsedTemplate::new)
126    }
127}
128
129/// Convert a [parser](Parse) into a [`TemplateSystem`].
130pub trait IntoTemplateSystem: Sized {
131    fn into_template_system<Query>(self) -> TemplateSystem<Self, Query> {
132        TemplateSystem::new(self)
133    }
134}
135impl<'a, Parser> IntoTemplateSystem for Parser where Parser: Parse<'a> {}