162 lines
4.3 KiB
Go
162 lines
4.3 KiB
Go
// Command toml-test-decoder satisfies BurntSushi's toml-test interface for testing
|
|
// TOML decoders. Namely, it accepts TOML on stdin and outputs JSON on stdout.
|
|
// See: https://github.com/BurntSushi/toml-test
|
|
package main
|
|
|
|
import (
|
|
//"encoding/json"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
//"fmt"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
|
|
//"time"
|
|
|
|
"git.makaay.nl/mauricem/go-toml/ast"
|
|
"git.makaay.nl/mauricem/go-toml/parse"
|
|
)
|
|
|
|
func init() {
|
|
log.SetFlags(0)
|
|
|
|
flag.Usage = usage
|
|
flag.Parse()
|
|
}
|
|
|
|
func usage() {
|
|
log.Printf("Usage: %s < toml-file\n", path.Base(os.Args[0]))
|
|
flag.PrintDefaults()
|
|
|
|
os.Exit(1)
|
|
}
|
|
|
|
func main() {
|
|
if flag.NArg() != 0 {
|
|
flag.Usage()
|
|
}
|
|
|
|
toml, err := parse.Run(os.Stdin)
|
|
if err != nil {
|
|
log.Fatalf("Error decoding TOML: %s", err)
|
|
}
|
|
|
|
sushi := makeSushi(ast.NewValue(ast.TypeTable, toml))
|
|
var v = new(interface{})
|
|
if err := json.NewDecoder(strings.NewReader(sushi)).Decode(v); err != nil {
|
|
log.Fatalf("Error decoding JSON: %s\n%s\n", err, sushi)
|
|
}
|
|
encoder := json.NewEncoder(os.Stdout)
|
|
encoder.SetIndent("", " ")
|
|
if err := encoder.Encode(v); err != nil {
|
|
log.Fatalf("Error encoding JSON: %s", err)
|
|
}
|
|
}
|
|
|
|
// func translate(node *ast.Value) interface{} {
|
|
// switch node.Type {
|
|
// case ast.TypeTable:
|
|
// typed := make(map[string]interface{}, len(node.Data))
|
|
// for k, v := range node.Data[0].(ast.Table) {
|
|
// typed[k] = translate(v)
|
|
// }
|
|
// return typed
|
|
// case ast.TypeArrayOfTables:
|
|
// typed := make([]map[string]interface{}, len(node.Data))
|
|
// for i, v := range node.Data {
|
|
// value := v.(*ast.Value)
|
|
// typed[i] = translate(value).(map[string]interface{})
|
|
// }
|
|
// return typed
|
|
// case []interface{}:
|
|
// typed := make([]interface{}, len(orig))
|
|
// for i, v := range orig {
|
|
// typed[i] = translate(v)
|
|
// }
|
|
|
|
// // We don't really need to tag arrays, but let's be future proof.
|
|
// // (If TOML ever supports tuples, we'll need this.)
|
|
// return tag("array", typed)
|
|
// case time.Time:
|
|
// return tag("datetime", orig.Format("2006-01-02T15:04:05Z"))
|
|
// case bool:
|
|
// return tag("bool", fmt.Sprintf("%v", orig))
|
|
// case int64:
|
|
// return tag("integer", fmt.Sprintf("%d", orig))
|
|
// case float64:
|
|
// return tag("float", fmt.Sprintf("%v", orig))
|
|
// case string:
|
|
// return tag("string", orig)
|
|
// }
|
|
|
|
// panic(fmt.Sprintf("Unknown type: %T", tomlData))
|
|
// }
|
|
|
|
// func tag(typeName string, data interface{}) map[string]interface{} {
|
|
// return map[string]interface{}{
|
|
// "type": typeName,
|
|
// "value": data,
|
|
// }
|
|
// }
|
|
|
|
func makeSushi(value *ast.Value) string {
|
|
switch value.Type {
|
|
case ast.TypeString:
|
|
return renderValue("string", value.Data[0].(string))
|
|
case ast.TypeInteger:
|
|
return renderValue("integer", fmt.Sprintf("%d", value.Data[0].(int64)))
|
|
case ast.TypeFloat:
|
|
return renderValue("float", fmt.Sprintf("%v", value.Data[0].(float64)))
|
|
case ast.TypeBoolean:
|
|
return renderValue("bool", fmt.Sprintf("%t", value.Data[0].(bool)))
|
|
case ast.TypeOffsetDateTime:
|
|
return renderValue("datetime", value.Data[0].(time.Time).Format(time.RFC3339Nano))
|
|
case ast.TypeLocalDateTime:
|
|
return renderValue("local_datetime", value.Data[0].(time.Time).Format("2006-01-02 15:04:05.999999999"))
|
|
case ast.TypeLocalDate:
|
|
return renderValue("local_date", value.Data[0].(time.Time).Format("2006-01-02"))
|
|
case ast.TypeLocalTime:
|
|
return renderValue("local_time", value.Data[0].(time.Time).Format("15:04:05.999999999"))
|
|
case ast.TypeArrayOfTables:
|
|
fallthrough
|
|
case ast.TypeArray:
|
|
values := make([]string, len(value.Data))
|
|
isArrayOfTables := false
|
|
for i, value := range value.Data {
|
|
isArrayOfTables = value.(*ast.Value).Type == ast.TypeTable
|
|
values[i] = makeSushi(value.(*ast.Value))
|
|
}
|
|
if isArrayOfTables {
|
|
return fmt.Sprintf("[%s]", strings.Join(values, ", "))
|
|
} else {
|
|
return fmt.Sprintf(`{"type": "array", "value": [%s]}`, strings.Join(values, ", "))
|
|
}
|
|
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)
|
|
values := make([]string, len(pairs))
|
|
for i, k := range keys {
|
|
values[i] = fmt.Sprintf("%q: %s", k, makeSushi(pairs[k]))
|
|
}
|
|
return fmt.Sprintf("{%s}", strings.Join(values, ", "))
|
|
default:
|
|
return renderValue(string(value.Type), fmt.Sprintf("%q", value.Data[0]))
|
|
}
|
|
}
|
|
|
|
func renderValue(t string, v string) string {
|
|
return fmt.Sprintf("{%q: %q, %q: %q}", "type", t, "value", v)
|
|
}
|