markup 0.16.0

A blazing fast, type-safe template engine for Rust.
Documentation
use std::fmt::Write;

pub use markup_proc_macro::{define, new};

mod escape;

pub trait Render {
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result;
}

pub trait RenderAttributeValue: Render {
    #[inline]
    fn is_none(&self) -> bool {
        false
    }

    #[inline]
    fn is_true(&self) -> bool {
        false
    }

    #[inline]
    fn is_false(&self) -> bool {
        false
    }
}

impl<'a, T: Render + ?Sized> Render for &'a T {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        T::render(self, writer)
    }
}

impl<'a, T: RenderAttributeValue + ?Sized> RenderAttributeValue for &'a T {
    #[inline]
    fn is_none(&self) -> bool {
        T::is_none(self)
    }

    #[inline]
    fn is_true(&self) -> bool {
        T::is_true(self)
    }

    #[inline]
    fn is_false(&self) -> bool {
        T::is_false(self)
    }
}

impl<T: Render + ?Sized> Render for Box<T> {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        T::render(self, writer)
    }
}

impl<T: RenderAttributeValue + ?Sized> RenderAttributeValue for Box<T> {
    #[inline]
    fn is_none(&self) -> bool {
        T::is_none(self)
    }

    #[inline]
    fn is_true(&self) -> bool {
        T::is_true(self)
    }

    #[inline]
    fn is_false(&self) -> bool {
        T::is_false(self)
    }
}

impl<'a, T: Render + ToOwned + ?Sized> Render for std::borrow::Cow<'a, T> {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        T::render(self, writer)
    }
}

impl<'a, T: RenderAttributeValue + ToOwned + ?Sized> RenderAttributeValue
    for std::borrow::Cow<'a, T>
{
    #[inline]
    fn is_none(&self) -> bool {
        T::is_none(self)
    }

    #[inline]
    fn is_true(&self) -> bool {
        T::is_true(self)
    }

    #[inline]
    fn is_false(&self) -> bool {
        T::is_false(self)
    }
}

impl Render for bool {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        write!(writer, "{}", self)
    }
}

impl RenderAttributeValue for bool {
    #[inline]
    fn is_true(&self) -> bool {
        *self
    }

    #[inline]
    fn is_false(&self) -> bool {
        !self
    }
}

impl<T: Render> Render for Option<T> {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        match self {
            Some(t) => t.render(writer),
            None => Ok(()),
        }
    }
}

impl<T: RenderAttributeValue> RenderAttributeValue for Option<T> {
    #[inline]
    fn is_none(&self) -> bool {
        self.is_none()
    }
}

struct Raw<T: std::fmt::Display>(T);

impl<T: std::fmt::Display> Render for Raw<T> {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        write!(writer, "{}", self.0)
    }
}

impl<T: std::fmt::Display> RenderAttributeValue for Raw<T> {}

#[inline]
pub fn raw(value: impl std::fmt::Display) -> impl Render + RenderAttributeValue {
    Raw(value)
}

macro_rules! tfor {
    (for $ty:ident in [$($typ:ident),*] $tt:tt) => {
        $( const _: () = { type $ty = $typ; tfor! { @extract $tt } }; )*
    };
    (@extract { $($tt:tt)* }) => { $($tt)* };
}

tfor! {
    for Ty in [char, f32, f64] {
        impl Render for Ty {
            #[inline]
            fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
                write!(writer, "{}", self)
            }
        }

        impl RenderAttributeValue for Ty {
        }
    }
}

tfor! {
    for Ty in [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize] {
        impl Render for Ty {
            #[inline]
            fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
                #[cfg(feature = "itoa")] {
                    let mut buffer = itoa::Buffer::new();
                    let str = buffer.format(*self);
                    writer.write_str(str)
                }
                #[cfg(not(feature = "itoa"))] {
                    write!(writer, "{}", self)
                }
            }
        }

        impl RenderAttributeValue for Ty {
        }
    }
}

impl Render for str {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        escape::escape(self, writer)
    }
}

impl RenderAttributeValue for str {}

impl Render for String {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        self.as_str().render(writer)
    }
}

impl RenderAttributeValue for String {}

impl Render for std::fmt::Arguments<'_> {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        escape::Escape(writer).write_fmt(*self)
    }
}

impl RenderAttributeValue for std::fmt::Arguments<'_> {}

macro_rules! tuple_impl {
    ($($ident:ident)+) => {
        impl<$($ident: Render,)+> Render for ($($ident,)+) {
            #[allow(non_snake_case)]
            #[inline]
            fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
                let ($(ref $ident,)+) = *self;
                $($ident.render(writer)?;)+
                Ok(())
            }
        }

        impl<$($ident: RenderAttributeValue,)+> RenderAttributeValue for ($($ident,)+) {
        }
    }
}

tuple_impl! { A }
tuple_impl! { A B }
tuple_impl! { A B C }
tuple_impl! { A B C D }
tuple_impl! { A B C D E }
tuple_impl! { A B C D E F }
tuple_impl! { A B C D E F G }
tuple_impl! { A B C D E F G H }
tuple_impl! { A B C D E F G H I }
tuple_impl! { A B C D E F G H I J }

pub struct DynRender<'a> {
    f: Box<dyn Fn(&mut dyn std::fmt::Write) -> std::fmt::Result + 'a>,
}

pub fn new<'a, F>(f: F) -> DynRender<'a>
where
    F: Fn(&mut dyn std::fmt::Write) -> std::fmt::Result + 'a,
{
    DynRender { f: Box::new(f) }
}

impl<'a> Render for DynRender<'a> {
    #[inline]
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        (self.f)(writer)
    }
}

impl<'a> std::fmt::Display for DynRender<'a> {
    #[inline]
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        Render::render(self, fmt)
    }
}

#[inline]
pub fn doctype() -> impl Render {
    raw("<!DOCTYPE html>")
}