dysql_tpl/
traits.rs

1//! This module contains helper traits that are used internally to manage
2//! sequences of types implementing the `Content` trait, allowing us to
3//! statically manage parent section lookups without doing extra work on
4//! runtime.
5
6use crate::encoding::Encoder;
7use crate::simple::simple_section::SimpleSection;
8use crate::simple::{SimpleValue, SimpleError, SimpleInnerError};
9use crate::template::Section;
10use crate::Content;
11
12/// Helper trait used to rotate a queue of parent `Content`s. Think of this as of a
13/// rotating buffer such that:
14///
15/// ```text
16/// (A, B, C, D).combine(X) -> (B, C, D, X)
17/// ```
18///
19/// This allows us to keep track of up to 3 parent contexts. The constraint is implemented
20/// so that self-referencing `Content`s don't blow up the stack on compilation.
21pub trait Combine {
22    /// First type for the result tuple
23    type I: Content + Copy + Sized;
24    /// Second type for the result tuple
25    type J: Content + Copy + Sized;
26    /// Third type for the result tuple
27    type K: Content + Copy + Sized;
28
29    /// Type when we crawl back one item
30    type Previous: ContentSequence;
31
32    /// Combines current tuple with a new element.
33    fn combine<X: Content + ?Sized>(self, other: &X) -> (Self::I, Self::J, Self::K, &X);
34
35    /// Crawl back to the previous tuple
36    fn crawl_back(self) -> Self::Previous;
37}
38
39/// Helper trait that re-exposes `render_field_x` methods of a `Content` trait,
40/// calling those methods internally on all `Content`s contained within `Self`.
41pub trait ContentSequence: Combine + Sized + Copy {
42    /// Render a field by the hash **or** string of its name.
43    ///
44    /// This will escape HTML characters, eg: `<` will become `&lt;`.
45    #[inline]
46    fn render_field_escaped<E: Encoder>(
47        &self,
48        _hash: u64,
49        _name: &str,
50        _encoder: &mut E,
51    ) -> Result<(), E::Error> {
52        Ok(())
53    }
54
55    /// Render a field by the hash **or** string of its name.
56    ///
57    /// This doesn't perform any escaping at all.
58    #[inline]
59    fn render_field_unescaped<E: Encoder>(
60        &self,
61        _hash: u64,
62        _name: &str,
63        _encoder: &mut E,
64    ) -> Result<(), E::Error> {
65        Ok(())
66    }
67
68    /// Apply a dto field by the hash **or** string of its name.
69    ///
70    /// This doesn't perform any escaping at all.
71    #[inline]
72    fn apply_field_unescaped(
73        &self,
74        _hash: u64,
75        _name: &str
76    ) -> Result<SimpleValue, SimpleError> {
77        Err(SimpleInnerError(format!("the data type of field is not supported")).into())
78    }
79
80    /// Render a field by the hash **or** string of its name, as a section.
81    #[inline]
82    fn render_field_section<P, E>(
83        &self,
84        _hash: u64,
85        _name: &str,
86        _section: Section<P>,
87        _encoder: &mut E,
88    ) -> Result<(), E::Error>
89    where
90        P: ContentSequence,
91        E: Encoder,
92    {
93        Ok(())
94    }
95
96    /// Apply a dto field by the hash **or** string of its name, as a section.
97    #[inline]
98    fn apply_field_section<P>(
99        &self,
100        _hash: u64,
101        _name: &str,
102        _section: SimpleSection<P>,
103    ) -> Result<SimpleValue, SimpleError>
104    where
105        P: ContentSequence,
106    {
107        Err(SimpleInnerError(format!("the data type of field: {} is not supported ", _name)).into())
108    }
109
110
111    /// Render a field, by the hash of **or** string its name, as an inverse section.
112    #[inline]
113    fn render_field_inverse<P, E>(
114        &self,
115        _hash: u64,
116        _name: &str,
117        _section: Section<P>,
118        _encoder: &mut E,
119    ) -> Result<(), E::Error>
120    where
121        P: ContentSequence,
122        E: Encoder,
123    {
124        Ok(())
125    }
126
127    /// Render a field by the hash **or** string of its name, as a section.
128    #[inline]
129    fn render_field_notnone_section<P, E>(
130        &self,
131        _hash: u64,
132        _name: &str,
133        _section: Section<P>,
134        _encoder: &mut E,
135    ) -> Result<bool, E::Error>
136    where
137        P: ContentSequence,
138        E: Encoder,
139    {
140        Ok(false)
141    }
142}
143
144impl Combine for () {
145    type I = ();
146    type J = ();
147    type K = ();
148    type Previous = ();
149
150    #[inline]
151    fn combine<X: Content + ?Sized>(self, other: &X) -> ((), (), (), &X) {
152        ((), (), (), other)
153    }
154
155    #[inline]
156    fn crawl_back(self) -> Self::Previous {}
157}
158
159impl ContentSequence for () {}
160
161impl<A, B, C, D> Combine for (A, B, C, D)
162where
163    A: Content + Copy,
164    B: Content + Copy,
165    C: Content + Copy,
166    D: Content + Copy,
167{
168    type I = B;
169    type J = C;
170    type K = D;
171    type Previous = ((), A, B, C);
172
173    #[inline]
174    fn combine<X: Content + ?Sized>(self, other: &X) -> (B, C, D, &X) {
175        (self.1, self.2, self.3, other)
176    }
177
178    #[inline]
179    fn crawl_back(self) -> ((), A, B, C) {
180        ((), self.0, self.1, self.2)
181    }
182}
183
184impl<A, B, C, D> ContentSequence for (A, B, C, D)
185where
186    A: Content + Copy,
187    B: Content + Copy,
188    C: Content + Copy,
189    D: Content + Copy,
190{
191    #[inline]
192    fn render_field_escaped<E: Encoder>(
193        &self,
194        hash: u64,
195        name: &str,
196        encoder: &mut E,
197    ) -> Result<(), E::Error> {
198        if !self.3.render_field_escaped(hash, name, encoder)?
199            && !self.2.render_field_escaped(hash, name, encoder)?
200            && !self.1.render_field_escaped(hash, name, encoder)?
201        {
202            self.0.render_field_escaped(hash, name, encoder)?;
203        }
204        Ok(())
205    }
206
207    #[inline]
208    fn render_field_unescaped<E: Encoder>(
209        &self,
210        hash: u64,
211        name: &str,
212        encoder: &mut E,
213    ) -> Result<(), E::Error> {
214        if !self.3.render_field_unescaped(hash, name, encoder)?
215            && !self.2.render_field_unescaped(hash, name, encoder)?
216            && !self.1.render_field_unescaped(hash, name, encoder)?
217        {
218            self.0.render_field_unescaped(hash, name, encoder)?;
219        }
220        Ok(())
221    }
222
223    #[inline]
224    fn apply_field_unescaped(
225        &self,
226        hash: u64,
227        name: &str,
228    ) -> Result<SimpleValue, SimpleError> {
229        self.3.apply_field_unescaped(hash, name)
230    }
231
232    #[inline]
233    fn render_field_section<P, E>(
234        &self,
235        hash: u64,
236        name: &str,
237        section: Section<P>,
238        encoder: &mut E,
239    ) -> Result<(), E::Error>
240    where
241        P: ContentSequence,
242        E: Encoder,
243    {
244        let rst = self.3.render_field_section(hash, name, section, encoder)?;
245
246        if !rst {
247            let section = section.without_last();
248
249            let rst = self.2.render_field_section(hash, name, section, encoder)?;
250            if !rst {
251                let section = section.without_last();
252
253                let rst = self.1.render_field_section(hash, name, section, encoder)?;
254                if !rst {
255                    let section = section.without_last();
256
257                    self.0.render_field_section(hash, name, section, encoder)?;
258                }
259            }
260        }
261        Ok(())
262    }
263
264    #[inline]
265    fn apply_field_section<P>(
266        &self,
267        hash: u64,
268        name: &str,
269        section: SimpleSection<P>,
270    ) -> Result<SimpleValue, SimpleError>
271    where
272        P: ContentSequence,
273    {
274        self.3.apply_field_section(hash, name, section)
275    }
276
277    #[inline]
278    fn render_field_inverse<P, E>(
279        &self,
280        hash: u64,
281        name: &str,
282        section: Section<P>,
283        encoder: &mut E,
284    ) -> Result<(), E::Error>
285    where
286        P: ContentSequence,
287        E: Encoder,
288    {
289        if !self.3.render_field_inverse(hash, name, section, encoder)?
290            && !self.2.render_field_inverse(hash, name, section, encoder)?
291            && !self.1.render_field_inverse(hash, name, section, encoder)?
292            && !self.0.render_field_inverse(hash, name, section, encoder)?
293        {
294            section.render(encoder, Option::<&()>::None)?;
295        }
296        Ok(())
297    }
298
299    #[inline]
300    fn render_field_notnone_section<P, E>(
301        &self,
302        hash: u64,
303        name: &str,
304        section: Section<P>,
305        encoder: &mut E,
306    ) -> Result<bool, E::Error>
307    where
308        P: ContentSequence,
309        E: Encoder,
310    {
311        let mut rst = self.3.render_field_notnone_section(hash, name, section, encoder)?;
312        if !rst {
313            let section = section.without_last();
314
315            rst = self.2.render_field_notnone_section(hash, name, section, encoder)?;
316            if !rst {
317                let section = section.without_last();
318
319                rst = self.1.render_field_notnone_section(hash, name, section, encoder)?;
320                if !rst {
321                    let section = section.without_last();
322
323                    rst = self.0.render_field_notnone_section(hash, name, section, encoder)?;
324                }
325            }
326        }
327
328        Ok(rst)
329    }
330}