Avoid unstable specialization with this one weird trick! (#223)
Rustaceans hate him!
This commit is contained in:
parent
165cfd5fe0
commit
49f3c46bd2
7 changed files with 32 additions and 41 deletions
|
@ -18,11 +18,6 @@ jobs:
|
|||
done
|
||||
(exit $CLIPPY_STATUS)
|
||||
fi
|
||||
- name: "Stable"
|
||||
rust: stable
|
||||
script:
|
||||
# Skip `--all-features` because stable Rocket isn't released yet
|
||||
- cargo test --workspace
|
||||
- name: "Benchmarks"
|
||||
script:
|
||||
- (cd benchmarks && cargo test --benches --locked)
|
||||
|
|
1
benchmarks/Cargo.lock
generated
1
benchmarks/Cargo.lock
generated
|
@ -413,7 +413,6 @@ version = "0.22.0"
|
|||
dependencies = [
|
||||
"maud_htmlescape",
|
||||
"maud_macros",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
1
docs/Cargo.lock
generated
1
docs/Cargo.lock
generated
|
@ -295,7 +295,6 @@ version = "0.22.0"
|
|||
dependencies = [
|
||||
"maud_htmlescape",
|
||||
"maud_macros",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -25,9 +25,6 @@ rocket = { version = ">= 0.3, < 0.5", optional = true }
|
|||
futures = { version = "0.3.0", optional = true }
|
||||
actix-web-dep = { version = "2.0.0", optional = true, default-features = false, package = "actix-web" }
|
||||
|
||||
[build-dependencies]
|
||||
version_check = "0.9.2"
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = { version = "1.0.33", features = ["diff"] }
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
fn main() {
|
||||
if version_check::is_feature_flaggable() == Some(true) {
|
||||
println!("cargo:rustc-cfg=unstable");
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
#![cfg_attr(unstable, feature(min_specialization))]
|
||||
|
||||
//! A macro for writing HTML templates.
|
||||
//!
|
||||
//! This documentation only describes the runtime API. For a general
|
||||
|
@ -78,32 +76,46 @@ pub trait Render {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unstable))]
|
||||
impl<T: fmt::Display + ?Sized> Render for T {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
let _ = write!(Escaper::new(w), "{}", self);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unstable)]
|
||||
impl<T: fmt::Display + ?Sized> Render for T {
|
||||
default fn render_to(&self, w: &mut String) {
|
||||
let _ = write!(Escaper::new(w), "{}", self);
|
||||
/// 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::Render;
|
||||
use maud_htmlescape::Escaper;
|
||||
use std::fmt::Write;
|
||||
|
||||
pub trait RenderInternal {
|
||||
fn __maud_render_to(&self, w: &mut String);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unstable)]
|
||||
impl Render for String {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
let _ = Escaper::new(w).write_str(self);
|
||||
impl<'a, T: Render + ?Sized> RenderInternal for RenderWrapper<'a, T> {
|
||||
fn __maud_render_to(&self, w: &mut String) {
|
||||
self.0.render_to(w);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unstable)]
|
||||
impl Render for str {
|
||||
fn render_to(&self, w: &mut String) {
|
||||
let _ = Escaper::new(w).write_str(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper that renders the inner value without escaping.
|
||||
|
|
|
@ -91,14 +91,8 @@ impl Generator {
|
|||
fn splice(&self, expr: TokenStream) -> TokenStream {
|
||||
let output_ident = self.output_ident.clone();
|
||||
quote!({
|
||||
// Create a local trait alias so that autoref works
|
||||
trait Render: maud::Render {
|
||||
fn __maud_render_to(&self, output_ident: &mut ::std::string::String) {
|
||||
maud::Render::render_to(self, output_ident);
|
||||
}
|
||||
}
|
||||
impl<T: maud::Render> Render for T {}
|
||||
#expr.__maud_render_to(&mut #output_ident);
|
||||
use maud::render::{RenderInternal, RenderWrapper};
|
||||
RenderWrapper(&#expr).__maud_render_to(&mut #output_ident);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue