mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-03 15:55:31 +00:00
Merge pull request #253 from Kijewski/pr-refactor-generator
derive: refactor generator for greater re-usability
This commit is contained in:
commit
f86feddbb4
@ -1,6 +1,5 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::hash_map::{Entry, HashMap};
|
use std::collections::hash_map::{Entry, HashMap};
|
||||||
use std::fmt::{Arguments, Display, Write};
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -14,23 +13,51 @@ use parser::{
|
|||||||
CharLit, CharPrefix, Expr, Filter, FloatKind, IntKind, Node, Num, StrLit, StrPrefix, Target,
|
CharLit, CharPrefix, Expr, Filter, FloatKind, IntKind, Node, Num, StrLit, StrPrefix, Target,
|
||||||
WithSpan,
|
WithSpan,
|
||||||
};
|
};
|
||||||
use quote::quote;
|
|
||||||
use rustc_hash::FxBuildHasher;
|
use rustc_hash::FxBuildHasher;
|
||||||
|
|
||||||
use crate::config::WhitespaceHandling;
|
use crate::config::WhitespaceHandling;
|
||||||
use crate::heritage::{Context, Heritage};
|
use crate::heritage::{Context, Heritage};
|
||||||
use crate::html::write_escaped_str;
|
use crate::html::write_escaped_str;
|
||||||
use crate::input::{Source, TemplateInput};
|
use crate::input::{Source, TemplateInput};
|
||||||
|
use crate::integration::{Buffer, impl_everything, write_header};
|
||||||
use crate::{BUILT_IN_FILTERS, CRATE, CompileError, FileInfo, MsgValidEscapers};
|
use crate::{BUILT_IN_FILTERS, CRATE, CompileError, FileInfo, MsgValidEscapers};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
pub(crate) fn template_to_string(
|
||||||
enum EvaluatedResult {
|
input: &TemplateInput<'_>,
|
||||||
AlwaysTrue,
|
contexts: &HashMap<&Arc<Path>, Context<'_>, FxBuildHasher>,
|
||||||
AlwaysFalse,
|
heritage: Option<&Heritage<'_>>,
|
||||||
Unknown,
|
) -> Result<String, CompileError> {
|
||||||
|
let mut buf = Buffer::new();
|
||||||
|
template_into_buffer(input, contexts, heritage, &mut buf, false)?;
|
||||||
|
Ok(buf.into_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Generator<'a> {
|
pub(crate) fn template_into_buffer(
|
||||||
|
input: &TemplateInput<'_>,
|
||||||
|
contexts: &HashMap<&Arc<Path>, Context<'_>, FxBuildHasher>,
|
||||||
|
heritage: Option<&Heritage<'_>>,
|
||||||
|
buf: &mut Buffer,
|
||||||
|
only_template: bool,
|
||||||
|
) -> Result<(), CompileError> {
|
||||||
|
let ctx = &contexts[&input.path];
|
||||||
|
let generator = Generator::new(
|
||||||
|
input,
|
||||||
|
contexts,
|
||||||
|
heritage,
|
||||||
|
MapChain::default(),
|
||||||
|
input.block.is_some(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
let mut result = generator.build(ctx, buf, only_template);
|
||||||
|
if let Err(err) = &mut result {
|
||||||
|
if err.span.is_none() {
|
||||||
|
err.span = input.source_span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Generator<'a> {
|
||||||
// The template input state: original struct AST and attributes
|
// The template input state: original struct AST and attributes
|
||||||
input: &'a TemplateInput<'a>,
|
input: &'a TemplateInput<'a>,
|
||||||
// All contexts, keyed by the package-relative template path
|
// All contexts, keyed by the package-relative template path
|
||||||
@ -55,7 +82,7 @@ pub(crate) struct Generator<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Generator<'a> {
|
impl<'a> Generator<'a> {
|
||||||
pub(crate) fn new<'n>(
|
fn new<'n>(
|
||||||
input: &'n TemplateInput<'_>,
|
input: &'n TemplateInput<'_>,
|
||||||
contexts: &'n HashMap<&'n Arc<Path>, Context<'n>, FxBuildHasher>,
|
contexts: &'n HashMap<&'n Arc<Path>, Context<'n>, FxBuildHasher>,
|
||||||
heritage: Option<&'n Heritage<'_>>,
|
heritage: Option<&'n Heritage<'_>>,
|
||||||
@ -80,37 +107,28 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Takes a Context and generates the relevant implementations.
|
// Takes a Context and generates the relevant implementations.
|
||||||
pub(crate) fn build(mut self, ctx: &Context<'a>) -> Result<String, CompileError> {
|
fn build(
|
||||||
let mut buf = Buffer::new();
|
mut self,
|
||||||
|
ctx: &Context<'a>,
|
||||||
|
buf: &mut Buffer,
|
||||||
|
only_template: bool,
|
||||||
|
) -> Result<(), CompileError> {
|
||||||
|
if !only_template {
|
||||||
buf.write(format_args!(
|
buf.write(format_args!(
|
||||||
"\
|
"\
|
||||||
const _: () = {{\
|
const _: () = {{\
|
||||||
extern crate {CRATE} as rinja;\
|
extern crate {CRATE} as rinja;\
|
||||||
"
|
"
|
||||||
));
|
));
|
||||||
|
|
||||||
if let Err(mut err) = self.impl_template(ctx, &mut buf) {
|
|
||||||
if err.span.is_none() {
|
|
||||||
err.span = self.input.source_span;
|
|
||||||
}
|
|
||||||
return Err(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.impl_display(&mut buf);
|
self.impl_template(ctx, buf)?;
|
||||||
self.impl_fast_writable(&mut buf);
|
impl_everything(self.input.ast, buf, only_template);
|
||||||
|
|
||||||
#[cfg(feature = "with-actix-web")]
|
|
||||||
self.impl_actix_web_responder(&mut buf);
|
|
||||||
#[cfg(feature = "with-axum")]
|
|
||||||
self.impl_axum_into_response(&mut buf);
|
|
||||||
#[cfg(feature = "with-rocket")]
|
|
||||||
self.impl_rocket_responder(&mut buf);
|
|
||||||
#[cfg(feature = "with-warp")]
|
|
||||||
self.impl_warp_reply(&mut buf);
|
|
||||||
|
|
||||||
|
if !only_template {
|
||||||
buf.write("};");
|
buf.write("};");
|
||||||
|
}
|
||||||
Ok(buf.buf)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_locals<T, F: FnOnce(&mut Self) -> Result<T, CompileError>>(
|
fn push_locals<T, F: FnOnce(&mut Self) -> Result<T, CompileError>>(
|
||||||
@ -124,8 +142,12 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implement `Template` for the given context struct.
|
// Implement `Template` for the given context struct.
|
||||||
fn impl_template(&mut self, ctx: &Context<'a>, buf: &mut Buffer) -> Result<(), CompileError> {
|
fn impl_template(
|
||||||
self.write_header(buf, "rinja::Template", None);
|
&mut self,
|
||||||
|
ctx: &Context<'a>,
|
||||||
|
buf: &mut Buffer,
|
||||||
|
) -> Result<usize, CompileError> {
|
||||||
|
write_header(self.input.ast, buf, "rinja::Template", None);
|
||||||
buf.write(
|
buf.write(
|
||||||
"fn render_into<RinjaW>(&self, writer: &mut RinjaW) -> rinja::Result<()>\
|
"fn render_into<RinjaW>(&self, writer: &mut RinjaW) -> rinja::Result<()>\
|
||||||
where \
|
where \
|
||||||
@ -177,147 +199,7 @@ impl<'a> Generator<'a> {
|
|||||||
));
|
));
|
||||||
|
|
||||||
buf.write('}');
|
buf.write('}');
|
||||||
Ok(())
|
Ok(size_hint)
|
||||||
}
|
|
||||||
|
|
||||||
// Implement `Display` for the given context struct.
|
|
||||||
fn impl_display(&mut self, buf: &mut Buffer) {
|
|
||||||
let ident = &self.input.ast.ident;
|
|
||||||
buf.write(format_args!(
|
|
||||||
"\
|
|
||||||
/// Implement the [`format!()`][rinja::helpers::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, "rinja::helpers::core::fmt::Display", None);
|
|
||||||
buf.write(
|
|
||||||
"\
|
|
||||||
#[inline]\
|
|
||||||
fn fmt(\
|
|
||||||
&self,\
|
|
||||||
f: &mut rinja::helpers::core::fmt::Formatter<'_>\
|
|
||||||
) -> rinja::helpers::core::fmt::Result {\
|
|
||||||
rinja::Template::render_into(self, f)\
|
|
||||||
.map_err(|_| rinja::helpers::core::fmt::Error)\
|
|
||||||
}\
|
|
||||||
}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement `FastWritable` for the given context struct.
|
|
||||||
fn impl_fast_writable(&mut self, buf: &mut Buffer) {
|
|
||||||
self.write_header(buf, "rinja::filters::FastWritable", None);
|
|
||||||
buf.write(
|
|
||||||
"\
|
|
||||||
#[inline]\
|
|
||||||
fn write_into<RinjaW>(&self, dest: &mut RinjaW) -> rinja::Result<()> \
|
|
||||||
where \
|
|
||||||
RinjaW: rinja::helpers::core::fmt::Write + ?rinja::helpers::core::marker::Sized,\
|
|
||||||
{\
|
|
||||||
rinja::Template::render_into(self, dest)\
|
|
||||||
}\
|
|
||||||
}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement Actix-web's `Responder`.
|
|
||||||
#[cfg(feature = "with-actix-web")]
|
|
||||||
fn impl_actix_web_responder(&mut self, buf: &mut Buffer) {
|
|
||||||
self.write_header(buf, "::rinja_actix::actix_web::Responder", None);
|
|
||||||
buf.write(
|
|
||||||
"\
|
|
||||||
type Body = ::rinja_actix::actix_web::body::BoxBody;\
|
|
||||||
#[inline]\
|
|
||||||
fn respond_to(self, _req: &::rinja_actix::actix_web::HttpRequest)\
|
|
||||||
-> ::rinja_actix::actix_web::HttpResponse<Self::Body> {\
|
|
||||||
::rinja_actix::into_response(&self)\
|
|
||||||
}\
|
|
||||||
}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement Axum's `IntoResponse`.
|
|
||||||
#[cfg(feature = "with-axum")]
|
|
||||||
fn impl_axum_into_response(&mut self, buf: &mut Buffer) {
|
|
||||||
self.write_header(buf, "::rinja_axum::axum_core::response::IntoResponse", None);
|
|
||||||
buf.write(
|
|
||||||
"\
|
|
||||||
#[inline]\
|
|
||||||
fn into_response(self) -> ::rinja_axum::axum_core::response::Response {\
|
|
||||||
::rinja_axum::into_response(&self)\
|
|
||||||
}\
|
|
||||||
}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement Rocket's `Responder`.
|
|
||||||
#[cfg(feature = "with-rocket")]
|
|
||||||
fn impl_rocket_responder(&mut self, buf: &mut Buffer) {
|
|
||||||
let lifetime1 = syn::Lifetime::new("'rinja1", proc_macro2::Span::call_site());
|
|
||||||
let param1 = syn::GenericParam::Lifetime(syn::LifetimeParam::new(lifetime1));
|
|
||||||
|
|
||||||
self.write_header(
|
|
||||||
buf,
|
|
||||||
"::rinja_rocket::rocket::response::Responder<'rinja1, 'static>",
|
|
||||||
Some(vec![param1]),
|
|
||||||
);
|
|
||||||
buf.write(
|
|
||||||
"\
|
|
||||||
#[inline]\
|
|
||||||
fn respond_to(self, _: &'rinja1 ::rinja_rocket::rocket::request::Request<'_>)\
|
|
||||||
-> ::rinja_rocket::rocket::response::Result<'static>\
|
|
||||||
{\
|
|
||||||
::rinja_rocket::respond(&self)\
|
|
||||||
}\
|
|
||||||
}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "with-warp")]
|
|
||||||
fn impl_warp_reply(&mut self, buf: &mut Buffer) {
|
|
||||||
self.write_header(buf, "::rinja_warp::warp::reply::Reply", None);
|
|
||||||
buf.write(
|
|
||||||
"\
|
|
||||||
#[inline]\
|
|
||||||
fn into_response(self) -> ::rinja_warp::warp::reply::Response {\
|
|
||||||
::rinja_warp::into_response(&self)\
|
|
||||||
}\
|
|
||||||
}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes header for the `impl` for `TraitFromPathName` or `Template`
|
|
||||||
// for the given context struct.
|
|
||||||
fn write_header(
|
|
||||||
&mut self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
target: impl Display,
|
|
||||||
params: Option<Vec<syn::GenericParam>>,
|
|
||||||
) {
|
|
||||||
let mut generics;
|
|
||||||
let (impl_generics, orig_ty_generics, where_clause) = if let Some(params) = params {
|
|
||||||
generics = self.input.ast.generics.clone();
|
|
||||||
for param in params {
|
|
||||||
generics.params.push(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (_, orig_ty_generics, _) = self.input.ast.generics.split_for_impl();
|
|
||||||
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
|
||||||
(impl_generics, orig_ty_generics, where_clause)
|
|
||||||
} else {
|
|
||||||
self.input.ast.generics.split_for_impl()
|
|
||||||
};
|
|
||||||
|
|
||||||
let ident = &self.input.ast.ident;
|
|
||||||
buf.write(format_args!(
|
|
||||||
"impl {} {} for {} {{",
|
|
||||||
quote!(#impl_generics),
|
|
||||||
target,
|
|
||||||
quote!(#ident #orig_ty_generics #where_clause),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods for handling node types
|
// Helper methods for handling node types
|
||||||
@ -640,7 +522,7 @@ impl<'a> Generator<'a> {
|
|||||||
this.visit_target(buf, true, true, target);
|
this.visit_target(buf, true, true, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.write(format_args!("= &{} {{", expr_buf.buf));
|
buf.write(format_args!("= &{expr_buf} {{"));
|
||||||
} else if cond_info.generate_condition {
|
} else if cond_info.generate_condition {
|
||||||
this.visit_condition(ctx, buf, expr)?;
|
this.visit_condition(ctx, buf, expr)?;
|
||||||
buf.write('{');
|
buf.write('{');
|
||||||
@ -949,7 +831,8 @@ impl<'a> Generator<'a> {
|
|||||||
let mut attr_buf = Buffer::new();
|
let mut attr_buf = Buffer::new();
|
||||||
this.visit_attr(ctx, &mut attr_buf, obj, attr)?;
|
this.visit_attr(ctx, &mut attr_buf, obj, attr)?;
|
||||||
|
|
||||||
let var = this.locals.resolve(&attr_buf.buf).unwrap_or(attr_buf.buf);
|
let attr = attr_buf.into_string();
|
||||||
|
let var = this.locals.resolve(&attr).unwrap_or(attr);
|
||||||
this.locals
|
this.locals
|
||||||
.insert(Cow::Borrowed(arg), LocalMeta::with_ref(var));
|
.insert(Cow::Borrowed(arg), LocalMeta::with_ref(var));
|
||||||
}
|
}
|
||||||
@ -965,7 +848,7 @@ impl<'a> Generator<'a> {
|
|||||||
("", "")
|
("", "")
|
||||||
};
|
};
|
||||||
value.write(this.visit_expr_root(ctx, expr)?);
|
value.write(this.visit_expr_root(ctx, expr)?);
|
||||||
buf.write(format_args!("let {arg} = {before}{}{after};", value.buf));
|
buf.write(format_args!("let {arg} = {before}{value}{after};"));
|
||||||
this.locals.insert_with_default(Cow::Borrowed(arg));
|
this.locals.insert_with_default(Cow::Borrowed(arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1022,10 +905,10 @@ impl<'a> Generator<'a> {
|
|||||||
filter,
|
filter,
|
||||||
)?;
|
)?;
|
||||||
let filter_buf = match display_wrap {
|
let filter_buf = match display_wrap {
|
||||||
DisplayWrap::Wrapped => filter_buf.buf,
|
DisplayWrap::Wrapped => filter_buf.into_string(),
|
||||||
DisplayWrap::Unwrapped => format!(
|
DisplayWrap::Unwrapped => format!(
|
||||||
"(&&rinja::filters::AutoEscaper::new(&({}), {})).rinja_auto_escape()?",
|
"(&&rinja::filters::AutoEscaper::new(&({filter_buf}), {})).rinja_auto_escape()?",
|
||||||
filter_buf.buf, self.input.escaper,
|
self.input.escaper,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
buf.write(format_args!(
|
buf.write(format_args!(
|
||||||
@ -1177,7 +1060,7 @@ impl<'a> Generator<'a> {
|
|||||||
} else {
|
} else {
|
||||||
("", "")
|
("", "")
|
||||||
};
|
};
|
||||||
buf.write(format_args!(" = {before}{}{after};", &expr_buf.buf));
|
buf.write(format_args!(" = {before}{expr_buf}{after};"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1361,11 +1244,11 @@ impl<'a> Generator<'a> {
|
|||||||
|
|
||||||
let mut expr_buf = Buffer::new();
|
let mut expr_buf = Buffer::new();
|
||||||
let expr = match self.visit_expr(ctx, &mut expr_buf, s)? {
|
let expr = match self.visit_expr(ctx, &mut expr_buf, s)? {
|
||||||
DisplayWrap::Wrapped => expr_buf.buf,
|
DisplayWrap::Wrapped => expr_buf.into_string(),
|
||||||
DisplayWrap::Unwrapped => format!(
|
DisplayWrap::Unwrapped => format!(
|
||||||
"(&&rinja::filters::AutoEscaper::new(&({}), {})).\
|
"(&&rinja::filters::AutoEscaper::new(&({expr_buf}), {})).\
|
||||||
rinja_auto_escape()?",
|
rinja_auto_escape()?",
|
||||||
expr_buf.buf, self.input.escaper,
|
self.input.escaper,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let idx = if is_cacheable(s) {
|
let idx = if is_cacheable(s) {
|
||||||
@ -1391,11 +1274,10 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
buf.write(format_args!(
|
buf.write(format_args!(
|
||||||
") {{\
|
") {{\
|
||||||
({}) => {{\
|
({targets}) => {{\
|
||||||
{}\
|
{lines}\
|
||||||
}}\
|
}}\
|
||||||
}}",
|
}}"
|
||||||
targets.buf, lines.buf,
|
|
||||||
));
|
));
|
||||||
|
|
||||||
for s in trailing_simple_lines {
|
for s in trailing_simple_lines {
|
||||||
@ -1452,7 +1334,7 @@ impl<'a> Generator<'a> {
|
|||||||
) -> Result<String, CompileError> {
|
) -> Result<String, CompileError> {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
self.visit_expr(ctx, &mut buf, expr)?;
|
self.visit_expr(ctx, &mut buf, expr)?;
|
||||||
Ok(buf.buf)
|
Ok(buf.into_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(
|
fn visit_expr(
|
||||||
@ -2355,7 +2237,7 @@ impl<'a> Generator<'a> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Target::Tuple(path, targets) => {
|
Target::Tuple(path, targets) => {
|
||||||
buf.write(SeparatedPath(path));
|
buf.write_separated_path(path);
|
||||||
buf.write('(');
|
buf.write('(');
|
||||||
for target in targets {
|
for target in targets {
|
||||||
self.visit_target(buf, initialized, false, target);
|
self.visit_target(buf, initialized, false, target);
|
||||||
@ -2364,7 +2246,7 @@ impl<'a> Generator<'a> {
|
|||||||
buf.write(')');
|
buf.write(')');
|
||||||
}
|
}
|
||||||
Target::Array(path, targets) => {
|
Target::Array(path, targets) => {
|
||||||
buf.write(SeparatedPath(path));
|
buf.write_separated_path(path);
|
||||||
buf.write('[');
|
buf.write('[');
|
||||||
for target in targets {
|
for target in targets {
|
||||||
self.visit_target(buf, initialized, false, target);
|
self.visit_target(buf, initialized, false, target);
|
||||||
@ -2373,7 +2255,7 @@ impl<'a> Generator<'a> {
|
|||||||
buf.write(']');
|
buf.write(']');
|
||||||
}
|
}
|
||||||
Target::Struct(path, targets) => {
|
Target::Struct(path, targets) => {
|
||||||
buf.write(SeparatedPath(path));
|
buf.write_separated_path(path);
|
||||||
buf.write('{');
|
buf.write('{');
|
||||||
for (name, target) in targets {
|
for (name, target) in targets {
|
||||||
if let Target::Rest(_) = target {
|
if let Target::Rest(_) = target {
|
||||||
@ -2566,7 +2448,7 @@ fn expr_is_int_lit_plus_minus_one(expr: &WithSpan<'_, Expr<'_>>) -> Option<bool>
|
|||||||
Some(IntKind::$svar) => is_signed_singular($sty::from_str_radix, $value, 1, -1),
|
Some(IntKind::$svar) => is_signed_singular($sty::from_str_radix, $value, 1, -1),
|
||||||
)*
|
)*
|
||||||
$(
|
$(
|
||||||
Some(IntKind::$uvar) => is_unsigned_singular($sty::from_str_radix, $value, 1),
|
Some(IntKind::$uvar) => is_unsigned_singular($uty::from_str_radix, $value, 1),
|
||||||
)*
|
)*
|
||||||
None => match $value.starts_with('-') {
|
None => match $value.starts_with('-') {
|
||||||
true => is_signed_singular(i128::from_str_radix, $value, 1, -1),
|
true => is_signed_singular(i128::from_str_radix, $value, 1, -1),
|
||||||
@ -2714,105 +2596,6 @@ fn compile_time_escape<'a>(expr: &Expr<'a>, escaper: &str) -> Option<Writable<'a
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Buffer {
|
|
||||||
// The buffer to generate the code into
|
|
||||||
buf: String,
|
|
||||||
discard: bool,
|
|
||||||
last_was_write_str: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Buffer {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
buf: String::new(),
|
|
||||||
discard: false,
|
|
||||||
last_was_write_str: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_discard(&self) -> bool {
|
|
||||||
self.discard
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_discard(&mut self, discard: bool) {
|
|
||||||
self.discard = discard;
|
|
||||||
self.last_was_write_str = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, src: impl BufferFmt) {
|
|
||||||
if !self.discard {
|
|
||||||
src.append_to(&mut self.buf);
|
|
||||||
self.last_was_write_str = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_escaped_str(&mut self, s: &str) {
|
|
||||||
if !self.discard {
|
|
||||||
self.buf.push('"');
|
|
||||||
string_escape(&mut self.buf, s);
|
|
||||||
self.buf.push('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_writer(&mut self, s: &str) -> usize {
|
|
||||||
const OPEN: &str = r#"writer.write_str(""#;
|
|
||||||
const CLOSE: &str = r#"")?;"#;
|
|
||||||
|
|
||||||
if !s.is_empty() && !self.discard {
|
|
||||||
if !self.last_was_write_str {
|
|
||||||
self.last_was_write_str = true;
|
|
||||||
self.buf.push_str(OPEN);
|
|
||||||
} else {
|
|
||||||
// strip trailing `")?;`, leaving an unterminated string
|
|
||||||
self.buf.truncate(self.buf.len() - CLOSE.len());
|
|
||||||
}
|
|
||||||
string_escape(&mut self.buf, s);
|
|
||||||
self.buf.push_str(CLOSE);
|
|
||||||
}
|
|
||||||
s.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.buf.clear();
|
|
||||||
self.last_was_write_str = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait BufferFmt {
|
|
||||||
fn append_to(&self, buf: &mut String);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: BufferFmt + ?Sized> BufferFmt for &T {
|
|
||||||
fn append_to(&self, buf: &mut String) {
|
|
||||||
T::append_to(self, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferFmt for char {
|
|
||||||
fn append_to(&self, buf: &mut String) {
|
|
||||||
buf.push(*self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferFmt for str {
|
|
||||||
fn append_to(&self, buf: &mut String) {
|
|
||||||
buf.push_str(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferFmt for String {
|
|
||||||
fn append_to(&self, buf: &mut String) {
|
|
||||||
buf.push_str(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferFmt for Arguments<'_> {
|
|
||||||
fn append_to(&self, buf: &mut String) {
|
|
||||||
buf.write_fmt(*self).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CondInfo<'a> {
|
struct CondInfo<'a> {
|
||||||
cond: &'a WithSpan<'a, Cond<'a>>,
|
cond: &'a WithSpan<'a, Cond<'a>>,
|
||||||
cond_expr: Option<WithSpan<'a, Expr<'a>>>,
|
cond_expr: Option<WithSpan<'a, Expr<'a>>>,
|
||||||
@ -2827,6 +2610,13 @@ struct Conds<'a> {
|
|||||||
nb_conds: usize,
|
nb_conds: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
enum EvaluatedResult {
|
||||||
|
AlwaysTrue,
|
||||||
|
AlwaysFalse,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Conds<'a> {
|
impl<'a> Conds<'a> {
|
||||||
fn compute_branches(generator: &Generator<'a>, i: &'a If<'a>) -> Self {
|
fn compute_branches(generator: &Generator<'a>, i: &'a If<'a>) -> Self {
|
||||||
let mut conds = Vec::with_capacity(i.branches.len());
|
let mut conds = Vec::with_capacity(i.branches.len());
|
||||||
@ -2929,21 +2719,8 @@ impl<'a> Conds<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SeparatedPath<I>(I);
|
|
||||||
|
|
||||||
impl<I: IntoIterator<Item = E> + Copy, E: BufferFmt> BufferFmt for SeparatedPath<I> {
|
|
||||||
fn append_to(&self, buf: &mut String) {
|
|
||||||
for (idx, item) in self.0.into_iter().enumerate() {
|
|
||||||
if idx > 0 {
|
|
||||||
buf.push_str("::");
|
|
||||||
}
|
|
||||||
item.append_to(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub(crate) struct LocalMeta {
|
struct LocalMeta {
|
||||||
refs: Option<String>,
|
refs: Option<String>,
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
}
|
}
|
||||||
@ -2965,7 +2742,7 @@ impl LocalMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct MapChain<'a, K, V>
|
struct MapChain<'a, K, V>
|
||||||
where
|
where
|
||||||
K: cmp::Eq + hash::Hash,
|
K: cmp::Eq + hash::Hash,
|
||||||
{
|
{
|
||||||
@ -3069,7 +2846,7 @@ fn is_copyable_within_op(expr: &Expr<'_>, within_op: bool) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if this is an `Attr` where the `obj` is `"self"`.
|
/// Returns `true` if this is an `Attr` where the `obj` is `"self"`.
|
||||||
pub(crate) fn is_attr_self(mut expr: &Expr<'_>) -> bool {
|
fn is_attr_self(mut expr: &Expr<'_>) -> bool {
|
||||||
loop {
|
loop {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Attr(obj, _) if matches!(***obj, Expr::Var("self")) => return true,
|
Expr::Attr(obj, _) if matches!(***obj, Expr::Var("self")) => return true,
|
||||||
@ -3082,7 +2859,7 @@ pub(crate) fn is_attr_self(mut expr: &Expr<'_>) -> bool {
|
|||||||
/// Returns `true` if the outcome of this expression may be used multiple times in the same
|
/// Returns `true` if the outcome of this expression may be used multiple times in the same
|
||||||
/// `write!()` call, without evaluating the expression again, i.e. the expression should be
|
/// `write!()` call, without evaluating the expression again, i.e. the expression should be
|
||||||
/// side-effect free.
|
/// side-effect free.
|
||||||
pub(crate) fn is_cacheable(expr: &WithSpan<'_, Expr<'_>>) -> bool {
|
fn is_cacheable(expr: &WithSpan<'_, Expr<'_>>) -> bool {
|
||||||
match &**expr {
|
match &**expr {
|
||||||
// Literals are the definition of pure:
|
// Literals are the definition of pure:
|
||||||
Expr::BoolLit(_) => true,
|
Expr::BoolLit(_) => true,
|
||||||
@ -3209,26 +2986,3 @@ fn normalize_identifier(ident: &str) -> &str {
|
|||||||
// SAFETY: We know that the input byte slice is pure-ASCII.
|
// SAFETY: We know that the input byte slice is pure-ASCII.
|
||||||
unsafe { std::str::from_utf8_unchecked(&replacement[..ident.len() + 2]) }
|
unsafe { std::str::from_utf8_unchecked(&replacement[..ident.len() + 2]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to `write!(dest, "{src:?}")`, but only escapes the strictly needed characters,
|
|
||||||
/// and without the surrounding `"…"` quotation marks.
|
|
||||||
pub(crate) fn string_escape(dest: &mut String, src: &str) {
|
|
||||||
// SAFETY: we will only push valid str slices
|
|
||||||
let dest = unsafe { dest.as_mut_vec() };
|
|
||||||
let src = src.as_bytes();
|
|
||||||
let mut last = 0;
|
|
||||||
|
|
||||||
// According to <https://doc.rust-lang.org/reference/tokens.html#string-literals>, every
|
|
||||||
// character is valid except `" \ IsolatedCR`. We don't test if the `\r` is isolated or not,
|
|
||||||
// but always escape it.
|
|
||||||
for x in memchr::memchr3_iter(b'\\', b'"', b'\r', src) {
|
|
||||||
dest.extend(&src[last..x]);
|
|
||||||
dest.extend(match src[x] {
|
|
||||||
b'\\' => br"\\",
|
|
||||||
b'\"' => br#"\""#,
|
|
||||||
_ => br"\r",
|
|
||||||
});
|
|
||||||
last = x + 1;
|
|
||||||
}
|
|
||||||
dest.extend(&src[last..]);
|
|
||||||
}
|
|
||||||
|
318
rinja_derive/src/integration.rs
Normal file
318
rinja_derive/src/integration.rs
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
use std::fmt::{Arguments, Display, Write};
|
||||||
|
|
||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
|
||||||
|
/// Implement every integration for the given item
|
||||||
|
pub(crate) fn impl_everything(ast: &DeriveInput, buf: &mut Buffer, only_template: bool) {
|
||||||
|
impl_display(ast, buf);
|
||||||
|
impl_fast_writable(ast, buf);
|
||||||
|
|
||||||
|
if !only_template {
|
||||||
|
#[cfg(feature = "with-actix-web")]
|
||||||
|
impl_actix_web_responder(ast, buf);
|
||||||
|
#[cfg(feature = "with-axum")]
|
||||||
|
impl_axum_into_response(ast, buf);
|
||||||
|
#[cfg(feature = "with-rocket")]
|
||||||
|
impl_rocket_responder(ast, buf);
|
||||||
|
#[cfg(feature = "with-warp")]
|
||||||
|
impl_warp_reply(ast, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes header for the `impl` for `TraitFromPathName` or `Template` for the given item
|
||||||
|
pub(crate) fn write_header(
|
||||||
|
ast: &DeriveInput,
|
||||||
|
buf: &mut Buffer,
|
||||||
|
target: impl Display,
|
||||||
|
params: Option<Vec<syn::GenericParam>>,
|
||||||
|
) {
|
||||||
|
let mut generics;
|
||||||
|
let (impl_generics, orig_ty_generics, where_clause) = if let Some(params) = params {
|
||||||
|
generics = ast.generics.clone();
|
||||||
|
for param in params {
|
||||||
|
generics.params.push(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, orig_ty_generics, _) = ast.generics.split_for_impl();
|
||||||
|
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
||||||
|
(impl_generics, orig_ty_generics, where_clause)
|
||||||
|
} else {
|
||||||
|
ast.generics.split_for_impl()
|
||||||
|
};
|
||||||
|
|
||||||
|
let ident = &ast.ident;
|
||||||
|
buf.write(format_args!(
|
||||||
|
"impl {} {} for {} {{",
|
||||||
|
quote!(#impl_generics),
|
||||||
|
target,
|
||||||
|
quote!(#ident #orig_ty_generics #where_clause),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement `Display` for the given item.
|
||||||
|
fn impl_display(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
let ident = &ast.ident;
|
||||||
|
buf.write(format_args!(
|
||||||
|
"\
|
||||||
|
/// Implement the [`format!()`][rinja::helpers::std::format] trait for [`{}`]\n\
|
||||||
|
///\n\
|
||||||
|
/// Please be aware of the rendering performance notice in the \
|
||||||
|
[`Template`][rinja::Template] trait.\n\
|
||||||
|
",
|
||||||
|
quote!(#ident),
|
||||||
|
));
|
||||||
|
write_header(ast, buf, "rinja::helpers::core::fmt::Display", None);
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[inline]\
|
||||||
|
fn fmt(\
|
||||||
|
&self,\
|
||||||
|
f: &mut rinja::helpers::core::fmt::Formatter<'_>\
|
||||||
|
) -> rinja::helpers::core::fmt::Result {\
|
||||||
|
rinja::Template::render_into(self, f)\
|
||||||
|
.map_err(|_| rinja::helpers::core::fmt::Error)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement `FastWritable` for the given item.
|
||||||
|
fn impl_fast_writable(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
write_header(ast, buf, "rinja::filters::FastWritable", None);
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[inline]\
|
||||||
|
fn write_into<RinjaW>(&self, dest: &mut RinjaW) -> rinja::Result<()> \
|
||||||
|
where \
|
||||||
|
RinjaW: rinja::helpers::core::fmt::Write + ?rinja::helpers::core::marker::Sized,\
|
||||||
|
{\
|
||||||
|
rinja::Template::render_into(self, dest)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement Actix-web's `Responder`.
|
||||||
|
#[cfg(feature = "with-actix-web")]
|
||||||
|
fn impl_actix_web_responder(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
write_header(ast, buf, "::rinja_actix::actix_web::Responder", None);
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
type Body = ::rinja_actix::actix_web::body::BoxBody;\
|
||||||
|
#[inline]\
|
||||||
|
fn respond_to(self, _req: &::rinja_actix::actix_web::HttpRequest)\
|
||||||
|
-> ::rinja_actix::actix_web::HttpResponse<Self::Body> {\
|
||||||
|
::rinja_actix::into_response(&self)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement Axum's `IntoResponse`.
|
||||||
|
#[cfg(feature = "with-axum")]
|
||||||
|
fn impl_axum_into_response(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
write_header(
|
||||||
|
ast,
|
||||||
|
buf,
|
||||||
|
"::rinja_axum::axum_core::response::IntoResponse",
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[inline]\
|
||||||
|
fn into_response(self) -> ::rinja_axum::axum_core::response::Response {\
|
||||||
|
::rinja_axum::into_response(&self)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement Rocket's `Responder`.
|
||||||
|
#[cfg(feature = "with-rocket")]
|
||||||
|
fn impl_rocket_responder(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
let lifetime1 = syn::Lifetime::new("'rinja1", proc_macro2::Span::call_site());
|
||||||
|
let param1 = syn::GenericParam::Lifetime(syn::LifetimeParam::new(lifetime1));
|
||||||
|
|
||||||
|
write_header(
|
||||||
|
ast,
|
||||||
|
buf,
|
||||||
|
"::rinja_rocket::rocket::response::Responder<'rinja1, 'static>",
|
||||||
|
Some(vec![param1]),
|
||||||
|
);
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[inline]\
|
||||||
|
fn respond_to(self, _: &'rinja1 ::rinja_rocket::rocket::request::Request<'_>)\
|
||||||
|
-> ::rinja_rocket::rocket::response::Result<'static>\
|
||||||
|
{\
|
||||||
|
::rinja_rocket::respond(&self)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement Warp's `Reply`.
|
||||||
|
#[cfg(feature = "with-warp")]
|
||||||
|
fn impl_warp_reply(ast: &DeriveInput, buf: &mut Buffer) {
|
||||||
|
write_header(ast, buf, "::rinja_warp::warp::reply::Reply", None);
|
||||||
|
buf.write(
|
||||||
|
"\
|
||||||
|
#[inline]\
|
||||||
|
fn into_response(self) -> ::rinja_warp::warp::reply::Response {\
|
||||||
|
::rinja_warp::into_response(&self)\
|
||||||
|
}\
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Buffer {
|
||||||
|
// The buffer to generate the code into
|
||||||
|
buf: String,
|
||||||
|
discard: bool,
|
||||||
|
last_was_write_str: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Buffer {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(&self.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buffer {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
buf: String::new(),
|
||||||
|
discard: false,
|
||||||
|
last_was_write_str: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_string(self) -> String {
|
||||||
|
self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_discard(&self) -> bool {
|
||||||
|
self.discard
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_discard(&mut self, discard: bool) {
|
||||||
|
self.discard = discard;
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write(&mut self, src: impl BufferFmt) {
|
||||||
|
if self.discard {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
|
||||||
|
src.append_to(&mut self.buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_separated_path(&mut self, path: &[&str]) {
|
||||||
|
if self.discard {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
|
||||||
|
for (idx, item) in path.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
self.buf.push_str("::");
|
||||||
|
}
|
||||||
|
self.buf.push_str(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_escaped_str(&mut self, s: &str) {
|
||||||
|
if self.discard {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
|
||||||
|
self.buf.push('"');
|
||||||
|
string_escape(&mut self.buf, s);
|
||||||
|
self.buf.push('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_writer(&mut self, s: &str) -> usize {
|
||||||
|
const OPEN: &str = r#"writer.write_str(""#;
|
||||||
|
const CLOSE: &str = r#"")?;"#;
|
||||||
|
|
||||||
|
if !s.is_empty() && !self.discard {
|
||||||
|
if !self.last_was_write_str {
|
||||||
|
self.last_was_write_str = true;
|
||||||
|
self.buf.push_str(OPEN);
|
||||||
|
} else {
|
||||||
|
// strip trailing `")?;`, leaving an unterminated string
|
||||||
|
self.buf.truncate(self.buf.len() - CLOSE.len());
|
||||||
|
}
|
||||||
|
string_escape(&mut self.buf, s);
|
||||||
|
self.buf.push_str(CLOSE);
|
||||||
|
}
|
||||||
|
s.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear(&mut self) {
|
||||||
|
self.buf.clear();
|
||||||
|
self.last_was_write_str = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait BufferFmt {
|
||||||
|
fn append_to(&self, buf: &mut String);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BufferFmt + ?Sized> BufferFmt for &T {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
T::append_to(self, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for char {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.push(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for str {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.push_str(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for String {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.push_str(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferFmt for Arguments<'_> {
|
||||||
|
fn append_to(&self, buf: &mut String) {
|
||||||
|
buf.write_fmt(*self).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Similar to `write!(dest, "{src:?}")`, but only escapes the strictly needed characters,
|
||||||
|
/// and without the surrounding `"…"` quotation marks.
|
||||||
|
fn string_escape(dest: &mut String, src: &str) {
|
||||||
|
// SAFETY: we will only push valid str slices
|
||||||
|
let dest = unsafe { dest.as_mut_vec() };
|
||||||
|
let src = src.as_bytes();
|
||||||
|
let mut last = 0;
|
||||||
|
|
||||||
|
// According to <https://doc.rust-lang.org/reference/tokens.html#string-literals>, every
|
||||||
|
// character is valid except `" \ IsolatedCR`. We don't test if the `\r` is isolated or not,
|
||||||
|
// but always escape it.
|
||||||
|
for x in memchr::memchr3_iter(b'\\', b'"', b'\r', src) {
|
||||||
|
dest.extend(&src[last..x]);
|
||||||
|
dest.extend(match src[x] {
|
||||||
|
b'\\' => br"\\",
|
||||||
|
b'\"' => br#"\""#,
|
||||||
|
_ => br"\r",
|
||||||
|
});
|
||||||
|
last = x + 1;
|
||||||
|
}
|
||||||
|
dest.extend(&src[last..]);
|
||||||
|
}
|
@ -7,6 +7,7 @@ mod generator;
|
|||||||
mod heritage;
|
mod heritage;
|
||||||
mod html;
|
mod html;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod integration;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ use std::path::Path;
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use config::{Config, read_config_file};
|
use config::{Config, read_config_file};
|
||||||
use generator::{Generator, MapChain};
|
use generator::template_to_string;
|
||||||
use heritage::{Context, Heritage};
|
use heritage::{Context, Heritage};
|
||||||
use input::{Print, TemplateArgs, TemplateInput};
|
use input::{Print, TemplateArgs, TemplateInput};
|
||||||
use parser::{Parsed, WithSpan, strip_common};
|
use parser::{Parsed, WithSpan, strip_common};
|
||||||
@ -160,15 +161,7 @@ fn build_skeleton(ast: &syn::DeriveInput) -> Result<String, CompileError> {
|
|||||||
let mut contexts = HashMap::default();
|
let mut contexts = HashMap::default();
|
||||||
let parsed = parser::Parsed::default();
|
let parsed = parser::Parsed::default();
|
||||||
contexts.insert(&input.path, Context::empty(&parsed));
|
contexts.insert(&input.path, Context::empty(&parsed));
|
||||||
Generator::new(
|
template_to_string(&input, &contexts, None)
|
||||||
&input,
|
|
||||||
&contexts,
|
|
||||||
None,
|
|
||||||
MapChain::default(),
|
|
||||||
input.block.is_some(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.build(&contexts[&input.path])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a `syn::DeriveInput` and generates source code for it
|
/// Takes a `syn::DeriveInput` and generates source code for it
|
||||||
@ -233,15 +226,7 @@ fn build_template_inner(
|
|||||||
eprintln!("{:?}", templates[&input.path].nodes());
|
eprintln!("{:?}", templates[&input.path].nodes());
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = Generator::new(
|
let code = template_to_string(&input, &contexts, heritage.as_ref())?;
|
||||||
&input,
|
|
||||||
&contexts,
|
|
||||||
heritage.as_ref(),
|
|
||||||
MapChain::default(),
|
|
||||||
input.block.is_some(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.build(&contexts[&input.path])?;
|
|
||||||
if input.print == Print::Code || input.print == Print::All {
|
if input.print == Print::Code || input.print == Print::All {
|
||||||
eprintln!("{code}");
|
eprintln!("{code}");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user