generator: use re-exported core and std

This way, even a shadowed `core` or `std` does not prevent the generated
code from working correctly.
This commit is contained in:
René Kijewski 2024-10-04 05:08:07 +02:00
parent bba4e26ce6
commit dfcfde9ce9
8 changed files with 103 additions and 71 deletions

View File

@ -1,6 +1,6 @@
[licenses]
version = 2
allow = ["Apache-2.0", "BSD-2-Clause", "MIT", "Unicode-DFS-2016"]
allow = ["Apache-2.0", "MIT", "MIT-0", "Unicode-DFS-2016"]
private = { ignore = true }
[[licenses.clarify]]

View File

@ -67,6 +67,8 @@ mod html;
use std::{fmt, io};
pub use rinja_derive::Template;
#[doc(hidden)]
pub use {core, std};
#[doc(hidden)]
pub use crate as shared;

View File

@ -20,7 +20,7 @@ use crate::config::WhitespaceHandling;
use crate::heritage::{Context, Heritage};
use crate::html::write_escaped_str;
use crate::input::{Source, TemplateInput};
use crate::{BUILT_IN_FILTERS, CompileError, FileInfo, MsgValidEscapers};
use crate::{BUILT_IN_FILTERS, CRATE, CompileError, FileInfo, MsgValidEscapers};
#[derive(Clone, Copy, PartialEq, Debug)]
enum EvaluatedResult {
@ -80,18 +80,6 @@ impl<'a> Generator<'a> {
// Takes a Context and generates the relevant implementations.
pub(crate) fn build(mut self, ctx: &Context<'a>) -> Result<String, CompileError> {
const CRATE: &str = if cfg!(feature = "with-actix-web") {
"rinja_actix"
} else if cfg!(feature = "with-axum") {
"rinja_axum"
} else if cfg!(feature = "with-rocket") {
"rinja_rocket"
} else if cfg!(feature = "with-warp") {
"rinja_warp"
} else {
"rinja"
};
let mut buf = Buffer::new();
buf.write(format_args!(
"\
@ -140,10 +128,10 @@ impl<'a> Generator<'a> {
buf.write(
"fn render_into<RinjaW>(&self, writer: &mut RinjaW) -> rinja::Result<()>\
where \
RinjaW: ::core::fmt::Write + ?::core::marker::Sized\
RinjaW: rinja::core::fmt::Write + ?rinja::core::marker::Sized\
{\
use rinja::filters::{AutoEscape as _, WriteWritable as _};\
use ::core::fmt::Write as _;",
use rinja::core::fmt::Write as _;",
);
buf.set_discard(self.buf_writable.discard);
@ -163,7 +151,8 @@ impl<'a> Generator<'a> {
if path_is_valid {
let path = path.to_str().unwrap();
buf.write(format_args!(
"const _: &[::core::primitive::u8] = ::core::include_bytes!({path:#?});",
"const _: &[rinja::core::primitive::u8] =\
rinja::core::include_bytes!({path:#?});",
));
}
}
@ -181,10 +170,10 @@ impl<'a> Generator<'a> {
"\
rinja::Result::Ok(())\
}}\
const EXTENSION: ::core::option::Option<&'static ::core::primitive::str> =\
::core::option::Option::{:?};\
const SIZE_HINT: ::core::primitive::usize = {size_hint}usize;\
const MIME_TYPE: &'static ::core::primitive::str = {:?};",
const EXTENSION: rinja::core::option::Option<&'static rinja::core::primitive::str> =\
rinja::core::option::Option::{:?};\
const SIZE_HINT: rinja::core::primitive::usize = {size_hint}usize;\
const MIME_TYPE: &'static rinja::core::primitive::str = {:?};",
self.input.extension(),
self.input.mime_type,
));
@ -198,19 +187,19 @@ impl<'a> Generator<'a> {
let ident = &self.input.ast.ident;
buf.write(format_args!(
"\
/// Implement the [`format!()`][::std::format] trait for [`{}`]\n\
/// Implement the [`format!()`][rinja::std::format] trait for [`{}`]\n\
///\n\
/// Please be aware of the rendering performance notice in the \
[`Template`][rinja::Template] trait.\n\
",
quote!(#ident),
));
self.write_header(buf, "::core::fmt::Display", None);
self.write_header(buf, "rinja::core::fmt::Display", None);
buf.write(
"\
#[inline]\
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {\
rinja::Template::render_into(self, f).map_err(|_| ::core::fmt::Error)\
fn fmt(&self, f: &mut rinja::core::fmt::Formatter<'_>) -> rinja::core::fmt::Result {\
rinja::Template::render_into(self, f).map_err(|_| rinja::core::fmt::Error)\
}\
}",
);
@ -222,11 +211,11 @@ impl<'a> Generator<'a> {
buf.write(
"\
#[inline]\
fn write_into<RinjaW>(&self, dest: &mut RinjaW) -> ::core::fmt::Result \
fn write_into<RinjaW>(&self, dest: &mut RinjaW) -> rinja::core::fmt::Result \
where \
RinjaW: ::core::fmt::Write + ?::core::marker::Sized,\
RinjaW: rinja::core::fmt::Write + ?rinja::core::marker::Sized,\
{\
rinja::Template::render_into(self, dest).map_err(|_| ::core::fmt::Error)\
rinja::Template::render_into(self, dest).map_err(|_| rinja::core::fmt::Error)\
}\
}",
);
@ -657,7 +646,7 @@ impl<'a> Generator<'a> {
// finally dereferences it to `bool`.
buf.write("*(&(");
buf.write(this.visit_expr_root(ctx, expr)?);
buf.write(") as &::core::primitive::bool) {");
buf.write(") as &rinja::core::primitive::bool) {");
}
} else if pos != 0 {
buf.write("} else {");
@ -1012,7 +1001,7 @@ impl<'a> Generator<'a> {
// build `FmtCell` that contains the inner block
buf.write(format_args!(
"let {FILTER_SOURCE} = rinja::helpers::FmtCell::new(\
|writer: &mut ::core::fmt::Formatter<'_>| -> rinja::Result<()> {{"
|writer: &mut rinja::core::fmt::Formatter<'_>| -> rinja::Result<()> {{"
));
let size_hint = self.push_locals(|this| {
this.prepare_ws(filter.ws1);
@ -1044,7 +1033,7 @@ impl<'a> Generator<'a> {
),
};
buf.write(format_args!(
"if ::core::write!(writer, \"{{}}\", {filter_buf}).is_err() {{\
"if rinja::core::write!(writer, \"{{}}\", {filter_buf}).is_err() {{\
return {FILTER_SOURCE}.take_err();\
}}"
));
@ -1523,7 +1512,7 @@ impl<'a> Generator<'a> {
) -> Result<DisplayWrap, CompileError> {
buf.write("rinja::helpers::get_primitive_value(&(");
self.visit_expr(ctx, buf, expr)?;
buf.write(format_args!(")) as ::core::primitive::{target}"));
buf.write(format_args!(")) as rinja::core::primitive::{target}"));
Ok(DisplayWrap::Unwrapped)
}
@ -1533,9 +1522,9 @@ impl<'a> Generator<'a> {
buf: &mut Buffer,
expr: &WithSpan<'_, Expr<'_>>,
) -> Result<DisplayWrap, CompileError> {
buf.write("::core::result::Result::map_err(");
buf.write("rinja::core::result::Result::map_err(");
self.visit_expr(ctx, buf, expr)?;
buf.write(", |err| rinja::shared::Error::Custom(::core::convert::Into::into(err)))?");
buf.write(", |err| rinja::shared::Error::Custom(rinja::core::convert::Into::into(err)))?");
Ok(DisplayWrap::Unwrapped)
}
@ -1896,7 +1885,7 @@ impl<'a> Generator<'a> {
) -> Result<DisplayWrap, CompileError> {
if !args.is_empty() {
if let Expr::StrLit(ref fmt) = *args[0] {
buf.write("::std::format!(");
buf.write("rinja::std::format!(");
self.visit_str_lit(buf, fmt);
if args.len() > 1 {
buf.write(',');
@ -1919,7 +1908,7 @@ impl<'a> Generator<'a> {
) -> Result<DisplayWrap, CompileError> {
if let [_, arg2] = args {
if let Expr::StrLit(ref fmt) = **arg2 {
buf.write("::std::format!(");
buf.write("rinja::std::format!(");
self.visit_str_lit(buf, fmt);
buf.write(',');
self._visit_args(ctx, buf, &args[..1])?;
@ -2108,7 +2097,7 @@ impl<'a> Generator<'a> {
);\
let _len = _cycle.len();\
if _len == 0 {\
return ::core::result::Result::Err(rinja::Error::Fmt);\
return rinja::core::result::Result::Err(rinja::Error::Fmt);\
}\
_cycle[_loop_item.index % _len]\
})",

View File

@ -27,8 +27,8 @@ use proc_macro::TokenStream as TokenStream12;
#[cfg(feature = "__standalone")]
use proc_macro2::TokenStream as TokenStream12;
use proc_macro2::{Span, TokenStream};
use quote::quote_spanned;
use rustc_hash::FxBuildHasher;
use syn::parse_quote_spanned;
/// The `Template` derive macro and its `template()` attribute.
///
@ -120,7 +120,10 @@ use syn::parse_quote_spanned;
pub fn derive_template(input: TokenStream12) -> TokenStream12 {
let ast = match syn::parse2(input.into()) {
Ok(ast) => ast,
Err(err) => return err.to_compile_error().into(),
Err(err) => {
let msgs = err.into_iter().map(|err| err.to_string());
return compile_error(msgs, Span::call_site()).into();
}
};
match build_template(&ast) {
Ok(source) => source.parse().unwrap(),
@ -129,10 +132,7 @@ pub fn derive_template(input: TokenStream12) -> TokenStream12 {
span,
rendered: _rendered,
}) => {
let mut ts: TokenStream = parse_quote_spanned! {
span.unwrap_or(ast.ident.span()) =>
::core::compile_error!(#msg);
};
let mut ts = compile_error(std::iter::once(msg), span.unwrap_or(ast.ident.span()));
if let Ok(source) = build_skeleton(&ast) {
let source: TokenStream = source.parse().unwrap();
ts.extend(source);
@ -142,6 +142,17 @@ pub fn derive_template(input: TokenStream12) -> TokenStream12 {
}
}
fn compile_error(msgs: impl Iterator<Item = String>, span: Span) -> TokenStream {
let crate_ = syn::Ident::new(CRATE, Span::call_site());
quote_spanned! {
span =>
const _: () = {
extern crate #crate_ as rinja;
#(rinja::core::compile_error!(#msgs);)*
};
}
}
fn build_skeleton(ast: &syn::DeriveInput) -> Result<String, CompileError> {
let template_args = TemplateArgs::fallback();
let config = Config::new("", None, None, None)?;
@ -433,3 +444,15 @@ const BUILT_IN_FILTERS: &[&str] = &[
"urlencode",
"wordcount",
];
const CRATE: &str = if cfg!(feature = "with-actix-web") {
"rinja_actix"
} else if cfg!(feature = "with-axum") {
"rinja_axum"
} else if cfg!(feature = "with-rocket") {
"rinja_rocket"
} else if cfg!(feature = "with-warp") {
"rinja_warp"
} else {
"rinja"
};

View File

@ -46,36 +46,36 @@ struct Foo {{ {} }}"##,
impl rinja::Template for Foo {
fn render_into<RinjaW>(&self, writer: &mut RinjaW) -> rinja::Result<()>
where
RinjaW: ::core::fmt::Write + ?::core::marker::Sized,
RinjaW: rinja::core::fmt::Write + ?rinja::core::marker::Sized,
{
use rinja::filters::{AutoEscape as _, WriteWritable as _};
use ::core::fmt::Write as _;
use rinja::core::fmt::Write as _;
#expected
rinja::Result::Ok(())
}
const EXTENSION: ::core::option::Option<&'static ::core::primitive::str> =
::core::option::Option::Some("txt");
const SIZE_HINT: ::core::primitive::usize = #size_hint;
const MIME_TYPE: &'static ::core::primitive::str = "text/plain; charset=utf-8";
const EXTENSION: rinja::core::option::Option<&'static rinja::core::primitive::str> =
rinja::core::option::Option::Some("txt");
const SIZE_HINT: rinja::core::primitive::usize = #size_hint;
const MIME_TYPE: &'static rinja::core::primitive::str = "text/plain; charset=utf-8";
}
/// Implement the [`format!()`][::std::format] trait for [`Foo`]
/// Implement the [`format!()`][rinja::std::format] trait for [`Foo`]
///
/// Please be aware of the rendering performance notice in the [`Template`][rinja::Template] trait.
impl ::core::fmt::Display for Foo {
impl rinja::core::fmt::Display for Foo {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
rinja::Template::render_into(self, f).map_err(|_| ::core::fmt::Error)
fn fmt(&self, f: &mut rinja::core::fmt::Formatter<'_>) -> rinja::core::fmt::Result {
rinja::Template::render_into(self, f).map_err(|_| rinja::core::fmt::Error)
}
}
impl rinja::filters::FastWritable for Foo {
#[inline]
fn write_into<RinjaW>(&self, dest: &mut RinjaW) -> ::core::fmt::Result
fn write_into<RinjaW>(&self, dest: &mut RinjaW) -> rinja::core::fmt::Result
where
RinjaW: ::core::fmt::Write + ?::core::marker::Sized,
RinjaW: rinja::core::fmt::Write + ?rinja::core::marker::Sized,
{
rinja::Template::render_into(self, dest).map_err(|_| ::core::fmt::Error)
rinja::Template::render_into(self, dest).map_err(|_| rinja::core::fmt::Error)
}
}
};
@ -194,9 +194,9 @@ fn check_if_let() {
compare(
r#"{% include "include1.html" %}"#,
&format!(
r#"const _: &[::core::primitive::u8] = ::core::include_bytes!({path1:#?});
const _: &[::core::primitive::u8] = ::core::include_bytes!({path2:#?});
const _: &[::core::primitive::u8] = ::core::include_bytes!({path3:#?});
r#"const _: &[rinja::core::primitive::u8] = rinja::core::include_bytes!({path1:#?});
const _: &[rinja::core::primitive::u8] = rinja::core::include_bytes!({path2:#?});
const _: &[rinja::core::primitive::u8] = rinja::core::include_bytes!({path3:#?});
writer.write_str("3333")?;"#
),
&[],
@ -292,7 +292,7 @@ writer.write_str("bla")?;"#,
"{% if x == 12 %}bli
{%- else if x is defined %}12
{%- else %}nope{% endif %}",
r#"if *(&(self.x == 12) as &::core::primitive::bool) {
r#"if *(&(self.x == 12) as &rinja::core::primitive::bool) {
writer.write_str("bli")?;
} else {
writer.write_str("12")?;
@ -305,7 +305,7 @@ writer.write_str("12")?;
// are present.
compare(
"{% if y is defined || x == 12 %}{{x}}{% endif %}",
r"if *(&(self.x == 12) as &::core::primitive::bool) {
r"if *(&(self.x == 12) as &rinja::core::primitive::bool) {
match (
&((&&rinja::filters::AutoEscaper::new(&(self.x), rinja::filters::Text)).rinja_auto_escape()?),
) {
@ -346,7 +346,7 @@ writer.write_str("12")?;
compare(
"{% if y is defined && y == 12 %}{{y}}{% else %}bli{% endif %}",
r#"
if *(&(self.y == 12) as &::core::primitive::bool) {
if *(&(self.y == 12) as &rinja::core::primitive::bool) {
match (
&((&&rinja::filters::AutoEscaper::new(
&(self.y),
@ -417,7 +417,7 @@ match (
// Ensure that the `!` is kept .
compare(
"{% if y is defined && !y %}bla{% endif %}",
r#"if *(&(!self.y) as &::core::primitive::bool) {
r#"if *(&(!self.y) as &rinja::core::primitive::bool) {
writer.write_str("bla")?;
}"#,
&[("y", "bool")],
@ -425,7 +425,7 @@ match (
);
compare(
"{% if y is defined && !(y) %}bla{% endif %}",
r#"if *(&(!(self.y)) as &::core::primitive::bool) {
r#"if *(&(!(self.y)) as &rinja::core::primitive::bool) {
writer.write_str("bla")?;
}"#,
&[("y", "bool")],
@ -433,7 +433,7 @@ match (
);
compare(
"{% if y is not defined || !y %}bla{% endif %}",
r#"if *(&(!self.y) as &::core::primitive::bool) {
r#"if *(&(!self.y) as &rinja::core::primitive::bool) {
writer.write_str("bla")?;
}"#,
&[("y", "bool")],
@ -441,7 +441,7 @@ match (
);
compare(
"{% if y is not defined || !(y) %}bla{% endif %}",
r#"if *(&(!(self.y)) as &::core::primitive::bool) {
r#"if *(&(!(self.y)) as &rinja::core::primitive::bool) {
writer.write_str("bla")?;
}"#,
&[("y", "bool")],
@ -506,7 +506,7 @@ fn check_bool_conditions() {
);
compare(
"{% if false || x == 12 %}{{x}}{% endif %}",
r"if *(&(self.x == 12) as &::core::primitive::bool) {
r"if *(&(self.x == 12) as &rinja::core::primitive::bool) {
match (
&((&&rinja::filters::AutoEscaper::new(
&(self.x),
@ -530,7 +530,7 @@ fn check_bool_conditions() {
// condition.
compare(
"{% if y == 3 || (true || x == 12) %}{{x}}{% endif %}",
r"if *(&(self.y == 3 || (true)) as &::core::primitive::bool) {
r"if *(&(self.y == 3 || (true)) as &rinja::core::primitive::bool) {
match (
&((&&rinja::filters::AutoEscaper::new(
&(self.x),
@ -568,7 +568,7 @@ fn check_bool_conditions() {
);
compare(
"{% if y == 3 || (x == 12 || true) %}{{x}}{% endif %}",
r"if *(&(self.y == 3 || (self.x == 12 || true)) as &::core::primitive::bool) {
r"if *(&(self.y == 3 || (self.x == 12 || true)) as &rinja::core::primitive::bool) {
match (
&((&&rinja::filters::AutoEscaper::new(
&(self.x),

View File

@ -17,6 +17,9 @@ rinja = { path = "../rinja", version = "0.3.4" }
serde_json = { version = "1.0", optional = true }
# intentionally shadow the name `::core` to test if the generated code still works fine
core = { package = "intentionally-empty", version = "1.0.0" }
[dev-dependencies]
rinja = { path = "../rinja", version = "0.3.4", features = ["code-in-doc", "serde_json"] }

View File

@ -207,7 +207,7 @@ hello
{% set y = 12 %}
{% filter wordcount %}
{%- include "../Cargo.toml" +%}
{%- include "../test_trim.toml" +%}
y is {{ y }}
{% endfilter %}
{%- endblock body %}
@ -233,7 +233,7 @@ fn filter_block_include() {
<body class=""><h1>Metadata</h1>
105</body>
7</body>
</html>"#
);
}

View File

@ -0,0 +1,15 @@
#![no_implicit_prelude]
use ::rinja::Template;
#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate<'a> {
name: &'a str,
}
#[test]
fn main() {
let hello = HelloTemplate { name: "world" };
::std::assert_eq!("Hello, world!", hello.render().unwrap());
}