1use crate::escape::escape;
2use std::{
3 borrow::Cow,
4 fmt::{self, Arguments},
5};
6
7#[diagnostic::on_unimplemented(
37 message = "the type `{Self}` cannot be safely rendered as HTML",
38 note = "to safely render `{Self}` as HTML, implement the `Render` trait or wrap it in `Escaped<{Self}>`"
39)]
40pub trait Render {
41 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result;
51
52 fn render(&self) -> Raw<String> {
60 use std::fmt::Write;
61 let mut buf = String::new();
62 write!(&mut buf, "{}", render_fn(|f| self.render_to(f))).expect("render_to returned Err");
63 Raw(buf)
64 }
65}
66
67impl Render for str {
68 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 escape(self, f)
70 }
71}
72
73impl Render for String {
74 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 self.as_str().render_to(f)
76 }
77}
78
79impl Render for Arguments<'_> {
80 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 match self.as_str() {
82 Some(s) => s.render_to(f),
83 None => self.to_string().render_to(f),
84 }
85 }
86}
87
88impl<B> Render for Cow<'_, B>
89where
90 B: Render + ToOwned + ?Sized,
91{
92 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 self.as_ref().render_to(f)
94 }
95}
96
97macro_rules! ref_render_impl {
98 ( $( $t:ty )* ) => {
99 $(
100 impl<T> Render for $t
101 where
102 T: Render + ?Sized,
103 {
104 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
105 T::render_to(self, f)
106 }
107 }
108 )*
109 };
110}
111
112ref_render_impl! {
113 &T
114 &mut T
115 Box<T>
116 std::rc::Rc<T>
117 std::sync::Arc<T>
118}
119
120macro_rules! trusted_render_impl {
121 ( $( $t:ty )* ) => {
122 $(
123 impl Render for $t {
124 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
125 write!(f, "{self}")
126 }
127 }
128 )*
129 };
130}
131
132trusted_render_impl! {
133 f32 f64
134 i8 i16 i32 i64 i128 isize
135 u8 u16 u32 u64 u128 usize
136}
137
138#[derive(Debug, Clone, Copy)]
157pub struct Raw<T>(pub T);
158
159impl<T: fmt::Display> fmt::Display for Raw<T> {
160 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161 self.0.fmt(f)
162 }
163}
164
165impl<T: fmt::Display> Render for Raw<T> {
166 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
167 self.0.fmt(f)
168 }
169}
170
171pub struct RenderFn<F> {
175 f: F,
176}
177
178impl<F> fmt::Debug for RenderFn<F> {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 f.debug_struct("RenderFn").finish()
181 }
182}
183
184impl<F> Render for RenderFn<F>
185where
186 F: Fn(&mut fmt::Formatter) -> fmt::Result,
187{
188 fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
189 (self.f)(f)
190 }
191}
192
193impl<F> fmt::Display for RenderFn<F>
194where
195 F: Fn(&mut fmt::Formatter) -> fmt::Result,
196{
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198 (self.f)(f)
199 }
200}
201
202pub fn render_fn<F>(f: F) -> RenderFn<F>
204where
205 F: Fn(&mut fmt::Formatter) -> fmt::Result,
206{
207 RenderFn { f }
208}