Update all dependencies

Update all deps.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
Gabriel Adrian Samfira 2026-01-05 23:04:01 +00:00 committed by Gabriel
parent 3640235eeb
commit 47537fb8b6
757 changed files with 87315 additions and 14280 deletions

181
vendor/github.com/go-openapi/jsonpointer/.cliff.toml generated vendored Normal file
View file

@ -0,0 +1,181 @@
# 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"

View file

@ -1 +1,4 @@
secrets.yml
*.out
*.cov
.idea
.env

View file

@ -2,34 +2,17 @@ version: "2"
linters:
default: all
disable:
- cyclop
- depguard
- errchkjson
- errorlint
- exhaustruct
- forcetypeassert
- funlen
- gochecknoglobals
- gochecknoinits
- gocognit
- godot
- godox
- gosmopolitan
- inamedparam
#- intrange # disabled while < go1.22
- ireturn
- lll
- musttag
- nestif
- exhaustruct
- nlreturn
- nonamedreturns
- noinlineerr
- paralleltest
- recvcheck
- testpackage
- thelper
- tparallel
- unparam
- varnamelen
- whitespace
- wrapcheck
@ -41,8 +24,15 @@ linters:
goconst:
min-len: 2
min-occurrences: 3
cyclop:
max-complexity: 20
gocyclo:
min-complexity: 45
min-complexity: 20
exhaustive:
default-signifies-exhaustive: true
default-case-required: true
lll:
line-length: 180
exclusions:
generated: lax
presets:
@ -58,6 +48,7 @@ formatters:
enable:
- gofmt
- goimports
- gofumpt
exclusions:
generated: lax
paths:

View file

@ -0,0 +1,24 @@
# Contributors
- Repository: ['go-openapi/jsonpointer']
| Total Contributors | Total Contributions |
| --- | --- |
| 12 | 95 |
| Username | All Time Contribution Count | All Commits |
| --- | --- | --- |
| @fredbi | 48 | 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 |
| @gaiaz-iusipov | 1 | https://github.com/go-openapi/jsonpointer/commits?author=gaiaz-iusipov |
| @gbjk | 1 | https://github.com/go-openapi/jsonpointer/commits?author=gbjk |
| @gordallott | 1 | https://github.com/go-openapi/jsonpointer/commits?author=gordallott |
| @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 |
| @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)_

View file

@ -1,4 +1,3 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

39
vendor/github.com/go-openapi/jsonpointer/NOTICE generated vendored Normal file
View file

