Render with Display using autoref specialization ()

This commit is contained in:
Simon Ask 2022-12-15 11:39:54 +01:00 committed by GitHub
parent 81ace856a4
commit 6cdf12b427
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 1 deletions
maud
maud_macros/src

View file

@ -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<T>(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<T: Render> ViaRender for &ChooseRenderOrDisplay<T> {}
impl<T: Display> ViaDisplay for ChooseRenderOrDisplay<T> {}
impl ViaRenderTag {
pub fn render_to<T: Render + ?Sized>(self, value: &T, buffer: &mut String) {
value.render_to(buffer);
}
}
impl ViaDisplayTag {
pub fn render_to<T: Display + ?Sized>(self, value: &T, buffer: &mut String) {
display(value).render_to(buffer);
}
}
}

View file

@ -83,3 +83,50 @@ fn issue_97() {
assert_eq!(html! { (Pinkie) }.into_string(), "42"); 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, "<hello>")
}
}
assert_eq!(html! { (OnlyDisplay) }.into_string(), "&lt;hello&gt;");
assert_eq!(html! { (&OnlyDisplay) }.into_string(), "&lt;hello&gt;");
assert_eq!(html! { (&&OnlyDisplay) }.into_string(), "&lt;hello&gt;");
assert_eq!(html! { (&&&OnlyDisplay) }.into_string(), "&lt;hello&gt;");
assert_eq!(html! { (&&&&OnlyDisplay) }.into_string(), "&lt;hello&gt;");
}
#[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, "<display>")
}
}
impl Render for RenderAndDisplay {
fn render_to(&self, buffer: &mut String) {
buffer.push_str("<render>");
}
}
assert_eq!(html! { (RenderAndDisplay) }.into_string(), "<render>");
assert_eq!(html! { (&RenderAndDisplay) }.into_string(), "<render>");
assert_eq!(html! { (&&RenderAndDisplay) }.into_string(), "<render>");
assert_eq!(html! { (&&&RenderAndDisplay) }.into_string(), "<render>");
assert_eq!(html! { (&&&&RenderAndDisplay) }.into_string(), "<render>");
assert_eq!(
html! { (maud::display(RenderAndDisplay)) }.into_string(),
"&lt;display&gt;"
);
}

View file

@ -103,7 +103,7 @@ impl Generator {
fn splice(&self, expr: TokenStream, build: &mut Builder) { fn splice(&self, expr: TokenStream, build: &mut Builder) {
let output_ident = self.output_ident.clone(); 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<Attr>, body: ElementBody, build: &mut Builder) { fn element(&self, name: TokenStream, attrs: Vec<Attr>, body: ElementBody, build: &mut Builder) {