documentation
This commit is contained in:
parent
f999a77ff9
commit
1becff5c5d
445
README.md
Normal file
445
README.md
Normal 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
292
example_test.go
Normal 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}
|
||||
}
|
54
notation.go
54
notation.go
@ -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
|
||||
|
||||
import (
|
||||
@ -199,98 +203,148 @@ func sprintValues(o opts, v []interface{}) 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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
return sprintValues(wrap|allTypes, v)
|
||||
}
|
||||
|
@ -12,21 +12,21 @@ func TestPrint(t *testing.T) {
|
||||
e string
|
||||
}{{
|
||||
p: Print,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `{foo: 42}`,
|
||||
}, {
|
||||
p: Printw,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `{
|
||||
foo: 42,
|
||||
}`,
|
||||
}, {
|
||||
p: Printt,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `struct{foo int}{foo: 42}`,
|
||||
}, {
|
||||
p: Printwt,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `struct{
|
||||
foo int
|
||||
}{
|
||||
@ -34,11 +34,11 @@ func TestPrint(t *testing.T) {
|
||||
}`,
|
||||
}, {
|
||||
p: Printv,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `struct{foo int}{foo: int(42)}`,
|
||||
}, {
|
||||
p: Printwv,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `struct{
|
||||
foo int
|
||||
}{
|
||||
@ -76,22 +76,22 @@ func TestPrintln(t *testing.T) {
|
||||
e string
|
||||
}{{
|
||||
p: Println,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: "{foo: 42}\n",
|
||||
}, {
|
||||
p: Printlnw,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `{
|
||||
foo: 42,
|
||||
}
|
||||
`,
|
||||
}, {
|
||||
p: Printlnt,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: "struct{foo int}{foo: 42}\n",
|
||||
}, {
|
||||
p: Printlnwt,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `struct{
|
||||
foo int
|
||||
}{
|
||||
@ -100,11 +100,11 @@ func TestPrintln(t *testing.T) {
|
||||
`,
|
||||
}, {
|
||||
p: Printlnv,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: "struct{foo int}{foo: int(42)}\n",
|
||||
}, {
|
||||
p: Printlnwv,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
e: `struct{
|
||||
foo int
|
||||
}{
|
||||
@ -113,7 +113,7 @@ func TestPrintln(t *testing.T) {
|
||||
`,
|
||||
}, {
|
||||
p: Println,
|
||||
o: struct{foo int}{42},
|
||||
o: struct{ foo int }{42},
|
||||
f: true,
|
||||
}} {
|
||||
defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")()
|
||||
|
36
reflect.go
36
reflect.go
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -136,12 +137,7 @@ func reflectMap(o opts, p *pending, r reflect.Value) node {
|
||||
return reflectNil(o, true, r)
|
||||
}
|
||||
|
||||
var (
|
||||
nkeys []node
|
||||
skeys []string
|
||||
)
|
||||
|
||||
// TODO: simplify this when no sorting is required
|
||||
var skeys []string
|
||||
items := wrapper{sep: ", ", suffix: ","}
|
||||
itemOpts := o | skipTypes
|
||||
keys := r.MapKeys()
|
||||
@ -150,7 +146,6 @@ func reflectMap(o opts, p *pending, r reflect.Value) node {
|
||||
for _, key := range keys {
|
||||
var b bytes.Buffer
|
||||
nk := reflectValue(itemOpts, p, key)
|
||||
nkeys = append(nkeys, nk)
|
||||
wr := writer{w: &b}
|
||||
fprint(&wr, 0, nk)
|
||||
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 {
|
||||
sv := r.String()
|
||||
b := []byte(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))}
|
||||
s := str{val: strconv.Quote(sv)}
|
||||
if !strings.Contains(sv, "`") && strings.Contains(sv, "\n") {
|
||||
s.raw = fmt.Sprintf("`%s`", sv)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user