public.go raw

   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