askama/filters/
escape.rs

1use core::convert::Infallible;
2use core::fmt::{self, Formatter, Write};
3use core::ops::Deref;
4use core::pin::Pin;
5use core::str;
6
7use crate::Values;
8
9/// Marks a string (or other `Display` type) as safe
10///
11/// Use this if you want to allow markup in an expression, or if you know
12/// that the expression's contents don't need to be escaped.
13///
14/// Askama will automatically insert the first (`Escaper`) argument,
15/// so this filter only takes a single argument of any type that implements
16/// `Display`.
17///
18/// ```
19/// # #[cfg(feature = "code-in-doc")] {
20/// # use askama::Template;
21/// /// ```jinja
22/// /// <div>{{ example|safe }}</div>
23/// /// ```
24/// #[derive(Template)]
25/// #[template(ext = "html", in_doc = true)]
26/// struct Example<'a> {
27///     example: &'a str,
28/// }
29///
30/// assert_eq!(
31///     Example { example: "<p>I'm Safe</p>" }.to_string(),
32///     "<div><p>I'm Safe</p></div>"
33/// );
34/// # }
35/// ```
36#[inline]
37pub fn safe<T, E>(text: T, escaper: E) -> Result<Safe<T>, Infallible> {
38    let _ = escaper; // it should not be part of the interface that the `escaper` is unused
39    Ok(Safe(text))
40}
41
42/// Escapes strings according to the escape mode.
43///
44/// Askama will automatically insert the first (`Escaper`) argument,
45/// so this filter only takes a single argument of any type that implements
46/// `Display`.
47///
48/// It is possible to optionally specify an escaper other than the default for
49/// the template's extension, like `{{ val|escape("txt") }}`.
50///
51/// ```
52/// # #[cfg(feature = "code-in-doc")] {
53/// # use askama::Template;
54/// /// ```jinja
55/// /// <div>{{ example|escape }}</div>
56/// /// ```
57/// #[derive(Template)]
58/// #[template(ext = "html", in_doc = true)]
59/// struct Example<'a> {
60///     example: &'a str,
61/// }
62///
63/// assert_eq!(
64///     Example { example: "Escape <>&" }.to_string(),
65///     "<div>Escape &#60;&#62;&#38;</div>"
66/// );
67/// # }
68/// ```
69#[inline]
70pub fn escape<T, E>(text: T, escaper: E) -> Result<Safe<EscapeDisplay<T, E>>, Infallible> {
71    Ok(Safe(EscapeDisplay(text, escaper)))
72}
73
74pub struct EscapeDisplay<T, E>(T, E);
75
76impl<T: fmt::Display, E: Escaper> fmt::Display for EscapeDisplay<T, E> {
77    #[inline]
78    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
79        write!(EscapeWriter(fmt, self.1), "{}", &self.0)
80    }
81}
82
83impl<T: FastWritable, E: Escaper> FastWritable for EscapeDisplay<T, E> {
84    #[inline]
85    fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
86        self.0.write_into(&mut EscapeWriter(dest, self.1))
87    }
88}
89
90struct EscapeWriter<W, E>(W, E);
91
92impl<W: Write, E: Escaper> Write for EscapeWriter<W, E> {
93    #[inline]
94    fn write_str(&mut self, s: &str) -> fmt::Result {
95        self.1.write_escaped_str(&mut self.0, s)
96    }
97
98    #[inline]
99    fn write_char(&mut self, c: char) -> fmt::Result {
100        self.1.write_escaped_char(&mut self.0, c)
101    }
102}
103
104/// Alias for [`escape()`]
105///
106/// ```
107/// # #[cfg(feature = "code-in-doc")] {
108/// # use askama::Template;
109/// /// ```jinja
110/// /// <div>{{ example|e }}</div>
111/// /// ```
112/// #[derive(Template)]
113/// #[template(ext = "html", in_doc = true)]
114/// struct Example<'a> {
115///     example: &'a str,
116/// }
117///
118/// assert_eq!(
119///     Example { example: "Escape <>&" }.to_string(),
120///     "<div>Escape &#60;&#62;&#38;</div>"
121/// );
122/// # }
123/// ```
124#[inline]
125pub fn e<T, E>(text: T, escaper: E) -> Result<Safe<EscapeDisplay<T, E>>, Infallible> {
126    escape(text, escaper)
127}
128
129/// Escape characters in a safe way for HTML texts and attributes
130///
131/// * `"` => `&#34;`
132/// * `&` => `&#38;`
133/// * `'` => `&#39;`
134/// * `<` => `&#60;`
135/// * `>` => `&#62;`
136#[derive(Debug, Clone, Copy, Default)]
137pub struct Html;
138
139impl Escaper for Html {
140    #[inline]
141    fn write_escaped_str<W: Write>(&self, dest: W, string: &str) -> fmt::Result {
142        crate::html::write_escaped_str(dest, string)
143    }
144
145    #[inline]
146    fn write_escaped_char<W: Write>(&self, dest: W, c: char) -> fmt::Result {
147        crate::html::write_escaped_char(dest, c)
148    }
149}
150
151/// Don't escape the input but return in verbatim
152#[derive(Debug, Clone, Copy, Default)]
153pub struct Text;
154
155impl Escaper for Text {
156    #[inline]
157    fn write_escaped_str<W: Write>(&self, mut dest: W, string: &str) -> fmt::Result {
158        dest.write_str(string)
159    }
160
161    #[inline]
162    fn write_escaped_char<W: Write>(&self, mut dest: W, c: char) -> fmt::Result {
163        dest.write_char(c)
164    }
165}
166
167/// Escapers are used to make generated text safe for printing in some context.
168///
169/// E.g. in an [`Html`] context, any and all generated text can be used in HTML/XML text nodes and
170/// attributes, without for for maliciously injected data.
171pub trait Escaper: Copy {
172    /// Escaped the input string `string` into `dest`
173    fn write_escaped_str<W: Write>(&self, dest: W, string: &str) -> fmt::Result;
174
175    /// Escaped the input char `c` into `dest`
176    #[inline]
177    fn write_escaped_char<W: Write>(&self, dest: W, c: char) -> fmt::Result {
178        self.write_escaped_str(dest, c.encode_utf8(&mut [0; 4]))
179    }
180}
181
182/// Used internally by askama to select the appropriate escaper
183pub trait AutoEscape {
184    /// The wrapped or converted result type
185    type Escaped: fmt::Display;
186    /// Early error testing for the input value, usually [`Infallible`]
187    type Error: Into<crate::Error>;
188
189    /// Used internally by askama to select the appropriate escaper
190    fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error>;
191}
192
193/// Used internally by askama to select the appropriate escaper
194#[derive(Debug, Clone)]
195pub struct AutoEscaper<'a, T: ?Sized, E> {
196    text: &'a T,
197    escaper: E,
198}
199
200impl<'a, T: ?Sized, E> AutoEscaper<'a, T, E> {
201    /// Used internally by askama to select the appropriate escaper
202    #[inline]
203    pub fn new(text: &'a T, escaper: E) -> Self {
204        Self { text, escaper }
205    }
206}
207
208/// Use the provided escaper
209impl<'a, T: fmt::Display + ?Sized, E: Escaper> AutoEscape for &&AutoEscaper<'a, T, E> {
210    type Escaped = EscapeDisplay<&'a T, E>;
211    type Error = Infallible;
212
213    #[inline]
214    fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
215        Ok(EscapeDisplay(self.text, self.escaper))
216    }
217}
218
219/// Types that implement this marker trait don't need to be HTML escaped
220///
221/// Please note that this trait is only meant as speed-up helper. In some odd circumcises askama
222/// might still decide to HTML escape the input, so if this must not happen, then you need to use
223/// the [`|safe`](super::safe) filter to prevent the auto escaping.
224///
225/// If you are unsure if your type generates HTML safe output in all cases, then DON'T mark it.
226/// Better safe than sorry!
227pub trait HtmlSafe: fmt::Display {}
228
229/// Don't escape HTML safe types
230impl<'a, T: HtmlSafe + ?Sized> AutoEscape for &AutoEscaper<'a, T, Html> {
231    type Escaped = &'a T;
232    type Error = Infallible;
233
234    #[inline]
235    fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
236        Ok(self.text)
237    }
238}
239
240/// Mark the output of a filter as "maybe safe"
241///
242/// This enum can be used as a transparent return type of custom filters that want to mark
243/// their output as "safe" depending on some circumstances, i.e. that their output maybe does not
244/// need to be escaped.
245///
246/// If the filter is not used as the last element in the filter chain, then any assumption is void.
247/// Let the next filter decide if the output is safe or not.
248///
249/// ## Example
250///
251/// ```rust
252/// mod filters {
253///     use askama::{filters::MaybeSafe, Result};
254///
255///     // Do not actually use this filter! It's an intentionally bad example.
256///     pub fn backdoor<T: std::fmt::Display>(s: T, enable: &bool) -> Result<MaybeSafe<T>> {
257///         Ok(match *enable {
258///             true => MaybeSafe::Safe(s),
259///             false => MaybeSafe::NeedsEscaping(s),
260///         })
261///     }
262/// }
263///
264/// #[derive(askama::Template)]
265/// #[template(
266///     source = "<div class='{{ klass|backdoor(enable_backdoor) }}'></div>",
267///     ext = "html"
268/// )]
269/// struct DivWithBackdoor<'a> {
270///     klass: &'a str,
271///     enable_backdoor: bool,
272/// }
273///
274/// assert_eq!(
275///     DivWithBackdoor { klass: "<script>", enable_backdoor: false }.to_string(),
276///     "<div class='&#60;script&#62;'></div>",
277/// );
278/// assert_eq!(
279///     DivWithBackdoor { klass: "<script>", enable_backdoor: true }.to_string(),
280///     "<div class='<script>'></div>",
281/// );
282/// ```
283pub enum MaybeSafe<T> {
284    /// The contained value does not need escaping
285    Safe(T),
286    /// The contained value needs to be escaped
287    NeedsEscaping(T),
288}
289
290const _: () = {
291    // This is the fallback. The filter is not the last element of the filter chain.
292    impl<T: fmt::Display> fmt::Display for MaybeSafe<T> {
293        #[inline]
294        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
295            let inner = match self {
296                MaybeSafe::Safe(inner) => inner,
297                MaybeSafe::NeedsEscaping(inner) => inner,
298            };
299            write!(f, "{inner}")
300        }
301    }
302
303    // This is the fallback. The filter is not the last element of the filter chain.
304    impl<T: FastWritable> FastWritable for MaybeSafe<T> {
305        #[inline]
306        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
307            let inner = match self {
308                MaybeSafe::Safe(inner) => inner,
309                MaybeSafe::NeedsEscaping(inner) => inner,
310            };
311            inner.write_into(dest)
312        }
313    }
314
315    macro_rules! add_ref {
316        ($([$($tt:tt)*])*) => { $(
317            impl<'a, T: fmt::Display, E: Escaper> AutoEscape
318            for &AutoEscaper<'a, $($tt)* MaybeSafe<T>, E> {
319                type Escaped = Wrapped<'a, T, E>;
320                type Error = Infallible;
321
322                #[inline]
323                fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
324                    match self.text {
325                        MaybeSafe::Safe(t) => Ok(Wrapped::Safe(t)),
326                        MaybeSafe::NeedsEscaping(t) => Ok(Wrapped::NeedsEscaping(t, self.escaper)),
327                    }
328                }
329            }
330        )* };
331    }
332
333    add_ref!([] [&] [&&] [&&&]);
334
335    pub enum Wrapped<'a, T: ?Sized, E> {
336        Safe(&'a T),
337        NeedsEscaping(&'a T, E),
338    }
339
340    impl<T: FastWritable + ?Sized, E: Escaper> FastWritable for Wrapped<'_, T, E> {
341        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
342            match *self {
343                Wrapped::Safe(t) => t.write_into(dest),
344                Wrapped::NeedsEscaping(t, e) => EscapeDisplay(t, e).write_into(dest),
345            }
346        }
347    }
348
349    impl<T: fmt::Display + ?Sized, E: Escaper> fmt::Display for Wrapped<'_, T, E> {
350        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
351            match *self {
352                Wrapped::Safe(t) => write!(f, "{t}"),
353                Wrapped::NeedsEscaping(t, e) => EscapeDisplay(t, e).fmt(f),
354            }
355        }
356    }
357};
358
359/// Mark the output of a filter as "safe"
360///
361/// This struct can be used as a transparent return type of custom filters that want to mark their
362/// output as "safe" no matter what, i.e. that their output does not need to be escaped.
363///
364/// If the filter is not used as the last element in the filter chain, then any assumption is void.
365/// Let the next filter decide if the output is safe or not.
366///
367/// ## Example
368///
369/// ```rust
370/// mod filters {
371///     use askama::{filters::Safe, Result};
372///
373///     // Do not actually use this filter! It's an intentionally bad example.
374///     pub fn strip_except_apos(s: impl ToString) -> Result<Safe<String>> {
375///         Ok(Safe(s
376///             .to_string()
377///             .chars()
378///             .filter(|c| !matches!(c, '<' | '>' | '"' | '&'))
379///             .collect()
380///         ))
381///     }
382/// }
383///
384/// #[derive(askama::Template)]
385/// #[template(
386///     source = "<div class='{{ klass|strip_except_apos }}'></div>",
387///     ext = "html"
388/// )]
389/// struct DivWithClass<'a> {
390///     klass: &'a str,
391/// }
392///
393/// assert_eq!(
394///     DivWithClass { klass: "<&'lifetime X>" }.to_string(),
395///     "<div class=''lifetime X'></div>",
396/// );
397/// ```
398pub struct Safe<T>(pub T);
399
400const _: () = {
401    // This is the fallback. The filter is not the last element of the filter chain.
402    impl<T: fmt::Display> fmt::Display for Safe<T> {
403        #[inline]
404        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
405            write!(f, "{}", self.0)
406        }
407    }
408
409    // This is the fallback. The filter is not the last element of the filter chain.
410    impl<T: FastWritable> FastWritable for Safe<T> {
411        #[inline]
412        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
413            self.0.write_into(dest)
414        }
415    }
416
417    macro_rules! add_ref {
418        ($([$($tt:tt)*])*) => { $(
419            impl<'a, T: fmt::Display, E> AutoEscape for &AutoEscaper<'a, $($tt)* Safe<T>, E> {
420                type Escaped = &'a T;
421                type Error = Infallible;
422
423                #[inline]
424                fn askama_auto_escape(&self) -> Result<Self::Escaped, Self::Error> {
425                    Ok(&self.text.0)
426                }
427            }
428        )* };
429    }
430
431    add_ref!([] [&] [&&] [&&&]);
432};
433
434/// There is not need to mark the output of a custom filter as "unsafe"; this is simply the default
435pub struct Unsafe<T>(pub T);
436
437impl<T: fmt::Display> fmt::Display for Unsafe<T> {
438    #[inline]
439    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
440        write!(f, "{}", self.0)
441    }
442}
443
444/// Like [`Safe`], but only for HTML output
445pub struct HtmlSafeOutput<T>(pub T);
446
447impl<T: fmt::Display> fmt::Display for HtmlSafeOutput<T> {
448    #[inline]
449    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
450        write!(f, "{}", self.0)
451    }
452}
453
454macro_rules! mark_html_safe {
455    ($($ty:ty),* $(,)?) => {$(
456        impl HtmlSafe for $ty {}
457    )*};
458}
459
460mark_html_safe! {
461    bool,
462    f32, f64,
463    i8, i16, i32, i64, i128, isize,
464    u8, u16, u32, u64, u128, usize,
465    core::num::NonZeroI8, core::num::NonZeroI16, core::num::NonZeroI32,
466    core::num::NonZeroI64, core::num::NonZeroI128, core::num::NonZeroIsize,
467    core::num::NonZeroU8, core::num::NonZeroU16, core::num::NonZeroU32,
468    core::num::NonZeroU64, core::num::NonZeroU128, core::num::NonZeroUsize,
469}
470
471impl<T: HtmlSafe> HtmlSafe for core::num::Wrapping<T> {}
472impl<T: fmt::Display> HtmlSafe for HtmlSafeOutput<T> {}
473
474#[cfg(feature = "alloc")]
475impl<T> HtmlSafe for alloc::borrow::Cow<'_, T>
476where
477    T: HtmlSafe + alloc::borrow::ToOwned + ?Sized,
478    T::Owned: HtmlSafe,
479{
480}
481
482crate::impl_for_ref! {
483    impl HtmlSafe for T {}
484}
485
486impl<T: HtmlSafe> HtmlSafe for Pin<T> {}
487
488/// Used internally by askama to select the appropriate [`write!()`] mechanism
489pub struct Writable<'a, S: ?Sized>(pub &'a S);
490
491/// Used internally by askama to select the appropriate [`write!()`] mechanism
492pub trait WriteWritable {
493    /// Used internally by askama to select the appropriate [`write!()`] mechanism
494    fn askama_write<W: fmt::Write + ?Sized>(
495        &self,
496        dest: &mut W,
497        values: &dyn Values,
498    ) -> crate::Result<()>;
499}
500
501/// Used internally by askama to speed up writing some types.
502///
503/// Types implementing this trait can be written without needing to employ an [`fmt::Formatter`].
504pub trait FastWritable {
505    /// Used internally by askama to speed up writing some types.
506    fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()>;
507}
508
509const _: () = {
510    crate::impl_for_ref! {
511        impl FastWritable for T {
512            #[inline]
513            fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
514                <T>::write_into(self, dest)
515            }
516        }
517    }
518
519    impl<T> FastWritable for Pin<T>
520    where
521        T: Deref,
522        <T as Deref>::Target: FastWritable,
523    {
524        #[inline]
525        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
526            self.as_ref().get_ref().write_into(dest)
527        }
528    }
529
530    #[cfg(feature = "alloc")]
531    impl<T: FastWritable + alloc::borrow::ToOwned> FastWritable for alloc::borrow::Cow<'_, T> {
532        #[inline]
533        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
534            T::write_into(self.as_ref(), dest)
535        }
536    }
537
538    // implement FastWritable for a list of types
539    macro_rules! impl_for_int {
540        ($($ty:ty)*) => { $(
541            impl FastWritable for $ty {
542                #[inline]
543                fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
544                    itoa::Buffer::new().format(*self).write_into(dest)
545                }
546            }
547        )* };
548    }
549
550    impl_for_int!(
551        u8 u16 u32 u64 u128 usize
552        i8 i16 i32 i64 i128 isize
553    );
554
555    // implement FastWritable for a list of non-zero integral types
556    macro_rules! impl_for_nz_int {
557        ($($id:ident)*) => { $(
558            impl FastWritable for core::num::$id {
559                #[inline]
560                fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
561                    self.get().write_into(dest)
562                }
563            }
564        )* };
565    }
566
567    impl_for_nz_int!(
568        NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
569        NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize
570    );
571
572    impl FastWritable for str {
573        #[inline]
574        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
575            Ok(dest.write_str(self)?)
576        }
577    }
578
579    #[cfg(feature = "alloc")]
580    impl FastWritable for alloc::string::String {
581        #[inline]
582        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
583            self.as_str().write_into(dest)
584        }
585    }
586
587    impl FastWritable for bool {
588        #[inline]
589        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
590            Ok(dest.write_str(match self {
591                true => "true",
592                false => "false",
593            })?)
594        }
595    }
596
597    impl FastWritable for char {
598        #[inline]
599        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
600            Ok(dest.write_char(*self)?)
601        }
602    }
603
604    impl FastWritable for fmt::Arguments<'_> {
605        fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
606            Ok(match self.as_str() {
607                Some(s) => dest.write_str(s),
608                None => dest.write_fmt(*self),
609            }?)
610        }
611    }
612
613    impl<S: crate::Template + ?Sized> WriteWritable for &Writable<'_, S> {
614        #[inline]
615        fn askama_write<W: fmt::Write + ?Sized>(
616            &self,
617            dest: &mut W,
618            values: &dyn Values,
619        ) -> crate::Result<()> {
620            self.0.render_into_with_values(dest, values)
621        }
622    }
623
624    impl<S: FastWritable + ?Sized> WriteWritable for &&Writable<'_, S> {
625        #[inline]
626        fn askama_write<W: fmt::Write + ?Sized>(
627            &self,
628            dest: &mut W,
629            _: &dyn Values,
630        ) -> crate::Result<()> {
631            self.0.write_into(dest)
632        }
633    }
634
635    impl<S: fmt::Display + ?Sized> WriteWritable for &&&Writable<'_, S> {
636        #[inline]
637        fn askama_write<W: fmt::Write + ?Sized>(
638            &self,
639            dest: &mut W,
640            _: &dyn Values,
641        ) -> crate::Result<()> {
642            Ok(write!(dest, "{}", self.0)?)
643        }
644    }
645};
646
647#[test]
648#[cfg(feature = "alloc")]
649fn test_escape() {
650    use alloc::string::ToString;
651
652    assert_eq!(escape("", Html).unwrap().to_string(), "");
653    assert_eq!(escape("<&>", Html).unwrap().to_string(), "&#60;&#38;&#62;");
654    assert_eq!(escape("bla&", Html).unwrap().to_string(), "bla&#38;");
655    assert_eq!(escape("<foo", Html).unwrap().to_string(), "&#60;foo");
656    assert_eq!(escape("bla&h", Html).unwrap().to_string(), "bla&#38;h");
657
658    assert_eq!(escape("", Text).unwrap().to_string(), "");
659    assert_eq!(escape("<&>", Text).unwrap().to_string(), "<&>");
660    assert_eq!(escape("bla&", Text).unwrap().to_string(), "bla&");
661    assert_eq!(escape("<foo", Text).unwrap().to_string(), "<foo");
662    assert_eq!(escape("bla&h", Text).unwrap().to_string(), "bla&h");
663}
664
665#[test]
666#[cfg(feature = "alloc")]
667fn test_html_safe_marker() {
668    use alloc::string::ToString;
669
670    struct Script1;
671    struct Script2;
672
673    impl fmt::Display for Script1 {
674        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
675            f.write_str("<script>")
676        }
677    }
678
679    impl fmt::Display for Script2 {
680        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
681            f.write_str("<script>")
682        }
683    }
684
685    impl HtmlSafe for Script2 {}
686
687    assert_eq!(
688        (&&AutoEscaper::new(&Script1, Html))
689            .askama_auto_escape()
690            .unwrap()
691            .to_string(),
692        "&#60;script&#62;",
693    );
694    assert_eq!(
695        (&&AutoEscaper::new(&Script2, Html))
696            .askama_auto_escape()
697            .unwrap()
698            .to_string(),
699        "<script>",
700    );
701
702    assert_eq!(
703        (&&AutoEscaper::new(&Script1, Text))
704            .askama_auto_escape()
705            .unwrap()
706            .to_string(),
707        "<script>",
708    );
709    assert_eq!(
710        (&&AutoEscaper::new(&Script2, Text))
711            .askama_auto_escape()
712            .unwrap()
713            .to_string(),
714        "<script>",
715    );
716
717    assert_eq!(
718        (&&AutoEscaper::new(&Safe(Script1), Html))
719            .askama_auto_escape()
720            .unwrap()
721            .to_string(),
722        "<script>",
723    );
724    assert_eq!(
725        (&&AutoEscaper::new(&Safe(Script2), Html))
726            .askama_auto_escape()
727            .unwrap()
728            .to_string(),
729        "<script>",
730    );
731
732    assert_eq!(
733        (&&AutoEscaper::new(&Unsafe(Script1), Html))
734            .askama_auto_escape()
735            .unwrap()
736            .to_string(),
737        "&#60;script&#62;",
738    );
739    assert_eq!(
740        (&&AutoEscaper::new(&Unsafe(Script2), Html))
741            .askama_auto_escape()
742            .unwrap()
743            .to_string(),
744        "&#60;script&#62;",
745    );
746
747    assert_eq!(
748        (&&AutoEscaper::new(&MaybeSafe::Safe(Script1), Html))
749            .askama_auto_escape()
750            .unwrap()
751            .to_string(),
752        "<script>",
753    );
754    assert_eq!(
755        (&&AutoEscaper::new(&MaybeSafe::Safe(Script2), Html))
756            .askama_auto_escape()
757            .unwrap()
758            .to_string(),
759        "<script>",
760    );
761    assert_eq!(
762        (&&AutoEscaper::new(&MaybeSafe::NeedsEscaping(Script1), Html))
763            .askama_auto_escape()
764            .unwrap()
765            .to_string(),
766        "&#60;script&#62;",
767    );
768    assert_eq!(
769        (&&AutoEscaper::new(&MaybeSafe::NeedsEscaping(Script2), Html))
770            .askama_auto_escape()
771            .unwrap()
772            .to_string(),
773        "&#60;script&#62;",
774    );
775
776    assert_eq!(
777        (&&AutoEscaper::new(&Safe(std::pin::Pin::new(&Script1)), Html))
778            .askama_auto_escape()
779            .unwrap()
780            .to_string(),
781        "<script>",
782    );
783    assert_eq!(
784        (&&AutoEscaper::new(&Safe(std::pin::Pin::new(&Script2)), Html))
785            .askama_auto_escape()
786            .unwrap()
787            .to_string(),
788        "<script>",
789    );
790}