oxiplate_traits/
cow_str.rs

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