askama/
helpers.rs

1// The re-exports are used in the generated code for macro hygiene. Even if the paths `::core` or
2// `::std` are shadowed, the generated code will still be able to access the crates.
3#[cfg(feature = "alloc")]
4pub extern crate alloc;
5pub extern crate core;
6#[cfg(feature = "std")]
7pub extern crate std;
8
9use core::cell::Cell;
10use core::fmt;
11use core::iter::{Enumerate, Peekable};
12use core::ops::Deref;
13use core::pin::Pin;
14
15pub use crate::error::{ErrorMarker, ResultConverter};
16pub use crate::values::get_value;
17use crate::{FastWritable, Values};
18
19pub struct TemplateLoop<I>
20where
21    I: Iterator,
22{
23    iter: Peekable<Enumerate<I>>,
24}
25
26impl<I> TemplateLoop<I>
27where
28    I: Iterator,
29{
30    #[inline]
31    pub fn new(iter: I) -> Self {
32        TemplateLoop {
33            iter: iter.enumerate().peekable(),
34        }
35    }
36}
37
38impl<I> Iterator for TemplateLoop<I>
39where
40    I: Iterator,
41{
42    type Item = (<I as Iterator>::Item, LoopItem);
43
44    #[inline]
45    fn next(&mut self) -> Option<(<I as Iterator>::Item, LoopItem)> {
46        self.iter.next().map(|(index0, item)| {
47            (
48                item,
49                LoopItem {
50                    index0,
51                    last: self.iter.peek().is_none(),
52                },
53            )
54        })
55    }
56}
57
58#[derive(Copy, Clone)]
59pub struct LoopItem {
60    pub index0: usize,
61    pub last: bool,
62}
63
64pub struct FmtCell<F> {
65    func: Cell<Option<F>>,
66    err: Cell<Option<crate::Error>>,
67}
68
69impl<F> FmtCell<F>
70where
71    F: for<'a, 'b> FnOnce(&'a mut fmt::Formatter<'b>) -> crate::Result<()>,
72{
73    #[inline]
74    pub fn new(f: F) -> Self {
75        Self {
76            func: Cell::new(Some(f)),
77            err: Cell::new(None),
78        }
79    }
80
81    #[inline]
82    pub fn take_err(&self) -> crate::Result<()> {
83        Err(self.err.take().unwrap_or(crate::Error::Fmt))
84    }
85}
86
87impl<F> fmt::Display for FmtCell<F>
88where
89    F: for<'a, 'b> FnOnce(&'a mut fmt::Formatter<'b>) -> crate::Result<()>,
90{
91    #[inline]
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        if let Some(func) = self.func.take()
94            && let Err(err) = func(f)
95        {
96            self.err.set(Some(err));
97            return Err(fmt::Error);
98        }
99        Ok(())
100    }
101}
102
103#[inline]
104pub fn get_primitive_value<T: PrimitiveType>(value: T) -> T::Value {
105    value.get()
106}
107
108/// A type that is, references, or wraps a [primitive][std::primitive] type
109pub trait PrimitiveType {
110    type Value: Copy + Send + Sync + 'static;
111
112    fn get(&self) -> Self::Value;
113}
114
115macro_rules! primitive_type {
116    ($($ty:ty),* $(,)?) => {$(
117        impl PrimitiveType for $ty {
118            type Value = $ty;
119
120            #[inline]
121            fn get(&self) -> Self::Value {
122                *self
123            }
124        }
125    )*};
126}
127
128primitive_type! {
129    bool,
130    f32, f64,
131    i8, i16, i32, i64, i128, isize,
132    u8, u16, u32, u64, u128, usize,
133}
134
135crate::impl_for_ref! {
136    impl PrimitiveType for T {
137        type Value = T::Value;
138
139        #[inline]
140        fn get(&self) -> Self::Value {
141            <T>::get(self)
142        }
143    }
144}
145
146impl<T> PrimitiveType for Pin<T>
147where
148    T: Deref,
149    <T as Deref>::Target: PrimitiveType,
150{
151    type Value = <<T as Deref>::Target as PrimitiveType>::Value;
152
153    #[inline]
154    fn get(&self) -> Self::Value {
155        self.as_ref().get_ref().get()
156    }
157}
158
159/// Implement [`PrimitiveType`] for [`Cell<T>`]
160///
161/// ```
162/// # use std::cell::Cell;
163/// # use std::num::{NonZeroI16, Saturating};
164/// # use std::rc::Rc;
165/// # use std::pin::Pin;
166/// # use askama::Template;
167/// #[derive(Template)]
168/// #[template(ext = "txt", source = "{{ value as u16 }}")]
169/// struct Test<'a> {
170///     value: &'a Pin<Rc<Cell<Saturating<NonZeroI16>>>>
171/// }
172///
173/// assert_eq!(
174///     Test { value: &Rc::pin(Cell::new(Saturating(NonZeroI16::new(-1).unwrap()))) }.to_string(),
175///     "65535",
176/// );
177/// ```
178impl<T: PrimitiveType + Copy> PrimitiveType for Cell<T> {
179    type Value = T::Value;
180
181    #[inline]
182    fn get(&self) -> Self::Value {
183        self.get().get()
184    }
185}
186
187impl<T: PrimitiveType> PrimitiveType for core::num::Wrapping<T> {
188    type Value = T::Value;
189
190    #[inline]
191    fn get(&self) -> Self::Value {
192        self.0.get()
193    }
194}
195
196impl<T: PrimitiveType> PrimitiveType for core::num::Saturating<T> {
197    type Value = T::Value;
198
199    #[inline]
200    fn get(&self) -> Self::Value {
201        self.0.get()
202    }
203}
204
205macro_rules! primitize_nz {
206    ($($nz:ty => $bare:ident,)+) => { $(
207        impl PrimitiveType for $nz {
208            type Value = $bare;
209
210            #[inline]
211            fn get(&self) -> Self::Value {
212                <$nz>::get(*self).get()
213            }
214        }
215    )+ };
216}
217
218primitize_nz! {
219    core::num::NonZeroI8 => i8,
220    core::num::NonZeroI16 => i16,
221    core::num::NonZeroI32 => i32,
222    core::num::NonZeroI64 => i64,
223    core::num::NonZeroI128 => i128,
224    core::num::NonZeroIsize => isize,
225    core::num::NonZeroU8 => u8,
226    core::num::NonZeroU16 => u16,
227    core::num::NonZeroU32 => u32,
228    core::num::NonZeroU64 => u64,
229    core::num::NonZeroU128 => u128,
230    core::num::NonZeroUsize => usize,
231}
232
233/// An empty element, so nothing will be written.
234#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
235pub struct Empty;
236
237impl fmt::Display for Empty {
238    #[inline]
239    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
240        Ok(())
241    }
242}
243
244impl FastWritable for Empty {
245    #[inline]
246    fn write_into(&self, _: &mut dyn fmt::Write, _: &dyn Values) -> crate::Result<()> {
247        Ok(())
248    }
249}
250
251impl fmt::Write for Empty {
252    #[inline]
253    fn write_str(&mut self, _: &str) -> fmt::Result {
254        Ok(())
255    }
256
257    #[inline]
258    fn write_char(&mut self, _: char) -> fmt::Result {
259        Ok(())
260    }
261}
262
263#[inline]
264pub fn as_bool<T: PrimitiveType<Value = bool>>(value: T) -> bool {
265    value.get()
266}
267
268pub struct Concat<L, R>(pub L, pub R);
269
270impl<L: fmt::Display, R: fmt::Display> fmt::Display for Concat<L, R> {
271    #[inline]
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        self.0.fmt(f)?;
274        self.1.fmt(f)
275    }
276}
277
278impl<L: FastWritable, R: FastWritable> FastWritable for Concat<L, R> {
279    #[inline]
280    fn write_into(&self, dest: &mut dyn fmt::Write, values: &dyn Values) -> crate::Result<()> {
281        self.0.write_into(dest, values)?;
282        self.1.write_into(dest, values)
283    }
284}
285
286pub trait EnumVariantTemplate {
287    fn render_into_with_values(
288        &self,
289        writer: &mut dyn fmt::Write,
290        values: &dyn crate::Values,
291    ) -> crate::Result<()>;
292}