Skip to main content

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}