@@ -12,7 +12,10 @@ use parser::node::{Macro, Whitespace};
12
12
use parser:: {
13
13
CharLit , Expr , FloatKind , IntKind , MAX_RUST_KEYWORD_LEN , Num , RUST_KEYWORDS , StrLit , WithSpan ,
14
14
} ;
15
+ use proc_macro2:: Span ;
16
+ use quote:: quote_spanned;
15
17
use rustc_hash:: FxBuildHasher ;
18
+ use syn:: { GenericParam , Ident , Lifetime , LifetimeParam , Token } ;
16
19
17
20
use crate :: heritage:: { Context , Heritage } ;
18
21
use crate :: html:: write_escaped_str;
@@ -25,7 +28,7 @@ pub(crate) fn template_to_string(
25
28
input : & TemplateInput < ' _ > ,
26
29
contexts : & HashMap < & Arc < Path > , Context < ' _ > , FxBuildHasher > ,
27
30
heritage : Option < & Heritage < ' _ , ' _ > > ,
28
- tmpl_kind : TmplKind ,
31
+ tmpl_kind : TmplKind < ' _ > ,
29
32
) -> Result < usize , CompileError > {
30
33
let generator = Generator :: new (
31
34
input,
@@ -50,11 +53,13 @@ pub(crate) fn template_to_string(
50
53
}
51
54
52
55
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
53
- pub ( crate ) enum TmplKind {
56
+ pub ( crate ) enum TmplKind < ' a > {
54
57
/// [`rinja::Template`]
55
58
Struct ,
56
59
/// [`rinja::helpers::EnumVariantTemplate`]
57
60
Variant ,
61
+ /// Used in `blocks` implementation
62
+ Block ( & ' a str ) ,
58
63
}
59
64
60
65
struct Generator < ' a , ' h > {
@@ -113,13 +118,14 @@ impl<'a, 'h> Generator<'a, 'h> {
113
118
fn impl_template (
114
119
mut self ,
115
120
buf : & mut Buffer ,
116
- tmpl_kind : TmplKind ,
121
+ tmpl_kind : TmplKind < ' a > ,
117
122
) -> Result < usize , CompileError > {
118
123
let ctx = & self . contexts [ & self . input . path ] ;
119
124
120
125
let target = match tmpl_kind {
121
126
TmplKind :: Struct => "rinja::Template" ,
122
127
TmplKind :: Variant => "rinja::helpers::EnumVariantTemplate" ,
128
+ TmplKind :: Block ( trait_name) => trait_name,
123
129
} ;
124
130
write_header ( self . input . ast , buf, target) ;
125
131
buf. write (
@@ -170,9 +176,147 @@ impl<'a, 'h> Generator<'a, 'h> {
170
176
}
171
177
172
178
buf. write ( '}' ) ;
179
+
180
+ for ( block, span) in self . input . blocks {
181
+ self . impl_block ( buf, block, span) ?;
182
+ }
183
+
173
184
Ok ( size_hint)
174
185
}
175
186
187
+ fn impl_block ( & self , buf : & mut Buffer , block : & str , span : & Span ) -> Result < ( ) , CompileError > {
188
+ // RATIONALE: `*self` must be the input type, implementation details should not leak:
189
+ // - impl Self { fn as_block(self) } ->
190
+ // - struct __Rinja__Self__as__block__Wrapper { this: self } ->
191
+ // - impl Template for __Rinja__Self__as__block__Wrapper { fn render_into_with_values() } ->
192
+ // - impl __Rinja__Self__as__block for Self { render_into_with_values() }
193
+
194
+ let span = * span;
195
+ buf. write (
196
+ "\
197
+ #[allow(missing_docs, non_camel_case_types, non_snake_case, unreachable_pub)]\
198
+ const _: () = {",
199
+ ) ;
200
+
201
+ let ident = & self . input . ast . ident ;
202
+
203
+ let doc = format ! ( "A sub-template that renders only the block `{block}` of [`{ident}`]." ) ;
204
+ let method_name = format ! ( "as_{block}" ) ;
205
+ let trait_name = format ! ( "__Rinja__{ident}__as__{block}" ) ;
206
+ let wrapper_name = format ! ( "__Rinja__{ident}__as__{block}__Wrapper" ) ;
207
+ let self_lt_name = format ! ( "'__Rinja__{ident}__as__{block}__self" ) ;
208
+
209
+ let method_id = Ident :: new ( & method_name, span) ;
210
+ let trait_id = Ident :: new ( & trait_name, span) ;
211
+ let wrapper_id = Ident :: new ( & wrapper_name, span) ;
212
+ let self_lt = Lifetime :: new ( & self_lt_name, span) ;
213
+
214
+ // generics of the input with an additional lifetime to capture `self`
215
+ let mut wrapper_generics = self . input . ast . generics . clone ( ) ;
216
+ if wrapper_generics. lt_token . is_none ( ) {
217
+ wrapper_generics. lt_token = Some ( Token ! [ <] ( span) ) ;
218
+ wrapper_generics. gt_token = Some ( Token ! [ >] ( span) ) ;
219
+ }
220
+ wrapper_generics. params . insert (
221
+ 0 ,
222
+ GenericParam :: Lifetime ( LifetimeParam :: new ( self_lt. clone ( ) ) ) ,
223
+ ) ;
224
+
225
+ let ( impl_generics, ty_generics, where_clause) = self . input . ast . generics . split_for_impl ( ) ;
226
+ let ( wrapper_impl_generics, wrapper_ty_generics, wrapper_where_clause) =
227
+ wrapper_generics. split_for_impl ( ) ;
228
+
229
+ let input = TemplateInput {
230
+ block : Some ( ( block, span) ) ,
231
+ blocks : & [ ] ,
232
+ ..self . input . clone ( )
233
+ } ;
234
+ let size_hint = template_to_string (
235
+ buf,
236
+ & input,
237
+ self . contexts ,
238
+ self . heritage ,
239
+ TmplKind :: Block ( & trait_name) ,
240
+ ) ?;
241
+
242
+ buf. write ( quote_spanned ! {
243
+ span =>
244
+ pub trait #trait_id {
245
+ fn render_into_with_values<RinjaW >(
246
+ & self ,
247
+ writer: & mut RinjaW ,
248
+ values: & dyn rinja:: Values ,
249
+ ) -> rinja:: Result <( ) >
250
+ where
251
+ RinjaW :
252
+ rinja:: helpers:: core:: fmt:: Write + ?rinja:: helpers:: core:: marker:: Sized ;
253
+ }
254
+
255
+ impl #impl_generics #ident #ty_generics #where_clause {
256
+ #[ inline]
257
+ #[ doc = #doc]
258
+ pub fn #method_id( & self ) -> impl rinja:: Template + ' _ {
259
+ #wrapper_id {
260
+ this: self ,
261
+ }
262
+ }
263
+ }
264
+
265
+ #[ rinja:: helpers:: core:: prelude:: rust_2021:: derive(
266
+ rinja:: helpers:: core:: prelude:: rust_2021:: Clone ,
267
+ rinja:: helpers:: core:: prelude:: rust_2021:: Copy
268
+ ) ]
269
+ pub struct #wrapper_id #wrapper_generics #wrapper_where_clause {
270
+ this: & #self_lt #ident #ty_generics,
271
+ }
272
+
273
+ impl #wrapper_impl_generics rinja:: Template
274
+ for #wrapper_id #wrapper_ty_generics #wrapper_where_clause {
275
+ #[ inline]
276
+ fn render_into_with_values<RinjaW >(
277
+ & self ,
278
+ writer: & mut RinjaW ,
279
+ values: & dyn rinja:: Values
280
+ ) -> rinja:: Result <( ) >
281
+ where
282
+ RinjaW : rinja:: helpers:: core:: fmt:: Write + ?rinja:: helpers:: core:: marker:: Sized
283
+ {
284
+ <_ as #trait_id>:: render_into_with_values( self . this, writer, values)
285
+ }
286
+
287
+ const SIZE_HINT : rinja:: helpers:: core:: primitive:: usize = #size_hint;
288
+ }
289
+
290
+ // cannot use `crate::integrations::impl_fast_writable()` w/o cloning the struct
291
+ impl #wrapper_impl_generics rinja:: filters:: FastWritable
292
+ for #wrapper_id #wrapper_ty_generics #wrapper_where_clause {
293
+ #[ inline]
294
+ fn write_into<RinjaW >( & self , dest: & mut RinjaW ) -> rinja:: Result <( ) >
295
+ where
296
+ RinjaW : rinja:: helpers:: core:: fmt:: Write + ?rinja:: helpers:: core:: marker:: Sized
297
+ {
298
+ <_ as rinja:: Template >:: render_into( self , dest)
299
+ }
300
+ }
301
+
302
+ // cannot use `crate::integrations::impl_display()` w/o cloning the struct
303
+ impl #wrapper_impl_generics rinja:: helpers:: core:: fmt:: Display
304
+ for #wrapper_id #wrapper_ty_generics #wrapper_where_clause {
305
+ #[ inline]
306
+ fn fmt(
307
+ & self ,
308
+ f: & mut rinja:: helpers:: core:: fmt:: Formatter <' _>
309
+ ) -> rinja:: helpers:: core:: fmt:: Result {
310
+ <_ as rinja:: Template >:: render_into( self , f)
311
+ . map_err( |_| rinja:: helpers:: core:: fmt:: Error )
312
+ }
313
+ }
314
+ } ) ;
315
+
316
+ buf. write ( "};" ) ;
317
+ Ok ( ( ) )
318
+ }
319
+
176
320
fn is_var_defined ( & self , var_name : & str ) -> bool {
177
321
self . locals . get ( var_name) . is_some ( ) || self . input . fields . iter ( ) . any ( |f| f == var_name)
178
322
}
0 commit comments