documentation

This commit is contained in:
Arpad Ryszka 2020-11-23 15:42:05 +01:00
parent f999a77ff9
commit 1becff5c5d
5 changed files with 807 additions and 46 deletions

445
README.md Normal file
View File

@ -0,0 +1,445 @@
# Notation - Print Go objects
This package can be used to print (or sprint) Go objects with optional wrapping (indentation) and optional type
information for debugging purposes.
### Alternatives
Notation is similar to the following great, more established and mature packages:
- [go-spew](https://github.com/davecgh/go-spew/)
- [litter](https://github.com/sanity-io/litter)
- [utter](https://github.com/kortschak/utter)
Notation differs from these primarily in the 'flavour' of printing and the package interface.
### Installation
`go get github.com/aryszka/notation`
### Usage
Pass in the Go object(s) to be printed to one of the notation functions. These functions can be categorized into
four groups:
- **Println:** the Println type functions print Go objects to stderr with an additional newline at the end.
- **Print:** similar to Println but without the extra newline.
- **Fprint:** the Fprint type functions print Go objects to an arbitrary writer passed in as an argument.
- **Sprint:** the Sprint type functions return the string representation of Go objects instead of printing them.
The format and the verbosity can be controlled with the suffixed variants of the above functions. By default,
the input arguments are printed without types on a single line. The following suffixes are available:
- **`w`**: wrap the output based on Go-style indentation
- **`t`**: print moderately verbose type information, omitting where it can be trivially inferred
- **`v`**: print verbose type information
When **`t`** or **`v`** are used together with **`w`**, they must follow **`w`**. The suffixes **`t`** and
**`v`** cannot be used together.
Wrapping is **not eager**. It doesn't wrap a line when it can fit on 72 columns. It also tolerates longer lines up
to 112 columns, when the output is considered to be more readable that way. This means very simple Go objects
are not wrapped even with the **`w`** variant of the functions.
For the available functions, see also the [godoc](https://godoc.org/github.com/aryszka/notation).
### Example
Assuming to have the required types defined, if we do the following:
```go
b := bike{
frame: frame{
fork: fork{},
saddlePost: saddlePost{},
},
driveTrain: driveTrain{
bottomBracket: bracket{},
crank: crank{wheels: 2},
brakes: []brake{{discSize: 160}, {discSize: 140}},
derailleurs: []derailleur{{gears: 2}, {gears: 11}},
cassette: cassette{wheels: 11},
chain: chain{},
levers: []lever{{true}, {true}},
},
wheels: []wheel{{size: 70}, {size: 70}},
handlebar: handlebar{},
saddle: saddle{},
}
b.frame.fork.wheel = &b.wheels[0]
b.frame.fork.handlebar = &b.handlebar
b.frame.fork.handlebar.levers = []*lever{&b.driveTrain.levers[0], &b.driveTrain.levers[1]}
b.frame.fork.frontBrake = &b.driveTrain.brakes[0]
b.frame.saddlePost.saddle = &b.saddle
b.frame.bottomBracket = &b.driveTrain.bottomBracket
b.frame.frontDerailleur = &b.driveTrain.derailleurs[0]
b.frame.rearDerailleur = &b.driveTrain.derailleurs[1]
b.frame.rearBrake = &b.driveTrain.brakes[1]
b.frame.rearWheel = &b.wheels[1]
b.frame.bottomBracket.crank = &b.driveTrain.crank
b.frame.bottomBracket.crank.chain = &b.driveTrain.chain
b.frame.rearWheel.cassette = &b.driveTrain.cassette
b.frame.rearWheel.cassette.chain = &b.driveTrain.chain
s := notation.Sprintw(b)
```
We get the following string:
```go
{
frame: {
fork: {
wheel: {size: 70, cassette: nil},
handlebar: {levers: []{{withShift: true}, {withShift: true}}},
frontBrake: {discSize: 160},
},
saddlePost: {saddle: {}},
bottomBracket: {crank: {wheels: 2, chain: {}}},
frontDerailleur: {gears: 2},
rearDerailleur: {gears: 11},
rearBrake: {discSize: 140},
rearWheel: {size: 70, cassette: {wheels: 11, chain: {}}},
},
driveTrain: {
bottomBracket: {crank: {wheels: 2, chain: {}}},
crank: {wheels: 2, chain: {}},
brakes: []{{discSize: 160}, {discSize: 140}},
derailleurs: []{{gears: 2}, {gears: 11}},
cassette: {wheels: 11, chain: {}},
chain: {},
levers: []{{withShift: true}, {withShift: true}},
},
wheels: []{{size: 70, cassette: nil}, {size: 70, cassette: {wheels: 11, chain: {}}}},
handlebar: {levers: []{{withShift: true}, {withShift: true}}},
saddle: {},
}
```
Using `notation.Sprintwv` instead of `notation.Sprintw`, we would get the following string:
```go
bike{
frame: frame{
fork: fork{
wheel: *wheel{size: float64(70), cassette: (*cassette)(nil)},
handlebar: *handlebar{
levers: []*lever{
*lever{withShift: bool(true)},
*lever{withShift: bool(true)},
},
},
frontBrake: *brake{discSize: float64(160)},
},
saddlePost: saddlePost{saddle: *saddle{}},
bottomBracket: *bracket{crank: *crank{wheels: int(2), chain: *chain{}}},
frontDerailleur: *derailleur{gears: int(2)},
rearDerailleur: *derailleur{gears: int(11)},
rearBrake: *brake{discSize: float64(140)},
rearWheel: *wheel{
size: float64(70),
cassette: *cassette{wheels: int(11), chain: *chain{}},
},
},
driveTrain: driveTrain{
bottomBracket: bracket{crank: *crank{wheels: int(2), chain: *chain{}}},
crank: crank{wheels: int(2), chain: *chain{}},
brakes: []brake{brake{discSize: float64(160)}, brake{discSize: float64(140)}},
derailleurs: []derailleur{
derailleur{gears: int(2)},
derailleur{gears: int(11)},
},
cassette: cassette{wheels: int(11), chain: *chain{}},
chain: chain{},
levers: []lever{lever{withShift: bool(true)}, lever{withShift: bool(true)}},
},
wheels: []wheel{
wheel{size: float64(70), cassette: (*cassette)(nil)},
wheel{size: float64(70), cassette: *cassette{wheels: int(11), chain: *chain{}}},
},
handlebar: handlebar{levers: []*lever{*lever{withShift: bool(true)}, *lever{withShift: bool(true)}}},
saddle: saddle{},
}
```
### Subtleties
Notation doesn't provide configuration options, we can just pick the preferred function and call it with the
objects to be printed. The following details describe some of the behavior to be expected in case of various
input objects.
##### Numbers
Numbers are printed based on the `fmt` package's default formatting. When printing with moderate type
information, the type for the `int`, default witdth signed integers, will be omitted.
```go
i := 42
notation.Printlnt(i)
```
Output:
```go
42
```
##### Strings
When printing strings, by default they are escaped using the `strconv.Quote` function. However, when wrapping
long strings, and the string contains a newline and doesn't contain a backquote '`', then the string is printed
as a raw string literal, delimited by '`'.
Short string:
```go
s := `foobar
baz`
notation.Printlnw(s)
```
Output:
```go
"foobar\nbaz"
```
Long string:
```go
s := `The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog.`
notation.Println(s)
```
Output:
```go
`The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog.`
```
##### Arrays/Slices
Slices are are printed by printing their elements between braces, prefixed either by '[]' or the type of the
slice. Example:
```go
l := []int{1, 2, 3}
notation.Println(l)
```
Output:
```go
[]{1, 2, 3}
```
To differentiate arrays from slices, arrays are always prefixed with their type or square brackets containing
the length of the array:
```go
a := [...]{1, 2, 3}
notation.Println(a)
```
Output:
```go
[3]{1, 2, 3}
```
##### Bytes
When the type of a slice is `uint8`, or an alias of it, e.g. `byte`, then it is printed as []byte, with the hexa
representation of its bytes:
```go
b := []byte(
`The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog.`,
)
notation.Printlnwt(b)
```
Output:
```go
[]byte{
54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a
75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f
67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f
78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79
20 64 6f 67 2e 20 54 68 65 0a 71 75 69 63 6b 20 62 72 6f 77 6e
20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c
61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72
6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68
65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b
20 62 72 6f 77 6e 0a 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72
20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75
69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f
76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65
20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70
73 0a 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e 20
54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a
75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f
67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f
78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79
0a 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e
20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c
61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72
6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68
65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65 0a 71 75 69 63 6b
20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72
20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e
}
```
##### Maps
Maps are printed with their entries sorted by the string representation of their keys:
```go
m := map[string]int{"b": 1, "c": 2, "a": 3}
notation.Println(m)
```
Output:
```go
map{"a": 3, "b": 1, "c": 2}
```
This way a map is printed always the same way. If, for a reason, this is undesired, then this behavior can be
disabled via the `MAPSORT=0` environment variable.
##### Hidden values: channels, functions
Certain values, like channels and functions are printed without expanding their internals, e.g. channee state or
function body. When printing with types, the signature of these objects is printed:
```go
f := func(int) int { return 42 }
notation.Println(f)
```
Output:
```go
func()
```
With types:
```go
f := func(int) int { return 42 }
notation.Printlnt(f)
```
Output:
```go
func(int) int
```
##### Wrapping
Whe 'w' variant of the printing functions wraps the output with Go style indentation where the lines would be
too long otherwise. The wrapping is not eager, it only aims for fitting the lines on 72 columns. To measure the
indentation, it assumes 8 character width tabs. In certain cases, it tolerates longer lines up to 112 columns,
when the output would probably more readable that way. Of course, readability is subjective.
As a hidden feature, when it's really necessary, it is possible to change the above control values via
environmetn variables. TABWIDTH controls the measuring of the indentation. LINEWIDTH sets the aimed column width
of the printed lines. LINEWIDTH1 sets the tolerated threshold for those lines that are allowed to exceed the
default line width. E.g. if somebody uses two-character wide tabs in their console, they can use the package
like this:
```
TABWIDTH=2 go test -v -count 1
```
As a consequence, it is also possible to wrap all lines:
```
TABWIDTH=0 LINEWIDTH=0 LINEWIDTH1=0 go test -v -count 1
```
##### Types
Using the 't' or 'v' suffixed variants of the printing functions, notation prints the types together with the
values. When the name of a type is available, the name is printed instead of the literal representation of the
type. The package path is not printed.
Named type:
```go
type t struct{foo int}
v := t{42}
notation.Printlnt(v)
```
Output:
```go
t{foo: 42}
```
Unnamed type:
```go
v := struct{foo int}{42}
notation.Printlnt(v)
```
Output:
```go
struct{foo int}{foo: 42}
```
Using the 't' suffixed variants of the printing functions, displaying only moderately verbose type information,
the types of certain values is omitted, where it can be inferred from the context:
```go
v := []struct{foo int}{{42}, {84}}
notation.Printlnt(os.Stdout, v)
```
Output:
```go
[]struct{foo int}{{foo: 42}, {foo: 84}}
```
##### Cyclic references
Cyclic references are detected based on an approach similar to the one in the stdlib's reflect.DeepEqual
function. Such occurences are displayed in the output with references:
```go
l := []interface{}{"foo"}
l[0] = l
notation.Fprint(os.Stdout, l)
```
Output:
```go
r0=[]{r0}
```

292
example_test.go Normal file
View File

@ -0,0 +1,292 @@
package notation_test
import (
"os"
"github.com/aryszka/notation"
)
type bike struct {
frame frame
driveTrain driveTrain
wheels []wheel
handlebar handlebar
saddle saddle
}
type frame struct {
fork fork
saddlePost saddlePost
bottomBracket *bracket
frontDerailleur *derailleur
rearDerailleur *derailleur
rearBrake *brake
rearWheel *wheel
}
type driveTrain struct {
bottomBracket bracket
crank crank
brakes []brake
derailleurs []derailleur
cassette cassette
chain chain
levers []lever
}
type wheel struct {
size float64
cassette *cassette
}
type handlebar struct{ levers []*lever }
type saddle struct{}
type fork struct {
wheel *wheel
handlebar *handlebar
frontBrake *brake
}
type saddlePost struct{ saddle *saddle }
type bracket struct{ crank *crank }
type derailleur struct{ gears int }
type brake struct{ discSize float64 }
type crank struct {
wheels int
chain *chain
}
type cassette struct {
wheels int
chain *chain
}
type chain struct{}
type lever struct{ withShift bool }
func Example() {
b := bike{
frame: frame{
fork: fork{},
saddlePost: saddlePost{},
},
driveTrain: driveTrain{
bottomBracket: bracket{},
crank: crank{wheels: 2},
brakes: []brake{{discSize: 160}, {discSize: 140}},
derailleurs: []derailleur{{gears: 2}, {gears: 11}},
cassette: cassette{wheels: 11},
chain: chain{},
levers: []lever{{true}, {true}},
},
wheels: []wheel{{size: 70}, {size: 70}},
handlebar: handlebar{},
saddle: saddle{},
}
b.frame.fork.wheel = &b.wheels[0]
b.frame.fork.handlebar = &b.handlebar
b.frame.fork.handlebar.levers = []*lever{&b.driveTrain.levers[0], &b.driveTrain.levers[1]}
b.frame.fork.frontBrake = &b.driveTrain.brakes[0]
b.frame.saddlePost.saddle = &b.saddle
b.frame.bottomBracket = &b.driveTrain.bottomBracket
b.frame.frontDerailleur = &b.driveTrain.derailleurs[0]
b.frame.rearDerailleur = &b.driveTrain.derailleurs[1]
b.frame.rearBrake = &b.driveTrain.brakes[1]
b.frame.rearWheel = &b.wheels[1]
b.frame.bottomBracket.crank = &b.driveTrain.crank
b.frame.bottomBracket.crank.chain = &b.driveTrain.chain
b.frame.rearWheel.cassette = &b.driveTrain.cassette
b.frame.rearWheel.cassette.chain = &b.driveTrain.chain
notation.Fprintw(os.Stdout, b)
// Output:
//
// {
// frame: {
// fork: {
// wheel: {size: 70, cassette: nil},
// handlebar: {levers: []{{withShift: true}, {withShift: true}}},
// frontBrake: {discSize: 160},
// },
// saddlePost: {saddle: {}},
// bottomBracket: {crank: {wheels: 2, chain: {}}},
// frontDerailleur: {gears: 2},
// rearDerailleur: {gears: 11},
// rearBrake: {discSize: 140},
// rearWheel: {size: 70, cassette: {wheels: 11, chain: {}}},
// },
// driveTrain: {
// bottomBracket: {crank: {wheels: 2, chain: {}}},
// crank: {wheels: 2, chain: {}},
// brakes: []{{discSize: 160}, {discSize: 140}},
// derailleurs: []{{gears: 2}, {gears: 11}},
// cassette: {wheels: 11, chain: {}},
// chain: {},
// levers: []{{withShift: true}, {withShift: true}},
// },
// wheels: []{{size: 70, cassette: nil}, {size: 70, cassette: {wheels: 11, chain: {}}}},
// handlebar: {levers: []{{withShift: true}, {withShift: true}}},
// saddle: {},
// }
}
func Example_int() {
i := 42
notation.Fprintt(os.Stdout, i)
// Output:
//
// 42
}
func Example_short_string() {
s := `foobar
baz`
notation.Fprintw(os.Stdout, s)
// Output:
//
// "foobar\nbaz"
}
func Example_long_string() {
s := `The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog.`
notation.Fprintw(os.Stdout, s)
// Output:
//
// `The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
// quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
// fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
// over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
// dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
// quick brown fox jumps over the lazy dog.`
}
func Example_slice() {
l := []int{1, 2, 3}
notation.Fprint(os.Stdout, l)
// Output:
//
// []{1, 2, 3}
}
func Example_array() {
a := [...]int{1, 2, 3}
notation.Fprint(os.Stdout, a)
// Output:
//
// [3]{1, 2, 3}
}
func Example_bytes() {
b := []byte(
`The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
quick brown fox jumps over the lazy dog.`,
)
notation.Fprintwt(os.Stdout, b)
// Output:
//
// []byte{
// 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a
// 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f
// 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f
// 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79
// 20 64 6f 67 2e 20 54 68 65 0a 71 75 69 63 6b 20 62 72 6f 77 6e
// 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c
// 61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72
// 6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68
// 65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b
// 20 62 72 6f 77 6e 0a 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72
// 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75
// 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f
// 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65
// 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70
// 73 0a 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e 20
// 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a
// 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f
// 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f
// 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79
// 0a 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e
// 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c
// 61 7a 79 20 64 6f 67 2e 20 54 68 65 20 71 75 69 63 6b 20 62 72
// 6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72 20 74 68
// 65 20 6c 61 7a 79 20 64 6f 67 2e 20 54 68 65 0a 71 75 69 63 6b
// 20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70 73 20 6f 76 65 72
// 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e
// }
}
func Example_maps_sorted_by_keys() {
m := map[string]int{"b": 1, "c": 2, "a": 3}
notation.Fprint(os.Stdout, m)
// Output:
//
// map{"a": 3, "b": 1, "c": 2}
}
func Example_function() {
f := func(int) int { return 42 }
notation.Fprint(os.Stdout, f)
// Output:
//
// func()
}
func Example_function_signature() {
f := func(int) int { return 42 }
notation.Fprintt(os.Stdout, f)
// Output:
//
// func(int) int
}
func Example_named_type() {
type t struct{ foo int }
v := t{42}
notation.Fprintt(os.Stdout, v)
// Output:
//
// t{foo: 42}
}
func Example_unnamed() {
v := struct{ foo int }{42}
notation.Fprintt(os.Stdout, v)
// Output:
//
// struct{foo int}{foo: 42}
}
func Example_type_inferred() {
v := []struct{ foo int }{{42}, {84}}
notation.Fprintt(os.Stdout, v)
// Output:
//
// []struct{foo int}{{foo: 42}, {foo: 84}}
}
func Example_cyclic_reference() {
l := []interface{}{"foo"}
l[0] = l
notation.Fprint(os.Stdout, l)
// Output:
//
// r0=[]{r0}
}

View File

@ -1,3 +1,7 @@
/*
Package notation can be used to print (or sprint) Go objects with optional wrapping (pretty printing) and
optional type information.
*/
package notation package notation
import ( import (
@ -199,98 +203,148 @@ func sprintValues(o opts, v []interface{}) string {
return b.String() return b.String()
} }
// Fprint prints the provided objects to the provided writer. When multiple objects are printed, they'll be
// separated by a space.
func Fprint(w io.Writer, v ...interface{}) (int, error) { func Fprint(w io.Writer, v ...interface{}) (int, error) {
return fprintValues(w, none, v) return fprintValues(w, none, v)
} }
// Fprintw prints the provided objects to the provided writer, with wrapping (pretty-printing) where necessary.
// When multiple objects are printed, they'll be separated by a newline.
func Fprintw(w io.Writer, v ...interface{}) (int, error) { func Fprintw(w io.Writer, v ...interface{}) (int, error) {
return fprintValues(w, wrap, v) return fprintValues(w, wrap, v)
} }
// Fprintt prints the provided objects to the provided writer with moderate type information. When multiple
// objects are printed, they'll be separated by a space.
func Fprintt(w io.Writer, v ...interface{}) (int, error) { func Fprintt(w io.Writer, v ...interface{}) (int, error) {
return fprintValues(w, types, v) return fprintValues(w, types, v)
} }
// Fprintwt prints the provided objects to the provided writer, with wrapping (pretty-printing) where necessary,
// and with moderate type information. When multiple objects are printed, they'll be separated by a newline.
func Fprintwt(w io.Writer, v ...interface{}) (int, error) { func Fprintwt(w io.Writer, v ...interface{}) (int, error) {
return fprintValues(w, wrap|types, v) return fprintValues(w, wrap|types, v)
} }
// Fprintv prints the provided objects to the provided writer with verbose type information. When multiple
// objects are printed, they'll be separated by a space.
func Fprintv(w io.Writer, v ...interface{}) (int, error) { func Fprintv(w io.Writer, v ...interface{}) (int, error) {
return fprintValues(w, allTypes, v) return fprintValues(w, allTypes, v)
} }
// Fprintwv prints the provided objects to the provided writer, with wrapping (pretty-printing) where necessary,
// and with verbose type information. When multiple objects are printed, they'll be separated by a newline.
func Fprintwv(w io.Writer, v ...interface{}) (int, error) { func Fprintwv(w io.Writer, v ...interface{}) (int, error) {
return fprintValues(w, wrap|allTypes, v) return fprintValues(w, wrap|allTypes, v)
} }
// Print prints the provided objects to stderr. When multiple objects are printed, they'll be separated by a
// space.
func Print(v ...interface{}) (int, error) { func Print(v ...interface{}) (int, error) {
return printValues(none, v) return printValues(none, v)
} }
// Printw prints the provided objects to stderr, with wrapping (pretty-printing) where necessary. When multiple
// objects are printed, they'll be separated by a newline.
func Printw(v ...interface{}) (int, error) { func Printw(v ...interface{}) (int, error) {
return printValues(wrap, v) return printValues(wrap, v)
} }
// Printt prints the provided objects to stderr with moderate type information. When multiple objects are
// printed, they'll be separated by a space.
func Printt(v ...interface{}) (int, error) { func Printt(v ...interface{}) (int, error) {
return printValues(types, v) return printValues(types, v)
} }
// Printwt prints the provided objects to stderr, with wrapping (pretty-printing) where necessary, and with
// moderate type information. When multiple objects are printed, they'll be separated by a newline.
func Printwt(v ...interface{}) (int, error) { func Printwt(v ...interface{}) (int, error) {
return printValues(wrap|types, v) return printValues(wrap|types, v)
} }
// Printv prints the provided objects to stderr with verbose type information. When multiple objects are
// printed, they'll be separated by a space.
func Printv(v ...interface{}) (int, error) { func Printv(v ...interface{}) (int, error) {
return printValues(allTypes, v) return printValues(allTypes, v)
} }
// Printwv prints the provided objects to stderr, with wrapping (pretty-printing) where necessary, and with
// verbose type information. When multiple objects are printed, they'll be separated by a newline.
func Printwv(v ...interface{}) (int, error) { func Printwv(v ...interface{}) (int, error) {
return printValues(wrap|allTypes, v) return printValues(wrap|allTypes, v)
} }
// Println prints the provided objects to stderr with a closing newline. When multiple objects are printed,
// they'll be separated by a space.
func Println(v ...interface{}) (int, error) { func Println(v ...interface{}) (int, error) {
return printlnValues(none, v) return printlnValues(none, v)
} }
// Printlnw prints the provided objects to stderr with a closing newline, with wrapping (pretty-printing) where
// necessary. When multiple objects are printed, they'll be separated by a newline.
func Printlnw(v ...interface{}) (int, error) { func Printlnw(v ...interface{}) (int, error) {
return printlnValues(wrap, v) return printlnValues(wrap, v)
} }
// Printlnt prints the provided objects to stderr with a closing newline, and with moderate type information. When
// multiple objects are printed, they'll be separated by a space.
func Printlnt(v ...interface{}) (int, error) { func Printlnt(v ...interface{}) (int, error) {
return printlnValues(types, v) return printlnValues(types, v)
} }
// Printlnwt prints the provided objects to stderr with a closing newline, with wrapping (pretty-printing) where
// necessary, and with moderate type information. When multiple objects are printed, they'll be separated by a
// newline.
func Printlnwt(v ...interface{}) (int, error) { func Printlnwt(v ...interface{}) (int, error) {
return printlnValues(wrap|types, v) return printlnValues(wrap|types, v)
} }
// Printlnv prints the provided objects to stderr with a closing newline, and with verbose type information. When
// multiple objects are printed, they'll be separated by a space.
func Printlnv(v ...interface{}) (int, error) { func Printlnv(v ...interface{}) (int, error) {
return printlnValues(allTypes, v) return printlnValues(allTypes, v)
} }
// Printlnwv prints the provided objects to stderr with a closing newline, with wrapping (pretty-printing) where
// necessary, and with verbose type information. When multiple objects are printed, they'll be separated by a
// newline.
func Printlnwv(v ...interface{}) (int, error) { func Printlnwv(v ...interface{}) (int, error) {
return printlnValues(wrap|allTypes, v) return printlnValues(wrap|allTypes, v)
} }
// Sprint returns the string representation of the Go objects. When multiple objects are provided, they'll be
// seprated by a space.
func Sprint(v ...interface{}) string { func Sprint(v ...interface{}) string {
return sprintValues(none, v) return sprintValues(none, v)
} }
// Sprint returns the string representation of the Go objects, with wrapping (pretty-printing) where necessary.
// When multiple objects are provided, they'll be seprated by a newline.
func Sprintw(v ...interface{}) string { func Sprintw(v ...interface{}) string {
return sprintValues(wrap, v) return sprintValues(wrap, v)
} }
// Sprint returns the string representation of the Go objects, with moderate type information. When multiple
// objects are provided, they'll be seprated by a space.
func Sprintt(v ...interface{}) string { func Sprintt(v ...interface{}) string {
return sprintValues(types, v) return sprintValues(types, v)
} }
// Sprint returns the string representation of the Go objects, with wrapping (pretty-printing) where necessary,
// and with moderate type information. When multiple objects are provided, they'll be seprated by a newline.
func Sprintwt(v ...interface{}) string { func Sprintwt(v ...interface{}) string {
return sprintValues(wrap|types, v) return sprintValues(wrap|types, v)
} }
// Sprint returns the string representation of the Go objects, with verbose type information. When multiple
// objects are provided, they'll be seprated by a space.
func Sprintv(v ...interface{}) string { func Sprintv(v ...interface{}) string {
return sprintValues(allTypes, v) return sprintValues(allTypes, v)
} }
// Sprint returns the string representation of the Go objects, with wrapping (pretty-printing) where necessary,
// and with verbose type information. When multiple objects are provided, they'll be seprated by a newline.
func Sprintwv(v ...interface{}) string { func Sprintwv(v ...interface{}) string {
return sprintValues(wrap|allTypes, v) return sprintValues(wrap|allTypes, v)
} }

View File

@ -12,21 +12,21 @@ func TestPrint(t *testing.T) {
e string e string
}{{ }{{
p: Print, p: Print,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `{foo: 42}`, e: `{foo: 42}`,
}, { }, {
p: Printw, p: Printw,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `{ e: `{
foo: 42, foo: 42,
}`, }`,
}, { }, {
p: Printt, p: Printt,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `struct{foo int}{foo: 42}`, e: `struct{foo int}{foo: 42}`,
}, { }, {
p: Printwt, p: Printwt,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `struct{ e: `struct{
foo int foo int
}{ }{
@ -34,11 +34,11 @@ func TestPrint(t *testing.T) {
}`, }`,
}, { }, {
p: Printv, p: Printv,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `struct{foo int}{foo: int(42)}`, e: `struct{foo int}{foo: int(42)}`,
}, { }, {
p: Printwv, p: Printwv,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `struct{ e: `struct{
foo int foo int
}{ }{
@ -76,22 +76,22 @@ func TestPrintln(t *testing.T) {
e string e string
}{{ }{{
p: Println, p: Println,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: "{foo: 42}\n", e: "{foo: 42}\n",
}, { }, {
p: Printlnw, p: Printlnw,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `{ e: `{
foo: 42, foo: 42,
} }
`, `,
}, { }, {
p: Printlnt, p: Printlnt,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: "struct{foo int}{foo: 42}\n", e: "struct{foo int}{foo: 42}\n",
}, { }, {
p: Printlnwt, p: Printlnwt,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `struct{ e: `struct{
foo int foo int
}{ }{
@ -100,11 +100,11 @@ func TestPrintln(t *testing.T) {
`, `,
}, { }, {
p: Printlnv, p: Printlnv,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: "struct{foo int}{foo: int(42)}\n", e: "struct{foo int}{foo: int(42)}\n",
}, { }, {
p: Printlnwv, p: Printlnwv,
o: struct{foo int}{42}, o: struct{ foo int }{42},
e: `struct{ e: `struct{
foo int foo int
}{ }{
@ -113,7 +113,7 @@ func TestPrintln(t *testing.T) {
`, `,
}, { }, {
p: Println, p: Println,
o: struct{foo int}{42}, o: struct{ foo int }{42},
f: true, f: true,
}} { }} {
defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")()

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"sort" "sort"
"strconv"
"strings" "strings"
) )
@ -136,12 +137,7 @@ func reflectMap(o opts, p *pending, r reflect.Value) node {
return reflectNil(o, true, r) return reflectNil(o, true, r)
} }
var ( var skeys []string
nkeys []node
skeys []string
)
// TODO: simplify this when no sorting is required
items := wrapper{sep: ", ", suffix: ","} items := wrapper{sep: ", ", suffix: ","}
itemOpts := o | skipTypes itemOpts := o | skipTypes
keys := r.MapKeys() keys := r.MapKeys()
@ -150,7 +146,6 @@ func reflectMap(o opts, p *pending, r reflect.Value) node {
for _, key := range keys { for _, key := range keys {
var b bytes.Buffer var b bytes.Buffer
nk := reflectValue(itemOpts, p, key) nk := reflectValue(itemOpts, p, key)
nkeys = append(nkeys, nk)
wr := writer{w: &b} wr := writer{w: &b}
fprint(&wr, 0, nk) fprint(&wr, 0, nk)
skey := b.String() skey := b.String()
@ -208,32 +203,7 @@ func reflectList(o opts, p *pending, r reflect.Value) node {
func reflectString(o opts, r reflect.Value) node { func reflectString(o opts, r reflect.Value) node {
sv := r.String() sv := r.String()
b := []byte(sv) s := str{val: strconv.Quote(sv)}
e := make([]byte, 0, len(b))
for _, c := range b {
switch c {
case '\\':
e = append(e, '\\', '\\')
case '"':
e = append(e, '\\', '"')
case '\b':
e = append(e, '\\', 'b')
case '\f':
e = append(e, '\\', 'f')
case '\n':
e = append(e, '\\', 'n')
case '\r':
e = append(e, '\\', 'r')
case '\t':
e = append(e, '\\', 't')
case '\v':
e = append(e, '\\', 'v')
default:
e = append(e, c)
}
}
s := str{val: fmt.Sprintf("\"%s\"", string(e))}
if !strings.Contains(sv, "`") && strings.Contains(sv, "\n") { if !strings.Contains(sv, "`") && strings.Contains(sv, "\n") {
s.raw = fmt.Sprintf("`%s`", sv) s.raw = fmt.Sprintf("`%s`", sv)
} }