// 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) }