From 3b8497a31aa2d80ae7f20addf0f852f69a810da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johahnnes=20Gr=C3=A4ger?= Date: Wed, 24 Jan 2024 17:08:12 +0100 Subject: [PATCH 1/2] Builtins for escapeStringJson and manifestYamlDoc --- builtins.go | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 257 insertions(+), 9 deletions(-) diff --git a/builtins.go b/builtins.go index e426b069..36616fa3 100644 --- a/builtins.go +++ b/builtins.go @@ -29,6 +29,7 @@ import ( "io" "math" "reflect" + "regexp" "sort" "strconv" "strings" @@ -233,17 +234,25 @@ func builtinLength(i *interpreter, x value) (value, error) { return makeValueNumber(float64(num)), nil } -func builtinToString(i *interpreter, x value) (value, error) { +func valueToString(i *interpreter, x value) (string, error) { switch x := x.(type) { case valueString: - return x, nil + return x.getGoString(), nil } + var buf bytes.Buffer - err := i.manifestAndSerializeJSON(&buf, x, false, "") + if err := i.manifestAndSerializeJSON(&buf, x, false, ""); err != nil { + return "", err + } + return buf.String(), nil +} + +func builtinToString(i *interpreter, x value) (value, error) { + s, err := valueToString(i, x) if err != nil { return nil, err } - return makeValueString(buf.String()), nil + return makeValueString(s), nil } func builtinTrace(i *interpreter, x value, y value) (value, error) { @@ -1556,8 +1565,16 @@ func tomlIsSection(i *interpreter, val value) (bool, error) { } } -// tomlEncodeString encodes a string as quoted TOML string -func tomlEncodeString(s string) string { +func builtinEscapeStringJson(i *interpreter, v value) (value, error) { + s, err := valueToString(i, v) + if err != nil { + return nil, err + } + + return makeValueString(escapeStringJson(s)), nil +} + +func escapeStringJson(s string) string { res := "\"" for _, c := range s { @@ -1589,6 +1606,11 @@ func tomlEncodeString(s string) string { return res } +// tomlEncodeString encodes a string as quoted TOML string +func tomlEncodeString(s string) string { + return escapeStringJson(s) +} + // tomlEncodeKey encodes a key - returning same string if it does not need quoting, // otherwise return it quoted; returns empty key as ” func tomlEncodeKey(s string) string { @@ -1980,6 +2002,226 @@ func builtinManifestJSONEx(i *interpreter, arguments []value) (value, error) { return makeValueString(finalString), nil } +const ( + yamlIndent = " " +) + +var ( + yamlReserved = []string{ + // Boolean types taken from https://yaml.org/type/bool.html + "true", "false", "yes", "no", "on", "off", "y", "n", + // Numerical words taken from https://yaml.org/type/float.html + ".nan", "-.inf", "+.inf", ".inf", "null", + // Invalid keys that contain no invalid characters + "-", "---", "''", + } + yamlTimestampPattern = regexp.MustCompile(`^(?:[0-9]*-){2}[0-9]*$`) + yamlBinaryPattern = regexp.MustCompile(`^[-+]?0b[0-1_]+$`) + yamlHexPattern = regexp.MustCompile(`[-+]?0x[0-9a-fA-F_]+`) +) + +func yamlIsReserved(s string) bool { + for _, r := range yamlReserved { + if strings.EqualFold(s, r) { + return true + } + } + return false +} + +func yamlBareSafe(s string) bool { + if len(s) == 0 { + return false + } + + // Starts with unsafe char + if strings.ContainsRune("&*?|<>=!%@", rune(s[0])) { + return false + } + + // String contains unsafe char + for _, c := range s { + if c == ':' || c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || + c == '#' || c == '`' || c == '"' || c == '\'' || c == '\\' || c == '~' || + (c >= '\x00' && c <= '\x06') || c == '\t' || c == '\n' || c == '\r' || + (c >= '\x0e' && c <= '\x1a') || (c >= '\x1c' && c <= '\x1f') { + return false + } + } + + if yamlIsReserved(s) { + return false + } + + if yamlTimestampPattern.MatchString(s) { + return false + } + + // Check binary / + if yamlBinaryPattern.MatchString(s) || yamlHexPattern.MatchString(s) { + return false + } + + // Is integer + if _, err := strconv.Atoi(s); err == nil { + return false + } + // Is float + if _, err := strconv.ParseFloat(s, 64); err == nil { + return false + } + + return true +} + +func builtinManifestYamlDoc(i *interpreter, arguments []value) (value, error) { + val := arguments[0] + vindentArrInObj, err := i.getBoolean(arguments[1]) + if err != nil { + return nil, err + } + vQuoteKeys, err := i.getBoolean(arguments[2]) + if err != nil { + return nil, err + } + + var buf strings.Builder + + var aux func(ov value, buf *strings.Builder, cindent string) error + aux = func(ov value, buf *strings.Builder, cindent string) error { + switch v := ov.(type) { + case *valueNull: + buf.WriteString("null") + case *valueBoolean: + if v.value { + buf.WriteString("true") + } else { + buf.WriteString("false") + } + case valueString: + s := v.getGoString() + if s == "" { + buf.WriteString(`""`) + } else if strings.HasSuffix(s, "\n") { + s := strings.TrimSuffix(s, "\n") + buf.WriteString("|") + for _, line := range strings.Split(s, "\n") { + buf.WriteByte('\n') + buf.WriteString(cindent) + buf.WriteString(yamlIndent) + buf.WriteString(line) + } + } else if strings.Contains(s, "\n") { + buf.WriteString("|-") + for _, line := range strings.Split(s, "\n") { + buf.WriteByte('\n') + buf.WriteString(cindent) + buf.WriteString(yamlIndent) + buf.WriteString(line) + } + } else { + buf.WriteString(escapeStringJson(s)) + } + case *valueNumber: + buf.WriteString(strconv.FormatFloat(v.value, 'f', -1, 64)) + case *valueArray: + if v.length() == 0 { + buf.WriteString("[]") + } else { + for ix, elem := range v.elements { + if ix != 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + } + thunkValue, err := elem.getValue(i) + if err != nil { + return err + } + buf.WriteByte('-') + + if v, isArr := thunkValue.(*valueArray); isArr && v.length() > 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + buf.WriteString(yamlIndent) + } else { + buf.WriteByte(' ') + } + + prevIndent := cindent + addIndent := false + switch thunkValue.(type) { + case *valueArray, *valueObject: + addIndent = true + } + if addIndent { + cindent = cindent + yamlIndent + } + + if err := aux(thunkValue, buf, cindent); err != nil { + return err + } + cindent = prevIndent + } + } + case *valueObject: + fields := objectFields(v, withoutHidden) + if len(fields) == 0 { + buf.WriteString("{}") + } else { + sort.Strings(fields) + for ix, fieldName := range fields { + fieldValue, err := v.index(i, fieldName) + if err != nil { + return err + } + + if ix != 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + } + + keyStr := fieldName + if vQuoteKeys.value || !yamlBareSafe(fieldName) { + keyStr = escapeStringJson(fieldName) + } + buf.WriteString(keyStr) + buf.WriteByte(':') + + prevIndent := cindent + if v, isArr := fieldValue.(*valueArray); isArr && v.length() > 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + if vindentArrInObj.value { + buf.WriteString(yamlIndent) + cindent = cindent + yamlIndent + } + } else if v, isObj := fieldValue.(*valueObject); isObj { + if len(objectFields(v, withoutHidden)) > 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + buf.WriteString(yamlIndent) + cindent = cindent + yamlIndent + } else { + buf.WriteByte(' ') + } + } else { + buf.WriteByte(' ') + } + aux(fieldValue, buf, cindent) + cindent = prevIndent + } + } + } + return nil + } + + if err := aux(val, &buf, ""); err != nil { + return nil, err + } + + return makeValueString(buf.String()), nil +} + func builtinExtVar(i *interpreter, name value) (value, error) { str, err := i.getString(name) if err != nil { @@ -2097,12 +2339,12 @@ func builtinAvg(i *interpreter, arrv value) (value, error) { if err != nil { return nil, err } - + len := float64(arr.length()) if len == 0 { return nil, i.Error("Cannot calculate average of an empty array.") } - + sumValue, err := builtinSum(i, arrv) if err != nil { return nil, err @@ -2112,7 +2354,7 @@ func builtinAvg(i *interpreter, arrv value) (value, error) { return nil, err } - avg := sum.value/len + avg := sum.value / len return makeValueNumber(avg), nil } @@ -2459,6 +2701,7 @@ var funcBuiltins = buildBuiltinMap([]builtin{ &unaryBuiltin{name: "extVar", function: builtinExtVar, params: ast.Identifiers{"x"}}, &unaryBuiltin{name: "length", function: builtinLength, params: ast.Identifiers{"x"}}, &unaryBuiltin{name: "toString", function: builtinToString, params: ast.Identifiers{"a"}}, + &unaryBuiltin{name: "escapeStringJson", function: builtinEscapeStringJson, params: ast.Identifiers{"str_"}}, &binaryBuiltin{name: "trace", function: builtinTrace, params: ast.Identifiers{"str", "rest"}}, &binaryBuiltin{name: "makeArray", function: builtinMakeArray, params: ast.Identifiers{"sz", "func"}}, &binaryBuiltin{name: "flatMap", function: builtinFlatMap, params: ast.Identifiers{"func", "arr"}}, @@ -2524,6 +2767,11 @@ var funcBuiltins = buildBuiltinMap([]builtin{ {name: "newline", defaultValue: &valueFlatString{value: []rune("\n")}}, {name: "key_val_sep", defaultValue: &valueFlatString{value: []rune(": ")}}}}, &generalBuiltin{name: "manifestTomlEx", function: builtinManifestTomlEx, params: []generalBuiltinParameter{{name: "value"}, {name: "indent"}}}, + &generalBuiltin{name: "manifestYamlDoc", function: builtinManifestYamlDoc, params: []generalBuiltinParameter{ + {name: "value"}, + {name: "indent_array_in_object", defaultValue: &valueBoolean{value: false}}, + {name: "quote_keys", defaultValue: &valueBoolean{value: true}}, + }}, &unaryBuiltin{name: "base64", function: builtinBase64, params: ast.Identifiers{"input"}}, &unaryBuiltin{name: "encodeUTF8", function: builtinEncodeUTF8, params: ast.Identifiers{"str"}}, &unaryBuiltin{name: "decodeUTF8", function: builtinDecodeUTF8, params: ast.Identifiers{"arr"}}, From 8f78ece3b74c480519b3d6cb7bcef00959ec3b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johahnnes=20Gr=C3=A4ger?= Date: Fri, 26 Jan 2024 14:48:09 +0100 Subject: [PATCH 2/2] Benchmark and tests --- builtin-benchmarks/escapeStringJson.jsonnet | 1 + builtin-benchmarks/manifestYamlDoc.jsonnet | 51 ++++++ builtins.go | 163 ++++++++---------- builtins_benchmark_test.go | 8 + testdata/builtin_escapeStringJson.golden | 17 ++ testdata/builtin_escapeStringJson.jsonnet | 17 ++ .../builtin_escapeStringJson.linter.golden | 0 testdata/builtin_manifestYamlDoc.golden | 14 ++ testdata/builtin_manifestYamlDoc.jsonnet | 105 +++++++++++ .../builtin_manifestYamlDoc.linter.golden | 0 10 files changed, 286 insertions(+), 90 deletions(-) create mode 100644 builtin-benchmarks/escapeStringJson.jsonnet create mode 100644 builtin-benchmarks/manifestYamlDoc.jsonnet create mode 100644 testdata/builtin_escapeStringJson.golden create mode 100644 testdata/builtin_escapeStringJson.jsonnet create mode 100644 testdata/builtin_escapeStringJson.linter.golden create mode 100644 testdata/builtin_manifestYamlDoc.golden create mode 100644 testdata/builtin_manifestYamlDoc.jsonnet create mode 100644 testdata/builtin_manifestYamlDoc.linter.golden diff --git a/builtin-benchmarks/escapeStringJson.jsonnet b/builtin-benchmarks/escapeStringJson.jsonnet new file mode 100644 index 00000000..df5ffff6 --- /dev/null +++ b/builtin-benchmarks/escapeStringJson.jsonnet @@ -0,0 +1 @@ +std.escapeStringJson("Lorem ipsum dolor sit amet, consectetur \"adipiscing\" elit. Nullam \\nec sagittis \\u0065lit, sed do \\teiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco \flaboris nisi ut aliquip ex ea commodo consequat.\rDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n\tExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit \\anim id est laborum.\nCurabitur pretium tincidunt lacus. Nulla gravida orci a odio. \\Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris.\nInteger in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst.") diff --git a/builtin-benchmarks/manifestYamlDoc.jsonnet b/builtin-benchmarks/manifestYamlDoc.jsonnet new file mode 100644 index 00000000..fe294fe0 --- /dev/null +++ b/builtin-benchmarks/manifestYamlDoc.jsonnet @@ -0,0 +1,51 @@ +{ + bar: { + prometheusOperator+: { + service+: { + spec+: { + ports: [ + { + name: 'https', + port: 8443, + targetPort: 'https', + }, + ], + }, + }, + serviceMonitor+: { + spec+: { + endpoints: [ + { + port: 'https', + scheme: 'https', + honorLabels: true, + bearerTokenFile: '/var/run/secrets/kubernetes.io/serviceaccount/token', + tlsConfig: { + insecureSkipVerify: true, + }, + }, + ], + }, + }, + clusterRole+: { + rules+: [ + { + apiGroups: ['authentication.k8s.io'], + resources: ['tokenreviews'], + verbs: ['create'], + }, + { + apiGroups: ['authorization.k8s.io'], + resources: ['subjectaccessreviews'], + verbs: ['create'], + }, + ], + }, + }, + additional+: { + '$schema': "http://json-schema.org/draft-07/schema#", + '09': ['no', 'yes'], + }, + }, + nothing: std.manifestYamlDoc(self.bar, indent_array_in_object=true, quote_keys=true), +} diff --git a/builtins.go b/builtins.go index 36616fa3..1191c7d2 100644 --- a/builtins.go +++ b/builtins.go @@ -1571,7 +1571,7 @@ func builtinEscapeStringJson(i *interpreter, v value) (value, error) { return nil, err } - return makeValueString(escapeStringJson(s)), nil + return makeValueString(unparseString(s)), nil } func escapeStringJson(s string) string { @@ -1608,7 +1608,7 @@ func escapeStringJson(s string) string { // tomlEncodeString encodes a string as quoted TOML string func tomlEncodeString(s string) string { - return escapeStringJson(s) + return unparseString(s) } // tomlEncodeKey encodes a key - returning same string if it does not need quoting, @@ -2020,7 +2020,7 @@ var ( yamlHexPattern = regexp.MustCompile(`[-+]?0x[0-9a-fA-F_]+`) ) -func yamlIsReserved(s string) bool { +func yamlReservedString(s string) bool { for _, r := range yamlReserved { if strings.EqualFold(s, r) { return true @@ -2034,22 +2034,17 @@ func yamlBareSafe(s string) bool { return false } - // Starts with unsafe char - if strings.ContainsRune("&*?|<>=!%@", rune(s[0])) { - return false - } - // String contains unsafe char for _, c := range s { - if c == ':' || c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || - c == '#' || c == '`' || c == '"' || c == '\'' || c == '\\' || c == '~' || - (c >= '\x00' && c <= '\x06') || c == '\t' || c == '\n' || c == '\r' || - (c >= '\x0e' && c <= '\x1a') || (c >= '\x1c' && c <= '\x1f') { + isAlpha := (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + isDigit := c >= '0' && c <= '9' + + if !isAlpha && !isDigit && c != '_' && c != '-' && c != '/' && c != '.' { return false } } - if yamlIsReserved(s) { + if yamlReservedString(s) { return false } @@ -2085,10 +2080,10 @@ func builtinManifestYamlDoc(i *interpreter, arguments []value) (value, error) { return nil, err } - var buf strings.Builder + var buf bytes.Buffer - var aux func(ov value, buf *strings.Builder, cindent string) error - aux = func(ov value, buf *strings.Builder, cindent string) error { + var aux func(ov value, buf *bytes.Buffer, cindent string) error + aux = func(ov value, buf *bytes.Buffer, cindent string) error { switch v := ov.(type) { case *valueNull: buf.WriteString("null") @@ -2111,105 +2106,93 @@ func builtinManifestYamlDoc(i *interpreter, arguments []value) (value, error) { buf.WriteString(yamlIndent) buf.WriteString(line) } - } else if strings.Contains(s, "\n") { - buf.WriteString("|-") - for _, line := range strings.Split(s, "\n") { - buf.WriteByte('\n') - buf.WriteString(cindent) - buf.WriteString(yamlIndent) - buf.WriteString(line) - } } else { - buf.WriteString(escapeStringJson(s)) + buf.WriteString(unparseString(s)) } case *valueNumber: buf.WriteString(strconv.FormatFloat(v.value, 'f', -1, 64)) case *valueArray: if v.length() == 0 { buf.WriteString("[]") - } else { - for ix, elem := range v.elements { - if ix != 0 { - buf.WriteByte('\n') - buf.WriteString(cindent) - } - thunkValue, err := elem.getValue(i) - if err != nil { - return err - } - buf.WriteByte('-') + return nil + } + for ix, elem := range v.elements { + if ix != 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + } + thunkValue, err := elem.getValue(i) + if err != nil { + return err + } + buf.WriteByte('-') - if v, isArr := thunkValue.(*valueArray); isArr && v.length() > 0 { - buf.WriteByte('\n') - buf.WriteString(cindent) - buf.WriteString(yamlIndent) - } else { - buf.WriteByte(' ') - } + if v, isArr := thunkValue.(*valueArray); isArr && v.length() > 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + buf.WriteString(yamlIndent) + } else { + buf.WriteByte(' ') + } - prevIndent := cindent - addIndent := false - switch thunkValue.(type) { - case *valueArray, *valueObject: - addIndent = true - } - if addIndent { - cindent = cindent + yamlIndent - } + prevIndent := cindent + switch thunkValue.(type) { + case *valueArray, *valueObject: + cindent = cindent + yamlIndent + } - if err := aux(thunkValue, buf, cindent); err != nil { - return err - } - cindent = prevIndent + if err := aux(thunkValue, buf, cindent); err != nil { + return err } + cindent = prevIndent } case *valueObject: fields := objectFields(v, withoutHidden) if len(fields) == 0 { buf.WriteString("{}") - } else { - sort.Strings(fields) - for ix, fieldName := range fields { - fieldValue, err := v.index(i, fieldName) - if err != nil { - return err - } + return nil + } + sort.Strings(fields) + for ix, fieldName := range fields { + fieldValue, err := v.index(i, fieldName) + if err != nil { + return err + } - if ix != 0 { - buf.WriteByte('\n') - buf.WriteString(cindent) - } + if ix != 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + } - keyStr := fieldName - if vQuoteKeys.value || !yamlBareSafe(fieldName) { - keyStr = escapeStringJson(fieldName) - } - buf.WriteString(keyStr) - buf.WriteByte(':') + keyStr := fieldName + if vQuoteKeys.value || !yamlBareSafe(fieldName) { + keyStr = escapeStringJson(fieldName) + } + buf.WriteString(keyStr) + buf.WriteByte(':') - prevIndent := cindent - if v, isArr := fieldValue.(*valueArray); isArr && v.length() > 0 { + prevIndent := cindent + if v, isArr := fieldValue.(*valueArray); isArr && v.length() > 0 { + buf.WriteByte('\n') + buf.WriteString(cindent) + if vindentArrInObj.value { + buf.WriteString(yamlIndent) + cindent = cindent + yamlIndent + } + } else if v, isObj := fieldValue.(*valueObject); isObj { + if len(objectFields(v, withoutHidden)) > 0 { buf.WriteByte('\n') buf.WriteString(cindent) - if vindentArrInObj.value { - buf.WriteString(yamlIndent) - cindent = cindent + yamlIndent - } - } else if v, isObj := fieldValue.(*valueObject); isObj { - if len(objectFields(v, withoutHidden)) > 0 { - buf.WriteByte('\n') - buf.WriteString(cindent) - buf.WriteString(yamlIndent) - cindent = cindent + yamlIndent - } else { - buf.WriteByte(' ') - } + buf.WriteString(yamlIndent) + cindent = cindent + yamlIndent } else { buf.WriteByte(' ') } - aux(fieldValue, buf, cindent) - cindent = prevIndent + } else { + buf.WriteByte(' ') } + aux(fieldValue, buf, cindent) + cindent = prevIndent } } return nil diff --git a/builtins_benchmark_test.go b/builtins_benchmark_test.go index 20ba47e0..65a7d414 100644 --- a/builtins_benchmark_test.go +++ b/builtins_benchmark_test.go @@ -56,6 +56,10 @@ func Benchmark_Builtin_base64_byte_array(b *testing.B) { RunBenchmark(b, "base64_byte_array") } +func Benchmark_Builtin_escapeStringJson(b *testing.B) { + RunBenchmark(b, "escapeStringJson") +} + func Benchmark_Builtin_manifestJsonEx(b *testing.B) { RunBenchmark(b, "manifestJsonEx") } @@ -64,6 +68,10 @@ func Benchmark_Builtin_manifestTomlEx(b *testing.B) { RunBenchmark(b, "manifestTomlEx") } +func Benchmark_Builtin_manifestYamlDoc(b *testing.B) { + RunBenchmark(b, "manifestYamlDoc") +} + func Benchmark_Builtin_comparison(b *testing.B) { RunBenchmark(b, "comparison") } diff --git a/testdata/builtin_escapeStringJson.golden b/testdata/builtin_escapeStringJson.golden new file mode 100644 index 00000000..d2dabd34 --- /dev/null +++ b/testdata/builtin_escapeStringJson.golden @@ -0,0 +1,17 @@ +[ + "\"\"", + "\"\\\"quoted string\\\"\"", + "\"backslash \\\\\"", + "\"new\\nline\"", + "\"tab\\tcharacter\"", + "\"controler\\r\\fcharacters\\r\\f\"", + "\"null\"", + "\"null\"", + "\"1.4199999999999999\"", + "\"[1, 2, 3]\"", + "\"{\\\"a\\\": 1, \\\"b\\\": 2, \\\"c\\\": 3}\"", + "\"true\"", + "\"false\"", + "\"true\"", + "\"false\"" +] diff --git a/testdata/builtin_escapeStringJson.jsonnet b/testdata/builtin_escapeStringJson.jsonnet new file mode 100644 index 00000000..4817c845 --- /dev/null +++ b/testdata/builtin_escapeStringJson.jsonnet @@ -0,0 +1,17 @@ +std.map(std.escapeStringJson, [ + "", + "\"quoted string\"", + "backslash \\", + "new\nline", + "tab\tcharacter", + "controler\r\fcharacters\r\f", + null, + "null", + 1.42, + [1,2,3], + {a:1,b:2,c:3}, + true, + false, + "true", + "false", +]) diff --git a/testdata/builtin_escapeStringJson.linter.golden b/testdata/builtin_escapeStringJson.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtin_manifestYamlDoc.golden b/testdata/builtin_manifestYamlDoc.golden new file mode 100644 index 00000000..03ca87d1 --- /dev/null +++ b/testdata/builtin_manifestYamlDoc.golden @@ -0,0 +1,14 @@ +{ + "object": "\"abc\": \"def\"\n\"bam\": true\n\"bar\": \"baz\"\n\"baz\": 1\n\"bazel\": 1.42\n\"bim\": false\n\"blamo\":\n \"cereal\":\n - \"<>& fizbuzz\"\n -\n - \"a\"\n -\n - \"b\"\n \"treats\":\n - \"name\": \"chocolate\"\n\"boom\": -1\n\"foo\": \"baz\"", + "object2": "\"\\\"\": 4\n\"array\":\n- \"s\"\n- 1\n-\n - 2\n - 3\n- \"a\":\n - \"0\"\n - \"z\"\n \"r\": 6\n\"arraySection\":\n- \"q\": 1\n- \"w\": 2\n\"bool\": true\n\"emptyArray\": []\n\"emptyArraySection\":\n- {}\n\"emptySection\": {}\n\"escaped\\\"Section\":\n \"z\": \"q\"\n\"key\": \"value\"\n\"notBool\": false\n\"number\": 7\n\"section\":\n \"a\": 1\n \"array\":\n - \"c\": 3\n - \"d\": 4\n \"e$caped\":\n \"q\": \"t\"\n \"nested\":\n \"b\": 2\n \"nestedArray\":\n - \"k\": \"v\"\n \"nested\":\n \"e\": 5\n\"simple\":\n \"t\": 5", + "object2_indent": "\"\\\"\": 4\n\"array\":\n - \"s\"\n - 1\n -\n - 2\n - 3\n - \"a\":\n - \"0\"\n - \"z\"\n \"r\": 6\n\"arraySection\":\n - \"q\": 1\n - \"w\": 2\n\"bool\": true\n\"emptyArray\": []\n\"emptyArraySection\":\n - {}\n\"emptySection\": {}\n\"escaped\\\"Section\":\n \"z\": \"q\"\n\"key\": \"value\"\n\"notBool\": false\n\"number\": 7\n\"section\":\n \"a\": 1\n \"array\":\n - \"c\": 3\n - \"d\": 4\n \"e$caped\":\n \"q\": \"t\"\n \"nested\":\n \"b\": 2\n \"nestedArray\":\n - \"k\": \"v\"\n \"nested\":\n \"e\": 5\n\"simple\":\n \"t\": 5", + "object2_indent_unquoted": "\"\\\"\": 4\narray:\n - \"s\"\n - 1\n -\n - 2\n - 3\n - a:\n - \"0\"\n - \"z\"\n r: 6\narraySection:\n - q: 1\n - w: 2\nbool: true\nemptyArray: []\nemptyArraySection:\n - {}\nemptySection: {}\n\"escaped\\\"Section\":\n z: \"q\"\nkey: \"value\"\nnotBool: false\nnumber: 7\nsection:\n a: 1\n array:\n - c: 3\n - d: 4\n \"e$caped\":\n q: \"t\"\n nested:\n b: 2\n nestedArray:\n - k: \"v\"\n nested:\n e: 5\nsimple:\n t: 5", + "object2_unquoted": "\"\\\"\": 4\narray:\n- \"s\"\n- 1\n-\n - 2\n - 3\n- a:\n - \"0\"\n - \"z\"\n r: 6\narraySection:\n- q: 1\n- w: 2\nbool: true\nemptyArray: []\nemptyArraySection:\n- {}\nemptySection: {}\n\"escaped\\\"Section\":\n z: \"q\"\nkey: \"value\"\nnotBool: false\nnumber: 7\nsection:\n a: 1\n array:\n - c: 3\n - d: 4\n \"e$caped\":\n q: \"t\"\n nested:\n b: 2\n nestedArray:\n - k: \"v\"\n nested:\n e: 5\nsimple:\n t: 5", + "object3": "\"-0B1010_0111_0100_1010_1110\": \"string\\n with some\\n newlines\\n \"\n\"0X_0a_74_ae\": \"BARE_KEY\"\n\"1-234-567-8901\": null\n\"192.168.0.1\":\n- \"a\": 2\n \"b\": \"str\"\n- \"c\": []\n\"__-0B1010_0111_0100_1010_1110\": \"a new line\\n \"\n\"__-0X_0a_74_ae\": \"BARE_KEY\"\n\"b\":\n \"N\": \"boolean false\"\n \"NO\": \"boolean false\"\n \"NULL\": \"null word capital\"\n \"Null\": \"null word\"\n \"OFF\": \"boolean false\"\n \"On\": \"boolean true\"\n \"True\": \"boolean true\"\n \"Yes\": \"boolean true\"\n \"n\": \"boolean false\"\n \"null\": \"null word\"\n \"off\": \"boolean false\"\n \"on\": \"boolean true\"\n \"true\": \"boolean true\"\n \"y\": \"boolean true\"\n \"yes\": \"boolean true\"\n\"jsonnet.org/k8s-label-like\": \"0600\"\n\"just-letters-dashes\": \"+1101_1111\"\n\"just_letters_underscores\": 142321\n\"x\": \"BARE_KEY\"", + "object3_indent": "\"-0B1010_0111_0100_1010_1110\": \"string\\n with some\\n newlines\\n \"\n\"0X_0a_74_ae\": \"BARE_KEY\"\n\"1-234-567-8901\": null\n\"192.168.0.1\":\n - \"a\": 2\n \"b\": \"str\"\n - \"c\": []\n\"__-0B1010_0111_0100_1010_1110\": \"a new line\\n \"\n\"__-0X_0a_74_ae\": \"BARE_KEY\"\n\"b\":\n \"N\": \"boolean false\"\n \"NO\": \"boolean false\"\n \"NULL\": \"null word capital\"\n \"Null\": \"null word\"\n \"OFF\": \"boolean false\"\n \"On\": \"boolean true\"\n \"True\": \"boolean true\"\n \"Yes\": \"boolean true\"\n \"n\": \"boolean false\"\n \"null\": \"null word\"\n \"off\": \"boolean false\"\n \"on\": \"boolean true\"\n \"true\": \"boolean true\"\n \"y\": \"boolean true\"\n \"yes\": \"boolean true\"\n\"jsonnet.org/k8s-label-like\": \"0600\"\n\"just-letters-dashes\": \"+1101_1111\"\n\"just_letters_underscores\": 142321\n\"x\": \"BARE_KEY\"", + "object3_indent_unquoted": "-0B1010_0111_0100_1010_1110: \"string\\n with some\\n newlines\\n \"\n0X_0a_74_ae: \"BARE_KEY\"\n1-234-567-8901: null\n192.168.0.1:\n - a: 2\n b: \"str\"\n - c: []\n__-0B1010_0111_0100_1010_1110: \"a new line\\n \"\n__-0X_0a_74_ae: \"BARE_KEY\"\nb:\n \"N\": \"boolean false\"\n \"NO\": \"boolean false\"\n \"NULL\": \"null word capital\"\n \"Null\": \"null word\"\n \"OFF\": \"boolean false\"\n \"On\": \"boolean true\"\n \"True\": \"boolean true\"\n \"Yes\": \"boolean true\"\n \"n\": \"boolean false\"\n \"null\": \"null word\"\n \"off\": \"boolean false\"\n \"on\": \"boolean true\"\n \"true\": \"boolean true\"\n \"y\": \"boolean true\"\n \"yes\": \"boolean true\"\njsonnet.org/k8s-label-like: \"0600\"\njust-letters-dashes: \"+1101_1111\"\njust_letters_underscores: 142321\nx: \"BARE_KEY\"", + "object3_unquoted": "-0B1010_0111_0100_1010_1110: \"string\\n with some\\n newlines\\n \"\n0X_0a_74_ae: \"BARE_KEY\"\n1-234-567-8901: null\n192.168.0.1:\n- a: 2\n b: \"str\"\n- c: []\n__-0B1010_0111_0100_1010_1110: \"a new line\\n \"\n__-0X_0a_74_ae: \"BARE_KEY\"\nb:\n \"N\": \"boolean false\"\n \"NO\": \"boolean false\"\n \"NULL\": \"null word capital\"\n \"Null\": \"null word\"\n \"OFF\": \"boolean false\"\n \"On\": \"boolean true\"\n \"True\": \"boolean true\"\n \"Yes\": \"boolean true\"\n \"n\": \"boolean false\"\n \"null\": \"null word\"\n \"off\": \"boolean false\"\n \"on\": \"boolean true\"\n \"true\": \"boolean true\"\n \"y\": \"boolean true\"\n \"yes\": \"boolean true\"\njsonnet.org/k8s-label-like: \"0600\"\njust-letters-dashes: \"+1101_1111\"\njust_letters_underscores: 142321\nx: \"BARE_KEY\"", + "object_indent": "\"abc\": \"def\"\n\"bam\": true\n\"bar\": \"baz\"\n\"baz\": 1\n\"bazel\": 1.42\n\"bim\": false\n\"blamo\":\n \"cereal\":\n - \"<>& fizbuzz\"\n -\n - \"a\"\n -\n - \"b\"\n \"treats\":\n - \"name\": \"chocolate\"\n\"boom\": -1\n\"foo\": \"baz\"", + "object_indent_unquoted": "abc: \"def\"\nbam: true\nbar: \"baz\"\nbaz: 1\nbazel: 1.42\nbim: false\nblamo:\n cereal:\n - \"<>& fizbuzz\"\n -\n - \"a\"\n -\n - \"b\"\n treats:\n - name: \"chocolate\"\nboom: -1\nfoo: \"baz\"", + "object_unquoted": "abc: \"def\"\nbam: true\nbar: \"baz\"\nbaz: 1\nbazel: 1.42\nbim: false\nblamo:\n cereal:\n - \"<>& fizbuzz\"\n -\n - \"a\"\n -\n - \"b\"\n treats:\n - name: \"chocolate\"\nboom: -1\nfoo: \"baz\"" +} diff --git a/testdata/builtin_manifestYamlDoc.jsonnet b/testdata/builtin_manifestYamlDoc.jsonnet new file mode 100644 index 00000000..4460aa17 --- /dev/null +++ b/testdata/builtin_manifestYamlDoc.jsonnet @@ -0,0 +1,105 @@ +local object = { + foo: 'baz', + abc: 'def', + bar: self.foo, + baz: 1, + bazel: 1.42, + boom: -1, + bim: false, + bam: true, + blamo: { + cereal: [ + '<>& fizbuzz', + ['a', ['b']], + ], + + treats: [ + { + name: 'chocolate', + }, + ], + }, +}; + +local object2 = { + key: 'value', + simple: { t: 5 }, + section: { + a: 1, + nested: { b: 2 }, + 'e$caped': { q: 't' }, + array: [ + { c: 3 }, + { d: 4 }, + ], + nestedArray: [{ + k: 'v', + nested: { e: 5 }, + }], + }, + arraySection: [ + { q: 1 }, + { w: 2 }, + ], + 'escaped"Section': { z: 'q' }, + emptySection: {}, + emptyArraySection: [{}], + bool: true, + notBool: false, + number: 7, + array: ['s', 1, [2, 3], { r: 6, a: ['0', 'z'] }], + emptyArray: [], + '"': 4, +}; + +local object3 = { + '0X_0a_74_ae': 'BARE_KEY', + '__-0X_0a_74_ae': 'BARE_KEY', + '-0B1010_0111_0100_1010_1110': 'string + with some + newlines + ', + '__-0B1010_0111_0100_1010_1110': 'a new line + ', + x: 'BARE_KEY', + b: { + y: 'boolean true', + yes: 'boolean true', + Yes: 'boolean true', + True: 'boolean true', + 'true': 'boolean true', + on: 'boolean true', + On: 'boolean true', + NO: 'boolean false', + n: 'boolean false', + N: 'boolean false', + off: 'boolean false', + OFF: 'boolean false', + 'null': 'null word', + NULL: 'null word capital', + Null: 'null word', + }, + just_letters_underscores: 142321, + 'just-letters-dashes': '+1101_1111', + 'jsonnet.org/k8s-label-like': '0600', + '192.168.0.1': [{a: 2, b: 'str'}, {c : []}], + '1-234-567-8901': null, +}; + +{ + object: std.manifestYamlDoc(object), + object2: std.manifestYamlDoc(object2), + object3: std.manifestYamlDoc(object3), + + object_indent: std.manifestYamlDoc(object, indent_array_in_object=true), + object2_indent: std.manifestYamlDoc(object2, indent_array_in_object=true), + object3_indent: std.manifestYamlDoc(object3, indent_array_in_object=true), + + object_unquoted: std.manifestYamlDoc(object, quote_keys=false), + object2_unquoted: std.manifestYamlDoc(object2, quote_keys=false), + object3_unquoted: std.manifestYamlDoc(object3, quote_keys=false), + + object_indent_unquoted: std.manifestYamlDoc(object, indent_array_in_object=true, quote_keys=false), + object2_indent_unquoted: std.manifestYamlDoc(object2, indent_array_in_object=true, quote_keys=false), + object3_indent_unquoted: std.manifestYamlDoc(object3, indent_array_in_object=true, quote_keys=false), +} diff --git a/testdata/builtin_manifestYamlDoc.linter.golden b/testdata/builtin_manifestYamlDoc.linter.golden new file mode 100644 index 00000000..e69de29b