From 992fe066aa034ab81d82eaf9a6e5638b7b75076e Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Tue, 1 Feb 2022 15:34:17 +1100 Subject: [PATCH] Improved property conversion docs --- .../doc/operators/env-variable-operators.md | 2 +- pkg/yqlib/doc/usage/headers/properties.md | 5 + pkg/yqlib/doc/usage/properties.md | 90 +++++++++++++++ pkg/yqlib/json_test.go | 8 +- pkg/yqlib/properties_test.go | 104 ++++++++++++++++++ 5 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 pkg/yqlib/doc/usage/headers/properties.md create mode 100644 pkg/yqlib/doc/usage/properties.md create mode 100644 pkg/yqlib/properties_test.go diff --git a/pkg/yqlib/doc/operators/env-variable-operators.md b/pkg/yqlib/doc/operators/env-variable-operators.md index 8f0d98db..1050dfa5 100644 --- a/pkg/yqlib/doc/operators/env-variable-operators.md +++ b/pkg/yqlib/doc/operators/env-variable-operators.md @@ -89,7 +89,7 @@ a: ``` then ```bash -valueEnv="moo" pathEnv=".a.b[0].name" yq 'eval(strenv(pathEnv)) = strenv(valueEnv)' sample.yml +pathEnv=".a.b[0].name" valueEnv="moo" yq 'eval(strenv(pathEnv)) = strenv(valueEnv)' sample.yml ``` will output ```yaml diff --git a/pkg/yqlib/doc/usage/headers/properties.md b/pkg/yqlib/doc/usage/headers/properties.md new file mode 100644 index 00000000..c0c1910e --- /dev/null +++ b/pkg/yqlib/doc/usage/headers/properties.md @@ -0,0 +1,5 @@ +# Properties + +Encode to a property file (decode not yet supported). Line comments on value nodes will be copied across. + +By default, empty maps and arrays are not encoded - see below for an example on how to encode a value for these. diff --git a/pkg/yqlib/doc/usage/properties.md b/pkg/yqlib/doc/usage/properties.md new file mode 100644 index 00000000..76dc6318 --- /dev/null +++ b/pkg/yqlib/doc/usage/properties.md @@ -0,0 +1,90 @@ +# Properties + +Encode to a property file (decode not yet supported). Line comments on value nodes will be copied across. + +By default, empty maps and arrays are not encoded - see below for an example on how to encode a value for these. + +## Encode properties +Note that empty arrays and maps are not encoded by default. + +Given a sample.yml file of: +```yaml +# block comments don't come through +person: # neither do comments on maps + name: Mike # comments on values appear + pets: + - cat # comments on array values appear + food: [pizza] # comments on arrays do not +emptyArray: [] +emptyMap: [] + +``` +then +```bash +yq -o=props -I=0 '.' sample.yml +``` +will output +```properties +# comments on values appear +person.name = Mike + +# comments on array values appear +person.pets.0 = cat +person.food.0 = pizza +``` + +## Encode properties: no comments +Given a sample.yml file of: +```yaml +# block comments don't come through +person: # neither do comments on maps + name: Mike # comments on values appear + pets: + - cat # comments on array values appear + food: [pizza] # comments on arrays do not +emptyArray: [] +emptyMap: [] + +``` +then +```bash +yq -o=props -I=0 '... comments = ""' sample.yml +``` +will output +```properties +person.name = Mike +person.pets.0 = cat +person.food.0 = pizza +``` + +## Encode properties: include empty maps and arrays +Use a yq expression to set the empty maps and sequences to your desired value. + +Given a sample.yml file of: +```yaml +# block comments don't come through +person: # neither do comments on maps + name: Mike # comments on values appear + pets: + - cat # comments on array values appear + food: [pizza] # comments on arrays do not +emptyArray: [] +emptyMap: [] + +``` +then +```bash +yq -o=props -I=0 '(.. | select( (tag == "!!map" or tag =="!!seq") and length == 0)) = ""' sample.yml +``` +will output +```properties +# comments on values appear +person.name = Mike + +# comments on array values appear +person.pets.0 = cat +person.food.0 = pizza +emptyArray = +emptyMap = +``` + diff --git a/pkg/yqlib/json_test.go b/pkg/yqlib/json_test.go index addbcdac..0680e09b 100644 --- a/pkg/yqlib/json_test.go +++ b/pkg/yqlib/json_test.go @@ -98,20 +98,18 @@ func decodeJson(t *testing.T, jsonString string) *CandidateNode { func testJsonScenario(t *testing.T, s formatScenario) { if s.scenarioType == "encode" || s.scenarioType == "roundtrip" { - test.AssertResultWithContext(t, s.expected, processJsonScenario(s), s.description) + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewJsonEncoder(s.indent)), s.description) } else { var actual = resultToString(t, decodeJson(t, s.input)) test.AssertResultWithContext(t, s.expected, actual, s.description) } } -func processJsonScenario(s formatScenario) string { +func processFormatScenario(s formatScenario, encoder Encoder) string { var output bytes.Buffer writer := bufio.NewWriter(&output) - var encoder = NewJsonEncoder(s.indent) - var decoder = NewYamlDecoder() inputs, err := readDocuments(strings.NewReader(s.input), "sample.yml", 0, decoder) @@ -214,7 +212,7 @@ func documentJsonEncodeScenario(w *bufio.Writer, s formatScenario) { } writeOrPanic(w, "will output\n") - writeOrPanic(w, fmt.Sprintf("```json\n%v```\n\n", processJsonScenario(s))) + writeOrPanic(w, fmt.Sprintf("```json\n%v```\n\n", processFormatScenario(s, NewJsonEncoder(s.indent)))) } func TestJsonScenarios(t *testing.T) { diff --git a/pkg/yqlib/properties_test.go b/pkg/yqlib/properties_test.go new file mode 100644 index 00000000..d136b008 --- /dev/null +++ b/pkg/yqlib/properties_test.go @@ -0,0 +1,104 @@ +package yqlib + +import ( + "bufio" + "fmt" + "testing" + + "github.com/mikefarah/yq/v4/test" +) + +var samplePropertiesYaml = `# block comments don't come through +person: # neither do comments on maps + name: Mike # comments on values appear + pets: + - cat # comments on array values appear + food: [pizza] # comments on arrays do not +emptyArray: [] +emptyMap: [] +` + +var expectedProperties = `# comments on values appear +person.name = Mike + +# comments on array values appear +person.pets.0 = cat +person.food.0 = pizza +` + +var expectedPropertiesNoComments = `person.name = Mike +person.pets.0 = cat +person.food.0 = pizza +` + +var expectedPropertiesWithEmptyMapsAndArrays = `# comments on values appear +person.name = Mike + +# comments on array values appear +person.pets.0 = cat +person.food.0 = pizza +emptyArray = +emptyMap = +` + +var propertyScenarios = []formatScenario{ + { + description: "Encode properties", + subdescription: "Note that empty arrays and maps are not encoded by default.", + input: samplePropertiesYaml, + expected: expectedProperties, + }, + { + description: "Encode properties: no comments", + input: samplePropertiesYaml, + expected: expectedPropertiesNoComments, + expression: `... comments = ""`, + }, + { + description: "Encode properties: include empty maps and arrays", + subdescription: "Use a yq expression to set the empty maps and sequences to your desired value.", + expression: `(.. | select( (tag == "!!map" or tag =="!!seq") and length == 0)) = ""`, + input: samplePropertiesYaml, + expected: expectedPropertiesWithEmptyMapsAndArrays, + }, +} + +func documentPropertyScenario(t *testing.T, w *bufio.Writer, i interface{}) { + s := i.(formatScenario) + writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) + + if s.subdescription != "" { + writeOrPanic(w, s.subdescription) + writeOrPanic(w, "\n\n") + } + + writeOrPanic(w, "Given a sample.yml file of:\n") + writeOrPanic(w, fmt.Sprintf("```yaml\n%v\n```\n", s.input)) + + writeOrPanic(w, "then\n") + + expression := s.expression + if expression == "" { + expression = "." + } + + if s.indent == 2 { + writeOrPanic(w, fmt.Sprintf("```bash\nyq -o=props '%v' sample.yml\n```\n", expression)) + } else { + writeOrPanic(w, fmt.Sprintf("```bash\nyq -o=props -I=%v '%v' sample.yml\n```\n", s.indent, expression)) + } + writeOrPanic(w, "will output\n") + + writeOrPanic(w, fmt.Sprintf("```properties\n%v```\n\n", processFormatScenario(s, NewPropertiesEncoder()))) +} + +func TestPropertyScenarios(t *testing.T) { + for _, s := range propertyScenarios { + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewPropertiesEncoder()), s.description) + } + genericScenarios := make([]interface{}, len(propertyScenarios)) + for i, s := range propertyScenarios { + genericScenarios[i] = s + } + documentScenarios(t, "usage", "properties", genericScenarios, documentPropertyScenario) +}