orderedkv: initial commit
This commit is contained in:
parent
4735c391bd
commit
8d051a14e5
85
sqliteclidriver/orderedkv.go
Normal file
85
sqliteclidriver/orderedkv.go
Normal 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
|
||||
}
|
30
sqliteclidriver/orderedkv_test.go
Normal file
30
sqliteclidriver/orderedkv_test.go
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user