• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sunng87 / handlebars-rust / 20639563022

01 Jan 2026 01:41PM UTC coverage: 83.8% (+0.1%) from 83.673%
20639563022

Pull #732

github

web-flow
Merge 093bb2df0 into c349c3955
Pull Request #732: fix: correct partial-block render

35 of 36 new or added lines in 2 files covered. (97.22%)

1 existing line in 1 file now uncovered.

1676 of 2000 relevant lines covered (83.8%)

6.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.08
/src/partial.rs
1
use std::collections::{HashMap, VecDeque};
2

3
use serde_json::Value as Json;
4

5
use crate::block::BlockContext;
6
use crate::context::{merge_json, Context};
7
use crate::error::RenderError;
8
use crate::output::Output;
9
use crate::registry::Registry;
10
use crate::render::{Decorator, Evaluable, RenderContext, Renderable};
11
use crate::template::Template;
12
use crate::{Path, RenderErrorReason, StringOutput};
13

14
pub(crate) const PARTIAL_BLOCK: &str = "@partial-block";
15

16
fn find_partial<'reg: 'rc, 'rc>(
6✔
17
    rc: &RenderContext<'reg, 'rc>,
18
    r: &'reg Registry<'reg>,
19
    d: &Decorator<'rc>,
20
    name: &str,
21
) -> Result<Option<&'rc Template>, RenderError> {
22
    if let Some(partial) = rc.get_partial(name) {
5✔
23
        return Ok(Some(partial));
4✔
24
    }
25

26
    if let Some(t) = rc.get_dev_mode_template(name) {
3✔
27
        return Ok(Some(t));
×
28
    }
29

30
    if let Some(t) = r.get_template(name) {
3✔
31
        return Ok(Some(t));
3✔
32
    }
33

34
    if let Some(tpl) = d.template() {
1✔
35
        return Ok(Some(tpl));
1✔
36
    }
37

38
    Ok(None)
1✔
39
}
40

41
pub fn expand_partial<'reg: 'rc, 'rc>(
6✔
42
    d: &Decorator<'rc>,
43
    r: &'reg Registry<'reg>,
44
    ctx: &'rc Context,
45
    rc: &mut RenderContext<'reg, 'rc>,
46
    out: &mut dyn Output,
47
) -> Result<(), RenderError> {
48
    // try eval inline partials first
49
    if let Some(t) = d.template() {
5✔
50
        t.eval(r, ctx, rc)?;
1✔
51
    }
52

53
    let tname = d.name();
6✔
54

55
    let current_template_before = rc.get_current_template_name();
5✔
56
    let indent_before = rc.get_indent_string().cloned();
6✔
57

58
    if rc.is_current_template(tname) {
11✔
59
        return Err(RenderErrorReason::CannotIncludeSelf.into());
2✔
60
    }
61

62
    // check if referencing partial_block
63
    if tname == PARTIAL_BLOCK {
11✔
64
        if let Some(Some(content)) = rc.peek_partial_block() {
5✔
65
            out.write(content.as_str())?;
1✔
66
            Ok(())
1✔
67
        } else {
68
            // no partial_block for this scope
69
            Err(RenderErrorReason::PartialBlockNotFound.into())
2✔
70
        }
71
    } else {
72
        // normal partial
73
        let partial = find_partial(rc, r, d, tname)?;
10✔
74
        let Some(partial) = partial else {
6✔
75
            return Err(RenderErrorReason::PartialNotFound(tname.to_owned()).into());
2✔
76
        };
77

78
        // check if this inclusion has a block
79
        if let Some(current_parital_block) = d.template() {
11✔
80
            let mut tmp_out = StringOutput::new();
1✔
81
            current_parital_block.render(r, ctx, rc, &mut tmp_out)?;
3✔
82
            rc.push_partial_block(Some(tmp_out.into_string()?));
1✔
83
        } else {
84
            rc.push_partial_block(None);
11✔
85
        }
86

87
        // hash
NEW
88
        let hash_ctx = d
×
89
            .hash()
90
            .iter()
91
            .map(|(k, v)| (*k, v.value()))
9✔
92
            .collect::<HashMap<&str, &Json>>();
93

94
        let mut partial_include_block = BlockContext::new();
6✔
95
        // evaluate context for partial
96
        let merged_context = if let Some(p) = d.param(0) {
11✔
97
            if let Some(relative_path) = p.relative_path() {
6✔
98
                // path as parameter provided
99
                merge_json(rc.evaluate(ctx, relative_path)?.as_json(), &hash_ctx)
9✔
100
            } else {
101
                // literal provided
102
                merge_json(p.value(), &hash_ctx)
2✔
103
            }
104
        } else {
105
            // use current path
106
            merge_json(rc.evaluate2(ctx, &Path::current())?.as_json(), &hash_ctx)
21✔
107
        };
108
        partial_include_block.set_base_value(merged_context);
5✔
109

110
        // replace and hold blocks from current render context
111
        let current_blocks = rc.replace_blocks(VecDeque::with_capacity(1));
5✔
112
        rc.push_block(partial_include_block);
7✔
113

114
        // indent
115
        rc.set_indent_string(d.indent().cloned());
6✔
116

117
        let result = partial.render(r, ctx, rc, out);
6✔
118

119
        // cleanup
120
        let trailing_newline = rc.get_trailine_newline();
12✔
121

122
        // remove current partial_block
123
        rc.pop_partial_block();
6✔
124

125
        let _ = rc.replace_blocks(current_blocks);
7✔
126
        rc.set_trailing_newline(trailing_newline);
7✔
127
        rc.set_current_template_name(current_template_before);
7✔
128
        rc.set_indent_string(indent_before);
7✔
129

130
        result
8✔
131
    }
132
}
133

