mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
4.4 KiB
223 lines
4.4 KiB
// Copyright 2013 Google Inc. All rights reserved. |
|
// |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
package pretty |
|
|
|
import ( |
|
"bufio" |
|
"bytes" |
|
"fmt" |
|
"io" |
|
"strconv" |
|
"strings" |
|
) |
|
|
|
// a formatter stores stateful formatting information as well as being |
|
// an io.Writer for simplicity. |
|
type formatter struct { |
|
*bufio.Writer |
|
*Config |
|
|
|
// Self-referential structure tracking |
|
tagNumbers map[int]int // tagNumbers[id] = <#n> |
|
} |
|
|
|
// newFormatter creates a new buffered formatter. For the output to be written |
|
// to the given writer, this must be accompanied by a call to write (or Flush). |
|
func newFormatter(cfg *Config, w io.Writer) *formatter { |
|
return &formatter{ |
|
Writer: bufio.NewWriter(w), |
|
Config: cfg, |
|
tagNumbers: make(map[int]int), |
|
} |
|
} |
|
|
|
func (f *formatter) write(n node) { |
|
defer f.Flush() |
|
n.format(f, "") |
|
} |
|
|
|
func (f *formatter) tagFor(id int) int { |
|
if tag, ok := f.tagNumbers[id]; ok { |
|
return tag |
|
} |
|
if f.tagNumbers == nil { |
|
return 0 |
|
} |
|
tag := len(f.tagNumbers) + 1 |
|
f.tagNumbers[id] = tag |
|
return tag |
|
} |
|
|
|
type node interface { |
|
format(f *formatter, indent string) |
|
} |
|
|
|
func (f *formatter) compactString(n node) string { |
|
switch k := n.(type) { |
|
case stringVal: |
|
return string(k) |
|
case rawVal: |
|
return string(k) |
|
} |
|
|
|
buf := new(bytes.Buffer) |
|
f2 := newFormatter(&Config{Compact: true}, buf) |
|
f2.tagNumbers = f.tagNumbers // reuse tagNumbers just in case |
|
f2.write(n) |
|
return buf.String() |
|
} |
|
|
|
type stringVal string |
|
|
|
func (str stringVal) format(f *formatter, indent string) { |
|
f.WriteString(strconv.Quote(string(str))) |
|
} |
|
|
|
type rawVal string |
|
|
|
func (r rawVal) format(f *formatter, indent string) { |
|
f.WriteString(string(r)) |
|
} |
|
|
|
type keyval struct { |
|
key string |
|
val node |
|
} |
|
|
|
type keyvals []keyval |
|
|
|
func (l keyvals) format(f *formatter, indent string) { |
|
f.WriteByte('{') |
|
|
|
switch { |
|
case f.Compact: |
|
// All on one line: |
|
for i, kv := range l { |
|
if i > 0 { |
|
f.WriteByte(',') |
|
} |
|
f.WriteString(kv.key) |
|
f.WriteByte(':') |
|
kv.val.format(f, indent) |
|
} |
|
case f.Diffable: |
|
f.WriteByte('\n') |
|
inner := indent + " " |
|
// Each value gets its own line: |
|
for _, kv := range l { |
|
f.WriteString(inner) |
|
f.WriteString(kv.key) |
|
f.WriteString(": ") |
|
kv.val.format(f, inner) |
|
f.WriteString(",\n") |
|
} |
|
f.WriteString(indent) |
|
default: |
|
keyWidth := 0 |
|
for _, kv := range l { |
|
if kw := len(kv.key); kw > keyWidth { |
|
keyWidth = kw |
|
} |
|
} |
|
alignKey := indent + " " |
|
alignValue := strings.Repeat(" ", keyWidth) |
|
inner := alignKey + alignValue + " " |
|
// First and last line shared with bracket: |
|
for i, kv := range l { |
|
if i > 0 { |
|
f.WriteString(",\n") |
|
f.WriteString(alignKey) |
|
} |
|
f.WriteString(kv.key) |
|
f.WriteString(": ") |
|
f.WriteString(alignValue[len(kv.key):]) |
|
kv.val.format(f, inner) |
|
} |
|
} |
|
|
|
f.WriteByte('}') |
|
} |
|
|
|
type list []node |
|
|
|
func (l list) format(f *formatter, indent string) { |
|
if max := f.ShortList; max > 0 { |
|
short := f.compactString(l) |
|
if len(short) <= max { |
|
f.WriteString(short) |
|
return |
|
} |
|
} |
|
|
|
f.WriteByte('[') |
|
|
|
switch { |
|
case f.Compact: |
|
// All on one line: |
|
for i, v := range l { |
|
if i > 0 { |
|
f.WriteByte(',') |
|
} |
|
v.format(f, indent) |
|
} |
|
case f.Diffable: |
|
f.WriteByte('\n') |
|
inner := indent + " " |
|
// Each value gets its own line: |
|
for _, v := range l { |
|
f.WriteString(inner) |
|
v.format(f, inner) |
|
f.WriteString(",\n") |
|
} |
|
f.WriteString(indent) |
|
default: |
|
inner := indent + " " |
|
// First and last line shared with bracket: |
|
for i, v := range l { |
|
if i > 0 { |
|
f.WriteString(",\n") |
|
f.WriteString(inner) |
|
} |
|
v.format(f, inner) |
|
} |
|
} |
|
|
|
f.WriteByte(']') |
|
} |
|
|
|
type ref struct { |
|
id int |
|
} |
|
|
|
func (r ref) format(f *formatter, indent string) { |
|
fmt.Fprintf(f, "<see #%d>", f.tagFor(r.id)) |
|
} |
|
|
|
type target struct { |
|
id int |
|
value node |
|
} |
|
|
|
func (t target) format(f *formatter, indent string) { |
|
tag := fmt.Sprintf("<#%d> ", f.tagFor(t.id)) |
|
switch { |
|
case f.Diffable, f.Compact: |
|
// no indent changes |
|
default: |
|
indent += strings.Repeat(" ", len(tag)) |
|
} |
|
f.WriteString(tag) |
|
t.value.format(f, indent) |
|
}
|
|
|