lazy_template/
template.rs

1use crate::Render;
2use core::{convert::Infallible, fmt, marker::PhantomData};
3use derive_more::{Display, Error, IntoIterator};
4use pipe_trait::Pipe;
5
6#[derive(Debug, Clone, Copy, IntoIterator)]
7pub struct Template<SegmentResultIntoIter, Query> {
8    #[into_iterator]
9    iter: SegmentResultIntoIter,
10    _query: PhantomData<Query>, // phantom Query is necessary to enable type inference later on
11}
12
13impl<SegmentResultIntoIter, Query> Template<SegmentResultIntoIter, Query> {
14    pub(crate) fn new(iter: SegmentResultIntoIter) -> Self {
15        Self {
16            iter,
17            _query: PhantomData,
18        }
19    }
20}
21
22#[derive(Debug, Display, Error, Clone, Copy)]
23pub enum TemplateApplicationError<ParseError, QueryError, WriteError> {
24    Parse(ParseError),
25    Query(QueryError),
26    Write(WriteError),
27}
28
29impl<SegmentResultIntoIter, Query> Template<SegmentResultIntoIter, Query>
30where
31    SegmentResultIntoIter: IntoIterator,
32{
33    /// Apply the template, and write the resulting segment outputs that implement [`fmt::Display`] to a
34    /// buffer that implements [`fmt::Write`].
35    pub fn write_to<Output, Segment, ParseError, RenderOutput, QueryOutput, QueryError, Respond>(
36        self,
37        output: &mut Output,
38        respond: Respond,
39    ) -> Result<(), TemplateApplicationError<ParseError, QueryError, fmt::Error>>
40    where
41        Output: fmt::Write,
42        SegmentResultIntoIter::Item: Into<Result<Segment, ParseError>>,
43        RenderOutput: fmt::Display,
44        Segment: Render<Respond, RenderOutput, QueryError>,
45        Respond: FnMut(Query) -> Result<QueryOutput, QueryError>,
46    {
47        let mut write_error = None;
48
49        self.apply(respond, |response| {
50            write_error = write!(output, "{response}").err()
51        })
52        .map_err(|error| match error {
53            TemplateApplicationError::Parse(error) => TemplateApplicationError::Parse(error),
54            TemplateApplicationError::Query(error) => TemplateApplicationError::Query(error),
55            TemplateApplicationError::Write(error) => match error {},
56        })?;
57
58        if let Some(error) = write_error {
59            return error.pipe(TemplateApplicationError::Write).pipe(Err);
60        }
61
62        Ok(())
63    }
64
65    /// Apply the template, and send the resulting segment outputs to `handle_query_output`.
66    fn apply<
67        Segment,
68        ParseError,
69        RenderOutput,
70        QueryOutput,
71        QueryError,
72        Respond,
73        HandleSegmentOutput,
74    >(
75        self,
76        mut respond: Respond,
77        mut handle_query_output: HandleSegmentOutput,
78    ) -> Result<(), TemplateApplicationError<ParseError, QueryError, Infallible>>
79    where
80        SegmentResultIntoIter::Item: Into<Result<Segment, ParseError>>,
81        HandleSegmentOutput: FnMut(RenderOutput),
82        Segment: Render<Respond, RenderOutput, QueryError>,
83        Respond: FnMut(Query) -> Result<QueryOutput, QueryError>,
84    {
85        for segment in self.iter {
86            let () = segment
87                .into()
88                .map_err(TemplateApplicationError::Parse)?
89                .render(&mut respond)
90                .map_err(TemplateApplicationError::Query)?
91                .pipe(&mut handle_query_output);
92        }
93
94        Ok(())
95    }
96}
97
98mod std_extensions;