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