/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.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); |
311 | 7.58k | } |
312 | 7.58k | if generics.gt_token.is_none() { |
313 | 7.58k | generics.gt_token = Some(Token); |
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), |
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 | } |