Coverage Report

Created: 2026-05-24 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/askama/askama_derive/src/integration.rs
Line
Count
Source
1
use std::fmt::Display;
2
use std::mem::take;
3
use std::str::FromStr;
4
5
use parser::PathComponent;
6
use proc_macro2::{Literal, Span, TokenStream, TokenTree};
7
use quote::{ToTokens, quote_spanned};
8
use syn::spanned::Spanned;
9
use syn::{
10
    Data, DeriveInput, Fields, GenericParam, Generics, Ident, Lifetime, LifetimeParam, LitStr,
11
    Token, Type, Variant, parse_quote,
12
};
13
14
use crate::generator::TmplKind;
15
use crate::input::{PartialTemplateArgs, TemplateArgs};
16
use crate::{CompileError, Context, Print, SizeHint, build_template_item, field_new, quote_into};
17
18
/// Implement every integration for the given item
19
20.3k
pub(crate) fn impl_everything(ast: &DeriveInput, buf: &mut Buffer) {
20
20.3k
    impl_display(ast, buf);
21
20.3k
    impl_fast_writable(ast, buf);
22
20.3k
}
23
24
/// Writes header for the `impl` for `TraitFromPathName` or `Template` for the given item
25
168k
pub(crate) fn write_header(ast: &DeriveInput, buf: &mut Buffer, target: TokenStream) {
26
168k
    let (impl_generics, orig_ty_generics, where_clause) = ast.generics.split_for_impl();
27
28
168k
    let ident = &ast.ident;
29
168k
    let span = Span::call_site();
30
168k
    quote_into!(buf, span, {
31
        #[automatically_derived]
32
        impl #impl_generics #target for #ident #orig_ty_generics #where_clause
33
    });
34
168k
}
35
36
/// Implement `Display` for the given item.
37
20.3k
fn impl_display(ast: &DeriveInput, buf: &mut Buffer) {
38
20.3k
    let ident = &ast.ident;
39
20.3k
    let span = Span::call_site();
40
20.3k
    let msg =
41
20.3k
        format!(" Implement the [`format!()`][askama::helpers::std::format] trait for [`{ident}`]");
42
20.3k
    quote_into!(buf, span, {
43
        #[doc = #msg]
44
        ///
45
        /// Please be aware of the rendering performance notice in the [`Template`][askama::Template] trait.
46
    });
47
20.3k
    write_header(
48
20.3k
        ast,
49
20.3k
        buf,
50
20.3k
        quote_spanned!(span => askama::helpers::core::fmt::Display),
51
    );
52
20.3k
    quote_into!(buf, span, {
53
        {
54
            #[inline]
55
            fn fmt(
56
                &self,
57
                f: &mut askama::helpers::core::fmt::Formatter<'_>,
58
            ) -> askama::helpers::core::fmt::Result {
59
                askama::Template::render_into(self, f)
60
                    .map_err(|_| askama::helpers::core::fmt::Error)
61
            }
62
        }
63
    });
64
20.3k
}
65
66
/// Implement `FastWritable` for the given item.
67
20.3k
fn impl_fast_writable(ast: &DeriveInput, buf: &mut Buffer) {
68
20.3k
    let span = Span::call_site();
69
20.3k
    write_header(ast, buf, quote_spanned!(span => askama::FastWritable));
70
20.3k
    quote_into!(buf, span, {
71
        {
72
            #[inline]
73
            fn write_into(
74
                &self,
75
                dest: &mut dyn askama::helpers::core::fmt::Write,
76
                values: &dyn askama::Values,
77
            ) -> askama::Result<()> {
78
                askama::Template::render_into_with_values(self, dest, values)
79
            }
80
        }
81
    });
82
20.3k
}
83
84
#[derive(Debug)]
85
pub(crate) struct Buffer {
86
    // The buffer to generate the code into
87
    buf: TokenStream,
88
    discard: bool,
89
    string_literals: Vec<(String, proc_macro2::Span)>,
90
}
91
92
impl Display for Buffer {
93
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94
0
        f.write_str(self.buf.to_string().as_str())
95
0
    }
