Format a slice of bytes as printable string when possible Based on `strconv.IsPrint()`, when the slice is fully made of printable bytes, format it as a string using `formatString()`. Also reverts `format_test.go` to its previous state including printable byte slices.
diff --git a/format/format.go b/format/format.go index 38d5d79..06355d9 100644 --- a/format/format.go +++ b/format/format.go
@@ -7,6 +7,7 @@ "fmt" "reflect" "strings" + "strconv" ) // Use MaxDepth to set the maximum recursion depth when printing deeply nested objects @@ -186,6 +187,10 @@ } func formatSlice(v reflect.Value, indentation uint) string { + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && isPrintableString(string(v.Bytes())){ + return formatString(v.Bytes(), indentation) + } + l := v.Len() result := make([]string, l) longest := 0 @@ -258,3 +263,15 @@ return false } + +/* +Returns true when the string is entirely made of printable runes, false otherwise. +*/ +func isPrintableString(str string) bool { + for _, runeValue := range str { + if !strconv.IsPrint(runeValue) { + return false + } + } + return true +}
diff --git a/format/format_test.go b/format/format_test.go index 1fdf29a..1391fe7 100644 --- a/format/format_test.go +++ b/format/format_test.go
@@ -162,11 +162,19 @@ }) Describe("formatting []byte slices", func() { - It("should present them as slices", func() { - b := []byte("a\nb\nc") - Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `\[97, 10, 98, 10, 99\]`)) - }) - }) + Context("when the slice is made of printable bytes", func () { + It("should present it as string", func() { + b := []byte("a b c") + Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a b c`)) + }) + }) + Context("when the slice contains non-printable bytes", func () { + It("should present it as slice", func() { + b := []byte("a b c\n\x01\x02\x03\xff\x1bH") + Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:12, cap:\d+`, `\[97, 32, 98, 32, 99, 10, 1, 2, 3, 255, 27, 72\]`)) + }) + }) + }) Describe("formatting functions", func() { It("should give the type and format values correctly", func() { @@ -253,11 +261,10 @@ m["Toby Ziegler"] = []byte("Richard Schiff") m["CJ Cregg"] = []byte("Allison Janney") expected := `{ -( "Josiah Edward Bartlet": \[77, 97, 114, 116, 105, 110, 32, 83, 104, 101, 101, 110\], -| "Toby Ziegler": \[82, 105, 99, 104, 97, 114, 100, 32, 83, 99, 104, 105, 102, 102\], -| "CJ Cregg": \[65, 108, 108, 105, 115, 111, 110, 32, 74, 97, 110, 110, 101, 121\], -){3} }` - + ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"), + ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"), + ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"), + }` Ω(Object(m, 1)).Should(matchRegexp(`map\[string\]\[\]uint8 \| len:3`, expected)) }) }) @@ -269,11 +276,11 @@ Name: "Oswald", Enumeration: 17, Veritas: true, - Data: []byte(""), + Data: []byte("datum"), secret: 1983, } - Ω(Object(s, 1)).Should(match("format_test.SimpleStruct", `{Name: "Oswald", Enumeration: 17, Veritas: true, Data: [], secret: 1983}`)) + Ω(Object(s, 1)).Should(match("format_test.SimpleStruct", `{Name: "Oswald", Enumeration: 17, Veritas: true, Data: "datum", secret: 1983}`)) }) Context("when the struct contains long entries", func() { @@ -290,7 +297,7 @@ Name: "Mithrandir Gandalf Greyhame", Enumeration: 2021, Veritas: true, - Data: [119, 105, 122, 97, 114, 100], + Data: "wizard", secret: 3, }`)) }) @@ -314,7 +321,7 @@ Describe("formatting aliased types", func() { It("should print out the correct alias type", func() { Ω(Object(StringAlias("alias"), 1)).Should(match("format_test.StringAlias", `alias`)) - Ω(Object(ByteAlias("alias"), 1)).Should(matchRegexp(`format_test\.ByteAlias \| len:5, cap:\d+`, `\[97, 108, 105, 97, 115\]`)) + Ω(Object(ByteAlias("alias"), 1)).Should(matchRegexp(`format_test\.ByteAlias \| len:5, cap:\d+`, `alias`)) Ω(Object(IntAlias(3), 1)).Should(match("format_test.IntAlias", "3")) }) }) @@ -324,7 +331,7 @@ s := ComplexStruct{ Strings: []string{"lots", "of", "short", "strings"}, SimpleThings: []*SimpleStruct{ - {"short", 7, true, []byte("sm"), 17}, + {"short", 7, true, []byte("succinct"), 17}, {"something longer", 427, true, []byte("designed to wrap around nicely"), 30}, }, DataMaps: map[int]ByteAlias{ @@ -335,19 +342,19 @@ expected := `{ Strings: \["lots", "of", "short", "strings"\], SimpleThings: \[ - {Name: "short", Enumeration: 7, Veritas: true, Data: \[115, 109\], secret: 17}, + {Name: "short", Enumeration: 7, Veritas: true, Data: "succinct", secret: 17}, { Name: "something longer", Enumeration: 427, Veritas: true, - Data: \[100, 101, 115, 105, 103, 110, 101, 100, 32, 116, 111, 32, 119, 114, 97, 112, 32, 97, 114, 111, 117, 110, 100, 32, 110, 105, 99, 101, 108, 121\], + Data: "designed to wrap around nicely", secret: 30, }, \], DataMaps: { -( 17: \[115, 111, 109, 101, 32, 115, 117, 98, 115, 116, 97, 110, 116, 105, 97, 108, 108, 121, 32, 108, 111, 110, 103, 101, 114, 32, 99, 104, 117, 110, 107, 115, 32, 111, 102, 32, 100, 97, 116, 97\], -| 1138: \[116, 104, 97, 116, 32, 115, 104, 111, 117, 108, 100, 32, 109, 97, 107, 101, 32, 116, 104, 105, 110, 103, 115, 32, 119, 114, 97, 112\], -){2} }, + (17: "some substantially longer chunks of data"|1138: "that should make things wrap"), + (17: "some substantially longer chunks of data"|1138: "that should make things wrap"), + }, }` Ω(Object(s, 1)).Should(matchRegexp(`format_test\.ComplexStruct`, expected)) }) @@ -388,7 +395,7 @@ funcValue: %p, pointerValue: 5, sliceValue: \["string", "slice"\], - byteSliceValue: \[98, 121, 116, 101, 115\], + byteSliceValue: "bytes", stringValue: "a string", arrValue: \[11, 12, 13\], byteArrValue: \[17, 20, 32\],