From 2193c2b0af7ba9a8b2ee68e72cd4c2ad51ee2782 Mon Sep 17 00:00:00 2001 From: Simon Ask Ulsnes Date: Sat, 19 Nov 2022 14:42:14 +0100 Subject: [PATCH] Render with Display using autoref specialization --- maud/src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++ maud/tests/misc.rs | 47 ++++++++++++++++++++++++++++++++++ maud_macros/src/generate.rs | 2 +- 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/maud/src/lib.rs b/maud/src/lib.rs index a23c2b17..9c4a1809 100644 --- a/maud/src/lib.rs +++ b/maud/src/lib.rs @@ -340,3 +340,54 @@ mod axum_support { } } } + +#[doc(hidden)] +pub mod macro_private { + use crate::{display, Render}; + use alloc::string::String; + use core::fmt::Display; + + #[doc(hidden)] + #[macro_export] + macro_rules! render_to { + ($x:expr, $buffer:expr) => {{ + use $crate::macro_private::*; + match ChooseRenderOrDisplay($x) { + x => (&&x).implements_render_or_display().render_to(x.0, $buffer), + } + }}; + } + + pub use render_to; + + pub struct ChooseRenderOrDisplay(pub T); + + pub struct ViaRenderTag; + pub struct ViaDisplayTag; + + pub trait ViaRender { + fn implements_render_or_display(&self) -> ViaRenderTag { + ViaRenderTag + } + } + pub trait ViaDisplay { + fn implements_render_or_display(&self) -> ViaDisplayTag { + ViaDisplayTag + } + } + + impl ViaRender for &ChooseRenderOrDisplay {} + impl ViaDisplay for ChooseRenderOrDisplay {} + + impl ViaRenderTag { + pub fn render_to(self, value: &T, buffer: &mut String) { + value.render_to(buffer); + } + } + + impl ViaDisplayTag { + pub fn render_to(self, value: &T, buffer: &mut String) { + display(value).render_to(buffer); + } + } +} diff --git a/maud/tests/misc.rs b/maud/tests/misc.rs index 741af42d..b9aae522 100644 --- a/maud/tests/misc.rs +++ b/maud/tests/misc.rs @@ -83,3 +83,50 @@ fn issue_97() { assert_eq!(html! { (Pinkie) }.into_string(), "42"); } + +#[test] +fn only_display() { + use core::fmt::Display; + + struct OnlyDisplay; + impl Display for OnlyDisplay { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } + } + + assert_eq!(html! { (OnlyDisplay) }.into_string(), "<hello>"); + assert_eq!(html! { (&OnlyDisplay) }.into_string(), "<hello>"); + assert_eq!(html! { (&&OnlyDisplay) }.into_string(), "<hello>"); + assert_eq!(html! { (&&&OnlyDisplay) }.into_string(), "<hello>"); + assert_eq!(html! { (&&&&OnlyDisplay) }.into_string(), "<hello>"); +} + +#[test] +fn prefer_render_over_display() { + use core::fmt::Display; + use maud::Render; + + struct RenderAndDisplay; + impl Display for RenderAndDisplay { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } + } + impl Render for RenderAndDisplay { + fn render_to(&self, buffer: &mut String) { + buffer.push_str(""); + } + } + + assert_eq!(html! { (RenderAndDisplay) }.into_string(), ""); + assert_eq!(html! { (&RenderAndDisplay) }.into_string(), ""); + assert_eq!(html! { (&&RenderAndDisplay) }.into_string(), ""); + assert_eq!(html! { (&&&RenderAndDisplay) }.into_string(), ""); + assert_eq!(html! { (&&&&RenderAndDisplay) }.into_string(), ""); + + assert_eq!( + html! { (maud::display(RenderAndDisplay)) }.into_string(), + "<display>" + ); +} diff --git a/maud_macros/src/generate.rs b/maud_macros/src/generate.rs index ad8a0889..c9ba9fb6 100644 --- a/maud_macros/src/generate.rs +++ b/maud_macros/src/generate.rs @@ -103,7 +103,7 @@ impl Generator { fn splice(&self, expr: TokenStream, build: &mut Builder) { let output_ident = self.output_ident.clone(); - build.push_tokens(quote!(maud::Render::render_to(&#expr, &mut #output_ident);)); + build.push_tokens(quote!(maud::macro_private::render_to!(&#expr, &mut #output_ident);)); } fn element(&self, name: TokenStream, attrs: Vec, body: ElementBody, build: &mut Builder) {