// Command interpres-decode reads a TOML document from standard input and writes // the toml-test "tagged JSON" representation to standard output. // // It exits non-zero on a parse error, which is how the toml-test harness checks // that invalid documents are rejected. Run the official suite against it with: // // toml-test ./interpres-decode package main import ( "encoding/json" "fmt" "io" "math" "os" "strconv" "time" "codeberg.org/petrbalvin/interpres" ) func main() { data, err := io.ReadAll(os.Stdin) if err != nil { fmt.Fprintln(os.Stderr, "read stdin:", err) os.Exit(2) } tree, err := interpres.Parse(data) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } enc := json.NewEncoder(os.Stdout) enc.SetEscapeHTML(false) if err := enc.Encode(tag(tree)); err != nil { fmt.Fprintln(os.Stderr, "encode:", err) os.Exit(2) } } // tag converts an interpres value into its toml-test tagged-JSON form. Tables // become JSON objects and arrays become JSON arrays; scalars are wrapped in a // {"type", "value"} object. func tag(v any) any { switch x := v.(type) { case map[string]any: out := make(map[string]any, len(x)) for k, val := range x { out[k] = tag(val) } return out case []any: out := make([]any, len(x)) for i, e := range x { out[i] = tag(e) } return out case []map[string]any: out := make([]any, len(x)) for i, e := range x { out[i] = tag(e) } return out case string: return tagged("string", x) case bool: return tagged("bool", strconv.FormatBool(x)) case int64: return tagged("integer", strconv.FormatInt(x, 10)) case float64: return tagged("float", formatFloat(x)) case time.Time: return tagged("datetime", x.Format(time.RFC3339Nano)) case interpres.LocalDateTime: return tagged("datetime-local", x.Format("2006-01-02T15:04:05.999999999")) case interpres.LocalDate: return tagged("date-local", x.Format("2006-01-02")) case interpres.LocalTime: return tagged("time-local", x.Format("15:04:05.999999999")) default: fmt.Fprintf(os.Stderr, "unsupported value type %T\n", v) os.Exit(2) return nil } } func tagged(typ, value string) map[string]string { return map[string]string{"type": typ, "value": value} } func formatFloat(f float64) string { switch { case math.IsInf(f, 1): return "inf" case math.IsInf(f, -1): return "-inf" case math.IsNaN(f): return "nan" default: return strconv.FormatFloat(f, 'g', -1, 64) } }