topcoat_view/string.rs
1use std::ops::Deref;
2
3/// A reference to a static string that a view can store without copying.
4///
5/// Use this when interpolating a string constant into a view:
6///
7/// ```rust
8/// # use topcoat::view::{PromotedStr, View, component, view};
9/// # #[component]
10/// # async fn example() -> topcoat::Result<impl View> {
11/// Ok(view! {
12/// <div>(PromotedStr(&"hello"))</div>
13/// })
14/// # }
15/// ```
16///
17/// The leading `&` borrows the constant for the `'static` lifetime. Use
18/// [`StaticStr`] for a static string selected at runtime.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub struct PromotedStr(pub &'static &'static str);
21
22impl Deref for PromotedStr {
23 type Target = &'static str;
24
25 #[inline]
26 fn deref(&self) -> &Self::Target {
27 self.0
28 }
29}
30
31/// A static string a view records without copying it.
32///
33/// Wrap a `&'static str` in this type to avoid the copy made when a view
34/// captures an ordinary `&str`:
35///
36/// ```rust
37/// # use topcoat::view::{StaticStr, View, component, view};
38/// # #[component]
39/// # async fn example() -> topcoat::Result<impl View> {
40/// # let name: &'static str = "hello";
41/// Ok(view! {
42/// <div>(StaticStr(name))</div>
43/// })
44/// # }
45/// ```
46///
47/// A string written as a literal can be further optimized by using [`PromotedStr`].
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub struct StaticStr(pub &'static str);
50
51impl Deref for StaticStr {
52 type Target = &'static str;
53
54 #[inline]
55 fn deref(&self) -> &Self::Target {
56 &self.0
57 }
58}
59
60/// A wrapper that marks its contents as already-safe HTML.
61///
62/// Use this only for trusted markup such as pre-rendered or sanitized HTML.
63/// Passing untrusted input through this type defeats the runtime's escaping.
64#[derive(Debug, Clone, PartialEq)]
65pub struct Unescaped<T>(pub(crate) T);
66
67impl<T> Unescaped<T> {
68 /// Wraps `inner` as already-escaped content.
69 ///
70 /// # Safety (logical)
71 ///
72 /// The caller must ensure `inner` does not contain untrusted HTML.
73 /// Misuse can lead to XSS vulnerabilities.
74 #[inline]
75 pub const fn new_unchecked(inner: T) -> Self {
76 Self(inner)
77 }
78}
79
80impl<T> Deref for Unescaped<T> {
81 type Target = T;
82
83 fn deref(&self) -> &Self::Target {
84 &self.0
85 }
86}