diff --git a/pkg/template/funcs.go b/pkg/template/funcs.go index 0e123185b..43aba9d17 100644 --- a/pkg/template/funcs.go +++ b/pkg/template/funcs.go @@ -33,7 +33,7 @@ func SplitLines(o interface{}) ([]string, error) { // FromJSON decode the input JSON encoded as string or byte slice into a map. func FromJSON(o interface{}) (interface{}, error) { - ret := map[string]interface{}{} + var ret interface{} switch o := o.(type) { case string: err := json.Unmarshal([]byte(o), &ret) @@ -136,11 +136,37 @@ func (t *Template) DefaultFuncs() map[string]interface{} { return make([]struct{}, c) }, - "var": func(name, doc string, v ...interface{}) interface{} { + "def": func(name string, args ...interface{}) (string, error) { + if _, has := t.defaults[name]; has { + // not sure if this is good, but should complain loudly + return "", fmt.Errorf("already defined: %v", name) + } + var doc string + var value interface{} + switch len(args) { + case 1: + // just value, no docs + value = args[0] + case 2: + // docs and value + doc = fmt.Sprintf("%v", args[0]) + value = args[1] + } + t.defaults[name] = defaultValue{ + Name: name, + Value: value, + Doc: doc, + } + return "", nil + }, + + "ref": func(name string) interface{} { if found, has := t.binds[name]; has { return found + } else if v, has := t.defaults[name]; has { + return v.Value } - return v // default + return nil }, "global": func(name string, v interface{}) interface{} { diff --git a/pkg/template/funcs_test.go b/pkg/template/funcs_test.go index 7733e5c7b..e70d41022 100644 --- a/pkg/template/funcs_test.go +++ b/pkg/template/funcs_test.go @@ -57,6 +57,14 @@ func TestQueryObjectEncodeDecode(t *testing.T) { require.NoError(t, err) require.Equal(t, decoded, decoded2) + + decoded, err = FromJSON("[]") + require.NoError(t, err) + require.Equal(t, []interface{}{}, decoded) + + decoded, err = FromJSON(`{"foo":"bar"}`) + require.NoError(t, err) + require.Equal(t, map[string]interface{}{"foo": "bar"}, decoded) } func TestQueryObject(t *testing.T) { diff --git a/pkg/template/template.go b/pkg/template/template.go index 3c3d85998..96095b075 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -45,16 +45,23 @@ type Options struct { SocketDir string } +type defaultValue struct { + Name string + Value interface{} + Doc string +} + // Template is the templating engine type Template struct { options Options - url string - body []byte - parsed *template.Template - funcs map[string]interface{} - binds map[string]interface{} - lock sync.Mutex + url string + body []byte + parsed *template.Template + funcs map[string]interface{} + binds map[string]interface{} + defaults map[string]defaultValue + lock sync.Mutex } // NewTemplate fetches the content at the url and returns a template. If the string begins @@ -84,11 +91,12 @@ func NewTemplateFromBytes(buff []byte, contextURL string, opt Options) (*Templat } return &Template{ - options: opt, - url: contextURL, - body: buff, - funcs: map[string]interface{}{}, - binds: map[string]interface{}{}, + options: opt, + url: contextURL, + body: buff, + funcs: map[string]interface{}{}, + binds: map[string]interface{}{}, + defaults: map[string]defaultValue{}, }, nil } diff --git a/pkg/template/template_test.go b/pkg/template/template_test.go index a67076369..35ece01a7 100644 --- a/pkg/template/template_test.go +++ b/pkg/template/template_test.go @@ -34,10 +34,15 @@ func TestVarAndGlobal(t *testing.T) { str := `{{ q "locations[?state == 'WA'].name | sort(@) | {WashingtonCities: join(', ', @)}" . | global "washington-cities"}} {{/* The query above is exported and referenced somewhere else */}} +{{ from_json "[\"SF\",\"LA\"]" | def "california-cities" "Default value for California cities" }} +{{ from_json "{\"SF\":\"94109\",\"LA\":\"90210\"}" | def "zip-codes" "Default value for zip codes" }} + { "test" : "hello", "val" : true, - "result" : {{var "washington-cities" "A json with washington cities" | to_json}} + "result" : {{ref "washington-cities" | to_json}}, + "california" : {{ ref "california-cities" | to_json}}, + "sf_zip" : {{ ref "zip-codes" | q "SF" | to_json }} } ` @@ -59,12 +64,20 @@ func TestVarAndGlobal(t *testing.T) { expected := ` + + + { "test" : "hello", "val" : true, "result" : { "WashingtonCities": "Bellevue, Olympia, Seattle" -} +}, + "california" : [ + "SF", + "LA" +], + "sf_zip" : "94109" } ` require.Equal(t, expected, view)