diff --git a/js/test_cases/helpers_first/input1.json b/js/test_cases/helpers_first/input1.json new file mode 100644 index 0000000..f5fc911 --- /dev/null +++ b/js/test_cases/helpers_first/input1.json @@ -0,0 +1,23 @@ +{ + "people": [ + { + "name": "Alice", + "pet": "cat" + }, + { + "name": "Bob", + "pet": "dog" + }, + { + "name": "Chris", + "pet": "lizard" + } + ], + "toys": [ + "ball", + "bone" + ], + "scalar": 7, + "name": "global name", + "pet": "global pet" +} diff --git a/js/test_cases/helpers_first/main.dust b/js/test_cases/helpers_first/main.dust new file mode 100644 index 0000000..a7b4e1d --- /dev/null +++ b/js/test_cases/helpers_first/main.dust @@ -0,0 +1,29 @@ +Tags inside a first{~n} +==================={~n} +{#people} + {name}{@first petname="fluffy"},{pet}{petname}{/first} +{/people}{~n} + +first inside a scalar{~n} +====================={~n} +{#scalar} + {name}{@first petname="fluffy"},{pet}{petname}{/first} +{/scalar}{~n} + +Nested first inside another non-array section{~n} +============================================={~n} +{#people} + {#toys} + {name}'s pet {pet} plays with a {.}{@first}, {/first} + {/toys} +{/people}{~n} + +Else block inside a first{~n} +========================={~n} +{#people} + {name}{@first},{pet}{petname}{:else}elseblock{/first} +{/people}{~n} + +Sep outside any section{~n} +======================={~n} +{@first}first is printed outside any section{/first} diff --git a/js/test_cases/helpers_last/input1.json b/js/test_cases/helpers_last/input1.json new file mode 100644 index 0000000..f5fc911 --- /dev/null +++ b/js/test_cases/helpers_last/input1.json @@ -0,0 +1,23 @@ +{ + "people": [ + { + "name": "Alice", + "pet": "cat" + }, + { + "name": "Bob", + "pet": "dog" + }, + { + "name": "Chris", + "pet": "lizard" + } + ], + "toys": [ + "ball", + "bone" + ], + "scalar": 7, + "name": "global name", + "pet": "global pet" +} diff --git a/js/test_cases/helpers_last/main.dust b/js/test_cases/helpers_last/main.dust new file mode 100644 index 0000000..5b6cfdd --- /dev/null +++ b/js/test_cases/helpers_last/main.dust @@ -0,0 +1,29 @@ +Tags inside a last{~n} +=================={~n} +{#people} + {name}{@last petname="fluffy"},{pet}{petname}{/last} +{/people}{~n} + +last inside a scalar{~n} +===================={~n} +{#scalar} + {name}{@last petname="fluffy"},{pet}{petname}{/last} +{/scalar}{~n} + +Nested last inside another non-array section{~n} +============================================{~n} +{#people} + {#toys} + {name}'s pet {pet} plays with a {.}{@last}, {/last} + {/toys} +{/people}{~n} + +Else block inside a last{~n} +========================{~n} +{#people} + {name}{@last},{pet}{petname}{:else}elseblock{/last} +{/people}{~n} + +Sep outside any section{~n} +======================={~n} +{@last}last is printed outside any section{/last} diff --git a/js/test_cases/helpers_sep/input1.json b/js/test_cases/helpers_sep/input1.json new file mode 100644 index 0000000..f5fc911 --- /dev/null +++ b/js/test_cases/helpers_sep/input1.json @@ -0,0 +1,23 @@ +{ + "people": [ + { + "name": "Alice", + "pet": "cat" + }, + { + "name": "Bob", + "pet": "dog" + }, + { + "name": "Chris", + "pet": "lizard" + } + ], + "toys": [ + "ball", + "bone" + ], + "scalar": 7, + "name": "global name", + "pet": "global pet" +} diff --git a/js/test_cases/helpers_sep/main.dust b/js/test_cases/helpers_sep/main.dust new file mode 100644 index 0000000..492d43e --- /dev/null +++ b/js/test_cases/helpers_sep/main.dust @@ -0,0 +1,29 @@ +Tags inside a sep{~n} +================={~n} +{#people} + {name}{@sep petname="fluffy"},{pet}{petname}{/sep} +{/people}{~n} + +sep inside a scalar{~n} +==================={~n} +{#scalar} + {name}{@sep petname="fluffy"},{pet}{petname}{/sep} +{/scalar}{~n} + +Nested sep inside another non-array section{~n} +==========================================={~n} +{#people} + {#toys} + {name}'s pet {pet} plays with a {.}{@sep}, {/sep} + {/toys} +{/people}{~n} + +Else block inside a sep{~n} +======================={~n} +{#people} + {name}{@sep},{pet}{petname}{:else}elseblock{/sep} +{/people}{~n} + +Sep outside any section{~n} +======================={~n} +{@sep}sep is printed outside any section{/sep} diff --git a/src/parser/parser.rs b/src/parser/parser.rs index a671bbc..e965976 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -41,6 +41,9 @@ pub enum DustTag<'a> { DTHelperLessThan(ParameterizedBlock<'a>), DTHelperGreaterThanOrEquals(ParameterizedBlock<'a>), DTHelperLessThanOrEquals(ParameterizedBlock<'a>), + DTHelperSep(ParameterizedBlock<'a>), + DTHelperFirst(ParameterizedBlock<'a>), + DTHelperLast(ParameterizedBlock<'a>), } #[derive(Clone, Debug, PartialEq)] @@ -240,6 +243,18 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> { parameterized_block("{@", &tag_to_path("lt")), DustTag::DTHelperLessThan, ), + map( + parameterized_block("{@", &tag_to_path("sep")), + DustTag::DTHelperSep, + ), + map( + parameterized_block("{@", &tag_to_path("first")), + DustTag::DTHelperFirst, + ), + map( + parameterized_block("{@", &tag_to_path("last")), + DustTag::DTHelperLast, + ), ))(i) } diff --git a/src/renderer/inline_partial_tree.rs b/src/renderer/inline_partial_tree.rs index aa5779c..132c1d2 100644 --- a/src/renderer/inline_partial_tree.rs +++ b/src/renderer/inline_partial_tree.rs @@ -160,5 +160,35 @@ fn extract_inline_partials_from_tag<'a, 'b>( Some(body) => extract_inline_partials_from_body(blocks, &body), }; } + DustTag::DTHelperSep(parameterized_block) => { + match ¶meterized_block.contents { + None => (), + Some(body) => extract_inline_partials_from_body(blocks, &body), + }; + match ¶meterized_block.else_contents { + None => (), + Some(body) => extract_inline_partials_from_body(blocks, &body), + }; + } + DustTag::DTHelperFirst(parameterized_block) => { + match ¶meterized_block.contents { + None => (), + Some(body) => extract_inline_partials_from_body(blocks, &body), + }; + match ¶meterized_block.else_contents { + None => (), + Some(body) => extract_inline_partials_from_body(blocks, &body), + }; + } + DustTag::DTHelperLast(parameterized_block) => { + match ¶meterized_block.contents { + None => (), + Some(body) => extract_inline_partials_from_body(blocks, &body), + }; + match ¶meterized_block.else_contents { + None => (), + Some(body) => extract_inline_partials_from_body(blocks, &body), + }; + } } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index d1ca186..a338ac4 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -2,6 +2,7 @@ use crate::parser::template; use crate::parser::Body; use crate::parser::DustTag; use crate::parser::Filter; +use crate::parser::OwnedLiteral; use crate::parser::PartialNameElement; use crate::parser::Path; use crate::parser::Special; @@ -20,6 +21,7 @@ use crate::renderer::inline_partial_tree::InlinePartialTreeElement; use crate::renderer::iteration_context::IterationContext; use crate::renderer::parameters_context::ParametersContext; use crate::renderer::walking::walk_path; +use std::borrow::Borrow; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -644,11 +646,178 @@ impl<'a> DustRenderer<'a> { } } } + DustTag::DTHelperSep(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let index = self.get(breadcrumbs, &vec!["$idx"]); + let len = self.get(breadcrumbs, &vec!["$len"]); + match (index, len) { + (Err(_), _) | (_, Err(_)) => { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } + (Ok(index_resolved), Ok(len_resolved)) => { + // Iteration contexts use OwnedLiteral::LPositiveinteger + let index_cast = index_resolved + .get_context_element_reference() + .to_any() + .downcast_ref::(); + let len_cast = len_resolved + .get_context_element_reference() + .to_any() + .downcast_ref::(); + match (index_cast, len_cast) { + ( + Some(OwnedLiteral::LPositiveInteger(index_number)), + Some(OwnedLiteral::LPositiveInteger(len_number)), + ) => { + if *index_number == len_number - 1 { + return Ok("".to_owned()); + } else { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + _ => return Ok("".to_owned()), + } + } + } + } + DustTag::DTHelperFirst(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let index = self.get(breadcrumbs, &vec!["$idx"]); + match index { + Err(_) => return Ok("".to_owned()), + Ok(index_resolved) => { + // Iteration contexts use OwnedLiteral::LPositiveinteger + let index_cast = index_resolved + .get_context_element_reference() + .to_any() + .downcast_ref::(); + match index_cast { + Some(OwnedLiteral::LPositiveInteger(index_number)) => { + if *index_number != 0 { + return Ok("".to_owned()); + } else { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + _ => return Ok("".to_owned()), + } + } + } + } + DustTag::DTHelperLast(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let index = self.get(breadcrumbs, &vec!["$idx"]); + let len = self.get(breadcrumbs, &vec!["$len"]); + match (index, len) { + (Err(_), _) | (_, Err(_)) => return Ok("".to_owned()), + (Ok(index_resolved), Ok(len_resolved)) => { + // Iteration contexts use OwnedLiteral::LPositiveinteger + let index_cast = index_resolved + .get_context_element_reference() + .to_any() + .downcast_ref::(); + let len_cast = len_resolved + .get_context_element_reference() + .to_any() + .downcast_ref::(); + match (index_cast, len_cast) { + ( + Some(OwnedLiteral::LPositiveInteger(index_number)), + Some(OwnedLiteral::LPositiveInteger(len_number)), + ) => { + if *index_number != len_number - 1 { + return Ok("".to_owned()); + } else { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + _ => return Ok("".to_owned()), + } + } + } + } } Ok("".to_owned()) } + /// Read a value from the context tree + pub fn get

( + &'a self, + breadcrumbs: &'a Vec>, + name: &Vec

, + ) -> Result, WalkError> + where + P: Borrow, + { + let val = + walk_path(breadcrumbs, name).map(|ice| ice.into_context_element(self, breadcrumbs)); + match val { + Ok(Some(ice_result)) => Ok(ice_result), + Ok(None) => Err(WalkError::CantWalk), + Err(walk_error) => Err(walk_error), + } + } + + /// Read a value from the parameters context + /// + /// Returns None if the key is not present at all + pub fn tap

( + &'a self, + breadcrumbs: &'a Vec>, + parameters: &'a ParametersContext<'a>, + name: &P, + ) -> Option, WalkError>> + where + P: Borrow, + { + if !parameters.contains_key(name.borrow()) { + return None; + } + let val = parameters + .walk("key") + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match val { + Ok(Some(ice_result)) => Some(Ok(ice_result)), + Ok(None) => Some(Err(WalkError::CantWalk)), + Err(walk_error) => Some(Err(walk_error)), + } + } + fn new_breadcrumbs_section<'b>( &'b self, breadcrumbs: &'b Vec>,