@ -0,0 +1,39 @@
Copyright 2015-2025 go-swagger maintainers
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
This software library, github.com/go-openapi/jsonpointer, includes software developed
by the go-swagger and go-openapi maintainers ("go-swagger maintainers").
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this software except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
This software is copied from, derived from, and inspired by other original software products.
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
===========================
// SPDX-FileCopyrightText: Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
// SPDX-License-Identifier: Apache-2.0
Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,16 +1,78 @@
# gojsonpointer [![Build Status](https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer)
# jsonpointer
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer.svg)](https://pkg.go.dev/github.com/go-openapi/jsonpointer)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonpointer)](https://goreportcard.com/report/github.com/go-openapi/jsonpointer)
<!-- Badges: status -->
[![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vuln scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url]
<!-- Badges: release & docker images -->
<!-- Badges: code quality -->
<!-- Badges: license & compliance -->
[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url]
<!-- Badges: documentation & support -->
<!-- Badges: others & stats -->
[![GoDoc][godoc-badge]][godoc-url] [![Slack Channel][slack-logo]![slack-badge]][slack-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge]
An implementation of JSON Pointer - Go language
---
An implementation of JSON Pointer for golang, which supports go `struct`.
## Status
Completed YES
Tested YES
API is stable.
## Import this library in your project
```cmd
go get github.com/go-openapi/jsonpointer
```
## Basic usage
See also some [examples](./examples_test.go)
### Retrieving a value
```go
import (
"github.com/go-openapi/jsonpointer"
)
var doc any
...
pointer, err := jsonpointer.New("/foo/1")
if err != nil {
... // error: e.g. invalid JSON pointer specification
}
value, kind, err := pointer.Get(doc)
if err != nil {
... // error: e.g. key not found, index out of bounds, etc.
}
...
```
### Setting a value
```go
...
var doc any
...
pointer, err := jsonpointer.New("/foo/1")
if err != nil {
... // error: e.g. invalid JSON pointer specification
}
doc, err = p.Set(doc, "value")
if err != nil {
... // error: e.g. key not found, index out of bounds, etc.
}
```
## Change log
See <https://github.com/go-openapi/jsonpointer/releases>
## References
@ -18,9 +80,70 @@ Tested YES
also known as [RFC6901](https://www.rfc-editor.org/rfc/rfc6901)
### Note
## Licensing
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.
This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE).
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 "-".
See the license [NOTICE](./NOTICE), which recalls the licensing terms of all the pieces of software
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.
## Other documentation
* [All-time contributors](./CONTRIBUTORS.md)
* [Contributing guidelines](.github/CONTRIBUTING.md)
* [Maintainers documentation](docs/MAINTAINERS.md)
* [Code style](docs/STYLE.md)
## Cutting a new release
Maintainers can cut a new release by either:
* running [this workflow](https://github.com/go-openapi/jsonpointer/actions/workflows/bump-release.yml)
* or pushing a semver tag
* signed tags are preferred
* The tag message is prepended to release notes
<!-- Badges: status -->
[test-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg
[test-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml
[cov-badge]: https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg
[cov-url]: https://codecov.io/gh/go-openapi/jsonpointer
[vuln-scan-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/scanner.yml/badge.svg
[vuln-scan-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/scanner.yml
[codeql-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/codeql.yml/badge.svg
[codeql-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/codeql.yml
<!-- Badges: release & docker images -->
[release-badge]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer.svg
[release-url]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer
[gomod-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonpointer.svg
[gomod-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonpointer
<!-- Badges: code quality -->
[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/jsonpointer
[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/jsonpointer
[codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/jsonpointer
[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/jsonpointer
<!-- Badges: documentation & support -->
[doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgoswagger.io%2Fgo-openapi%2F
[doc-url]: https://goswagger.io/go-openapi
[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
<!-- Badges: license & compliance -->
[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg
[license-url]: https://github.com/go-openapi/jsonpointer/?tab=Apache-2.0-1-ov-file#readme
<!-- Badges: others & stats -->
[goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/jsonpointer
[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

19
vendor/github.com/go-openapi/jsonpointer/SECURITY.md generated vendored Normal file
View file

@ -0,0 +1,19 @@
# Security Policy
This policy outlines the commitment and practices of the go-openapi maintainers regarding security.
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 0.22.x | :white_check_mark: |
## Reporting a vulnerability
If you become aware of a security vulnerability that affects the current repository,
please report it privately to the maintainers.
Please follow the instructions provided by github to
[Privately report a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability).
TL;DR: on Github, navigate to the project's "Security" tab then click on "Report a vulnerability".

View file

@ -1,5 +1,10 @@
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
package jsonpointer
import "fmt"
type pointerError string
func (e pointerError) Error() string {
@ -7,12 +12,24 @@ func (e pointerError) Error() string {
}
const (
// ErrPointer is an error raised by the jsonpointer package
// ErrPointer is a sentinel error raised by all errors from this package.
ErrPointer pointerError = "JSON pointer error"
// ErrInvalidStart states that a JSON pointer must start with a separator ("/")
// ErrInvalidStart states that a JSON pointer must start with a separator ("/").
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 indicates that a value of the wrong type is being set.
ErrUnsupportedValueType pointerError = "only structs, pointers, maps and slices are supported for setting values"
)
func errNoKey(key string) error {
return fmt.Errorf("object has no key %q: %w", key, ErrPointer)
}
func errOutOfBounds(length, idx int) error {
return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", length-1, idx, ErrPointer)
}
func errInvalidReference(token string) error {
return fmt.Errorf("invalid token reference %q: %w", token, ErrPointer)
}

View file

@ -1,28 +1,7 @@
// Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// author sigu-399
// author-github https://github.com/sigu-399
// author-mail sigu.399@gmail.com
//
// repository-name jsonpointer
// repository-desc An implementation of JSON Pointer - Go language
//
// description Main and unique file.
//
// created 25-02-2013
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
// Package jsonpointer provides a golang implementation for json pointers.
package jsonpointer
import (
@ -41,29 +20,47 @@ const (
pointerSeparator = `/`
)
var (
jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
)
// JSONPointable is an interface for structs to implement when they need to customize the
// json pointer process
// 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(string) (any, error)
// 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
// 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(string, any) error
// JSONSet sets the value pointed at the (unescaped) key.
JSONSet(key string, value any) error
}
// Pointer is a representation of a json pointer
// Pointer is a representation of a json pointer.
//
// Use [Pointer.Get] to retrieve a value or [Pointer.Set] to set a value.
//
// 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]
// - 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
// - promoted fields from an embedded struct are traversed
// - scalars (e.g. int, float64 ...), channels, functions and go arrays cannot be traversed
//
// For struct s resolved by reflection, key mappings honor the conventional struct tag `json`.
//
// Fields that do not specify a `json` tag, or specify an empty one, or are tagged as `json:"-"` are ignored.
//
// # Limitations
//
// - Unlike go standard marshaling, untagged fields do not default to the go field name and are ignored.
// - anonymous fields are not traversed if untagged
type Pointer struct {
referenceTokens []string
}
// New creates a new json pointer for the given string
// New creates a new json pointer from its string representation.
func New(jsonPointerString string) (Pointer, error) {
var p Pointer
err := p.parse(jsonPointerString)
@ -71,34 +68,40 @@ func New(jsonPointerString string) (Pointer, error) {
return p, err
}
// Get uses the pointer to retrieve a value from a JSON document
// 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)
}
// Set uses the pointer to set a value from a JSON document
// 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)
}
// DecodedTokens returns the decoded tokens of this JSON pointer
// DecodedTokens returns the decoded (unescaped) tokens of this JSON pointer.
func (p *Pointer) DecodedTokens() []string {
result := make([]string, 0, len(p.referenceTokens))
for _, t := range p.referenceTokens {
result = append(result, Unescape(t))
for _, token := range p.referenceTokens {
result = append(result, Unescape(token))
}
return result
}
// IsEmpty returns true if this is an empty json pointer
// this indicates that it points to the root document
// IsEmpty returns true if this is an empty json pointer.
//
// This indicates that it points to the root document.
func (p *Pointer) IsEmpty() bool {
return len(p.referenceTokens) == 0
}
// Pointer to string representation function
// String representation of a pointer.
func (p *Pointer) String() string {
if len(p.referenceTokens) == 0 {
return emptyPointer
}
@ -137,20 +140,21 @@ func (p *Pointer) Offset(document string) (int64, error) {
return offset, nil
}
// "Constructor", parses the given string JSON pointer
// "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
}
return err
if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
// non empty pointer must start with "/"
return errors.Join(ErrInvalidStart, ErrPointer)
}
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) {
@ -160,7 +164,7 @@ func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, refle
kind := reflect.Invalid
// Full document when empty
// full document when empty
if len(p.referenceTokens) == 0 {
return node, kind, nil
}
@ -186,101 +190,103 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error
if knd != reflect.Pointer && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
return 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 nameProvider == nil {
nameProvider = jsonname.DefaultJSONNameProvider
}
// Full document when empty
if len(p.referenceTokens) == 0 {
return nil
}
var decodedToken string
lastIndex := l - 1
lastI := len(p.referenceTokens) - 1
for i, token := range p.referenceTokens {
isLastToken := i == lastI
decodedToken := Unescape(token)
if isLastToken {
return setSingleImpl(node, data, decodedToken, nameProvider)
}
// Check for nil during traversal
if isNil(node) {
return fmt.Errorf("cannot traverse through nil value at %q: %w", decodedToken, ErrPointer)
}
rValue := reflect.Indirect(reflect.ValueOf(node))
kind := rValue.Kind()
if rValue.Type().Implements(jsonPointableType) {
r, err := node.(JSONPointable).JSONLookup(decodedToken)
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
}
fld := reflect.ValueOf(r)
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer {
node = fld.Addr().Interface()
continue
}
node = r
continue
}
switch kind { //nolint:exhaustive
case reflect.Struct:
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
}
fld := rValue.FieldByName(nm)
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer {
node = fld.Addr().Interface()
continue
}
node = fld.Interface()
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
mv := rValue.MapIndex(kv)
if !mv.IsValid() {
return fmt.Errorf("object has no key %q: %w", decodedToken, ErrPointer)
}
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Pointer {
node = mv.Addr().Interface()
continue
}
node = mv.Interface()
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return err
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", sLength, tokenIndex, ErrPointer)
}
elem := rValue.Index(tokenIndex)
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Pointer {
node = elem.Addr().Interface()
continue
}
node = elem.Interface()
default:
return fmt.Errorf("invalid token reference %q: %w", decodedToken, ErrPointer)
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) {
// check for nil during traversal
if isNil(node) {
return nil, fmt.Errorf("cannot traverse through nil value at %q: %w", decodedToken, ErrPointer)
}
pointable, ok := node.(JSONPointable)
if ok {
r, err := pointable.JSONLookup(decodedToken)
if err != nil {
return nil, err
}
fld := reflect.ValueOf(r)
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer {
return fld.Addr().Interface(), nil
}
return r, nil
}
rValue := reflect.Indirect(reflect.ValueOf(node))
kind := rValue.Kind()
switch kind {
case reflect.Struct:
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return nil, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
}
return typeFromValue(rValue.FieldByName(nm)), nil
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
mv := rValue.MapIndex(kv)
if !mv.IsValid() {
return nil, errNoKey(decodedToken)
}
return typeFromValue(mv), nil
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return nil, errors.Join(err, ErrPointer)
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return nil, errOutOfBounds(sLength, tokenIndex)
}
return typeFromValue(rValue.Index(tokenIndex)), nil
default:
return nil, errInvalidReference(decodedToken)
}
}
func isNil(input any) bool {
@ -289,7 +295,7 @@ func isNil(input any) bool {
}
kind := reflect.TypeOf(input).Kind()
switch kind { //nolint:exhaustive
switch kind {
case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Chan:
return reflect.ValueOf(input).IsNil()
default:
@ -297,12 +303,20 @@ func isNil(input any) bool {
}
}
// GetForToken gets a value for a json pointer token 1 level deep
func typeFromValue(v reflect.Value) any {
if v.CanAddr() && v.Kind() != reflect.Interface && v.Kind() != reflect.Map && v.Kind() != reflect.Slice && v.Kind() != reflect.Pointer {
return v.Addr().Interface()
}
return v.Interface()
}
// 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)
}
// SetForToken gets a value for a json pointer token 1 level deep
// 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)
}
@ -325,13 +339,15 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro
return getSingleImpl(*typed, decodedToken, nameProvider)
}
switch kind { //nolint:exhaustive
switch kind {
case reflect.Struct:
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return nil, kind, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
}
fld := rValue.FieldByName(nm)
return fld.Interface(), kind, nil
case reflect.Map:
@ -341,78 +357,99 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro
if mv.IsValid() {
return mv.Interface(), kind, nil
}
return nil, kind, fmt.Errorf("object has no key %q: %w", decodedToken, ErrPointer)
return nil, kind, errNoKey(decodedToken)
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return nil, kind, err
return nil, kind, errors.Join(err, ErrPointer)
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", sLength-1, tokenIndex, ErrPointer)
return nil, kind, errOutOfBounds(sLength, tokenIndex)
}
elem := rValue.Index(tokenIndex)
return elem.Interface(), kind, nil
default:
return nil, kind, fmt.Errorf("invalid token reference %q: %w", decodedToken, ErrPointer)
return nil, kind, errInvalidReference(decodedToken)
}
}
func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.NameProvider) error {
rValue := reflect.Indirect(reflect.ValueOf(node))
// Check for nil to prevent panic when calling rValue.Type()
// 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)
}
if ns, ok := node.(JSONSetable); ok { // pointer impl
if ns, ok := node.(JSONSetable); ok {
return ns.JSONSet(decodedToken, data)
}
if rValue.Type().Implements(jsonSetableType) {
return node.(JSONSetable).JSONSet(decodedToken, data)
}
rValue := reflect.Indirect(reflect.ValueOf(node))
switch rValue.Kind() { //nolint:exhaustive
switch rValue.Kind() {
case reflect.Struct:
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer)
}
fld := rValue.FieldByName(nm)
if fld.IsValid() {
fld.Set(reflect.ValueOf(data))
if !fld.CanSet() {
return fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer)
}
value := reflect.ValueOf(data)
valueType := value.Type()
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)
}
fld.Set(value)
return nil
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
rValue.SetMapIndex(kv, reflect.ValueOf(data))
return nil
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return err
return errors.Join(err, ErrPointer)
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", sLength, tokenIndex, ErrPointer)
return 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)
}
elem.Set(reflect.ValueOf(data))
value := reflect.ValueOf(data)
valueType := value.Type()
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)
}
elem.Set(value)
return nil
default:
return fmt.Errorf("invalid token reference %q: %w", decodedToken, ErrPointer)
return errInvalidReference(decodedToken)
}
}
@ -443,13 +480,14 @@ 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)
}
func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) {
idx, err := strconv.Atoi(decodedToken)
if err != nil {
return 0, fmt.Errorf("token reference %q is not a number: %v: %w", decodedToken, err, ErrPointer)
return 0, fmt.Errorf("token reference %q is not a number: %w: %w", decodedToken, err, ErrPointer)
}
var i int
for i = 0; i < idx && dec.More(); i++ {
@ -475,10 +513,12 @@ 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
}
// drainSingle drains a single level of object or array.
//
// The decoder has to guarantee the beginning delim (i.e. '{' or '[') has been consumed.
func drainSingle(dec *json.Decoder) error {
for dec.More() {
@ -500,14 +540,15 @@ func drainSingle(dec *json.Decoder) error {
}
}
// Consumes the ending delim
// consumes the ending delim
if _, err := dec.Token(); err != nil {
return err
}
return nil
}
// Specific JSON pointer encoding here
// JSON pointer encoding:
// ~0 => ~
// ~1 => /
// ... and vice versa
@ -520,16 +561,23 @@ const (
)
var (
encRefTokReplacer = strings.NewReplacer(encRefTok1, decRefTok1, encRefTok0, decRefTok0)
decRefTokReplacer = strings.NewReplacer(decRefTok1, encRefTok1, decRefTok0, encRefTok0)
encRefTokReplacer = strings.NewReplacer(encRefTok1, decRefTok1, encRefTok0, decRefTok0) //nolint:gochecknoglobals // it's okay to declare a replacer as a private global
decRefTokReplacer = strings.NewReplacer(decRefTok1, encRefTok1, decRefTok0, encRefTok0) //nolint:gochecknoglobals // it's okay to declare a replacer as a private global
)
// Unescape unescapes a json pointer reference token string to the original representation
// Unescape unescapes a json pointer reference token string to the original representation.
func Unescape(token string) string {
return encRefTokReplacer.Replace(token)
}
// Escape escapes a pointer reference token string
// Escape escapes a pointer reference token string.
//
// The JSONPointer specification defines "/" as a separator and "~" as an escape prefix.
//
// Keys containing such characters are escaped with the following rules:
//
// - "~" is escaped as "~0"
// - "/" is escaped as "~1"
func Escape(token string) string {
return decRefTokReplacer.Replace(token)
}