134 lines
3.3 KiB
Go
134 lines
3.3 KiB
Go
// Package burntsushi translates a TOML AST into a JSON format, for BurntSushi's testing tool (https://github.com/BurntSushi/toml-test)
|
|
package burntsushi
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.makaay.nl/mauricem/go-toml/ast"
|
|
)
|
|
|
|
// PrintJSON outputs the JSON string for an ast Table to STDOUT.
|
|
func PrintJSON(t ast.Table) {
|
|
w := bufio.NewWriter(os.Stdout)
|
|
writeSushi(w, ast.NewValue(ast.TypeTable, t))
|
|
w.Flush()
|
|
}
|
|
|
|
// ToJSON generates the JSON string for an ast Table.
|
|
func ToJSON(t ast.Table) string {
|
|
sb := &strings.Builder{}
|
|
w := bufio.NewWriter(sb)
|
|
writeSushi(w, ast.NewValue(ast.TypeTable, t))
|
|
w.Flush()
|
|
return sb.String()
|
|
}
|
|
|
|
func writeSushi(w *bufio.Writer, value *ast.Value) {
|
|
switch value.Type {
|
|
case ast.TypeString:
|
|
w.WriteString(`{"type": "string", "value":"`)
|
|
for _, c := range value.Data[0].(string) {
|
|
switch c {
|
|
default:
|
|
w.WriteRune(c)
|
|
case '"':
|
|
w.WriteString(`\"`)
|
|
case '\\':
|
|
w.WriteString(`\\`)
|
|
case '\b':
|
|
w.WriteString(`\b`)
|
|
case '\f':
|
|
w.WriteString(`\f`)
|
|
case '\n':
|
|
w.WriteString(`\n`)
|
|
case '\r':
|
|
w.WriteString(`\r`)
|
|
case '\t':
|
|
w.WriteString(`\t`)
|
|
case '\u0000':
|
|
w.WriteString(`\u0000`)
|
|
}
|
|
}
|
|
w.WriteString(`"}`)
|
|
case ast.TypeInteger:
|
|
renderValue(w, "integer", fmt.Sprintf("%d", value.Data[0].(int64)))
|
|
case ast.TypeFloat:
|
|
renderValue(w, "float", fmt.Sprintf("%v", value.Data[0].(float64)))
|
|
case ast.TypeBool:
|
|
if value.Data[0].(bool) {
|
|
renderValue(w, "bool", "true")
|
|
} else {
|
|
renderValue(w, "bool", "false")
|
|
}
|
|
case ast.TypeOffsetDateTime:
|
|
renderValue(w, "datetime", value.Data[0].(time.Time).Format(time.RFC3339Nano))
|
|
case ast.TypeLocalDateTime:
|
|
renderValue(w, "local_datetime", value.Data[0].(time.Time).Format("2006-01-02 15:04:05.999999999"))
|
|
case ast.TypeLocalDate:
|
|
renderValue(w, "local_date", value.Data[0].(time.Time).Format("2006-01-02"))
|
|
case ast.TypeLocalTime:
|
|
renderValue(w, "local_time", value.Data[0].(time.Time).Format("15:04:05.999999999"))
|
|
case ast.TypeArrayOfTables:
|
|
fallthrough
|
|
case ast.TypeArray:
|
|
// BurntSushi's tests sees [ {inline: "table"}, {array: "definitions"} ]
|
|
// as an array of tables, so here we accomodate for that situation
|
|
// by checking for that case and render such inline array definition
|
|
// as if it were an [[array.of.tables]].
|
|
arr := value.Data[0].(*ast.Array)
|
|
|
|
if arr.ItemType == ast.TypeTable {
|
|
w.WriteByte('[')
|
|
} else {
|
|
w.WriteString(`{"type": "array", "value": [`)
|
|
}
|
|
for i := arr.First; i != nil; i = i.Next {
|
|
if i != arr.First {
|
|
w.WriteString(", ")
|
|
}
|
|
writeSushi(w, i.Value)
|
|
}
|
|
if arr.ItemType == ast.TypeTable {
|
|
w.WriteByte(']')
|
|
} else {
|
|
w.WriteString(`]}`)
|
|
}
|
|
case ast.TypeImplicitTable:
|
|
fallthrough
|
|
case ast.TypeTable:
|
|
pairs := value.Data[0].(ast.Table)
|
|
keys := make([]string, len(pairs))
|
|
i := 0
|
|
for k := range pairs {
|
|
keys[i] = k
|
|
i++
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
w.WriteByte('{')
|
|
for i, k := range keys {
|
|
if i > 0 {
|
|
w.WriteByte(',')
|
|
}
|
|
w.WriteString(fmt.Sprintf("%q: ", k))
|
|
writeSushi(w, pairs[k])
|
|
}
|
|
w.WriteByte('}')
|
|
default:
|
|
panic(fmt.Sprintf("Unhandled data type: %s", value.Type))
|
|
}
|
|
}
|
|
|
|
func renderValue(w *bufio.Writer, t string, v string) {
|
|
w.WriteString(`{"type":"`)
|
|
w.WriteString(t)
|
|
w.WriteString(`", "value":"`)
|
|
w.WriteString(v)
|
|
w.WriteString(`"}`)
|
|
}
|