Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render with Display using autoref specialization #359

Merged
merged 1 commit into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions maud/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
47 changes: 47 additions & 0 deletions maud/tests/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<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;"
);
}
2 changes: 1 addition & 1 deletion maud_macros/src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Attr>, body: ElementBody, build: &mut Builder) {
Expand Down