askama/filters/
default.rs

1use core::convert::Infallible;
2use core::num::{self, FpCategory, Saturating, Wrapping};
3use core::ops::Deref;
4use core::pin::Pin;
5
6use crate::filters::Either;
7use crate::{Result, impl_for_ref};
8
9/// Render `value` if it is not its "default" value, see [`DefaultFilterable`],
10/// otherwise `fallback`.
11#[inline]
12pub fn assigned_or<L: DefaultFilterable, R>(
13    value: &L,
14    fallback: R,
15) -> Result<Either<L::Filtered<'_>, R>, L::Error> {
16    match value.as_filtered()? {
17        Some(value) => Ok(Either::Left(value)),
18        None => Ok(Either::Right(fallback)),
19    }
20}
21
22/// A type (or a reference to it) that can be used in [`|assigned_or`](assigned_or).
23///
24/// The type is either a monad such as [`Option`] or [`Result`], or a type that has a well defined,
25/// trivial default value, e.g. an [empty](str::is_empty) [`str`] or `0` for integer types.
26#[diagnostic::on_unimplemented(
27    label = "`{Self}` is not `|assigned_or` filterable",
28    message = "`{Self}` is not `|assigned_or` filterable"
29)]
30pub trait DefaultFilterable {
31    /// The contained value
32    type Filtered<'a>
33    where
34        Self: 'a;
35
36    /// An error that prevented [`as_filtered()`](DefaultFilterable::as_filtered) to succeed,
37    /// e.g. a poisoned state or an unacquirable lock.
38    type Error: Into<crate::Error>;
39
40    /// Return the contained value, if a value was contained, and it's not the default value.
41    ///
42    /// Returns `Ok(None)` if the value could not be unwrapped.
43    fn as_filtered(&self) -> Result<Option<Self::Filtered<'_>>, Self::Error>;
44}
45
46impl_for_ref! {
47    impl DefaultFilterable for T {
48        type Filtered<'a> = T::Filtered<'a>
49        where
50            Self: 'a;
51
52        type Error = T::Error;
53
54        #[inline]
55        fn as_filtered(&self) -> Result<Option<Self::Filtered<'_>>, Self::Error> {
56            <T>::as_filtered(self)
57        }
58    }
59}
60
61/// A [pinned][Pin] reference has a value if the referenced data has a value.
62impl<T> DefaultFilterable for Pin<T>
63where
64    T: Deref,
65    <T as Deref>::Target: DefaultFilterable,
66{
67    type Filtered<'a>
68        = <<T as Deref>::Target as DefaultFilterable>::Filtered<'a>
69    where
70        Self: 'a;
71
72    type Error = <<T as Deref>::Target as DefaultFilterable>::Error;
73
74    #[inline]
75    fn as_filtered(&self) -> Result<Option<Self::Filtered<'_>>, Self::Error> {
76        self.as_ref().get_ref().as_filtered()
77    }
78}
79
80/// An [`Option`] has a value if it is `Some`.
81impl<T> DefaultFilterable for Option<T> {
82    type Filtered<'a>
83        = &'a T
84    where
85        Self: 'a;
86
87    type Error = Infallible;
88
89    #[inline]
90    fn as_filtered(&self) -> Result<Option<&T>, Infallible> {
91        Ok(self.as_ref())
92    }
93}
94
95/// A [`Result`] has a value if it is `Ok`.
96impl<T, E> DefaultFilterable for Result<T, E> {
97    type Filtered<'a>
98        = &'a T
99    where
100        Self: 'a;
101
102    type Error = Infallible;
103
104    #[inline]
105    fn as_filtered(&self) -> Result<Option<&T>, Infallible> {
106        Ok(self.as_ref().ok())
107    }
108}
109
110/// A [`str`] has a value if it is not empty.
111impl DefaultFilterable for str {
112    type Filtered<'a>
113        = &'a str
114    where
115        Self: 'a;
116
117    type Error = Infallible;
118
119    #[inline]
120    fn as_filtered(&self) -> Result<Option<&str>, Infallible> {
121        match self.is_empty() {
122            false => Ok(Some(self)),
123            true => Ok(None),
124        }
125    }
126}
127
128/// A [`String`][alloc::string::String] has a value if it is not empty.
129#[cfg(feature = "alloc")]
130impl DefaultFilterable for alloc::string::String {
131    type Filtered<'a>
132        = &'a str
133    where
134        Self: 'a;
135
136    type Error = Infallible;
137
138    #[inline]
139    fn as_filtered(&self) -> Result<Option<&str>, Infallible> {
140        self.as_str().as_filtered()
141    }
142}
143
144/// A [`Cow`][alloc::borrow::Cow] has a value if it's borrowed data has a value.
145#[cfg(feature = "alloc")]
146impl<T: DefaultFilterable + alloc::borrow::ToOwned + ?Sized> DefaultFilterable
147    for alloc::borrow::Cow<'_, T>
148{
149    type Filtered<'a>
150        = T::Filtered<'a>
151    where
152        Self: 'a;
153
154    type Error = T::Error;
155
156    #[inline]
157    fn as_filtered(&self) -> Result<Option<Self::Filtered<'_>>, Self::Error> {
158        self.as_ref().as_filtered()
159    }
160}
161
162/// A [`Wrapping`] integer has a value if it is not `0`.
163impl<T: DefaultFilterable> DefaultFilterable for Wrapping<T> {
164    type Filtered<'a>
165        = T::Filtered<'a>
166    where
167        Self: 'a;
168
169    type Error = T::Error;
170
171    #[inline]
172    fn as_filtered(&self) -> Result<Option<Self::Filtered<'_>>, Self::Error> {
173        self.0.as_filtered()
174    }
175}
176
177/// A [`Saturating`] integer has a value if it is not `0`.
178impl<T: DefaultFilterable> DefaultFilterable for Saturating<T> {
179    type Filtered<'a>
180        = T::Filtered<'a>
181    where
182        Self: 'a;
183
184    type Error = T::Error;
185
186    #[inline]
187    fn as_filtered(&self) -> Result<Option<Self::Filtered<'_>>, Self::Error> {
188        self.0.as_filtered()
189    }
190}
191
192macro_rules! impl_for_int {
193    ($($name:ident : $ty:ty)*) => { $(
194        #[doc = concat!("A [`", stringify!($ty), "`] has a value if it is not `0`.")]
195        impl DefaultFilterable for $ty {
196            type Filtered<'a> = $ty;
197            type Error = Infallible;
198
199            #[inline]
200            fn as_filtered(&self) -> Result<Option<$ty>, Infallible> {
201                match *self {
202                    0 => Ok(None),
203                    value => Ok(Some(value)),
204                }
205            }
206        }
207
208        #[doc = concat!("A [`", stringify!($name), "`][num::", stringify!($name),"] always has a value.")]
209        impl DefaultFilterable for num::$name {
210            type Filtered<'a> = $ty;
211            type Error = Infallible;
212
213            #[inline]
214            fn as_filtered(&self) -> Result<Option<$ty>, Infallible> {
215                Ok(Some(self.get()))
216            }
217        }
218    )* };
219}
220
221impl_for_int!(
222    NonZeroU8:u8 NonZeroU16:u16 NonZeroU32:u32 NonZeroU64:u64 NonZeroU128:u128 NonZeroUsize:usize
223    NonZeroI8:i8 NonZeroI16:i16 NonZeroI32:i32 NonZeroI64:i64 NonZeroI128:i128 NonZeroIsize:isize
224);
225
226/// A `bool` has a value if it is [`true`].
227impl DefaultFilterable for bool {
228    type Filtered<'a> = bool;
229    type Error = Infallible;
230
231    #[inline]
232    fn as_filtered(&self) -> Result<Option<bool>, Infallible> {
233        match *self {
234            true => Ok(Some(true)),
235            false => Ok(None),
236        }
237    }
238}
239
240macro_rules! impl_for_float {
241    ($($ty:ty)*) => { $(
242        #[doc = concat!(
243            "An [`",
244            stringify!($ty),
245            "`] has a value if it is [`Normal`][FpCategory::Normal], i.e. it is not zero, \
246            not sub-normal, not infinite and not NaN."
247        )]
248        impl DefaultFilterable for $ty {
249            type Filtered<'a>
250                = Self
251            where
252                Self: 'a;
253
254            type Error = Infallible;
255
256            #[inline]
257            fn as_filtered(&self) -> Result<Option<Self::Filtered<'_>>, Self::Error> {
258                Ok((self.classify() == FpCategory::Normal).then_some(*self))
259            }
260        }
261    )* }
262}
263
264impl_for_float!(f32 f64);
265
266#[test]
267#[cfg(feature = "std")]
268fn test_default_filterable() {
269    use std::borrow::Cow;
270    use std::rc::Rc;
271    use std::string::ToString;
272    use std::sync::{Arc, Mutex};
273
274    use assert_matches::assert_matches;
275
276    // integers
277    assert_matches!(0_u8.as_filtered(), Ok(None));
278    assert_matches!(0_u16.as_filtered(), Ok(None));
279    assert_matches!(0_u32.as_filtered(), Ok(None));
280    assert_matches!(0_u64.as_filtered(), Ok(None));
281    assert_matches!(0_u128.as_filtered(), Ok(None));
282    assert_matches!(0_usize.as_filtered(), Ok(None));
283    assert_matches!(0_i8.as_filtered(), Ok(None));
284    assert_matches!(0_i16.as_filtered(), Ok(None));
285    assert_matches!(0_i32.as_filtered(), Ok(None));
286    assert_matches!(0_i64.as_filtered(), Ok(None));
287    assert_matches!(0_i128.as_filtered(), Ok(None));
288    assert_matches!(0_isize.as_filtered(), Ok(None));
289    assert_matches!(1_u8.as_filtered(), Ok(Some(1)));
290    assert_matches!(1_u16.as_filtered(), Ok(Some(1)));
291    assert_matches!(1_u32.as_filtered(), Ok(Some(1)));
292    assert_matches!(1_u64.as_filtered(), Ok(Some(1)));
293    assert_matches!(1_u128.as_filtered(), Ok(Some(1)));
294    assert_matches!(1_usize.as_filtered(), Ok(Some(1)));
295    assert_matches!(1_i8.as_filtered(), Ok(Some(1)));
296    assert_matches!(1_i16.as_filtered(), Ok(Some(1)));
297    assert_matches!(1_i32.as_filtered(), Ok(Some(1)));
298    assert_matches!(1_i64.as_filtered(), Ok(Some(1)));
299    assert_matches!(1_i128.as_filtered(), Ok(Some(1)));
300    assert_matches!(1_isize.as_filtered(), Ok(Some(1)));
301    assert_matches!((-1_i8).as_filtered(), Ok(Some(-1)));
302    assert_matches!((-1_i16).as_filtered(), Ok(Some(-1)));
303    assert_matches!((-1_i32).as_filtered(), Ok(Some(-1)));
304    assert_matches!((-1_i64).as_filtered(), Ok(Some(-1)));
305    assert_matches!((-1_i128).as_filtered(), Ok(Some(-1)));
306    assert_matches!((-1_isize).as_filtered(), Ok(Some(-1)));
307
308    // floats
309    // -> zero
310    assert_matches!(0_f32.as_filtered(), Ok(None));
311    assert_matches!(0_f64.as_filtered(), Ok(None));
312    // -> subnormal
313    assert_matches!((f32::MIN_POSITIVE / 2.0).as_filtered(), Ok(None));
314    assert_matches!((f64::MIN_POSITIVE / 2.0).as_filtered(), Ok(None));
315    // -> nan
316    assert_matches!(f32::NAN.as_filtered(), Ok(None));
317    assert_matches!(f64::NAN.as_filtered(), Ok(None));
318    // -> infinite
319    assert_matches!(f32::NEG_INFINITY.as_filtered(), Ok(None));
320    assert_matches!(f32::INFINITY.as_filtered(), Ok(None));
321    assert_matches!(f64::NEG_INFINITY.as_filtered(), Ok(None));
322    assert_matches!(f64::INFINITY.as_filtered(), Ok(None));
323    // -> normal
324    assert_matches!(1_f32.as_filtered(), Ok(Some(1.0)));
325    assert_matches!((-1_f32).as_filtered(), Ok(Some(-1.0)));
326    assert_matches!(f32::MIN.as_filtered(), Ok(Some(f32::MIN)));
327    assert_matches!(f32::MIN_POSITIVE.as_filtered(), Ok(Some(f32::MIN_POSITIVE)));
328    assert_matches!(f32::MAX.as_filtered(), Ok(Some(f32::MAX)));
329    assert_matches!(1_f64.as_filtered(), Ok(Some(1.0)));
330    assert_matches!((-1_f64).as_filtered(), Ok(Some(-1.0)));
331    assert_matches!(f64::MIN.as_filtered(), Ok(Some(f64::MIN)));
332    assert_matches!(f64::MIN_POSITIVE.as_filtered(), Ok(Some(f64::MIN_POSITIVE)));
333    assert_matches!(f64::MAX.as_filtered(), Ok(Some(f64::MAX)));
334
335    // non-zero integers
336    assert_matches!(num::NonZeroU8::new(1).unwrap().as_filtered(), Ok(Some(1)));
337    assert_matches!(num::NonZeroU16::new(1).unwrap().as_filtered(), Ok(Some(1)));
338    assert_matches!(num::NonZeroU32::new(1).unwrap().as_filtered(), Ok(Some(1)));
339    assert_matches!(num::NonZeroU64::new(1).unwrap().as_filtered(), Ok(Some(1)));
340    assert_matches!(num::NonZeroU128::new(1).unwrap().as_filtered(), Ok(Some(1)));
341    assert_matches!(
342        num::NonZeroUsize::new(1).unwrap().as_filtered(),
343        Ok(Some(1))
344    );
345    assert_matches!(num::NonZeroI8::new(1).unwrap().as_filtered(), Ok(Some(1)));
346    assert_matches!(num::NonZeroI16::new(1).unwrap().as_filtered(), Ok(Some(1)));
347    assert_matches!(num::NonZeroI32::new(1).unwrap().as_filtered(), Ok(Some(1)));
348    assert_matches!(num::NonZeroI64::new(1).unwrap().as_filtered(), Ok(Some(1)));
349    assert_matches!(num::NonZeroI128::new(1).unwrap().as_filtered(), Ok(Some(1)));
350    assert_matches!(
351        num::NonZeroIsize::new(1).unwrap().as_filtered(),
352        Ok(Some(1))
353    );
354
355    // strings
356    assert_matches!("".as_filtered(), Ok(None));
357    assert_matches!("hello".as_filtered(), Ok(Some("hello")));
358    assert_matches!("".to_string().as_filtered(), Ok(None));
359    assert_matches!("hello".to_string().as_filtered(), Ok(Some("hello")));
360    assert_matches!(Cow::Borrowed("").as_filtered(), Ok(None));
361    assert_matches!(Cow::Borrowed("hello").as_filtered(), Ok(Some("hello")));
362    assert_matches!(Cow::<str>::Owned("".to_string()).as_filtered(), Ok(None));
363    assert_matches!(
364        Cow::<str>::Owned("hello".to_string()).as_filtered(),
365        Ok(Some("hello"))
366    );
367
368    // results + options
369    assert_matches!(Ok::<(), ()>(()).as_filtered(), Ok(Some(())));
370    assert_matches!(Err::<(), ()>(()).as_filtered(), Ok(None));
371    assert_matches!(Some(()).as_filtered(), Ok(Some(())));
372    assert_matches!(None::<()>.as_filtered(), Ok(None));
373
374    // references
375    assert_matches!(Arc::new("").as_filtered(), Ok(None));
376    assert_matches!(Arc::new("hello").as_filtered(), Ok(Some("hello")));
377    assert_matches!(Arc::pin("").as_filtered(), Ok(None));
378    assert_matches!(Arc::pin("hello").as_filtered(), Ok(Some("hello")));
379    assert_matches!(Rc::new("").as_filtered(), Ok(None));
380    assert_matches!(Rc::new("hello").as_filtered(), Ok(Some("hello")));
381    assert_matches!(Rc::pin("").as_filtered(), Ok(None));
382    assert_matches!(Rc::pin("hello").as_filtered(), Ok(Some("hello")));
383    assert_matches!(Mutex::new("").try_lock().unwrap().as_filtered(), Ok(None));
384    assert_matches!(
385        Mutex::new("hello").try_lock().unwrap().as_filtered(),
386        Ok(Some("hello"))
387    );
388}