96
}
97
98
impl ToTokens for Buffer {
99
    #[inline]
100
323k
    fn to_tokens(&self, tokens: &mut TokenStream) {
101
323k
        self.buf.to_tokens(tokens);
102
323k
    }
103
104
    #[inline]
105
48.9k
    fn to_token_stream(&self) -> TokenStream {
106
48.9k
        self.buf.clone()
107
48.9k
    }
108
109
    #[inline]
110
0
    fn into_token_stream(self) -> TokenStream {
111
0
        self.buf
112
0
    }
113
}
114
115
impl IntoIterator for Buffer {
116
    type Item = <TokenStream as IntoIterator>::Item;
117
    type IntoIter = <TokenStream as IntoIterator>::IntoIter;
118
119
    #[inline]
120
0
    fn into_iter(self) -> Self::IntoIter {
121
0
        self.buf.into_iter()
122
0
    }
123
}
124
125
impl Buffer {
126
920k
    pub(crate) fn new() -> Self {
127
920k
        Self {
128
920k
            buf: TokenStream::new(),
129
920k
            discard: false,
130
920k
            string_literals: Vec::new(),
131
920k
        }
132
920k
    }
133
134
3.15M
    fn handle_str_lit(&mut self) {
135
3.15M
        let Some((literal, span)) = take(&mut self.string_literals).into_iter().reduce(
136
0
            |(mut acc_lit, acc_span), (literal, span)| {
137
0
                acc_lit.push_str(&literal);
138
0
                (acc_lit, acc_span.join(span).unwrap_or(acc_span))
139
0
            },
140
        ) else {
141
2.89M
            return;
142
        };
143
144
257k
        let mut literal: Literal = format!(r#""{literal}""#).parse().unwrap();
145
257k
        literal.set_span(span);
146
257k
        let askama_writer = crate::var_writer();
147
257k
        self.buf.extend(quote_spanned! {
148
257k
            span =>
149
            #askama_writer.write_str(#literal)?;
150
        });
151
3.15M
    }
152
153
258k
    pub(crate) fn write_str_lit(&mut self, literal: String, span: proc_macro2::Span) {
154
258k
        if self.discard || literal.is_empty() {
155
1.02k
            return;
156
257k
        }
157
257k
        self.string_literals.push((literal, span));
158
258k
    }
159
160
    #[inline]
161
544k
    pub(crate) fn into_token_stream(mut self) -> TokenStream {
162
544k
        self.handle_str_lit();
163
544k
        self.buf
164
544k
    }
165
166
    #[inline]
167
1.20M
    pub(crate) fn is_discard(&self) -> bool {
168
1.20M
        self.discard
169
1.20M
    }
170
171
    #[inline]
172
241k
    pub(crate) fn set_discard(&mut self, discard: bool) {
173
241k
        self.discard = discard;
174
241k
    }
175
176
    #[inline]
177
1.49M
    pub(crate) fn write_tokens(&mut self, src: impl IntoIterator<Item = TokenTree>) {
178
1.49M
        if self.discard {
179
0
            return;
180
1.49M
        }
181
1.49M
        self.handle_str_lit();
182
1.49M
        self.buf.extend(src);
183
1.49M
    }
184
185
379k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
379k
    where
187
379k
        F: Fn(proc_macro2::Span) -> T,
188
379k
        T: syn::token::Token + ToTokens,
189
    {
190
379k
        if self.discard {
191
0
            return;
192
379k
        }
193
379k
        self.handle_str_lit();
194
379k
        token(span).to_tokens(&mut self.buf);
195
379k
    }
Unexecuted instantiation: <askama_derive::integration::Buffer>::write_token::<syn::token::Eq<proc_macro2::Span>, syn::token::Eq>
<askama_derive::integration::Buffer>::write_token::<syn::token::Gt<proc_macro2::Span>, syn::token::Gt>
Line
Count
Source
185
6.85k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
6.85k
    where
187
6.85k
        F: Fn(proc_macro2::Span) -> T,
188
6.85k
        T: syn::token::Token + ToTokens,
189
    {
190
6.85k
        if self.discard {
191
0
            return;
192
6.85k
        }
193
6.85k
        self.handle_str_lit();
194
6.85k
        token(span).to_tokens(&mut self.buf);
195
6.85k
    }
Unexecuted instantiation: <askama_derive::integration::Buffer>::write_token::<syn::token::If<proc_macro2::Span>, syn::token::If>
<askama_derive::integration::Buffer>::write_token::<syn::token::Lt<proc_macro2::Span>, syn::token::Lt>
Line
Count
Source
185
6.85k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
6.85k
    where
187
6.85k
        F: Fn(proc_macro2::Span) -> T,
188
6.85k
        T: syn::token::Token + ToTokens,
189
    {
190
6.85k
        if self.discard {
191
0
            return;
192
6.85k
        }
193
6.85k
        self.handle_str_lit();
194
6.85k
        token(span).to_tokens(&mut self.buf);
195
6.85k
    }
Unexecuted instantiation: <askama_derive::integration::Buffer>::write_token::<syn::token::Or<proc_macro2::Span>, syn::token::Or>
<askama_derive::integration::Buffer>::write_token::<syn::token::And<proc_macro2::Span>, syn::token::And>
Line
Count
Source
185
5.05k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
5.05k
    where
187
5.05k
        F: Fn(proc_macro2::Span) -> T,
188
5.05k
        T: syn::token::Token + ToTokens,
189
    {
190
5.05k
        if self.discard {
191
0
            return;
192
5.05k
        }
193
5.05k
        self.handle_str_lit();
194
5.05k
        token(span).to_tokens(&mut self.buf);
195
5.05k
    }
<askama_derive::integration::Buffer>::write_token::<syn::token::Dot<proc_macro2::Span>, syn::token::Dot>
Line
Count
Source
185
205k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
205k
    where
187
205k
        F: Fn(proc_macro2::Span) -> T,
188
205k
        T: syn::token::Token + ToTokens,
189
    {
190
205k
        if self.discard {
191
0
            return;
192
205k
        }
193
205k
        self.handle_str_lit();
194
205k
        token(span).to_tokens(&mut self.buf);
195
205k
    }
<askama_derive::integration::Buffer>::write_token::<syn::token::Let<proc_macro2::Span>, syn::token::Let>
Line
Count
Source
185
27.4k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
27.4k
    where
187
27.4k
        F: Fn(proc_macro2::Span) -> T,
188
27.4k
        T: syn::token::Token + ToTokens,
189
    {
190
27.4k
        if self.discard {
191
0
            return;
192
27.4k
        }
193
27.4k
        self.handle_str_lit();
194
27.4k
        token(span).to_tokens(&mut self.buf);
195
27.4k
    }
<askama_derive::integration::Buffer>::write_token::<syn::token::Mut<proc_macro2::Span>, syn::token::Mut>
Line
Count
Source
185
7.13k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
7.13k
    where
187
7.13k
        F: Fn(proc_macro2::Span) -> T,
188
7.13k
        T: syn::token::Token + ToTokens,
189
    {
190
7.13k
        if self.discard {
191
0
            return;
192
7.13k
        }
193
7.13k
        self.handle_str_lit();
194
7.13k
        token(span).to_tokens(&mut self.buf);
195
7.13k
    }
Unexecuted instantiation: <askama_derive::integration::Buffer>::write_token::<syn::token::Not<proc_macro2::Span>, syn::token::Not>
Unexecuted instantiation: <askama_derive::integration::Buffer>::write_token::<syn::token::Else<proc_macro2::Span>, syn::token::Else>
<askama_derive::integration::Buffer>::write_token::<syn::token::Semi<proc_macro2::Span>, syn::token::Semi>
Line
Count
Source
185
11.0k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
11.0k
    where
187
11.0k
        F: Fn(proc_macro2::Span) -> T,
188
11.0k
        T: syn::token::Token + ToTokens,
189
    {
190
11.0k
        if self.discard {
191
0
            return;
192
11.0k
        }
193
11.0k
        self.handle_str_lit();
194
11.0k
        token(span).to_tokens(&mut self.buf);
195
11.0k
    }
Unexecuted instantiation: <askama_derive::integration::Buffer>::write_token::<syn::token::Star<proc_macro2::Span>, syn::token::Star>
<askama_derive::integration::Buffer>::write_token::<syn::token::Colon<proc_macro2::Span>, syn::token::Colon>
Line
Count
Source
185
2.77k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
2.77k
    where
187
2.77k
        F: Fn(proc_macro2::Span) -> T,
188
2.77k
        T: syn::token::Token + ToTokens,
189
    {
190
2.77k
        if self.discard {
191
0
            return;
192
2.77k
        }
193
2.77k
        self.handle_str_lit();
194
2.77k
        token(span).to_tokens(&mut self.buf);
195
2.77k
    }
<askama_derive::integration::Buffer>::write_token::<syn::token::Comma<proc_macro2::Span>, syn::token::Comma>
Line
Count
Source
185
23.7k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
23.7k
    where
187
23.7k
        F: Fn(proc_macro2::Span) -> T,
188
23.7k
        T: syn::token::Token + ToTokens,
189
    {
190
23.7k
        if self.discard {
191
0
            return;
192
23.7k
        }
193
23.7k
        self.handle_str_lit();
194
23.7k
        token(span).to_tokens(&mut self.buf);
195
23.7k
    }
<askama_derive::integration::Buffer>::write_token::<syn::token::DotDot<proc_macro2::Span>, syn::token::DotDot>
Line
Count
Source
185
939
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
939
    where
187
939
        F: Fn(proc_macro2::Span) -> T,
188
939
        T: syn::token::Token + ToTokens,
189
    {
190
939
        if self.discard {
191
0
            return;
192
939
        }
193
939
        self.handle_str_lit();
194
939
        token(span).to_tokens(&mut self.buf);
195
939
    }
<askama_derive::integration::Buffer>::write_token::<syn::token::PathSep<proc_macro2::Span>, syn::token::PathSep>
Line
Count
Source
185
82.1k
    pub(crate) fn write_token<F, T>(&mut self, token: F, span: proc_macro2::Span)
186
82.1k
    where
187
82.1k
        F: Fn(proc_macro2::Span) -> T,
188
82.1k
        T: syn::token::Token + ToTokens,
189
    {
190
82.1k
        if self.discard {
191
0
            return;
192
82.1k
        }
193
82.1k
        self.handle_str_lit();
194
82.1k
        token(span).to_tokens(&mut self.buf);
195
82.1k
    }
196
197
81.8k
    pub(crate) fn write_literal(&mut self, repr: &str, span: proc_macro2::Span) {
198
81.8k
        if self.discard {
199
0
            return;
200
81.8k
        }
201
81.8k
        self.handle_str_lit();
202
81.8k
        self._write_literal_repr(repr, span);
203
81.8k
    }
204
205
81.8k
    fn _write_literal_repr(&mut self, repr: &str, span: proc_macro2::Span) {
206
81.8k
        let mut literal: Literal = repr.parse().unwrap();
207
81.8k
        literal.set_span(span);
208
81.8k
        literal.to_tokens(&mut self.buf);
209
81.8k
    }
210
211
641k
    pub(crate) fn write_field(&mut self, name: &str, span: proc_macro2::Span) {
212
641k
        if self.discard {
213
0
            return;
214
641k
        }
215
641k
        self.handle_str_lit();
216
641k
        self.buf.extend(field_new(name, span));
217
641k
    }
218
219
4.24k
    pub(crate) fn write_separated_path(&mut self, ctx: &Context<'_>, path: &[PathComponent<'_>]) {
220
4.24k
        if self.discard {
221
0
            return;
222
4.24k
        }
223
224
4.24k
        self.handle_str_lit();
225
8.86k
        for (idx, item) in path.iter().enumerate() {
226
8.86k
            let span = ctx.span_for_node(item.name.span());
227
8.86k
            if idx > 0 {
228
4.81k
                Token![::](span).to_tokens(&mut self.buf);
229
4.81k
            }
230
8.86k
            if !item.name.is_empty() {
231
8.32k
                Ident::new(*item.name, span).to_tokens(&mut self.buf);
232
8.32k
            }
233
        }
234
4.24k
    }
235
236
0
    pub(crate) fn write_escaped_str(&mut self, s: &str, span: proc_macro2::Span) {
237
0
        if self.discard {
238
0
            return;
239
0
        }
240
0
        self.handle_str_lit();
241
0
        LitStr::new(s, span).to_tokens(&mut self.buf);
242
0
    }
243
244
8.93k
    pub(crate) fn clear(&mut self) {
245
8.93k
        self.string_literals.clear();
246
8.93k
        self.buf = TokenStream::new();
247
8.93k
    }
248
249
0
    pub(crate) fn write_buf(
250
0
        &mut self,
251
0
        Buffer {
252
0
            buf,
253
0
            string_literals,
254
0
            ..
255
0
        }: Buffer,
256
0
    ) {
257
0
        if self.discard {
258
0
            return;
259
0
        }
260
0
        self.handle_str_lit();
261
0
        self.buf.extend(buf);
262
0
        self.string_literals.extend(string_literals);
263
0
    }
264
}
265
266
/// Similar to `write!(dest, "{src:?}")`, but only escapes the strictly needed characters,
267
/// and without the surrounding `"…"` quotation marks.
268
616k
pub(crate) fn string_escape(dest: &mut String, src: &str) {
269
    // SAFETY: we will only push valid str slices
270
616k
    let dest = unsafe { dest.as_mut_vec() };
271
616k
    let src = src.as_bytes();
272
616k
    let mut last = 0;
273
274
    // According to <https://doc.rust-lang.org/reference/tokens.html#string-literals>, every
275
    // character is valid except `" \ IsolatedCR`. We don't test if the `\r` is isolated or not,
276
    // but always escape it.
277
764k
    for x in memchr::memchr3_iter(b'\\', b'"', b'\r', src) {
278
764k
        dest.extend(&src[last..x]);
279
764k
        dest.extend(match src[x] {
280
23.5k
            b'\\' => br"\\",
281
661k
            b'\"' => br#"\""#,
282
80.3k
            _ => br"\r",
283
        });
284
764k
        last = x + 1;
285
    }
286
616k
    dest.extend(&src[last..]);
287
616k
}
288
289
// FIXME: Add span
290
7.58k
pub(crate) fn build_template_enum(
291
7.58k
    buf: &mut Buffer,
292
7.58k
    enum_ast: &DeriveInput,
293
7.58k
    mut enum_args: Option<PartialTemplateArgs>,
294
7.58k
    vars_args: Vec<Option<PartialTemplateArgs>>,
295
7.58k
    has_default_impl: bool,
296
7.58k
) -> Result<SizeHint, CompileError> {
297
7.58k
    let Data::Enum(enum_data) = &enum_ast.data else {
298
0
        unreachable!();
299
    };
300
7.58k
    let span = enum_ast.span();
301
302
7.58k
    impl_everything(enum_ast, buf);
303
304
7.58k
    let enum_id = &enum_ast.ident;
305
7.58k
    let enum_span = enum_id.span();
306
7.58k
    let lifetime = Lifetime::new(&format!("'__Askama_{enum_id}"), enum_span);
307
308
7.58k
    let mut generics = enum_ast.generics.clone();
309
7.58k
    if generics.lt_token.is_none() {
310
7.58k
        generics.lt_token = Some(Token![<](enum_span));
311
7.58k
    }
312
7.58k
    if generics.gt_token.is_none() {
313
7.58k
        generics.gt_token = Some(Token![>](enum_span));
314
7.58k
    }
315
7.58k
    generics
316
7.58k
        .params
317
7.58k
        .insert(0, GenericParam::Lifetime(LifetimeParam::new(lifetime)));
318
319
7.58k
    let mut biggest_size_hint = SizeHint::EMPTY;
320
7.58k
    let mut render_into_arms = TokenStream::new();
321
7.58k
    let mut size_hint_arms = TokenStream::new();
322
115k
    for (var, var_args) in enum_data.variants.iter().zip(vars_args) {
323
115k
        let Some(mut var_args) = var_args else {
324
13.3k
            continue;
325
        };
326
327
102k
        let (var_ast, deref_impl) = type_for_enum_variant(enum_ast, &generics, var);
328
102k
        quote_into!(buf, span, { #var_ast #deref_impl });
329
330
        // not inherited: template, meta_docs, block, print
331
102k
        if let Some(enum_args) = &mut enum_args {
332
101k
            set_default(&mut var_args, enum_args, |v| &mut v.source);
333
101k
            set_default(&mut var_args, enum_args, |v| &mut v.escape);
334
101k
            set_default(&mut var_args, enum_args, |v| &mut v.ext);
335
101k
            set_default(&mut var_args, enum_args, |v| &mut v.syntax);
336
101k
            set_default(&mut var_args, enum_args, |v| &mut v.config);
337
101k
            set_default(&mut var_args, enum_args, |v| &mut v.whitespace);
338
998
        }
339
102k
        let size_hint = biggest_size_hint.max(build_template_item(
340
102k
            buf,
341
102k
            &var_ast,
342
102k
            Some(enum_ast),
343
102k
            &TemplateArgs::from_partial(&var_ast, Some(var_args))?,
344
102k
            TmplKind::Variant,
345
558
        )?);
346
101k
        biggest_size_hint = biggest_size_hint.max(size_hint);
347
348
101k
        variant_as_arm(
349
101k
            &var_ast,
350
101k
            var,
351
101k
            size_hint,
352
101k
            &mut render_into_arms,
353
101k
            &mut size_hint_arms,
354
        );
355
    }
356
7.02k
    let print_code = enum_args.as_ref().is_some_and(|args| {
357
6.77k
        args.print
358
6.77k
            .is_some_and(|print| print == Print::Code || print == Print::All)
359
6.77k
    });
360
361
7.02k
    if has_default_impl {
362
5.78k
        let size_hint = build_template_item(
363
5.78k
            buf,
364
5.78k
            enum_ast,
365
5.78k
            None,
366
5.78k
            &TemplateArgs::from_partial(enum_ast, enum_args)?,
367
5.78k
            TmplKind::Variant,
368
118
        )?;
369
5.66k
        biggest_size_hint = biggest_size_hint.max(size_hint);
370
371
5.66k
        let var_arg = crate::var_arg();
372
5.66k
        let var_writer = crate::var_writer();
373
5.66k
        let var_values = crate::var_values();
374
5.66k
        render_into_arms.extend(quote_spanned! {
375
5.66k
            span =>
376
            ref #var_arg => {
377
                <_ as askama::helpers::EnumVariantTemplate>::render_into_with_values(
378
                    #var_arg,
379
                    #var_writer,
380
                    #var_values,
381
                )
382
            }
383
        });
384
5.66k
        size_hint_arms.extend(quote_spanned! {
385
5.66k
            span =>
386
            _ => {
387
                #size_hint
388
            }
389
        });
390
1.24k
    }
391
392
6.90k
    let mut methods = TokenStream::new();
393
6.90k
    let var_writer = crate::var_writer();
394
6.90k
    let var_values = crate::var_values();
395
6.90k
    methods.extend(quote_spanned!(span =>
396
        fn render_into_with_values(
397
            &self,
398
            #var_writer: &mut dyn askama::helpers::core::fmt::Write,
399
            #var_values: &dyn askama::Values,
400
        ) -> askama::Result<()> {
401
            match *self {
402
                #render_into_arms
403
            }
404
        }
405
    ));
406
407
    #[cfg(feature = "alloc")]
408
6.90k
    methods.extend(quote_spanned!(
409
6.90k
        span =>
410
        fn render_with_values(
411
            &self,
412
            #var_values: &dyn askama::Values,
413
        ) -> askama::Result<askama::helpers::alloc::string::String> {
414
            let size_hint = match self {
415
                #size_hint_arms
416
            };
417
            let mut buf = askama::helpers::alloc::string::String::new();
418
            let _ = buf.try_reserve(size_hint);
419
            self.render_into_with_values(&mut buf, #var_values)?;
420
            askama::Result::Ok(buf)
421
        }
422
    ));
423
424
6.90k
    write_header(enum_ast, buf, quote_spanned!(span => askama::Template));
425
6.90k
    quote_into!(buf, span, {
426
        {
427
            #methods
428
            const SIZE_HINT: askama::helpers::core::primitive::usize = #biggest_size_hint;
429
        }
430
    });
431
6.90k
    if print_code {
432
0
        eprintln!("{buf}");
433
6.90k
    }
434
6.90k
    Ok(biggest_size_hint)
435
7.58k
}
436
437
609k
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
438
609k
where
439
609k
    T: Clone,
440
609k
    A: FnMut(&mut S) -> &mut Option<T>,
441
{
442
609k
    let dest = access(dest);
443
609k
    if dest.is_none()
444
598k
        && let Some(parent) = access(parent)
445
192k
    {
446
192k
        *dest = Some(parent.clone());
447
416k
    }
448
609k
}
askama_derive::integration::set_default::<askama_derive::input::PartialTemplateArgs, askama_derive::input::PartialTemplateArgsSource, askama_derive::integration::build_template_enum::{closure#0}>
Line
Count
Source
437
101k
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
438
101k
where
439
101k
    T: Clone,
440
101k
    A: FnMut(&mut S) -> &mut Option<T>,
441
{
442
101k
    let dest = access(dest);
443
101k
    if dest.is_none()
444
93.3k
        && let Some(parent) = access(parent)
445
93.3k
    {
446
93.3k
        *dest = Some(parent.clone());
447
93.3k
    }
448
101k
}
askama_derive::integration::set_default::<askama_derive::input::PartialTemplateArgs, syn::lit::LitStr, askama_derive::integration::build_template_enum::{closure#2}>
Line
Count
Source
437
101k
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
438
101k
where
439
101k
    T: Clone,
440
101k
    A: FnMut(&mut S) -> &mut Option<T>,
441
{
442
101k
    let dest = access(dest);
443
101k
    if dest.is_none()
444
99.4k
        && let Some(parent) = access(parent)
445
99.3k
    {
446
99.3k
        *dest = Some(parent.clone());
447
99.3k
    }
448
101k
}
askama_derive::integration::set_default::<askama_derive::input::PartialTemplateArgs, syn::lit::LitStr, askama_derive::integration::build_template_enum::{closure#3}>
Line
Count
Source
437
101k
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
438
101k
where
439
101k
    T: Clone,
440
101k
    A: FnMut(&mut S) -> &mut Option<T>,
441
{
442
101k
    let dest = access(dest);
443
101k
    if dest.is_none()
444
101k
        && let Some(parent) = access(parent)
445
0
    {
446
0
        *dest = Some(parent.clone());
447
101k
    }
448
101k
}
askama_derive::integration::set_default::<askama_derive::input::PartialTemplateArgs, syn::lit::LitStr, askama_derive::integration::build_template_enum::{closure#4}>
Line
Count
Source
437
101k
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
438
101k
where
439
101k
    T: Clone,
440
101k
    A: FnMut(&mut S) -> &mut Option<T>,
441
{
442
101k
    let dest = access(dest);
443
101k
    if dest.is_none()
444
101k
        && let Some(parent) = access(parent)
445
0
    {
446
0
        *dest = Some(parent.clone());
447
101k
    }
448
101k
}
askama_derive::integration::set_default::<askama_derive::input::PartialTemplateArgs, syn::lit::LitStr, askama_derive::integration::build_template_enum::{closure#1}>
Line
Count
Source
437
101k
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
438
101k
where
439
101k
    T: Clone,
440
101k
    A: FnMut(&mut S) -> &mut Option<T>,
441
{
442
101k
    let dest = access(dest);
443
101k
    if dest.is_none()
444
101k
        && let Some(parent) = access(parent)
445
0
    {
446
0
        *dest = Some(parent.clone());
447
101k
    }
448
101k
}
askama_derive::integration::set_default::<askama_derive::input::PartialTemplateArgs, askama_parser::node::Whitespace, askama_derive::integration::build_template_enum::{closure#5}>
Line
Count
Source
437
101k
fn set_default<S, T, A>(dest: &mut S, parent: &mut S, mut access: A)
438
101k
where
439
101k
    T: Clone,
440
101k
    A: FnMut(&mut S) -> &mut Option<T>,
441
{
442
101k
    let dest = access(dest);
443
101k
    if dest.is_none()
444
101k
        && let Some(parent) = access(parent)
445
0
    {
446
0
        *dest = Some(parent.clone());
447
101k
    }
448
101k
}
449
450
/// Generates a `struct` to contain the data of an enum variant
451
102k
fn type_for_enum_variant(
452
102k
    enum_ast: &DeriveInput,
453
102k
    enum_generics: &Generics,
454
102k
    var: &Variant,
455
102k
) -> (DeriveInput, TokenStream) {
456
102k
    let enum_id = &enum_ast.ident;
457
102k
    let lt = enum_generics.params.first().unwrap();
458
102k
    let (_, ty_generics, _) = enum_ast.generics.split_for_impl();
459
102k
    let mut generics = enum_ast.generics.clone();
460
102k
    generics.params.insert(0, lt.clone());
461
462
102k
    let id = &var.ident;
463
102k
    let span = id.span();
464
102k
    let id = Ident::new(&format!("__Askama__{enum_id}__{id}"), span);
465
466
102k
    let field: Type = parse_quote! {
467
        &#lt #enum_id #ty_generics
468
    };
469
102k
    let (impl_generics, ty_generics, _) = generics.split_for_impl();
470
471
102k
    let (fields, deref_impl) = match &var.fields {
472
4.27k
        Fields::Named(fields) => {
473
4.27k
            let mut fields = fields.clone();
474
4.27k
            for f in fields.named.iter_mut() {
475
426
                let ty = &f.ty;
476
426
                f.ty = parse_quote!(&#lt #ty);
477
426
            }
478
4.27k
            let field_name = Ident::new(&format!("__Askama__{enum_id}__phantom"), span);
479
4.27k
            fields.named.push(parse_quote!(#field_name: #field));
480
4.27k
            let deref_impl = quote_spanned! {
481
4.27k
                span=>
482
                impl #impl_generics askama::helpers::core::ops::Deref for #id #ty_generics {
483
                    type Target = #field;
484
485
                    fn deref(&self) -> &Self::Target {
486
                        &self.#field_name
487
                    }
488
                }
489
            };
490
4.27k
            (Fields::Named(fields), deref_impl)
491
        }
492
2.74k
        Fields::Unnamed(fields) => {
493
2.74k
            let mut fields = fields.clone();
494
129k
            for f in fields.unnamed.iter_mut() {
495
129k
                let ty = &f.ty;
496
129k
                f.ty = parse_quote!(&#lt #ty);
497
129k
            }
498
2.74k
            fields.unnamed.push(parse_quote!(#field));
499
2.74k
            let idx = TokenStream::from_str(&format!("self.{}", fields.unnamed.len() - 1)).unwrap();
500
2.74k
            let deref_impl = quote_spanned! {
501
2.74k
                span=>
502
                impl #impl_generics askama::helpers::core::ops::Deref for #id #ty_generics {
503
                    type Target = #field;
504
505
                    fn deref(&self) -> &Self::Target {
506
                        &#idx
507
                    }
508
                }
509
            };
510
2.74k
            (Fields::Unnamed(fields), deref_impl)
511
        }
512
        Fields::Unit => {
513
95.4k
            let fields = Fields::Unnamed(parse_quote!((#field)));
514
95.4k
            let deref_impl = quote_spanned! {
515
95.4k
                span=>
516
                impl #impl_generics askama::helpers::core::ops::Deref for #id #ty_generics {
517
                    type Target = #field;
518
519
                    fn deref(&self) -> &Self::Target {
520
                        &self.0
521
                    }
522
                }
523
            };
524
95.4k
            (fields, deref_impl)
525
        }
526
    };
527
102k
    let semicolon = match &var.fields {
528
4.27k
        Fields::Named(_) => None,
529
98.2k
        _ => Some(Token![;](span)),
530
    };
531
532
102k
    let span = enum_ast.span().resolved_at(proc_macro2::Span::call_site());
533
102k
    (
534
102k
        syn::parse_quote_spanned! {
535
102k
            span=>
536
102k
            #[askama::helpers::core::prelude::rust_2021::derive(
537
102k
                askama::helpers::core::prelude::rust_2021::Clone,
538
102k
                askama::helpers::core::prelude::rust_2021::Copy,
539
102k
            )]
540
102k
            struct #id #enum_generics #fields #semicolon
541
102k
        },
542
102k
        deref_impl,
543
102k
    )
544
102k
}
545
546
/// Generates a `match` arm for an `enum` variant, that calls `<_ as EnumVariantTemplate>::render_into()`
547
/// for that type and data
548
101k
fn variant_as_arm(
549
101k
    var_ast: &DeriveInput,
550
101k
    var: &Variant,
551
101k
    size_hint: SizeHint,
552
101k
    render_into_arms: &mut TokenStream,
553
101k
    size_hint_arms: &mut TokenStream,
554
101k
) {
555
101k
    let var_id = &var_ast.ident;
556
101k
    let ident = &var.ident;
557
101k
    let span = ident.span();
558
559
101k
    let generics = var_ast.generics.clone();
560
101k
    let (_, ty_generics, _) = generics.split_for_impl();
561
101k
    let ty_generics: TokenStream = ty_generics
562
101k
        .as_turbofish()
563
101k
        .to_token_stream()
564
101k
        .into_iter()
565
101k
        .enumerate()
566
611k
        .map(|(idx, token)| match idx {
567
            // 0 1 2 3 4   =>   : : < ' __Askama_Foo
568
101k
            4 => TokenTree::Ident(Ident::new("_", span)),
569
509k
            _ => token,
570
611k
        })
571
101k
        .collect();
572
573
101k
    let Data::Struct(ast_data) = &var_ast.data else {
574
0
        unreachable!();
575
    };
576
101k
    let mut src = TokenStream::new();
577
101k
    let mut this = TokenStream::new();
578
101k
    match &var.fields {
579
4.14k
        Fields::Named(fields) => {
580
4.14k
            for (idx, field) in fields.named.iter().enumerate() {
581
256
                let arg = Ident::new(&format!("__askama_arg_{idx}"), field.span());
582
256
                let id = field.ident.as_ref().unwrap();
583
256
                src.extend(quote_spanned!(span => #id: ref #arg,));
584
256
                this.extend(quote_spanned!(span => #id: #arg,));
585
256
            }
586
587
4.14k
            let phantom = match &ast_data.fields {
588
4.14k
                Fields::Named(fields) => fields
589
4.14k
                    .named
590
4.14k
                    .iter()
591
4.14k
                    .next_back()
592
4.14k
                    .unwrap()
593
4.14k
                    .ident
594
4.14k
                    .as_ref()
595
4.14k
                    .unwrap(),
596
0
                Fields::Unnamed(_) | Fields::Unit => unreachable!(),
597
            };
598
4.14k
            this.extend(quote_spanned!(
599
4.14k
                span => #phantom: &self,
600
            ));
601
        }
602
603
2.65k
        Fields::Unnamed(fields) => {
604
129k
            for (idx, field) in fields.unnamed.iter().enumerate() {
605
129k
                let span = field.ident.span();
606
129k
                let arg = Ident::new(&format!("__askama_arg_{idx}"), span);
607
129k
                let idx = syn::LitInt::new(&format!("{idx}"), span);
608
129k
                src.extend(quote_spanned!(span => #idx: ref #arg,));
609
129k
                this.extend(quote_spanned!(span => #idx: #arg,));
610
129k
            }
611
2.65k
            let idx = syn::LitInt::new(&format!("{}", fields.unnamed.len()), span);
612
2.65k
            this.extend(quote_spanned!(span => #idx: &self,));
613
        }
614
615
95.1k
        Fields::Unit => {
616
95.1k
            this.extend(quote_spanned!(span => 0: &self,));
617
95.1k
        }
618
    };
619
101k
    let var_writer = crate::var_writer();
620
101k
    let var_values = crate::var_values();
621
101k
    render_into_arms.extend(quote_spanned! {
622
101k
        span =>
623
        Self :: #ident { #src } => {
624
            <_ as askama::helpers::EnumVariantTemplate>::render_into_with_values(
625
                & #var_id #ty_generics { #this },
626
                #var_writer,
627
                #var_values,
628
            )
629
        }
630
    });
631
101k
    size_hint_arms.extend(quote_spanned! {
632
101k
        span =>
633
        Self :: #ident { .. } => {
634
            #size_hint
635
        }
636
    });
637
101k
}