From 34620fdbfdd16df5d9f1bf2ee94cafa212e3e650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Pavl=C3=ADn?= Date: Thu, 2 Feb 2023 16:03:37 +0100 Subject: [PATCH 1/3] Add support for array index selector --- mustache.go | 10 ++++++++++ mustache_test.go | 34 +++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/mustache.go b/mustache.go index fa2b90b..22b3485 100644 --- a/mustache.go +++ b/mustache.go @@ -515,6 +515,16 @@ Outer: return ret, nil } continue Outer + case reflect.Slice: + index, err := strconv.Atoi(name) + if err != nil { + return v, err + } + ret := av.Index(index) + if ret.IsValid() { + return ret, nil + } + continue Outer default: continue Outer } diff --git a/mustache_test.go b/mustache_test.go index e106ff7..e1db9f6 100644 --- a/mustache_test.go +++ b/mustache_test.go @@ -206,6 +206,22 @@ var tests = []Test{ {`"{{{person.name}}}" == "{{#person}}{{{name}}}{{/person}}"`, map[string]interface{}{"person": map[string]string{"name": "Joe"}}, `"Joe" == "Joe"`, nil}, {`"{{a.b.c.d.e.name}}" == "Phil"`, map[string]interface{}{"a": map[string]interface{}{"b": map[string]interface{}{"c": map[string]interface{}{"d": map[string]interface{}{"e": map[string]string{"name": "Phil"}}}}}}, `"Phil" == "Phil"`, nil}, {`"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"`, map[string]interface{}{"a": map[string]interface{}{"b": map[string]interface{}{"c": map[string]interface{}{"d": map[string]interface{}{"e": map[string]string{"name": "Phil"}}}}}, "b": map[string]interface{}{"c": map[string]interface{}{"d": map[string]interface{}{"e": map[string]string{"name": "Wrong"}}}}}, `"Phil" == "Phil"`, nil}, + {`{{a.b.0.c}} => {{a.b.1.c}}`, + map[string]interface{}{ + "a": map[string]interface{}{ + "b": []map[string]interface{}{ + { + "c": "hello", + }, + { + "c": "ehlo", + }, + }, + }, + }, + "hello => ehlo", + nil, + }, } func TestBasic(t *testing.T) { @@ -323,15 +339,15 @@ func TestPartial(t *testing.T) { } /* -func TestSectionPartial(t *testing.T) { - filename := path.Join(path.Join(os.Getenv("PWD"), "tests"), "test3.mustache") - expected := "Mike\nJoe\n" - context := map[string]interface{}{"users": []User{{"Mike", 1}, {"Joe", 2}}} - output := RenderFile(filename, context) - if output != expected { - t.Fatalf("testSectionPartial expected %q got %q", expected, output) - } -} + func TestSectionPartial(t *testing.T) { + filename := path.Join(path.Join(os.Getenv("PWD"), "tests"), "test3.mustache") + expected := "Mike\nJoe\n" + context := map[string]interface{}{"users": []User{{"Mike", 1}, {"Joe", 2}}} + output := RenderFile(filename, context) + if output != expected { + t.Fatalf("testSectionPartial expected %q got %q", expected, output) + } + } */ func TestMultiContext(t *testing.T) { output, err := Render(`{{hello}} {{World}}`, map[string]string{"hello": "hello"}, struct{ World string }{"world"}) From 2d1dfea780c8fa1f18fd034be04c2d7473ca72d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Pavl=C3=ADn?= Date: Thu, 2 Feb 2023 16:22:16 +0100 Subject: [PATCH 2/3] Add Experimental flag and move tests --- mustache.go | 7 ++++++ mustache_test.go | 59 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/mustache.go b/mustache.go index 22b3485..32b3db0 100644 --- a/mustache.go +++ b/mustache.go @@ -18,6 +18,10 @@ var ( // is true (the default), an empty string is emitted. If it is false, an error // is generated instead. AllowMissingVariables = true + // Experimental defines the behavior for experimental features (e.g. commonly + // used, but not part of the spec). If it is false (default) it will ignore + // the code paths, if true, it will allow you to leverage the experimental features. + Experimental = false ) // RenderFunc is provided to lambda functions for rendering. @@ -516,6 +520,9 @@ Outer: } continue Outer case reflect.Slice: + if !Experimental { + continue Outer + } index, err := strconv.Atoi(name) if err != nil { return v, err diff --git a/mustache_test.go b/mustache_test.go index e1db9f6..b4bb097 100644 --- a/mustache_test.go +++ b/mustache_test.go @@ -206,22 +206,6 @@ var tests = []Test{ {`"{{{person.name}}}" == "{{#person}}{{{name}}}{{/person}}"`, map[string]interface{}{"person": map[string]string{"name": "Joe"}}, `"Joe" == "Joe"`, nil}, {`"{{a.b.c.d.e.name}}" == "Phil"`, map[string]interface{}{"a": map[string]interface{}{"b": map[string]interface{}{"c": map[string]interface{}{"d": map[string]interface{}{"e": map[string]string{"name": "Phil"}}}}}}, `"Phil" == "Phil"`, nil}, {`"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"`, map[string]interface{}{"a": map[string]interface{}{"b": map[string]interface{}{"c": map[string]interface{}{"d": map[string]interface{}{"e": map[string]string{"name": "Phil"}}}}}, "b": map[string]interface{}{"c": map[string]interface{}{"d": map[string]interface{}{"e": map[string]string{"name": "Wrong"}}}}}, `"Phil" == "Phil"`, nil}, - {`{{a.b.0.c}} => {{a.b.1.c}}`, - map[string]interface{}{ - "a": map[string]interface{}{ - "b": []map[string]interface{}{ - { - "c": "hello", - }, - { - "c": "ehlo", - }, - }, - }, - }, - "hello => ehlo", - nil, - }, } func TestBasic(t *testing.T) { @@ -732,3 +716,46 @@ func compareTags(t *testing.T, actual []Tag, expected []tag) { } } } + +var experimental = []Test{ + {`{{a.b.0.c}}{{a.b.1.c}}`, + map[string]interface{}{ + "a": map[string]interface{}{ + "b": []map[string]interface{}{ + { + "c": "hello", + }, + { + "c": "ehlo", + }, + }, + }, + }, + "helloehlo", + nil, + }, +} + +func TestExperimental(t *testing.T) { + // Default behavior, Experimental=false + for _, test := range experimental { + output, err := Render(test.tmpl, test.context) + if err != nil { + t.Errorf("%q expected %q but got error %q", test.tmpl, test.expected, err.Error()) + } else if output != "" { + t.Errorf("%q expected %q got %q", test.tmpl, test.expected, output) + } + } + + // Now set Experimental=true and test again + Experimental = true + defer func() { Experimental = false }() + for _, test := range experimental { + output, err := Render(test.tmpl, test.context) + if err != nil { + t.Errorf("%s expected %s but got error %s", test.tmpl, test.expected, err.Error()) + } else if output != test.expected { + t.Errorf("%q expected %q got %q", test.tmpl, test.expected, output) + } + } +} From 30855ffd2468837ec9059cc03bb5c039ac095d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Pavl=C3=ADn?= Date: Thu, 2 Feb 2023 16:43:31 +0100 Subject: [PATCH 3/3] Add the experimental option to cmd --- cmd/mustache/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/mustache/main.go b/cmd/mustache/main.go index e31536e..9facbd8 100644 --- a/cmd/mustache/main.go +++ b/cmd/mustache/main.go @@ -33,6 +33,8 @@ func main() { rootCmd.Flags().StringVar(&layoutFile, "layout", "", "location of layout file") rootCmd.Flags().StringVar(&overrideFile, "override", "", "location of data.yml override yml") rootCmd.Flags().BoolVar(&mustache.AllowMissingVariables, "allow-missing-variables", true, "allow missing variables") + rootCmd.Flags().BoolVar(&mustache.Experimental, "experimental", false, "allow experimental features") + if err := rootCmd.Execute(); err != nil { os.Exit(1) }