vendor: update buildkit to v0.31.0-rc2
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
-181
@@ -1,181 +0,0 @@
|
||||
# git-cliff ~ configuration file
|
||||
# https://git-cliff.org/docs/configuration
|
||||
|
||||
[changelog]
|
||||
header = """
|
||||
"""
|
||||
|
||||
footer = """
|
||||
|
||||
-----
|
||||
|
||||
**[{{ remote.github.repo }}]({{ self::remote_url() }}) license terms**
|
||||
|
||||
[![License][license-badge]][license-url]
|
||||
|
||||
[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg
|
||||
[license-url]: {{ self::remote_url() }}/?tab=Apache-2.0-1-ov-file#readme
|
||||
|
||||
{%- macro remote_url() -%}
|
||||
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
|
||||
{%- endmacro -%}
|
||||
"""
|
||||
|
||||
body = """
|
||||
{%- if version %}
|
||||
## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||
{%- else %}
|
||||
## [unreleased]
|
||||
{%- endif %}
|
||||
{%- if message %}
|
||||
{%- raw %}\n{% endraw %}
|
||||
{{ message }}
|
||||
{%- raw %}\n{% endraw %}
|
||||
{%- endif %}
|
||||
{%- if version %}
|
||||
{%- if previous.version %}
|
||||
|
||||
**Full Changelog**: <{{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}>
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
{%- raw %}\n{% endraw %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if statistics %}{% if statistics.commit_count %}
|
||||
{%- raw %}\n{% endraw %}
|
||||
{{ statistics.commit_count }} commits in this release.
|
||||
{%- raw %}\n{% endraw %}
|
||||
{%- endif %}{% endif %}
|
||||
-----
|
||||
|
||||
{%- for group, commits in commits | group_by(attribute="group") %}
|
||||
{%- raw %}\n{% endraw %}
|
||||
### {{ group | upper_first }}
|
||||
{%- raw %}\n{% endraw %}
|
||||
{%- for commit in commits %}
|
||||
{%- if commit.remote.pr_title %}
|
||||
{%- set commit_message = commit.remote.pr_title %}
|
||||
{%- else %}
|
||||
{%- set commit_message = commit.message %}
|
||||
{%- endif %}
|
||||
* {{ commit_message | split(pat="\n") | first | trim }}
|
||||
{%- if commit.remote.username %}
|
||||
{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }})
|
||||
{%- endif %}
|
||||
{%- if commit.remote.pr_number %}
|
||||
{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }})
|
||||
{%- endif %}
|
||||
{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }})
|
||||
{%- endfor %}
|
||||
{%- endfor %}
|
||||
|
||||
{%- if github %}
|
||||
{%- raw %}\n{% endraw -%}
|
||||
{%- set all_contributors = github.contributors | length %}
|
||||
{%- if github.contributors | filter(attribute="username", value="dependabot[bot]") | length < all_contributors %}
|
||||
-----
|
||||
|
||||
### People who contributed to this release
|
||||
{% endif %}
|
||||
{%- for contributor in github.contributors | filter(attribute="username") | sort(attribute="username") %}
|
||||
{%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %}
|
||||
* [@{{ contributor.username }}](https://github.com/{{ contributor.username }})
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
|
||||
-----
|
||||
{%- raw %}\n{% endraw %}
|
||||
|
||||
### New Contributors
|
||||
{%- endif %}
|
||||
|
||||
{%- for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
|
||||
{%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %}
|
||||
* @{{ contributor.username }} made their first contribution
|
||||
{%- if contributor.pr_number %}
|
||||
in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
{%- raw %}\n{% endraw %}
|
||||
|
||||
{%- macro remote_url() -%}
|
||||
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
|
||||
{%- endmacro -%}
|
||||
"""
|
||||
# Remove leading and trailing whitespaces from the changelog's body.
|
||||
trim = true
|
||||
# Render body even when there are no releases to process.
|
||||
render_always = true
|
||||
# An array of regex based postprocessors to modify the changelog.
|
||||
postprocessors = [
|
||||
# Replace the placeholder <REPO> with a URL.
|
||||
#{ pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" },
|
||||
]
|
||||
# output file path
|
||||
# output = "test.md"
|
||||
|
||||
[git]
|
||||
# Parse commits according to the conventional commits specification.
|
||||
# See https://www.conventionalcommits.org
|
||||
conventional_commits = false
|
||||
# Exclude commits that do not match the conventional commits specification.
|
||||
filter_unconventional = false
|
||||
# Require all commits to be conventional.
|
||||
# Takes precedence over filter_unconventional.
|
||||
require_conventional = false
|
||||
# Split commits on newlines, treating each line as an individual commit.
|
||||
split_commits = false
|
||||
# An array of regex based parsers to modify commit messages prior to further processing.
|
||||
commit_preprocessors = [
|
||||
# Replace issue numbers with link templates to be updated in `changelog.postprocessors`.
|
||||
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
|
||||
# Check spelling of the commit message using https://github.com/crate-ci/typos.
|
||||
# If the spelling is incorrect, it will be fixed automatically.
|
||||
#{ pattern = '.*', replace_command = 'typos --write-changes -' }
|
||||
]
|
||||
# Prevent commits that are breaking from being excluded by commit parsers.
|
||||
protect_breaking_commits = false
|
||||
# An array of regex based parsers for extracting data from the commit message.
|
||||
# Assigns commits to groups.
|
||||
# Optionally sets the commit's scope and can decide to exclude commits from further processing.
|
||||
commit_parsers = [
|
||||
{ message = "^[Cc]hore\\([Rr]elease\\): prepare for", skip = true },
|
||||
{ message = "(^[Mm]erge)|([Mm]erge conflict)", skip = true },
|
||||
{ field = "author.name", pattern = "dependabot*", group = "<!-- 0A -->Updates" },
|
||||
{ message = "([Ss]ecurity)|([Vv]uln)", group = "<!-- 08 -->Security" },
|
||||
{ body = "(.*[Ss]ecurity)|([Vv]uln)", group = "<!-- 08 -->Security" },
|
||||
{ message = "([Cc]hore\\(lint\\))|(style)|(lint)|(codeql)|(golangci)", group = "<!-- 05 -->Code quality" },
|
||||
{ message = "(^[Dd]oc)|((?i)readme)|(badge)|(typo)|(documentation)", group = "<!-- 03 -->Documentation" },
|
||||
{ message = "(^[Ff]eat)|(^[Ee]nhancement)", group = "<!-- 00 -->Implemented enhancements" },
|
||||
{ message = "(^ci)|(\\(ci\\))|(fixup\\s+ci)|(fix\\s+ci)|(license)|(example)", group = "<!-- 07 -->Miscellaneous tasks" },
|
||||
{ message = "^test", group = "<!-- 06 -->Testing" },
|
||||
{ message = "(^fix)|(panic)", group = "<!-- 01 -->Fixed bugs" },
|
||||
{ message = "(^refact)|(rework)", group = "<!-- 02 -->Refactor" },
|
||||
{ message = "(^[Pp]erf)|(performance)", group = "<!-- 04 -->Performance" },
|
||||
{ message = "(^[Cc]hore)", group = "<!-- 07 -->Miscellaneous tasks" },
|
||||
{ message = "^[Rr]evert", group = "<!-- 09 -->Reverted changes" },
|
||||
{ message = "(upgrade.*?go)|(go\\s+version)", group = "<!-- 0A -->Updates" },
|
||||
{ message = ".*", group = "<!-- 0B -->Other" },
|
||||
]
|
||||
# Exclude commits that are not matched by any commit parser.
|
||||
filter_commits = false
|
||||
# An array of link parsers for extracting external references, and turning them into URLs, using regex.
|
||||
link_parsers = []
|
||||
# Include only the tags that belong to the current branch.
|
||||
use_branch_tags = false
|
||||
# Order releases topologically instead of chronologically.
|
||||
topo_order = false
|
||||
# Order releases topologically instead of chronologically.
|
||||
topo_order_commits = true
|
||||
# Order of commits in each group/release within the changelog.
|
||||
# Allowed values: newest, oldest
|
||||
sort_commits = "newest"
|
||||
# Process submodules commits
|
||||
recurse_submodules = false
|
||||
|
||||
#[remote.github]
|
||||
#owner = "go-openapi"
|
||||
@@ -3,4 +3,3 @@
|
||||
.idea
|
||||
.env
|
||||
.mcp.json
|
||||
.claude/
|
||||
|
||||
+4
-3
@@ -4,11 +4,11 @@
|
||||
|
||||
| Total Contributors | Total Contributions |
|
||||
| --- | --- |
|
||||
| 12 | 101 |
|
||||
| 13 | 111 |
|
||||
|
||||
| Username | All Time Contribution Count | All Commits |
|
||||
| --- | --- | --- |
|
||||
| @fredbi | 54 | <https://github.com/go-openapi/jsonpointer/commits?author=fredbi> |
|
||||
| @fredbi | 63 | <https://github.com/go-openapi/jsonpointer/commits?author=fredbi> |
|
||||
| @casualjim | 33 | <https://github.com/go-openapi/jsonpointer/commits?author=casualjim> |
|
||||
| @magodo | 3 | <https://github.com/go-openapi/jsonpointer/commits?author=magodo> |
|
||||
| @youyuanwu | 3 | <https://github.com/go-openapi/jsonpointer/commits?author=youyuanwu> |
|
||||
@@ -18,7 +18,8 @@
|
||||
| @ianlancetaylor | 1 | <https://github.com/go-openapi/jsonpointer/commits?author=ianlancetaylor> |
|
||||
| @mfleader | 1 | <https://github.com/go-openapi/jsonpointer/commits?author=mfleader> |
|
||||
| @Neo2308 | 1 | <https://github.com/go-openapi/jsonpointer/commits?author=Neo2308> |
|
||||
| @alexandear | 1 | <https://github.com/go-openapi/jsonpointer/commits?author=alexandear> |
|
||||
| @olivierlemasle | 1 | <https://github.com/go-openapi/jsonpointer/commits?author=olivierlemasle> |
|
||||
| @testwill | 1 | <https://github.com/go-openapi/jsonpointer/commits?author=testwill> |
|
||||
|
||||
_this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_
|
||||
_this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ It ships with copies of other software which license terms are recalled below.
|
||||
|
||||
The original software was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com).
|
||||
|
||||
github.com/sigh-399/jsonpointer
|
||||
github.com/sigu-399/jsonpointer
|
||||
===========================
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
|
||||
|
||||
+31
-21
@@ -16,17 +16,25 @@ An implementation of JSON Pointer for golang, which supports go `struct`.
|
||||
|
||||
## Announcements
|
||||
|
||||
* **2025-12-19** : new community chat on discord
|
||||
* a new discord community channel is available to be notified of changes and support users
|
||||
* our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31**
|
||||
* **2026-04-15** : added support for trailing "-" for arrays (v0.23.0)
|
||||
* this brings full support of [RFC6901][RFC6901]
|
||||
* this is supported for types relying on the reflection-based implemented
|
||||
* API semantics remain essentially unaltered. Exception: `Pointer.Set(document any,value any) (document any, err error)`
|
||||
can only perform a best-effort to mutate the input document in place. In the case of adding elements to an array with a
|
||||
trailing "-", either pass a mutable array (`*[]T`) as the input document, or use the returned updated document instead.
|
||||
* types that implement the `JSONSetable` interface may not implement the mutation implied by the trailing "-"
|
||||
|
||||
You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url]
|
||||
|
||||
Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url]
|
||||
* **2026-04-15** : added support for optional alternate JSON name providers
|
||||
* for struct support the defaults might not suit all situations: there are known limitations
|
||||
when it comes to handle untagged fields or embedded types.
|
||||
* the default name provider in use is not fully aligned with go JSON stdlib
|
||||
* exposed an option (or global setting) to change the provider that resolves a struct into json keys
|
||||
* the default behavior is not altered
|
||||
* a new alternate name provider is added (imported from `go-openapi/swag/jsonname`), aligned with JSON stdlib behavior
|
||||
|
||||
## Status
|
||||
|
||||
API is stable.
|
||||
API is stable and feature-complete.
|
||||
|
||||
## Import this library in your project
|
||||
|
||||
@@ -88,7 +96,7 @@ See <https://github.com/go-openapi/jsonpointer/releases>
|
||||
|
||||
<https://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07>
|
||||
|
||||
also known as [RFC6901](https://www.rfc-editor.org/rfc/rfc6901)
|
||||
also known as [RFC6901][RFC6901].
|
||||
|
||||
## Licensing
|
||||
|
||||
@@ -99,19 +107,19 @@ on top of which it has been built.
|
||||
|
||||
## Limitations
|
||||
|
||||
The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array,
|
||||
the reference token MUST contain either...' is not implemented.
|
||||
|
||||
That is because our implementation of the JSON pointer only supports explicit references to array elements:
|
||||
the provision in the spec to resolve non-existent members as "the last element in the array",
|
||||
using the special trailing character "-" is not implemented.
|
||||
* [RFC6901][RFC6901] is now fully supported, including trailing "-" semantics for arrays (for `Set` operations).
|
||||
* Default behavior: JSON name detection in go `struct`s
|
||||
- Unlike go standard marshaling, untagged fields do not default to the go field name and are ignored.
|
||||
- anonymous fields are not traversed if untagged
|
||||
- the above limitations may be overcome by calling `UseGoNameProvider()` at initialization time.
|
||||
- alternatively, users may inject the desired custom behavior for naming fields as an option.
|
||||
|
||||
## Other documentation
|
||||
|
||||
* [All-time contributors](./CONTRIBUTORS.md)
|
||||
* [Contributing guidelines](.github/CONTRIBUTING.md)
|
||||
* [Maintainers documentation](docs/MAINTAINERS.md)
|
||||
* [Code style](docs/STYLE.md)
|
||||
* [Contributing guidelines][contributing-doc-site]
|
||||
* [Maintainers documentation][maintainers-doc-site]
|
||||
* [Code style][style-doc-site]
|
||||
|
||||
## Cutting a new release
|
||||
|
||||
@@ -142,11 +150,8 @@ Maintainers can cut a new release by either:
|
||||
<!-- Badges: documentation & support -->
|
||||
[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer
|
||||
[godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonpointer
|
||||
[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png
|
||||
[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM
|
||||
[slack-url]: https://goswagger.slack.com/archives/C04R30YMU
|
||||
[discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue
|
||||
[discord-url]: https://discord.gg/twZ9BwT3
|
||||
[discord-url]: https://discord.gg/FfnFYaC3k5
|
||||
|
||||
<!-- Badges: license & compliance -->
|
||||
[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg
|
||||
@@ -156,3 +161,8 @@ Maintainers can cut a new release by either:
|
||||
[goversion-url]: https://github.com/go-openapi/jsonpointer/blob/master/go.mod
|
||||
[top-badge]: https://img.shields.io/github/languages/top/go-openapi/jsonpointer
|
||||
[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/jsonpointer/latest
|
||||
[RFC6901]: https://www.rfc-editor.org/rfc/rfc6901
|
||||
<!-- Organization docs -->
|
||||
[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html
|
||||
[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html
|
||||
[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html
|
||||
|
||||
+25
-1
@@ -16,12 +16,24 @@ const (
|
||||
ErrPointer pointerError = "JSON pointer error"
|
||||
|
||||
// ErrInvalidStart states that a JSON pointer must start with a separator ("/").
|
||||
ErrInvalidStart pointerError = `JSON pointer must be empty or start with a "` + pointerSeparator
|
||||
ErrInvalidStart pointerError = `JSON pointer must be empty or start with a "` + pointerSeparator + `"`
|
||||
|
||||
// ErrUnsupportedValueType indicates that a value of the wrong type is being set.
|
||||
ErrUnsupportedValueType pointerError = "only structs, pointers, maps and slices are supported for setting values"
|
||||
|
||||
// ErrDashToken indicates use of the RFC 6901 "-" reference token
|
||||
// in a context where it cannot be resolved.
|
||||
//
|
||||
// Per RFC 6901 §4 the "-" token refers to the (nonexistent) element
|
||||
// after the last array element. It may only be used as the terminal
|
||||
// token of a [Pointer.Set] against a slice, where it means "append".
|
||||
// Any other use (get, offset, intermediate traversal, non-slice target)
|
||||
// is an error condition that wraps this sentinel.
|
||||
ErrDashToken pointerError = `the "-" array token cannot be resolved here` //nolint:gosec // G101 false positive: this is a JSON Pointer reference token, not a credential.
|
||||
)
|
||||
|
||||
const dashToken = "-"
|
||||
|
||||
func errNoKey(key string) error {
|
||||
return fmt.Errorf("object has no key %q: %w", key, ErrPointer)
|
||||
}
|
||||
@@ -33,3 +45,15 @@ func errOutOfBounds(length, idx int) error {
|
||||
func errInvalidReference(token string) error {
|
||||
return fmt.Errorf("invalid token reference %q: %w", token, ErrPointer)
|
||||
}
|
||||
|
||||
func errDashOnGet() error {
|
||||
return fmt.Errorf("cannot resolve %q token on get: %w: %w", dashToken, ErrDashToken, ErrPointer)
|
||||
}
|
||||
|
||||
func errDashIntermediate() error {
|
||||
return fmt.Errorf("the %q token may only appear as the terminal token of a pointer: %w: %w", dashToken, ErrDashToken, ErrPointer)
|
||||
}
|
||||
|
||||
func errDashOnOffset() error {
|
||||
return fmt.Errorf("cannot compute offset for %q token (nonexistent element): %w: %w", dashToken, ErrDashToken, ErrPointer)
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonpointer
|
||||
|
||||
import "reflect"
|
||||
|
||||
// JSONPointable is an interface for structs to implement,
|
||||
// when they need to customize the json pointer process or want to avoid the use of reflection.
|
||||
type JSONPointable interface {
|
||||
// JSONLookup returns a value pointed at this (unescaped) key.
|
||||
JSONLookup(key string) (any, error)
|
||||
}
|
||||
|
||||
// JSONSetable is an interface for structs to implement,
|
||||
// when they need to customize the json pointer process or want to avoid the use of reflection.
|
||||
//
|
||||
// # Handling of the RFC 6901 "-" token
|
||||
//
|
||||
// When a type implementing JSONSetable is the terminal parent of a [Pointer.Set]
|
||||
// call, the library passes the raw reference token to JSONSet without
|
||||
// interpretation. In particular, the RFC 6901 "-" token (which conventionally
|
||||
// means "append" for arrays, per RFC 6902) is forwarded verbatim as the key
|
||||
// argument. Implementations that model an array-like container are expected
|
||||
// to give "-" the append semantics; implementations that do not should return
|
||||
// an error wrapping [ErrDashToken] (or [ErrPointer]) for clarity.
|
||||
//
|
||||
// Implementations are responsible for any in-place mutation: the library does
|
||||
// not attempt to rebind the result of JSONSet into a parent container.
|
||||
type JSONSetable interface {
|
||||
// JSONSet sets the value pointed at the (unescaped) key.
|
||||
//
|
||||
// The key may be the RFC 6901 "-" token when the pointer targets a
|
||||
// slice-like member; see the interface documentation for details.
|
||||
JSONSet(key string, value any) error
|
||||
}
|
||||
|
||||
// NameProvider knows how to resolve go struct fields into json names.
|
||||
//
|
||||
// The default provider is brought by [github.com/go-openapi/swag/jsonname.DefaultJSONNameProvider].
|
||||
type NameProvider interface {
|
||||
// GetGoName gets the go name for a json property name
|
||||
GetGoName(subject any, name string) (string, bool)
|
||||
|
||||
// GetGoNameForType gets the go name for a given type for a json property name
|
||||
GetGoNameForType(tpe reflect.Type, name string) (string, bool)
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonpointer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/swag/jsonname"
|
||||
)
|
||||
|
||||
// Option to tune the behavior of a JSON [Pointer].
|
||||
type Option func(*options)
|
||||
|
||||
var (
|
||||
//nolint:gochecknoglobals // package level defaults are provided as a convenient, backward-compatible way to adopt options.
|
||||
defaultOptions = options{
|
||||
provider: jsonname.DefaultJSONNameProvider,
|
||||
}
|
||||
//nolint:gochecknoglobals // guards defaultOptions against concurrent SetDefaultNameProvider / read races (testing)
|
||||
defaultOptionsMu sync.RWMutex
|
||||
)
|
||||
|
||||
// SetDefaultNameProvider sets the [NameProvider] as a package-level default.
|
||||
//
|
||||
// By default, the default provider is [jsonname.DefaultJSONNameProvider].
|
||||
//
|
||||
// It is safe to call concurrently with [Pointer.Get], [Pointer.Set],
|
||||
// [GetForToken] and [SetForToken]. The typical usage is to call it once
|
||||
// at initialization time.
|
||||
//
|
||||
// A nil provider is ignored.
|
||||
func SetDefaultNameProvider(provider NameProvider) {
|
||||
if provider == nil {
|
||||
return
|
||||
}
|
||||
|
||||
defaultOptionsMu.Lock()
|
||||
defer defaultOptionsMu.Unlock()
|
||||
|
||||
defaultOptions.provider = provider
|
||||
}
|
||||
|
||||
// UseGoNameProvider sets the [NameProvider] as a package-level default
|
||||
// to the alternative provider [jsonname.GoNameProvider], that covers a few areas
|
||||
// not supported by the default name provider.
|
||||
//
|
||||
// This implementation supports untagged exported fields and embedded types in go struct.
|
||||
// It follows strictly the behavior of the JSON standard library regarding field naming conventions.
|
||||
//
|
||||
// It is safe to call concurrently with [Pointer.Get], [Pointer.Set],
|
||||
// [GetForToken] and [SetForToken]. The typical usage is to call it once
|
||||
// at initialization time.
|
||||
func UseGoNameProvider() {
|
||||
SetDefaultNameProvider(jsonname.NewGoNameProvider())
|
||||
}
|
||||
|
||||
// DefaultNameProvider returns the current package-level [NameProvider].
|
||||
func DefaultNameProvider() NameProvider { //nolint:ireturn // returning the interface is the point — callers pick their own implementation.
|
||||
defaultOptionsMu.RLock()
|
||||
defer defaultOptionsMu.RUnlock()
|
||||
|
||||
return defaultOptions.provider
|
||||
}
|
||||
|
||||
// WithNameProvider injects a custom [NameProvider] to resolve json names from go struct types.
|
||||
func WithNameProvider(provider NameProvider) Option {
|
||||
return func(o *options) {
|
||||
o.provider = provider
|
||||
}
|
||||
}
|
||||
|
||||
type options struct {
|
||||
provider NameProvider
|
||||
}
|
||||
|
||||
func optionsWithDefaults(opts []Option) options {
|
||||
var o options
|
||||
o.provider = DefaultNameProvider()
|
||||
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
+273
-85
@@ -11,8 +11,6 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/swag/jsonname"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,20 +18,6 @@ const (
|
||||
pointerSeparator = `/`
|
||||
)
|
||||
|
||||
// JSONPointable is an interface for structs to implement,
|
||||
// when they need to customize the json pointer process or want to avoid the use of reflection.
|
||||
type JSONPointable interface {
|
||||
// JSONLookup returns a value pointed at this (unescaped) key.
|
||||
JSONLookup(key string) (any, error)
|
||||
}
|
||||
|
||||
// JSONSetable is an interface for structs to implement,
|
||||
// when they need to customize the json pointer process or want to avoid the use of reflection.
|
||||
type JSONSetable interface {
|
||||
// JSONSet sets the value pointed at the (unescaped) key.
|
||||
JSONSet(key string, value any) error
|
||||
}
|
||||
|
||||
// Pointer is a representation of a json pointer.
|
||||
//
|
||||
// Use [Pointer.Get] to retrieve a value or [Pointer.Set] to set a value.
|
||||
@@ -41,7 +25,7 @@ type JSONSetable interface {
|
||||
// It works with any go type interpreted as a JSON document, which means:
|
||||
//
|
||||
// - if a type implements [JSONPointable], its [JSONPointable.JSONLookup] method is used to resolve [Pointer.Get]
|
||||
// - if a type implements [JSONSetable], its [JSONPointable.JSONSet] method is used to resolve [Pointer.Set]
|
||||
// - if a type implements [JSONSetable], its [JSONSetable.JSONSet] method is used to resolve [Pointer.Set]
|
||||
// - a go map[K]V is interpreted as an object, with type K assignable to a string
|
||||
// - a go slice []T is interpreted as an array
|
||||
// - a go struct is interpreted as an object, with exported fields interpreted as keys
|
||||
@@ -71,16 +55,35 @@ func New(jsonPointerString string) (Pointer, error) {
|
||||
// Get uses the pointer to retrieve a value from a JSON document.
|
||||
//
|
||||
// It returns the value with its type as a [reflect.Kind] or an error.
|
||||
func (p *Pointer) Get(document any) (any, reflect.Kind, error) {
|
||||
return p.get(document, jsonname.DefaultJSONNameProvider)
|
||||
func (p *Pointer) Get(document any, opts ...Option) (any, reflect.Kind, error) {
|
||||
o := optionsWithDefaults(opts)
|
||||
|
||||
return p.get(document, o.provider)
|
||||
}
|
||||
|
||||
// Set uses the pointer to set a value from a data type
|
||||
// that represent a JSON document.
|
||||
//
|
||||
// It returns the updated document.
|
||||
func (p *Pointer) Set(document any, value any) (any, error) {
|
||||
return document, p.set(document, value, jsonname.DefaultJSONNameProvider)
|
||||
// # Mutation contract
|
||||
//
|
||||
// Set mutates the provided document in place whenever Go's type system allows
|
||||
// it: when document is a map, a pointer, or when the targeted value is reached
|
||||
// through an addressable ancestor (e.g. a struct field traversed via a pointer,
|
||||
// a slice element). Callers that rely on this in-place behavior may continue
|
||||
// to ignore the returned document.
|
||||
//
|
||||
// The returned document is only load-bearing when Set cannot mutate in place.
|
||||
// This happens in one specific case: appending to a top-level slice passed by
|
||||
// value (e.g. document of type []T rather than *[]T) via the RFC 6901 "-"
|
||||
// terminal token. reflect.Append produces a new slice header that the library
|
||||
// cannot rebind into the caller's variable; the updated document is returned
|
||||
// instead. Pass *[]T if you want in-place rebind for that case as well.
|
||||
//
|
||||
// See [ErrDashToken] for the semantics of the "-" token.
|
||||
func (p *Pointer) Set(document any, value any, opts ...Option) (any, error) {
|
||||
o := optionsWithDefaults(opts)
|
||||
|
||||
return p.set(document, value, o.provider)
|
||||
}
|
||||
|
||||
// DecodedTokens returns the decoded (unescaped) tokens of this JSON pointer.
|
||||
@@ -109,6 +112,46 @@ func (p *Pointer) String() string {
|
||||
return pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
|
||||
}
|
||||
|
||||
// Offset returns the byte offset, in the raw JSON text of document, of the
|
||||
// location referenced by this pointer's terminal token.
|
||||
//
|
||||
// Unlike [Pointer.Get] and [Pointer.Set], which operate on a decoded Go value,
|
||||
// Offset operates directly on the textual JSON source. It drives an
|
||||
// [encoding/json.Decoder] over the string and stops at the terminal token,
|
||||
// returning the position at which the decoder was about to read that token.
|
||||
//
|
||||
// It is primarily intended for tooling that needs to map a pointer back to a
|
||||
// region of the original source: reporting line/column for validation or
|
||||
// parse diagnostics, extracting a sub-document by slicing the raw bytes, or
|
||||
// highlighting the referenced span in an editor.
|
||||
//
|
||||
// # Offset semantics
|
||||
//
|
||||
// The meaning of the returned offset depends on whether the terminal token
|
||||
// addresses an object property or an array element:
|
||||
//
|
||||
// - Object property: the offset points to the first byte of the key (its
|
||||
// opening quote character), not to the associated value. For example,
|
||||
// pointer "/foo/bar" against {"foo": {"bar": 21}} returns 9, the index of
|
||||
// the opening quote of "bar".
|
||||
// - Array element: the offset points to the first byte of the value at that
|
||||
// index. For example, pointer "/0/1" against [[1,2], [3,4]] returns 4,
|
||||
// the index of the digit 2.
|
||||
//
|
||||
// # Errors
|
||||
//
|
||||
// Offset returns an error in any of these cases:
|
||||
//
|
||||
// - document is not syntactically valid JSON;
|
||||
// - the structure of document does not match the pointer (e.g. traversing
|
||||
// into a scalar, or a token that is neither a valid key nor a valid
|
||||
// numeric index);
|
||||
// - a referenced key or index does not exist in document;
|
||||
// - the pointer's terminal token is the RFC 6901 "-" array token, which
|
||||
// designates a nonexistent element and therefore has no offset in the
|
||||
// source. The returned error wraps [ErrDashToken].
|
||||
//
|
||||
// All errors wrap [ErrPointer].
|
||||
func (p *Pointer) Offset(document string) (int64, error) {
|
||||
dec := json.NewDecoder(strings.NewReader(document))
|
||||
var offset int64
|
||||
@@ -137,7 +180,35 @@ func (p *Pointer) Offset(document string) (int64, error) {
|
||||
return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer)
|
||||
}
|
||||
}
|
||||
return offset, nil
|
||||
return skipJSONSeparator(document, offset), nil
|
||||
}
|
||||
|
||||
// skipJSONSeparator advances offset past trailing JSON whitespace and at most
|
||||
// one value separator (comma) in document, so the result points at the first
|
||||
// byte of the next JSON token.
|
||||
//
|
||||
// The streaming decoder's InputOffset sits right after the most recently
|
||||
// consumed token, which between values is the comma (or whitespace) — not
|
||||
// the following token. Normalizing here keeps Offset's contract uniform:
|
||||
// for both object keys and array elements, and regardless of position within
|
||||
// the parent container, the returned offset always points at the first byte
|
||||
// of the addressed token.
|
||||
func skipJSONSeparator(document string, offset int64) int64 {
|
||||
n := int64(len(document))
|
||||
for offset < n && isJSONWhitespace(document[offset]) {
|
||||
offset++
|
||||
}
|
||||
if offset < n && document[offset] == ',' {
|
||||
offset++
|
||||
}
|
||||
for offset < n && isJSONWhitespace(document[offset]) {
|
||||
offset++
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
func isJSONWhitespace(c byte) bool {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
|
||||
}
|
||||
|
||||
// "Constructor", parses the given string JSON pointer.
|
||||
@@ -157,9 +228,9 @@ func (p *Pointer) parse(jsonPointerString string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) {
|
||||
func (p *Pointer) get(node any, nameProvider NameProvider) (any, reflect.Kind, error) {
|
||||
if nameProvider == nil {
|
||||
nameProvider = jsonname.DefaultJSONNameProvider
|
||||
nameProvider = defaultOptions.provider
|
||||
}
|
||||
|
||||
kind := reflect.Invalid
|
||||
@@ -185,50 +256,130 @@ func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, refle
|
||||
return node, kind, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error {
|
||||
func (p *Pointer) set(node, data any, nameProvider NameProvider) (any, error) {
|
||||
knd := reflect.ValueOf(node).Kind()
|
||||
|
||||
if knd != reflect.Pointer && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
|
||||
return errors.Join(
|
||||
return node, errors.Join(
|
||||
fmt.Errorf("unexpected type: %T", node), //nolint:err113 // err wrapping is carried out by errors.Join, not fmt.Errorf.
|
||||
ErrUnsupportedValueType,
|
||||
ErrPointer,
|
||||
)
|
||||
}
|
||||
|
||||
l := len(p.referenceTokens)
|
||||
|
||||
// full document when empty
|
||||
if l == 0 {
|
||||
return nil
|
||||
if len(p.referenceTokens) == 0 {
|
||||
return node, nil
|
||||
}
|
||||
|
||||
if nameProvider == nil {
|
||||
nameProvider = jsonname.DefaultJSONNameProvider
|
||||
nameProvider = defaultOptions.provider
|
||||
}
|
||||
|
||||
var decodedToken string
|
||||
lastIndex := l - 1
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// last token
|
||||
decodedToken = Unescape(p.referenceTokens[lastIndex])
|
||||
|
||||
return setSingleImpl(node, data, decodedToken, nameProvider)
|
||||
return p.setAt(node, p.referenceTokens, data, nameProvider)
|
||||
}
|
||||
|
||||
func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider *jsonname.NameProvider) (next any, err error) {
|
||||
// setAt recursively walks the token list, setting the data at the terminal
|
||||
// token and rebinding any new child reference (e.g. a slice header returned
|
||||
// by an "-" append) into its parent on the way back up.
|
||||
//
|
||||
// Returning the (possibly new) node at each level is what makes append work
|
||||
// at any depth without requiring the caller to pass a pointer to the
|
||||
// containing slice: the new slice header propagates up and each parent
|
||||
// rebinds it via the appropriate kind-specific setter.
|
||||
func (p *Pointer) setAt(node any, tokens []string, data any, nameProvider NameProvider) (any, error) {
|
||||
decodedToken := Unescape(tokens[0])
|
||||
|
||||
if len(tokens) == 1 {
|
||||
return setSingleImpl(node, data, decodedToken, nameProvider)
|
||||
}
|
||||
|
||||
child, err := p.resolveNodeForToken(node, decodedToken, nameProvider)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
|
||||
newChild, err := p.setAt(child, tokens[1:], data, nameProvider)
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
|
||||
return rebindChild(node, decodedToken, newChild, nameProvider)
|
||||
}
|
||||
|
||||
// rebindChild writes newChild back into node at decodedToken.
|
||||
//
|
||||
// For cases where the child was already mutated in place (pointer aliasing,
|
||||
// addressable slice elements) the rebind is a safe no-op. For cases where
|
||||
// the child was returned by value (map entries holding a slice, slices
|
||||
// reached through a non-addressable ancestor), the rebind propagates the
|
||||
// new value into the parent.
|
||||
//
|
||||
// Parents implementing [JSONPointable] are left alone: they took ownership
|
||||
// of the child via JSONLookup and did not opt into a JSONSet-based rebind
|
||||
// on intermediate tokens.
|
||||
func rebindChild(node any, decodedToken string, newChild any, nameProvider NameProvider) (any, error) {
|
||||
if _, ok := node.(JSONPointable); ok {
|
||||
return node, nil
|
||||
}
|
||||
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
|
||||
switch rValue.Kind() {
|
||||
case reflect.Struct:
|
||||
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
|
||||
if !ok {
|
||||
return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
|
||||
}
|
||||
fld := rValue.FieldByName(nm)
|
||||
if !fld.CanSet() {
|
||||
return node, nil
|
||||
}
|
||||
assignReflectValue(fld, newChild)
|
||||
return node, nil
|
||||
|
||||
case reflect.Map:
|
||||
rValue.SetMapIndex(reflect.ValueOf(decodedToken), reflect.ValueOf(newChild))
|
||||
return node, nil
|
||||
|
||||
case reflect.Slice:
|
||||
if decodedToken == dashToken {
|
||||
return node, errDashIntermediate()
|
||||
}
|
||||
idx, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return node, errors.Join(err, ErrPointer)
|
||||
}
|
||||
elem := rValue.Index(idx)
|
||||
if !elem.CanSet() {
|
||||
return node, nil
|
||||
}
|
||||
assignReflectValue(elem, newChild)
|
||||
return node, nil
|
||||
|
||||
default:
|
||||
return node, errInvalidReference(decodedToken)
|
||||
}
|
||||
}
|
||||
|
||||
// assignReflectValue assigns src into dst, unwrapping a pointer when dst
|
||||
// expects the pointee type. This tolerates the pointer-wrapping performed
|
||||
// by [typeFromValue] for addressable fields.
|
||||
func assignReflectValue(dst reflect.Value, src any) {
|
||||
nv := reflect.ValueOf(src)
|
||||
if !nv.IsValid() {
|
||||
return
|
||||
}
|
||||
if nv.Type().AssignableTo(dst.Type()) {
|
||||
dst.Set(nv)
|
||||
return
|
||||
}
|
||||
if nv.Kind() == reflect.Pointer && nv.Elem().Type().AssignableTo(dst.Type()) {
|
||||
dst.Set(nv.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider NameProvider) (next any, err error) {
|
||||
// check for nil during traversal
|
||||
if isNil(node) {
|
||||
return nil, fmt.Errorf("cannot traverse through nil value at %q: %w", decodedToken, ErrPointer)
|
||||
@@ -272,6 +423,9 @@ func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvide
|
||||
return typeFromValue(mv), nil
|
||||
|
||||
case reflect.Slice:
|
||||
if decodedToken == dashToken {
|
||||
return nil, errDashIntermediate()
|
||||
}
|
||||
tokenIndex, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return nil, errors.Join(err, ErrPointer)
|
||||
@@ -312,16 +466,23 @@ func typeFromValue(v reflect.Value) any {
|
||||
}
|
||||
|
||||
// GetForToken gets a value for a json pointer token 1 level deep.
|
||||
func GetForToken(document any, decodedToken string) (any, reflect.Kind, error) {
|
||||
return getSingleImpl(document, decodedToken, jsonname.DefaultJSONNameProvider)
|
||||
func GetForToken(document any, decodedToken string, opts ...Option) (any, reflect.Kind, error) {
|
||||
o := optionsWithDefaults(opts)
|
||||
|
||||
return getSingleImpl(document, decodedToken, o.provider)
|
||||
}
|
||||
|
||||
// SetForToken sets a value for a json pointer token 1 level deep.
|
||||
func SetForToken(document any, decodedToken string, value any) (any, error) {
|
||||
return document, setSingleImpl(document, value, decodedToken, jsonname.DefaultJSONNameProvider)
|
||||
//
|
||||
// See [Pointer.Set] for the mutation contract, in particular the handling of
|
||||
// the RFC 6901 "-" token on slices.
|
||||
func SetForToken(document any, decodedToken string, value any, opts ...Option) (any, error) {
|
||||
o := optionsWithDefaults(opts)
|
||||
|
||||
return setSingleImpl(document, value, decodedToken, o.provider)
|
||||
}
|
||||
|
||||
func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) {
|
||||
func getSingleImpl(node any, decodedToken string, nameProvider NameProvider) (any, reflect.Kind, error) {
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind := rValue.Kind()
|
||||
if isNil(node) {
|
||||
@@ -361,6 +522,9 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro
|
||||
return nil, kind, errNoKey(decodedToken)
|
||||
|
||||
case reflect.Slice:
|
||||
if decodedToken == dashToken {
|
||||
return nil, kind, errDashOnGet()
|
||||
}
|
||||
tokenIndex, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return nil, kind, errors.Join(err, ErrPointer)
|
||||
@@ -378,14 +542,14 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro
|
||||
}
|
||||
}
|
||||
|
||||
func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.NameProvider) error {
|
||||
func setSingleImpl(node, data any, decodedToken string, nameProvider NameProvider) (any, error) {
|
||||
// check for nil to prevent panic when calling rValue.Type()
|
||||
if isNil(node) {
|
||||
return fmt.Errorf("cannot set field %q on nil value: %w", decodedToken, ErrPointer)
|
||||
return node, fmt.Errorf("cannot set field %q on nil value: %w", decodedToken, ErrPointer)
|
||||
}
|
||||
|
||||
if ns, ok := node.(JSONSetable); ok {
|
||||
return ns.JSONSet(decodedToken, data)
|
||||
return node, ns.JSONSet(decodedToken, data)
|
||||
}
|
||||
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
@@ -394,12 +558,12 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.N
|
||||
case reflect.Struct:
|
||||
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
|
||||
if !ok {
|
||||
return fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
|
||||
return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
|
||||
}
|
||||
|
||||
fld := rValue.FieldByName(nm)
|
||||
if !fld.CanSet() {
|
||||
return fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer)
|
||||
return node, fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer)
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(data)
|
||||
@@ -407,33 +571,51 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.N
|
||||
assignedType := fld.Type()
|
||||
|
||||
if !valueType.AssignableTo(assignedType) {
|
||||
return fmt.Errorf("can't set value with type %T to field %s with type %v: %w", data, nm, assignedType, ErrPointer)
|
||||
return node, fmt.Errorf("can't set value with type %T to field %s with type %v: %w", data, nm, assignedType, ErrPointer)
|
||||
}
|
||||
|
||||
fld.Set(value)
|
||||
|
||||
return nil
|
||||
return node, nil
|
||||
|
||||
case reflect.Map:
|
||||
kv := reflect.ValueOf(decodedToken)
|
||||
rValue.SetMapIndex(kv, reflect.ValueOf(data))
|
||||
|
||||
return nil
|
||||
return node, nil
|
||||
|
||||
case reflect.Slice:
|
||||
if decodedToken == dashToken {
|
||||
// RFC 6901 §4 / RFC 6902 append semantics: terminal "-" appends
|
||||
// the value to the slice. We rebind in place when the slice is
|
||||
// reachable via an addressable ancestor; otherwise we return the
|
||||
// new slice header for the parent (or the public Set) to rebind.
|
||||
value := reflect.ValueOf(data)
|
||||
elemType := rValue.Type().Elem()
|
||||
if !value.Type().AssignableTo(elemType) {
|
||||
return node, fmt.Errorf("can't append value of type %T to slice of %v: %w", data, elemType, ErrPointer)
|
||||
}
|
||||
newSlice := reflect.Append(rValue, value)
|
||||
if rValue.CanSet() {
|
||||
rValue.Set(newSlice)
|
||||
return node, nil
|
||||
}
|
||||
return newSlice.Interface(), nil
|
||||
}
|
||||
|
||||
tokenIndex, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return errors.Join(err, ErrPointer)
|
||||
return node, errors.Join(err, ErrPointer)
|
||||
}
|
||||
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
return errOutOfBounds(sLength, tokenIndex)
|
||||
return node, errOutOfBounds(sLength, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
if !elem.CanSet() {
|
||||
return fmt.Errorf("can't set slice index %s to %v: %w", decodedToken, data, ErrPointer)
|
||||
return node, fmt.Errorf("can't set slice index %s to %v: %w", decodedToken, data, ErrPointer)
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(data)
|
||||
@@ -441,15 +623,15 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.N
|
||||
assignedType := elem.Type()
|
||||
|
||||
if !valueType.AssignableTo(assignedType) {
|
||||
return fmt.Errorf("can't set value with type %T to slice element %d with type %v: %w", data, tokenIndex, assignedType, ErrPointer)
|
||||
return node, fmt.Errorf("can't set value with type %T to slice element %d with type %v: %w", data, tokenIndex, assignedType, ErrPointer)
|
||||
}
|
||||
|
||||
elem.Set(value)
|
||||
|
||||
return nil
|
||||
return node, nil
|
||||
|
||||
default:
|
||||
return errInvalidReference(decodedToken)
|
||||
return node, errInvalidReference(decodedToken)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,24 +642,27 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch tk := tk.(type) {
|
||||
case json.Delim:
|
||||
switch tk {
|
||||
case '{':
|
||||
if err = drainSingle(dec); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case '[':
|
||||
key, ok := tk.(string)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("invalid key token %#v: %w", tk, ErrPointer)
|
||||
}
|
||||
if key == decodedToken {
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// Consume the associated value. Scalars are fully read by a single
|
||||
// Token() call; composite values must be drained.
|
||||
tk, err = dec.Token()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if delim, isDelim := tk.(json.Delim); isDelim {
|
||||
switch delim {
|
||||
case '{', '[':
|
||||
if err = drainSingle(dec); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
case string:
|
||||
if tk == decodedToken {
|
||||
return offset, nil
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,6 +670,9 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) {
|
||||
}
|
||||
|
||||
func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) {
|
||||
if decodedToken == dashToken {
|
||||
return 0, errDashOnOffset()
|
||||
}
|
||||
idx, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("token reference %q is not a number: %w: %w", decodedToken, err, ErrPointer)
|
||||
|
||||
Reference in New Issue
Block a user