From f8f0e19982126ad0bd45a26e20cf42dc6ec8934e Mon Sep 17 00:00:00 2001 From: Gabe Cook Date: Mon, 17 Apr 2023 11:29:16 -0500 Subject: [PATCH] Add YAML funcs: `fromYaml`, `toYaml`, `mustFromYaml`, `mustToYaml` --- defaults.go | 29 +++++++++++++++++++++++++++++ defaults_test.go | 26 ++++++++++++++++++++++++++ docs/defaults.md | 18 ++++++++++++++++++ docs/index.md | 2 +- functions.go | 4 ++++ go.mod | 1 + go.sum | 2 ++ 7 files changed, 81 insertions(+), 1 deletion(-) diff --git a/defaults.go b/defaults.go index b9f97966..8efef884 100644 --- a/defaults.go +++ b/defaults.go @@ -7,6 +7,8 @@ import ( "reflect" "strings" "time" + + "gopkg.in/yaml.v3" ) func init() { @@ -161,3 +163,30 @@ func ternary(vt interface{}, vf interface{}, v bool) interface{} { return vf } + +// fromYaml decodes YAML into a structured value, ignoring errors. +func fromYaml(v string) interface{} { + output, _ := mustFromYaml(v) + return output +} + +// mustFromYaml decodes YAML into a structured value, returning errors. +func mustFromYaml(v string) (interface{}, error) { + var output interface{} + err := yaml.Unmarshal([]byte(v), &output) + return output, err +} + +// toYaml encodes an item into a YAML string +func toYaml(v interface{}) string { + output, _ := yaml.Marshal(v) + return string(output) +} + +func mustToYaml(v interface{}) (string, error) { + output, err := yaml.Marshal(v) + if err != nil { + return "", err + } + return string(output), nil +} diff --git a/defaults_test.go b/defaults_test.go index a35ebf62..ee27109d 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -194,3 +194,29 @@ func TestTernary(t *testing.T) { t.Error(err) } } + +func TestFromYaml(t *testing.T) { + dict := map[string]interface{}{"Input": `foo: 55`} + + tpl := `{{.Input | fromYaml}}` + expected := `map[foo:55]` + if err := runtv(tpl, expected, dict); err != nil { + t.Error(err) + } + + tpl = `{{(.Input | fromYaml).foo}}` + expected = `55` + if err := runtv(tpl, expected, dict); err != nil { + t.Error(err) + } +} + +func TestToYaml(t *testing.T) { + dict := map[string]interface{}{"Top": map[string]interface{}{"bool": true, "string": "test", "number": 42}} + + tpl := `{{.Top | toYaml}}` + expected := "bool: true\nnumber: 42\nstring: test\n" + if err := runtv(tpl, expected, dict); err != nil { + t.Error(err) + } +} diff --git a/docs/defaults.md b/docs/defaults.md index b68cd83f..333078d4 100644 --- a/docs/defaults.md +++ b/docs/defaults.md @@ -167,3 +167,21 @@ false | ternary "foo" "bar" ``` The above returns `"bar"`. + +## fromYaml, mustFromYaml + +`fromYaml` decodes a YAML document into a structure. If the input cannot be decoded as YAML the function will return an empty string. +`mustFromYaml` will return an error in case the YAML is invalid. + +``` +fromYaml "foo: 55" +``` + +## toYaml, mustToYaml + +The `toYaml` function encodes an item into a YAML string. If the item cannot be converted to YAML the function will return an empty string. +`mustToYaml` will return an error in case the item cannot be encoded in YAML. + +``` +toYaml .Item +``` diff --git a/docs/index.md b/docs/index.md index 7e898fa5..066d1387 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ The Sprig library provides over 70 template functions for Go's template language - [Integer Slice Functions](integer_slice.md): `until`, `untilStep` - [Float Math Functions](mathf.md): `addf`, `maxf`, `mulf`, etc. - [Date Functions](date.md): `now`, `date`, etc. -- [Defaults Functions](defaults.md): `default`, `empty`, `coalesce`, `fromJson`, `toJson`, `toPrettyJson`, `toRawJson`, `ternary` +- [Defaults Functions](defaults.md): `default`, `empty`, `coalesce`, `fromJson`, `toJson`, `toPrettyJson`, `toRawJson`, `ternary`, `fromYaml`, `toYaml` - [Encoding Functions](encoding.md): `b64enc`, `b64dec`, etc. - [Lists and List Functions](lists.md): `list`, `first`, `uniq`, etc. - [Dictionaries and Dict Functions](dicts.md): `get`, `set`, `dict`, `hasKey`, `pluck`, `dig`, `deepCopy`, etc. diff --git a/functions.go b/functions.go index 57fcec1d..62bfac96 100644 --- a/functions.go +++ b/functions.go @@ -252,6 +252,10 @@ var genericMap = map[string]interface{}{ "mustToPrettyJson": mustToPrettyJson, "mustToRawJson": mustToRawJson, "ternary": ternary, + "fromYaml": fromYaml, + "toYaml": toYaml, + "mustFromYaml": mustFromYaml, + "mustToYaml": mustToYaml, "deepCopy": deepCopy, "mustDeepCopy": mustDeepCopy, diff --git a/go.mod b/go.mod index 494916f1..c96ec052 100644 --- a/go.mod +++ b/go.mod @@ -13,4 +13,5 @@ require ( github.com/spf13/cast v1.3.1 github.com/stretchr/testify v1.5.1 golang.org/x/crypto v0.3.0 + gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 11a67119..8fe52150 100644 --- a/go.sum +++ b/go.sum @@ -59,3 +59,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=