Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions jsonpointer/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ func Resolve(base any, pointer Pointer) (any, error) {
}

func findByLabel(base reflect.Value, jsonLabel string) (reflect.Value, bool) {
if resolver, ok := base.Interface().(LabelResolver); ok {
value, ok := resolver.ResolveJsonLabel(jsonLabel)
if ok {
return reflect.ValueOf(value).Elem(), true
}
return reflect.Value{}, false
}

for base.Kind() == reflect.Ptr || base.Kind() == reflect.Interface {
if base.IsNil() {
return reflect.Value{}, false
Expand Down Expand Up @@ -75,3 +83,10 @@ func findStructFieldByLabel(base reflect.Value, label string) (reflect.Value, bo
}
return reflect.Value{}, false
}

type LabelResolver interface {
// ResolveJsonLabel resolves the given JSON label to a value.
// If the label is not found, (nil, false) is returned.
// The returned value must be a pointer to the field.
ResolveJsonLabel(label string) (any, bool)
}
55 changes: 34 additions & 21 deletions thorlog/v3/kvlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,20 @@ type KeyValue struct {
Value string
}

type KeyValueList struct {
KvList []KeyValue
}
type KeyValueList []KeyValue

func (d KeyValueList) MarshalJSON() ([]byte, error) {
var builder strings.Builder
builder.WriteString("{")
for i, kv := range d.KvList {
for i, kv := range d {
if err := json.NewEncoder(&builder).Encode(kv.Key); err != nil {
return nil, err
}
builder.WriteString(": ")
if err := json.NewEncoder(&builder).Encode(kv.Value); err != nil {
return nil, err
}
if i < len(d.KvList)-1 {
if i < len(d)-1 {
builder.WriteString(", ")
}
}
Expand All @@ -48,16 +46,22 @@ func (d *KeyValueList) UnmarshalJSON(data []byte) error {
}
var kvList []KeyValue
for decoder.More() {
var key string
err = decoder.Decode(&key)
keyToken, err := decoder.Token()
if err != nil {
return err
}
var value string
err = decoder.Decode(&value)
key, isString := keyToken.(string)
if !isString {
return errors.New("expected string key")
}
valueToken, err := decoder.Token()
if err != nil {
return err
}
value, isString := valueToken.(string)
if !isString {
return errors.New("expected string value")
}
kvList = append(kvList, KeyValue{Key: key, Value: value})
}
token, err = decoder.Token()
Expand All @@ -67,18 +71,27 @@ func (d *KeyValueList) UnmarshalJSON(data []byte) error {
if delim, isDelim := token.(json.Delim); !isDelim || delim != '}' {
return errors.New("expected '}'")
}
d.KvList = kvList
*d = kvList
return nil
}

func (d KeyValueList) ResolveJsonLabel(label string) (any, bool) {
for i := range d {
if d[i].Key == label {
return &d[i].Value, true
}
}
return nil, false
}

func (d KeyValueList) RelativeJsonPointer(pointee any) jsonpointer.Pointer {
stringPointer, isStringPointer := pointee.(*string)
if !isStringPointer {
return nil
}
for i := range d.KvList {
if &d.KvList[i].Value == stringPointer {
return jsonpointer.New(d.KvList[i].Key)
for i := range d {
if &d[i].Value == stringPointer {
return jsonpointer.New(d[i].Key)
}
}
return nil
Expand All @@ -89,30 +102,30 @@ func (d KeyValueList) RelativeTextPointer(pointee any) (string, bool) {
if !isStringPointer {
return "", false
}
for i := range d.KvList {
if &d.KvList[i].Value == stringPointer {
return d.KvList[i].Key, true
for i := range d {
if &d[i].Value == stringPointer {
return d[i].Key, true
}
}
return "", false
}

func (d KeyValueList) Find(key string) *string {
for i := range d.KvList {
if d.KvList[i].Key == key {
return &d.KvList[i].Value
for i := range d {
if d[i].Key == key {
return &d[i].Value
}
}
return nil
}

func (d KeyValueList) String() string {
var dataBuilder strings.Builder
for i, kv := range d.KvList {
for i, kv := range d {
dataBuilder.WriteString(kv.Key)
dataBuilder.WriteString(": ")
dataBuilder.WriteString(kv.Value)
if i < len(d.KvList)-1 {
if i < len(d)-1 {
dataBuilder.WriteString(" ")
}
}
Expand Down
47 changes: 47 additions & 0 deletions thorlog/v3/kvlist_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package thorlog

import (
"encoding/json"
"testing"

"github.com/NextronSystems/jsonlog"
"github.com/NextronSystems/jsonlog/jsonpointer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestKeyValueList_MarshalJSON(t *testing.T) {
var kvList = KeyValueList{
{Key: "key1", Value: "value1"},
{Key: "key2", Value: "value2"},
}
data, err := json.Marshal(kvList)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(data) != `{"key1":"value1","key2":"value2"}` {
t.Errorf("unexpected JSON: %s", data)
}
var unmarshaled KeyValueList
err = json.Unmarshal(data, &unmarshaled)
if err != nil {
t.Fatalf("unexpected error during unmarshal: %v", err)
}
if unmarshaled[0].Key != "key1" || unmarshaled[0].Value != "value1" || unmarshaled[1].Key != "key2" || unmarshaled[1].Value != "value2" {
t.Errorf("unexpected unmarshaled data: %+v", unmarshaled)
}
}

func TestKeyValueList_JsonPointers(t *testing.T) {
var kvList = KeyValueList{
{Key: "key1", Value: "value1"},
{Key: "key2", Value: "value2"},
}
reference := jsonlog.Reference{Base: &kvList, PointedField: &kvList[1].Value}
pointer := reference.ToJsonPointer()
assert.Equal(t, "/key2", pointer.String())

reverse, err := jsonpointer.Resolve(&kvList, pointer)
require.NoError(t, err)
assert.Equal(t, &kvList[1].Value, reverse)
}