mrml 6.0.1

Rust implementation of MJML renderer
Documentation
use std::fmt::Write;

use super::{Classes, Styles};

#[derive(Debug, Default)]
pub(crate) struct RenderBuffer {
    inner: String,
}

impl std::fmt::Write for RenderBuffer {
    #[inline]
    fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::fmt::Result {
        self.inner.write_fmt(args)
    }

    #[inline]
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        self.inner.write_str(s)
    }

    #[inline]
    fn write_char(&mut self, c: char) -> std::fmt::Result {
        self.inner.write_char(c)
    }
}

pub(crate) struct RenderAttribute<N, V>(N, V);

impl<'a> std::fmt::Display for RenderAttribute<&'a str, &'a str> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}={:?}", self.0, self.1)
    }
}

impl<'a> std::fmt::Display for RenderAttribute<&'a str, &'a Classes<'a>> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}={:?}", self.0, self.1)
    }
}

impl<'a> std::fmt::Display for RenderAttribute<&'a str, &'a Styles<'a>> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}={:?}", self.0, self.1)
    }
}

impl<'a> std::fmt::Display for RenderAttribute<&'a str, Option<&'a str>> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.1 {
            Some(ref value) => write!(f, "{}={value:?}", self.0),
            None => write!(f, "{}", self.0),
        }
    }
}

impl RenderBuffer {
    #[inline]
    pub fn push_str(&mut self, value: &str) {
        self.inner.push_str(value);
    }

    #[inline]
    pub fn push(&mut self, value: char) {
        self.inner.push(value);
    }

    #[inline]
    pub fn push_attribute<N, V>(&mut self, key: N, value: V) -> std::fmt::Result
    where
        RenderAttribute<N, V>: std::fmt::Display,
    {
        write!(&mut self.inner, " {}", RenderAttribute(key, value))
    }

    #[inline]
    pub fn open_tag(&mut self, tag: &str) {
        self.inner.push('<');
        self.inner.push_str(tag);
    }

    #[inline]
    pub fn closed_tag(&mut self) {
        self.inner.push_str(" />");
    }

    #[inline]
    pub fn close_tag(&mut self) {
        self.inner.push('>');
    }

    #[inline]
    pub fn end_tag(&mut self, tag: &str) {
        self.inner.push_str("</");
        self.inner.push_str(tag);
        self.inner.push('>');
    }
}

const START_CONDITIONAL_TAG: &str = "<!--[if mso | IE]>";
const START_MSO_CONDITIONAL_TAG: &str = "<!--[if mso]>";
const END_CONDITIONAL_TAG: &str = "<![endif]-->";
const START_NEGATION_CONDITIONAL_TAG: &str = "<!--[if !mso | IE]><!-->";
const START_MSO_NEGATION_CONDITIONAL_TAG: &str = "<!--[if !mso]><!-->";
const END_NEGATION_CONDITIONAL_TAG: &str = "<!--<![endif]-->";

impl RenderBuffer {
    #[inline]
    pub fn start_conditional_tag(&mut self) {
        self.inner.push_str(START_CONDITIONAL_TAG);
    }

    #[inline]
    pub fn start_negation_conditional_tag(&mut self) {
        self.inner.push_str(START_NEGATION_CONDITIONAL_TAG);
    }

    #[inline]
    pub fn start_mso_conditional_tag(&mut self) {
        self.inner.push_str(START_MSO_CONDITIONAL_TAG);
    }

    #[inline]
    pub fn start_mso_negation_conditional_tag(&mut self) {
        self.inner.push_str(START_MSO_NEGATION_CONDITIONAL_TAG);
    }

    #[inline]
    pub fn end_conditional_tag(&mut self) {
        self.inner.push_str(END_CONDITIONAL_TAG);
    }

    #[inline]
    pub fn end_negation_conditional_tag(&mut self) {
        self.inner.push_str(END_NEGATION_CONDITIONAL_TAG);
    }
}

impl AsRef<str> for RenderBuffer {
    fn as_ref(&self) -> &str {
        self.inner.as_str()
    }
}

impl From<RenderBuffer> for String {
    fn from(value: RenderBuffer) -> Self {
        value.inner
    }
}