structure.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  	"bufio"
  19  	"bytes"
  20  	"fmt"
  21  	"io"
  22  	"strconv"
  23  	"strings"
  24  )
  25  
  26  // a formatter stores stateful formatting information as well as being
  27  // an io.Writer for simplicity.
  28  type formatter struct {
  29  	*bufio.Writer
  30  	*Config
  31  
  32  	// Self-referential structure tracking
  33  	tagNumbers map[int]int // tagNumbers[id] = <#n>
  34  }
  35  
  36  // newFormatter creates a new buffered formatter.  For the output to be written
  37  // to the given writer, this must be accompanied by a call to write (or Flush).
  38  func newFormatter(cfg *Config, w io.Writer) *formatter {
  39  	return &formatter{
  40  		Writer:     bufio.NewWriter(w),
  41  		Config:     cfg,
  42  		tagNumbers: make(map[int]int),
  43  	}
  44  }
  45  
  46  func (f *formatter) write(n node) {
  47  	defer f.Flush()
  48  	n.format(f, "")
  49  }
  50  
  51  func (f *formatter) tagFor(id int) int {
  52  	if tag, ok := f.tagNumbers[id]; ok {
  53  		return tag
  54  	}
  55  	if f.tagNumbers == nil {
  56  		return 0
  57  	}
  58  	tag := len(f.tagNumbers) + 1
  59  	f.tagNumbers[id] = tag
  60  	return tag
  61  }
  62  
  63  type node interface {
  64  	format(f *formatter, indent string)
  65  }
  66  
  67  func (f *formatter) compactString(n node) string {
  68  	switch k := n.(type) {
  69  	case stringVal:
  70  		return string(k)
  71  	case rawVal:
  72  		return string(k)
  73  	}
  74  
  75  	buf := new(bytes.Buffer)
  76  	f2 := newFormatter(&Config{Compact: true}, buf)
  77  	f2.tagNumbers = f.tagNumbers // reuse tagNumbers just in case
  78  	f2.write(n)
  79  	return buf.String()
  80  }
  81  
  82  type stringVal string
  83  
  84  func (str stringVal) format(f *formatter, indent string) {
  85  	f.WriteString(strconv.Quote(string(str)))
  86  }
  87  
  88  type rawVal string
  89  
  90  func (r rawVal) format(f *formatter, indent string) {
  91  	f.WriteString(string(r))
  92  }
  93  
  94  type keyval struct {
  95  	key string
  96  	val node
  97  }
  98  
  99  type keyvals []keyval
 100  
 101  func (l keyvals) format(f *formatter, indent string) {
 102  	f.WriteByte('{')
 103  
 104  	switch {
 105  	case f.Compact:
 106  		// All on one line:
 107  		for i, kv := range l {
 108  			if i > 0 {
 109  				f.WriteByte(',')
 110  			}
 111  			f.WriteString(kv.key)
 112  			f.WriteByte(':')
 113  			kv.val.format(f, indent)
 114  		}
 115  	case f.Diffable:
 116  		f.WriteByte('\n')
 117  		inner := indent + " "
 118  		// Each value gets its own line:
 119  		for _, kv := range l {
 120  			f.WriteString(inner)
 121  			f.WriteString(kv.key)
 122  			f.WriteString(": ")
 123  			kv.val.format(f, inner)
 124  			f.WriteString(",\n")
 125  		}
 126  		f.WriteString(indent)
 127  	default:
 128  		keyWidth := 0
 129  		for _, kv := range l {
 130  			if kw := len(kv.key); kw > keyWidth {
 131  				keyWidth = kw
 132  			}
 133  		}
 134  		alignKey := indent + " "
 135  		alignValue := strings.Repeat(" ", keyWidth)
 136  		inner := alignKey + alignValue + "  "
 137  		// First and last line shared with bracket:
 138  		for i, kv := range l {
 139  			if i > 0 {
 140  				f.WriteString(",\n")
 141  				f.WriteString(alignKey)
 142  			}
 143  			f.WriteString(kv.key)
 144  			f.WriteString(": ")
 145  			f.WriteString(alignValue[len(kv.key):])
 146  			kv.val.format(f, inner)
 147  		}
 148  	}
 149  
 150  	f.WriteByte('}')
 151  }
 152  
 153  type list []node
 154  
 155  func (l list) format(f *formatter, indent string) {
 156  	if max := f.ShortList; max > 0 {
 157  		short := f.compactString(l)
 158  		if len(short) <= max {
 159  			f.WriteString(short)
 160  			return
 161  		}
 162  	}
 163  
 164  	f.WriteByte('[')
 165  
 166  	switch {
 167  	case f.Compact:
 168  		// All on one line:
 169  		for i, v := range l {
 170  			if i > 0 {
 171  				f.WriteByte(',')
 172  			}
 173  			v.format(f, indent)
 174  		}
 175  	case f.Diffable:
 176  		f.WriteByte('\n')
 177  		inner := indent + " "
 178  		// Each value gets its own line:
 179  		for _, v := range l {
 180  			f.WriteString(inner)
 181  			v.format(f, inner)
 182  			f.WriteString(",\n")
 183  		}
 184  		f.WriteString(indent)
 185  	default:
 186  		inner := indent + " "
 187  		// First and last line shared with bracket:
 188  		for i, v := range l {
 189  			if i > 0 {
 190  				f.WriteString(",\n")
 191  				f.WriteString(inner)
 192  			}
 193  			v.format(f, inner)
 194  		}
 195  	}
 196  
 197  	f.WriteByte(']')
 198  }
 199  
 200  type ref struct {
 201  	id int
 202  }
 203  
 204  func (r ref) format(f *formatter, indent string) {
 205  	fmt.Fprintf(f, "<see #%d>", f.tagFor(r.id))
 206  }
 207  
 208  type target struct {
 209  	id    int
 210  	value node
 211  }
 212  
 213  func (t target) format(f *formatter, indent string) {
 214  	tag := fmt.Sprintf("<#%d> ", f.tagFor(t.id))
 215  	switch {
 216  	case f.Diffable, f.Compact:
 217  		// no indent changes
 218  	default:
 219  		indent += strings.Repeat(" ", len(tag))
 220  	}
 221  	f.WriteString(tag)
 222  	t.value.format(f, indent)
 223  }
 224