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 15 package pretty
16 17 import (
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.
32 type 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
79 var (
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 107 func (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.
123 func Print(vals ...interface{}) {
124 DefaultConfig.Print(vals...)
125 }
126 127 // Print writes the configured presentation of the given values to standard output.
128 func (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.
133 func Sprint(vals ...interface{}) string {
134 return DefaultConfig.Sprint(vals...)
135 }
136 137 // Sprint returns a string representation of the given value according to cfg.
138 func (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.
145 func 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.
150 func (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.
168 func 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.
184 func (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 }
189