orderedkv: initial commit

This commit is contained in:
mappu 2024-06-30 13:11:54 +12:00
parent 4735c391bd
commit 8d051a14e5
2 changed files with 115 additions and 0 deletions

View File

@ -0,0 +1,85 @@
package sqliteclidriver
import (
"bytes"
"encoding/json"
"fmt"
)
type Pair struct {
Key string
Value any
}
type OrderedKV []Pair
func (o *OrderedKV) UnmarshalJSON(data []byte) error {
// Rough estimate malloc size based on number of `:`
// This is a lower bound since there might be nested elements
var elCount int64
for _, c := range data {
if c == ':' {
elCount += 1
}
}
*o = make([]Pair, 0, elCount)
// Parse the initial opening { delimiter from the JSON stream
reader := bytes.NewReader(data)
dec := json.NewDecoder(reader)
tok, err := dec.Token()
if err != nil {
return fmt.Errorf("expected '{': %w", err)
}
if d, ok := tok.(json.Delim); !ok || (rune(d) != '{') {
return fmt.Errorf("expected '{', got %v", tok)
}
// Read remaining content
for dec.More() {
var p Pair
// Parse key: either string or Delim('}')
tok, err := dec.Token()
if err != nil {
return fmt.Errorf("expected '{': %w", err)
}
switch tok := tok.(type) {
case json.Delim:
if rune(tok) == '}' {
// Finished
break
}
// Something else
return fmt.Errorf("expected string or }, got %v", tok)
case string:
// Valid key
p.Key = tok
default:
return fmt.Errorf("expected string or }, got %v", tok)
}
// Parse value (any)
err = dec.Decode(&p.Value)
if err != nil {
return err
}
*o = append(*o, p)
}
// Assert that there is no remaining content
if reader.Len() != 0 {
return fmt.Errorf("Unexpected trailing data (%d bytes remaining)", reader.Len())
}
// Done
return nil
}

View File

@ -0,0 +1,30 @@
package sqliteclidriver
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func TestOrderedKV(t *testing.T) {
input := `
{
"zzz": "foo",
"aaa": "bar"
}
`
var got OrderedKV
err := json.Unmarshal([]byte(input), &got)
if err != nil {
t.Fatal(err)
}
expect := OrderedKV{
Pair{Key: "zzz", Value: "foo"},
Pair{Key: "aaa", Value: "bar"},
}
require.EqualValues(t, expect, got)
}