go-toml/cmd/toml-test-decoder/main.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)
}