Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested @partial-block support #401

Merged
merged 5 commits into from Dec 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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