use crate::escape::escape;
use std::{
borrow::Cow,
fmt::{self, Arguments},
};
#[diagnostic::on_unimplemented(
message = "the type `{Self}` cannot be safely rendered as HTML",
note = "to safely render `{Self}` as HTML, implement the `Render` trait or wrap it in `Escaped<{Self}>`"
)]
pub trait Render {
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result;
fn render(&self) -> Raw<String> {
use std::fmt::Write;
let mut buf = String::new();
write!(&mut buf, "{}", render_fn(|f| self.render_to(f))).expect("render_to returned Err");
Raw(buf)
}
}
impl Render for str {
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
escape(self, f)
}
}
impl Render for String {
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_str().render_to(f)
}
}
impl Render for Arguments<'_> {
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.as_str() {
Some(s) => s.render_to(f),
None => self.to_string().render_to(f),
}
}
}
impl<B> Render for Cow<'_, B>
where
B: Render + ToOwned + ?Sized,
{
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_ref().render_to(f)
}
}
macro_rules! ref_render_impl {
( $( $t:ty )* ) => {
$(
impl<T> Render for $t
where
T: Render + ?Sized,
{
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
T::render_to(self, f)
}
}
)*
};
}
ref_render_impl! {
&T
&mut T
Box<T>
std::rc::Rc<T>
std::sync::Arc<T>
}
macro_rules! trusted_render_impl {
( $( $t:ty )* ) => {
$(
impl Render for $t {
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}")
}
}
)*
};
}
trusted_render_impl! {
f32 f64
i8 i16 i32 i64 i128 isize
u8 u16 u32 u64 u128 usize
}
#[derive(Debug, Clone, Copy)]
pub struct Raw<T>(pub T);
impl<T: fmt::Display> fmt::Display for Raw<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: fmt::Display> Render for Raw<T> {
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
pub struct RenderFn<F> {
f: F,
}
impl<F> fmt::Debug for RenderFn<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RenderFn").finish()
}
}
impl<F> Render for RenderFn<F>
where
F: Fn(&mut fmt::Formatter) -> fmt::Result,
{
fn render_to(&self, f: &mut fmt::Formatter) -> fmt::Result {
(self.f)(f)
}
}
impl<F> fmt::Display for RenderFn<F>
where
F: Fn(&mut fmt::Formatter) -> fmt::Result,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(self.f)(f)
}
}
pub fn render_fn<F>(f: F) -> RenderFn<F>
where
F: Fn(&mut fmt::Formatter) -> fmt::Result,
{
RenderFn { f }
}