mirror of
https://github.com/thegeeklab/drone-yaml.git
synced 2024-11-22 18:10:40 +00:00
355 lines
5.8 KiB
Go
355 lines
5.8 KiB
Go
// Copyright (c), the Drone Authors.
|
|
// Copyright (c) 2021, Robert Kaussow <mail@thegeeklab.de>
|
|
|
|
package pretty
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/drone/drone-yaml/yaml"
|
|
)
|
|
|
|
// TODO rename WriteTag to WriteKey
|
|
// TODO rename WriteTagValue to WriteKeyValue
|
|
|
|
// ESCAPING:
|
|
//
|
|
// The string starts with a special character:
|
|
// One of !#%@&*`?|>{[ or -.
|
|
// The string starts or ends with whitespace characters.
|
|
// The string contains : or # character sequences.
|
|
// The string ends with a colon.
|
|
// The value looks like a number or boolean (123, 1.23, true, false, null) but should be a string.
|
|
|
|
// Implement state pooling. See:
|
|
// https://golang.org/src/fmt/print.go#L131
|
|
|
|
type writer interface {
|
|
// Indent appends padding to the buffer.
|
|
Indent()
|
|
|
|
// IndentIncrease increases indentation.
|
|
IndentIncrease()
|
|
|
|
// IndentDecrease decreases indentation.
|
|
IndentDecrease()
|
|
|
|
// IncludeZero includes zero value values
|
|
IncludeZero()
|
|
|
|
// ExcludeZero excludes zero value values.
|
|
ExcludeZero()
|
|
|
|
// Write appends the contents of p to the buffer.
|
|
Write(p []byte) (n int, err error)
|
|
|
|
// WriteByte appends the contents of b to the buffer.
|
|
WriteByte(b byte) error
|
|
|
|
// WriteString appends the contents of s to the buffer.
|
|
WriteString(s string) (n int, err error)
|
|
|
|
// WriteTag appends the key to the buffer.
|
|
WriteTag(v interface{})
|
|
|
|
// WriteTag appends the keypair to the buffer.
|
|
WriteTagValue(k, v interface{})
|
|
}
|
|
|
|
//
|
|
// node writer
|
|
//
|
|
|
|
type baseWriter struct {
|
|
bytes.Buffer
|
|
depth int
|
|
zero bool
|
|
}
|
|
|
|
func (w *baseWriter) Indent() {
|
|
for i := 0; i < w.depth; i++ {
|
|
w.WriteString(" ")
|
|
}
|
|
}
|
|
|
|
func (w *baseWriter) IndentIncrease() {
|
|
w.depth++
|
|
}
|
|
|
|
func (w *baseWriter) IndentDecrease() {
|
|
w.depth--
|
|
}
|
|
|
|
func (w *baseWriter) IncludeZero() {
|
|
w.zero = true
|
|
}
|
|
|
|
func (w *baseWriter) ExcludeZero() {
|
|
w.zero = false
|
|
}
|
|
|
|
func (w *baseWriter) WriteTag(v interface{}) {
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
writeValue(w, v)
|
|
w.WriteByte(':')
|
|
}
|
|
|
|
func (w *baseWriter) WriteTagValue(k, v interface{}) {
|
|
if isZero(v) && !w.zero {
|
|
return
|
|
}
|
|
w.WriteTag(k)
|
|
if isPrimative(v) {
|
|
w.WriteByte(' ')
|
|
writeValue(w, v)
|
|
} else if isSlice(v) {
|
|
w.WriteByte('\n')
|
|
w.IndentIncrease()
|
|
w.Indent()
|
|
writeValue(w, v)
|
|
w.IndentDecrease()
|
|
} else {
|
|
w.depth++
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
writeValue(w, v)
|
|
w.depth--
|
|
}
|
|
}
|
|
|
|
//
|
|
// sequence writer
|
|
//
|
|
|
|
type indexWriter struct {
|
|
writer
|
|
index int
|
|
zero bool
|
|
}
|
|
|
|
func (w *indexWriter) IncludeZero() {
|
|
w.zero = true
|
|
}
|
|
|
|
func (w *indexWriter) ExcludeZero() {
|
|
w.zero = false
|
|
}
|
|
|
|
func (w *indexWriter) WriteTag(v interface{}) {
|
|
w.WriteByte('\n')
|
|
if w.index == 0 {
|
|
w.IndentDecrease()
|
|
w.Indent()
|
|
w.IndentIncrease()
|
|
w.WriteByte('-')
|
|
w.WriteByte(' ')
|
|
} else {
|
|
w.Indent()
|
|
}
|
|
writeValue(w, v)
|
|
w.WriteByte(':')
|
|
w.index++
|
|
}
|
|
|
|
func (w *indexWriter) WriteTagValue(k, v interface{}) {
|
|
if isZero(v) && !w.zero {
|
|
return
|
|
}
|
|
w.WriteTag(k)
|
|
if isPrimative(v) {
|
|
w.WriteByte(' ')
|
|
writeValue(w, v)
|
|
} else if isSlice(v) {
|
|
w.WriteByte('\n')
|
|
w.IndentIncrease()
|
|
w.Indent()
|
|
writeValue(w, v)
|
|
w.IndentDecrease()
|
|
} else {
|
|
w.IndentIncrease()
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
writeValue(w, v)
|
|
w.IndentDecrease()
|
|
}
|
|
}
|
|
|
|
//
|
|
// helper functions
|
|
//
|
|
|
|
func writeBool(w writer, v bool) {
|
|
w.WriteString(
|
|
strconv.FormatBool(v),
|
|
)
|
|
}
|
|
|
|
func writeFloat(w writer, v float64) {
|
|
w.WriteString(
|
|
strconv.FormatFloat(v, 'g', -1, 64),
|
|
)
|
|
}
|
|
|
|
func writeInt(w writer, v int) {
|
|
w.WriteString(
|
|
strconv.Itoa(v),
|
|
)
|
|
}
|
|
|
|
func writeInt64(w writer, v int64) {
|
|
w.WriteString(
|
|
strconv.FormatInt(v, 10),
|
|
)
|
|
}
|
|
|
|
func writeEncode(w writer, v string) {
|
|
if len(v) == 0 {
|
|
w.WriteByte('"')
|
|
w.WriteByte('"')
|
|
return
|
|
}
|
|
if isQuoted(v) {
|
|
fmt.Fprintf(w, "%q", v)
|
|
} else {
|
|
w.WriteString(v)
|
|
}
|
|
}
|
|
|
|
func writeValue(w writer, v interface{}) {
|
|
if v == nil {
|
|
w.WriteByte('~')
|
|
return
|
|
}
|
|
switch v := v.(type) {
|
|
case bool, int, int64, float64, string:
|
|
writeScalar(w, v)
|
|
case []interface{}:
|
|
writeSequence(w, v)
|
|
case []string:
|
|
writeSequenceStr(w, v)
|
|
case map[interface{}]interface{}:
|
|
writeMapping(w, v)
|
|
case map[string]string:
|
|
writeMappingStr(w, v)
|
|
case yaml.BytesSize:
|
|
writeValue(w, v.String())
|
|
}
|
|
}
|
|
|
|
func writeScalar(w writer, v interface{}) {
|
|
switch v := v.(type) {
|
|
case bool:
|
|
writeBool(w, v)
|
|
case int:
|
|
writeInt(w, v)
|
|
case int64:
|
|
writeInt64(w, v)
|
|
case float64:
|
|
writeFloat(w, v)
|
|
case string:
|
|
writeEncode(w, v)
|
|
}
|
|
}
|
|
|
|
func writeSequence(w writer, v []interface{}) {
|
|
if len(v) == 0 {
|
|
w.WriteByte('[')
|
|
w.WriteByte(']')
|
|
return
|
|
}
|
|
for i, v := range v {
|
|
if i != 0 {
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
}
|
|
w.WriteByte('-')
|
|
w.WriteByte(' ')
|
|
w.IndentIncrease()
|
|
writeValue(w, v)
|
|
w.IndentDecrease()
|
|
}
|
|
}
|
|
|
|
func writeSequenceStr(w writer, v []string) {
|
|
if len(v) == 0 {
|
|
w.WriteByte('[')
|
|
w.WriteByte(']')
|
|
return
|
|
}
|
|
for i, v := range v {
|
|
if i != 0 {
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
}
|
|
w.WriteByte('-')
|
|
w.WriteByte(' ')
|
|
writeEncode(w, v)
|
|
}
|
|
}
|
|
|
|
func writeMapping(w writer, v map[interface{}]interface{}) {
|
|
if len(v) == 0 {
|
|
w.WriteByte('{')
|
|
w.WriteByte('}')
|
|
return
|
|
}
|
|
var keys []string
|
|
for k := range v {
|
|
s := fmt.Sprint(k)
|
|
keys = append(keys, s)
|
|
}
|
|
sort.Strings(keys)
|
|
for i, k := range keys {
|
|
v := v[k]
|
|
if i != 0 {
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
}
|
|
writeEncode(w, k)
|
|
w.WriteByte(':')
|
|
if v == nil || isPrimative(v) || isZero(v) {
|
|
w.WriteByte(' ')
|
|
writeValue(w, v)
|
|
} else {
|
|
slice := isSlice(v)
|
|
if !slice {
|
|
w.IndentIncrease()
|
|
}
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
writeValue(w, v)
|
|
if !slice {
|
|
w.IndentDecrease()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func writeMappingStr(w writer, v map[string]string) {
|
|
if len(v) == 0 {
|
|
w.WriteByte('{')
|
|
w.WriteByte('}')
|
|
return
|
|
}
|
|
var keys []string
|
|
for k := range v {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
for i, k := range keys {
|
|
v := v[k]
|
|
if i != 0 {
|
|
w.WriteByte('\n')
|
|
w.Indent()
|
|
}
|
|
writeEncode(w, k)
|
|
w.WriteByte(':')
|
|
w.WriteByte(' ')
|
|
writeEncode(w, v)
|
|
}
|
|
}
|