allowthem-server 0.0.9

HTTP server and middleware for allowthem
Documentation
use std::fmt;

use wavefunk_ui::Template;
pub use wavefunk_ui::components::{HtmlAttr, TrustedHtml};

pub type UiRenderResult<T> = Result<T, wavefunk_ui::askama::Error>;

#[derive(Debug, Default, Eq, PartialEq)]
pub struct RenderedUi {
    html: String,
}

impl RenderedUi {
    pub fn new(html: String) -> Self {
        Self { html }
    }

    pub fn as_str(&self) -> &str {
        &self.html
    }

    pub fn as_trusted_html(&self) -> TrustedHtml<'_> {
        TrustedHtml::new(&self.html)
    }

    pub fn into_string(self) -> String {
        self.html
    }
}

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

impl fmt::Display for RenderedUi {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.html)
    }
}

impl wavefunk_ui::askama::FastWritable for RenderedUi {
    #[inline]
    fn write_into(
        &self,
        dest: &mut dyn fmt::Write,
        _: &dyn wavefunk_ui::askama::Values,
    ) -> wavefunk_ui::askama::Result<()> {
        Ok(dest.write_str(&self.html)?)
    }
}

impl wavefunk_ui::askama::filters::HtmlSafe for RenderedUi {}

pub fn render_component<T>(component: &T) -> UiRenderResult<RenderedUi>
where
    T: Template + ?Sized,
{
    component.render().map(RenderedUi::new)
}

pub const fn trusted_html<'a>(html: &'a str) -> TrustedHtml<'a> {
    TrustedHtml::new(html)
}

#[cfg(test)]
mod tests {
    use super::*;
    use wavefunk_ui::components::{FormPanel, SplitShell};

    #[test]
    fn rendered_html_can_be_reused_as_trusted_component_slot() {
        let body = trusted_html(r#"<form action="/login"></form>"#);
        let panel = FormPanel::new("Sign in", body);
        let panel_html = render_component(&panel).expect("form panel should render");

        let shell = SplitShell::new(panel_html.as_trusted_html()).with_mode("dark");
        let shell_html = render_component(&shell).expect("split shell should render");

        assert!(shell_html.as_str().contains("wf-split-shell"));
        assert!(shell_html.as_str().contains("data-mode=\"dark\""));
        assert!(shell_html.as_str().contains("wf-form-panel"));
        assert!(
            shell_html
                .as_str()
                .contains(r#"<form action="/login"></form>"#)
        );
    }

    #[test]
    fn rendered_ui_wraps_owned_html_without_losing_access_to_string() {
        let rendered = RenderedUi::new("<section>owned</section>".to_owned());

        assert_eq!(rendered.as_str(), "<section>owned</section>");
        assert_eq!(
            rendered.as_trusted_html().as_str(),
            "<section>owned</section>"
        );
        assert_eq!(
            rendered.into_string(),
            "<section>owned</section>".to_owned()
        );
    }

    fn assert_html_safe<T: wavefunk_ui::askama::filters::HtmlSafe>() {}

    #[test]
    fn rendered_ui_can_be_embedded_as_owned_safe_html() {
        assert_html_safe::<RenderedUi>();
    }
}