structure.go

  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	"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.
 28type 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).
 38func 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
 46func (f *formatter) write(n node) {
 47	defer f.Flush()
 48	n.format(f, "")
 49}
 50
 51func (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
 63type node interface {
 64	format(f *formatter, indent string)
 65}
 66
 67func (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
 82type stringVal string
 83
 84func (str stringVal) format(f *formatter, indent string) {
 85	f.WriteString(strconv.Quote(string(str)))
 86}
 87
 88type rawVal string
 89
 90func (r rawVal) format(f *formatter, indent string) {
 91	f.WriteString(string(r))
 92}
 93
 94type keyval struct {
 95	key string
 96	val node
 97}
 98
 99type keyvals []keyval
100
101func (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
153type list []node
154
155func (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
200type ref struct {
201	id int
202}
203
204func (r ref) format(f *formatter, indent string) {
205	fmt.Fprintf(f, "<see #%d>", f.tagFor(r.id))
206}
207
208type target struct {
209	id    int
210	value node
211}
212
213func (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}