Skip to content

Commit

Permalink
Merge pull request #401 from sunng87/fix/partial-block-nested
Browse files Browse the repository at this point in the history
Nested @partial-block support
  • Loading branch information
sunng87 committed Dec 25, 2020
2 parents 792880a + c90059c commit 6b25917
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 5 deletions.
38 changes: 34 additions & 4 deletions src/partial.rs
Expand Up @@ -9,7 +9,7 @@ use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Decorator, Evaluable, RenderContext, Renderable};

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

pub fn expand_partial<'reg: 'rc, 'rc>(
d: &Decorator<'reg, 'rc>,
Expand All @@ -28,13 +28,19 @@ pub fn expand_partial<'reg: 'rc, 'rc>(
return Err(RenderError::new("Cannot include self in >"));
}

// if tname == PARTIAL_BLOCK
let partial = rc
.get_partial(tname)
.or_else(|| r.get_template(tname))
.or_else(|| d.template());

if let Some(t) = partial {
let mut local_rc = rc.clone();
let is_partial_block = tname == PARTIAL_BLOCK;

if is_partial_block {
local_rc.inc_partial_block_depth();
}

// partial context path
if let Some(ref param_ctx) = d.param(0) {
Expand All @@ -44,8 +50,8 @@ pub fn expand_partial<'reg: 'rc, 'rc>(
}

// @partial-block
if let Some(t) = d.template() {
local_rc.set_partial(PARTIAL_BLOCK.to_owned(), t);
if let Some(pb) = d.template() {
local_rc.push_partial_block(pb);
}

let result = if d.hash().is_empty() {
Expand All @@ -64,7 +70,13 @@ pub fn expand_partial<'reg: 'rc, 'rc>(
t.render(r, &ctx, &mut partial_rc, out)
};

local_rc.remove_partial(PARTIAL_BLOCK);
if is_partial_block {
local_rc.dec_partial_block_depth();
}

if d.template().is_some() {
local_rc.pop_partial_block();
}

result
} else {
Expand Down Expand Up @@ -250,4 +262,22 @@ mod test {
let r0 = handlebars.render("t", &data);
assert_eq!(r0.ok().unwrap(), "2 true2 false");
}

#[test]
fn test_nested_partials() {
let mut handlebars = Registry::new();
let template1 = "<outer>{{> @partial-block }}</outer>";
let template2 = "{{#> t1 }}<inner>{{> @partial-block }}</inner>{{/ t1 }}";
let template3 = "{{#> t2 }}Hello{{/ t2 }}";

handlebars
.register_template_string("t1", &template1)
.unwrap();
handlebars
.register_template_string("t2", &template2)
.unwrap();

let page = handlebars.render_template(&template3, &json!({})).unwrap();
assert_eq!("<outer><inner>Hello</inner></outer>", page);
}
}
30 changes: 29 additions & 1 deletion src/render.rs
Expand Up @@ -39,6 +39,8 @@ pub struct RenderContext<'reg, 'rc> {
#[derive(Clone)]
pub struct RenderContextInner<'reg: 'rc, 'rc> {
partials: BTreeMap<String, &'reg Template>,
partial_block_stack: VecDeque<&'reg Template>,
partial_block_depth: isize,
local_helpers: BTreeMap<String, Rc<dyn HelperDef + Send + Sync + 'rc>>,
/// current template name
current_template: Option<&'reg String>,
Expand All @@ -52,6 +54,8 @@ impl<'reg: 'rc, 'rc> RenderContext<'reg, 'rc> {
pub fn new(root_template: Option<&'reg String>) -> RenderContext<'reg, 'rc> {
let inner = Rc::new(RenderContextInner {
partials: BTreeMap::new(),
partial_block_stack: VecDeque::new(),
partial_block_depth: 0,
local_helpers: BTreeMap::new(),
current_template: None,
root_template,
Expand Down Expand Up @@ -158,14 +162,37 @@ impl<'reg: 'rc, 'rc> RenderContext<'reg, 'rc> {

/// Get registered partial in this render context
pub fn get_partial(&self, name: &str) -> Option<&Template> {
self.inner().partials.get(name).map(|v| *v)
if name == partial::PARTIAL_BLOCK {
return self
.inner()
.partial_block_stack
.get(self.inner().partial_block_depth as usize)
.copied();
}
self.inner().partials.get(name).copied()
}

/// Register a partial for this context
pub fn set_partial(&mut self, name: String, partial: &'reg Template) {
self.inner_mut().partials.insert(name, partial);
}

pub(crate) fn push_partial_block(&mut self, partial: &'reg Template) {
self.inner_mut().partial_block_stack.push_front(partial);
}

pub(crate) fn pop_partial_block(&mut self) {
self.inner_mut().partial_block_stack.pop_front();
}

pub(crate) fn inc_partial_block_depth(&mut self) {
self.inner_mut().partial_block_depth += 1;
}

pub(crate) fn dec_partial_block_depth(&mut self) {
self.inner_mut().partial_block_depth -= 1;
}

/// Remove a registered partial
pub fn remove_partial(&mut self, name: &str) {
self.inner_mut().partials.remove(name);
Expand Down Expand Up @@ -247,6 +274,7 @@ impl<'reg, 'rc> fmt::Debug for RenderContextInner<'reg, 'rc> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("RenderContextInner")
.field("partials", &self.partials)
.field("partial_block_stack", &self.partial_block_stack)
.field("root_template", &self.root_template)
.field("current_template", &self.current_template)
.field("disable_eacape", &self.disable_escape)
Expand Down

0 comments on commit 6b25917

Please sign in to comment.