1// Copyright 2013 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package pretty
16
17import (
18 "bytes"
19 "fmt"
20 "io"
21 "net"
22 "reflect"
23 "time"
24
25 "github.com/kylelemons/godebug/diff"
26)
27
28// A Config represents optional configuration parameters for formatting.
29//
30// Some options, notably ShortList, dramatically increase the overhead
31// of pretty-printing a value.
32type Config struct {
33 // Verbosity options
34 Compact bool // One-line output. Overrides Diffable.
35 Diffable bool // Adds extra newlines for more easily diffable output.
36
37 // Field and value options
38 IncludeUnexported bool // Include unexported fields in output
39 PrintStringers bool // Call String on a fmt.Stringer
40 PrintTextMarshalers bool // Call MarshalText on an encoding.TextMarshaler
41 SkipZeroFields bool // Skip struct fields that have a zero value.
42
43 // Output transforms
44 ShortList int // Maximum character length for short lists if nonzero.
45
46 // Type-specific overrides
47 //
48 // Formatter maps a type to a function that will provide a one-line string
49 // representation of the input value. Conceptually:
50 // Formatter[reflect.TypeOf(v)](v) = "v as a string"
51 //
52 // Note that the first argument need not explicitly match the type, it must
53 // merely be callable with it.
54 //
55 // When processing an input value, if its type exists as a key in Formatter:
56 // 1) If the value is nil, no stringification is performed.
57 // This allows overriding of PrintStringers and PrintTextMarshalers.
58 // 2) The value will be called with the input as its only argument.
59 // The function must return a string as its first return value.
60 //
61 // In addition to func literals, two common values for this will be:
62 // fmt.Sprint (function) func Sprint(...interface{}) string
63 // Type.String (method) func (Type) String() string
64 //
65 // Note that neither of these work if the String method is a pointer
66 // method and the input will be provided as a value. In that case,
67 // use a function that calls .String on the formal value parameter.
68 Formatter map[reflect.Type]interface{}
69
70 // If TrackCycles is enabled, pretty will detect and track
71 // self-referential structures. If a self-referential structure (aka a
72 // "recursive" value) is detected, numbered placeholders will be emitted.
73 //
74 // Pointer tracking is disabled by default for performance reasons.
75 TrackCycles bool
76}
77
78// Default Config objects
79var (
80 // DefaultFormatter is the default set of overrides for stringification.
81 DefaultFormatter = map[reflect.Type]interface{}{
82 reflect.TypeOf(time.Time{}): fmt.Sprint,
83 reflect.TypeOf(net.IP{}): fmt.Sprint,
84 reflect.TypeOf((*error)(nil)).Elem(): fmt.Sprint,
85 }
86
87 // CompareConfig is the default configuration used for Compare.
88 CompareConfig = &Config{
89 Diffable: true,
90 IncludeUnexported: true,
91 Formatter: DefaultFormatter,
92 }
93
94 // DefaultConfig is the default configuration used for all other top-level functions.
95 DefaultConfig = &Config{
96 Formatter: DefaultFormatter,
97 }
98
99 // CycleTracker is a convenience config for formatting and comparing recursive structures.
100 CycleTracker = &Config{
101 Diffable: true,
102 Formatter: DefaultFormatter,
103 TrackCycles: true,
104 }
105)
106
107func (cfg *Config) fprint(buf *bytes.Buffer, vals ...interface{}) {
108 ref := &reflector{
109 Config: cfg,
110 }
111 if cfg.TrackCycles {
112 ref.pointerTracker = new(pointerTracker)
113 }
114 for i, val := range vals {
115 if i > 0 {
116 buf.WriteByte('\n')
117 }
118 newFormatter(cfg, buf).write(ref.val2node(reflect.ValueOf(val)))
119 }
120}
121
122// Print writes the DefaultConfig representation of the given values to standard output.
123func Print(vals ...interface{}) {
124 DefaultConfig.Print(vals...)
125}
126
127// Print writes the configured presentation of the given values to standard output.
128func (cfg *Config) Print(vals ...interface{}) {
129 fmt.Println(cfg.Sprint(vals...))
130}
131
132// Sprint returns a string representation of the given value according to the DefaultConfig.
133func Sprint(vals ...interface{}) string {
134 return DefaultConfig.Sprint(vals...)
135}
136
137// Sprint returns a string representation of the given value according to cfg.
138func (cfg *Config) Sprint(vals ...interface{}) string {
139 buf := new(bytes.Buffer)
140 cfg.fprint(buf, vals...)
141 return buf.String()
142}
143
144// Fprint writes the representation of the given value to the writer according to the DefaultConfig.
145func Fprint(w io.Writer, vals ...interface{}) (n int64, err error) {
146 return DefaultConfig.Fprint(w, vals...)
147}
148
149// Fprint writes the representation of the given value to the writer according to the cfg.
150func (cfg *Config) Fprint(w io.Writer, vals ...interface{}) (n int64, err error) {
151 buf := new(bytes.Buffer)
152 cfg.fprint(buf, vals...)
153 return buf.WriteTo(w)
154}
155
156// Compare returns a string containing a line-by-line unified diff of the
157// values in a and b, using the CompareConfig.
158//
159// Each line in the output is prefixed with '+', '-', or ' ' to indicate which
160// side it's from. Lines from the a side are marked with '-', lines from the
161// b side are marked with '+' and lines that are the same on both sides are
162// marked with ' '.
163//
164// The comparison is based on the intentionally-untyped output of Print, and as
165// such this comparison is pretty forviving. In particular, if the types of or
166// types within in a and b are different but have the same representation,
167// Compare will not indicate any differences between them.
168func Compare(a, b interface{}) string {
169 return CompareConfig.Compare(a, b)
170}
171
172// Compare returns a string containing a line-by-line unified diff of the
173// values in got and want according to the cfg.
174//
175// Each line in the output is prefixed with '+', '-', or ' ' to indicate which
176// side it's from. Lines from the a side are marked with '-', lines from the
177// b side are marked with '+' and lines that are the same on both sides are
178// marked with ' '.
179//
180// The comparison is based on the intentionally-untyped output of Print, and as
181// such this comparison is pretty forviving. In particular, if the types of or
182// types within in a and b are different but have the same representation,
183// Compare will not indicate any differences between them.
184func (cfg *Config) Compare(a, b interface{}) string {
185 diffCfg := *cfg
186 diffCfg.Diffable = true
187 return diff.Diff(cfg.Sprint(a), cfg.Sprint(b))
188}