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
62 changes: 34 additions & 28 deletions pointer.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,18 @@ func (p *Pointer) Offset(document string) (int64, error) {

// "Constructor", parses the given string JSON pointer
func (p *Pointer) parse(jsonPointerString string) error {
var err error

if jsonPointerString != emptyPointer {
if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
err = errors.Join(ErrInvalidStart, ErrPointer)
} else {
referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...)
}
if jsonPointerString == emptyPointer {
return nil
}

if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
return errors.Join(ErrInvalidStart, ErrPointer)
}

return err
referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...)

return nil
}

func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) {
Expand Down Expand Up @@ -166,33 +166,36 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error
)
}

if nameProvider == nil {
nameProvider = jsonname.DefaultJSONNameProvider
}
l := len(p.referenceTokens)

// full document when empty
if len(p.referenceTokens) == 0 {
if l == 0 {
return nil
}

lastI := len(p.referenceTokens) - 1
for i, token := range p.referenceTokens {
isLastToken := i == lastI
decodedToken := Unescape(token)
if nameProvider == nil {
nameProvider = jsonname.DefaultJSONNameProvider
}

if isLastToken {
return setSingleImpl(node, data, decodedToken, nameProvider)
}
var decodedToken string
lastIndex := l - 1

next, err := p.resolveNodeForToken(node, decodedToken, nameProvider)
if err != nil {
return err
}
if lastIndex > 0 { // skip if we only have one token in pointer
for _, token := range p.referenceTokens[:lastIndex] {
decodedToken = Unescape(token)
next, err := p.resolveNodeForToken(node, decodedToken, nameProvider)
if err != nil {
return err
}

node = next
node = next
}
}

return nil
// last token
decodedToken = Unescape(p.referenceTokens[lastIndex])

return setSingleImpl(node, data, decodedToken, nameProvider)
}

func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider *jsonname.NameProvider) (next any, err error) {
Expand Down Expand Up @@ -420,6 +423,7 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) {
return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer)
}
}

return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer)
}

Expand Down Expand Up @@ -452,6 +456,7 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) {
if !dec.More() {
return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer)
}

return dec.InputOffset(), nil
}

Expand All @@ -477,10 +482,11 @@ func drainSingle(dec *json.Decoder) error {
}
}

// Consumes the ending delim
// consumes the ending delim
if _, err := dec.Token(); err != nil {
return err
}

return nil
}

Expand Down
46 changes: 45 additions & 1 deletion pointer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,27 @@ func TestFullDocument(t *testing.T) {
asMap, ok := result.(map[string]any)
require.True(t, ok)
require.Lenf(t, asMap, testDocumentNBItems(), "Get(%v) = %v, expect full document", in, result)

t.Run("should set value in doc, with nil name provider", func(t *testing.T) {
setter, err := New("/foo/0")
require.NoErrorf(t, err, "New(%v) error %v", in, err)

const value = "hey"
require.NoError(t, setter.set(asMap, value, nil))

foos, ok := asMap["foo"]
require.True(t, ok)

asArray, ok := foos.([]any)
require.True(t, ok)
require.Len(t, asArray, 2)

foo := asArray[0]
bar, ok := foo.(string)
require.True(t, ok)

require.Equal(t, value, bar)
})
})
})
}
Expand Down Expand Up @@ -367,7 +388,7 @@ func TestOtherThings(t *testing.T) {
require.Error(t, err)
})

t.Run("resolving pointer against an unsupport type (int) should error", func(t *testing.T) {
t.Run("resolving pointer against an unsupported type (int) should error", func(t *testing.T) {
p, err := New("/invalid")
require.NoError(t, err)
_, _, err = p.Get(1234)
Expand Down Expand Up @@ -861,3 +882,26 @@ func TestOffset(t *testing.T) {
})
}
}

func TestEdgeCases(t *testing.T) {
t.Parallel()

t.Run("set at pointer against an unsupported type (int) should error", func(t *testing.T) {
p, err := New("/invalid")
require.NoError(t, err)
_, err = p.Set(1, 1234)
require.Error(t, err)
require.ErrorIs(t, err, ErrUnsupportedValueType)
})

t.Run("set with empty pointer", func(t *testing.T) {
p, err := New("")
require.NoError(t, err)

doc := testDocumentJSON(t)
newDoc, err := p.Set(doc, 1)
require.NoError(t, err)

require.Equal(t, doc, newDoc)
})
}
Loading