ramhorns/
content.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use crate::encoding::Encoder;
6#[cfg(feature = "indexes")]
7use crate::template::Indexed;
8use crate::template::{Section, Template};
9use crate::traits::ContentSequence;
10
11use arrayvec::ArrayVec;
12use std::borrow::{Borrow, Cow, ToOwned};
13use std::collections::{BTreeMap, HashMap};
14use std::hash::{BuildHasher, Hash};
15use std::ops::Deref;
16
17/// Trait allowing the rendering to quickly access data stored in the type that
18/// implements it. You needn't worry about implementing it, in virtually all
19/// cases the `#[derive(Content)]` attribute above your types should be sufficient.
20pub trait Content {
21    /// Marks whether this content is truthy. Used when attempting to render a section.
22    #[inline]
23    fn is_truthy(&self) -> bool {
24        true
25    }
26
27    /// How much capacity is _likely_ required for all the data in this `Content`
28    /// for a given `Template`.
29    #[inline]
30    fn capacity_hint(&self, _tpl: &Template) -> usize {
31        0
32    }
33
34    /// Renders self as a variable to the encoder.
35    ///
36    /// This will escape HTML characters, eg: `<` will become `&lt;`.
37    #[inline]
38    fn render_escaped<E: Encoder>(&self, _encoder: &mut E) -> Result<(), E::Error> {
39        Ok(())
40    }
41
42    /// Renders self as a variable to the encoder.
43    ///
44    /// This doesn't perform any escaping at all.
45    #[inline]
46    fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
47        self.render_escaped(encoder)
48    }
49
50    /// Render a section with self.
51    #[inline]
52    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
53    where
54        C: ContentSequence,
55        E: Encoder,
56    {
57        if self.is_truthy() {
58            section.render(encoder)
59        } else {
60            Ok(())
61        }
62    }
63
64    /// Render a section with self.
65    #[inline]
66    fn render_inverse<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
67    where
68        C: ContentSequence,
69        E: Encoder,
70    {
71        if !self.is_truthy() {
72            section.render(encoder)
73        } else {
74            Ok(())
75        }
76    }
77
78    /// Render a field by the hash **or** string of its name.
79    ///
80    /// This will escape HTML characters, eg: `<` will become `&lt;`.
81    /// If successful, returns `true` if the field exists in this content, otherwise `false`.
82    #[inline]
83    fn render_field_escaped<E: Encoder>(
84        &self,
85        _hash: u64,
86        _name: &str,
87        _encoder: &mut E,
88    ) -> Result<bool, E::Error> {
89        Ok(false)
90    }
91
92    /// Render a field by the hash **or** string of its name.
93    ///
94    /// This doesn't perform any escaping at all.
95    /// If successful, returns `true` if the field exists in this content, otherwise `false`.
96    #[inline]
97    fn render_field_unescaped<E: Encoder>(
98        &self,
99        _hash: u64,
100        _name: &str,
101        _encoder: &mut E,
102    ) -> Result<bool, E::Error> {
103        Ok(false)
104    }
105
106    /// Render a field by the hash **or** string of its name, as a section.
107    /// If successful, returns `true` if the field exists in this content, otherwise `false`.
108    #[inline]
109    fn render_field_section<C, E>(
110        &self,
111        _hash: u64,
112        _name: &str,
113        _section: Section<C>,
114        _encoder: &mut E,
115    ) -> Result<bool, E::Error>
116    where
117        C: ContentSequence,
118        E: Encoder,
119    {
120        Ok(false)
121    }
122
123    /// Render a field, by the hash of **or** string its name, as an inverse section.
124    /// If successful, returns `true` if the field exists in this content, otherwise `false`.
125    #[inline]
126    fn render_field_inverse<C, E>(
127        &self,
128        _hash: u64,
129        _name: &str,
130        _section: Section<C>,
131        _encoder: &mut E,
132    ) -> Result<bool, E::Error>
133    where
134        C: ContentSequence,
135        E: Encoder,
136    {
137        Ok(false)
138    }
139
140    /// Render an index based section.
141    /// If successful, returns `true` if the index exists in this content, otherwise `false`.
142    #[cfg(feature = "indexes")]
143    #[inline]
144    fn render_index_section<'section, P, E>(
145        &self,
146        _indexed: &Indexed,
147        _section: Section<'section, P>,
148        _encoder: &mut E,
149    ) -> Result<bool, E::Error>
150    where
151        P: ContentSequence,
152        E: Encoder,
153    {
154        Ok(false)
155    }
156}
157
158impl Content for () {
159    #[inline]
160    fn is_truthy(&self) -> bool {
161        false
162    }
163}
164
165impl Content for str {
166    #[inline]
167    fn is_truthy(&self) -> bool {
168        !self.is_empty()
169    }
170
171    #[inline]
172    fn capacity_hint(&self, _tpl: &Template) -> usize {
173        self.len()
174    }
175
176    #[inline]
177    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
178        encoder.write_escaped(self)
179    }
180
181    #[inline]
182    fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
183        encoder.write_unescaped(self)
184    }
185}
186
187impl Content for String {
188    #[inline]
189    fn is_truthy(&self) -> bool {
190        !self.is_empty()
191    }
192
193    #[inline]
194    fn capacity_hint(&self, _tpl: &Template) -> usize {
195        self.len()
196    }
197
198    #[inline]
199    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
200        encoder.write_escaped(self)
201    }
202
203    #[inline]
204    fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
205        encoder.write_unescaped(self)
206    }
207}
208
209impl Content for bool {
210    #[inline]
211    fn is_truthy(&self) -> bool {
212        *self
213    }
214
215    #[inline]
216    fn capacity_hint(&self, _tpl: &Template) -> usize {
217        5
218    }
219
220    #[inline]
221    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
222        // Nothing to escape here
223        encoder.write_unescaped(if *self { "true" } else { "false" })
224    }
225}
226
227macro_rules! impl_number_types {
228    ($( $ty:ty ),*) => {
229        $(
230            impl Content for $ty {
231                #[inline]
232                fn is_truthy(&self) -> bool {
233                    *self != 0 as $ty
234                }
235
236                #[inline]
237                fn capacity_hint(&self, _tpl: &Template) -> usize {
238                    5
239                }
240
241                #[inline]
242                fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error>
243                {
244                    // Nothing to escape here
245                    encoder.format_unescaped(self)
246                }
247            }
248        )*
249    }
250}
251
252impl_number_types!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
253
254impl Content for f32 {
255    #[inline]
256    fn is_truthy(&self) -> bool {
257        // Floats shoudn't be directly compared to 0
258        self.abs() > f32::EPSILON
259    }
260
261    #[inline]
262    fn capacity_hint(&self, _tpl: &Template) -> usize {
263        5
264    }
265
266    #[inline]
267    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
268        // Nothing to escape here
269        encoder.format_unescaped(self)
270    }
271}
272
273impl Content for f64 {
274    #[inline]
275    fn is_truthy(&self) -> bool {
276        // Floats shoudn't be directly compared to 0
277        self.abs() > f64::EPSILON
278    }
279
280    #[inline]
281    fn capacity_hint(&self, _tpl: &Template) -> usize {
282        5
283    }
284
285    #[inline]
286    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
287        // Nothing to escape here
288        encoder.format_unescaped(self)
289    }
290}
291
292impl<T: Content> Content for Option<T> {
293    #[inline]
294    fn is_truthy(&self) -> bool {
295        self.is_some()
296    }
297
298    #[inline]
299    fn capacity_hint(&self, tpl: &Template) -> usize {
300        match self {
301            Some(inner) => inner.capacity_hint(tpl),
302            _ => 0,
303        }
304    }
305
306    #[inline]
307    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
308        if let Some(inner) = self {
309            inner.render_escaped(encoder)?;
310        }
311
312        Ok(())
313    }
314
315    #[inline]
316    fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
317        if let Some(ref inner) = self {
318            inner.render_unescaped(encoder)?;
319        }
320
321        Ok(())
322    }
323
324    #[inline]
325    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
326    where
327        C: ContentSequence,
328        E: Encoder,
329    {
330        if let Some(ref item) = self {
331            item.render_section(section, encoder)?;
332        }
333
334        Ok(())
335    }
336}
337
338impl<T: Content, U> Content for Result<T, U> {
339    #[inline]
340    fn is_truthy(&self) -> bool {
341        self.is_ok()
342    }
343
344    #[inline]
345    fn capacity_hint(&self, tpl: &Template) -> usize {
346        match self {
347            Ok(inner) => inner.capacity_hint(tpl),
348            _ => 0,
349        }
350    }
351
352    #[inline]
353    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
354        if let Ok(inner) = self {
355            inner.render_escaped(encoder)?;
356        }
357
358        Ok(())
359    }
360
361    #[inline]
362    fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
363        if let Ok(ref inner) = self {
364            inner.render_unescaped(encoder)?;
365        }
366
367        Ok(())
368    }
369
370    #[inline]
371    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
372    where
373        C: ContentSequence,
374        E: Encoder,
375    {
376        if let Ok(item) = self {
377            item.render_section(section, encoder)?;
378        }
379
380        Ok(())
381    }
382}
383
384impl<T: Content> Content for Vec<T> {
385    #[inline]
386    fn is_truthy(&self) -> bool {
387        !self.is_empty()
388    }
389
390    #[inline]
391    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
392    where
393        C: ContentSequence,
394        E: Encoder,
395    {
396        #[cfg(feature = "indexes")]
397        for (index, item) in self.iter().enumerate() {
398            IndexBasedRender {
399                length: self.len(),
400                index,
401                item,
402            }
403            .render_section(section, encoder)?;
404        }
405        #[cfg(not(feature = "indexes"))]
406        for item in self.iter() {
407            item.render_section(section, encoder)?;
408        }
409
410        Ok(())
411    }
412}
413
414#[cfg(feature = "indexes")]
415struct IndexBasedRender<'a, T> {
416    length: usize,
417    index: usize,
418    item: &'a T,
419}
420#[cfg(feature = "indexes")]
421impl<T: Content> Content for IndexBasedRender<'_, T> {
422    #[inline]
423    fn is_truthy(&self) -> bool {
424        true
425    }
426
427    /// Render a section with self.
428    #[inline]
429    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
430    where
431        C: ContentSequence,
432        E: Encoder,
433    {
434        if self.is_truthy() {
435            section.with(self).render(encoder)
436        } else {
437            Ok(())
438        }
439    }
440
441    #[inline]
442    fn render_index_section<'section, P, E>(
443        &self,
444        indexed: &Indexed,
445        section: Section<'section, P>,
446        encoder: &mut E,
447    ) -> Result<bool, E::Error>
448    where
449        P: ContentSequence,
450        E: Encoder,
451    {
452        if indexed.is_truthy(self.length, self.index) {
453            self.item.render_section(section, encoder)?;
454        }
455        Ok(true)
456    }
457
458    fn render_field_escaped<E>(
459        &self,
460        hash: u64,
461        name: &str,
462        encoder: &mut E,
463    ) -> Result<bool, E::Error>
464    where
465        E: Encoder,
466    {
467        self.item.render_field_escaped(hash, name, encoder)
468    }
469
470    fn render_field_unescaped<E>(
471        &self,
472        hash: u64,
473        name: &str,
474        encoder: &mut E,
475    ) -> Result<bool, E::Error>
476    where
477        E: Encoder,
478    {
479        self.item.render_field_unescaped(hash, name, encoder)
480    }
481
482    fn render_field_section<C, E>(
483        &self,
484        hash: u64,
485        name: &str,
486        section: Section<C>,
487        encoder: &mut E,
488    ) -> Result<bool, E::Error>
489    where
490        C: ContentSequence,
491        E: Encoder,
492    {
493        self.item.render_field_section(hash, name, section, encoder)
494    }
495
496    fn render_field_inverse<C, E>(
497        &self,
498        hash: u64,
499        name: &str,
500        section: Section<C>,
501        encoder: &mut E,
502    ) -> Result<bool, E::Error>
503    where
504        C: ContentSequence,
505        E: Encoder,
506    {
507        self.item.render_field_inverse(hash, name, section, encoder)
508    }
509}
510
511impl<T: Content> Content for [T] {
512    #[inline]
513    fn is_truthy(&self) -> bool {
514        !self.is_empty()
515    }
516
517    #[inline]
518    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
519    where
520        C: ContentSequence,
521        E: Encoder,
522    {
523        for item in self.iter() {
524            item.render_section(section, encoder)?;
525        }
526
527        Ok(())
528    }
529}
530
531impl<T: Content, const N: usize> Content for [T; N] {
532    #[inline]
533    fn is_truthy(&self) -> bool {
534        !self.is_empty()
535    }
536
537    #[inline]
538    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
539    where
540        C: ContentSequence,
541        E: Encoder,
542    {
543        for item in self.iter() {
544            item.render_section(section, encoder)?;
545        }
546
547        Ok(())
548    }
549}
550
551impl<T: Content, const N: usize> Content for ArrayVec<T, N> {
552    #[inline]
553    fn is_truthy(&self) -> bool {
554        !self.is_empty()
555    }
556
557    #[inline]
558    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
559    where
560        C: ContentSequence,
561        E: Encoder,
562    {
563        for item in self.iter() {
564            item.render_section(section, encoder)?;
565        }
566
567        Ok(())
568    }
569}
570
571impl<K, V, S> Content for HashMap<K, V, S>
572where
573    K: Borrow<str> + Hash + Eq,
574    V: Content,
575    S: BuildHasher,
576{
577    fn is_truthy(&self) -> bool {
578        !self.is_empty()
579    }
580
581    /// Render a section with self.
582    #[inline]
583    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
584    where
585        C: ContentSequence,
586        E: Encoder,
587    {
588        if self.is_truthy() {
589            section.with(self).render(encoder)
590        } else {
591            Ok(())
592        }
593    }
594
595    fn render_field_escaped<E>(&self, _: u64, name: &str, encoder: &mut E) -> Result<bool, E::Error>
596    where
597        E: Encoder,
598    {
599        match self.get(name) {
600            Some(v) => v.render_escaped(encoder).map(|_| true),
601            None => Ok(false),
602        }
603    }
604
605    fn render_field_unescaped<E>(
606        &self,
607        _: u64,
608        name: &str,
609        encoder: &mut E,
610    ) -> Result<bool, E::Error>
611    where
612        E: Encoder,
613    {
614        match self.get(name) {
615            Some(v) => v.render_unescaped(encoder).map(|_| true),
616            None => Ok(false),
617        }
618    }
619
620    fn render_field_section<C, E>(
621        &self,
622        _: u64,
623        name: &str,
624        section: Section<C>,
625        encoder: &mut E,
626    ) -> Result<bool, E::Error>
627    where
628        C: ContentSequence,
629        E: Encoder,
630    {
631        match self.get(name) {
632            Some(v) => v.render_section(section, encoder).map(|_| true),
633            None => Ok(false),
634        }
635    }
636
637    fn render_field_inverse<C, E>(
638        &self,
639        _: u64,
640        name: &str,
641        section: Section<C>,
642        encoder: &mut E,
643    ) -> Result<bool, E::Error>
644    where
645        C: ContentSequence,
646        E: Encoder,
647    {
648        match self.get(name) {
649            Some(v) => v.render_inverse(section, encoder).map(|_| true),
650            None => Ok(false),
651        }
652    }
653}
654
655impl<K, V> Content for BTreeMap<K, V>
656where
657    K: Borrow<str> + Ord,
658    V: Content,
659{
660    fn is_truthy(&self) -> bool {
661        !self.is_empty()
662    }
663
664    /// Render a section with self.
665    #[inline]
666    fn render_section<C, E>(&self, section: Section<C>, encoder: &mut E) -> Result<(), E::Error>
667    where
668        C: ContentSequence,
669        E: Encoder,
670    {
671        if self.is_truthy() {
672            section.with(self).render(encoder)
673        } else {
674            Ok(())
675        }
676    }
677
678    fn render_field_escaped<E>(&self, _: u64, name: &str, encoder: &mut E) -> Result<bool, E::Error>
679    where
680        E: Encoder,
681    {
682        match self.get(name) {
683            Some(v) => v.render_escaped(encoder).map(|_| true),
684            None => Ok(false),
685        }
686    }
687
688    fn render_field_unescaped<E>(
689        &self,
690        _: u64,
691        name: &str,
692        encoder: &mut E,
693    ) -> Result<bool, E::Error>
694    where
695        E: Encoder,
696    {
697        match self.get(name) {
698            Some(v) => v.render_unescaped(encoder).map(|_| true),
699            None => Ok(false),
700        }
701    }
702
703    fn render_field_section<C, E>(
704        &self,
705        _: u64,
706        name: &str,
707        section: Section<C>,
708        encoder: &mut E,
709    ) -> Result<bool, E::Error>
710    where
711        C: ContentSequence,
712        E: Encoder,
713    {
714        match self.get(name) {
715            Some(v) => v.render_section(section, encoder).map(|_| true),
716            None => Ok(false),
717        }
718    }
719
720    fn render_field_inverse<C, E>(
721        &self,
722        _: u64,
723        name: &str,
724        section: Section<C>,
725        encoder: &mut E,
726    ) -> Result<bool, E::Error>
727    where
728        C: ContentSequence,
729        E: Encoder,
730    {
731        match self.get(name) {
732            Some(v) => v.render_inverse(section, encoder).map(|_| true),
733            None => Ok(false),
734        }
735    }
736}
737
738macro_rules! impl_pointer_types {
739    ($( $ty:ty $(: $bounds:ident)? ),*) => {
740        $(
741            impl<T: Content $(+ $bounds)? + ?Sized> Content for $ty {
742                #[inline]
743                fn is_truthy(&self) -> bool {
744                    self.deref().is_truthy()
745                }
746
747                #[inline]
748                fn capacity_hint(&self, tpl: &Template) -> usize {
749                    self.deref().capacity_hint(tpl)
750                }
751
752                #[inline]
753                fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
754                    self.deref().render_escaped(encoder)
755                }
756
757                #[inline]
758                fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
759                    self.deref().render_unescaped(encoder)
760                }
761
762                #[inline]
763                fn render_section<C, E>(
764                    &self,
765                    section: Section<C>,
766                    encoder: &mut E,
767                ) -> Result<(), E::Error>
768                where
769                    C: ContentSequence,
770                    E: Encoder,
771                {
772                    self.deref().render_section(section, encoder)
773                }
774
775                #[inline]
776                fn render_inverse<C, E>(
777                    &self,
778                    section: Section<C>,
779                    encoder: &mut E,
780                ) -> Result<(), E::Error>
781                where
782                    C: ContentSequence,
783                    E: Encoder,
784                {
785                    self.deref().render_inverse(section, encoder)
786                }
787
788                #[inline]
789                fn render_field_escaped<E: Encoder>(
790                    &self,
791                    hash: u64,
792                    name: &str,
793                    encoder: &mut E,
794                ) -> Result<bool, E::Error> {
795                    self.deref().render_field_escaped(hash, name, encoder)
796                }
797
798                #[inline]
799                fn render_field_unescaped<E: Encoder>(
800                    &self,
801                    hash: u64,
802                    name: &str,
803                    encoder: &mut E,
804                ) -> Result<bool, E::Error> {
805                    self.deref().render_field_unescaped(hash, name, encoder)
806                }
807
808                #[inline]
809                fn render_field_section<C, E>(
810                    &self,
811                    hash: u64,
812                    name: &str,
813                    section: Section<C>,
814                    encoder: &mut E,
815                ) -> Result<bool, E::Error>
816                where
817                    C: ContentSequence,
818                    E: Encoder,
819                {
820                    self.deref().render_field_section(hash, name, section, encoder)
821                }
822
823                #[inline]
824                fn render_field_inverse<C, E>(
825                    &self,
826                    hash: u64,
827                    name: &str,
828                    section: Section<C>,
829                    encoder: &mut E,
830                ) -> Result<bool, E::Error>
831                where
832                    C: ContentSequence,
833                    E: Encoder,
834                {
835                    self.deref().render_field_inverse(hash, name, section, encoder)
836                }
837
838                #[cfg(feature = "indexes")]
839                #[inline]
840                fn render_index_section<'section, P, E>(
841                    &self,
842                    indexed: &Indexed,
843                    section: Section<'section, P>,
844                    encoder: &mut E,
845                ) -> Result<bool, E::Error>
846                where
847                    P: ContentSequence,
848                    E: Encoder,
849                {
850                    self.deref().render_index_section(indexed, section, encoder)
851                }
852            }
853        )*
854    }
855}
856
857impl_pointer_types!(&T, Box<T>, std::rc::Rc<T>, std::sync::Arc<T>, Cow<'_, T>: ToOwned, beef::Cow<'_, [T]>: Clone);
858
859#[cfg(target_pointer_width = "64")]
860impl_pointer_types!(beef::lean::Cow<'_, [T]>: Clone);
861
862// Can't implement for generic beef::Cow as it uses an internal trait.
863impl Content for beef::Cow<'_, str> {
864    #[inline]
865    fn is_truthy(&self) -> bool {
866        !self.is_empty()
867    }
868
869    #[inline]
870    fn capacity_hint(&self, _tpl: &Template) -> usize {
871        self.len()
872    }
873
874    #[inline]
875    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
876        encoder.write_escaped(self)
877    }
878
879    #[inline]
880    fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
881        encoder.write_unescaped(self)
882    }
883}
884
885#[cfg(target_pointer_width = "64")]
886impl Content for beef::lean::Cow<'_, str> {
887    #[inline]
888    fn is_truthy(&self) -> bool {
889        !self.is_empty()
890    }
891
892    #[inline]
893    fn capacity_hint(&self, _tpl: &Template) -> usize {
894        self.len()
895    }
896
897    #[inline]
898    fn render_escaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
899        encoder.write_escaped(self)
900    }
901
902    #[inline]
903    fn render_unescaped<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
904        encoder.write_unescaped(self)
905    }
906}