package ast import ( "encoding/json" "fmt" "sort" "strings" "time" ) // MakeSushi generates a JSON string for an ast Table, which is compatible // with BurntSushi's TOML testing tool (https://github.com/BurntSushi/toml-test) func (t Table) MakeSushi() string { return MakeSushi(NewValue(TypeTable, t)) } // MakeSushi generates a JSON string for an ast Value, which is compatible // with BurntSushi's TOML testing tool (https://github.com/BurntSushi/toml-test) func MakeSushi(value *Value) string { switch value.Type { case TypeString: return renderValue("string", value.Data[0].(string)) case TypeInteger: return renderValue("integer", fmt.Sprintf("%d", value.Data[0].(int64))) case TypeFloat: return renderValue("float", fmt.Sprintf("%v", value.Data[0].(float64))) case TypeBool: return renderValue("bool", fmt.Sprintf("%t", value.Data[0].(bool))) case TypeOffsetDateTime: return renderValue("datetime", value.Data[0].(time.Time).Format(time.RFC3339Nano)) case TypeLocalDateTime: return renderValue("local_datetime", value.Data[0].(time.Time).Format("2006-01-02 15:04:05.999999999")) case TypeLocalDate: return renderValue("local_date", value.Data[0].(time.Time).Format("2006-01-02")) case TypeLocalTime: return renderValue("local_time", value.Data[0].(time.Time).Format("15:04:05.999999999")) case TypeArrayOfTables: fallthrough case 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]]. values := make([]string, len(value.Data)) isArrayOfTables := false for i, value := range value.Data { isArrayOfTables = value.(*Value).Type == TypeTable values[i] = MakeSushi(value.(*Value)) } if isArrayOfTables { return fmt.Sprintf("[%s]", strings.Join(values, ", ")) } else { return fmt.Sprintf(`{"type": "array", "value": [%s]}`, strings.Join(values, ", ")) } case TypeImplicitTable: fallthrough case TypeTable: pairs := value.Data[0].(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: panic(fmt.Sprintf("Unhandled data type: %s", value.Type)) } } func renderValue(t string, v string) string { return fmt.Sprintf("{%q: %q, %q: %s}", "type", t, "value", toJSON(v)) } func toJSON(s string) string { j, err := json.Marshal(s) if err != nil { panic(fmt.Sprintf("unable to JSON encode %q: %s", s, err)) } return string(j) }