parent
f6bbece1c6
commit
2f3d68c8a4
7 changed files with 80 additions and 60 deletions
|
@ -2,6 +2,9 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
- Remove blanket `Render` impl for `T: Display`
|
||||
[#320](https://github.com/lambda-fairy/maud/pull/320)
|
||||
|
||||
## [0.23.0] - 2021-11-10
|
||||
|
||||
- Update to support axum 0.2
|
||||
|
|
1
docs/Cargo.lock
generated
1
docs/Cargo.lock
generated
|
@ -326,6 +326,7 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
|||
name = "maud"
|
||||
version = "0.23.0"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"maud_macros",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
# The `Render` trait
|
||||
|
||||
By default,
|
||||
a [`(splice)`](splices-toggles.md) is rendered using the [`std::fmt::Display`][Display] trait,
|
||||
with any HTML special characters escaped automatically.
|
||||
Maud uses the [`Render`][Render] trait to convert [`(spliced)`](splices-toggles.md) values to HTML.
|
||||
This is implemented for many Rust primitive types (`&str`, `i32`) by default, but you can implement it for your own types as well.
|
||||
|
||||
To change this behavior,
|
||||
implement the [`Render`][Render] trait for your type.
|
||||
Then, when a value of this type is used in a template,
|
||||
Maud will call your custom code instead.
|
||||
|
||||
Below are some examples of using `Render`.
|
||||
Below are some examples of implementing `Render`.
|
||||
Feel free to use these snippets in your own project!
|
||||
|
||||
## Example: a shorthand for including CSS stylesheets
|
||||
|
|
|
@ -94,11 +94,11 @@ html! {
|
|||
|
||||
### What can be spliced?
|
||||
|
||||
You can splice any value that implements [`std::fmt::Display`][Display].
|
||||
You can splice any value that implements [`Render`][Render].
|
||||
Most primitive types (such as `str` and `i32`) implement this trait,
|
||||
so they should work out of the box.
|
||||
|
||||
To change this behavior for some type,
|
||||
To get this behavior for a custom type,
|
||||
you can implement the [`Render`][Render] trait by hand.
|
||||
The [`PreEscaped`][PreEscaped] wrapper type,
|
||||
which outputs its argument without escaping,
|
||||
|
@ -116,7 +116,6 @@ html! {
|
|||
# ;
|
||||
```
|
||||
|
||||
[Display]: http://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||
[Render]: https://docs.rs/maud/*/maud/trait.Render.html
|
||||
[PreEscaped]: https://docs.rs/maud/*/maud/struct.PreEscaped.html
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ actix-web = ["actix-web-dep", "futures-util"]
|
|||
|
||||
[dependencies]
|
||||
maud_macros = { version = "0.23.0", path = "../maud_macros" }
|
||||
itoa = { version = "0.4.8", default-features = false, features = ["i128"] }
|
||||
rocket = { version = ">= 0.3, < 0.5", optional = true }
|
||||
futures-util = { version = "0.3.0", optional = true, default-features = false }
|
||||
actix-web-dep = { package = "actix-web", version = ">= 2, < 4", optional = true, default-features = false }
|
||||
|
|
112
maud/src/lib.rs
112
maud/src/lib.rs
|
@ -11,8 +11,8 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::string::String;
|
||||
use core::fmt::{self, Write};
|
||||
use alloc::{borrow::Cow, boxed::Box, string::String};
|
||||
use core::fmt::{self, Arguments, Write};
|
||||
|
||||
pub use maud_macros::{html, html_debug};
|
||||
|
||||
|
@ -59,16 +59,9 @@ impl<'a> fmt::Write for Escaper<'a> {
|
|||
|
||||
/// Represents a type that can be rendered as HTML.
|
||||
///
|
||||
/// If your type implements [`Display`][1], then it will implement this
|
||||
/// trait automatically through a blanket impl.
|
||||
///
|
||||
/// [1]: https://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||
///
|
||||
/// On the other hand, if your type has a custom HTML representation,
|
||||
/// then you can implement `Render` by hand. To do this, override
|
||||
/// either the `.render()` or `.render_to()` methods; since each is
|
||||
/// defined in terms of the other, you only need to implement one of
|
||||
/// them. See the example below.
|
||||
/// To implement this for your own type, override either the `.render()`
|
||||
/// or `.render_to()` methods; since each is defined in terms of the
|
||||
/// other, you only need to implement one of them. See the example below.
|
||||
///
|
||||
/// # Minimal implementation
|
||||
///
|
||||
|
@ -115,48 +108,81 @@ pub trait Render {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + ?Sized> Render for T {
|
||||
impl Render for str {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
let _ = write!(Escaper::new(w), "{}", self);
|
||||
escape::escape_to_string(self, w);
|
||||
}
|
||||
}
|
||||
|
||||
/// Spicy hack to specialize `Render` for `T: AsRef<str>`.
|
||||
///
|
||||
/// The `std::fmt` machinery is rather heavyweight, both in code size and speed.
|
||||
/// It would be nice to skip this overhead for the common cases of `&str` and
|
||||
/// `String`. But the obvious solution uses *specialization*, which (as of this
|
||||
/// writing) requires Nightly. The [*inherent method specialization*][1] trick
|
||||
/// is less clear but works on Stable.
|
||||
///
|
||||
/// This module is an implementation detail and should not be used directly.
|
||||
///
|
||||
/// [1]: https://github.com/dtolnay/case-studies/issues/14
|
||||
#[doc(hidden)]
|
||||
pub mod render {
|
||||
use crate::{Escaper, Render};
|
||||
use alloc::string::String;
|
||||
use core::fmt::Write;
|
||||
|
||||
pub trait RenderInternal {
|
||||
fn __maud_render_to(&self, w: &mut String);
|
||||
impl Render for String {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
str::render_to(self, w);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RenderWrapper<'a, T: ?Sized>(pub &'a T);
|
||||
|
||||
impl<'a, T: AsRef<str> + ?Sized> RenderWrapper<'a, T> {
|
||||
pub fn __maud_render_to(&self, w: &mut String) {
|
||||
let _ = Escaper::new(w).write_str(self.0.as_ref());
|
||||
}
|
||||
impl<'a> Render for Cow<'a, str> {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
str::render_to(self, w);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Render + ?Sized> RenderInternal for RenderWrapper<'a, T> {
|
||||
fn __maud_render_to(&self, w: &mut String) {
|
||||
self.0.render_to(w);
|
||||
}
|
||||
impl<'a> Render for Arguments<'a> {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
let _ = Escaper::new(w).write_fmt(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Render + ?Sized> Render for &'a T {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
T::render_to(self, w);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Render + ?Sized> Render for &'a mut T {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
T::render_to(self, w);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render + ?Sized> Render for Box<T> {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
T::render_to(self, w);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_render_with_display {
|
||||
($($ty:ty)*) => {
|
||||
$(
|
||||
impl Render for $ty {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
format_args!("{self}").render_to(w);
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_render_with_display! {
|
||||
char f32 f64
|
||||
}
|
||||
|
||||
macro_rules! impl_render_with_itoa {
|
||||
($($ty:ty)*) => {
|
||||
$(
|
||||
impl Render for $ty {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
let _ = itoa::fmt(w, *self);
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_render_with_itoa! {
|
||||
i8 i16 i32 i64 i128 isize
|
||||
u8 u16 u32 u64 u128 usize
|
||||
}
|
||||
|
||||
/// A wrapper that renders the inner value without escaping.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PreEscaped<T: AsRef<str>>(pub T);
|
||||
|
|
|
@ -103,11 +103,7 @@ impl Generator {
|
|||
|
||||
fn splice(&self, expr: TokenStream, build: &mut Builder) {
|
||||
let output_ident = self.output_ident.clone();
|
||||
let tokens = quote!({
|
||||
use maud::render::{RenderInternal, RenderWrapper};
|
||||
RenderWrapper(&#expr).__maud_render_to(&mut #output_ident);
|
||||
});
|
||||
build.push_tokens(tokens);
|
||||
build.push_tokens(quote!(maud::Render::render_to(&#expr, &mut #output_ident);));
|
||||
}
|
||||
|
||||
fn element(&self, name: TokenStream, attrs: Vec<Attr>, body: ElementBody, build: &mut Builder) {
|
||||
|
|
Loading…
Add table
Reference in a new issue