134
#[cfg(test)]
135
mod test {
136
    use crate::context::Context;
137
    use crate::error::RenderError;
138
    use crate::output::Output;
139
    use crate::registry::Registry;
140
    use crate::render::{Helper, RenderContext};
141
    use crate::RenderErrorReason;
142

143
    #[test]
144
    fn test() {
145
        let mut handlebars = Registry::new();
146
        assert!(handlebars
147
            .register_template_string("t0", "{{> t1}}")
148
            .is_ok());
149
        assert!(handlebars
150
            .register_template_string("t1", "{{this}}")
151
            .is_ok());
152
        assert!(handlebars
153
            .register_template_string("t2", "{{#> t99}}not there{{/t99}}")
154
            .is_ok());
155
        assert!(handlebars
156
            .register_template_string("t3", "{{#*inline \"t31\"}}{{this}}{{/inline}}{{> t31}}")
157
            .is_ok());
158
        assert!(handlebars
159
            .register_template_string(
160
                "t4",
161
                "{{#> t5}}{{#*inline \"nav\"}}navbar{{/inline}}{{/t5}}"
162
            )
163
            .is_ok());
164
        assert!(handlebars
165
            .register_template_string("t5", "include {{> nav}}")
166
            .is_ok());
167
        assert!(handlebars
168
            .register_template_string("t6", "{{> t1 a}}")
169
            .is_ok());
170
        assert!(handlebars
171
            .register_template_string(
172
                "t7",
173
                "{{#*inline \"t71\"}}{{a}}{{/inline}}{{> t71 a=\"world\"}}"
174
            )
175
            .is_ok());
176
        assert!(handlebars.register_template_string("t8", "{{a}}").is_ok());
177
        assert!(handlebars
178
            .register_template_string("t9", "{{> t8 a=2}}")
179
            .is_ok());
180

181
        assert_eq!(handlebars.render("t0", &1).ok().unwrap(), "1".to_string());
182
        assert_eq!(
183
            handlebars.render("t2", &1).ok().unwrap(),
184
            "not there".to_string()
185
        );
186
        assert_eq!(handlebars.render("t3", &1).ok().unwrap(), "1".to_string());
187
        assert_eq!(
188
            handlebars.render("t4", &1).ok().unwrap(),
189
            "include navbar".to_string()
190
        );
191
        assert_eq!(
192
            handlebars.render("t6", &json!({"a": "2"})).ok().unwrap(),
193
            "2".to_string()
194
        );
195
        assert_eq!(
196
            handlebars.render("t7", &1).ok().unwrap(),
197
            "world".to_string()
198
        );
199
        assert_eq!(handlebars.render("t9", &1).ok().unwrap(), "2".to_string());
200
    }
201

202
    #[test]
203
    fn test_include_partial_block() {
204
        let t0 = "hello {{> @partial-block}}";
205
        let t1 = "{{#> t0}}inner {{this}}{{/t0}}";
206

207
        let mut handlebars = Registry::new();
208
        assert!(handlebars.register_template_string("t0", t0).is_ok());
209
        assert!(handlebars.register_template_string("t1", t1).is_ok());
210

211
        let r0 = handlebars.render("t1", &true);
212
        assert_eq!(r0.ok().unwrap(), "hello inner true".to_string());
213
    }
214

215
    #[test]
216
    fn test_self_inclusion() {
217
        let t0 = "hello {{> t1}} {{> t0}}";
218
        let t1 = "some template";
219
        let mut handlebars = Registry::new();
220
        assert!(handlebars.register_template_string("t0", t0).is_ok());
221
        assert!(handlebars.register_template_string("t1", t1).is_ok());
222

223
        let r0 = handlebars.render("t0", &true);
224
        assert!(r0.is_err());
225
    }
226

227
    #[test]
228
    fn test_issue_143() {
229
        let main_template = "one{{> two }}three{{> two }}";
230
        let two_partial = "--- two ---";
231

232
        let mut handlebars = Registry::new();
233
        assert!(handlebars
234
            .register_template_string("template", main_template)
235
            .is_ok());
236
        assert!(handlebars
237
            .register_template_string("two", two_partial)
238
            .is_ok());
239

240
        let r0 = handlebars.render("template", &true);
241
        assert_eq!(r0.ok().unwrap(), "one--- two ---three--- two ---");
242
    }
243

244
    #[test]
245
    fn test_hash_context_outscope() {
246
        let main_template = "In: {{> p a=2}} Out: {{a}}";
247
        let p_partial = "{{a}}";
248

249
        let mut handlebars = Registry::new();
250
        assert!(handlebars
251
            .register_template_string("template", main_template)
252
            .is_ok());
253
        assert!(handlebars.register_template_string("p", p_partial).is_ok());
254

255
        let r0 = handlebars.render("template", &true);
256
        assert_eq!(r0.ok().unwrap(), "In: 2 Out: ");
257
    }
258

259
    #[test]
260
    fn test_partial_context_hash() {
261
        let mut hbs = Registry::new();
262
        hbs.register_template_string("one", "This is a test. {{> two name=\"fred\" }}")
263
            .unwrap();
264
        hbs.register_template_string("two", "Lets test {{name}}")
265
            .unwrap();
266
        assert_eq!(
267
            "This is a test. Lets test fred",
268
            hbs.render("one", &0).unwrap()
269
        );
270
    }
271

272
    #[test]
273
    fn teset_partial_context_with_both_hash_and_param() {
274
        let mut hbs = Registry::new();
275
        hbs.register_template_string("one", "This is a test. {{> two this name=\"fred\" }}")
276
            .unwrap();
277
        hbs.register_template_string("two", "Lets test {{name}} and {{root_name}}")
278
            .unwrap();
279
        assert_eq!(
280
            "This is a test. Lets test fred and tom",
281
            hbs.render("one", &json!({"root_name": "tom"})).unwrap()
282
        );
283
    }
284

285
    #[test]
286
    fn test_partial_subexpression_context_hash() {
287
        let mut hbs = Registry::new();
288
        hbs.register_template_string("one", "This is a test. {{> (x @root) name=\"fred\" }}")
289
            .unwrap();
290
        hbs.register_template_string("two", "Lets test {{name}}")
291
            .unwrap();
292

293
        hbs.register_helper(
294
            "x",
295
            Box::new(
296
                |_: &Helper<'_>,
297
                 _: &Registry<'_>,
298
                 _: &Context,
299
                 _: &mut RenderContext<'_, '_>,
300
                 out: &mut dyn Output|
301
                 -> Result<(), RenderError> {
302
                    out.write("two")?;
303
                    Ok(())
304
                },
305
            ),
306
        );
307
        assert_eq!(
308
            "This is a test. Lets test fred",
309
            hbs.render("one", &0).unwrap()
310
        );
311
    }
312

313
    #[test]
314
    fn test_nested_partial_scope() {
315
        let t = "{{#*inline \"pp\"}}{{a}} {{b}}{{/inline}}{{#each c}}{{> pp a=2}}{{/each}}";
316
        let data = json!({"c": [{"b": true}, {"b": false}]});
317

318
        let mut handlebars = Registry::new();
319
        assert!(handlebars.register_template_string("t", t).is_ok());
320
        let r0 = handlebars.render("t", &data);
321
        assert_eq!(r0.ok().unwrap(), "2 true2 false");
322
    }
323

324
    #[test]
325
    fn test_nested_partial_block() {
326
        let mut handlebars = Registry::new();
327
        let template1 = "<outer>{{> @partial-block }}</outer>";
328
        let template2 = "{{#> t1 }}<inner>{{> @partial-block }}</inner>{{/ t1 }}";
329
        let template3 = "{{#> t2 }}Hello{{/ t2 }}";
330

331
        handlebars
332
            .register_template_string("t1", template1)
333
            .unwrap();
334
        handlebars
335
            .register_template_string("t2", template2)
336
            .unwrap();
337

338
        let page = handlebars.render_template(template3, &json!({})).unwrap();
339
        assert_eq!("<outer><inner>Hello</inner></outer>", page);
340
    }
341

342
    #[test]
343
    fn test_subexpression_partial_block() {
344
        let mut handlebars = Registry::new();
345
        let template1 = "<outer>{{> @partial-block }}</outer>";
346
        let template2 = "{{#> (x 'foo') }}<inner>{{> @partial-block }}</inner>{{/}}";
347
        let template3 = "{{#> (y this) }}Hello{{/}} World";
348

349
        handlebars.register_helper(
350
            "x",
351
            Box::new(
352
                |_: &Helper<'_>,
353
                 _: &Registry<'_>,
354
                 _: &Context,
355
                 _: &mut RenderContext<'_, '_>,
356
                 out: &mut dyn Output|
357
                 -> Result<(), RenderError> {
358
                    out.write("t1")?;
359
                    Ok(())
360
                },
361
            ),
362
        );
363
        handlebars.register_helper(
364
            "y",
365
            Box::new(
366
                |_: &Helper<'_>,
367
                 _: &Registry<'_>,
368
                 _: &Context,
369
                 _: &mut RenderContext<'_, '_>,
370
                 out: &mut dyn Output|
371
                 -> Result<(), RenderError> {
372
                    out.write("t2")?;
373
                    Ok(())
374
                },
375
            ),
376
        );
377
        handlebars
378
            .register_template_string("t1", template1)
379
            .unwrap();
380
        handlebars
381
            .register_template_string("t2", template2)
382
            .unwrap();
383

384
        let page = handlebars.render_template(template3, &json!({})).unwrap();
385
        assert_eq!("<outer><inner>Hello</inner></outer> World", page);
386
    }
387

388
    #[test]
389
    fn test_up_to_partial_level() {
390
        let outer = r#"{{>inner name="fruit:" vegetables=fruits}}"#;
391
        let inner = "{{#each vegetables}}{{../name}} {{this}},{{/each}}";
392

393
        let data = json!({ "fruits": ["carrot", "tomato"] });
394

395
        let mut handlebars = Registry::new();
396
        handlebars.register_template_string("outer", outer).unwrap();
397
        handlebars.register_template_string("inner", inner).unwrap();
398

399
        assert_eq!(
400
            handlebars.render("outer", &data).unwrap(),
401
            "fruit: carrot,fruit: tomato,"
402
        );
403
    }
404

405
    #[test]
406
    fn line_stripping_with_inline_and_partial() {
407
        let tpl0 = r#"{{#*inline "foo"}}foo
408
{{/inline}}
409
{{> foo}}
410
{{> foo}}
411
{{> foo}}"#;
412
        let tpl1 = r#"{{#*inline "foo"}}foo{{/inline}}
413
{{> foo}}
414
{{> foo}}
415
{{> foo}}"#;
416

417
        let hbs = Registry::new();
418
        assert_eq!(
419
            r"foo
420
foo
421
foo
422
",
423
            hbs.render_template(tpl0, &json!({})).unwrap()
424
        );
425
        assert_eq!(
426
            r"
427
foofoofoo",
428
            hbs.render_template(tpl1, &json!({})).unwrap()
429
        );
430
    }
431

432
    #[test]
433
    fn test_partial_indent() {
434
        let outer = r"                {{> inner inner_solo}}
435

436
{{#each inners}}
437
                {{> inner}}
438
{{/each}}
439

440
        {{#each inners}}
441
        {{> inner}}
442
        {{/each}}
443
";
444
        let inner = r"name: {{name}}
445
";
446

447
        let mut hbs = Registry::new();
448

449
        hbs.register_template_string("inner", inner).unwrap();
450
        hbs.register_template_string("outer", outer).unwrap();
451

452
        let result = hbs
453
            .render(
454
                "outer",
455
                &json!({
456
                    "inner_solo": {"name": "inner_solo"},
457
                    "inners": [
458
                        {"name": "hello"},
459
                        {"name": "there"}
460
                    ]
461
                }),
462
            )
463
            .unwrap();
464

465
        assert_eq!(
466
            result,
467
            r"                name: inner_solo
468

469
                name: hello
470
                name: there
471

472
        name: hello
473
        name: there
474
"
475
        );
476
    }
477
    // Rule::partial_expression should not trim leading indent  by default
478

479
    #[test]
480
    fn test_partial_prevent_indent() {
481
        let outer = r"                {{> inner inner_solo}}
482

483
{{#each inners}}
484
                {{> inner}}
485
{{/each}}
486

487
        {{#each inners}}
488
        {{> inner}}
489
        {{/each}}
490
";
491
        let inner = r"name: {{name}}
492
";
493

494
        let mut hbs = Registry::new();
495
        hbs.set_prevent_indent(true);
496

497
        hbs.register_template_string("inner", inner).unwrap();
498
        hbs.register_template_string("outer", outer).unwrap();
499

500
        let result = hbs
501
            .render(
502
                "outer",
503
                &json!({
504
                    "inner_solo": {"name": "inner_solo"},
505
                    "inners": [
506
                        {"name": "hello"},
507
                        {"name": "there"}
508
                    ]
509
                }),
510
            )
511
            .unwrap();
512

513
        assert_eq!(
514
            result,
515
            r"                name: inner_solo
516

517
                name: hello
518
                name: there
519

520
        name: hello
521
        name: there
522
"
523
        );
524
    }
525

526
    #[test]
527
    fn test_nested_partials() {
528
        let mut hb = Registry::new();
529
        hb.register_template_string("partial", "{{> @partial-block}}")
530
            .unwrap();
531
        hb.register_template_string(
532
            "index",
533
            r"{{#>partial}}
534
    Yo
535
    {{#>partial}}
536
    Yo 2
537
    {{/partial}}
538
{{/partial}}",
539
        )
540
        .unwrap();
541
        assert_eq!(
542
            r"    Yo
543
    Yo 2
544
",
545
            hb.render("index", &()).unwrap()
546
        );
547

548
        hb.register_template_string("partial2", "{{> @partial-block}}")
549
            .unwrap();
550
        let r2 = hb
551
            .render_template(
552
                r"{{#> partial}}
553
{{#> partial2}}
554
:(
555
{{/partial2}}
556
{{/partial}}",
557
                &(),
558
            )
559
            .unwrap();
560
        assert_eq!(":(\n", r2);
561
    }
562

563
    #[test]
564
    fn test_partial_context_issue_495() {
565
        let mut hb = Registry::new();
566
        hb.register_template_string(
567
            "t1",
568
            r#"{{~#*inline "displayName"~}}
569
Template:{{name}}
570
{{/inline}}
571
{{#each data as |name|}}
572
Name:{{name}}
573
{{>displayName name="aaaa"}}
574
{{/each}}"#,
575
        )
576
        .unwrap();
577

578
        hb.register_template_string(
579
            "t2",
580
            r#"{{~#*inline "displayName"~}}
581
Template:{{this}}
582
{{/inline}}
583
{{#each data as |name|}}
584
Name:{{name}}
585
{{>displayName}}
586
{{/each}}"#,
587
        )
588
        .unwrap();
589

590
        let data = json!({
591
            "data": ["hudel", "test"]
592
        });
593

594
        assert_eq!(
595
            r"Name:hudel
596
Template:aaaa
597
Name:test
598
Template:aaaa
599
",
600
            hb.render("t1", &data).unwrap()
601
        );
602
        assert_eq!(
603
            r"Name:hudel
604
Template:hudel
605
Name:test
606
Template:test
607
",
608
            hb.render("t2", &data).unwrap()
609
        );
610
    }
611

612
    #[test]
613
    fn test_multiline_partial_indent() {
614
        let mut hb = Registry::new();
615

616
        hb.register_template_string(
617
            "t1",
618
            r#"{{#*inline "thepartial"}}
619
  inner first line
620
  inner second line
621
{{/inline}}
622
  {{> thepartial}}
623
outer third line"#,
624
        )
625
        .unwrap();
626
        assert_eq!(
627
            r"    inner first line
628
    inner second line
629
outer third line",
630
            hb.render("t1", &()).unwrap()
631
        );
632

633
        hb.register_template_string(
634
            "t2",
635
            r#"{{#*inline "thepartial"}}inner first line
636
inner second line
637
{{/inline}}
638
  {{> thepartial}}
639
outer third line"#,
640
        )
641
        .unwrap();
642
        assert_eq!(
643
            r"  inner first line
644
  inner second line
645
outer third line",
646
            hb.render("t2", &()).unwrap()
647
        );
648

649
        hb.register_template_string(
650
            "t3",
651
            r#"{{#*inline "thepartial"}}{{a}}{{/inline}}
652
  {{> thepartial}}
653
outer third line"#,
654
        )
655
        .unwrap();
656
        assert_eq!(
657
            r"
658
  inner first line
659
  inner second lineouter third line",
660
            hb.render("t3", &json!({"a": "inner first line\ninner second line"}))
661
                .unwrap()
662
        );
663

664
        hb.register_template_string(
665
            "t4",
666
            r#"{{#*inline "thepartial"}}
667
  inner first line
668
  inner second line
669
{{/inline}}
670
  {{~> thepartial}}
671
outer third line"#,
672
        )
673
        .unwrap();
674
        assert_eq!(
675
            r"  inner first line
676
  inner second line
677
outer third line",
678
            hb.render("t4", &()).unwrap()
679
        );
680

681
        let mut hb2 = Registry::new();
682
        hb2.set_prevent_indent(true);
683

684
        hb2.register_template_string(
685
            "t1",
686
            r#"{{#*inline "thepartial"}}
687
  inner first line
688
  inner second line
689
{{/inline}}
690
  {{> thepartial}}
691
outer third line"#,
692
        )
693
        .unwrap();
694
        assert_eq!(
695
            r"    inner first line
696
  inner second line
697
outer third line",
698
            hb2.render("t1", &()).unwrap()
699
        );
700
    }
701

702
    #[test]
703
    fn test_indent_level_on_nested_partials() {
704
        let nested_partial = "
705
<div>
706
    content
707
</div>
708
";
709
        let partial = "
710
<div>
711
    {{>nested_partial}}
712
</div>
713
";
714

715
        let partial_indented = "
716
<div>
717
    {{>partial}}
718
</div>
719
";
720

721
        let result = "
722
<div>
723
    <div>
724
        <div>
725
            content
726
        </div>
727
    </div>
728
</div>
729
";
730

731
        let mut hb = Registry::new();
732
        hb.register_template_string("nested_partial", nested_partial.trim_start())
733
            .unwrap();
734
        hb.register_template_string("partial", partial.trim_start())
735
            .unwrap();
736
        hb.register_template_string("partial_indented", partial_indented.trim_start())
737
            .unwrap();
738

739
        let s = hb.render("partial_indented", &()).unwrap();
740

741
        assert_eq!(&s, result.trim_start());
742
    }
743

744
    #[test]
745
    fn test_issue_534() {
746
        let t1 = "{{title}}";
747
        let t2 = "{{#each modules}}{{> (lookup this \"module\") content name=0}}{{/each}}";
748

749
        let data = json!({
750
          "modules": [
751
            {"module": "t1", "content": {"title": "foo"}},
752
            {"module": "t1", "content": {"title": "bar"}},
753
          ]
754
        });
755

756
        let mut hbs = Registry::new();
757
        hbs.register_template_string("t1", t1).unwrap();
758
        hbs.register_template_string("t2", t2).unwrap();
759

760
        assert_eq!("foobar", hbs.render("t2", &data).unwrap());
761
    }
762

763
    #[test]
764
    fn test_partial_not_found() {
765
        let t1 = "{{> bar}}";
766
        let hbs = Registry::new();
767
        assert!(hbs.render_template(t1, &()).is_err());
768
    }
769

770
    #[test]
771
    fn test_issue_643_this_context() {
772
        let t1 = "{{this}}";
773
        let t2 = "{{> t1 \"hello world\"}}";
774

775
        let mut hbs = Registry::new();
776
        hbs.register_template_string("t1", t1).unwrap();
777
        hbs.register_template_string("t2", t2).unwrap();
778

779
        assert_eq!("hello world", hbs.render("t2", &()).unwrap());
780

781
        let t1 = "{{a}} {{[0]}} {{[1]}}";
782
        let t2 = "{{> t1 \"hello world\" a=1}}";
783

784
        let mut hbs = Registry::new();
785
        hbs.register_template_string("t1", t1).unwrap();
786
        hbs.register_template_string("t2", t2).unwrap();
787

788
        assert_eq!("1 h e", hbs.render("t2", &()).unwrap());
789

790
        let t1 = "{{#each this}}{{@key}}:{{this}},{{/each}}";
791
        let t2 = "{{> t1 a=1}}";
792

793
        let mut hbs = Registry::new();
794
        hbs.register_template_string("t1", t1).unwrap();
795
        hbs.register_template_string("t2", t2).unwrap();
796

797
        assert_eq!("a:1,", hbs.render("t2", &()).unwrap());
798

799
        let t1 = "{{#each this}}{{@key}}:{{this}},{{/each}}";
800
        let t2 = "{{> t1 a=1}}";
801

802
        let mut hbs = Registry::new();
803
        hbs.register_template_string("t1", t1).unwrap();
804
        hbs.register_template_string("t2", t2).unwrap();
805

806
        assert_eq!("a:1,b:2,", hbs.render("t2", &json!({"b": 2})).unwrap());
807

808
        let t1 = "{{#each this}}{{@key}}:{{this}},{{/each}}";
809
        let t2 = "{{> t1 b a=1}}";
810

811
        let mut hbs = Registry::new();
812
        hbs.register_template_string("t1", t1).unwrap();
813
        hbs.register_template_string("t2", t2).unwrap();
814

815
        assert_eq!("a:1,", hbs.render("t2", &json!({"b": 2})).unwrap());
816
    }
817

818
    #[test]
819
    fn test_nested_partial_block_scope_issue() {
820
        let mut hs = Registry::new();
821
        hs.register_template_string("primary", "{{> @partial-block }}")
822
            .unwrap();
823
        hs.register_template_string("secondary", "{{#*inline \"inl\"}}Bug{{/inline}}{{#>primary}}{{> @partial-block }}{{>inl}}{{/primary}}").unwrap();
824
        hs.register_template_string("current", "{{>secondary}}")
825
            .unwrap();
826

827
        assert!(matches!(
828
            hs.render("current", &()).unwrap_err().reason(),
829
            RenderErrorReason::PartialBlockNotFound
830
        ));
831

832
        let mut hs = Registry::new();
833
        hs.register_template_string("primary", "{{> @partial-block }}")
834
            .unwrap();
835
        hs.register_template_string("secondary", "{{#*inline \"inl\"}}Bug{{/inline}}{{#>primary}}{{> @partial-block }}{{>inl}}{{/primary}}").unwrap();
836
        hs.register_template_string("current", "{{#>secondary}}Not a {{/secondary}}")
837
            .unwrap();
838

839
        assert_eq!(hs.render("current", &()).unwrap(), "Not a Bug");
840
    }
841
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc