Skip to content

Commit 90c531c

Browse files
committed
Implement DefaultUnwrappable for {int}, str and bool
1 parent d00104e commit 90c531c

File tree

2 files changed

+204
-37
lines changed

2 files changed

+204
-37
lines changed

askama/src/filters/builtin.rs

Lines changed: 124 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ const _: () = {
433433
pub fn default<L: DefaultUnwrappable, R>(
434434
value: &L,
435435
default: R,
436-
) -> Result<Either<&L::Unwrapped, R>, L::Error> {
436+
) -> Result<Either<L::Unwrapped<'_>, R>, L::Error> {
437437
match value.try_unwrap()? {
438438
Some(value) => Ok(Either::Left(value)),
439439
None => Ok(Either::Right(default)),
@@ -447,64 +447,151 @@ pub fn default<L: DefaultUnwrappable, R>(
447447
)]
448448
pub trait DefaultUnwrappable {
449449
/// The wrapped value
450-
type Unwrapped;
450+
type Unwrapped<'a>
451+
where
452+
Self: 'a;
451453

452454
/// An error that prevented [`try_unwrap()`](DefaultUnwrappable::try_unwrap) to succeed,
453455
/// e.g. a poisoned state or an unacquirable lock.
454456
type Error: Into<crate::Error>;
455457

456458
/// Try to unwrap the contained value. Returns `Ok(None)` if the value could not be unwrapped.
457-
fn try_unwrap(&self) -> Result<Option<&Self::Unwrapped>, Self::Error>;
459+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error>;
458460
}
459461

460-
impl_for_ref! {
461-
impl DefaultUnwrappable for T {
462-
type Unwrapped = T::Unwrapped;
463-
type Error = T::Error;
462+
const _: () = {
463+
impl_for_ref! {
464+
impl DefaultUnwrappable for T {
465+
type Unwrapped<'a> = T::Unwrapped<'a>
466+
where
467+
Self: 'a;
468+
469+
type Error = T::Error;
470+
471+
#[inline]
472+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
473+
<T>::try_unwrap(self)
474+
}
475+
}
476+
}
477+
478+
impl<T> DefaultUnwrappable for Pin<T>
479+
where
480+
T: Deref,
481+
<T as Deref>::Target: DefaultUnwrappable,
482+
{
483+
type Unwrapped<'a>
484+
= <<T as Deref>::Target as DefaultUnwrappable>::Unwrapped<'a>
485+
where
486+
Self: 'a;
487+
488+
type Error = <<T as Deref>::Target as DefaultUnwrappable>::Error;
464489

465490
#[inline]
466-
fn try_unwrap(&self) -> Result<Option<&Self::Unwrapped>, Self::Error> {
467-
<T>::try_unwrap(self)
491+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
492+
self.as_ref().get_ref().try_unwrap()
468493
}
469494
}
470-
}
471495

472-
impl<T> DefaultUnwrappable for Pin<T>
473-
where
474-
T: Deref,
475-
<T as Deref>::Target: DefaultUnwrappable,
476-
{
477-
type Unwrapped = <<T as Deref>::Target as DefaultUnwrappable>::Unwrapped;
478-
type Error = <<T as Deref>::Target as DefaultUnwrappable>::Error;
496+
impl<T> DefaultUnwrappable for Option<T> {
497+
type Unwrapped<'a>
498+
= &'a T
499+
where
500+
Self: 'a;
479501

480-
#[inline]
481-
fn try_unwrap(&self) -> Result<Option<&Self::Unwrapped>, Self::Error> {
482-
self.as_ref().get_ref().try_unwrap()
502+
type Error = Infallible;
503+
504+
#[inline]
505+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
506+
Ok(self.as_ref())
507+
}
483508
}
484-
}
485509

486-
impl<T> DefaultUnwrappable for Option<T> {
487-
type Unwrapped = T;
488-
type Error = Infallible;
510+
impl<T, E> DefaultUnwrappable for Result<T, E> {
511+
type Unwrapped<'a>
512+
= &'a T
513+
where
514+
Self: 'a;
489515

490-
#[inline]
491-
fn try_unwrap(&self) -> Result<Option<&Self::Unwrapped>, Self::Error> {
492-
Ok(self.as_ref())
516+
type Error = Infallible;
517+
518+
#[inline]
519+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
520+
match self {
521+
Ok(value) => Ok(Some(value)),
522+
Err(_) => Ok(None),
523+
}
524+
}
493525
}
494-
}
495526

496-
impl<T, E> DefaultUnwrappable for Result<T, E> {
497-
type Unwrapped = T;
498-
type Error = Infallible;
527+
impl DefaultUnwrappable for str {
528+
type Unwrapped<'a>
529+
= &'a str
530+
where
531+
Self: 'a;
499532

500-
#[inline]
501-
fn try_unwrap(&self) -> Result<Option<&Self::Unwrapped>, Self::Error> {
502-
match self {
503-
Ok(value) => Ok(Some(value)),
504-
Err(_) => Ok(None),
533+
type Error = Infallible;
534+
535+
#[inline]
536+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
537+
match self.is_empty() {
538+
false => Ok(Some(self)),
539+
true => Ok(None),
540+
}
505541
}
506542
}
507-
}
543+
544+
#[cfg(feature = "alloc")]
545+
impl DefaultUnwrappable for alloc::string::String {
546+
type Unwrapped<'a>
547+
= &'a str
548+
where
549+
Self: 'a;
550+
551+
type Error = Infallible;
552+
553+
#[inline]
554+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
555+
self.as_str().try_unwrap()
556+
}
557+
}
558+
559+
macro_rules! impl_for_int {
560+
($($ty:ty)*) => { $(
561+
impl DefaultUnwrappable for $ty {
562+
type Unwrapped<'a> = Self;
563+
type Error = Infallible;
564+
565+
#[inline]
566+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
567+
match *self {
568+
0 => Ok(None),
569+
value => Ok(Some(value)),
570+
}
571+
}
572+
}
573+
)* };
574+
}
575+
576+
impl_for_int!(
577+
u8 u16 u32 u64 u128 usize
578+
i8 i16 i32 i64 i128 isize
579+
);
580+
581+
impl DefaultUnwrappable for bool {
582+
type Unwrapped<'a> = Self;
583+
584+
type Error = Infallible;
585+
586+
#[inline]
587+
fn try_unwrap(&self) -> Result<Option<Self::Unwrapped<'_>>, Self::Error> {
588+
match *self {
589+
true => Ok(Some(true)),
590+
false => Ok(None),
591+
}
592+
}
593+
}
594+
};
508595

