package main import ( "bytes" "encoding/json" "fmt" "io" ) // Ordered JSON: a minimal document model that keeps key order and unknown // fields, so the server can change a few values in milestones.json and write // the file back with a small, readable git diff. type kv struct { Key string Val any } // object is a JSON object with its keys in file order. type object []kv func (o *object) get(key string) (any, bool) { for _, p := range *o { if p.Key == key { return p.Val, true } } return nil, false } // set replaces a value in place, or appends the key at the end. func (o *object) set(key string, val any) { for i := range *o { if (*o)[i].Key == key { (*o)[i].Val = val return } } *o = append(*o, kv{key, val}) } func (o *object) getString(key string) string { v, _ := o.get(key) s, _ := v.(string) return s } func parseOrdered(data []byte) (any, error) { dec := json.NewDecoder(bytes.NewReader(data)) dec.UseNumber() v, err := readValue(dec) if err != nil { return nil, err } if _, err := dec.Token(); err != io.EOF { return nil, fmt.Errorf("unexpected data after JSON value") } return v, nil } func readValue(dec *json.Decoder) (any, error) { tok, err := dec.Token() if err != nil { return nil, err } switch t := tok.(type) { case json.Delim: switch t { case '{': obj := &object{} for dec.More() { k, err := dec.Token() if err != nil { return nil, err } v, err := readValue(dec) if err != nil { return nil, err } *obj = append(*obj, kv{k.(string), v}) } _, err := dec.Token() // } return obj, err case '[': arr := []any{} for dec.More() { v, err := readValue(dec) if err != nil { return nil, err } arr = append(arr, v) } _, err := dec.Token() // ] return arr, err } return nil, fmt.Errorf("unexpected delimiter %v", t) default: return t, nil // string, json.Number, bool or nil } } // writeOrdered formats like the rest of the data files: two-space indent, // one item per line, and Thai text and <, >, & left unescaped. func writeOrdered(v any) ([]byte, error) { var b bytes.Buffer if err := writeValue(&b, v, 0); err != nil { return nil, err } b.WriteByte('\n') return b.Bytes(), nil } func writeValue(b *bytes.Buffer, v any, depth int) error { indent := func(d int) { b.WriteByte('\n') for range d { b.WriteString(" ") } } switch t := v.(type) { case *object: if len(*t) == 0 { b.WriteString("{}") return nil } b.WriteByte('{') for i, p := range *t { if i > 0 { b.WriteByte(',') } indent(depth + 1) writeScalar(b, p.Key) b.WriteString(": ") if err := writeValue(b, p.Val, depth+1); err != nil { return err } } indent(depth) b.WriteByte('}') case []any: if len(t) == 0 { b.WriteString("[]") return nil } b.WriteByte('[') for i, item := range t { if i > 0 { b.WriteByte(',') } indent(depth + 1) if err := writeValue(b, item, depth+1); err != nil { return err } } indent(depth) b.WriteByte(']') default: return writeScalar(b, t) } return nil } func writeScalar(b *bytes.Buffer, v any) error { var tmp bytes.Buffer enc := json.NewEncoder(&tmp) enc.SetEscapeHTML(false) if err := enc.Encode(v); err != nil { return err } b.Write(bytes.TrimRight(tmp.Bytes(), "\n")) return nil }