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