509596
/// Render either `L` or `R`
510597
pub enum Either<L, R> {

testing/tests/filters.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
extern crate serde_json;
44

55
use std::num::NonZeroU32;
6+
use std::pin::Pin;
7+
use std::sync::Arc;
68

79
use askama::Template;
810
use assert_matches::assert_matches;
@@ -595,3 +597,81 @@ fn test_default_expr_with_unwrap() {
595597
assert_eq!(AResult { value: b"H\xffllo" }.render().unwrap(), "unknown");
596598
assert_eq!(AResult { value: b"Hello" }.render().unwrap(), "Hello");
597599
}
600+
601+
#[test]
602+
fn test_default_with_int() {
603+
// In this test a default value should be returned for an integer value 0.
604+
605+
#[derive(Template)]
606+
#[template(source = r#"{{ value | default("unknown", true) }}"#, ext = "html")]
607+
struct U8 {
608+
value: Arc<Pin<Box<u8>>>,
609+
}
610+
611+
assert_eq!(
612+
U8 {
613+
value: Arc::new(Box::pin(0))
614+
}
615+
.render()
616+
.unwrap(),
617+
"unknown"
618+
);
619+
assert_eq!(
620+
U8 {
621+
value: Arc::new(Box::pin(42))
622+
}
623+
.render()
624+
.unwrap(),
625+
"42"
626+
);
627+
}
628+
629+
#[test]
630+
fn test_default_with_str() {
631+
// In this test a default value should be returned for an empty string.
632+
633+
#[derive(Template)]
634+
#[template(source = r#"{{ value | default("unknown", true) }}"#, ext = "html")]
635+
struct Str<'a> {
636+
value: &'a str,
637+
}
638+
639+
assert_eq!(Str { value: "" }.render().unwrap(), "unknown");
640+
assert_eq!(Str { value: "Hello" }.render().unwrap(), "Hello");
641+
#[derive(Template)]
642+
#[template(source = r#"{{ value | default("unknown", true) }}"#, ext = "html")]
643+
struct AString {
644+
value: String,
645+
}
646+
647+
assert_eq!(
648+
AString {
649+
value: "".to_owned()
650+
}
651+
.render()
652+
.unwrap(),
653+
"unknown"
654+
);
655+
assert_eq!(
656+
AString {
657+
value: "Hello".to_owned()
658+
}
659+
.render()
660+
.unwrap(),
661+
"Hello"
662+
);
663+
}
664+
665+
#[test]
666+
fn test_default_with_bool() {
667+
// In this test a default value should be returned for `false`.
668+
669+
#[derive(Template)]
670+
#[template(source = r#"{{ value | default("unknown", true) }}"#, ext = "html")]
671+
struct Bool {
672+
value: bool,
673+
}
674+
675+
assert_eq!(Bool { value: false }.render().unwrap(), "unknown");
676+
assert_eq!(Bool { value: true }.render().unwrap(), "true");
677+
}

0 commit comments

Comments
 (0)