From 74c83ef37468fc0213c27d6a18d8c6be06f6b7fd Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 16:07:11 -0400 Subject: [PATCH 1/9] Adding a basic dynamic partials test. --- js/test_cases/dynamic_partial/input1.json | 3 +++ js/test_cases/dynamic_partial/main.dust | 2 ++ js/test_cases/dynamic_partial/tmplalpha.dust | 1 + js/test_cases/dynamic_partial/tmplbeta.dust | 1 + 4 files changed, 7 insertions(+) create mode 100644 js/test_cases/dynamic_partial/input1.json create mode 100644 js/test_cases/dynamic_partial/main.dust create mode 100644 js/test_cases/dynamic_partial/tmplalpha.dust create mode 100644 js/test_cases/dynamic_partial/tmplbeta.dust diff --git a/js/test_cases/dynamic_partial/input1.json b/js/test_cases/dynamic_partial/input1.json new file mode 100644 index 0000000..e696696 --- /dev/null +++ b/js/test_cases/dynamic_partial/input1.json @@ -0,0 +1,3 @@ +{ + "name": "beta" +} diff --git a/js/test_cases/dynamic_partial/main.dust b/js/test_cases/dynamic_partial/main.dust new file mode 100644 index 0000000..b4f3ce7 --- /dev/null +++ b/js/test_cases/dynamic_partial/main.dust @@ -0,0 +1,2 @@ +Testing dynamic partials{~n} +{>"tmpl{name}"/} diff --git a/js/test_cases/dynamic_partial/tmplalpha.dust b/js/test_cases/dynamic_partial/tmplalpha.dust new file mode 100644 index 0000000..4cfa36d --- /dev/null +++ b/js/test_cases/dynamic_partial/tmplalpha.dust @@ -0,0 +1 @@ +beta template{~n} diff --git a/js/test_cases/dynamic_partial/tmplbeta.dust b/js/test_cases/dynamic_partial/tmplbeta.dust new file mode 100644 index 0000000..4cfa36d --- /dev/null +++ b/js/test_cases/dynamic_partial/tmplbeta.dust @@ -0,0 +1 @@ +beta template{~n} From a33b46661e55dcb5533f5b584c79f403b31933f2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 16:32:16 -0400 Subject: [PATCH 2/9] Add a script to run a single test to make development easier since I often want to focus on one test at a time. --- js/run_compliance_suite.bash | 32 +++++---------- js/run_single_test.bash | 78 ++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 21 deletions(-) create mode 100755 js/run_single_test.bash diff --git a/js/run_compliance_suite.bash b/js/run_compliance_suite.bash index c7d5296..48324d7 100755 --- a/js/run_compliance_suite.bash +++ b/js/run_compliance_suite.bash @@ -27,27 +27,17 @@ EOF done while read -r test_group; do - test_group_name=$(basename "$test_group") - while read -r test_case; do - test_case_file_name=$(basename "$test_case") - test_case_name=${test_case_file_name%.*} - set +e - ( - if cmp -s <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case") <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case"); then - echo "$test_group_name::$test_case_name PASSED" - else - echo "$test_group_name::$test_case_name FAILED" - if [ $show_diff -eq 1 ]; then - diff --label "dustjs-linkedin" --label "duster" <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" 2>/dev/null ) <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" 2>/dev/null ) - fi - exit 1 - fi - ) - if [ $? -ne 0 ]; then - failed_count=$((failed_count + 1)) - fi - set -e - done <<<"$(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.json' | sort)" + set +e + if [ $show_diff -eq 1 ]; then + "$DIR/run_single_test.bash" --show-diff "$test_group" + else + "$DIR/run_single_test.bash" --show-diff "$test_group" + fi + result=$? + if [ $result -ne 0 ]; then + failed_count=$((failed_count + result)) + fi + set -e done <<<"$(find "$DIR/test_cases" -maxdepth 1 -mindepth 1 -type d ! -name '_*' | sort)" ignored_count=$(find "$DIR/test_cases" -maxdepth 1 -mindepth 1 -type d -name '_*' | wc -l) diff --git a/js/run_single_test.bash b/js/run_single_test.bash new file mode 100755 index 0000000..100c73e --- /dev/null +++ b/js/run_single_test.bash @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# +# Runs a single test against LinkedIn DustJS and duster to compare the result +set -euo pipefail +IFS=$'\n\t' +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +test_group="" +test_mode="" + +function show_help { + cat< + +Options: + --show-diff Shows the difference between the two dust implementations + --dustjs Print the output of dustjs instead of comparing + --duster Print the output of duster instead of comparing +EOF +} + +while (( "$#" )); do + if [ "$1" = "--help" ]; then + show_help + exit 0 + elif [ "$1" = "--show-diff" ]; then + show_diff=1 + elif [ "$1" = "--dustjs" ]; then + test_mode="dustjs" + elif [ "$1" = "--duster" ]; then + test_mode="duster" + elif [ ! "$1" = -* ]; then + test_group="$1" + else + (>&2 echo "Unrecognized option: $1") + exit 1 + fi + shift +done + +# Assert a test group was specified +if [ "$test_group" = "" ]; then + show_help + exit 1 +fi + +failed_count=0 +test_group_name=$(basename "$test_group") +while read -r test_case; do + test_case_file_name=$(basename "$test_case") + test_case_name=${test_case_file_name%.*} + set +e + if [ "$test_mode" = "dustjs" ]; then + xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" + elif [ "$test_mode" = "duster" ]; then + xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" + else + ( + if cmp -s <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case") <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case"); then + echo "$test_group_name::$test_case_name PASSED" + else + echo "$test_group_name::$test_case_name FAILED" + if [ $show_diff -eq 1 ]; then + diff --label "dustjs-linkedin" --label "duster" <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" 2>/dev/null ) <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" 2>/dev/null ) + fi + exit 1 + fi + ) + if [ $? -ne 0 ]; then + failed_count=$((failed_count + 1)) + fi + fi + set -e +done <<<"$(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.json' | sort)" + +exit "$failed_count" From 52ba4fd0a34d4454faacbc53eacddb89dbcc453d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 16:40:23 -0400 Subject: [PATCH 3/9] Only run each command once in the compliance test suite. --- js/run_single_test.bash | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/js/run_single_test.bash b/js/run_single_test.bash index 100c73e..d0dbfdb 100755 --- a/js/run_single_test.bash +++ b/js/run_single_test.bash @@ -52,18 +52,25 @@ while read -r test_case; do test_case_file_name=$(basename "$test_case") test_case_name=${test_case_file_name%.*} set +e + if [ "$test_mode" = "dustjs" ] || [ "$test_mode" = "" ]; then + dustjs_output=$(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case") + fi + if [ "$test_mode" = "duster" ] || [ "$test_mode" = "" ]; then + duster_output=$(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case") + fi + if [ "$test_mode" = "dustjs" ]; then - xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" + cat <<<"$dustjs_output" elif [ "$test_mode" = "duster" ]; then - xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" + cat <<<"$duster_output" else ( - if cmp -s <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case") <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case"); then + if cmp -s <(cat <<<"$dustjs_output") <(cat <<<"$duster_output"); then echo "$test_group_name::$test_case_name PASSED" else echo "$test_group_name::$test_case_name FAILED" if [ $show_diff -eq 1 ]; then - diff --label "dustjs-linkedin" --label "duster" <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" 2>/dev/null ) <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" 2>/dev/null ) + diff --label "dustjs-linkedin" --label "duster" <(cat <<<"$dustjs_output") <(cat <<<"$duster_output") fi exit 1 fi From 402d8679e20d23ef441a9d361f0131425f50ee47 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 20:44:17 -0400 Subject: [PATCH 4/9] Switch the parser over to a new partial implementation for dynamic partials. --- src/parser/parser.rs | 188 ++++++++++++++++++++++++++-- src/renderer/inline_partial_tree.rs | 1 + 2 files changed, 179 insertions(+), 10 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index a1ed027..56df718 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -34,6 +34,7 @@ pub enum DustTag<'a> { DTBlock(NamedBlock<'a>), DTInlinePartial(NamedBlock<'a>), DTPartial(Partial<'a>), + DTNewPartial(NewPartial<'a>), DTHelperEquals(ParameterizedBlock<'a>), DTHelperNotEquals(ParameterizedBlock<'a>), DTHelperGreaterThan(ParameterizedBlock<'a>), @@ -112,6 +113,12 @@ pub struct ParameterizedBlock<'a> { pub else_contents: Option>, } +#[derive(Clone, Debug, PartialEq)] +pub struct NewPartial<'a> { + pub name: Vec, + pub params: Vec>, +} + #[derive(Clone, Debug, PartialEq)] pub struct Partial<'a> { pub name: String, @@ -136,6 +143,17 @@ pub struct KVPair<'a> { pub value: RValue<'a>, } +#[derive(Clone, Debug, PartialEq)] +pub enum PartialNameElement { + PNSpan { + contents: String, + }, + PNReference { + path: Vec, + filters: Vec, + }, +} + #[derive(Clone, Debug, PartialEq)] pub struct Body<'a> { pub elements: Vec>, @@ -153,6 +171,28 @@ pub enum TemplateElement<'a> { TEIgnoredWhitespace(IgnoredWhitespace<'a>), } +impl From> for PartialNameElement { + fn from(original: TemplateElement) -> Self { + match original { + TemplateElement::TESpan(span) => PartialNameElement::PNSpan { + contents: span.contents.to_owned(), + }, + TemplateElement::TETag(DustTag::DTReference(reference)) => { + PartialNameElement::PNReference { + path: reference + .path + .keys + .into_iter() + .map(|s| s.to_owned()) + .collect(), + filters: reference.filters, + } + } + _ => panic!("Only spans and references can be used in partial names."), + } + } +} + /// Any element significant to dust that isn't plain text /// /// These elements are always wrapped in curly braces @@ -166,7 +206,7 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> { conditional("{^", DustTag::DTNotExists), named_block("{+", DustTag::DTBlock), named_block("{<", DustTag::DTInlinePartial), - partial("{>", DustTag::DTPartial), + new_partial("{>", DustTag::DTNewPartial), parameterized_block("{@", "gte", DustTag::DTHelperGreaterThanOrEquals), parameterized_block("{@", "lte", DustTag::DTHelperLessThanOrEquals), parameterized_block("{@", "eq", DustTag::DTHelperEquals), @@ -467,6 +507,91 @@ where } } +fn new_partial_with_plain_tag<'a>( + open_matcher: &'static str, +) -> impl Fn(&'a str) -> IResult<&'a str, NewPartial<'a>> { + move |i: &'a str| { + let (i, (name, params)) = delimited( + tag(open_matcher), + tuple(( + key, + opt(delimited( + space1, + separated_list1(space1, key_value_pair), + space0, + )), + )), + tag("/}"), + )(i)?; + + Ok(( + i, + NewPartial { + name: vec![PartialNameElement::PNSpan { + contents: name.to_owned(), + }], + params: params.unwrap_or(Vec::new()), + }, + )) + } +} + +fn new_partial_quoted_tag(i: &str) -> IResult<&str, Vec> { + // TODO: make all consuming + many1(alt(( + map(span, TemplateElement::TESpan), + map(map(reference, DustTag::DTReference), TemplateElement::TETag), + )))(i) +} + +fn new_partial_with_quoted_tag<'a>( + open_matcher: &'static str, +) -> impl Fn(&'a str) -> IResult<&'a str, NewPartial<'a>> { + move |i: &'a str| { + let (i, (name, params)) = delimited( + tag(open_matcher), + tuple(( + verify(quoted_string, |s: &String| { + new_partial_quoted_tag(s.as_str()).is_ok() + }), + opt(delimited( + space1, + separated_list1(space1, key_value_pair), + space0, + )), + )), + tag("/}"), + )(i)?; + + let (_remaining, template_name_elements) = new_partial_quoted_tag(name.as_str()) + .expect("A successful parse was verified earlier with a call to verify()"); + let partial_name_elements = template_name_elements + .into_iter() + .map(|e| e.into()) + .collect(); + + Ok(( + i, + NewPartial { + name: partial_name_elements, + params: params.unwrap_or(Vec::new()), + }, + )) + } +} + +fn new_partial<'a, F>( + open_matcher: &'static str, + constructor: F, +) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>> +where + F: Fn(NewPartial<'a>) -> DustTag<'a>, +{ + let plain = new_partial_with_plain_tag(open_matcher); + let quoted = new_partial_with_quoted_tag(open_matcher); + move |i: &'a str| map(alt((&plain, "ed)), &constructor)(i) +} + fn partial<'a, F>( open_matcher: &'static str, constructor: F, @@ -911,8 +1036,11 @@ mod tests { dust_tag(r#"{>foo bar=baz animal="cat"/}"#), Ok(( "", - DustTag::DTPartial(Partial { - name: "foo".to_owned(), + DustTag::DTNewPartial(NewPartial { + name: vec![PartialNameElement::PNSpan { + contents: "foo".to_owned() + },], + params: vec![ KVPair { key: "bar", @@ -934,8 +1062,44 @@ mod tests { dust_tag(r#"{>"template name * with * special \" characters" bar=baz animal="cat"/}"#), Ok(( "", - DustTag::DTPartial(Partial { - name: r#"template name * with * special " characters"#.to_owned(), + DustTag::DTNewPartial(NewPartial { + name: vec![PartialNameElement::PNSpan { + contents: r#"template name * with * special " characters"#.to_owned() + },], + params: vec![ + KVPair { + key: "bar", + value: RValue::RVPath(Path { keys: vec!["baz"] }) + }, + KVPair { + key: "animal", + value: RValue::RVLiteral(OwnedLiteral::LString("cat".to_owned())) + } + ] + }) + )) + ); + } + + #[test] + fn test_dynamic_partial() { + assert_eq!( + dust_tag(r#"{>"dynamic{ref}template" bar=baz animal="cat"/}"#), + Ok(( + "", + DustTag::DTNewPartial(NewPartial { + name: vec![ + PartialNameElement::PNSpan { + contents: "dynamic".to_owned() + }, + PartialNameElement::PNReference { + path: vec!["ref".to_owned()], + filters: Vec::new() + }, + PartialNameElement::PNSpan { + contents: "template".to_owned() + } + ], params: vec![ KVPair { key: "bar", @@ -957,8 +1121,10 @@ mod tests { dust_tag(r#"{>foo a="foo" b=179/}"#), Ok(( "", - DustTag::DTPartial(Partial { - name: "foo".to_owned(), + DustTag::DTNewPartial(NewPartial { + name: vec![PartialNameElement::PNSpan { + contents: "foo".to_owned() + },], params: vec![ KVPair { key: "a", @@ -1125,9 +1291,11 @@ mod tests { keys: vec!["level3", "level4"] }, contents: Some(Body { - elements: vec![TemplateElement::TETag(DustTag::DTPartial( - Partial { - name: "partialtwo".to_owned(), + elements: vec![TemplateElement::TETag(DustTag::DTNewPartial( + NewPartial { + name: vec![PartialNameElement::PNSpan { + contents: "partialtwo".to_owned() + },], params: vec![ KVPair { key: "v1", diff --git a/src/renderer/inline_partial_tree.rs b/src/renderer/inline_partial_tree.rs index ff7220b..7d992a2 100644 --- a/src/renderer/inline_partial_tree.rs +++ b/src/renderer/inline_partial_tree.rs @@ -95,6 +95,7 @@ fn extract_inline_partials_from_tag<'a, 'b>( }; } DustTag::DTPartial(..) => (), + DustTag::DTNewPartial(..) => (), DustTag::DTInlinePartial(named_block) => { blocks.insert(&named_block.name, &named_block.contents); } From 10c8b2581711934b8855fad152250e4fbaddffb3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 21:11:55 -0400 Subject: [PATCH 5/9] Implement the new partial type in the renderer. --- src/parser/mod.rs | 1 + src/parser/parser.rs | 18 ++++++++++++++++++ src/renderer/renderer.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 58e38e3..30184b2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8,6 +8,7 @@ pub use parser::DustTag; pub use parser::Filter; pub use parser::KVPair; pub use parser::OwnedLiteral; +pub use parser::PartialNameElement; pub use parser::RValue; pub use parser::Special; pub use parser::Template; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 56df718..a2f6290 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -193,6 +193,24 @@ impl From> for PartialNameElement { } } +impl<'a> From<&'a PartialNameElement> for TemplateElement<'a> { + fn from(original: &'a PartialNameElement) -> Self { + match original { + PartialNameElement::PNSpan { contents } => { + TemplateElement::TESpan(Span { contents: contents }) + } + PartialNameElement::PNReference { path, filters } => { + TemplateElement::TETag(DustTag::DTReference(Reference { + path: Path { + keys: path.into_iter().map(|s| s.as_str()).collect(), + }, + filters: filters.into_iter().map(|f| f.clone()).collect(), + })) + } + } + } +} + /// Any element significant to dust that isn't plain text /// /// These elements are always wrapped in curly braces diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index bf0b083..866c6d5 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::KVPair; +use crate::parser::PartialNameElement; use crate::parser::RValue; use crate::parser::Special; use crate::parser::Template; @@ -108,6 +109,23 @@ impl<'a> DustRenderer<'a> { Ok(output) } + fn render_partial_name( + &'a self, + body: &'a Vec, + breadcrumbs: &Vec<&'a dyn ContextElement>, + blocks: &'a InlinePartialTreeElement<'a>, + ) -> Result { + let converted_to_template_elements: Vec> = + body.into_iter().map(|e| e.into()).collect(); + self.render_body( + &Body { + elements: converted_to_template_elements, + }, + breadcrumbs, + blocks, + ) + } + fn render_tag( &'a self, tag: &'a DustTag, @@ -199,6 +217,21 @@ impl<'a> DustRenderer<'a> { return Ok(rendered_content); } } + DustTag::DTNewPartial(partial) => { + let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?; + if partial.params.is_empty() { + let rendered_content = + self.render_template(&partial_name, breadcrumbs, Some(blocks))?; + return Ok(rendered_content); + } else { + let injected_context = ParametersContext::new(breadcrumbs, &partial.params); + let mut new_breadcrumbs = breadcrumbs.clone(); + new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); + let rendered_content = + self.render_template(&partial_name, &new_breadcrumbs, Some(blocks))?; + return Ok(rendered_content); + } + } DustTag::DTInlinePartial(_named_block) => { // Inline partials are blank during rendering (they get injected into blocks) return Ok("".to_owned()); From ecf2576a0ac7acb11599a1f63fdeddeeff0759bd Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 21:12:27 -0400 Subject: [PATCH 6/9] Remove default case from render_tag. --- src/renderer/renderer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 866c6d5..ad23c03 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -464,7 +464,6 @@ impl<'a> DustRenderer<'a> { } } } - _ => (), // TODO: Implement the rest } Ok("".to_owned()) } From 7be00ffea68265e2a08555d7eda4ca69e373bf37 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 21:15:03 -0400 Subject: [PATCH 7/9] Remove the old partial implementation. --- src/parser/parser.rs | 38 ----------------------------- src/renderer/inline_partial_tree.rs | 1 - src/renderer/renderer.rs | 14 ----------- 3 files changed, 53 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index a2f6290..2b216a8 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -33,7 +33,6 @@ pub enum DustTag<'a> { DTNotExists(Container<'a>), DTBlock(NamedBlock<'a>), DTInlinePartial(NamedBlock<'a>), - DTPartial(Partial<'a>), DTNewPartial(NewPartial<'a>), DTHelperEquals(ParameterizedBlock<'a>), DTHelperNotEquals(ParameterizedBlock<'a>), @@ -119,12 +118,6 @@ pub struct NewPartial<'a> { pub params: Vec>, } -#[derive(Clone, Debug, PartialEq)] -pub struct Partial<'a> { - pub name: String, - pub params: Vec>, -} - #[derive(Clone, Debug, PartialEq)] pub enum OwnedLiteral { LString(String), @@ -610,37 +603,6 @@ where move |i: &'a str| map(alt((&plain, "ed)), &constructor)(i) } -fn partial<'a, F>( - open_matcher: &'static str, - constructor: F, -) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>> -where - F: Fn(Partial<'a>) -> DustTag<'a>, -{ - move |i: &'a str| { - let (i, (name, params)) = delimited( - tag(open_matcher), - tuple(( - alt((map(key, String::from), quoted_string)), - opt(delimited( - space1, - separated_list1(space1, key_value_pair), - space0, - )), - )), - tag("/}"), - )(i)?; - - Ok(( - i, - constructor(Partial { - name: name, - params: params.unwrap_or(Vec::new()), - }), - )) - } -} - fn filter(i: &str) -> IResult<&str, Filter> { preceded( tag("|"), diff --git a/src/renderer/inline_partial_tree.rs b/src/renderer/inline_partial_tree.rs index 7d992a2..61c4efb 100644 --- a/src/renderer/inline_partial_tree.rs +++ b/src/renderer/inline_partial_tree.rs @@ -94,7 +94,6 @@ fn extract_inline_partials_from_tag<'a, 'b>( Some(body) => extract_inline_partials_from_body(blocks, &body), }; } - DustTag::DTPartial(..) => (), DustTag::DTNewPartial(..) => (), DustTag::DTInlinePartial(named_block) => { blocks.insert(&named_block.name, &named_block.contents); diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index ad23c03..606a3a6 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -203,20 +203,6 @@ impl<'a> DustRenderer<'a> { self.render_maybe_body(&container.contents, breadcrumbs, blocks) }; } - DustTag::DTPartial(partial) => { - if partial.params.is_empty() { - let rendered_content = - self.render_template(&partial.name, breadcrumbs, Some(blocks))?; - return Ok(rendered_content); - } else { - let injected_context = ParametersContext::new(breadcrumbs, &partial.params); - let mut new_breadcrumbs = breadcrumbs.clone(); - new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); - let rendered_content = - self.render_template(&partial.name, &new_breadcrumbs, Some(blocks))?; - return Ok(rendered_content); - } - } DustTag::DTNewPartial(partial) => { let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?; if partial.params.is_empty() { From 93f9581d8a67f42936dab936b6108407019fd4e3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 21:17:34 -0400 Subject: [PATCH 8/9] Renamed the new partial implementation to the old partial name. --- src/parser/parser.rs | 44 ++++++++++++++--------------- src/renderer/inline_partial_tree.rs | 2 +- src/renderer/renderer.rs | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 2b216a8..41a1eca 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -33,7 +33,7 @@ pub enum DustTag<'a> { DTNotExists(Container<'a>), DTBlock(NamedBlock<'a>), DTInlinePartial(NamedBlock<'a>), - DTNewPartial(NewPartial<'a>), + DTPartial(Partial<'a>), DTHelperEquals(ParameterizedBlock<'a>), DTHelperNotEquals(ParameterizedBlock<'a>), DTHelperGreaterThan(ParameterizedBlock<'a>), @@ -113,7 +113,7 @@ pub struct ParameterizedBlock<'a> { } #[derive(Clone, Debug, PartialEq)] -pub struct NewPartial<'a> { +pub struct Partial<'a> { pub name: Vec, pub params: Vec>, } @@ -217,7 +217,7 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> { conditional("{^", DustTag::DTNotExists), named_block("{+", DustTag::DTBlock), named_block("{<", DustTag::DTInlinePartial), - new_partial("{>", DustTag::DTNewPartial), + partial("{>", DustTag::DTPartial), parameterized_block("{@", "gte", DustTag::DTHelperGreaterThanOrEquals), parameterized_block("{@", "lte", DustTag::DTHelperLessThanOrEquals), parameterized_block("{@", "eq", DustTag::DTHelperEquals), @@ -518,9 +518,9 @@ where } } -fn new_partial_with_plain_tag<'a>( +fn partial_with_plain_tag<'a>( open_matcher: &'static str, -) -> impl Fn(&'a str) -> IResult<&'a str, NewPartial<'a>> { +) -> impl Fn(&'a str) -> IResult<&'a str, Partial<'a>> { move |i: &'a str| { let (i, (name, params)) = delimited( tag(open_matcher), @@ -537,7 +537,7 @@ fn new_partial_with_plain_tag<'a>( Ok(( i, - NewPartial { + Partial { name: vec![PartialNameElement::PNSpan { contents: name.to_owned(), }], @@ -547,7 +547,7 @@ fn new_partial_with_plain_tag<'a>( } } -fn new_partial_quoted_tag(i: &str) -> IResult<&str, Vec> { +fn partial_quoted_tag(i: &str) -> IResult<&str, Vec> { // TODO: make all consuming many1(alt(( map(span, TemplateElement::TESpan), @@ -555,15 +555,15 @@ fn new_partial_quoted_tag(i: &str) -> IResult<&str, Vec> { )))(i) } -fn new_partial_with_quoted_tag<'a>( +fn partial_with_quoted_tag<'a>( open_matcher: &'static str, -) -> impl Fn(&'a str) -> IResult<&'a str, NewPartial<'a>> { +) -> impl Fn(&'a str) -> IResult<&'a str, Partial<'a>> { move |i: &'a str| { let (i, (name, params)) = delimited( tag(open_matcher), tuple(( verify(quoted_string, |s: &String| { - new_partial_quoted_tag(s.as_str()).is_ok() + partial_quoted_tag(s.as_str()).is_ok() }), opt(delimited( space1, @@ -574,7 +574,7 @@ fn new_partial_with_quoted_tag<'a>( tag("/}"), )(i)?; - let (_remaining, template_name_elements) = new_partial_quoted_tag(name.as_str()) + let (_remaining, template_name_elements) = partial_quoted_tag(name.as_str()) .expect("A successful parse was verified earlier with a call to verify()"); let partial_name_elements = template_name_elements .into_iter() @@ -583,7 +583,7 @@ fn new_partial_with_quoted_tag<'a>( Ok(( i, - NewPartial { + Partial { name: partial_name_elements, params: params.unwrap_or(Vec::new()), }, @@ -591,15 +591,15 @@ fn new_partial_with_quoted_tag<'a>( } } -fn new_partial<'a, F>( +fn partial<'a, F>( open_matcher: &'static str, constructor: F, ) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>> where - F: Fn(NewPartial<'a>) -> DustTag<'a>, + F: Fn(Partial<'a>) -> DustTag<'a>, { - let plain = new_partial_with_plain_tag(open_matcher); - let quoted = new_partial_with_quoted_tag(open_matcher); + let plain = partial_with_plain_tag(open_matcher); + let quoted = partial_with_quoted_tag(open_matcher); move |i: &'a str| map(alt((&plain, "ed)), &constructor)(i) } @@ -1016,7 +1016,7 @@ mod tests { dust_tag(r#"{>foo bar=baz animal="cat"/}"#), Ok(( "", - DustTag::DTNewPartial(NewPartial { + DustTag::DTPartial(Partial { name: vec![PartialNameElement::PNSpan { contents: "foo".to_owned() },], @@ -1042,7 +1042,7 @@ mod tests { dust_tag(r#"{>"template name * with * special \" characters" bar=baz animal="cat"/}"#), Ok(( "", - DustTag::DTNewPartial(NewPartial { + DustTag::DTPartial(Partial { name: vec![PartialNameElement::PNSpan { contents: r#"template name * with * special " characters"#.to_owned() },], @@ -1067,7 +1067,7 @@ mod tests { dust_tag(r#"{>"dynamic{ref}template" bar=baz animal="cat"/}"#), Ok(( "", - DustTag::DTNewPartial(NewPartial { + DustTag::DTPartial(Partial { name: vec![ PartialNameElement::PNSpan { contents: "dynamic".to_owned() @@ -1101,7 +1101,7 @@ mod tests { dust_tag(r#"{>foo a="foo" b=179/}"#), Ok(( "", - DustTag::DTNewPartial(NewPartial { + DustTag::DTPartial(Partial { name: vec![PartialNameElement::PNSpan { contents: "foo".to_owned() },], @@ -1271,8 +1271,8 @@ mod tests { keys: vec!["level3", "level4"] }, contents: Some(Body { - elements: vec![TemplateElement::TETag(DustTag::DTNewPartial( - NewPartial { + elements: vec![TemplateElement::TETag(DustTag::DTPartial( + Partial { name: vec![PartialNameElement::PNSpan { contents: "partialtwo".to_owned() },], diff --git a/src/renderer/inline_partial_tree.rs b/src/renderer/inline_partial_tree.rs index 61c4efb..ff7220b 100644 --- a/src/renderer/inline_partial_tree.rs +++ b/src/renderer/inline_partial_tree.rs @@ -94,7 +94,7 @@ fn extract_inline_partials_from_tag<'a, 'b>( Some(body) => extract_inline_partials_from_body(blocks, &body), }; } - DustTag::DTNewPartial(..) => (), + DustTag::DTPartial(..) => (), DustTag::DTInlinePartial(named_block) => { blocks.insert(&named_block.name, &named_block.contents); } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 606a3a6..1546e12 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -203,7 +203,7 @@ impl<'a> DustRenderer<'a> { self.render_maybe_body(&container.contents, breadcrumbs, blocks) }; } - DustTag::DTNewPartial(partial) => { + DustTag::DTPartial(partial) => { let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?; if partial.params.is_empty() { let rendered_content = From 5ddeeac966f581d915b9ed95394fb0c9f422d267 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 21:21:26 -0400 Subject: [PATCH 9/9] minor cleanup. --- js/run_single_test.bash | 2 +- src/parser/parser.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/js/run_single_test.bash b/js/run_single_test.bash index d0dbfdb..d3394e9 100755 --- a/js/run_single_test.bash +++ b/js/run_single_test.bash @@ -58,7 +58,7 @@ while read -r test_case; do if [ "$test_mode" = "duster" ] || [ "$test_mode" = "" ]; then duster_output=$(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case") fi - + if [ "$test_mode" = "dustjs" ]; then cat <<<"$dustjs_output" elif [ "$test_mode" = "duster" ]; then diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 41a1eca..4c0cfd2 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -548,11 +548,10 @@ fn partial_with_plain_tag<'a>( } fn partial_quoted_tag(i: &str) -> IResult<&str, Vec> { - // TODO: make all consuming - many1(alt(( + all_consuming(many1(alt(( map(span, TemplateElement::TESpan), map(map(reference, DustTag::DTReference), TemplateElement::TETag), - )))(i) + ))))(i) } fn partial_with_quoted_tag<'a>(