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