Skip to main content

oxiplate_traits/
cow_str.rs

1extern crate alloc;
2
3use alloc::borrow::Cow;
4use alloc::fmt;
5use alloc::string::ToString;
6
7use crate::{Escaper, FastEscape};
8
9/// A trait intended to be used by filters for any string-like arguments
10/// to deal with them more efficiently than `str` or `impl Display`.
11/// Any expression or filter prefixed by `>` in a template
12/// will convert the value to `CowStrWrapper`
13/// which implements this trait.
14///
15/// # Example template
16///
17/// ```oxip
18/// {{ >message | append(>"!") }}
19/// ```
20#[diagnostic::on_unimplemented(
21    message = "{Self} is expected to be prefixed by `>` and implement \
22               `::oxiplate_traits::ToCowString`.",
23    label = "Insert `>` prefix before this expression if `{Self}` is a string-like value (`str`, \
24             `impl Display`, etc).",
25    note = "Consider implementing `::oxiplate_traits::ToCowStr` if `{Self}` is owned by your \
26            crate, and then prefix with `>`.",
27    note = "If `{Self}` is not a string-like value, perhaps you meant to pass a different \
28            argument to the filter?",
29    note = "Oxiplate efficiently builds `::std::borrow::Cow<'a, str>` when a `>` prefix is used \
30            on an expression, and wraps it in `::oxiplate_traits::CowStr<'a>` which is used by \
31            filters for string arguments. Because the syntax for doing so is not obvious and \
32            prone to silently breaking, this alternative syntax is used to let Oxiplate handle \
33            the details while ensuring it's tested properly."
34)]
35pub trait CowStr<'a> {
36    /// Extract the contained `Cow<str>`.
37    /// Intended to be called from filters
38    /// to deal with strings more efficiently.
39    #[must_use]
40    fn cow_str(self) -> Cow<'a, str>;
41}
42
43/// Struct used in generated templates
44/// that is intended to be passed to filters asking for `CowStr`
45/// that will immediately extract the contained `Cow<str>`.
46pub struct CowStrWrapper<'a>(Cow<'a, str>);
47
48impl<'a> CowStrWrapper<'a> {
49    /// Create a new `CowStrWrapper`.
50    #[must_use]
51    #[inline]
52    pub fn new(cow_str: Cow<'a, str>) -> Self {
53        Self(cow_str)
54    }
55}
56
57impl<'a> CowStr<'a> for CowStrWrapper<'a> {
58    #[inline]
59    fn cow_str(self) -> Cow<'a, str> {
60        self.0
61    }
62}
63
64impl<'a, W: fmt::Write + ?Sized> FastEscape<'a, W> for CowStrWrapper<'a> {
65    #[inline]
66    fn oxiplate_fast_escape(&'a self, f: &mut W, escaper: &impl Escaper) -> fmt::Result {
67        #[cfg(feature = "debug-fast-escape-type-priority")]
68        f.write_str("CowStrWrapper(")?;
69
70        escaper.escape(f, self.0.as_ref())?;
71
72        #[cfg(feature = "debug-fast-escape-type-priority")]
73        f.write_str(")")?;
74
75        Ok(())
76    }
77
78    #[inline]
79    fn oxiplate_fast_raw(&'a self, f: &mut W) -> fmt::Result {
80        #[cfg(feature = "debug-fast-escape-type-priority")]
81        f.write_str("CowStrWrapper(")?;
82
83        f.write_str(self.0.as_ref())?;
84
85        #[cfg(feature = "debug-fast-escape-type-priority")]
86        f.write_str(")")?;
87
88        Ok(())
89    }
90}
91
92/// Wrapper around text
93/// that will implement `CowStr`
94/// via `FastCowStr` when possible,
95/// otherwise via `::std::fmt::Display`.
96/// Must borrow twice before calling `to_cow_str()`.
97///
98/// ```rust
99/// # use oxiplate_traits as oxiplate;
100/// use oxiplate::ToCowStr;
101/// let text = "hello world";
102/// assert_eq!(
103///     "hello world",
104///     (&&oxiplate::ToCowStrWrapper::new(&text)).to_cow_str()
105/// );
106/// ```
107pub struct ToCowStrWrapper<'a, T>(&'a T);
108
109impl<'a, T> ToCowStrWrapper<'a, T> {
110    /// Wrap text.
111    #[inline]
112    pub fn new(value: &'a T) -> Self {
113        Self(value)
114    }
115}
116
117/// Trait with a specialized implementation
118/// for items that implement `FastCowStr`,
119/// a trait that allows for more efficient conversions to `Cow<'a, str>`.
120pub trait ToCowStr<'a> {
121    /// Helper function to use the most efficient conversion to `&str`.
122    /// Called from generated templates whenever a cow prefix is used.
123    fn to_cow_str(&'a self) -> Cow<'a, str>;
124}
125
126impl<'a, T: FastCowStr<'a>> ToCowStr<'a> for &ToCowStrWrapper<'a, T> {
127    #[inline]
128    fn to_cow_str(&'a self) -> Cow<'a, str> {
129        self.0.oxiplate_cow_str()
130    }
131}
132
133impl<'a, T: ToString> ToCowStr<'a> for &&ToCowStrWrapper<'a, T> {
134    #[inline]
135    fn to_cow_str(&'a self) -> Cow<'a, str> {
136        Cow::Owned(self.0.to_string())
137    }
138}
139
140/// Trait that allows for more efficient conversions to `&str`.
141pub trait FastCowStr<'a> {
142    /// Helper function to use the most efficient conversion to `&str`.
143    /// Called from generated templates whenever a cow prefix is used.
144    fn oxiplate_cow_str(&'a self) -> Cow<'a, str>;
145}
146
147impl<'a> FastCowStr<'a> for &'a str {
148    #[inline]
149    fn oxiplate_cow_str(&'a self) -> Cow<'a, str> {
150        Cow::Borrowed(self)
151    }
152}