mirror of
https://github.com/askama-rs/askama.git
synced 2025-12-30 05:12:03 +00:00
Merge pull request #635 from GuillaumeGomez/method-call-enum-variants
Fix method call on enum variant templates
This commit is contained in:
commit
4e85c108df
@ -1,5 +1,6 @@
|
||||
use std::fmt::Display;
|
||||
use std::mem::take;
|
||||
use std::str::FromStr;
|
||||
|
||||
use parser::PathComponent;
|
||||
use proc_macro2::{Literal, TokenStream, TokenTree};
|
||||
@ -331,8 +332,8 @@ pub(crate) fn build_template_enum(
|
||||
continue;
|
||||
};
|
||||
|
||||
let var_ast = type_for_enum_variant(enum_ast, &generics, var);
|
||||
quote_into!(buf, span, { #var_ast });
|
||||
let (var_ast, deref_impl) = type_for_enum_variant(enum_ast, &generics, var);
|
||||
quote_into!(buf, span, { #var_ast #deref_impl });
|
||||
|
||||
// not inherited: template, meta_docs, block, print
|
||||
if let Some(enum_args) = &mut enum_args {
|
||||
@ -467,28 +468,42 @@ fn type_for_enum_variant(
|
||||
enum_ast: &DeriveInput,
|
||||
enum_generics: &Generics,
|
||||
var: &Variant,
|
||||
) -> DeriveInput {
|
||||
) -> (DeriveInput, TokenStream) {
|
||||
let enum_id = &enum_ast.ident;
|
||||
let (_, ty_generics, _) = enum_ast.generics.split_for_impl();
|
||||
let lt = enum_generics.params.first().unwrap();
|
||||
let (_, ty_generics, _) = enum_ast.generics.split_for_impl();
|
||||
let mut generics = enum_ast.generics.clone();
|
||||
generics.params.insert(0, lt.clone());
|
||||
|
||||
let id = &var.ident;
|
||||
let span = id.span();
|
||||
let id = Ident::new(&format!("__Askama__{enum_id}__{id}"), span);
|
||||
|
||||
let phantom: Type = parse_quote! {
|
||||
askama::helpers::core::marker::PhantomData < &#lt #enum_id #ty_generics >
|
||||
let field: Type = parse_quote! {
|
||||
&#lt #enum_id #ty_generics
|
||||
};
|
||||
let fields = match &var.fields {
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
|
||||
let (fields, deref_impl) = match &var.fields {
|
||||
Fields::Named(fields) => {
|
||||
let mut fields = fields.clone();
|
||||
for f in fields.named.iter_mut() {
|
||||
let ty = &f.ty;
|
||||
f.ty = parse_quote!(&#lt #ty);
|
||||
}
|
||||
let id = Ident::new(&format!("__Askama__{enum_id}__phantom"), span);
|
||||
fields.named.push(parse_quote!(#id: #phantom));
|
||||
Fields::Named(fields)
|
||||
let field_name = Ident::new(&format!("__Askama__{enum_id}__phantom"), span);
|
||||
fields.named.push(parse_quote!(#field_name: #field));
|
||||
let deref_impl = quote_spanned! {
|
||||
span=>
|
||||
impl #impl_generics askama::helpers::core::ops::Deref for #id #ty_generics {
|
||||
type Target = #field;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.#field_name
|
||||
}
|
||||
}
|
||||
};
|
||||
(Fields::Named(fields), deref_impl)
|
||||
}
|
||||
Fields::Unnamed(fields) => {
|
||||
let mut fields = fields.clone();
|
||||
@ -496,10 +511,34 @@ fn type_for_enum_variant(
|
||||
let ty = &f.ty;
|
||||
f.ty = parse_quote!(&#lt #ty);
|
||||
}
|
||||
fields.unnamed.push(parse_quote!(#phantom));
|
||||
Fields::Unnamed(fields)
|
||||
fields.unnamed.push(parse_quote!(#field));
|
||||
let idx = TokenStream::from_str(&format!("self.{}", fields.unnamed.len() - 1)).unwrap();
|
||||
let deref_impl = quote_spanned! {
|
||||
span=>
|
||||
impl #impl_generics askama::helpers::core::ops::Deref for #id #ty_generics {
|
||||
type Target = #field;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&#idx
|
||||
}
|
||||
}
|
||||
};
|
||||
(Fields::Unnamed(fields), deref_impl)
|
||||
}
|
||||
Fields::Unit => {
|
||||
let fields = Fields::Unnamed(parse_quote!((#field)));
|
||||
let deref_impl = quote_spanned! {
|
||||
span=>
|
||||
impl #impl_generics askama::helpers::core::ops::Deref for #id #ty_generics {
|
||||
type Target = #field;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
};
|
||||
(fields, deref_impl)
|
||||
}
|
||||
Fields::Unit => Fields::Unnamed(parse_quote!((#phantom))),
|
||||
};
|
||||
let semicolon = match &var.fields {
|
||||
Fields::Named(_) => None,
|
||||
@ -507,15 +546,17 @@ fn type_for_enum_variant(
|
||||
};
|
||||
|
||||
let span = enum_ast.span().resolved_at(proc_macro2::Span::call_site());
|
||||
syn::parse_quote_spanned! {
|
||||
span=>
|
||||
#[askama::helpers::core::prelude::rust_2021::derive(
|
||||
askama::helpers::core::prelude::rust_2021::Clone,
|
||||
askama::helpers::core::prelude::rust_2021::Copy,
|
||||
askama::helpers::core::prelude::rust_2021::Debug
|
||||
)]
|
||||
struct #id #enum_generics #fields #semicolon
|
||||
}
|
||||
(
|
||||
syn::parse_quote_spanned! {
|
||||
span=>
|
||||
#[askama::helpers::core::prelude::rust_2021::derive(
|
||||
askama::helpers::core::prelude::rust_2021::Clone,
|
||||
askama::helpers::core::prelude::rust_2021::Copy,
|
||||
)]
|
||||
struct #id #enum_generics #fields #semicolon
|
||||
},
|
||||
deref_impl,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates a `match` arm for an `enum` variant, that calls `<_ as EnumVariantTemplate>::render_into()`
|
||||
@ -571,7 +612,7 @@ fn variant_as_arm(
|
||||
Fields::Unnamed(_) | Fields::Unit => unreachable!(),
|
||||
};
|
||||
this.extend(quote_spanned!(
|
||||
span => #phantom: askama::helpers::core::marker::PhantomData {},
|
||||
span => #phantom: &self,
|
||||
));
|
||||
}
|
||||
|
||||
@ -584,13 +625,11 @@ fn variant_as_arm(
|
||||
this.extend(quote_spanned!(span => #idx: #arg,));
|
||||
}
|
||||
let idx = syn::LitInt::new(&format!("{}", fields.unnamed.len()), span);
|
||||
this.extend(
|
||||
quote_spanned!(span => #idx: askama::helpers::core::marker::PhantomData {},),
|
||||
);
|
||||
this.extend(quote_spanned!(span => #idx: &self,));
|
||||
}
|
||||
|
||||
Fields::Unit => {
|
||||
this.extend(quote_spanned!(span => 0: askama::helpers::core::marker::PhantomData {},));
|
||||
this.extend(quote_spanned!(span => 0: &self,));
|
||||
}
|
||||
};
|
||||
let var_writer = crate::var_writer();
|
||||
|
||||
@ -7,7 +7,7 @@ use std::process::Command;
|
||||
|
||||
#[test]
|
||||
fn test_custom_ui() {
|
||||
if !cfg!(unix) {
|
||||
if !cfg!(target_os = "linux") {
|
||||
return;
|
||||
}
|
||||
let Ok(cargo_home) = std::env::var("CARGO_MANIFEST_DIR") else {
|
||||
@ -58,16 +58,10 @@ path = "main.rs"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut nb_run_tests = 0;
|
||||
for entry in read_dir(custom_ui_folder).unwrap() {
|
||||
for entry in read_dir(&custom_ui_folder).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let test_path = entry.path();
|
||||
if !test_path.is_dir() && test_path.extension() == Some(OsStr::new("rs")) {
|
||||
if nb_run_tests == 0 {
|
||||
// To prevent having build logs in tests output, we run the build a first time.
|
||||
run_cargo(&test_dir);
|
||||
}
|
||||
nb_run_tests += 1;
|
||||
print!("> {}...", test_path.file_name().unwrap().display());
|
||||
if !run_test(bless, &test_path, &test_dir) {
|
||||
*errors += 1;
|
||||
@ -142,7 +136,10 @@ fn run_cargo(test_dir: &Path) -> String {
|
||||
match (start, end) {
|
||||
(Some(start), None) => stderr[start..].to_string(),
|
||||
(Some(start), Some(end)) => stderr[start..end].to_string(),
|
||||
_ => panic!("didn't find `askama_test` start line, full output:\n{stderr}"),
|
||||
_ => {
|
||||
let stdout = String::from_utf8_lossy(&out.stdout);
|
||||
panic!("didn't find `askama_test` start line, stderr:\n{stderr}\n\nstdout:\n{stdout}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -102,7 +102,7 @@ fn test_simple_enum() {
|
||||
#[derive(Template, Debug)]
|
||||
#[template(
|
||||
ext = "txt",
|
||||
source = "{{ self::type_name_of_val(self) }} | {{ self|fmt(\"{:?}\") }}"
|
||||
source = "{{ self::type_name_of_val(self) }} | {{ self.bar() }}"
|
||||
)]
|
||||
enum SimpleEnum<'a, B: Display + Debug> {
|
||||
#[template(source = "A")]
|
||||
@ -125,6 +125,16 @@ fn test_simple_enum() {
|
||||
G,
|
||||
}
|
||||
|
||||
impl<'a, B: Display + Debug> SimpleEnum<'a, B> {
|
||||
fn bar(&self) -> &str {
|
||||
match self {
|
||||
Self::F => "F",
|
||||
Self::G => "G",
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tmpl: SimpleEnum<'_, X> = SimpleEnum::A;
|
||||
assert_eq!(tmpl.render().unwrap(), "A");
|
||||
|
||||
@ -154,15 +164,9 @@ fn test_simple_enum() {
|
||||
let tmpl: SimpleEnum<'_, X> = SimpleEnum::G;
|
||||
assert_matches!(
|
||||
tmpl.render().unwrap().as_str(),
|
||||
"&enum::test_simple_enum::_::__Askama__SimpleEnum__G<enum::X> | \
|
||||
__Askama__SimpleEnum__G(\
|
||||
PhantomData<&enum::test_simple_enum::SimpleEnum<enum::X>>\
|
||||
)"
|
||||
"&enum::test_simple_enum::_::__Askama__SimpleEnum__G<enum::X> | G"
|
||||
// output since rustc 1.91.0-nightly 2025-08-17
|
||||
| "&enum::test_simple_enum::_::__Askama__SimpleEnum__G<'_, '_, enum::X> | \
|
||||
__Askama__SimpleEnum__G(\
|
||||
PhantomData<&enum::test_simple_enum::SimpleEnum<'_, enum::X>>\
|
||||
)"
|
||||
| "&enum::test_simple_enum::_::__Askama__SimpleEnum__G<'_, '_, enum::X> | G"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user