cue: clean up Format
Makes format more intuitive and gives bonus features
to mitigate any pain this causes for backwards
incompatibility.
Fixes #882
Change-Id: I6390f95c4e7b27b9e0a195bd936f6aae55aadf27
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9487
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/mod.go b/cmd/cue/cmd/mod.go
index ff95eb1..833ad0f 100644
--- a/cmd/cue/cmd/mod.go
+++ b/cmd/cue/cmd/mod.go
@@ -90,7 +90,7 @@
return fmt.Errorf("invalid module name: %v", module)
}
if h := u.Hostname(); !strings.Contains(h, ".") {
- return fmt.Errorf("invalid host name %q", h)
+ return fmt.Errorf("invalid host name %s", h)
}
}
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 20d92c8..e8b72d5 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -47,13 +47,13 @@
`3`,
}, {
test("math", "math.Pi(3)"),
- `_|_(cannot call non-function math.Pi (type float))`,
+ `_|_ // cannot call non-function math.Pi (type float)`,
}, {
test("math", "math.Floor(3, 5)"),
- `_|_(too many arguments in call to math.Floor (have 2, want 1))`,
+ `_|_ // too many arguments in call to math.Floor (have 2, want 1)`,
}, {
test("math", `math.Floor("foo")`),
- `_|_(cannot use "foo" (type string) as number in argument 1 to math.Floor)`,
+ `_|_ // cannot use "foo" (type string) as number in argument 1 to math.Floor`,
}, {
test("crypto/sha256", `sha256.Sum256("hash me")`),
`'\xeb \x1a\xf5\xaa\xf0\xd6\x06)\xd3Ҧ\x1eFl\xfc\x0f\xed\xb5\x17\xad\xd81\xec\xacR5\xe1کc\xd6'`,
@@ -62,13 +62,13 @@
`16`,
}, {
test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<3})`),
- `_|_(error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3))`,
+ `_|_ // error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3)`,
}, {
test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<5})`),
`true`,
}, {
test("encoding/yaml", `yaml.Validate("a: 2\n", {a:<5, b:int})`),
- `_|_(error in call to encoding/yaml.Validate: b: incomplete value int)`,
+ `_|_ // error in call to encoding/yaml.Validate: b: incomplete value int`,
}, {
test("strconv", `strconv.FormatUint(64, 16)`),
`"40"`,
@@ -77,7 +77,7 @@
`"foo"`,
}, {
test("regexp", `regexp.Find(#"f\w\w"#, "bar")`),
- `_|_(error in call to regexp.Find: no match)`,
+ `_|_ // error in call to regexp.Find: no match`,
}, {
testExpr(`len([1, 2, 3])`),
`3`,
@@ -86,33 +86,38 @@
`3`,
}, {
test("encoding/json", `json.MarshalStream([{a: 1}, {b: 2}])`),
- `"{\"a\":1}\n{\"b\":2}\n"`,
+ `"""` + "\n\t{\"a\":1}\n\t{\"b\":2}\n\t\n\t" + `"""`,
}, {
test("encoding/json", `{
x: int
y: json.Marshal({a: x})
}`),
- `{x:int,y:_|_(cannot convert incomplete value "int" to JSON)}`,
+ `{
+ x: int
+ y: _|_ // cannot convert incomplete value "int" to JSON
+}`,
}, {
test("encoding/yaml", `yaml.MarshalStream([{a: 1}, {b: 2}])`),
- `"a: 1\n---\nb: 2\n"`,
+ `"""` + "\n\ta: 1\n\t---\n\tb: 2\n\t\n\t" + `"""`,
}, {
test("struct", `struct.MinFields(0) & ""`),
- `_|_(conflicting values struct.MinFields(0) and "" (mismatched types struct and string))`,
+ `_|_ // conflicting values struct.MinFields(0) and "" (mismatched types struct and string)`,
}, {
test("struct", `struct.MinFields(0) & {a: 1}`),
- `{a:1}`,
+ `{
+ a: 1
+}`,
}, {
test("struct", `struct.MinFields(2) & {a: 1}`),
// TODO: original value may be better.
- // `_|_(invalid value {a:1} (does not satisfy struct.MinFields(2)))`,
- `_|_(invalid value {a:1} (does not satisfy struct.MinFields(2)): len(fields) < MinFields(2) (1 < 2))`,
+ // `_|_ // invalid value {a:1} (does not satisfy struct.MinFields(2))`,
+ `_|_ // invalid value {a:1} (does not satisfy struct.MinFields(2)): len(fields) < MinFields(2) (1 < 2)`,
}, {
test("time", `time.Time & "1937-01-01T12:00:27.87+00:20"`),
`"1937-01-01T12:00:27.87+00:20"`,
}, {
test("time", `time.Time & "no time"`),
- `_|_(invalid value "no time" (does not satisfy time.Time): error in call to time.Time: invalid time "no time")`,
+ `_|_ // invalid value "no time" (does not satisfy time.Time): error in call to time.Time: invalid time "no time"`,
}, {
test("time", `time.Unix(1500000000, 123456)`),
`"2017-07-14T02:40:00.000123456Z"`,
@@ -146,7 +151,7 @@
emit string
}{{
test("list", `list.Sort([{a:1}, {b:2}], list.Ascending)`),
- `_|_(error in call to list.Sort: less: invalid operands {b:2} and {a:1} to '<' (type struct and struct))`,
+ `_|_ // error in call to list.Sort: less: invalid operands {b:2} and {a:1} to '<' (type struct and struct)`,
}}
for i, tc := range testCases {
t.Run(fmt.Sprint(i), func(t *testing.T) {
diff --git a/cue/examplecompile_test.go b/cue/examplecompile_test.go
index 6803da3..0172235 100644
--- a/cue/examplecompile_test.go
+++ b/cue/examplecompile_test.go
@@ -31,13 +31,13 @@
`)
p("lookups")
- p("a: %+v", v.LookupPath(cue.ParsePath("a")))
- p("b: %+v", v.LookupPath(cue.ParsePath("b")))
- p(`"a+b": %+v`, v.LookupPath(cue.ParsePath(`"a+b"`)))
+ p("a: %v", v.LookupPath(cue.ParsePath("a")))
+ p("b: %v", v.LookupPath(cue.ParsePath("b")))
+ p(`"a+b": %v`, v.LookupPath(cue.ParsePath(`"a+b"`)))
p("")
p("expressions")
- p("a + b: %+v", ctx.CompileString("a + b", cue.Scope(v)))
- p("a * b: %+v", ctx.CompileString("a * b", cue.Scope(v)))
+ p("a + b: %v", ctx.CompileString("a + b", cue.Scope(v)))
+ p("a * b: %v", ctx.CompileString("a * b", cue.Scope(v)))
// Output:
// lookups
diff --git a/cue/format.go b/cue/format.go
new file mode 100644
index 0000000..707a799
--- /dev/null
+++ b/cue/format.go
@@ -0,0 +1,201 @@
+// Copyright 2021 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+
+ "cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/format"
+ "cuelang.org/go/internal/core/export"
+)
+
+// TODO:
+// * allow '-' to strip outer curly braces?
+// - simplify output; can be used in combination with other flags
+// * advertise:
+// c like v, but print comments
+// a like c, but print attributes and package-local hidden fields as well
+
+// Format prints a CUE value.
+//
+// WARNING:
+// although we are narrowing down the semantics, the verbs and options
+// are still subject to change. this API is experimental although it is
+// likely getting close to the final design.
+//
+// It recognizes the following verbs:
+//
+// v print CUE value
+//
+// The verbs support the following flags:
+// # print as schema and include definitions.
+// The result is printed as a self-contained file, instead of an the
+// expression format.
+// + evaluate: resolve defaults and error on incomplete errors
+//
+// Indentation can be controlled as follows:
+// width indent the cue block by <width> tab stops (e.g. %2v)
+// precision convert tabs to <precision> spaces (e.g. %.2v), where
+// a value of 0 means no indentation or newlines (TODO).
+//
+// If the value kind corresponds to one of the following Go types, the
+// usual Go formatting verbs for that type can be used:
+//
+// Int: b,d,o,O,q,x,X
+// Float: f,e,E,g,G
+// String/Bytes: s,q,x,X
+//
+// The %v directive will be used if the type is not supported for that verb.
+//
+func (v Value) Format(state fmt.State, verb rune) {
+ if v.v == nil {
+ fmt.Fprint(state, "<nil>")
+ return
+ }
+
+ switch verb {
+ case 'a':
+ formatCUE(state, v, true, true)
+ case 'c':
+ formatCUE(state, v, true, false)
+ case 'v':
+ formatCUE(state, v, false, false)
+
+ case 'd', 'o', 'O', 'U':
+ var i big.Int
+ if _, err := v.Int(&i); err != nil {
+ formatCUE(state, v, false, false)
+ return
+ }
+ i.Format(state, verb)
+
+ case 'f', 'e', 'E', 'g', 'G':
+ d, err := v.Decimal()
+ if err != nil {
+ formatCUE(state, v, false, false)
+ return
+ }
+ d.Format(state, verb)
+
+ case 's', 'q':
+ // TODO: this drops other formatting directives
+ msg := "%s"
+ if verb == 'q' {
+ msg = "%q"
+ }
+
+ if b, err := v.Bytes(); err == nil {
+ fmt.Fprintf(state, msg, b)
+ } else {
+ s := fmt.Sprintf("%+v", v)
+ fmt.Fprintf(state, msg, s)
+ }
+
+ case 'x', 'X':
+ switch v.Kind() {
+ case StringKind, BytesKind:
+ b, _ := v.Bytes()
+ // TODO: this drops other formatting directives
+ msg := "%x"
+ if verb == 'X' {
+ msg = "%X"
+ }
+ fmt.Fprintf(state, msg, b)
+
+ case IntKind, NumberKind:
+ var i big.Int
+ _, _ = v.Int(&i)
+ i.Format(state, verb)
+
+ case FloatKind:
+ dec, _ := v.Decimal()
+ dec.Format(state, verb)
+
+ default:
+ formatCUE(state, v, false, false)
+ }
+
+ default:
+ formatCUE(state, v, false, false)
+ }
+}
+
+func formatCUE(state fmt.State, v Value, showDocs, showAll bool) {
+
+ pkgPath := v.instance().ID()
+
+ p := *export.Simplified
+
+ isDef := false
+ switch {
+ case state.Flag('#'):
+ isDef = true
+ p = export.Profile{
+ ShowOptional: true,
+ ShowDefinitions: true,
+ ShowHidden: true,
+ }
+
+ case state.Flag('+'):
+ p = *export.Final
+ fallthrough
+
+ default:
+ p.ShowHidden = showAll
+ }
+
+ p.ShowDocs = showDocs
+ p.ShowAttributes = showAll
+
+ var n ast.Node
+ if isDef {
+ n, _ = p.Def(v.idx, pkgPath, v.v)
+ } else {
+ n, _ = p.Value(v.idx, pkgPath, v.v)
+ }
+
+ formatExpr(state, n)
+}
+
+func formatExpr(state fmt.State, n ast.Node) {
+ opts := make([]format.Option, 0, 3)
+ if state.Flag('-') {
+ opts = append(opts, format.Simplify())
+ }
+ // TODO: handle verbs to allow formatting based on type:
+ if width, ok := state.Width(); ok {
+ opts = append(opts, format.IndentPrefix(width))
+ }
+ // TODO: consider this: should tabs or spaces be the default?
+ if tabwidth, ok := state.Precision(); ok {
+ // TODO: 0 means no newlines.
+ opts = append(opts,
+ format.UseSpaces(tabwidth),
+ format.TabIndent(false))
+ }
+ // TODO: consider this.
+ // else if state.Flag(' ') {
+ // opts = append(opts,
+ // format.UseSpaces(4),
+ // format.TabIndent(false))
+ // }
+
+ b, _ := format.Node(n, opts...)
+ b = bytes.Trim(b, "\n\r")
+ _, _ = state.Write(b)
+}
diff --git a/cue/format_test.go b/cue/format_test.go
new file mode 100644
index 0000000..8b02725
--- /dev/null
+++ b/cue/format_test.go
@@ -0,0 +1,307 @@
+// Copyright 2021 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue_test
+
+import (
+ "fmt"
+ "path"
+ "testing"
+
+ "cuelang.org/go/cue"
+ "cuelang.org/go/cue/cuecontext"
+)
+
+func ExampleValue_Format() {
+ ctx := cuecontext.New()
+
+ v := ctx.CompileString(`
+ a: 2 + b
+ b: *3 | int
+ s: "foo\nbar"
+ `)
+
+ fmt.Println("### ALL")
+ fmt.Println(v)
+ fmt.Println("---")
+ fmt.Printf("%#v\n", v)
+ fmt.Println("---")
+ fmt.Printf("%+v\n", v)
+
+ a := v.LookupPath(cue.ParsePath("a"))
+ fmt.Println("\n### INT")
+ fmt.Printf("%%v: %v\n", a)
+ fmt.Printf("%%05d: %05d\n", a)
+
+ s := v.LookupPath(cue.ParsePath("s"))
+ fmt.Println("\n### STRING")
+ fmt.Printf("%%v: %v\n", s)
+ fmt.Printf("%%s: %s\n", s)
+ fmt.Printf("%%q: %q\n", s)
+
+ v = ctx.CompileString(`
+ #Def: a: [string]: int
+ b: #Def
+ b: a: {
+ a: 3
+ b: 3
+ }
+ `)
+ b := v.LookupPath(cue.ParsePath("b.a"))
+ fmt.Println("\n### DEF")
+ fmt.Println(b)
+ fmt.Println("---")
+ // This will indicate that the result is closed by including a hidden
+ // definition.
+ fmt.Printf("%#v\n", b)
+
+ // Output:
+ // ### ALL
+ // {
+ // a: 5
+ // b: *3 | int
+ // s: """
+ // foo
+ // bar
+ // """
+ // }
+ // ---
+ // a: 2 + b
+ // b: *3 | int
+ // s: "foo\nbar"
+ // ---
+ // {
+ // a: 5
+ // b: 3
+ // s: """
+ // foo
+ // bar
+ // """
+ // }
+ //
+ // ### INT
+ // %v: 5
+ // %05d: 00005
+ //
+ // ### STRING
+ // %v: """
+ // foo
+ // bar
+ // """
+ // %s: foo
+ // bar
+ // %q: "foo\nbar"
+ //
+ // ### DEF
+ // {
+ // a: 3
+ // b: 3
+ // }
+ // ---
+ // _#def
+ // _#def: {
+ // {
+ // [string]: int
+ // }
+ // a: 3
+ // b: 3
+ // }
+}
+
+func TestFormat(t *testing.T) {
+ tests := func(s ...string) (a [][2]string) {
+ for i := 0; i < len(s); i += 2 {
+ a = append(a, [2]string{s[i], s[i+1]})
+ }
+ return a
+ }
+ testCases := []struct {
+ desc string
+ in string
+ out [][2]string
+ }{{
+ desc: "int",
+ in: `12 + 14`,
+ out: tests(
+ "%#v", "26",
+ "%d", "26",
+ "%o", "32",
+ "%O", "0o32",
+ "%x", "1a",
+ "%X", "1A",
+ "%q", `"26"`,
+ "%0.3d", "026",
+ ),
+ }, {
+ desc: "float",
+ in: `12.2 + 14.4`,
+ out: tests(
+ "%#v", "26.6",
+ "%5f", " 26.6",
+ "%e", "2.66e+1",
+ "%08E", "02.66E+1",
+ "%g", "26.6",
+ "%3G", "26.6",
+ ),
+ }, {
+ desc: "strings",
+ in: `"string"`,
+ out: tests(
+ "%v", `"string"`,
+ "%s", "string",
+ "%x", "737472696e67",
+ "%X", "737472696E67",
+ ),
+ }, {
+ desc: "multiline string",
+ in: `"""
+ foo
+ bar
+ """`,
+ out: tests(
+ "%#v", `"""
+ foo
+ bar
+ """`,
+ "%s", "foo\nbar",
+ "%q", `"foo\nbar"`,
+ ),
+ }, {
+ desc: "multiline bytes",
+ in: `'''
+ foo
+ bar
+ '''`,
+ out: tests(
+ "%#v", `'''
+ foo
+ bar
+ '''`,
+ "%s", "foo\nbar",
+ "%q", `"foo\nbar"`,
+ ),
+ }, {
+ desc: "interpolation",
+ in: `
+ #D: {
+ a: string
+ b: "hello \(a)"
+ }
+ d: #D
+ d: a: "world"
+ x: *1 | int
+ `,
+ out: tests(
+ "%v", `{
+ d: {
+ a: "world"
+ b: "hello world"
+ }
+ x: *1 | int
+}`,
+ "%#v", `#D: {
+ a: string
+ b: "hello \(a)"
+}
+d: #D & {
+ a: "world"
+}
+x: *1 | int`,
+ "%+v", `{
+ d: {
+ a: "world"
+ b: "hello world"
+ }
+ x: 1
+}`,
+ ),
+ }, {
+ desc: "indent",
+ in: `
+a: {
+ b: """
+ foo
+ bar
+ """
+ c: int
+}`,
+ out: tests(
+ "%v", `{
+ a: {
+ b: """
+ foo
+ bar
+ """
+ c: int
+ }
+}`,
+ "%3v", `{
+ a: {
+ b: """
+ foo
+ bar
+ """
+ c: int
+ }
+ }`,
+ "%.1v", `{
+ a: {
+ b: """
+ foo
+ bar
+ """
+ c: int
+ }
+}`,
+ "%3.1v", `{
+ a: {
+ b: """
+ foo
+ bar
+ """
+ c: int
+ }
+ }`,
+ ),
+ }, {
+ desc: "imports",
+ in: `
+ import "strings"
+ a: strings.Contains("foo")
+ `,
+ out: tests(
+ "%v", `{
+ a: strings.Contains("foo")
+}`,
+ "%+v", `{
+ a: strings.Contains("foo")
+}`,
+ "%#v", `import "strings"
+
+a: strings.Contains("foo")`,
+ ),
+ }}
+ ctx := cuecontext.New()
+ for _, tc := range testCases {
+ for _, test := range tc.out {
+ t.Run(path.Join(tc.desc, test[0]), func(t *testing.T) {
+ v := ctx.CompileString(tc.in)
+ got := fmt.Sprintf(test[0], v)
+ if got != test[1] {
+ t.Errorf(" got: %s\nwant: %s", got, test[1])
+ }
+ })
+ }
+ }
+}
diff --git a/cue/marshal_test.go b/cue/marshal_test.go
index 64775aa..502dcc0 100644
--- a/cue/marshal_test.go
+++ b/cue/marshal_test.go
@@ -148,9 +148,9 @@
`package test
import pkg2 "example.com/foo/pkg1"
- pkg1: pkg2.Object
+ "Hello \(pkg1)!"
- "Hello \(pkg1)!"`),
+ pkg1: pkg2.Object`),
}),
`"Hello World!"`,
}, {
diff --git a/cue/types.go b/cue/types.go
index 2629a8a..6421c0a 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -29,7 +29,6 @@
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/format"
"cuelang.org/go/cue/literal"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
@@ -1828,25 +1827,6 @@
return adt.Equal(v.ctx(), v.v, other.v, 0)
}
-// Format prints a debug version of a value.
-func (v Value) Format(state fmt.State, verb rune) {
- ctx := v.ctx()
- if v.v == nil {
- fmt.Fprint(state, "<nil>")
- return
- }
- switch {
- case state.Flag('#'):
- _, _ = io.WriteString(state, str(ctx, v.v))
- case state.Flag('+'):
- _, _ = io.WriteString(state, ctx.Str(v.v))
- default:
- n, _ := export.Raw.Expr(v.idx, v.instance().ID(), v.v)
- b, _ := format.Node(n)
- _, _ = state.Write(b)
- }
-}
-
func (v Value) instance() *Instance {
if v.v == nil {
return nil
diff --git a/cue/types_test.go b/cue/types_test.go
index 18d52ae..fff8dfe 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -63,7 +63,7 @@
res := runSpec.Unify(v)
return res
},
- want: "_|_(#runSpec: field ction not allowed)",
+ want: "_|_ // #runSpec: field ction not allowed",
}, {
// Issue #567
input: `
@@ -77,7 +77,7 @@
res := runSpec.Unify(v)
return res
},
- want: "_|_(#runSpec.action: field Foo not allowed)",
+ want: "_|_ // #runSpec.action: field Foo not allowed",
}, {
input: `
#runSpec: v: {action: foo: int}
@@ -91,7 +91,7 @@
res := w.Unify(v)
return res
},
- want: "_|_(w: field ction not allowed)",
+ want: "_|_ // w: field ction not allowed",
}}
for _, tc := range testCases {
if tc.skip {
@@ -819,7 +819,7 @@
for _, tc := range testCases {
v := inst.Lookup(tc.ref...)
- if got := fmt.Sprint(v); got != tc.raw {
+ if got := fmt.Sprintf("%#v", v); got != tc.raw {
t.Errorf("got %v; want %v", got, tc.raw)
}
@@ -841,7 +841,7 @@
v = fi.Value
}
- if got := fmt.Sprint(v); got != tc.raw {
+ if got := fmt.Sprintf("%#v", v); got != tc.raw {
t.Errorf("got %v; want %v", got, tc.raw)
}
@@ -956,22 +956,20 @@
t.Fatal(err)
}
- got := fmt.Sprint(root.Value())
- want := `{
- #Provider: {
- ID: string
+ got := fmt.Sprintf("%#v", root.Value())
+ want := `#Provider: {
+ ID: string
+ notConcrete: bool
+ a: int
+ b: int
+}
+providers: {
+ myprovider: {
+ ID: "12345"
notConcrete: bool
a: int
b: int
}
- providers: {
- myprovider: {
- ID: "12345"
- notConcrete: bool
- a: int
- b: int
- }
- }
}`
if got != want {
t.Errorf("got: %s\nwant: %s", got, want)
diff --git a/encoding/openapi/types.go b/encoding/openapi/types.go
index 50a962e..64b2753 100644
--- a/encoding/openapi/types.go
+++ b/encoding/openapi/types.go
@@ -63,7 +63,7 @@
if op == cue.CallOp {
v = a[0]
if len(a) == 2 {
- arg = fmt.Sprintf(" (%s)", a[1].Eval())
+ arg = fmt.Sprintf(" (%v)", a[1].Eval())
}
}
if inst, ref := v.Reference(); len(ref) > 0 {
diff --git a/internal/diff/diff_test.go b/internal/diff/diff_test.go
index 1bfec73..1eb31a3 100644
--- a/internal/diff/diff_test.go
+++ b/internal/diff/diff_test.go
@@ -114,7 +114,7 @@
`,
kind: Modified,
diff: ` {
- ls: [2,3,4]
+ ls: [2, 3, 4]
- "foo-bar": 2
+ "foo-bar": 3
s: 4
@@ -128,7 +128,7 @@
lm2: [
- 6,
]
-+ la: [2,3,4]
++ la: [2, 3, 4]
}
`,
}, {
@@ -302,7 +302,9 @@
`,
kind: Modified,
diff: ` {
-- a: {x:"hello"}
+- a: {
+- x: "hello"
+- }
}
`,
}, {
diff --git a/pkg/net/host.go b/pkg/net/host.go
index 6018da4..5f2f2e3 100644
--- a/pkg/net/host.go
+++ b/pkg/net/host.go
@@ -57,7 +57,7 @@
case cue.ListKind:
ipdata := netGetIP(host)
if len(ipdata) != 4 && len(ipdata) != 16 {
- err = fmt.Errorf("invalid host %q", host)
+ err = fmt.Errorf("invalid host %s", host)
}
hostStr = ipdata.String()
case cue.BytesKind: