mirror of
https://github.com/kubernetes/node-problem-detector.git
synced 2026-08-23 22:26:27 +00:00
Fix Grype CVEs: update logrus and prometheus/prometheus
- Update github.com/sirupsen/logrus v1.9.0 -> v1.9.3 in test/go.mod to fix GHSA-4f99-4q7p-p3gh (High) - Update github.com/prometheus/prometheus v0.35.0 -> v0.311.3 to fix GHSA-vffh-x6r8-xx99 (Medium) - Run go mod tidy and go mod vendor to update vendor directory
This commit is contained in:
committed by
Ciprian Hacman
parent
63808794e6
commit
ddfb2c48d6
+181
@@ -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"
|
||||
+6
-1
@@ -1 +1,6 @@
|
||||
secrets.yml
|
||||
*.out
|
||||
*.cov
|
||||
.idea
|
||||
.env
|
||||
.mcp.json
|
||||
.claude/
|
||||
|
||||
+61
-55
@@ -1,61 +1,67 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 45
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 200
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 3
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
enable-all: true
|
||||
default: all
|
||||
disable:
|
||||
- maligned
|
||||
- unparam
|
||||
- lll
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
- depguard
|
||||
- funlen
|
||||
- godox
|
||||
- gocognit
|
||||
- whitespace
|
||||
- wsl
|
||||
- wrapcheck
|
||||
- testpackage
|
||||
- nlreturn
|
||||
- gomnd
|
||||
- exhaustivestruct
|
||||
- goerr113
|
||||
- errorlint
|
||||
- nestif
|
||||
- godot
|
||||
- gofumpt
|
||||
- paralleltest
|
||||
- tparallel
|
||||
- thelper
|
||||
- ifshort
|
||||
- exhaustruct
|
||||
- varnamelen
|
||||
- gci
|
||||
- depguard
|
||||
- errchkjson
|
||||
- inamedparam
|
||||
- nlreturn
|
||||
- nonamedreturns
|
||||
- musttag
|
||||
- ireturn
|
||||
- forcetypeassert
|
||||
- cyclop
|
||||
# deprecated linters
|
||||
- deadcode
|
||||
- interfacer
|
||||
- scopelint
|
||||
- varcheck
|
||||
- structcheck
|
||||
- golint
|
||||
- nosnakecase
|
||||
- noinlineerr
|
||||
- paralleltest
|
||||
- recvcheck
|
||||
- testpackage
|
||||
- thelper
|
||||
- tparallel
|
||||
- varnamelen
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
- wsl
|
||||
- wsl_v5
|
||||
settings:
|
||||
dupl:
|
||||
threshold: 200
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 3
|
||||
cyclop:
|
||||
max-complexity: 20
|
||||
gocyclo:
|
||||
min-complexity: 20
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
default-case-required: true
|
||||
lll:
|
||||
line-length: 180
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
- gofumpt
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
# Maximum issues count per one linter.
|
||||
# Set to 0 to disable.
|
||||
# Default: 50
|
||||
max-issues-per-linter: 0
|
||||
# Maximum count of issues with the same text.
|
||||
# Set to 0 to disable.
|
||||
# Default: 3
|
||||
max-same-issues: 0
|
||||
|
||||
+4
-2
@@ -23,7 +23,9 @@ include:
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
|
||||
advances
|
||||
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
@@ -55,7 +57,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at ivan+abuse@flanders.co.nz. All
|
||||
reported by contacting the project team at <ivan+abuse@flanders.co.nz>. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
@@ -68,7 +70,7 @@ members of the project's leadership.
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
available at [<http://contributor-covenant.org/version/1/4>][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Contributors
|
||||
|
||||
- Repository: ['go-openapi/jsonpointer']
|
||||
|
||||
| Total Contributors | Total Contributions |
|
||||
| --- | --- |
|
||||
| 12 | 101 |
|
||||
|
||||
| Username | All Time Contribution Count | All Commits |
|
||||
| --- | --- | --- |
|
||||
| @fredbi | 54 | <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)_
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
+39
@@ -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.
|
||||
+150
-11
@@ -1,19 +1,158 @@
|
||||
# gojsonpointer [](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [](https://codecov.io/gh/go-openapi/jsonpointer)
|
||||
# jsonpointer
|
||||
|
||||
[](https://slackin.goswagger.io)
|
||||
[](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE)
|
||||
[](https://pkg.go.dev/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] [![Discord Channel][discord-badge]][discord-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`.
|
||||
|
||||
## 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**
|
||||
|
||||
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]
|
||||
|
||||
## 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
|
||||
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
|
||||
|
||||
### Note
|
||||
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.
|
||||
<https://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07>
|
||||
|
||||
also known as [RFC6901](https://www.rfc-editor.org/rfc/rfc6901)
|
||||
|
||||
## Licensing
|
||||
|
||||
This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE).
|
||||
|
||||
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
|
||||
<!-- 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 -->
|
||||
[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
|
||||
|
||||
<!-- 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
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Security Policy
|
||||
|
||||
This policy outlines the commitment and practices of the go-openapi maintainers regarding security.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| O.x | :white_check_mark: |
|
||||
|
||||
## Vulnerability checks in place
|
||||
|
||||
This repository uses automated vulnerability scans, at every merged commit and at least once a week.
|
||||
|
||||
We use:
|
||||
|
||||
* [`GitHub CodeQL`][codeql-url]
|
||||
* [`trivy`][trivy-url]
|
||||
* [`govulncheck`][govulncheck-url]
|
||||
|
||||
Reports are centralized in github security reports and visible only to the maintainers.
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
If you become aware of a security vulnerability that affects the current repository,
|
||||
**please report it privately to the maintainers**
|
||||
rather than opening a publicly visible GitHub issue.
|
||||
|
||||
Please follow the instructions provided by github to [Privately report a security vulnerability][github-guidance-url].
|
||||
|
||||
> [!NOTE]
|
||||
> On Github, navigate to the project's "Security" tab then click on "Report a vulnerability".
|
||||
|
||||
[codeql-url]: https://github.com/github/codeql
|
||||
[trivy-url]: https://trivy.dev/docs/latest/getting-started
|
||||
[govulncheck-url]: https://go.dev/blog/govulncheck
|
||||
[github-guidance-url]: 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
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// 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 {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
const (
|
||||
// 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 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"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
+398
-346
@@ -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 (
|
||||
@@ -33,353 +12,101 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/swag/jsonname"
|
||||
)
|
||||
|
||||
const (
|
||||
emptyPointer = ``
|
||||
pointerSeparator = `/`
|
||||
|
||||
invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator
|
||||
notFound = `Can't find the pointer in the document`
|
||||
)
|
||||
|
||||
var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
|
||||
var 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
|
||||
}
|
||||
|
||||
// New creates a new json pointer for the given string
|
||||
func New(jsonPointerString string) (Pointer, error) {
|
||||
|
||||
var p Pointer
|
||||
err := p.parse(jsonPointerString)
|
||||
return p, err
|
||||
|
||||
}
|
||||
|
||||
// Pointer the json pointer reprsentation
|
||||
// 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
|
||||
}
|
||||
|
||||
// "Constructor", parses the given string JSON pointer
|
||||
func (p *Pointer) parse(jsonPointerString string) error {
|
||||
// New creates a new json pointer from its string representation.
|
||||
func New(jsonPointerString string) (Pointer, error) {
|
||||
var p Pointer
|
||||
err := p.parse(jsonPointerString)
|
||||
|
||||
var err error
|
||||
|
||||
if jsonPointerString != emptyPointer {
|
||||
if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
|
||||
err = errors.New(invalidStart)
|
||||
} else {
|
||||
referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
|
||||
p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
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, swag.DefaultJSONNameProvider)
|
||||
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, swag.DefaultJSONNameProvider)
|
||||
return document, p.set(document, value, jsonname.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
// 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, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
// SetForToken gets 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, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
func isNil(input any) bool {
|
||||
if input == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
kind := reflect.TypeOf(input).Kind()
|
||||
switch kind { //nolint:exhaustive
|
||||
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
return reflect.ValueOf(input).IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind := rValue.Kind()
|
||||
if isNil(node) {
|
||||
return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken)
|
||||
}
|
||||
|
||||
switch typed := node.(type) {
|
||||
case JSONPointable:
|
||||
r, err := typed.JSONLookup(decodedToken)
|
||||
if err != nil {
|
||||
return nil, kind, err
|
||||
}
|
||||
return r, kind, nil
|
||||
case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect
|
||||
return getSingleImpl(*typed, decodedToken, nameProvider)
|
||||
}
|
||||
|
||||
switch kind { //nolint:exhaustive
|
||||
case reflect.Struct:
|
||||
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
|
||||
if !ok {
|
||||
return nil, kind, fmt.Errorf("object has no field %q", decodedToken)
|
||||
}
|
||||
fld := rValue.FieldByName(nm)
|
||||
return fld.Interface(), kind, nil
|
||||
|
||||
case reflect.Map:
|
||||
kv := reflect.ValueOf(decodedToken)
|
||||
mv := rValue.MapIndex(kv)
|
||||
|
||||
if mv.IsValid() {
|
||||
return mv.Interface(), kind, nil
|
||||
}
|
||||
return nil, kind, fmt.Errorf("object has no key %q", decodedToken)
|
||||
|
||||
case reflect.Slice:
|
||||
tokenIndex, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return nil, kind, err
|
||||
}
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
return elem.Interface(), kind, nil
|
||||
|
||||
default:
|
||||
return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func setSingleImpl(node, data any, decodedToken string, nameProvider *swag.NameProvider) error {
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
|
||||
if ns, ok := node.(JSONSetable); ok { // pointer impl
|
||||
return ns.JSONSet(decodedToken, data)
|
||||
}
|
||||
|
||||
if rValue.Type().Implements(jsonSetableType) {
|
||||
return node.(JSONSetable).JSONSet(decodedToken, data)
|
||||
}
|
||||
|
||||
switch rValue.Kind() { //nolint:exhaustive
|
||||
case reflect.Struct:
|
||||
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
|
||||
if !ok {
|
||||
return fmt.Errorf("object has no field %q", decodedToken)
|
||||
}
|
||||
fld := rValue.FieldByName(nm)
|
||||
if fld.IsValid() {
|
||||
fld.Set(reflect.ValueOf(data))
|
||||
}
|
||||
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
|
||||
}
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
if !elem.CanSet() {
|
||||
return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
|
||||
}
|
||||
elem.Set(reflect.ValueOf(data))
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid token reference %q", decodedToken)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *Pointer) get(node any, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
|
||||
|
||||
if nameProvider == nil {
|
||||
nameProvider = swag.DefaultJSONNameProvider
|
||||
}
|
||||
|
||||
kind := reflect.Invalid
|
||||
|
||||
// Full document when empty
|
||||
if len(p.referenceTokens) == 0 {
|
||||
return node, kind, nil
|
||||
}
|
||||
|
||||
for _, token := range p.referenceTokens {
|
||||
|
||||
decodedToken := Unescape(token)
|
||||
|
||||
r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
|
||||
if err != nil {
|
||||
return nil, knd, err
|
||||
}
|
||||
node = r
|
||||
}
|
||||
|
||||
rValue := reflect.ValueOf(node)
|
||||
kind = rValue.Kind()
|
||||
|
||||
return node, kind, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error {
|
||||
knd := reflect.ValueOf(node).Kind()
|
||||
|
||||
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
|
||||
return errors.New("only structs, pointers, maps and slices are supported for setting values")
|
||||
}
|
||||
|
||||
if nameProvider == nil {
|
||||
nameProvider = swag.DefaultJSONNameProvider
|
||||
}
|
||||
|
||||
// Full document when empty
|
||||
if len(p.referenceTokens) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind := rValue.Kind()
|
||||
|
||||
if rValue.Type().Implements(jsonPointableType) {
|
||||
r, err := node.(JSONPointable).JSONLookup(decodedToken)
|
||||
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.Ptr {
|
||||
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", decodedToken)
|
||||
}
|
||||
fld := rValue.FieldByName(nm)
|
||||
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
|
||||
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", decodedToken)
|
||||
}
|
||||
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
|
||||
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'", sLength, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
|
||||
node = elem.Addr().Interface()
|
||||
continue
|
||||
}
|
||||
node = elem.Interface()
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid token reference %q", decodedToken)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodedTokens returns the decoded tokens
|
||||
// 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
|
||||
}
|
||||
|
||||
pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
|
||||
|
||||
return pointerString
|
||||
return pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
|
||||
}
|
||||
|
||||
func (p *Pointer) Offset(document string) (int64, error) {
|
||||
@@ -404,15 +131,328 @@ func (p *Pointer) Offset(document string) (int64, error) {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid token %#v", tk)
|
||||
return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer)
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid token %#v", tk)
|
||||
return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer)
|
||||
}
|
||||
}
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// "Constructor", parses the given string JSON pointer.
|
||||
func (p *Pointer) parse(jsonPointerString string) error {
|
||||
if jsonPointerString == emptyPointer {
|
||||
return nil
|
||||
}
|
||||
|
||||
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) {
|
||||
if nameProvider == nil {
|
||||
nameProvider = jsonname.DefaultJSONNameProvider
|
||||
}
|
||||
|
||||
kind := reflect.Invalid
|
||||
|
||||
// full document when empty
|
||||
if len(p.referenceTokens) == 0 {
|
||||
return node, kind, nil
|
||||
}
|
||||
|
||||
for _, token := range p.referenceTokens {
|
||||
decodedToken := Unescape(token)
|
||||
|
||||
r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
|
||||
if err != nil {
|
||||
return nil, knd, err
|
||||
}
|
||||
node = r
|
||||
}
|
||||
|
||||
rValue := reflect.ValueOf(node)
|
||||
kind = rValue.Kind()
|
||||
|
||||
return node, kind, nil
|
||||
}
|
||||
|
||||
func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) 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(
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
if input == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
kind := reflect.TypeOf(input).Kind()
|
||||
switch kind {
|
||||
case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
return reflect.ValueOf(input).IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) {
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind := rValue.Kind()
|
||||
if isNil(node) {
|
||||
return nil, kind, fmt.Errorf("nil value has no field %q: %w", decodedToken, ErrPointer)
|
||||
}
|
||||
|
||||
switch typed := node.(type) {
|
||||
case JSONPointable:
|
||||
r, err := typed.JSONLookup(decodedToken)
|
||||
if err != nil {
|
||||
return nil, kind, err
|
||||
}
|
||||
return r, kind, nil
|
||||
case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect
|
||||
return getSingleImpl(*typed, decodedToken, nameProvider)
|
||||
}
|
||||
|
||||
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:
|
||||
kv := reflect.ValueOf(decodedToken)
|
||||
mv := rValue.MapIndex(kv)
|
||||
|
||||
if mv.IsValid() {
|
||||
return mv.Interface(), kind, nil
|
||||
}
|
||||
|
||||
return nil, kind, errNoKey(decodedToken)
|
||||
|
||||
case reflect.Slice:
|
||||
tokenIndex, err := strconv.Atoi(decodedToken)
|
||||
if err != nil {
|
||||
return nil, kind, errors.Join(err, ErrPointer)
|
||||
}
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
return nil, kind, errOutOfBounds(sLength, tokenIndex)
|
||||
}
|
||||
|
||||
elem := rValue.Index(tokenIndex)
|
||||
return elem.Interface(), kind, nil
|
||||
|
||||
default:
|
||||
return nil, kind, errInvalidReference(decodedToken)
|
||||
}
|
||||
}
|
||||
|
||||
func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.NameProvider) 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)
|
||||
}
|
||||
|
||||
if ns, ok := node.(JSONSetable); ok {
|
||||
return ns.JSONSet(decodedToken, data)
|
||||
}
|
||||
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
|
||||
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.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 errors.Join(err, ErrPointer)
|
||||
}
|
||||
|
||||
sLength := rValue.Len()
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
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)
|
||||
}
|
||||
|
||||
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 errInvalidReference(decodedToken)
|
||||
}
|
||||
}
|
||||
|
||||
func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) {
|
||||
for dec.More() {
|
||||
offset := dec.InputOffset()
|
||||
@@ -437,16 +477,17 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) {
|
||||
return offset, nil
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid token %#v", tk)
|
||||
return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer)
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("token reference %q not found", decodedToken)
|
||||
|
||||
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", decodedToken, err)
|
||||
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++ {
|
||||
@@ -470,12 +511,14 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) {
|
||||
}
|
||||
|
||||
if !dec.More() {
|
||||
return 0, fmt.Errorf("token reference %q not found", decodedToken)
|
||||
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() {
|
||||
@@ -497,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
|
||||
@@ -516,16 +560,24 @@ const (
|
||||
decRefTok1 = `/`
|
||||
)
|
||||
|
||||
// Unescape unescapes a json pointer reference token string to the original representation
|
||||
var (
|
||||
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.
|
||||
func Unescape(token string) string {
|
||||
step1 := strings.ReplaceAll(token, encRefTok1, decRefTok1)
|
||||
step2 := strings.ReplaceAll(step1, encRefTok0, decRefTok0)
|
||||
return step2
|
||||
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 {
|
||||
step1 := strings.ReplaceAll(token, decRefTok0, encRefTok0)
|
||||
step2 := strings.ReplaceAll(step1, decRefTok1, encRefTok1)
|
||||
return step2
|
||||
return decRefTokReplacer.Replace(token)
|
||||
}
|
||||
|
||||
+181
@@ -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"
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Set default charset
|
||||
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
|
||||
charset = utf-8
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
+62
-46
@@ -1,50 +1,66 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
gocyclo:
|
||||
min-complexity: 30
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 100
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 4
|
||||
paralleltest:
|
||||
ignore-missing: true
|
||||
version: "2"
|
||||
linters:
|
||||
enable-all: true
|
||||
default: all
|
||||
disable:
|
||||
- maligned
|
||||
- lll
|
||||
- gochecknoglobals
|
||||
- godox
|
||||
- gocognit
|
||||
- whitespace
|
||||
- wsl
|
||||
- depguard
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- scopelint
|
||||
- wrapcheck
|
||||
- exhaustivestruct
|
||||
- exhaustive
|
||||
- nlreturn
|
||||
- testpackage
|
||||
- gci
|
||||
- gofumpt
|
||||
- goerr113
|
||||
- gomnd
|
||||
- tparallel
|
||||
- nestif
|
||||
- godot
|
||||
- errorlint
|
||||
- varcheck
|
||||
- interfacer
|
||||
- deadcode
|
||||
- golint
|
||||
- ifshort
|
||||
- structcheck
|
||||
- nosnakecase
|
||||
- varnamelen
|
||||
- godox
|
||||
- exhaustruct
|
||||
- nlreturn
|
||||
- nonamedreturns
|
||||
- noinlineerr
|
||||
- paralleltest
|
||||
- recvcheck
|
||||
- testpackage
|
||||
- tparallel
|
||||
- varnamelen
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
- wsl
|
||||
- wsl_v5
|
||||
settings:
|
||||
dupl:
|
||||
threshold: 200
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 3
|
||||
cyclop:
|
||||
max-complexity: 20
|
||||
gocyclo:
|
||||
min-complexity: 20
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
default-case-required: true
|
||||
lll:
|
||||
line-length: 180
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
- gofumpt
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
# Maximum issues count per one linter.
|
||||
# Set to 0 to disable.
|
||||
# Default: 50
|
||||
max-issues-per-linter: 0
|
||||
# Maximum count of issues with the same text.
|
||||
# Set to 0 to disable.
|
||||
# Default: 3
|
||||
max-same-issues: 0
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# Contributors
|
||||
|
||||
- Repository: ['go-openapi/jsonreference']
|
||||
|
||||
| Total Contributors | Total Contributions |
|
||||
| --- | --- |
|
||||
| 9 | 68 |
|
||||
|
||||
| Username | All Time Contribution Count | All Commits |
|
||||
| --- | --- | --- |
|
||||
| @fredbi | 31 | https://github.com/go-openapi/jsonreference/commits?author=fredbi |
|
||||
| @casualjim | 25 | https://github.com/go-openapi/jsonreference/commits?author=casualjim |
|
||||
| @youyuanwu | 5 | https://github.com/go-openapi/jsonreference/commits?author=youyuanwu |
|
||||
| @olivierlemasle | 2 | https://github.com/go-openapi/jsonreference/commits?author=olivierlemasle |
|
||||
| @apelisse | 1 | https://github.com/go-openapi/jsonreference/commits?author=apelisse |
|
||||
| @gbjk | 1 | https://github.com/go-openapi/jsonreference/commits?author=gbjk |
|
||||
| @honza | 1 | https://github.com/go-openapi/jsonreference/commits?author=honza |
|
||||
| @Neo2308 | 1 | https://github.com/go-openapi/jsonreference/commits?author=Neo2308 |
|
||||
| @erraggy | 1 | https://github.com/go-openapi/jsonreference/commits?author=erraggy |
|
||||
|
||||
_this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_
|
||||
+39
@@ -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.
|
||||
+91
-7
@@ -1,15 +1,99 @@
|
||||
# gojsonreference [](https://travis-ci.org/go-openapi/jsonreference) [](https://codecov.io/gh/go-openapi/jsonreference) [](https://slackin.goswagger.io)
|
||||
# jsonreference
|
||||
|
||||
[](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [](http://godoc.org/github.com/go-openapi/jsonreference)
|
||||
An implementation of JSON Reference - Go language
|
||||
<!-- 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 Reference for golang.
|
||||
|
||||
## Status
|
||||
Feature complete. Stable API
|
||||
|
||||
API is stable.
|
||||
|
||||
## Import this library in your project
|
||||
|
||||
```cmd
|
||||
go get github.com/go-openapi/jsonreference
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
https://github.com/go-openapi/jsonpointer
|
||||
|
||||
* https://github.com/go-openapi/jsonpointer
|
||||
|
||||
## Basic usage
|
||||
|
||||
## Change log
|
||||
|
||||
See <https://github.com/go-openapi/jsonreference/releases>
|
||||
|
||||
## References
|
||||
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
|
||||
|
||||
http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03
|
||||
* http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07
|
||||
* http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03
|
||||
|
||||
## Licensing
|
||||
|
||||
This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE).
|
||||
|
||||
See the license [NOTICE](./NOTICE), which recalls the licensing terms of all the pieces of software
|
||||
on top of which it has been built.
|
||||
|
||||
## 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/jsonreference/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/jsonreference/actions/workflows/go-test.yml/badge.svg
|
||||
[test-url]: https://github.com/go-openapi/jsonreference/actions/workflows/go-test.yml
|
||||
[cov-badge]: https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg
|
||||
[cov-url]: https://codecov.io/gh/go-openapi/jsonreference
|
||||
[vuln-scan-badge]: https://github.com/go-openapi/jsonreference/actions/workflows/scanner.yml/badge.svg
|
||||
[vuln-scan-url]: https://github.com/go-openapi/jsonreference/actions/workflows/scanner.yml
|
||||
[codeql-badge]: https://github.com/go-openapi/jsonreference/actions/workflows/codeql.yml/badge.svg
|
||||
[codeql-url]: https://github.com/go-openapi/jsonreference/actions/workflows/codeql.yml
|
||||
<!-- Badges: release & docker images -->
|
||||
[release-badge]: https://badge.fury.io/gh/go-openapi%2Fjsonreference.svg
|
||||
[release-url]: https://badge.fury.io/gh/go-openapi%2Fjsonreference
|
||||
[gomod-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonreference.svg
|
||||
[gomod-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonreference
|
||||
<!-- Badges: code quality -->
|
||||
[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/jsonreference
|
||||
[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/jsonreference
|
||||
[codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/jsonreference
|
||||
[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/jsonreference
|
||||
<!-- 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/jsonreference
|
||||
[godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonreference
|
||||
[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/jsonreference/?tab=Apache-2.0-1-ov-file#readme
|
||||
<!-- Badges: others & stats -->
|
||||
[goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/jsonreference
|
||||
[goversion-url]: https://github.com/go-openapi/jsonreference/blob/master/go.mod
|
||||
[top-badge]: https://img.shields.io/github/languages/top/go-openapi/jsonreference
|
||||
[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/jsonreference/latest
|
||||
|
||||
+19
@@ -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".
|
||||
+8
-3
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
@@ -11,9 +14,11 @@ const (
|
||||
defaultHTTPSPort = ":443"
|
||||
)
|
||||
|
||||
// Regular expressions used by the normalizations
|
||||
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
|
||||
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
|
||||
// Regular expressions used by the normalizations.
|
||||
var (
|
||||
rxPort = regexp.MustCompile(`(:\d+)/?$`)
|
||||
rxDupSlashes = regexp.MustCompile(`/{2,}`)
|
||||
)
|
||||
|
||||
// NormalizeURL will normalize the specified URL
|
||||
// This was added to replace a previous call to the no longer maintained purell library:
|
||||
|
||||
+47
-70
@@ -1,27 +1,5 @@
|
||||
// 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 jsonreference
|
||||
// repository-desc An implementation of JSON Reference - Go language
|
||||
//
|
||||
// description Main and unique file.
|
||||
//
|
||||
// created 26-02-2013
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonreference
|
||||
|
||||
@@ -38,26 +16,9 @@ const (
|
||||
fragmentRune = `#`
|
||||
)
|
||||
|
||||
// New creates a new reference for the given string
|
||||
func New(jsonReferenceString string) (Ref, error) {
|
||||
var ErrChildURL = errors.New("child url is nil")
|
||||
|
||||
var r Ref
|
||||
err := r.parse(jsonReferenceString)
|
||||
return r, err
|
||||
|
||||
}
|
||||
|
||||
// MustCreateRef parses the ref string and panics when it's invalid.
|
||||
// Use the New method for a version that returns an error
|
||||
func MustCreateRef(ref string) Ref {
|
||||
r, err := New(ref)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Ref represents a json reference object
|
||||
// Ref represents a json reference object.
|
||||
type Ref struct {
|
||||
referenceURL *url.URL
|
||||
referencePointer jsonpointer.Pointer
|
||||
@@ -69,19 +30,36 @@ type Ref struct {
|
||||
HasFullFilePath bool
|
||||
}
|
||||
|
||||
// GetURL gets the URL for this reference
|
||||
// New creates a new reference for the given string.
|
||||
func New(jsonReferenceString string) (Ref, error) {
|
||||
var r Ref
|
||||
err := r.parse(jsonReferenceString)
|
||||
return r, err
|
||||
}
|
||||
|
||||
// MustCreateRef parses the ref string and panics when it's invalid.
|
||||
// Use the New method for a version that returns an error.
|
||||
func MustCreateRef(ref string) Ref {
|
||||
r, err := New(ref)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// GetURL gets the URL for this reference.
|
||||
func (r *Ref) GetURL() *url.URL {
|
||||
return r.referenceURL
|
||||
}
|
||||
|
||||
// GetPointer gets the json pointer for this reference
|
||||
// GetPointer gets the json pointer for this reference.
|
||||
func (r *Ref) GetPointer() *jsonpointer.Pointer {
|
||||
return &r.referencePointer
|
||||
}
|
||||
|
||||
// String returns the best version of the url for this reference
|
||||
// String returns the best version of the url for this reference.
|
||||
func (r *Ref) String() string {
|
||||
|
||||
if r.referenceURL != nil {
|
||||
return r.referenceURL.String()
|
||||
}
|
||||
@@ -93,7 +71,7 @@ func (r *Ref) String() string {
|
||||
return r.referencePointer.String()
|
||||
}
|
||||
|
||||
// IsRoot returns true if this reference is a root document
|
||||
// IsRoot returns true if this reference is a root document.
|
||||
func (r *Ref) IsRoot() bool {
|
||||
return r.referenceURL != nil &&
|
||||
!r.IsCanonical() &&
|
||||
@@ -101,14 +79,32 @@ func (r *Ref) IsRoot() bool {
|
||||
r.referenceURL.Fragment == ""
|
||||
}
|
||||
|
||||
// IsCanonical returns true when this pointer starts with http(s):// or file://
|
||||
// IsCanonical returns true when this pointer starts with http(s):// or file://.
|
||||
func (r *Ref) IsCanonical() bool {
|
||||
return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL)
|
||||
}
|
||||
|
||||
// "Constructor", parses the given string JSON reference
|
||||
func (r *Ref) parse(jsonReferenceString string) error {
|
||||
// Inherits creates a new reference from a parent and a child
|
||||
// If the child cannot inherit from the parent, an error is returned.
|
||||
func (r *Ref) Inherits(child Ref) (*Ref, error) {
|
||||
childURL := child.GetURL()
|
||||
parentURL := r.GetURL()
|
||||
if childURL == nil {
|
||||
return nil, ErrChildURL
|
||||
}
|
||||
if parentURL == nil {
|
||||
return &child, nil
|
||||
}
|
||||
|
||||
ref, err := New(parentURL.ResolveReference(childURL).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ref, nil
|
||||
}
|
||||
|
||||
// "Constructor", parses the given string JSON reference.
|
||||
func (r *Ref) parse(jsonReferenceString string) error {
|
||||
parsed, err := url.Parse(jsonReferenceString)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -137,22 +133,3 @@ func (r *Ref) parse(jsonReferenceString string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Inherits creates a new reference from a parent and a child
|
||||
// If the child cannot inherit from the parent, an error is returned
|
||||
func (r *Ref) Inherits(child Ref) (*Ref, error) {
|
||||
childURL := child.GetURL()
|
||||
parentURL := r.GetURL()
|
||||
if childURL == nil {
|
||||
return nil, errors.New("child url is nil")
|
||||
}
|
||||
if parentURL == nil {
|
||||
return &child, nil
|
||||
}
|
||||
|
||||
ref, err := New(parentURL.ResolveReference(childURL).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ref, nil
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
ignore:
|
||||
- jsonutils/fixtures_test
|
||||
- jsonutils/adapters/ifaces/mocks
|
||||
- jsonutils/adapters/testintegration/benchmarks
|
||||
+2
@@ -3,3 +3,5 @@ vendor
|
||||
Godeps
|
||||
.idea
|
||||
*.out
|
||||
.mcp.json
|
||||
.claude/
|
||||
|
||||
+73
-55
@@ -1,60 +1,78 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 45
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 200
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 3
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
enable-all: true
|
||||
default: all
|
||||
disable:
|
||||
- maligned
|
||||
- lll
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
- funlen
|
||||
- godox
|
||||
- gocognit
|
||||
- whitespace
|
||||
- wsl
|
||||
- wrapcheck
|
||||
- testpackage
|
||||
- nlreturn
|
||||
- gomnd
|
||||
- exhaustivestruct
|
||||
- goerr113
|
||||
- errorlint
|
||||
- nestif
|
||||
- godot
|
||||
- gofumpt
|
||||
- paralleltest
|
||||
- tparallel
|
||||
- thelper
|
||||
- ifshort
|
||||
- exhaustruct
|
||||
- varnamelen
|
||||
- gci
|
||||
- cyclop
|
||||
- depguard
|
||||
- errchkjson
|
||||
- inamedparam
|
||||
- nonamedreturns
|
||||
- musttag
|
||||
- ireturn
|
||||
- errorlint
|
||||
- exhaustruct
|
||||
- forcetypeassert
|
||||
- cyclop
|
||||
# deprecated linters
|
||||
- deadcode
|
||||
- interfacer
|
||||
- scopelint
|
||||
- varcheck
|
||||
- structcheck
|
||||
- golint
|
||||
- nosnakecase
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- godot
|
||||
- godox
|
||||
- gomoddirectives
|
||||
- gosmopolitan
|
||||
- inamedparam
|
||||
- intrange
|
||||
- ireturn
|
||||
- lll
|
||||
- musttag
|
||||
- modernize
|
||||
- nestif
|
||||
- nlreturn
|
||||
- nonamedreturns
|
||||
- noinlineerr
|
||||
- paralleltest
|
||||
- recvcheck
|
||||
- testpackage
|
||||
- thelper
|
||||
- tagliatelle
|
||||
- tparallel
|
||||
- unparam
|
||||
- varnamelen
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
- wsl
|
||||
- wsl_v5
|
||||
settings:
|
||||
dupl:
|
||||
threshold: 200
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 3
|
||||
gocyclo:
|
||||
min-complexity: 45
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
# Maximum issues count per one linter.
|
||||
# Set to 0 to disable.
|
||||
# Default: 50
|
||||
max-issues-per-linter: 0
|
||||
# Maximum count of issues with the same text.
|
||||
# Set to 0 to disable.
|
||||
# Default: 3
|
||||
max-same-issues: 0
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
all: false
|
||||
dir: '{{.InterfaceDir}}'
|
||||
filename: mocks_test.go
|
||||
force-file-write: true
|
||||
formatter: goimports
|
||||
include-auto-generated: false
|
||||
log-level: info
|
||||
structname: '{{.Mock}}{{.InterfaceName}}'
|
||||
pkgname: '{{.SrcPackageName}}'
|
||||
recursive: false
|
||||
require-template-schema-exists: true
|
||||
template: matryer
|
||||
template-schema: '{{.Template}}.schema.json'
|
||||
packages:
|
||||
github.com/go-openapi/swag/jsonutils/adapters/ifaces:
|
||||
config:
|
||||
dir: jsonutils/adapters/ifaces/mocks
|
||||
filename: mocks.go
|
||||
pkgname: 'mocks'
|
||||
force-file-write: true
|
||||
all: true
|
||||
github.com/go-openapi/swag/jsonutils/adapters/testintegration:
|
||||
config:
|
||||
inpackage: true
|
||||
dir: jsonutils/adapters/testintegration
|
||||
force-file-write: true
|
||||
all: true
|
||||
interfaces:
|
||||
EJMarshaler:
|
||||
EJUnmarshaler:
|
||||
+4
-2
@@ -23,7 +23,9 @@ include:
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
|
||||
advances
|
||||
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
@@ -55,7 +57,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at ivan+abuse@flanders.co.nz. All
|
||||
reported by contacting the project team at <ivan+abuse@flanders.co.nz>. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
@@ -68,7 +70,7 @@ members of the project's leadership.
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
available at [<http://contributor-covenant.org/version/1/4>][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Contributors
|
||||
|
||||
- Repository: ['go-openapi/swag']
|
||||
|
||||
| Total Contributors | Total Contributions |
|
||||
| --- | --- |
|
||||
| 24 | 235 |
|
||||
|
||||
| Username | All Time Contribution Count | All Commits |
|
||||
| --- | --- | --- |
|
||||
| @fredbi | 105 | <https://github.com/go-openapi/swag/commits?author=fredbi> |
|
||||
| @casualjim | 98 | <https://github.com/go-openapi/swag/commits?author=casualjim> |
|
||||
| @alexandear | 4 | <https://github.com/go-openapi/swag/commits?author=alexandear> |
|
||||
| @orisano | 3 | <https://github.com/go-openapi/swag/commits?author=orisano> |
|
||||
| @reinerRubin | 2 | <https://github.com/go-openapi/swag/commits?author=reinerRubin> |
|
||||
| @n-inja | 2 | <https://github.com/go-openapi/swag/commits?author=n-inja> |
|
||||
| @nitinmohan87 | 2 | <https://github.com/go-openapi/swag/commits?author=nitinmohan87> |
|
||||
| @Neo2308 | 2 | <https://github.com/go-openapi/swag/commits?author=Neo2308> |
|
||||
| @michaelbowler-form3 | 2 | <https://github.com/go-openapi/swag/commits?author=michaelbowler-form3> |
|
||||
| @ujjwalsh | 1 | <https://github.com/go-openapi/swag/commits?author=ujjwalsh> |
|
||||
| @griffin-stewie | 1 | <https://github.com/go-openapi/swag/commits?author=griffin-stewie> |
|
||||
| @POD666 | 1 | <https://github.com/go-openapi/swag/commits?author=POD666> |
|
||||
| @pytlesk4 | 1 | <https://github.com/go-openapi/swag/commits?author=pytlesk4> |
|
||||
| @shirou | 1 | <https://github.com/go-openapi/swag/commits?author=shirou> |
|
||||
| @seanprince | 1 | <https://github.com/go-openapi/swag/commits?author=seanprince> |
|
||||
| @petrkotas | 1 | <https://github.com/go-openapi/swag/commits?author=petrkotas> |
|
||||
| @mszczygiel | 1 | <https://github.com/go-openapi/swag/commits?author=mszczygiel> |
|
||||
| @sosiska | 1 | <https://github.com/go-openapi/swag/commits?author=sosiska> |
|
||||
| @kzys | 1 | <https://github.com/go-openapi/swag/commits?author=kzys> |
|
||||
| @faguirre1 | 1 | <https://github.com/go-openapi/swag/commits?author=faguirre1> |
|
||||
| @posener | 1 | <https://github.com/go-openapi/swag/commits?author=posener> |
|
||||
| @diego-fu-hs | 1 | <https://github.com/go-openapi/swag/commits?author=diego-fu-hs> |
|
||||
| @davidalpert | 1 | <https://github.com/go-openapi/swag/commits?author=davidalpert> |
|
||||
| @Xe | 1 | <https://github.com/go-openapi/swag/commits?author=Xe> |
|
||||
|
||||
_this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_
|
||||
+216
-15
@@ -1,23 +1,224 @@
|
||||
# Swag [](https://github.com/go-openapi/swag/actions?query=workflow%3A"go+test") [](https://codecov.io/gh/go-openapi/swag)
|
||||
# Swag
|
||||
|
||||
[](https://slackin.goswagger.io)
|
||||
[](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE)
|
||||
[](https://pkg.go.dev/github.com/go-openapi/swag)
|
||||
[](https://goreportcard.com/report/github.com/go-openapi/swag)
|
||||
<!-- 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] [![Discord Channel][discord-badge]][discord-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge]
|
||||
|
||||
Contains a bunch of helper functions for go-openapi and go-swagger projects.
|
||||
---
|
||||
|
||||
A bunch of helper functions for go-openapi and go-swagger projects.
|
||||
|
||||
You may also use it standalone for your projects.
|
||||
|
||||
* convert between value and pointers for builtin types
|
||||
* convert from string to builtin types (wraps strconv)
|
||||
* fast json concatenation
|
||||
* search in path
|
||||
* load from file or http
|
||||
* name mangling
|
||||
> **NOTE**
|
||||
> `swag` is one of the foundational building blocks of the go-openapi initiative.
|
||||
>
|
||||
> Most repositories in `github.com/go-openapi/...` depend on it in some way.
|
||||
> And so does our CLI tool `github.com/go-swagger/go-swagger`,
|
||||
> as well as the code generated by this tool.
|
||||
|
||||
* [Contents](#contents)
|
||||
* [Dependencies](#dependencies)
|
||||
* [Change log](#change-log)
|
||||
* [Licensing](#licensing)
|
||||
* [Note to contributors](#note-to-contributors)
|
||||
* [Roadmap](#roadmap)
|
||||
|
||||
This repo has only few dependencies outside of the standard library:
|
||||
## Announcements
|
||||
|
||||
* YAML utilities depend on `gopkg.in/yaml.v3`
|
||||
* `github.com/mailru/easyjson v0.7.7`
|
||||
* **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**
|
||||
|
||||
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]
|
||||
|
||||
## Status
|
||||
|
||||
API is stable.
|
||||
|
||||
## Import this library in your project
|
||||
|
||||
```cmd
|
||||
go get github.com/go-openapi/swag/{module}
|
||||
```
|
||||
|
||||
Or for backward compatibility:
|
||||
|
||||
```cmd
|
||||
go get github.com/go-openapi/swag
|
||||
```
|
||||
|
||||
## Contents
|
||||
|
||||
`go-openapi/swag` exposes a collection of relatively independent modules.
|
||||
|
||||
Moving forward, no additional feature will be added to the `swag` API directly at the root package level,
|
||||
which remains there for backward-compatibility purposes. All exported top-level features are now deprecated.
|
||||
|
||||
Child modules will continue to evolve and some new ones may be added in the future.
|
||||
|
||||
| Module | Content | Main features |
|
||||
|---------------|---------|---------------|
|
||||
| `cmdutils` | utilities to work with CLIs ||
|
||||
| `conv` | type conversion utilities | convert between values and pointers for any types<br />convert from string to builtin types (wraps `strconv`)<br />require `./typeutils` (test dependency)<br /> |
|
||||
| `fileutils` | file utilities | |
|
||||
| `jsonname` | JSON utilities | infer JSON names from `go` properties<br /> |
|
||||
| `jsonutils` | JSON utilities | fast json concatenation<br />read and write JSON from and to dynamic `go` data structures<br />~require `github.com/mailru/easyjson`~<br /> |
|
||||
| `loading` | file loading | load from file or http<br />require `./yamlutils`<br /> |
|
||||
| `mangling` | safe name generation | name mangling for `go`<br /> |
|
||||
| `netutils` | networking utilities | host, port from address<br /> |
|
||||
| `stringutils` | `string` utilities | search in slice (with case-insensitive)<br />split/join query parameters as arrays<br /> |
|
||||
| `typeutils` | `go` types utilities | check the zero value for any type<br />safe check for a nil value<br /> |
|
||||
| `yamlutils` | YAML utilities | converting YAML to JSON<br />loading YAML into a dynamic YAML document<br />maintaining the original order of keys in YAML objects<br />require `./jsonutils`<br />~require `github.com/mailru/easyjson`~<br />require `go.yaml.in/yaml/v3`<br /> |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
The root module `github.com/go-openapi/swag` at the repo level maintains a few
|
||||
dependencies outside of the standard library.
|
||||
|
||||
* YAML utilities depend on `go.yaml.in/yaml/v3`
|
||||
* JSON utilities depend on their registered adapter module:
|
||||
* by default, only the standard library is used
|
||||
* `github.com/mailru/easyjson` is now only a dependency for module
|
||||
`github.com/go-openapi/swag/jsonutils/adapters/easyjson/json`,
|
||||
for users willing to import that module.
|
||||
* integration tests and benchmarks use all the dependencies are published as their own module
|
||||
* other dependencies are test dependencies drawn from `github.com/stretchr/testify`
|
||||
|
||||
## Usage
|
||||
|
||||
**How to explicitly register a dependency at runtime**?
|
||||
|
||||
The following would maintain how JSON utilities proposed by `swag` used work, up to `v0.24.1`.
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/go-openapi/swag/jsonutils/adapters"
|
||||
easyjson "github.com/go-openapi/swag/jsonutils/adapters/easyjson/json"
|
||||
)
|
||||
|
||||
func init() {
|
||||
easyjson.Register(adapters.Registry)
|
||||
}
|
||||
```
|
||||
|
||||
Subsequent calls to `jsonutils.ReadJSON()` or `jsonutils.WriteJSON()` will switch to `easyjson`
|
||||
whenever the passed data structures implement the `easyjson.Unmarshaler` or `easyjson.Marshaler` respectively,
|
||||
or fallback to the standard library.
|
||||
|
||||
For more details, you may also look at our
|
||||
[integration tests](jsonutils/adapters/testintegration/integration_suite_test.go#29).
|
||||
|
||||
---
|
||||
|
||||
## Note to contributors
|
||||
|
||||
All kinds of contributions are welcome.
|
||||
|
||||
This repo is a go mono-repo. See [docs](docs/MAINTAINERS.md).
|
||||
|
||||
More general guidelines are available [here](.github/CONTRIBUTING.md).
|
||||
|
||||
## Roadmap
|
||||
|
||||
See the current [TODO list](docs/TODOS.md)
|
||||
|
||||
## Change log
|
||||
|
||||
See <https://github.com/go-openapi/swag/releases>
|
||||
|
||||
For pre-v0.26.0 releases, see [release notes](./docs/NOTES.md).
|
||||
|
||||
**What coming next?**
|
||||
|
||||
Moving forward, we want to :
|
||||
|
||||
* provide an implementation of the JSON adapter based on `encoding/json/v2`, for `go1.25` builds.
|
||||
* provide similar implementations for `goccy/go-json` and `jsoniterator/go`, and perhaps some other
|
||||
similar libraries may be interesting too.
|
||||
|
||||
<!--
|
||||
|
||||
## References
|
||||
|
||||
-->
|
||||
|
||||
## Licensing
|
||||
|
||||
This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE).
|
||||
|
||||
<!--
|
||||
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
|
||||
|
||||
-->
|
||||
|
||||
## 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/swag/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/swag/actions/workflows/go-test.yml/badge.svg
|
||||
[test-url]: https://github.com/go-openapi/swag/actions/workflows/go-test.yml
|
||||
[cov-badge]: https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg
|
||||
[cov-url]: https://codecov.io/gh/go-openapi/swag
|
||||
[vuln-scan-badge]: https://github.com/go-openapi/swag/actions/workflows/scanner.yml/badge.svg
|
||||
[vuln-scan-url]: https://github.com/go-openapi/swag/actions/workflows/scanner.yml
|
||||
[codeql-badge]: https://github.com/go-openapi/swag/actions/workflows/codeql.yml/badge.svg
|
||||
[codeql-url]: https://github.com/go-openapi/swag/actions/workflows/codeql.yml
|
||||
<!-- Badges: release & docker images -->
|
||||
[release-badge]: https://badge.fury.io/gh/go-openapi%2Fswag.svg
|
||||
[release-url]: https://badge.fury.io/gh/go-openapi%2Fswag
|
||||
[gomod-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fswag.svg
|
||||
[gomod-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fswag
|
||||
<!-- Badges: code quality -->
|
||||
[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/swag
|
||||
[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/swag
|
||||
[codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/swag
|
||||
[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/swag
|
||||
<!-- 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/swag
|
||||
[godoc-url]: http://pkg.go.dev/github.com/go-openapi/swag
|
||||
[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
|
||||
|
||||
<!-- Badges: license & compliance -->
|
||||
[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg
|
||||
[license-url]: https://github.com/go-openapi/swag/?tab=Apache-2.0-1-ov-file#readme
|
||||
<!-- Badges: others & stats -->
|
||||
[goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/swag
|
||||
[goversion-url]: https://github.com/go-openapi/swag/blob/master/go.mod
|
||||
[top-badge]: https://img.shields.io/github/languages/top/go-openapi/swag
|
||||
[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/swag/latest
|
||||
|
||||
+19
@@ -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.25.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".
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmdutils
|
||||
|
||||
// CommandLineOptionsGroup represents a group of user-defined command line options.
|
||||
//
|
||||
// This is for instance used to configure command line arguments in API servers generated by go-swagger.
|
||||
type CommandLineOptionsGroup struct {
|
||||
ShortDescription string
|
||||
LongDescription string
|
||||
Options any
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package cmdutils brings helpers for CLIs produced by go-openapi
|
||||
package cmdutils
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
|
||||
import "github.com/go-openapi/swag/cmdutils"
|
||||
|
||||
// CommandLineOptionsGroup represents a group of user-defined command line options.
|
||||
//
|
||||
// Deprecated: use [cmdutils.CommandLineOptionsGroup] instead.
|
||||
type CommandLineOptionsGroup = cmdutils.CommandLineOptionsGroup
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package conv
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
|
||||
const (
|
||||
maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
||||
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
||||
epsilon float64 = 1e-9
|
||||
)
|
||||
|
||||
// IsFloat64AJSONInteger allows for integers [-2^53, 2^53-1] inclusive.
|
||||
func IsFloat64AJSONInteger(f float64) bool {
|
||||
if math.IsNaN(f) || math.IsInf(f, 0) || f < minJSONFloat || f > maxJSONFloat {
|
||||
return false
|
||||
}
|
||||
rounded := math.Round(f)
|
||||
if f == rounded {
|
||||
return true
|
||||
}
|
||||
if rounded == 0 { // f = 0.0 exited above
|
||||
return false
|
||||
}
|
||||
|
||||
diff := math.Abs(f - rounded)
|
||||
if diff == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// relative error Abs{f - Round(f)) / Round(f)} < ε ; Round(f)
|
||||
return diff < epsilon*math.Abs(rounded)
|
||||
}
|
||||
|
||||
// ConvertFloat turns a string into a float numerical value.
|
||||
func ConvertFloat[T Float](str string) (T, error) {
|
||||
var v T
|
||||
f, err := strconv.ParseFloat(str, bitsize(v))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return T(f), nil
|
||||
}
|
||||
|
||||
// ConvertInteger turns a string into a signed integer.
|
||||
func ConvertInteger[T Signed](str string) (T, error) {
|
||||
var v T
|
||||
f, err := strconv.ParseInt(str, 10, bitsize(v))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return T(f), nil
|
||||
}
|
||||
|
||||
// ConvertUinteger turns a string into an unsigned integer.
|
||||
func ConvertUinteger[T Unsigned](str string) (T, error) {
|
||||
var v T
|
||||
f, err := strconv.ParseUint(str, 10, bitsize(v))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return T(f), nil
|
||||
}
|
||||
|
||||
// ConvertBool turns a string into a boolean.
|
||||
//
|
||||
// It supports a few more "true" strings than [strconv.ParseBool]:
|
||||
//
|
||||
// - it is not case sensitive ("trUe" or "FalsE" work)
|
||||
// - "ok", "yes", "y", "on", "selected", "checked", "enabled" are all true
|
||||
// - everything that is not true is false: there is never an actual error returned
|
||||
func ConvertBool(str string) (bool, error) {
|
||||
switch strings.ToLower(str) {
|
||||
case "true",
|
||||
"1",
|
||||
"yes",
|
||||
"ok",
|
||||
"y",
|
||||
"on",
|
||||
"selected",
|
||||
"checked",
|
||||
"t",
|
||||
"enabled":
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertFloat32 turns a string into a float32.
|
||||
func ConvertFloat32(str string) (float32, error) { return ConvertFloat[float32](str) }
|
||||
|
||||
// ConvertFloat64 turns a string into a float64
|
||||
func ConvertFloat64(str string) (float64, error) { return ConvertFloat[float64](str) }
|
||||
|
||||
// ConvertInt8 turns a string into an int8
|
||||
func ConvertInt8(str string) (int8, error) { return ConvertInteger[int8](str) }
|
||||
|
||||
// ConvertInt16 turns a string into an int16
|
||||
func ConvertInt16(str string) (int16, error) {
|
||||
i, err := strconv.ParseInt(str, 10, 16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int16(i), nil
|
||||
}
|
||||
|
||||
// ConvertInt32 turns a string into an int32
|
||||
func ConvertInt32(str string) (int32, error) {
|
||||
i, err := strconv.ParseInt(str, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(i), nil
|
||||
}
|
||||
|
||||
// ConvertInt64 turns a string into an int64
|
||||
func ConvertInt64(str string) (int64, error) {
|
||||
return strconv.ParseInt(str, 10, 64)
|
||||
}
|
||||
|
||||
// ConvertUint8 turns a string into an uint8
|
||||
func ConvertUint8(str string) (uint8, error) {
|
||||
i, err := strconv.ParseUint(str, 10, 8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint8(i), nil
|
||||
}
|
||||
|
||||
// ConvertUint16 turns a string into an uint16
|
||||
func ConvertUint16(str string) (uint16, error) {
|
||||
i, err := strconv.ParseUint(str, 10, 16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint16(i), nil
|
||||
}
|
||||
|
||||
// ConvertUint32 turns a string into an uint32
|
||||
func ConvertUint32(str string) (uint32, error) {
|
||||
i, err := strconv.ParseUint(str, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(i), nil
|
||||
}
|
||||
|
||||
// ConvertUint64 turns a string into an uint64
|
||||
func ConvertUint64(str string) (uint64, error) {
|
||||
return strconv.ParseUint(str, 10, 64)
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package conv
|
||||
|
||||
// Unlicensed credits (idea, concept)
|
||||
//
|
||||
// The idea to convert values to pointers and the other way around, was inspired, eons ago, by the aws go sdk.
|
||||
//
|
||||
// Nowadays, all sensible API sdk's expose a similar functionality.
|
||||
|
||||
// Pointer returns a pointer to the value passed in.
|
||||
func Pointer[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Value returns a shallow copy of the value of the pointer passed in.
|
||||
//
|
||||
// If the pointer is nil, the returned value is the zero value.
|
||||
func Value[T any](v *T) T {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
|
||||
// PointerSlice converts a slice of values into a slice of pointers.
|
||||
func PointerSlice[T any](src []T) []*T {
|
||||
dst := make([]*T, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// ValueSlice converts a slice of pointers into a slice of values.
|
||||
//
|
||||
// nil elements are zero values.
|
||||
func ValueSlice[T any](src []*T) []T {
|
||||
dst := make([]T, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// PointerMap converts a map of values into a map of pointers.
|
||||
func PointerMap[K comparable, T any](src map[K]T) map[K]*T {
|
||||
dst := make(map[K]*T)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// ValueMap converts a map of pointers into a map of values.
|
||||
//
|
||||
// nil elements are skipped.
|
||||
func ValueMap[K comparable, T any](src map[K]*T) map[K]T {
|
||||
dst := make(map[K]T)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package conv exposes utilities to convert types.
|
||||
//
|
||||
// The Convert and Format families of functions are essentially a shorthand to [strconv] functions,
|
||||
// using the decimal representation of numbers.
|
||||
//
|
||||
// Features:
|
||||
//
|
||||
// - from string representation to value ("Convert*") and reciprocally ("Format*")
|
||||
// - from pointer to value ([Value]) and reciprocally ([Pointer])
|
||||
// - from slice of values to slice of pointers ([PointerSlice]) and reciprocally ([ValueSlice])
|
||||
// - from map of values to map of pointers ([PointerMap]) and reciprocally ([ValueMap])
|
||||
package conv
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package conv
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// FormatInteger turns an integer type into a string.
|
||||
func FormatInteger[T Signed](value T) string {
|
||||
return strconv.FormatInt(int64(value), 10)
|
||||
}
|
||||
|
||||
// FormatUinteger turns an unsigned integer type into a string.
|
||||
func FormatUinteger[T Unsigned](value T) string {
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
}
|
||||
|
||||
// FormatFloat turns a floating point numerical value into a string.
|
||||
func FormatFloat[T Float](value T) string {
|
||||
return strconv.FormatFloat(float64(value), 'f', -1, bitsize(value))
|
||||
}
|
||||
|
||||
// FormatBool turns a boolean into a string.
|
||||
func FormatBool(value bool) string {
|
||||
return strconv.FormatBool(value)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package conv
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// bitsize returns the size in bits of a type.
|
||||
//
|
||||
// NOTE: [unsafe.SizeOf] simply returns the size in bytes of the value.
|
||||
// For primitive types T, the generic stencil is precompiled and this value
|
||||
// is resolved at compile time, resulting in an immediate call to [strconv.ParseFloat].
|
||||
//
|
||||
// We may leave up to the go compiler to simplify this function into a
|
||||
// constant value, which happens in practice at least for primitive types
|
||||
// (e.g. numerical types).
|
||||
func bitsize[T Numerical](value T) int {
|
||||
const bitsPerByte = 8
|
||||
return int(unsafe.Sizeof(value)) * bitsPerByte
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package conv
|
||||
|
||||
type (
|
||||
// these type constraints are redefined after golang.org/x/exp/constraints,
|
||||
// because importing that package causes an undesired go upgrade.
|
||||
|
||||
// Signed integer types, cf. [golang.org/x/exp/constraints.Signed]
|
||||
Signed interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
// Unsigned integer types, cf. [golang.org/x/exp/constraints.Unsigned]
|
||||
Unsigned interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
// Float numerical types, cf. [golang.org/x/exp/constraints.Float]
|
||||
Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
// Numerical types
|
||||
Numerical interface {
|
||||
Signed | Unsigned | Float
|
||||
}
|
||||
)
|
||||
+486
@@ -0,0 +1,486 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/swag/conv"
|
||||
)
|
||||
|
||||
// IsFloat64AJSONInteger allows for integers [-2^53, 2^53-1] inclusive.
|
||||
//
|
||||
// Deprecated: use [conv.IsFloat64AJSONInteger] instead.
|
||||
func IsFloat64AJSONInteger(f float64) bool { return conv.IsFloat64AJSONInteger(f) }
|
||||
|
||||
// ConvertBool turns a string into a boolean.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertBool] instead.
|
||||
func ConvertBool(str string) (bool, error) { return conv.ConvertBool(str) }
|
||||
|
||||
// ConvertFloat32 turns a string into a float32.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertFloat32] instead. Alternatively, you may use the generic version [conv.ConvertFloat].
|
||||
func ConvertFloat32(str string) (float32, error) { return conv.ConvertFloat[float32](str) }
|
||||
|
||||
// ConvertFloat64 turns a string into a float64.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertFloat64] instead. Alternatively, you may use the generic version [conv.ConvertFloat].
|
||||
func ConvertFloat64(str string) (float64, error) { return conv.ConvertFloat[float64](str) }
|
||||
|
||||
// ConvertInt8 turns a string into an int8.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertInt8] instead. Alternatively, you may use the generic version [conv.ConvertInteger].
|
||||
func ConvertInt8(str string) (int8, error) { return conv.ConvertInteger[int8](str) }
|
||||
|
||||
// ConvertInt16 turns a string into an int16.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertInt16] instead. Alternatively, you may use the generic version [conv.ConvertInteger].
|
||||
func ConvertInt16(str string) (int16, error) { return conv.ConvertInteger[int16](str) }
|
||||
|
||||
// ConvertInt32 turns a string into an int32.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertInt32] instead. Alternatively, you may use the generic version [conv.ConvertInteger].
|
||||
func ConvertInt32(str string) (int32, error) { return conv.ConvertInteger[int32](str) }
|
||||
|
||||
// ConvertInt64 turns a string into an int64.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertInt64] instead. Alternatively, you may use the generic version [conv.ConvertInteger].
|
||||
func ConvertInt64(str string) (int64, error) { return conv.ConvertInteger[int64](str) }
|
||||
|
||||
// ConvertUint8 turns a string into an uint8.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertUint8] instead. Alternatively, you may use the generic version [conv.ConvertUinteger].
|
||||
func ConvertUint8(str string) (uint8, error) { return conv.ConvertUinteger[uint8](str) }
|
||||
|
||||
// ConvertUint16 turns a string into an uint16.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertUint16] instead. Alternatively, you may use the generic version [conv.ConvertUinteger].
|
||||
func ConvertUint16(str string) (uint16, error) { return conv.ConvertUinteger[uint16](str) }
|
||||
|
||||
// ConvertUint32 turns a string into an uint32.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertUint32] instead. Alternatively, you may use the generic version [conv.ConvertUinteger].
|
||||
func ConvertUint32(str string) (uint32, error) { return conv.ConvertUinteger[uint32](str) }
|
||||
|
||||
// ConvertUint64 turns a string into an uint64.
|
||||
//
|
||||
// Deprecated: use [conv.ConvertUint64] instead. Alternatively, you may use the generic version [conv.ConvertUinteger].
|
||||
func ConvertUint64(str string) (uint64, error) { return conv.ConvertUinteger[uint64](str) }
|
||||
|
||||
// FormatBool turns a boolean into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatBool] instead.
|
||||
func FormatBool(value bool) string { return conv.FormatBool(value) }
|
||||
|
||||
// FormatFloat32 turns a float32 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatFloat] instead.
|
||||
func FormatFloat32(value float32) string { return conv.FormatFloat(value) }
|
||||
|
||||
// FormatFloat64 turns a float64 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatFloat] instead.
|
||||
func FormatFloat64(value float64) string { return conv.FormatFloat(value) }
|
||||
|
||||
// FormatInt8 turns an int8 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatInteger] instead.
|
||||
func FormatInt8(value int8) string { return conv.FormatInteger(value) }
|
||||
|
||||
// FormatInt16 turns an int16 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatInteger] instead.
|
||||
func FormatInt16(value int16) string { return conv.FormatInteger(value) }
|
||||
|
||||
// FormatInt32 turns an int32 into a string
|
||||
//
|
||||
// Deprecated: use [conv.FormatInteger] instead.
|
||||
func FormatInt32(value int32) string { return conv.FormatInteger(value) }
|
||||
|
||||
// FormatInt64 turns an int64 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatInteger] instead.
|
||||
func FormatInt64(value int64) string { return conv.FormatInteger(value) }
|
||||
|
||||
// FormatUint8 turns an uint8 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatUinteger] instead.
|
||||
func FormatUint8(value uint8) string { return conv.FormatUinteger(value) }
|
||||
|
||||
// FormatUint16 turns an uint16 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatUinteger] instead.
|
||||
func FormatUint16(value uint16) string { return conv.FormatUinteger(value) }
|
||||
|
||||
// FormatUint32 turns an uint32 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatUinteger] instead.
|
||||
func FormatUint32(value uint32) string { return conv.FormatUinteger(value) }
|
||||
|
||||
// FormatUint64 turns an uint64 into a string.
|
||||
//
|
||||
// Deprecated: use [conv.FormatUinteger] instead.
|
||||
func FormatUint64(value uint64) string { return conv.FormatUinteger(value) }
|
||||
|
||||
// String turn a pointer to of the string value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func String(v string) *string { return conv.Pointer(v) }
|
||||
|
||||
// StringValue turn the value of the string pointer passed in or
|
||||
// "" if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func StringValue(v *string) string { return conv.Value(v) }
|
||||
|
||||
// StringSlice converts a slice of string values into a slice of string pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func StringSlice(src []string) []*string { return conv.PointerSlice(src) }
|
||||
|
||||
// StringValueSlice converts a slice of string pointers into a slice of string values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func StringValueSlice(src []*string) []string { return conv.ValueSlice(src) }
|
||||
|
||||
// StringMap converts a string map of string values into a string map of string pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func StringMap(src map[string]string) map[string]*string { return conv.PointerMap(src) }
|
||||
|
||||
// StringValueMap converts a string map of string pointers into a string map of string values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func StringValueMap(src map[string]*string) map[string]string { return conv.ValueMap(src) }
|
||||
|
||||
// Bool turn a pointer to of the bool value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Bool(v bool) *bool { return conv.Pointer(v) }
|
||||
|
||||
// BoolValue turn the value of the bool pointer passed in or false if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func BoolValue(v *bool) bool { return conv.Value(v) }
|
||||
|
||||
// BoolSlice converts a slice of bool values into a slice of bool pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func BoolSlice(src []bool) []*bool { return conv.PointerSlice(src) }
|
||||
|
||||
// BoolValueSlice converts a slice of bool pointers into a slice of bool values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func BoolValueSlice(src []*bool) []bool { return conv.ValueSlice(src) }
|
||||
|
||||
// BoolMap converts a string map of bool values into a string map of bool pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func BoolMap(src map[string]bool) map[string]*bool { return conv.PointerMap(src) }
|
||||
|
||||
// BoolValueMap converts a string map of bool pointers into a string map of bool values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func BoolValueMap(src map[string]*bool) map[string]bool { return conv.ValueMap(src) }
|
||||
|
||||
// Int turn a pointer to of the int value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Int(v int) *int { return conv.Pointer(v) }
|
||||
|
||||
// IntValue turn the value of the int pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func IntValue(v *int) int { return conv.Value(v) }
|
||||
|
||||
// IntSlice converts a slice of int values into a slice of int pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func IntSlice(src []int) []*int { return conv.PointerSlice(src) }
|
||||
|
||||
// IntValueSlice converts a slice of int pointers into a slice of int values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func IntValueSlice(src []*int) []int { return conv.ValueSlice(src) }
|
||||
|
||||
// IntMap converts a string map of int values into a string map of int pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func IntMap(src map[string]int) map[string]*int { return conv.PointerMap(src) }
|
||||
|
||||
// IntValueMap converts a string map of int pointers into a string map of int values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func IntValueMap(src map[string]*int) map[string]int { return conv.ValueMap(src) }
|
||||
|
||||
// Int32 turn a pointer to of the int32 value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Int32(v int32) *int32 { return conv.Pointer(v) }
|
||||
|
||||
// Int32Value turn the value of the int32 pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func Int32Value(v *int32) int32 { return conv.Value(v) }
|
||||
|
||||
// Int32Slice converts a slice of int32 values into a slice of int32 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func Int32Slice(src []int32) []*int32 { return conv.PointerSlice(src) }
|
||||
|
||||
// Int32ValueSlice converts a slice of int32 pointers into a slice of int32 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func Int32ValueSlice(src []*int32) []int32 { return conv.ValueSlice(src) }
|
||||
|
||||
// Int32Map converts a string map of int32 values into a string map of int32 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func Int32Map(src map[string]int32) map[string]*int32 { return conv.PointerMap(src) }
|
||||
|
||||
// Int32ValueMap converts a string map of int32 pointers into a string map of int32 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func Int32ValueMap(src map[string]*int32) map[string]int32 { return conv.ValueMap(src) }
|
||||
|
||||
// Int64 turn a pointer to of the int64 value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Int64(v int64) *int64 { return conv.Pointer(v) }
|
||||
|
||||
// Int64Value turn the value of the int64 pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func Int64Value(v *int64) int64 { return conv.Value(v) }
|
||||
|
||||
// Int64Slice converts a slice of int64 values into a slice of int64 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func Int64Slice(src []int64) []*int64 { return conv.PointerSlice(src) }
|
||||
|
||||
// Int64ValueSlice converts a slice of int64 pointers into a slice of int64 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func Int64ValueSlice(src []*int64) []int64 { return conv.ValueSlice(src) }
|
||||
|
||||
// Int64Map converts a string map of int64 values into a string map of int64 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func Int64Map(src map[string]int64) map[string]*int64 { return conv.PointerMap(src) }
|
||||
|
||||
// Int64ValueMap converts a string map of int64 pointers into a string map of int64 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func Int64ValueMap(src map[string]*int64) map[string]int64 { return conv.ValueMap(src) }
|
||||
|
||||
// Uint16 turn a pointer to of the uint16 value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Uint16(v uint16) *uint16 { return conv.Pointer(v) }
|
||||
|
||||
// Uint16Value turn the value of the uint16 pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func Uint16Value(v *uint16) uint16 { return conv.Value(v) }
|
||||
|
||||
// Uint16Slice converts a slice of uint16 values into a slice of uint16 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func Uint16Slice(src []uint16) []*uint16 { return conv.PointerSlice(src) }
|
||||
|
||||
// Uint16ValueSlice converts a slice of uint16 pointers into a slice of uint16 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func Uint16ValueSlice(src []*uint16) []uint16 { return conv.ValueSlice(src) }
|
||||
|
||||
// Uint16Map converts a string map of uint16 values into a string map of uint16 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func Uint16Map(src map[string]uint16) map[string]*uint16 { return conv.PointerMap(src) }
|
||||
|
||||
// Uint16ValueMap converts a string map of uint16 pointers into a string map of uint16 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func Uint16ValueMap(src map[string]*uint16) map[string]uint16 { return conv.ValueMap(src) }
|
||||
|
||||
// Uint turn a pointer to of the uint value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Uint(v uint) *uint { return conv.Pointer(v) }
|
||||
|
||||
// UintValue turn the value of the uint pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func UintValue(v *uint) uint { return conv.Value(v) }
|
||||
|
||||
// UintSlice converts a slice of uint values into a slice of uint pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func UintSlice(src []uint) []*uint { return conv.PointerSlice(src) }
|
||||
|
||||
// UintValueSlice converts a slice of uint pointers into a slice of uint values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func UintValueSlice(src []*uint) []uint { return conv.ValueSlice(src) }
|
||||
|
||||
// UintMap converts a string map of uint values into a string map of uint pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func UintMap(src map[string]uint) map[string]*uint { return conv.PointerMap(src) }
|
||||
|
||||
// UintValueMap converts a string map of uint pointers into a string map of uint values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func UintValueMap(src map[string]*uint) map[string]uint { return conv.ValueMap(src) }
|
||||
|
||||
// Uint32 turn a pointer to of the uint32 value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Uint32(v uint32) *uint32 { return conv.Pointer(v) }
|
||||
|
||||
// Uint32Value turn the value of the uint32 pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func Uint32Value(v *uint32) uint32 { return conv.Value(v) }
|
||||
|
||||
// Uint32Slice converts a slice of uint32 values into a slice of uint32 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func Uint32Slice(src []uint32) []*uint32 { return conv.PointerSlice(src) }
|
||||
|
||||
// Uint32ValueSlice converts a slice of uint32 pointers into a slice of uint32 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func Uint32ValueSlice(src []*uint32) []uint32 { return conv.ValueSlice(src) }
|
||||
|
||||
// Uint32Map converts a string map of uint32 values into a string map of uint32 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func Uint32Map(src map[string]uint32) map[string]*uint32 { return conv.PointerMap(src) }
|
||||
|
||||
// Uint32ValueMap converts a string map of uint32 pointers into a string map of uint32 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func Uint32ValueMap(src map[string]*uint32) map[string]uint32 { return conv.ValueMap(src) }
|
||||
|
||||
// Uint64 turn a pointer to of the uint64 value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Uint64(v uint64) *uint64 { return conv.Pointer(v) }
|
||||
|
||||
// Uint64Value turn the value of the uint64 pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func Uint64Value(v *uint64) uint64 { return conv.Value(v) }
|
||||
|
||||
// Uint64Slice converts a slice of uint64 values into a slice of uint64 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func Uint64Slice(src []uint64) []*uint64 { return conv.PointerSlice(src) }
|
||||
|
||||
// Uint64ValueSlice converts a slice of uint64 pointers into a slice of uint64 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func Uint64ValueSlice(src []*uint64) []uint64 { return conv.ValueSlice(src) }
|
||||
|
||||
// Uint64Map converts a string map of uint64 values into a string map of uint64 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func Uint64Map(src map[string]uint64) map[string]*uint64 { return conv.PointerMap(src) }
|
||||
|
||||
// Uint64ValueMap converts a string map of uint64 pointers into a string map of uint64 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func Uint64ValueMap(src map[string]*uint64) map[string]uint64 { return conv.ValueMap(src) }
|
||||
|
||||
// Float32 turn a pointer to of the float32 value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Float32(v float32) *float32 { return conv.Pointer(v) }
|
||||
|
||||
// Float32Value turn the value of the float32 pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func Float32Value(v *float32) float32 { return conv.Value(v) }
|
||||
|
||||
// Float32Slice converts a slice of float32 values into a slice of float32 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func Float32Slice(src []float32) []*float32 { return conv.PointerSlice(src) }
|
||||
|
||||
// Float32ValueSlice converts a slice of float32 pointers into a slice of float32 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func Float32ValueSlice(src []*float32) []float32 { return conv.ValueSlice(src) }
|
||||
|
||||
// Float32Map converts a string map of float32 values into a string map of float32 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func Float32Map(src map[string]float32) map[string]*float32 { return conv.PointerMap(src) }
|
||||
|
||||
// Float32ValueMap converts a string map of float32 pointers into a string map of float32 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func Float32ValueMap(src map[string]*float32) map[string]float32 { return conv.ValueMap(src) }
|
||||
|
||||
// Float64 turn a pointer to of the float64 value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Float64(v float64) *float64 { return conv.Pointer(v) }
|
||||
|
||||
// Float64Value turn the value of the float64 pointer passed in or 0 if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func Float64Value(v *float64) float64 { return conv.Value(v) }
|
||||
|
||||
// Float64Slice converts a slice of float64 values into a slice of float64 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func Float64Slice(src []float64) []*float64 { return conv.PointerSlice(src) }
|
||||
|
||||
// Float64ValueSlice converts a slice of float64 pointers into a slice of float64 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func Float64ValueSlice(src []*float64) []float64 { return conv.ValueSlice(src) }
|
||||
|
||||
// Float64Map converts a string map of float64 values into a string map of float64 pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func Float64Map(src map[string]float64) map[string]*float64 { return conv.PointerMap(src) }
|
||||
|
||||
// Float64ValueMap converts a string map of float64 pointers into a string map of float64 values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func Float64ValueMap(src map[string]*float64) map[string]float64 { return conv.ValueMap(src) }
|
||||
|
||||
// Time turn a pointer to of the time.Time value passed in.
|
||||
//
|
||||
// Deprecated: use [conv.Pointer] instead.
|
||||
func Time(v time.Time) *time.Time { return conv.Pointer(v) }
|
||||
|
||||
// TimeValue turn the value of the time.Time pointer passed in or time.Time{} if the pointer is nil.
|
||||
//
|
||||
// Deprecated: use [conv.Value] instead.
|
||||
func TimeValue(v *time.Time) time.Time { return conv.Value(v) }
|
||||
|
||||
// TimeSlice converts a slice of time.Time values into a slice of time.Time pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerSlice] instead.
|
||||
func TimeSlice(src []time.Time) []*time.Time { return conv.PointerSlice(src) }
|
||||
|
||||
// TimeValueSlice converts a slice of time.Time pointers into a slice of time.Time values
|
||||
//
|
||||
// Deprecated: use [conv.ValueSlice] instead.
|
||||
func TimeValueSlice(src []*time.Time) []time.Time { return conv.ValueSlice(src) }
|
||||
|
||||
// TimeMap converts a string map of time.Time values into a string map of time.Time pointers.
|
||||
//
|
||||
// Deprecated: use [conv.PointerMap] instead.
|
||||
func TimeMap(src map[string]time.Time) map[string]*time.Time { return conv.PointerMap(src) }
|
||||
|
||||
// TimeValueMap converts a string map of time.Time pointers into a string map of time.Time values.
|
||||
//
|
||||
// Deprecated: use [conv.ValueMap] instead.
|
||||
func TimeValueMap(src map[string]*time.Time) map[string]time.Time { return conv.ValueMap(src) }
|
||||
-208
@@ -1,208 +0,0 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
|
||||
const (
|
||||
maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
|
||||
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
|
||||
epsilon float64 = 1e-9
|
||||
)
|
||||
|
||||
// IsFloat64AJSONInteger allow for integers [-2^53, 2^53-1] inclusive
|
||||
func IsFloat64AJSONInteger(f float64) bool {
|
||||
if math.IsNaN(f) || math.IsInf(f, 0) || f < minJSONFloat || f > maxJSONFloat {
|
||||
return false
|
||||
}
|
||||
fa := math.Abs(f)
|
||||
g := float64(uint64(f))
|
||||
ga := math.Abs(g)
|
||||
|
||||
diff := math.Abs(f - g)
|
||||
|
||||
// more info: https://floating-point-gui.de/errors/comparison/#look-out-for-edge-cases
|
||||
switch {
|
||||
case f == g: // best case
|
||||
return true
|
||||
case f == float64(int64(f)) || f == float64(uint64(f)): // optimistic case
|
||||
return true
|
||||
case f == 0 || g == 0 || diff < math.SmallestNonzeroFloat64: // very close to 0 values
|
||||
return diff < (epsilon * math.SmallestNonzeroFloat64)
|
||||
}
|
||||
// check the relative error
|
||||
return diff/math.Min(fa+ga, math.MaxFloat64) < epsilon
|
||||
}
|
||||
|
||||
var evaluatesAsTrue map[string]struct{}
|
||||
|
||||
func init() {
|
||||
evaluatesAsTrue = map[string]struct{}{
|
||||
"true": {},
|
||||
"1": {},
|
||||
"yes": {},
|
||||
"ok": {},
|
||||
"y": {},
|
||||
"on": {},
|
||||
"selected": {},
|
||||
"checked": {},
|
||||
"t": {},
|
||||
"enabled": {},
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertBool turn a string into a boolean
|
||||
func ConvertBool(str string) (bool, error) {
|
||||
_, ok := evaluatesAsTrue[strings.ToLower(str)]
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// ConvertFloat32 turn a string into a float32
|
||||
func ConvertFloat32(str string) (float32, error) {
|
||||
f, err := strconv.ParseFloat(str, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return float32(f), nil
|
||||
}
|
||||
|
||||
// ConvertFloat64 turn a string into a float64
|
||||
func ConvertFloat64(str string) (float64, error) {
|
||||
return strconv.ParseFloat(str, 64)
|
||||
}
|
||||
|
||||
// ConvertInt8 turn a string into an int8
|
||||
func ConvertInt8(str string) (int8, error) {
|
||||
i, err := strconv.ParseInt(str, 10, 8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int8(i), nil
|
||||
}
|
||||
|
||||
// ConvertInt16 turn a string into an int16
|
||||
func ConvertInt16(str string) (int16, error) {
|
||||
i, err := strconv.ParseInt(str, 10, 16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int16(i), nil
|
||||
}
|
||||
|
||||
// ConvertInt32 turn a string into an int32
|
||||
func ConvertInt32(str string) (int32, error) {
|
||||
i, err := strconv.ParseInt(str, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(i), nil
|
||||
}
|
||||
|
||||
// ConvertInt64 turn a string into an int64
|
||||
func ConvertInt64(str string) (int64, error) {
|
||||
return strconv.ParseInt(str, 10, 64)
|
||||
}
|
||||
|
||||
// ConvertUint8 turn a string into an uint8
|
||||
func ConvertUint8(str string) (uint8, error) {
|
||||
i, err := strconv.ParseUint(str, 10, 8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint8(i), nil
|
||||
}
|
||||
|
||||
// ConvertUint16 turn a string into an uint16
|
||||
func ConvertUint16(str string) (uint16, error) {
|
||||
i, err := strconv.ParseUint(str, 10, 16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint16(i), nil
|
||||
}
|
||||
|
||||
// ConvertUint32 turn a string into an uint32
|
||||
func ConvertUint32(str string) (uint32, error) {
|
||||
i, err := strconv.ParseUint(str, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint32(i), nil
|
||||
}
|
||||
|
||||
// ConvertUint64 turn a string into an uint64
|
||||
func ConvertUint64(str string) (uint64, error) {
|
||||
return strconv.ParseUint(str, 10, 64)
|
||||
}
|
||||
|
||||
// FormatBool turns a boolean into a string
|
||||
func FormatBool(value bool) string {
|
||||
return strconv.FormatBool(value)
|
||||
}
|
||||
|
||||
// FormatFloat32 turns a float32 into a string
|
||||
func FormatFloat32(value float32) string {
|
||||
return strconv.FormatFloat(float64(value), 'f', -1, 32)
|
||||
}
|
||||
|
||||
// FormatFloat64 turns a float64 into a string
|
||||
func FormatFloat64(value float64) string {
|
||||
return strconv.FormatFloat(value, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// FormatInt8 turns an int8 into a string
|
||||
func FormatInt8(value int8) string {
|
||||
return strconv.FormatInt(int64(value), 10)
|
||||
}
|
||||
|
||||
// FormatInt16 turns an int16 into a string
|
||||
func FormatInt16(value int16) string {
|
||||
return strconv.FormatInt(int64(value), 10)
|
||||
}
|
||||
|
||||
// FormatInt32 turns an int32 into a string
|
||||
func FormatInt32(value int32) string {
|
||||
return strconv.Itoa(int(value))
|
||||
}
|
||||
|
||||
// FormatInt64 turns an int64 into a string
|
||||
func FormatInt64(value int64) string {
|
||||
return strconv.FormatInt(value, 10)
|
||||
}
|
||||
|
||||
// FormatUint8 turns an uint8 into a string
|
||||
func FormatUint8(value uint8) string {
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
}
|
||||
|
||||
// FormatUint16 turns an uint16 into a string
|
||||
func FormatUint16(value uint16) string {
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
}
|
||||
|
||||
// FormatUint32 turns an uint32 into a string
|
||||
func FormatUint32(value uint32) string {
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
}
|
||||
|
||||
// FormatUint64 turns an uint64 into a string
|
||||
func FormatUint64(value uint64) string {
|
||||
return strconv.FormatUint(value, 10)
|
||||
}
|
||||
-730
@@ -1,730 +0,0 @@
|
||||
package swag
|
||||
|
||||
import "time"
|
||||
|
||||
// This file was taken from the aws go sdk
|
||||
|
||||
// String returns a pointer to of the string value passed in.
|
||||
func String(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// StringValue returns the value of the string pointer passed in or
|
||||
// "" if the pointer is nil.
|
||||
func StringValue(v *string) string {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// StringSlice converts a slice of string values into a slice of
|
||||
// string pointers
|
||||
func StringSlice(src []string) []*string {
|
||||
dst := make([]*string, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringValueSlice converts a slice of string pointers into a slice of
|
||||
// string values
|
||||
func StringValueSlice(src []*string) []string {
|
||||
dst := make([]string, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringMap converts a string map of string values into a string
|
||||
// map of string pointers
|
||||
func StringMap(src map[string]string) map[string]*string {
|
||||
dst := make(map[string]*string)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringValueMap converts a string map of string pointers into a string
|
||||
// map of string values
|
||||
func StringValueMap(src map[string]*string) map[string]string {
|
||||
dst := make(map[string]string)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Bool returns a pointer to of the bool value passed in.
|
||||
func Bool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// BoolValue returns the value of the bool pointer passed in or
|
||||
// false if the pointer is nil.
|
||||
func BoolValue(v *bool) bool {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BoolSlice converts a slice of bool values into a slice of
|
||||
// bool pointers
|
||||
func BoolSlice(src []bool) []*bool {
|
||||
dst := make([]*bool, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolValueSlice converts a slice of bool pointers into a slice of
|
||||
// bool values
|
||||
func BoolValueSlice(src []*bool) []bool {
|
||||
dst := make([]bool, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolMap converts a string map of bool values into a string
|
||||
// map of bool pointers
|
||||
func BoolMap(src map[string]bool) map[string]*bool {
|
||||
dst := make(map[string]*bool)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolValueMap converts a string map of bool pointers into a string
|
||||
// map of bool values
|
||||
func BoolValueMap(src map[string]*bool) map[string]bool {
|
||||
dst := make(map[string]bool)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int returns a pointer to of the int value passed in.
|
||||
func Int(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
// IntValue returns the value of the int pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func IntValue(v *int) int {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IntSlice converts a slice of int values into a slice of
|
||||
// int pointers
|
||||
func IntSlice(src []int) []*int {
|
||||
dst := make([]*int, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntValueSlice converts a slice of int pointers into a slice of
|
||||
// int values
|
||||
func IntValueSlice(src []*int) []int {
|
||||
dst := make([]int, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntMap converts a string map of int values into a string
|
||||
// map of int pointers
|
||||
func IntMap(src map[string]int) map[string]*int {
|
||||
dst := make(map[string]*int)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntValueMap converts a string map of int pointers into a string
|
||||
// map of int values
|
||||
func IntValueMap(src map[string]*int) map[string]int {
|
||||
dst := make(map[string]int)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int32 returns a pointer to of the int32 value passed in.
|
||||
func Int32(v int32) *int32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int32Value returns the value of the int32 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Int32Value(v *int32) int32 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Int32Slice converts a slice of int32 values into a slice of
|
||||
// int32 pointers
|
||||
func Int32Slice(src []int32) []*int32 {
|
||||
dst := make([]*int32, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int32ValueSlice converts a slice of int32 pointers into a slice of
|
||||
// int32 values
|
||||
func Int32ValueSlice(src []*int32) []int32 {
|
||||
dst := make([]int32, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int32Map converts a string map of int32 values into a string
|
||||
// map of int32 pointers
|
||||
func Int32Map(src map[string]int32) map[string]*int32 {
|
||||
dst := make(map[string]*int32)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int32ValueMap converts a string map of int32 pointers into a string
|
||||
// map of int32 values
|
||||
func Int32ValueMap(src map[string]*int32) map[string]int32 {
|
||||
dst := make(map[string]int32)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64 returns a pointer to of the int64 value passed in.
|
||||
func Int64(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int64Value returns the value of the int64 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Int64Value(v *int64) int64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Int64Slice converts a slice of int64 values into a slice of
|
||||
// int64 pointers
|
||||
func Int64Slice(src []int64) []*int64 {
|
||||
dst := make([]*int64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64ValueSlice converts a slice of int64 pointers into a slice of
|
||||
// int64 values
|
||||
func Int64ValueSlice(src []*int64) []int64 {
|
||||
dst := make([]int64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64Map converts a string map of int64 values into a string
|
||||
// map of int64 pointers
|
||||
func Int64Map(src map[string]int64) map[string]*int64 {
|
||||
dst := make(map[string]*int64)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64ValueMap converts a string map of int64 pointers into a string
|
||||
// map of int64 values
|
||||
func Int64ValueMap(src map[string]*int64) map[string]int64 {
|
||||
dst := make(map[string]int64)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint16 returns a pointer to of the uint16 value passed in.
|
||||
func Uint16(v uint16) *uint16 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Uint16Value returns the value of the uint16 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Uint16Value(v *uint16) uint16 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Uint16Slice converts a slice of uint16 values into a slice of
|
||||
// uint16 pointers
|
||||
func Uint16Slice(src []uint16) []*uint16 {
|
||||
dst := make([]*uint16, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint16ValueSlice converts a slice of uint16 pointers into a slice of
|
||||
// uint16 values
|
||||
func Uint16ValueSlice(src []*uint16) []uint16 {
|
||||
dst := make([]uint16, len(src))
|
||||
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint16Map converts a string map of uint16 values into a string
|
||||
// map of uint16 pointers
|
||||
func Uint16Map(src map[string]uint16) map[string]*uint16 {
|
||||
dst := make(map[string]*uint16)
|
||||
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint16ValueMap converts a string map of uint16 pointers into a string
|
||||
// map of uint16 values
|
||||
func Uint16ValueMap(src map[string]*uint16) map[string]uint16 {
|
||||
dst := make(map[string]uint16)
|
||||
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint returns a pointer to of the uint value passed in.
|
||||
func Uint(v uint) *uint {
|
||||
return &v
|
||||
}
|
||||
|
||||
// UintValue returns the value of the uint pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func UintValue(v *uint) uint {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// UintSlice converts a slice of uint values into a slice of
|
||||
// uint pointers
|
||||
func UintSlice(src []uint) []*uint {
|
||||
dst := make([]*uint, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// UintValueSlice converts a slice of uint pointers into a slice of
|
||||
// uint values
|
||||
func UintValueSlice(src []*uint) []uint {
|
||||
dst := make([]uint, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// UintMap converts a string map of uint values into a string
|
||||
// map of uint pointers
|
||||
func UintMap(src map[string]uint) map[string]*uint {
|
||||
dst := make(map[string]*uint)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// UintValueMap converts a string map of uint pointers into a string
|
||||
// map of uint values
|
||||
func UintValueMap(src map[string]*uint) map[string]uint {
|
||||
dst := make(map[string]uint)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint32 returns a pointer to of the uint32 value passed in.
|
||||
func Uint32(v uint32) *uint32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Uint32Value returns the value of the uint32 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Uint32Value(v *uint32) uint32 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Uint32Slice converts a slice of uint32 values into a slice of
|
||||
// uint32 pointers
|
||||
func Uint32Slice(src []uint32) []*uint32 {
|
||||
dst := make([]*uint32, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint32ValueSlice converts a slice of uint32 pointers into a slice of
|
||||
// uint32 values
|
||||
func Uint32ValueSlice(src []*uint32) []uint32 {
|
||||
dst := make([]uint32, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint32Map converts a string map of uint32 values into a string
|
||||
// map of uint32 pointers
|
||||
func Uint32Map(src map[string]uint32) map[string]*uint32 {
|
||||
dst := make(map[string]*uint32)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint32ValueMap converts a string map of uint32 pointers into a string
|
||||
// map of uint32 values
|
||||
func Uint32ValueMap(src map[string]*uint32) map[string]uint32 {
|
||||
dst := make(map[string]uint32)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint64 returns a pointer to of the uint64 value passed in.
|
||||
func Uint64(v uint64) *uint64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Uint64Value returns the value of the uint64 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Uint64Value(v *uint64) uint64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Uint64Slice converts a slice of uint64 values into a slice of
|
||||
// uint64 pointers
|
||||
func Uint64Slice(src []uint64) []*uint64 {
|
||||
dst := make([]*uint64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint64ValueSlice converts a slice of uint64 pointers into a slice of
|
||||
// uint64 values
|
||||
func Uint64ValueSlice(src []*uint64) []uint64 {
|
||||
dst := make([]uint64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint64Map converts a string map of uint64 values into a string
|
||||
// map of uint64 pointers
|
||||
func Uint64Map(src map[string]uint64) map[string]*uint64 {
|
||||
dst := make(map[string]*uint64)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint64ValueMap converts a string map of uint64 pointers into a string
|
||||
// map of uint64 values
|
||||
func Uint64ValueMap(src map[string]*uint64) map[string]uint64 {
|
||||
dst := make(map[string]uint64)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float32 returns a pointer to of the float32 value passed in.
|
||||
func Float32(v float32) *float32 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float32Value returns the value of the float32 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Float32Value(v *float32) float32 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Float32Slice converts a slice of float32 values into a slice of
|
||||
// float32 pointers
|
||||
func Float32Slice(src []float32) []*float32 {
|
||||
dst := make([]*float32, len(src))
|
||||
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float32ValueSlice converts a slice of float32 pointers into a slice of
|
||||
// float32 values
|
||||
func Float32ValueSlice(src []*float32) []float32 {
|
||||
dst := make([]float32, len(src))
|
||||
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float32Map converts a string map of float32 values into a string
|
||||
// map of float32 pointers
|
||||
func Float32Map(src map[string]float32) map[string]*float32 {
|
||||
dst := make(map[string]*float32)
|
||||
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float32ValueMap converts a string map of float32 pointers into a string
|
||||
// map of float32 values
|
||||
func Float32ValueMap(src map[string]*float32) map[string]float32 {
|
||||
dst := make(map[string]float32)
|
||||
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64 returns a pointer to of the float64 value passed in.
|
||||
func Float64(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float64Value returns the value of the float64 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Float64Value(v *float64) float64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Float64Slice converts a slice of float64 values into a slice of
|
||||
// float64 pointers
|
||||
func Float64Slice(src []float64) []*float64 {
|
||||
dst := make([]*float64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64ValueSlice converts a slice of float64 pointers into a slice of
|
||||
// float64 values
|
||||
func Float64ValueSlice(src []*float64) []float64 {
|
||||
dst := make([]float64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64Map converts a string map of float64 values into a string
|
||||
// map of float64 pointers
|
||||
func Float64Map(src map[string]float64) map[string]*float64 {
|
||||
dst := make(map[string]*float64)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64ValueMap converts a string map of float64 pointers into a string
|
||||
// map of float64 values
|
||||
func Float64ValueMap(src map[string]*float64) map[string]float64 {
|
||||
dst := make(map[string]float64)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Time returns a pointer to of the time.Time value passed in.
|
||||
func Time(v time.Time) *time.Time {
|
||||
return &v
|
||||
}
|
||||
|
||||
// TimeValue returns the value of the time.Time pointer passed in or
|
||||
// time.Time{} if the pointer is nil.
|
||||
func TimeValue(v *time.Time) time.Time {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// TimeSlice converts a slice of time.Time values into a slice of
|
||||
// time.Time pointers
|
||||
func TimeSlice(src []time.Time) []*time.Time {
|
||||
dst := make([]*time.Time, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeValueSlice converts a slice of time.Time pointers into a slice of
|
||||
// time.Time values
|
||||
func TimeValueSlice(src []*time.Time) []time.Time {
|
||||
dst := make([]time.Time, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeMap converts a string map of time.Time values into a string
|
||||
// map of time.Time pointers
|
||||
func TimeMap(src map[string]time.Time) map[string]*time.Time {
|
||||
dst := make(map[string]*time.Time)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeValueMap converts a string map of time.Time pointers into a string
|
||||
// map of time.Time values
|
||||
func TimeValueMap(src map[string]*time.Time) map[string]time.Time {
|
||||
dst := make(map[string]time.Time)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
+43
-27
@@ -1,31 +1,47 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package swag contains a bunch of helper functions for go-openapi and go-swagger projects.
|
||||
//
|
||||
// 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
|
||||
// You may also use it standalone for your projects.
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// NOTE: all features that used to be exposed as package-level members (constants, variables,
|
||||
// functions and types) are now deprecated and are superseded by equivalent features in
|
||||
// more specialized sub-packages.
|
||||
// Moving forward, no additional feature will be added to the [swag] API directly at the root package level,
|
||||
// which remains there for backward-compatibility purposes.
|
||||
//
|
||||
// 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.
|
||||
|
||||
/*
|
||||
Package swag contains a bunch of helper functions for go-openapi and go-swagger projects.
|
||||
|
||||
You may also use it standalone for your projects.
|
||||
|
||||
- convert between value and pointers for builtin types
|
||||
- convert from string to builtin types (wraps strconv)
|
||||
- fast json concatenation
|
||||
- search in path
|
||||
- load from file or http
|
||||
- name mangling
|
||||
|
||||
This repo has only few dependencies outside of the standard library:
|
||||
|
||||
- YAML utilities depend on gopkg.in/yaml.v2
|
||||
*/
|
||||
// Child modules will continue to evolve or some new ones may be added in the future.
|
||||
//
|
||||
// # Modules
|
||||
//
|
||||
// - [cmdutils] utilities to work with CLIs
|
||||
//
|
||||
// - [conv] type conversion utilities
|
||||
//
|
||||
// - [fileutils] file utilities
|
||||
//
|
||||
// - [jsonname] JSON utilities
|
||||
//
|
||||
// - [jsonutils] JSON utilities
|
||||
//
|
||||
// - [loading] file loading
|
||||
//
|
||||
// - [mangling] safe name generation
|
||||
//
|
||||
// - [netutils] networking utilities
|
||||
//
|
||||
// - [stringutils] `string` utilities
|
||||
//
|
||||
// - [typeutils] `go` types utilities
|
||||
//
|
||||
// - [yamlutils] YAML utilities
|
||||
//
|
||||
// # Dependencies
|
||||
//
|
||||
// This repo has a few dependencies outside of the standard library:
|
||||
//
|
||||
// - YAML utilities depend on [go.yaml.in/yaml/v3]
|
||||
package swag
|
||||
|
||||
//go:generate mockery
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
|
||||
package swag
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
// File represents an uploaded file.
|
||||
type File struct {
|
||||
Data multipart.File
|
||||
Header *multipart.FileHeader
|
||||
}
|
||||
|
||||
// Read bytes from the file
|
||||
func (f *File) Read(p []byte) (n int, err error) {
|
||||
return f.Data.Read(p)
|
||||
}
|
||||
|
||||
// Close the file
|
||||
func (f *File) Close() error {
|
||||
return f.Data.Close()
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package fileutils exposes utilities to deal with files and paths.
|
||||
//
|
||||
// Currently, there is:
|
||||
// - [File] to represent an abstraction of an uploaded file.
|
||||
// For instance, this is used by [github.com/go-openapi/runtime.File].
|
||||
// - path search utilities (e.g. finding packages in the GO search path)
|
||||
package fileutils
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fileutils
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
// File represents an uploaded file.
|
||||
type File struct {
|
||||
Data multipart.File
|
||||
Header *multipart.FileHeader
|
||||
}
|
||||
|
||||
// Read bytes from the file
|
||||
func (f *File) Read(p []byte) (n int, err error) {
|
||||
return f.Data.Read(p)
|
||||
}
|
||||
|
||||
// Close the file
|
||||
func (f *File) Close() error {
|
||||
return f.Data.Close()
|
||||
}
|
||||
Generated
Vendored
+11
-18
@@ -1,18 +1,7 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
package fileutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -21,10 +10,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// GOPATHKey represents the env key for gopath
|
||||
GOPATHKey = "GOPATH"
|
||||
)
|
||||
// GOPATHKey represents the env key for gopath
|
||||
const GOPATHKey = "GOPATH"
|
||||
|
||||
// FindInSearchPath finds a package in a provided lists of paths
|
||||
func FindInSearchPath(searchPath, pkg string) string {
|
||||
@@ -40,11 +27,17 @@ func FindInSearchPath(searchPath, pkg string) string {
|
||||
}
|
||||
|
||||
// FindInGoSearchPath finds a package in the $GOPATH:$GOROOT
|
||||
//
|
||||
// Deprecated: this function is no longer relevant with modern go.
|
||||
// It uses [runtime.GOROOT] under the hood, which is deprecated as of go1.24.
|
||||
func FindInGoSearchPath(pkg string) string {
|
||||
return FindInSearchPath(FullGoSearchPath(), pkg)
|
||||
}
|
||||
|
||||
// FullGoSearchPath gets the search paths for finding packages
|
||||
//
|
||||
// Deprecated: this function is no longer relevant with modern go.
|
||||
// It uses [runtime.GOROOT] under the hood, which is deprecated as of go1.24.
|
||||
func FullGoSearchPath() string {
|
||||
allPaths := os.Getenv(GOPATHKey)
|
||||
if allPaths == "" {
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
|
||||
import "github.com/go-openapi/swag/fileutils"
|
||||
|
||||
// GOPATHKey represents the env key for gopath
|
||||
//
|
||||
// Deprecated: use [fileutils.GOPATHKey] instead.
|
||||
const GOPATHKey = fileutils.GOPATHKey
|
||||
|
||||
// File represents an uploaded file.
|
||||
//
|
||||
// Deprecated: use [fileutils.File] instead.
|
||||
type File = fileutils.File
|
||||
|
||||
// FindInSearchPath finds a package in a provided lists of paths.
|
||||
//
|
||||
// Deprecated: use [fileutils.FindInSearchPath] instead.
|
||||
func FindInSearchPath(searchPath, pkg string) string {
|
||||
return fileutils.FindInSearchPath(searchPath, pkg)
|
||||
}
|
||||
|
||||
// FindInGoSearchPath finds a package in the $GOPATH:$GOROOT
|
||||
//
|
||||
// Deprecated: use [fileutils.FindInGoSearchPath] instead.
|
||||
func FindInGoSearchPath(pkg string) string { return fileutils.FindInGoSearchPath(pkg) }
|
||||
|
||||
// FullGoSearchPath gets the search paths for finding packages
|
||||
//
|
||||
// Deprecated: use [fileutils.FullGoSearchPath] instead.
|
||||
func FullGoSearchPath() string { return fileutils.FullGoSearchPath() }
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
use (
|
||||
.
|
||||
./cmdutils
|
||||
./conv
|
||||
./fileutils
|
||||
./jsonname
|
||||
./jsonutils
|
||||
./jsonutils/adapters/easyjson
|
||||
./jsonutils/adapters/testintegration
|
||||
./jsonutils/adapters/testintegration/benchmarks
|
||||
./jsonutils/fixtures_test
|
||||
./loading
|
||||
./mangling
|
||||
./netutils
|
||||
./stringutils
|
||||
./typeutils
|
||||
./yamlutils
|
||||
)
|
||||
|
||||
go 1.24.0
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
github.com/go-openapi/testify/v2 v2.0.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// commonInitialisms are common acronyms that are kept as whole uppercased words.
|
||||
commonInitialisms *indexOfInitialisms
|
||||
|
||||
// initialisms is a slice of sorted initialisms
|
||||
initialisms []string
|
||||
|
||||
// a copy of initialisms pre-baked as []rune
|
||||
initialismsRunes [][]rune
|
||||
initialismsUpperCased [][]rune
|
||||
|
||||
isInitialism func(string) bool
|
||||
|
||||
maxAllocMatches int
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||
configuredInitialisms := map[string]bool{
|
||||
"ACL": true,
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTPS": true,
|
||||
"HTTP": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"IPv4": true,
|
||||
"IPv6": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"OAI": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"TCP": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UDP": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XMPP": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
}
|
||||
|
||||
// a thread-safe index of initialisms
|
||||
commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms)
|
||||
initialisms = commonInitialisms.sorted()
|
||||
initialismsRunes = asRunes(initialisms)
|
||||
initialismsUpperCased = asUpperCased(initialisms)
|
||||
maxAllocMatches = maxAllocHeuristic(initialismsRunes)
|
||||
|
||||
// a test function
|
||||
isInitialism = commonInitialisms.isInitialism
|
||||
}
|
||||
|
||||
func asRunes(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(initialism)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func asUpperCased(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(upper(trim(initialism)))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func maxAllocHeuristic(in [][]rune) int {
|
||||
heuristic := make(map[rune]int)
|
||||
for _, initialism := range in {
|
||||
heuristic[initialism[0]]++
|
||||
}
|
||||
|
||||
var maxAlloc int
|
||||
for _, val := range heuristic {
|
||||
if val > maxAlloc {
|
||||
maxAlloc = val
|
||||
}
|
||||
}
|
||||
|
||||
return maxAlloc
|
||||
}
|
||||
|
||||
// AddInitialisms add additional initialisms
|
||||
func AddInitialisms(words ...string) {
|
||||
for _, word := range words {
|
||||
// commonInitialisms[upper(word)] = true
|
||||
commonInitialisms.add(upper(word))
|
||||
}
|
||||
// sort again
|
||||
initialisms = commonInitialisms.sorted()
|
||||
initialismsRunes = asRunes(initialisms)
|
||||
initialismsUpperCased = asUpperCased(initialisms)
|
||||
}
|
||||
|
||||
// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms.
|
||||
// Since go1.9, this may be implemented with sync.Map.
|
||||
type indexOfInitialisms struct {
|
||||
sortMutex *sync.Mutex
|
||||
index *sync.Map
|
||||
}
|
||||
|
||||
func newIndexOfInitialisms() *indexOfInitialisms {
|
||||
return &indexOfInitialisms{
|
||||
sortMutex: new(sync.Mutex),
|
||||
index: new(sync.Map),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) load(initial map[string]bool) *indexOfInitialisms {
|
||||
m.sortMutex.Lock()
|
||||
defer m.sortMutex.Unlock()
|
||||
for k, v := range initial {
|
||||
m.index.Store(k, v)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) isInitialism(key string) bool {
|
||||
_, ok := m.index.Load(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) add(key string) *indexOfInitialisms {
|
||||
m.index.Store(key, true)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) sorted() (result []string) {
|
||||
m.sortMutex.Lock()
|
||||
defer m.sortMutex.Unlock()
|
||||
m.index.Range(func(key, _ interface{}) bool {
|
||||
k := key.(string)
|
||||
result = append(result, k)
|
||||
return true
|
||||
})
|
||||
sort.Sort(sort.Reverse(byInitialism(result)))
|
||||
return
|
||||
}
|
||||
|
||||
type byInitialism []string
|
||||
|
||||
func (s byInitialism) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s byInitialism) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s byInitialism) Less(i, j int) bool {
|
||||
if len(s[i]) != len(s[j]) {
|
||||
return len(s[i]) < len(s[j])
|
||||
}
|
||||
|
||||
return strings.Compare(s[i], s[j]) > 0
|
||||
}
|
||||
-312
@@ -1,312 +0,0 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mailru/easyjson/jlexer"
|
||||
"github.com/mailru/easyjson/jwriter"
|
||||
)
|
||||
|
||||
// nullJSON represents a JSON object with null type
|
||||
var nullJSON = []byte("null")
|
||||
|
||||
// DefaultJSONNameProvider the default cache for types
|
||||
var DefaultJSONNameProvider = NewNameProvider()
|
||||
|
||||
const comma = byte(',')
|
||||
|
||||
var closers map[byte]byte
|
||||
|
||||
func init() {
|
||||
closers = map[byte]byte{
|
||||
'{': '}',
|
||||
'[': ']',
|
||||
}
|
||||
}
|
||||
|
||||
type ejMarshaler interface {
|
||||
MarshalEasyJSON(w *jwriter.Writer)
|
||||
}
|
||||
|
||||
type ejUnmarshaler interface {
|
||||
UnmarshalEasyJSON(w *jlexer.Lexer)
|
||||
}
|
||||
|
||||
// WriteJSON writes json data, prefers finding an appropriate interface to short-circuit the marshaler
|
||||
// so it takes the fastest option available.
|
||||
func WriteJSON(data interface{}) ([]byte, error) {
|
||||
if d, ok := data.(ejMarshaler); ok {
|
||||
jw := new(jwriter.Writer)
|
||||
d.MarshalEasyJSON(jw)
|
||||
return jw.BuildBytes()
|
||||
}
|
||||
if d, ok := data.(json.Marshaler); ok {
|
||||
return d.MarshalJSON()
|
||||
}
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
// ReadJSON reads json data, prefers finding an appropriate interface to short-circuit the unmarshaler
|
||||
// so it takes the fastest option available
|
||||
func ReadJSON(data []byte, value interface{}) error {
|
||||
trimmedData := bytes.Trim(data, "\x00")
|
||||
if d, ok := value.(ejUnmarshaler); ok {
|
||||
jl := &jlexer.Lexer{Data: trimmedData}
|
||||
d.UnmarshalEasyJSON(jl)
|
||||
return jl.Error()
|
||||
}
|
||||
if d, ok := value.(json.Unmarshaler); ok {
|
||||
return d.UnmarshalJSON(trimmedData)
|
||||
}
|
||||
return json.Unmarshal(trimmedData, value)
|
||||
}
|
||||
|
||||
// DynamicJSONToStruct converts an untyped json structure into a struct
|
||||
func DynamicJSONToStruct(data interface{}, target interface{}) error {
|
||||
// TODO: convert straight to a json typed map (mergo + iterate?)
|
||||
b, err := WriteJSON(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ReadJSON(b, target)
|
||||
}
|
||||
|
||||
// ConcatJSON concatenates multiple json objects efficiently
|
||||
func ConcatJSON(blobs ...[]byte) []byte {
|
||||
if len(blobs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
last := len(blobs) - 1
|
||||
for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) {
|
||||
// strips trailing null objects
|
||||
last--
|
||||
if last < 0 {
|
||||
// there was nothing but "null"s or nil...
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if last == 0 {
|
||||
return blobs[0]
|
||||
}
|
||||
|
||||
var opening, closing byte
|
||||
var idx, a int
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
for i, b := range blobs[:last+1] {
|
||||
if b == nil || bytes.Equal(b, nullJSON) {
|
||||
// a null object is in the list: skip it
|
||||
continue
|
||||
}
|
||||
if len(b) > 0 && opening == 0 { // is this an array or an object?
|
||||
opening, closing = b[0], closers[b[0]]
|
||||
}
|
||||
|
||||
if opening != '{' && opening != '[' {
|
||||
continue // don't know how to concatenate non container objects
|
||||
}
|
||||
|
||||
if len(b) < 3 { // yep empty but also the last one, so closing this thing
|
||||
if i == last && a > 0 {
|
||||
if err := buf.WriteByte(closing); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
idx = 0
|
||||
if a > 0 { // we need to join with a comma for everything beyond the first non-empty item
|
||||
if err := buf.WriteByte(comma); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
idx = 1 // this is not the first or the last so we want to drop the leading bracket
|
||||
}
|
||||
|
||||
if i != last { // not the last one, strip brackets
|
||||
if _, err := buf.Write(b[idx : len(b)-1]); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
} else { // last one, strip only the leading bracket
|
||||
if _, err := buf.Write(b[idx:]); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
a++
|
||||
}
|
||||
// somehow it ended up being empty, so provide a default value
|
||||
if buf.Len() == 0 {
|
||||
if err := buf.WriteByte(opening); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if err := buf.WriteByte(closing); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// ToDynamicJSON turns an object into a properly JSON typed structure
|
||||
func ToDynamicJSON(data interface{}) interface{} {
|
||||
// TODO: convert straight to a json typed map (mergo + iterate?)
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
var res interface{}
|
||||
if err := json.Unmarshal(b, &res); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// FromDynamicJSON turns an object into a properly JSON typed structure
|
||||
func FromDynamicJSON(data, target interface{}) error {
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return json.Unmarshal(b, target)
|
||||
}
|
||||
|
||||
// NameProvider represents an object capable of translating from go property names
|
||||
// to json property names
|
||||
// This type is thread-safe.
|
||||
type NameProvider struct {
|
||||
lock *sync.Mutex
|
||||
index map[reflect.Type]nameIndex
|
||||
}
|
||||
|
||||
type nameIndex struct {
|
||||
jsonNames map[string]string
|
||||
goNames map[string]string
|
||||
}
|
||||
|
||||
// NewNameProvider creates a new name provider
|
||||
func NewNameProvider() *NameProvider {
|
||||
return &NameProvider{
|
||||
lock: &sync.Mutex{},
|
||||
index: make(map[reflect.Type]nameIndex),
|
||||
}
|
||||
}
|
||||
|
||||
func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) {
|
||||
for i := 0; i < tpe.NumField(); i++ {
|
||||
targetDes := tpe.Field(i)
|
||||
|
||||
if targetDes.PkgPath != "" { // unexported
|
||||
continue
|
||||
}
|
||||
|
||||
if targetDes.Anonymous { // walk embedded structures tree down first
|
||||
buildnameIndex(targetDes.Type, idx, reverseIdx)
|
||||
continue
|
||||
}
|
||||
|
||||
if tag := targetDes.Tag.Get("json"); tag != "" {
|
||||
|
||||
parts := strings.Split(tag, ",")
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
nm := parts[0]
|
||||
if nm == "-" {
|
||||
continue
|
||||
}
|
||||
if nm == "" { // empty string means we want to use the Go name
|
||||
nm = targetDes.Name
|
||||
}
|
||||
|
||||
idx[nm] = targetDes.Name
|
||||
reverseIdx[targetDes.Name] = nm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newNameIndex(tpe reflect.Type) nameIndex {
|
||||
var idx = make(map[string]string, tpe.NumField())
|
||||
var reverseIdx = make(map[string]string, tpe.NumField())
|
||||
|
||||
buildnameIndex(tpe, idx, reverseIdx)
|
||||
return nameIndex{jsonNames: idx, goNames: reverseIdx}
|
||||
}
|
||||
|
||||
// GetJSONNames gets all the json property names for a type
|
||||
func (n *NameProvider) GetJSONNames(subject interface{}) []string {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
||||
names, ok := n.index[tpe]
|
||||
if !ok {
|
||||
names = n.makeNameIndex(tpe)
|
||||
}
|
||||
|
||||
res := make([]string, 0, len(names.jsonNames))
|
||||
for k := range names.jsonNames {
|
||||
res = append(res, k)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetJSONName gets the json name for a go property name
|
||||
func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bool) {
|
||||
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
||||
return n.GetJSONNameForType(tpe, name)
|
||||
}
|
||||
|
||||
// GetJSONNameForType gets the json name for a go property name on a given type
|
||||
func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
names, ok := n.index[tpe]
|
||||
if !ok {
|
||||
names = n.makeNameIndex(tpe)
|
||||
}
|
||||
nme, ok := names.goNames[name]
|
||||
return nme, ok
|
||||
}
|
||||
|
||||
func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex {
|
||||
names := newNameIndex(tpe)
|
||||
n.index[tpe] = names
|
||||
return names
|
||||
}
|
||||
|
||||
// GetGoName gets the go name for a json property name
|
||||
func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool) {
|
||||
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
||||
return n.GetGoNameForType(tpe, name)
|
||||
}
|
||||
|
||||
// GetGoNameForType gets the go name for a given type for a json property name
|
||||
func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
names, ok := n.index[tpe]
|
||||
if !ok {
|
||||
names = n.makeNameIndex(tpe)
|
||||
}
|
||||
nme, ok := names.jsonNames[name]
|
||||
return nme, ok
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package jsonname is a provider of json property names from go properties.
|
||||
package jsonname
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonname
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// DefaultJSONNameProvider is the default cache for types.
|
||||
var DefaultJSONNameProvider = NewNameProvider()
|
||||
|
||||
// NameProvider represents an object capable of translating from go property names
|
||||
// to json property names.
|
||||
//
|
||||
// This type is thread-safe.
|
||||
//
|
||||
// See [github.com/go-openapi/jsonpointer.Pointer] for an example.
|
||||
type NameProvider struct {
|
||||
lock *sync.Mutex
|
||||
index map[reflect.Type]nameIndex
|
||||
}
|
||||
|
||||
type nameIndex struct {
|
||||
jsonNames map[string]string
|
||||
goNames map[string]string
|
||||
}
|
||||
|
||||
// NewNameProvider creates a new name provider
|
||||
func NewNameProvider() *NameProvider {
|
||||
return &NameProvider{
|
||||
lock: &sync.Mutex{},
|
||||
index: make(map[reflect.Type]nameIndex),
|
||||
}
|
||||
}
|
||||
|
||||
func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) {
|
||||
for i := 0; i < tpe.NumField(); i++ {
|
||||
targetDes := tpe.Field(i)
|
||||
|
||||
if targetDes.PkgPath != "" { // unexported
|
||||
continue
|
||||
}
|
||||
|
||||
if targetDes.Anonymous { // walk embedded structures tree down first
|
||||
buildnameIndex(targetDes.Type, idx, reverseIdx)
|
||||
continue
|
||||
}
|
||||
|
||||
if tag := targetDes.Tag.Get("json"); tag != "" {
|
||||
|
||||
parts := strings.Split(tag, ",")
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
nm := parts[0]
|
||||
if nm == "-" {
|
||||
continue
|
||||
}
|
||||
if nm == "" { // empty string means we want to use the Go name
|
||||
nm = targetDes.Name
|
||||
}
|
||||
|
||||
idx[nm] = targetDes.Name
|
||||
reverseIdx[targetDes.Name] = nm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newNameIndex(tpe reflect.Type) nameIndex {
|
||||
var idx = make(map[string]string, tpe.NumField())
|
||||
var reverseIdx = make(map[string]string, tpe.NumField())
|
||||
|
||||
buildnameIndex(tpe, idx, reverseIdx)
|
||||
return nameIndex{jsonNames: idx, goNames: reverseIdx}
|
||||
}
|
||||
|
||||
// GetJSONNames gets all the json property names for a type
|
||||
func (n *NameProvider) GetJSONNames(subject any) []string {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
||||
names, ok := n.index[tpe]
|
||||
if !ok {
|
||||
names = n.makeNameIndex(tpe)
|
||||
}
|
||||
|
||||
res := make([]string, 0, len(names.jsonNames))
|
||||
for k := range names.jsonNames {
|
||||
res = append(res, k)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetJSONName gets the json name for a go property name
|
||||
func (n *NameProvider) GetJSONName(subject any, name string) (string, bool) {
|
||||
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
||||
return n.GetJSONNameForType(tpe, name)
|
||||
}
|
||||
|
||||
// GetJSONNameForType gets the json name for a go property name on a given type
|
||||
func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
names, ok := n.index[tpe]
|
||||
if !ok {
|
||||
names = n.makeNameIndex(tpe)
|
||||
}
|
||||
nme, ok := names.goNames[name]
|
||||
return nme, ok
|
||||
}
|
||||
|
||||
// GetGoName gets the go name for a json property name
|
||||
func (n *NameProvider) GetGoName(subject any, name string) (string, bool) {
|
||||
tpe := reflect.Indirect(reflect.ValueOf(subject)).Type()
|
||||
return n.GetGoNameForType(tpe, name)
|
||||
}
|
||||
|
||||
// GetGoNameForType gets the go name for a given type for a json property name
|
||||
func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
names, ok := n.index[tpe]
|
||||
if !ok {
|
||||
names = n.makeNameIndex(tpe)
|
||||
}
|
||||
nme, ok := names.jsonNames[name]
|
||||
return nme, ok
|
||||
}
|
||||
|
||||
func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex {
|
||||
names := newNameIndex(tpe)
|
||||
n.index[tpe] = names
|
||||
return names
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/swag/jsonname"
|
||||
)
|
||||
|
||||
// DefaultJSONNameProvider is the default cache for types
|
||||
//
|
||||
// Deprecated: use [jsonname.DefaultJSONNameProvider] instead.
|
||||
var DefaultJSONNameProvider = jsonname.DefaultJSONNameProvider
|
||||
|
||||
// NameProvider represents an object capable of translating from go property names
|
||||
// to json property names.
|
||||
//
|
||||
// Deprecated: use [jsonname.NameProvider] instead.
|
||||
type NameProvider = jsonname.NameProvider
|
||||
|
||||
// NewNameProvider creates a new name provider
|
||||
//
|
||||
// Deprecated: use [jsonname.NewNameProvider] instead.
|
||||
func NewNameProvider() *NameProvider { return jsonname.NewNameProvider() }
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
# jsonutils
|
||||
|
||||
`jsonutils` exposes a few tools to work with JSON:
|
||||
|
||||
- a fast, simple `Concat` to concatenate (not merge) JSON objects and arrays
|
||||
- `FromDynamicJSON` to convert a data structure into a "dynamic JSON" data structure
|
||||
- `ReadJSON` and `WriteJSON` behave like `json.Unmarshal` and `json.Marshal`,
|
||||
with the ability to use another underlying serialization library through an `Adapter`
|
||||
configured at runtime
|
||||
- a `JSONMapSlice` structure that may be used to store JSON objects with the order of keys maintained
|
||||
|
||||
## Dynamic JSON
|
||||
|
||||
We call "dynamic JSON" the go data structure that results from unmarshaling JSON like this:
|
||||
|
||||
```go
|
||||
var value any
|
||||
jsonBytes := `{"a": 1, ... }`
|
||||
_ = json.Unmarshal(jsonBytes, &value)
|
||||
```
|
||||
|
||||
In this configuration, the standard library mappings are as follows:
|
||||
|
||||
| JSON | go |
|
||||
|-----------|------------------|
|
||||
| `number` | `float64` |
|
||||
| `string` | `string` |
|
||||
| `boolean` | `bool` |
|
||||
| `null` | `nil` |
|
||||
| `object` | `map[string]any` |
|
||||
| `array` | `[]any` |
|
||||
|
||||
## Map slices
|
||||
|
||||
When using `JSONMapSlice`, the ordering of keys is ensured by replacing
|
||||
mappings to `map[string]any` by a `JSONMapSlice` which is an (ordered)
|
||||
slice of `JSONMapItem`s.
|
||||
|
||||
Notice that a similar feature is available for YAML (see [`yamlutils`](../yamlutils)),
|
||||
with a `YAMLMapSlice` type based on the `JSONMapSlice`.
|
||||
|
||||
`JSONMapSlice` is similar to an ordered map, but the keys are not retrieved
|
||||
in constant time.
|
||||
|
||||
Another difference with the the above standard mappings is that numbers don't always map
|
||||
to a `float64`: if the value is a JSON integer, it unmarshals to `int64`.
|
||||
|
||||
See also [some examples](https://pkg.go.dev/github.com/go-openapi/swag/jsonutils#pkg-examples)
|
||||
|
||||
## Adapters
|
||||
|
||||
`ReadJSON`, `WriteJSON` and `FromDynamicJSON` (which is a combination of the latter two)
|
||||
are wrappers on top of `json.Unmarshal` and `json.Marshal`.
|
||||
|
||||
By default, the adapter merely wraps the standard library.
|
||||
|
||||
The adapter may be used to register other JSON serialization libraries,
|
||||
possibly several ones at the same time.
|
||||
|
||||
If the value passed is identified as an "ordered map" (i.e. implements `ifaces.Ordered`
|
||||
or `ifaces.SetOrdered`, the adapter favors the "ordered" JSON behavior and tries to
|
||||
find a registered implementation that support ordered keys in objects.
|
||||
|
||||
Our standard library implementation supports this.
|
||||
|
||||
As of `v0.25.0`, we support through such an adapter the popular `mailru/easyjson`
|
||||
library, which kicks in when the passed values support the `easyjson.Unmarshaler`
|
||||
or `easyjson.Marshaler` interfaces.
|
||||
|
||||
In the future, we plan to add more similar libraries that compete on the go JSON
|
||||
serializers scene.
|
||||
|
||||
## Registering an adapter
|
||||
|
||||
In package `github.com/go-openapi/swag/easyjson/adapters`, several adapters are available.
|
||||
|
||||
Each adapter is an independent go module. Hence you'll pick its dependencies only if you import it.
|
||||
|
||||
At this moment we provide:
|
||||
|
||||
- `stdlib`: JSON adapter based on the standard library
|
||||
- `easyjson`: JSON adapter based on the `github.com/mailru/easyjson`
|
||||
|
||||
The adapters provide the basic `Marshal` and `Unmarshal` capabilities, plus an implementation
|
||||
of the `MapSlice` pattern.
|
||||
|
||||
You may also build your own adapter based on your specific use-case. An adapter is not required to implement
|
||||
all capabilities.
|
||||
|
||||
Every adapter comes with a `Register` function, possibly with some options, to register the adapter
|
||||
to a global registry.
|
||||
|
||||
For example, to enable `easyjson` to be used in `ReadJSON` and `WriteJSON`, you would write something like:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/go-openapi/swag/jsonutils/adapters"
|
||||
easyjson "github.com/go-openapi/swag/jsonutils/adapters/easyjson/json"
|
||||
)
|
||||
|
||||
func init() {
|
||||
easyjson.Register(adapters.Registry)
|
||||
}
|
||||
```
|
||||
|
||||
You may register several adapters. In this case, capability matching is evaluated from the last registered
|
||||
adapters (LIFO).
|
||||
|
||||
## [Benchmarks](./adapters/testintegration/benchmarks/README.md)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package adapters exposes a registry of adapters to multiple
|
||||
// JSON serialization libraries.
|
||||
//
|
||||
// All interfaces are defined in package [ifaces.Adapter].
|
||||
package adapters
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package ifaces exposes all interfaces to work with adapters.
|
||||
package ifaces
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ifaces
|
||||
|
||||
import (
|
||||
_ "encoding/json" // for documentation purpose
|
||||
"iter"
|
||||
)
|
||||
|
||||
// Ordered knows how to iterate over the (key,value) pairs of a JSON object.
|
||||
type Ordered interface {
|
||||
OrderedItems() iter.Seq2[string, any]
|
||||
}
|
||||
|
||||
// SetOrdered knows how to append or update the keys of a JSON object,
|
||||
// given an iterator over (key,value) pairs.
|
||||
//
|
||||
// If the provided iterator is nil then the receiver should be set to nil.
|
||||
type SetOrdered interface {
|
||||
SetOrderedItems(iter.Seq2[string, any])
|
||||
}
|
||||
|
||||
// OrderedMap represent a JSON object (i.e. like a map[string,any]),
|
||||
// and knows how to serialize and deserialize JSON with the order of keys maintained.
|
||||
type OrderedMap interface {
|
||||
Ordered
|
||||
SetOrdered
|
||||
|
||||
OrderedMarshalJSON() ([]byte, error)
|
||||
OrderedUnmarshalJSON([]byte) error
|
||||
}
|
||||
|
||||
// MarshalAdapter behaves likes the standard library [json.Marshal].
|
||||
type MarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
Marshal(any) ([]byte, error)
|
||||
}
|
||||
|
||||
// OrderedMarshalAdapter behaves likes the standard library [json.Marshal], preserving the order of keys in objects.
|
||||
type OrderedMarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
OrderedMarshal(Ordered) ([]byte, error)
|
||||
}
|
||||
|
||||
// UnmarshalAdapter behaves likes the standard library [json.Unmarshal].
|
||||
type UnmarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
Unmarshal([]byte, any) error
|
||||
}
|
||||
|
||||
// OrderedUnmarshalAdapter behaves likes the standard library [json.Unmarshal], preserving the order of keys in objects.
|
||||
type OrderedUnmarshalAdapter interface {
|
||||
Poolable
|
||||
|
||||
OrderedUnmarshal([]byte, SetOrdered) error
|
||||
}
|
||||
|
||||
// Adapter exposes an interface like the standard [json] library.
|
||||
type Adapter interface {
|
||||
MarshalAdapter
|
||||
UnmarshalAdapter
|
||||
|
||||
OrderedAdapter
|
||||
}
|
||||
|
||||
// OrderedAdapter exposes interfaces to process JSON and keep the order of object keys.
|
||||
type OrderedAdapter interface {
|
||||
OrderedMarshalAdapter
|
||||
OrderedUnmarshalAdapter
|
||||
NewOrderedMap(capacity int) OrderedMap
|
||||
}
|
||||
|
||||
type Poolable interface {
|
||||
// Self-redeem: for [Adapter] s that are allocated from a pool.
|
||||
// The [Adapter] must not be used after calling [Redeem].
|
||||
Redeem()
|
||||
|
||||
// Reset the state of the [Adapter], if any.
|
||||
Reset()
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ifaces
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Capability indicates what a JSON adapter is capable of.
|
||||
type Capability uint8
|
||||
|
||||
const (
|
||||
CapabilityMarshalJSON Capability = 1 << iota
|
||||
CapabilityUnmarshalJSON
|
||||
CapabilityOrderedMarshalJSON
|
||||
CapabilityOrderedUnmarshalJSON
|
||||
CapabilityOrderedMap
|
||||
)
|
||||
|
||||
func (c Capability) String() string {
|
||||
switch c {
|
||||
case CapabilityMarshalJSON:
|
||||
return "MarshalJSON"
|
||||
case CapabilityUnmarshalJSON:
|
||||
return "UnmarshalJSON"
|
||||
case CapabilityOrderedMarshalJSON:
|
||||
return "OrderedMarshalJSON"
|
||||
case CapabilityOrderedUnmarshalJSON:
|
||||
return "OrderedUnmarshalJSON"
|
||||
case CapabilityOrderedMap:
|
||||
return "OrderedMap"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
// Capabilities holds several unitary capability flags
|
||||
type Capabilities uint8
|
||||
|
||||
// Has some capability flag enabled.
|
||||
func (c Capabilities) Has(capability Capability) bool {
|
||||
return Capability(c)&capability > 0
|
||||
}
|
||||
|
||||
func (c Capabilities) String() string {
|
||||
var w strings.Builder
|
||||
|
||||
first := true
|
||||
for _, capability := range []Capability{
|
||||
CapabilityMarshalJSON,
|
||||
CapabilityUnmarshalJSON,
|
||||
CapabilityOrderedMarshalJSON,
|
||||
CapabilityOrderedUnmarshalJSON,
|
||||
CapabilityOrderedMap,
|
||||
} {
|
||||
if c.Has(capability) {
|
||||
if !first {
|
||||
w.WriteByte('|')
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
w.WriteString(capability.String())
|
||||
}
|
||||
}
|
||||
|
||||
return w.String()
|
||||
}
|
||||
|
||||
const (
|
||||
AllCapabilities Capabilities = Capabilities(uint8(CapabilityMarshalJSON) |
|
||||
uint8(CapabilityUnmarshalJSON) |
|
||||
uint8(CapabilityOrderedMarshalJSON) |
|
||||
uint8(CapabilityOrderedUnmarshalJSON) |
|
||||
uint8(CapabilityOrderedMap))
|
||||
|
||||
AllUnorderedCapabilities Capabilities = Capabilities(uint8(CapabilityMarshalJSON) | uint8(CapabilityUnmarshalJSON))
|
||||
)
|
||||
|
||||
// RegistryEntry describes how any given adapter registers its capabilities to the [Registrar].
|
||||
type RegistryEntry struct {
|
||||
Who string
|
||||
What Capabilities
|
||||
Constructor func() Adapter
|
||||
Support func(what Capability, value any) bool
|
||||
}
|
||||
|
||||
// Registrar is a type that knows how to keep registration calls from adapters.
|
||||
type Registrar interface {
|
||||
RegisterFor(RegistryEntry)
|
||||
}
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package adapters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
stdlib "github.com/go-openapi/swag/jsonutils/adapters/stdlib/json"
|
||||
)
|
||||
|
||||
// Registry holds the global registry for registered adapters.
|
||||
var Registry = NewRegistrar()
|
||||
|
||||
var (
|
||||
defaultRegistered = stdlib.Register
|
||||
|
||||
_ ifaces.Registrar = &Registrar{}
|
||||
)
|
||||
|
||||
type registryError string
|
||||
|
||||
func (e registryError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ErrRegistry indicates an error returned by the [Registrar].
|
||||
var ErrRegistry registryError = "JSON adapters registry error"
|
||||
|
||||
type registry []*ifaces.RegistryEntry
|
||||
|
||||
// Registrar holds registered [ifaces.Adapters] for different serialization capabilities.
|
||||
//
|
||||
// Internally, it maintains a cache for data types that favor a given adapter.
|
||||
type Registrar struct {
|
||||
marshalerRegistry registry
|
||||
unmarshalerRegistry registry
|
||||
orderedMarshalerRegistry registry
|
||||
orderedUnmarshalerRegistry registry
|
||||
orderedMapRegistry registry
|
||||
|
||||
gmx sync.RWMutex
|
||||
|
||||
// cache indexed by value type, so we don't have to lookup
|
||||
marshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
unmarshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
orderedMarshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
orderedUnmarshalerCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
orderedMapCache map[reflect.Type]*ifaces.RegistryEntry
|
||||
}
|
||||
|
||||
func NewRegistrar() *Registrar {
|
||||
r := &Registrar{}
|
||||
|
||||
r.marshalerRegistry = make(registry, 0, 1)
|
||||
r.unmarshalerRegistry = make(registry, 0, 1)
|
||||
r.orderedMarshalerRegistry = make(registry, 0, 1)
|
||||
r.orderedUnmarshalerRegistry = make(registry, 0, 1)
|
||||
r.orderedMapRegistry = make(registry, 0, 1)
|
||||
|
||||
r.marshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.unmarshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.orderedMarshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.orderedUnmarshalerCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
r.orderedMapCache = make(map[reflect.Type]*ifaces.RegistryEntry)
|
||||
|
||||
defaultRegistered(r)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// ClearCache resets the internal type cache.
|
||||
func (r *Registrar) ClearCache() {
|
||||
r.gmx.Lock()
|
||||
r.clearCache()
|
||||
r.gmx.Unlock()
|
||||
}
|
||||
|
||||
// Reset the [Registrar] to its defaults.
|
||||
func (r *Registrar) Reset() {
|
||||
r.gmx.Lock()
|
||||
r.clearCache()
|
||||
r.marshalerRegistry = r.marshalerRegistry[:0]
|
||||
r.unmarshalerRegistry = r.unmarshalerRegistry[:0]
|
||||
r.orderedMarshalerRegistry = r.orderedMarshalerRegistry[:0]
|
||||
r.orderedUnmarshalerRegistry = r.orderedUnmarshalerRegistry[:0]
|
||||
r.orderedMapRegistry = r.orderedMapRegistry[:0]
|
||||
r.gmx.Unlock()
|
||||
|
||||
defaultRegistered(r)
|
||||
}
|
||||
|
||||
// RegisterFor registers an adapter for some JSON capabilities.
|
||||
func (r *Registrar) RegisterFor(entry ifaces.RegistryEntry) {
|
||||
r.gmx.Lock()
|
||||
if entry.What.Has(ifaces.CapabilityMarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityMarshalJSON)
|
||||
r.marshalerRegistry = slices.Insert(r.marshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityUnmarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityUnmarshalJSON)
|
||||
r.unmarshalerRegistry = slices.Insert(r.unmarshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityOrderedMarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityOrderedMarshalJSON)
|
||||
r.orderedMarshalerRegistry = slices.Insert(r.orderedMarshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityOrderedUnmarshalJSON) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityOrderedUnmarshalJSON)
|
||||
r.orderedUnmarshalerRegistry = slices.Insert(r.orderedUnmarshalerRegistry, 0, &e)
|
||||
}
|
||||
if entry.What.Has(ifaces.CapabilityOrderedMap) {
|
||||
e := entry
|
||||
e.What &= ifaces.Capabilities(ifaces.CapabilityOrderedMap)
|
||||
r.orderedMapRegistry = slices.Insert(r.orderedMapRegistry, 0, &e)
|
||||
}
|
||||
r.gmx.Unlock()
|
||||
}
|
||||
|
||||
// AdapterFor returns an [ifaces.Adapter] that supports this capability for this type of value.
|
||||
//
|
||||
// The [ifaces.Adapter] may be redeemed to its pool using its Redeem() method, for adapters that support global
|
||||
// pooling. When this is not the case, the redeem function is just a no-operation.
|
||||
func (r *Registrar) AdapterFor(capability ifaces.Capability, value any) ifaces.Adapter {
|
||||
entry := r.findFirstFor(capability, value)
|
||||
if entry == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return entry.Constructor()
|
||||
}
|
||||
|
||||
func (r *Registrar) clearCache() {
|
||||
clear(r.marshalerCache)
|
||||
clear(r.unmarshalerCache)
|
||||
clear(r.orderedMarshalerCache)
|
||||
clear(r.orderedUnmarshalerCache)
|
||||
clear(r.orderedMapCache)
|
||||
}
|
||||
|
||||
func (r *Registrar) findFirstFor(capability ifaces.Capability, value any) *ifaces.RegistryEntry {
|
||||
switch capability {
|
||||
case ifaces.CapabilityMarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.marshalerRegistry, r.marshalerCache, capability, value)
|
||||
case ifaces.CapabilityUnmarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.unmarshalerRegistry, r.unmarshalerCache, capability, value)
|
||||
case ifaces.CapabilityOrderedMarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.orderedMarshalerRegistry, r.orderedMarshalerCache, capability, value)
|
||||
case ifaces.CapabilityOrderedUnmarshalJSON:
|
||||
return r.findFirstInRegistryFor(r.orderedUnmarshalerRegistry, r.orderedUnmarshalerCache, capability, value)
|
||||
case ifaces.CapabilityOrderedMap:
|
||||
return r.findFirstInRegistryFor(r.orderedMapRegistry, r.orderedMapCache, capability, value)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported capability %d: %w", capability, ErrRegistry))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registrar) findFirstInRegistryFor(reg registry, cache map[reflect.Type]*ifaces.RegistryEntry, capability ifaces.Capability, value any) *ifaces.RegistryEntry {
|
||||
r.gmx.RLock()
|
||||
if len(reg) > 1 {
|
||||
if entry, ok := cache[reflect.TypeOf(value)]; ok {
|
||||
// cache hit
|
||||
r.gmx.RUnlock()
|
||||
return entry
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range reg {
|
||||
if !entry.Support(capability, value) {
|
||||
continue
|
||||
}
|
||||
|
||||
r.gmx.RUnlock()
|
||||
|
||||
// update the internal cache
|
||||
r.gmx.Lock()
|
||||
cache[reflect.TypeOf(value)] = entry
|
||||
r.gmx.Unlock()
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// no adapter found
|
||||
r.gmx.RUnlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalAdapterFor returns the first adapter that knows how to Marshal this type of value.
|
||||
func MarshalAdapterFor(value any) ifaces.MarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityMarshalJSON, value)
|
||||
}
|
||||
|
||||
// OrderedMarshalAdapterFor returns the first adapter that knows how to OrderedMarshal this type of value.
|
||||
func OrderedMarshalAdapterFor(value ifaces.Ordered) ifaces.OrderedMarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityOrderedMarshalJSON, value)
|
||||
}
|
||||
|
||||
// UnmarshalAdapterFor returns the first adapter that knows how to Unmarshal this type of value.
|
||||
func UnmarshalAdapterFor(value any) ifaces.UnmarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityUnmarshalJSON, value)
|
||||
}
|
||||
|
||||
// OrderedUnmarshalAdapterFor provides the first adapter that knows how to OrderedUnmarshal this type of value.
|
||||
func OrderedUnmarshalAdapterFor(value ifaces.SetOrdered) ifaces.OrderedUnmarshalAdapter {
|
||||
return Registry.AdapterFor(ifaces.CapabilityOrderedUnmarshalJSON, value)
|
||||
}
|
||||
|
||||
// NewOrderedMap provides the "ordered map" implementation provided by the registry.
|
||||
func NewOrderedMap(capacity int) ifaces.OrderedMap {
|
||||
var v any
|
||||
adapter := Registry.AdapterFor(ifaces.CapabilityOrderedUnmarshalJSON, v)
|
||||
if adapter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer adapter.Redeem()
|
||||
return adapter.NewOrderedMap(capacity)
|
||||
}
|
||||
|
||||
func noopRedeemer() {}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
stdjson "encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
"github.com/go-openapi/swag/typeutils"
|
||||
)
|
||||
|
||||
const sensibleBufferSize = 8192
|
||||
|
||||
type jsonError string
|
||||
|
||||
func (e jsonError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ErrStdlib indicates that an error comes from the stdlib JSON adapter
|
||||
var ErrStdlib jsonError = "error from the JSON adapter stdlib"
|
||||
|
||||
var _ ifaces.Adapter = &Adapter{}
|
||||
|
||||
type Adapter struct {
|
||||
}
|
||||
|
||||
// NewAdapter yields an [ifaces.Adapter] using the standard library.
|
||||
func NewAdapter() *Adapter {
|
||||
return &Adapter{}
|
||||
}
|
||||
|
||||
func (a *Adapter) Marshal(value any) ([]byte, error) {
|
||||
return stdjson.Marshal(value)
|
||||
}
|
||||
|
||||
func (a *Adapter) Unmarshal(data []byte, value any) error {
|
||||
return stdjson.Unmarshal(data, value)
|
||||
}
|
||||
|
||||
func (a *Adapter) OrderedMarshal(value ifaces.Ordered) ([]byte, error) {
|
||||
w := poolOfWriters.Borrow()
|
||||
defer func() {
|
||||
poolOfWriters.Redeem(w)
|
||||
}()
|
||||
|
||||
if typeutils.IsNil(value) {
|
||||
w.RawString("null")
|
||||
|
||||
return w.BuildBytes()
|
||||
}
|
||||
|
||||
w.RawByte('{')
|
||||
first := true
|
||||
for k, v := range value.OrderedItems() {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.RawByte(',')
|
||||
}
|
||||
|
||||
w.String(k)
|
||||
w.RawByte(':')
|
||||
|
||||
switch val := v.(type) {
|
||||
case ifaces.Ordered:
|
||||
w.Raw(a.OrderedMarshal(val))
|
||||
default:
|
||||
w.Raw(stdjson.Marshal(v))
|
||||
}
|
||||
}
|
||||
|
||||
w.RawByte('}')
|
||||
|
||||
return w.BuildBytes()
|
||||
}
|
||||
|
||||
func (a *Adapter) OrderedUnmarshal(data []byte, value ifaces.SetOrdered) error {
|
||||
var m MapSlice
|
||||
if err := m.OrderedUnmarshalJSON(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if typeutils.IsNil(m) {
|
||||
// force input value to nil
|
||||
value.SetOrderedItems(nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
value.SetOrderedItems(m.OrderedItems())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Adapter) NewOrderedMap(capacity int) ifaces.OrderedMap {
|
||||
m := make(MapSlice, 0, capacity)
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
// Redeem the [Adapter] when it comes from a pool.
|
||||
//
|
||||
// The adapter becomes immediately unusable once redeemed.
|
||||
func (a *Adapter) Redeem() {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
|
||||
RedeemAdapter(a)
|
||||
}
|
||||
|
||||
func (a *Adapter) Reset() {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package json implements an [ifaces.Adapter] using the standard library.
|
||||
package json
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
stdjson "encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/swag/conv"
|
||||
)
|
||||
|
||||
type token struct {
|
||||
stdjson.Token
|
||||
}
|
||||
|
||||
func (t token) String() string {
|
||||
if t == invalidToken {
|
||||
return "invalid token"
|
||||
}
|
||||
if t == eofToken {
|
||||
return "EOF"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", t.Token)
|
||||
}
|
||||
|
||||
func (t token) Kind() tokenKind {
|
||||
switch t.Token.(type) {
|
||||
case nil:
|
||||
return tokenNull
|
||||
case stdjson.Delim:
|
||||
return tokenDelim
|
||||
case bool:
|
||||
return tokenBool
|
||||
case float64:
|
||||
return tokenFloat
|
||||
case stdjson.Number:
|
||||
return tokenNumber
|
||||
case string:
|
||||
return tokenString
|
||||
default:
|
||||
return tokenUndef
|
||||
}
|
||||
}
|
||||
|
||||
func (t token) Delim() byte {
|
||||
r, ok := t.Token.(stdjson.Delim)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
return byte(r)
|
||||
}
|
||||
|
||||
type tokenKind uint8
|
||||
|
||||
const (
|
||||
tokenUndef tokenKind = iota
|
||||
tokenString
|
||||
tokenNumber
|
||||
tokenFloat
|
||||
tokenBool
|
||||
tokenNull
|
||||
tokenDelim
|
||||
)
|
||||
|
||||
var (
|
||||
invalidToken = token{
|
||||
Token: stdjson.Token(struct{}{}),
|
||||
}
|
||||
|
||||
eofToken = token{
|
||||
Token: stdjson.Token(&struct{}{}),
|
||||
}
|
||||
|
||||
undefToken = token{
|
||||
Token: stdjson.Token(uint8(0)),
|
||||
}
|
||||
)
|
||||
|
||||
// jlexer apes easyjson's jlexer, but uses the standard library decoder under the hood.
|
||||
type jlexer struct {
|
||||
buf *bytesReader
|
||||
dec *stdjson.Decoder
|
||||
err error
|
||||
// current token
|
||||
next token
|
||||
// started bool
|
||||
}
|
||||
|
||||
type bytesReader struct {
|
||||
buf []byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (b *bytesReader) Reset() {
|
||||
b.buf = nil
|
||||
b.offset = 0
|
||||
}
|
||||
|
||||
func (b *bytesReader) Read(p []byte) (int, error) {
|
||||
if b.offset >= len(b.buf) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n := len(p)
|
||||
buf := b.buf[b.offset:]
|
||||
m := len(buf)
|
||||
|
||||
if n >= m {
|
||||
copy(p, buf)
|
||||
b.offset += m
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
copy(p, buf[:n])
|
||||
b.offset += n
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
var _ io.Reader = &bytesReader{}
|
||||
|
||||
func newLexer(data []byte) *jlexer {
|
||||
l := &jlexer{
|
||||
// current: undefToken,
|
||||
next: undefToken,
|
||||
}
|
||||
l.buf = &bytesReader{
|
||||
buf: data,
|
||||
}
|
||||
l.dec = stdjson.NewDecoder(l.buf) // unfortunately, cannot pool this
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *jlexer) Reset() {
|
||||
l.err = nil
|
||||
l.next = undefToken
|
||||
// leave l.dec and l.buf alone, since they are replaced at every Borrow
|
||||
}
|
||||
|
||||
func (l *jlexer) Error() error {
|
||||
return l.err
|
||||
}
|
||||
|
||||
func (l *jlexer) SetErr(err error) {
|
||||
l.err = err
|
||||
}
|
||||
|
||||
func (l *jlexer) Ok() bool {
|
||||
return l.err == nil
|
||||
}
|
||||
|
||||
// NextToken consumes a token
|
||||
func (l *jlexer) NextToken() token {
|
||||
if !l.Ok() {
|
||||
return invalidToken
|
||||
}
|
||||
|
||||
if l.next != undefToken {
|
||||
next := l.next
|
||||
l.next = undefToken
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
return l.fetchToken()
|
||||
}
|
||||
|
||||
// PeekToken returns the next token without consuming it
|
||||
func (l *jlexer) PeekToken() token {
|
||||
if l.next == undefToken {
|
||||
l.next = l.fetchToken()
|
||||
}
|
||||
|
||||
return l.next
|
||||
}
|
||||
|
||||
func (l *jlexer) Skip() {
|
||||
_ = l.NextToken()
|
||||
}
|
||||
|
||||
func (l *jlexer) IsDelim(c byte) bool {
|
||||
if !l.Ok() {
|
||||
return false
|
||||
}
|
||||
|
||||
next := l.PeekToken()
|
||||
if next.Kind() != tokenDelim {
|
||||
return false
|
||||
}
|
||||
|
||||
if next.Delim() != c {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *jlexer) IsNull() bool {
|
||||
if !l.Ok() {
|
||||
return false
|
||||
}
|
||||
|
||||
next := l.PeekToken()
|
||||
|
||||
return next.Kind() == tokenNull
|
||||
}
|
||||
|
||||
func (l *jlexer) Delim(c byte) {
|
||||
if !l.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenDelim {
|
||||
l.err = fmt.Errorf("expected a delimiter token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if tok.Delim() != c {
|
||||
l.err = fmt.Errorf("expected delimiter '%q' but got '%q': %w", c, tok.Delim(), ErrStdlib)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jlexer) Null() {
|
||||
if !l.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenNull {
|
||||
l.err = fmt.Errorf("expected a null token but got '%v': %w", tok, ErrStdlib)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jlexer) Number() any {
|
||||
if !l.Ok() {
|
||||
return 0
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
|
||||
switch tok.Kind() { //nolint:exhaustive
|
||||
case tokenNumber:
|
||||
n := tok.Token.(stdjson.Number).String()
|
||||
f, _ := strconv.ParseFloat(n, 64)
|
||||
if conv.IsFloat64AJSONInteger(f) {
|
||||
return int64(math.Trunc(f))
|
||||
}
|
||||
|
||||
return f
|
||||
|
||||
case tokenFloat:
|
||||
f := tok.Token.(float64)
|
||||
if conv.IsFloat64AJSONInteger(f) {
|
||||
return int64(math.Trunc(f))
|
||||
}
|
||||
|
||||
return f
|
||||
|
||||
default:
|
||||
l.err = fmt.Errorf("expected a number token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jlexer) Bool() bool {
|
||||
if !l.Ok() {
|
||||
return false
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenBool {
|
||||
l.err = fmt.Errorf("expected a bool token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return tok.Token.(bool)
|
||||
}
|
||||
|
||||
func (l *jlexer) String() string {
|
||||
if !l.Ok() {
|
||||
return ""
|
||||
}
|
||||
|
||||
tok := l.NextToken()
|
||||
if tok.Kind() != tokenString {
|
||||
l.err = fmt.Errorf("expected a string token but got '%v': %w", tok, ErrStdlib)
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
return tok.Token.(string)
|
||||
}
|
||||
|
||||
// Commas and colons are elided.
|
||||
func (l *jlexer) fetchToken() token {
|
||||
jtok, err := l.dec.Token()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return eofToken
|
||||
}
|
||||
|
||||
l.err = errors.Join(err, ErrStdlib)
|
||||
return invalidToken
|
||||
}
|
||||
|
||||
return token{Token: jtok}
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
stdjson "encoding/json"
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
)
|
||||
|
||||
var _ ifaces.OrderedMap = &MapSlice{}
|
||||
|
||||
// MapSlice represents a JSON object, with the order of keys maintained.
|
||||
type MapSlice []MapItem
|
||||
|
||||
func (s MapSlice) OrderedItems() iter.Seq2[string, any] {
|
||||
return func(yield func(string, any) bool) {
|
||||
for _, item := range s {
|
||||
if !yield(item.Key, item.Value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MapSlice) SetOrderedItems(items iter.Seq2[string, any]) {
|
||||
if items == nil {
|
||||
*s = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
m := *s
|
||||
if len(m) > 0 {
|
||||
// update mode
|
||||
idx := make(map[string]int, len(m))
|
||||
|
||||
for i, item := range m {
|
||||
idx[item.Key] = i
|
||||
}
|
||||
|
||||
for k, v := range items {
|
||||
idx, ok := idx[k]
|
||||
if ok {
|
||||
m[idx].Value = v
|
||||
|
||||
continue
|
||||
}
|
||||
m = append(m, MapItem{Key: k, Value: v})
|
||||
}
|
||||
|
||||
*s = m
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range items {
|
||||
m = append(m, MapItem{Key: k, Value: v})
|
||||
}
|
||||
|
||||
*s = m
|
||||
}
|
||||
|
||||
// MarshalJSON renders a [MapSlice] as JSON bytes, preserving the order of keys.
|
||||
func (s MapSlice) MarshalJSON() ([]byte, error) {
|
||||
return s.OrderedMarshalJSON()
|
||||
}
|
||||
|
||||
func (s MapSlice) OrderedMarshalJSON() ([]byte, error) {
|
||||
w := poolOfWriters.Borrow()
|
||||
defer func() {
|
||||
poolOfWriters.Redeem(w)
|
||||
}()
|
||||
|
||||
s.marshalObject(w)
|
||||
|
||||
return w.BuildBytes() // this clones data, so it's okay to redeem the writer and its buffer
|
||||
}
|
||||
|
||||
// UnmarshalJSON builds a [MapSlice] from JSON bytes, preserving the order of keys.
|
||||
//
|
||||
// Inner objects are unmarshaled as [MapSlice] slices and not map[string]any.
|
||||
func (s *MapSlice) UnmarshalJSON(data []byte) error {
|
||||
return s.OrderedUnmarshalJSON(data)
|
||||
}
|
||||
|
||||
func (s *MapSlice) OrderedUnmarshalJSON(data []byte) error {
|
||||
l := poolOfLexers.Borrow(data)
|
||||
defer func() {
|
||||
poolOfLexers.Redeem(l)
|
||||
}()
|
||||
|
||||
s.unmarshalObject(l)
|
||||
|
||||
return l.Error()
|
||||
}
|
||||
|
||||
func (s MapSlice) marshalObject(w *jwriter) {
|
||||
if s == nil {
|
||||
w.RawString("null")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.RawByte('{')
|
||||
|
||||
if len(s) == 0 {
|
||||
w.RawByte('}')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s[0].marshalJSON(w)
|
||||
|
||||
for i := 1; i < len(s); i++ {
|
||||
w.RawByte(',')
|
||||
s[i].marshalJSON(w)
|
||||
}
|
||||
|
||||
w.RawByte('}')
|
||||
}
|
||||
|
||||
func (s *MapSlice) unmarshalObject(in *jlexer) {
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
in.Delim('{') // consume token
|
||||
if !in.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
result := make(MapSlice, 0)
|
||||
|
||||
for in.Ok() && !in.IsDelim('}') {
|
||||
var mi MapItem
|
||||
|
||||
mi.unmarshalKeyValue(in)
|
||||
result = append(result, mi)
|
||||
}
|
||||
|
||||
in.Delim('}')
|
||||
|
||||
if !in.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
*s = result
|
||||
}
|
||||
|
||||
// MapItem represents the value of a key in a JSON object held by [MapSlice].
|
||||
//
|
||||
// Notice that [MapItem] should not be marshaled to or unmarshaled from JSON directly,
|
||||
// use this type as part of a [MapSlice] when dealing with JSON bytes.
|
||||
type MapItem struct {
|
||||
Key string
|
||||
Value any
|
||||
}
|
||||
|
||||
func (s MapItem) marshalJSON(w *jwriter) {
|
||||
w.String(s.Key)
|
||||
w.RawByte(':')
|
||||
w.Raw(stdjson.Marshal(s.Value))
|
||||
}
|
||||
|
||||
func (s *MapItem) unmarshalKeyValue(in *jlexer) {
|
||||
key := in.String() // consume string
|
||||
value := s.asInterface(in) // consume any value, including termination tokens '}' or ']'
|
||||
|
||||
if !in.Ok() {
|
||||
return
|
||||
}
|
||||
|
||||
s.Key = key
|
||||
s.Value = value
|
||||
}
|
||||
|
||||
func (s *MapItem) unmarshalArray(in *jlexer) []any {
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
in.Delim('[') // consume token
|
||||
if !in.Ok() {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make([]any, 0)
|
||||
|
||||
for in.Ok() && !in.IsDelim(']') {
|
||||
ret = append(ret, s.asInterface(in))
|
||||
}
|
||||
|
||||
in.Delim(']')
|
||||
if !in.Ok() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// asInterface is very much like [jlexer.Lexer.Interface], but unmarshals an object
|
||||
// into a [MapSlice], not a map[string]any.
|
||||
//
|
||||
// We have to force parsing errors somehow, since [jlexer.Lexer] doesn't let us
|
||||
// set a parsing error directly.
|
||||
func (s *MapItem) asInterface(in *jlexer) any {
|
||||
if !in.Ok() {
|
||||
return nil
|
||||
}
|
||||
|
||||
tok := in.PeekToken() // look-ahead what the next token looks like
|
||||
kind := tok.Kind()
|
||||
|
||||
switch kind {
|
||||
case tokenString:
|
||||
return in.String() // consume string
|
||||
|
||||
case tokenNumber, tokenFloat:
|
||||
return in.Number()
|
||||
|
||||
case tokenBool:
|
||||
return in.Bool()
|
||||
|
||||
case tokenNull:
|
||||
in.Null()
|
||||
|
||||
return nil
|
||||
|
||||
case tokenDelim:
|
||||
switch tok.Delim() {
|
||||
case '{': // not consumed yet
|
||||
ret := make(MapSlice, 0)
|
||||
ret.unmarshalObject(in) // consumes the terminating '}'
|
||||
|
||||
if in.Ok() {
|
||||
return ret
|
||||
}
|
||||
|
||||
// lexer is in an error state: will exhaust
|
||||
return nil
|
||||
|
||||
case '[': // not consumed yet
|
||||
return s.unmarshalArray(in) // consumes the terminating ']'
|
||||
default:
|
||||
in.SetErr(fmt.Errorf("unexpected delimiter: %v: %w", tok, ErrStdlib)) // force error
|
||||
return nil
|
||||
}
|
||||
|
||||
case tokenUndef:
|
||||
fallthrough
|
||||
default:
|
||||
if in.Ok() {
|
||||
in.SetErr(fmt.Errorf("unexpected token: %v: %w", tok, ErrStdlib)) // force error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
)
|
||||
|
||||
type adaptersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *adaptersPool) Borrow() *Adapter {
|
||||
return p.Get().(*Adapter)
|
||||
}
|
||||
|
||||
func (p *adaptersPool) BorrowIface() ifaces.Adapter {
|
||||
return p.Get().(*Adapter)
|
||||
}
|
||||
|
||||
func (p *adaptersPool) Redeem(a *Adapter) {
|
||||
p.Put(a)
|
||||
}
|
||||
|
||||
type writersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *writersPool) Borrow() *jwriter {
|
||||
ptr := p.Get()
|
||||
|
||||
jw := ptr.(*jwriter)
|
||||
jw.Reset()
|
||||
|
||||
return jw
|
||||
}
|
||||
|
||||
func (p *writersPool) Redeem(w *jwriter) {
|
||||
p.Put(w)
|
||||
}
|
||||
|
||||
type lexersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *lexersPool) Borrow(data []byte) *jlexer {
|
||||
ptr := p.Get()
|
||||
|
||||
l := ptr.(*jlexer)
|
||||
l.buf = poolOfReaders.Borrow(data)
|
||||
l.dec = json.NewDecoder(l.buf) // cannot pool, not exposed by the encoding/json API
|
||||
l.Reset()
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (p *lexersPool) Redeem(l *jlexer) {
|
||||
l.dec = nil
|
||||
discard := l.buf
|
||||
l.buf = nil
|
||||
poolOfReaders.Redeem(discard)
|
||||
p.Put(l)
|
||||
}
|
||||
|
||||
type readersPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
func (p *readersPool) Borrow(data []byte) *bytesReader {
|
||||
ptr := p.Get()
|
||||
|
||||
b := ptr.(*bytesReader)
|
||||
b.Reset()
|
||||
b.buf = data
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (p *readersPool) Redeem(b *bytesReader) {
|
||||
p.Put(b)
|
||||
}
|
||||
|
||||
var (
|
||||
poolOfAdapters = &adaptersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return NewAdapter()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfWriters = &writersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return newJWriter()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfLexers = &lexersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return newLexer(nil)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfReaders = &readersPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() any {
|
||||
return &bytesReader{}
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// BorrowAdapter borrows an [Adapter] from the pool, recycling already allocated instances.
|
||||
func BorrowAdapter() *Adapter {
|
||||
return poolOfAdapters.Borrow()
|
||||
}
|
||||
|
||||
// BorrowAdapterIface borrows a stdlib [Adapter] and converts it directly
|
||||
// to [ifaces.Adapter]. This is useful to avoid further allocations when
|
||||
// translating the concrete type into an interface.
|
||||
func BorrowAdapterIface() ifaces.Adapter {
|
||||
return poolOfAdapters.BorrowIface()
|
||||
}
|
||||
|
||||
// RedeemAdapter redeems an [Adapter] to the pool, so it may be recycled.
|
||||
func RedeemAdapter(a *Adapter) {
|
||||
poolOfAdapters.Redeem(a)
|
||||
}
|
||||
|
||||
func RedeemAdapterIface(a ifaces.Adapter) {
|
||||
concrete, ok := a.(*Adapter)
|
||||
if ok {
|
||||
poolOfAdapters.Redeem(concrete)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
)
|
||||
|
||||
func Register(dispatcher ifaces.Registrar) {
|
||||
t := reflect.TypeOf(Adapter{})
|
||||
dispatcher.RegisterFor(
|
||||
ifaces.RegistryEntry{
|
||||
Who: fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()),
|
||||
What: ifaces.AllCapabilities,
|
||||
Constructor: BorrowAdapterIface,
|
||||
Support: support,
|
||||
})
|
||||
}
|
||||
|
||||
func support(_ ifaces.Capability, _ any) bool {
|
||||
return true
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type jwriter struct {
|
||||
buf *bytes.Buffer
|
||||
err error
|
||||
}
|
||||
|
||||
func newJWriter() *jwriter {
|
||||
buf := make([]byte, 0, sensibleBufferSize)
|
||||
|
||||
return &jwriter{buf: bytes.NewBuffer(buf)}
|
||||
}
|
||||
|
||||
func (w *jwriter) Reset() {
|
||||
w.buf.Reset()
|
||||
w.err = nil
|
||||
}
|
||||
|
||||
func (w *jwriter) RawString(s string) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.buf.WriteString(s)
|
||||
}
|
||||
|
||||
func (w *jwriter) Raw(b []byte, err error) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = w.buf.Write(b)
|
||||
}
|
||||
|
||||
func (w *jwriter) RawByte(c byte) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.buf.WriteByte(c)
|
||||
}
|
||||
|
||||
var quoteReplacer = strings.NewReplacer(`"`, `\"`, `\`, `\\`)
|
||||
|
||||
func (w *jwriter) String(s string) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
// escape quotes and \
|
||||
s = quoteReplacer.Replace(s)
|
||||
|
||||
_ = w.buf.WriteByte('"')
|
||||
json.HTMLEscape(w.buf, []byte(s))
|
||||
_ = w.buf.WriteByte('"')
|
||||
}
|
||||
|
||||
// BuildBytes returns a clone of the internal buffer.
|
||||
func (w *jwriter) BuildBytes() ([]byte, error) {
|
||||
if w.err != nil {
|
||||
return nil, w.err
|
||||
}
|
||||
|
||||
return bytes.Clone(w.buf.Bytes()), nil
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// nullJSON represents a JSON object with null type
|
||||
var nullJSON = []byte("null")
|
||||
|
||||
const comma = byte(',')
|
||||
|
||||
var closers map[byte]byte
|
||||
|
||||
func init() {
|
||||
closers = map[byte]byte{
|
||||
'{': '}',
|
||||
'[': ']',
|
||||
}
|
||||
}
|
||||
|
||||
// ConcatJSON concatenates multiple json objects or arrays efficiently.
|
||||
//
|
||||
// Note that [ConcatJSON] performs a very simple (and fast) concatenation
|
||||
// operation: it does not attempt to merge objects.
|
||||
func ConcatJSON(blobs ...[]byte) []byte {
|
||||
if len(blobs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
last := len(blobs) - 1
|
||||
for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) {
|
||||
// strips trailing null objects
|
||||
last--
|
||||
if last < 0 {
|
||||
// there was nothing but "null"s or nil...
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if last == 0 {
|
||||
return blobs[0]
|
||||
}
|
||||
|
||||
var opening, closing byte
|
||||
var idx, a int
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
for i, b := range blobs[:last+1] {
|
||||
if b == nil || bytes.Equal(b, nullJSON) {
|
||||
// a null object is in the list: skip it
|
||||
continue
|
||||
}
|
||||
if len(b) > 0 && opening == 0 { // is this an array or an object?
|
||||
opening, closing = b[0], closers[b[0]]
|
||||
}
|
||||
|
||||
if opening != '{' && opening != '[' {
|
||||
continue // don't know how to concatenate non container objects
|
||||
}
|
||||
|
||||
const minLengthIfNotEmpty = 3
|
||||
if len(b) < minLengthIfNotEmpty { // yep empty but also the last one, so closing this thing
|
||||
if i == last && a > 0 {
|
||||
_ = buf.WriteByte(closing) // never returns err != nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
idx = 0
|
||||
if a > 0 { // we need to join with a comma for everything beyond the first non-empty item
|
||||
_ = buf.WriteByte(comma) // never returns err != nil
|
||||
idx = 1 // this is not the first or the last so we want to drop the leading bracket
|
||||
}
|
||||
|
||||
if i != last { // not the last one, strip brackets
|
||||
_, _ = buf.Write(b[idx : len(b)-1]) // never returns err != nil
|
||||
} else { // last one, strip only the leading bracket
|
||||
_, _ = buf.Write(b[idx:])
|
||||
}
|
||||
a++
|
||||
}
|
||||
|
||||
// somehow it ended up being empty, so provide a default value
|
||||
if buf.Len() == 0 && (opening == '{' || opening == '[') {
|
||||
_ = buf.WriteByte(opening) // never returns err != nil
|
||||
_ = buf.WriteByte(closing)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package jsonutils provides helpers to work with JSON.
|
||||
//
|
||||
// These utilities work with dynamic go structures to and from JSON.
|
||||
package jsonutils
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters"
|
||||
"github.com/go-openapi/swag/jsonutils/adapters/ifaces"
|
||||
)
|
||||
|
||||
// WriteJSON marshals a data structure as JSON.
|
||||
//
|
||||
// The difference with [json.Marshal] is that it may check among several alternatives
|
||||
// to do so.
|
||||
//
|
||||
// See [adapters.Registrar] for more details about how to configure
|
||||
// multiple serialization alternatives.
|
||||
//
|
||||
// NOTE: to allow types that are [easyjson.Marshaler] s to use that route to process JSON,
|
||||
// you now need to register the adapter for easyjson at runtime.
|
||||
func WriteJSON(value any) ([]byte, error) {
|
||||
if orderedMap, isOrdered := value.(ifaces.Ordered); isOrdered {
|
||||
orderedMarshaler := adapters.OrderedMarshalAdapterFor(orderedMap)
|
||||
|
||||
if orderedMarshaler != nil {
|
||||
defer orderedMarshaler.Redeem()
|
||||
|
||||
return orderedMarshaler.OrderedMarshal(orderedMap)
|
||||
}
|
||||
|
||||
// no support found in registered adapters, fallback to the default (unordered) case
|
||||
}
|
||||
|
||||
marshaler := adapters.MarshalAdapterFor(value)
|
||||
if marshaler != nil {
|
||||
defer marshaler.Redeem()
|
||||
|
||||
return marshaler.Marshal(value)
|
||||
}
|
||||
|
||||
// no support found in registered adapters, fallback to the default standard library.
|
||||
//
|
||||
// This only happens when tinkering with the global registry of adapters, since the default handles all the above cases.
|
||||
return json.Marshal(value) // Codecov ignore // this is a safeguard not easily simulated in tests
|
||||
}
|
||||
|
||||
// ReadJSON unmarshals JSON data into a data structure.
|
||||
//
|
||||
// The difference with [json.Unmarshal] is that it may check among several alternatives
|
||||
// to do so.
|
||||
//
|
||||
// See [adapters.Registrar] for more details about how to configure
|
||||
// multiple serialization alternatives.
|
||||
//
|
||||
// NOTE: value must be a pointer.
|
||||
//
|
||||
// If the provided value implements [ifaces.SetOrdered], it is a considered an "ordered map" and [ReadJSON]
|
||||
// will favor an adapter that supports the [ifaces.OrderedUnmarshal] feature, or fallback to
|
||||
// an unordered behavior if none is found.
|
||||
//
|
||||
// NOTE: to allow types that are [easyjson.Unmarshaler] s to use that route to process JSON,
|
||||
// you now need to register the adapter for easyjson at runtime.
|
||||
func ReadJSON(data []byte, value any) error {
|
||||
trimmedData := bytes.Trim(data, "\x00")
|
||||
|
||||
if orderedMap, isOrdered := value.(ifaces.SetOrdered); isOrdered {
|
||||
// if the value is an ordered map, favors support for OrderedUnmarshal.
|
||||
|
||||
orderedUnmarshaler := adapters.OrderedUnmarshalAdapterFor(orderedMap)
|
||||
|
||||
if orderedUnmarshaler != nil {
|
||||
defer orderedUnmarshaler.Redeem()
|
||||
|
||||
return orderedUnmarshaler.OrderedUnmarshal(trimmedData, orderedMap)
|
||||
}
|
||||
|
||||
// no support found in registered adapters, fallback to the default (unordered) case
|
||||
}
|
||||
|
||||
unmarshaler := adapters.UnmarshalAdapterFor(value)
|
||||
if unmarshaler != nil {
|
||||
defer unmarshaler.Redeem()
|
||||
|
||||
return unmarshaler.Unmarshal(trimmedData, value)
|
||||
}
|
||||
|
||||
// no support found in registered adapters, fallback to the default standard library.
|
||||
//
|
||||
// This only happens when tinkering with the global registry of adapters, since the default handles all the above cases.
|
||||
return json.Unmarshal(trimmedData, value) // Codecov ignore // this is a safeguard not easily simulated in tests
|
||||
}
|
||||
|
||||
// FromDynamicJSON turns a go value into a properly JSON typed structure.
|
||||
//
|
||||
// "Dynamic JSON" refers to what you get when unmarshaling JSON into an untyped any,
|
||||
// i.e. objects are represented by map[string]any, arrays by []any, and
|
||||
// all numbers are represented as float64.
|
||||
//
|
||||
// NOTE: target must be a pointer.
|
||||
//
|
||||
// # Maintaining the order of keys in objects
|
||||
//
|
||||
// If source and target implement [ifaces.Ordered] and [ifaces.SetOrdered] respectively,
|
||||
// they are considered "ordered maps" and the order of keys is maintained in the
|
||||
// "jsonification" process. In that case, map[string]any values are replaced by (ordered) [JSONMapSlice] ones.
|
||||
func FromDynamicJSON(source, target any) error {
|
||||
b, err := WriteJSON(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ReadJSON(b, target)
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonutils
|
||||
|
||||
import (
|
||||
"iter"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils/adapters"
|
||||
"github.com/go-openapi/swag/typeutils"
|
||||
)
|
||||
|
||||
// JSONMapSlice represents a JSON object, with the order of keys maintained.
|
||||
//
|
||||
// It behaves like an ordered map, but keys can't be accessed in constant time.
|
||||
type JSONMapSlice []JSONMapItem
|
||||
|
||||
// OrderedItems iterates over all (key,value) pairs with the order of keys maintained.
|
||||
//
|
||||
// This implements the [ifaces.Ordered] interface, so that [ifaces.Adapter] s know how to marshal
|
||||
// keys in the desired order.
|
||||
func (s JSONMapSlice) OrderedItems() iter.Seq2[string, any] {
|
||||
return func(yield func(string, any) bool) {
|
||||
for _, item := range s {
|
||||
if !yield(item.Key, item.Value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetOrderedItems sets keys in the [JSONMapSlice] objects, as presented by
|
||||
// the provided iterator.
|
||||
//
|
||||
// As a special case, if items is nil, this sets to receiver to a nil slice.
|
||||
//
|
||||
// This implements the [ifaces.SetOrdered] interface, so that [ifaces.Adapter] s know how to unmarshal
|
||||
// keys in the desired order.
|
||||
func (s *JSONMapSlice) SetOrderedItems(items iter.Seq2[string, any]) {
|
||||
if items == nil {
|
||||
// force receiver to be a nil slice
|
||||
*s = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
m := *s
|
||||
if len(m) > 0 {
|
||||
// update mode: short-circuited when unmarshaling fresh data structures
|
||||
idx := make(map[string]int, len(m))
|
||||
|
||||
for i, item := range m {
|
||||
idx[item.Key] = i
|
||||
}
|
||||
|
||||
for k, v := range items {
|
||||
idx, ok := idx[k]
|
||||
if ok {
|
||||
m[idx].Value = v
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
m = append(m, JSONMapItem{Key: k, Value: v})
|
||||
}
|
||||
|
||||
*s = m
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range items {
|
||||
m = append(m, JSONMapItem{Key: k, Value: v})
|
||||
}
|
||||
|
||||
*s = m
|
||||
}
|
||||
|
||||
// MarshalJSON renders a [JSONMapSlice] as JSON bytes, preserving the order of keys.
|
||||
//
|
||||
// It will pick the JSON library currently configured by the [adapters.Registry] (defaults to the standard library).
|
||||
func (s JSONMapSlice) MarshalJSON() ([]byte, error) {
|
||||
orderedMarshaler := adapters.OrderedMarshalAdapterFor(s)
|
||||
defer orderedMarshaler.Redeem()
|
||||
|
||||
return orderedMarshaler.OrderedMarshal(s)
|
||||
}
|
||||
|
||||
// UnmarshalJSON builds a [JSONMapSlice] from JSON bytes, preserving the order of keys.
|
||||
//
|
||||
// Inner objects are unmarshaled as ordered [JSONMapSlice] slices and not map[string]any.
|
||||
//
|
||||
// It will pick the JSON library currently configured by the [adapters.Registry] (defaults to the standard library).
|
||||
func (s *JSONMapSlice) UnmarshalJSON(data []byte) error {
|
||||
if typeutils.IsNil(*s) {
|
||||
// allow to unmarshal with a simple var declaration (nil slice)
|
||||
*s = JSONMapSlice{}
|
||||
}
|
||||
|
||||
orderedUnmarshaler := adapters.OrderedUnmarshalAdapterFor(s)
|
||||
defer orderedUnmarshaler.Redeem()
|
||||
|
||||
return orderedUnmarshaler.OrderedUnmarshal(data, s)
|
||||
}
|
||||
|
||||
// JSONMapItem represents the value of a key in a JSON object held by [JSONMapSlice].
|
||||
//
|
||||
// Notice that JSONMapItem should not be marshaled to or unmarshaled from JSON directly.
|
||||
//
|
||||
// Use this type as part of a [JSONMapSlice] when dealing with JSON bytes.
|
||||
type JSONMapItem struct {
|
||||
Key string
|
||||
Value any
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/go-openapi/swag/jsonutils"
|
||||
)
|
||||
|
||||
// JSONMapSlice represents a JSON object, with the order of keys maintained
|
||||
//
|
||||
// Deprecated: use [jsonutils.JSONMapSlice] instead, or [yamlutils.YAMLMapSlice] if you marshal YAML.
|
||||
type JSONMapSlice = jsonutils.JSONMapSlice
|
||||
|
||||
// JSONMapItem represents a JSON object, with the order of keys maintained
|
||||
//
|
||||
// Deprecated: use [jsonutils.JSONMapItem] instead.
|
||||
type JSONMapItem = jsonutils.JSONMapItem
|
||||
|
||||
// WriteJSON writes json data.
|
||||
//
|
||||
// Deprecated: use [jsonutils.WriteJSON] instead.
|
||||
func WriteJSON(data any) ([]byte, error) { return jsonutils.WriteJSON(data) }
|
||||
|
||||
// ReadJSON reads json data.
|
||||
//
|
||||
// Deprecated: use [jsonutils.ReadJSON] instead.
|
||||
func ReadJSON(data []byte, value any) error { return jsonutils.ReadJSON(data, value) }
|
||||
|
||||
// DynamicJSONToStruct converts an untyped JSON structure into a target data type.
|
||||
//
|
||||
// Deprecated: use [jsonutils.FromDynamicJSON] instead.
|
||||
func DynamicJSONToStruct(data any, target any) error {
|
||||
return jsonutils.FromDynamicJSON(data, target)
|
||||
}
|
||||
|
||||
// ConcatJSON concatenates multiple JSON objects efficiently.
|
||||
//
|
||||
// Deprecated: use [jsonutils.ConcatJSON] instead.
|
||||
func ConcatJSON(blobs ...[]byte) []byte { return jsonutils.ConcatJSON(blobs...) }
|
||||
|
||||
// ToDynamicJSON turns a go value into a properly JSON untyped structure.
|
||||
//
|
||||
// It is the same as [FromDynamicJSON], but doesn't check for errors.
|
||||
//
|
||||
// Deprecated: this function is a misnomer and is unsafe. Use [jsonutils.FromDynamicJSON] instead.
|
||||
func ToDynamicJSON(value any) any {
|
||||
var res any
|
||||
if err := FromDynamicJSON(value, &res); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// FromDynamicJSON turns a go value into a properly JSON typed structure.
|
||||
//
|
||||
// "Dynamic JSON" refers to what you get when unmarshaling JSON into an untyped any,
|
||||
// i.e. objects are represented by map[string]any, arrays by []any, and all
|
||||
// scalar values are any.
|
||||
//
|
||||
// Deprecated: use [jsonutils.FromDynamicJSON] instead.
|
||||
func FromDynamicJSON(data, target any) error { return jsonutils.FromDynamicJSON(data, target) }
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package loading provides tools to load a file from http or from a local file system.
|
||||
package loading
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package loading
|
||||
|
||||
type loadingError string
|
||||
|
||||
const (
|
||||
// ErrLoader is an error raised by the file loader utility
|
||||
ErrLoader loadingError = "loader error"
|
||||
)
|
||||
|
||||
func (e loadingError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package loading
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// JSONMatcher matches json for a file loader.
|
||||
func JSONMatcher(path string) bool {
|
||||
ext := filepath.Ext(path)
|
||||
return ext == ".json" || ext == ".jsn" || ext == ".jso"
|
||||
}
|
||||
|
||||
// JSONDoc loads a json document from either a file or a remote url.
|
||||
func JSONDoc(path string, opts ...Option) (json.RawMessage, error) {
|
||||
data, err := LoadFromFileOrHTTP(path, opts...)
|
||||
if err != nil {
|
||||
return nil, errors.Join(err, ErrLoader)
|
||||
}
|
||||
return json.RawMessage(data), nil
|
||||
}
|
||||
Generated
Vendored
+38
-54
@@ -1,54 +1,26 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
package loading
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoadHTTPTimeout the default timeout for load requests
|
||||
var LoadHTTPTimeout = 30 * time.Second
|
||||
|
||||
// LoadHTTPBasicAuthUsername the username to use when load requests require basic auth
|
||||
var LoadHTTPBasicAuthUsername = ""
|
||||
|
||||
// LoadHTTPBasicAuthPassword the password to use when load requests require basic auth
|
||||
var LoadHTTPBasicAuthPassword = ""
|
||||
|
||||
// LoadHTTPCustomHeaders an optional collection of custom HTTP headers for load requests
|
||||
var LoadHTTPCustomHeaders = map[string]string{}
|
||||
|
||||
// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in
|
||||
func LoadFromFileOrHTTP(pth string) ([]byte, error) {
|
||||
return LoadStrategy(pth, os.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(pth)
|
||||
}
|
||||
|
||||
// LoadFromFileOrHTTPWithTimeout loads the bytes from a file or a remote http server based on the path passed in
|
||||
// timeout arg allows for per request overriding of the request timeout
|
||||
func LoadFromFileOrHTTPWithTimeout(pth string, timeout time.Duration) ([]byte, error) {
|
||||
return LoadStrategy(pth, os.ReadFile, loadHTTPBytes(timeout))(pth)
|
||||
func LoadFromFileOrHTTP(pth string, opts ...Option) ([]byte, error) {
|
||||
o := optionsWithDefaults(opts)
|
||||
return LoadStrategy(pth, o.ReadFileFunc(), loadHTTPBytes(opts...), opts...)(pth)
|
||||
}
|
||||
|
||||
// LoadStrategy returns a loader function for a given path or URI.
|
||||
@@ -81,10 +53,12 @@ func LoadFromFileOrHTTPWithTimeout(pth string, timeout time.Duration) ([]byte, e
|
||||
// - `file://host/folder/file` becomes an UNC path like `\\host\folder\file` (no port specification is supported)
|
||||
// - `file:///c:/folder/file` becomes `C:\folder\file`
|
||||
// - `file://c:/folder/file` is tolerated (without leading `/`) and becomes `c:\folder\file`
|
||||
func LoadStrategy(pth string, local, remote func(string) ([]byte, error)) func(string) ([]byte, error) {
|
||||
func LoadStrategy(pth string, local, remote func(string) ([]byte, error), opts ...Option) func(string) ([]byte, error) {
|
||||
if strings.HasPrefix(pth, "http") {
|
||||
return remote
|
||||
}
|
||||
o := optionsWithDefaults(opts)
|
||||
_, isEmbedFS := o.fs.(embed.FS)
|
||||
|
||||
return func(p string) ([]byte, error) {
|
||||
upth, err := url.PathUnescape(p)
|
||||
@@ -92,19 +66,19 @@ func LoadStrategy(pth string, local, remote func(string) ([]byte, error)) func(s
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(p, `file://`) {
|
||||
cpth, hasPrefix := strings.CutPrefix(upth, "file://")
|
||||
if !hasPrefix || isEmbedFS || runtime.GOOS != "windows" {
|
||||
// crude processing: trim the file:// prefix. This leaves full URIs with a host with a (mostly) unexpected result
|
||||
// regular file path provided: just normalize slashes
|
||||
return local(filepath.FromSlash(upth))
|
||||
if isEmbedFS {
|
||||
// on windows, we need to slash the path if FS is an embed FS.
|
||||
return local(strings.TrimLeft(filepath.ToSlash(cpth), "./")) // remove invalid leading characters for embed FS
|
||||
}
|
||||
|
||||
return local(filepath.FromSlash(cpth))
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
// crude processing: this leaves full URIs with a host with a (mostly) unexpected result
|
||||
upth = strings.TrimPrefix(upth, `file://`)
|
||||
|
||||
return local(filepath.FromSlash(upth))
|
||||
}
|
||||
|
||||
// windows-only pre-processing of file://... URIs
|
||||
// windows-only pre-processing of file://... URIs, excluding embed.FS
|
||||
|
||||
// support for canonical file URIs on windows.
|
||||
u, err := url.Parse(filepath.ToSlash(upth))
|
||||
@@ -139,19 +113,29 @@ func LoadStrategy(pth string, local, remote func(string) ([]byte, error)) func(s
|
||||
}
|
||||
}
|
||||
|
||||
func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) {
|
||||
func loadHTTPBytes(opts ...Option) func(path string) ([]byte, error) {
|
||||
o := optionsWithDefaults(opts)
|
||||
|
||||
return func(path string) ([]byte, error) {
|
||||
client := &http.Client{Timeout: timeout}
|
||||
req, err := http.NewRequest(http.MethodGet, path, nil) //nolint:noctx
|
||||
client := o.client
|
||||
timeoutCtx := context.Background()
|
||||
var cancel func()
|
||||
|
||||
if o.httpTimeout > 0 {
|
||||
timeoutCtx, cancel = context.WithTimeout(timeoutCtx, o.httpTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(timeoutCtx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if LoadHTTPBasicAuthUsername != "" && LoadHTTPBasicAuthPassword != "" {
|
||||
req.SetBasicAuth(LoadHTTPBasicAuthUsername, LoadHTTPBasicAuthPassword)
|
||||
if o.basicAuthUsername != "" && o.basicAuthPassword != "" {
|
||||
req.SetBasicAuth(o.basicAuthUsername, o.basicAuthPassword)
|
||||
}
|
||||
|
||||
for key, val := range LoadHTTPCustomHeaders {
|
||||
for key, val := range o.customHeaders {
|
||||
req.Header.Set(key, val)
|
||||
}
|
||||
|
||||
@@ -168,7 +152,7 @@ func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) {
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status)
|
||||
return nil, fmt.Errorf("could not access document at %q [%s]: %w", path, resp.Status, ErrLoader)
|
||||
}
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package loading
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
// Option provides options for loading a file over HTTP or from a file.
|
||||
Option func(*options)
|
||||
|
||||
httpOptions struct {
|
||||
httpTimeout time.Duration
|
||||
basicAuthUsername string
|
||||
basicAuthPassword string
|
||||
customHeaders map[string]string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
fileOptions struct {
|
||||
fs fs.ReadFileFS
|
||||
}
|
||||
|
||||
options struct {
|
||||
httpOptions
|
||||
fileOptions
|
||||
}
|
||||
)
|
||||
|
||||
func (fo fileOptions) ReadFileFunc() func(string) ([]byte, error) {
|
||||
if fo.fs == nil {
|
||||
return os.ReadFile
|
||||
}
|
||||
|
||||
return fo.fs.ReadFile
|
||||
}
|
||||
|
||||
// WithTimeout sets a timeout for the remote file loader.
|
||||
//
|
||||
// The default timeout is 30s.
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(o *options) {
|
||||
o.httpTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// WithBasicAuth sets a basic authentication scheme for the remote file loader.
|
||||
func WithBasicAuth(username, password string) Option {
|
||||
return func(o *options) {
|
||||
o.basicAuthUsername = username
|
||||
o.basicAuthPassword = password
|
||||
}
|
||||
}
|
||||
|
||||
// WithCustomHeaders sets custom headers for the remote file loader.
|
||||
func WithCustomHeaders(headers map[string]string) Option {
|
||||
return func(o *options) {
|
||||
if o.customHeaders == nil {
|
||||
o.customHeaders = make(map[string]string, len(headers))
|
||||
}
|
||||
|
||||
for header, value := range headers {
|
||||
o.customHeaders[header] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHTTPClient overrides the default HTTP client used to fetch a remote file.
|
||||
//
|
||||
// By default, [http.DefaultClient] is used.
|
||||
func WithHTTPClient(client *http.Client) Option {
|
||||
return func(o *options) {
|
||||
o.client = client
|
||||
}
|
||||
}
|
||||
|
||||
// WithFS sets a file system for the local file loader.
|
||||
//
|
||||
// If the provided file system is a [fs.ReadFileFS], the ReadFile function is used.
|
||||
// Otherwise, ReadFile is wrapped using [fs.ReadFile].
|
||||
//
|
||||
// By default, the file system is the one provided by the os package.
|
||||
//
|
||||
// For example, this may be set to consume from an embedded file system, or a rooted FS.
|
||||
func WithFS(filesystem fs.FS) Option {
|
||||
return func(o *options) {
|
||||
if rfs, ok := filesystem.(fs.ReadFileFS); ok {
|
||||
o.fs = rfs
|
||||
|
||||
return
|
||||
}
|
||||
o.fs = readFileFS{FS: filesystem}
|
||||
}
|
||||
}
|
||||
|
||||
type readFileFS struct {
|
||||
fs.FS
|
||||
}
|
||||
|
||||
func (r readFileFS) ReadFile(name string) ([]byte, error) {
|
||||
return fs.ReadFile(r.FS, name)
|
||||
}
|
||||
|
||||
func optionsWithDefaults(opts []Option) options {
|
||||
const defaultTimeout = 30 * time.Second
|
||||
|
||||
o := options{
|
||||
// package level defaults
|
||||
httpOptions: httpOptions{
|
||||
httpTimeout: defaultTimeout,
|
||||
client: http.DefaultClient,
|
||||
},
|
||||
}
|
||||
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package loading
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-openapi/swag/yamlutils"
|
||||
)
|
||||
|
||||
// YAMLMatcher matches yaml for a file loader.
|
||||
func YAMLMatcher(path string) bool {
|
||||
ext := filepath.Ext(path)
|
||||
return ext == ".yaml" || ext == ".yml"
|
||||
}
|
||||
|
||||
// YAMLDoc loads a yaml document from either http or a file and converts it to json.
|
||||
func YAMLDoc(path string, opts ...Option) (json.RawMessage, error) {
|
||||
yamlDoc, err := YAMLData(path, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return yamlutils.YAMLToJSON(yamlDoc)
|
||||
}
|
||||
|
||||
// YAMLData loads a yaml document from either http or a file.
|
||||
func YAMLData(path string, opts ...Option) (any, error) {
|
||||
data, err := LoadFromFileOrHTTP(path, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return yamlutils.BytesToYAMLDoc(data)
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/swag/loading"
|
||||
)
|
||||
|
||||
var (
|
||||
// Package-level defaults for the file loading utilities (deprecated).
|
||||
|
||||
// LoadHTTPTimeout the default timeout for load requests.
|
||||
//
|
||||
// Deprecated: use [loading.WithTimeout] instead.
|
||||
LoadHTTPTimeout = 30 * time.Second
|
||||
|
||||
// LoadHTTPBasicAuthUsername the username to use when load requests require basic auth.
|
||||
//
|
||||
// Deprecated: use [loading.WithBasicAuth] instead.
|
||||
LoadHTTPBasicAuthUsername = ""
|
||||
|
||||
// LoadHTTPBasicAuthPassword the password to use when load requests require basic auth.
|
||||
//
|
||||
// Deprecated: use [loading.WithBasicAuth] instead.
|
||||
LoadHTTPBasicAuthPassword = ""
|
||||
|
||||
// LoadHTTPCustomHeaders an optional collection of custom HTTP headers for load requests.
|
||||
//
|
||||
// Deprecated: use [loading.WithCustomHeaders] instead.
|
||||
LoadHTTPCustomHeaders = map[string]string{}
|
||||
)
|
||||
|
||||
// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the provided path.
|
||||
//
|
||||
// Deprecated: use [loading.LoadFromFileOrHTTP] instead.
|
||||
func LoadFromFileOrHTTP(pth string, opts ...loading.Option) ([]byte, error) {
|
||||
return loading.LoadFromFileOrHTTP(pth, loadingOptionsWithDefaults(opts)...)
|
||||
}
|
||||
|
||||
// LoadFromFileOrHTTPWithTimeout loads the bytes from a file or a remote http server based on the path passed in
|
||||
// timeout arg allows for per request overriding of the request timeout.
|
||||
//
|
||||
// Deprecated: use [loading.LoadFileOrHTTP] with the [loading.WithTimeout] option instead.
|
||||
func LoadFromFileOrHTTPWithTimeout(pth string, timeout time.Duration, opts ...loading.Option) ([]byte, error) {
|
||||
opts = append(opts, loading.WithTimeout(timeout))
|
||||
|
||||
return LoadFromFileOrHTTP(pth, opts...)
|
||||
}
|
||||
|
||||
// LoadStrategy returns a loader function for a given path or URL.
|
||||
//
|
||||
// Deprecated: use [loading.LoadStrategy] instead.
|
||||
func LoadStrategy(pth string, local, remote func(string) ([]byte, error), opts ...loading.Option) func(string) ([]byte, error) {
|
||||
return loading.LoadStrategy(pth, local, remote, loadingOptionsWithDefaults(opts)...)
|
||||
}
|
||||
|
||||
// YAMLMatcher matches yaml for a file loader.
|
||||
//
|
||||
// Deprecated: use [loading.YAMLMatcher] instead.
|
||||
func YAMLMatcher(path string) bool { return loading.YAMLMatcher(path) }
|
||||
|
||||
// YAMLDoc loads a yaml document from either http or a file and converts it to json.
|
||||
//
|
||||
// Deprecated: use [loading.YAMLDoc] instead.
|
||||
func YAMLDoc(path string) (json.RawMessage, error) {
|
||||
return loading.YAMLDoc(path)
|
||||
}
|
||||
|
||||
// YAMLData loads a yaml document from either http or a file.
|
||||
//
|
||||
// Deprecated: use [loading.YAMLData] instead.
|
||||
func YAMLData(path string) (any, error) {
|
||||
return loading.YAMLData(path)
|
||||
}
|
||||
|
||||
// loadingOptionsWithDefaults bridges deprecated default settings that use package-level variables,
|
||||
// with the recommended use of loading.Option.
|
||||
func loadingOptionsWithDefaults(opts []loading.Option) []loading.Option {
|
||||
o := []loading.Option{
|
||||
loading.WithTimeout(LoadHTTPTimeout),
|
||||
loading.WithBasicAuth(LoadHTTPBasicAuthUsername, LoadHTTPBasicAuthPassword),
|
||||
loading.WithCustomHeaders(LoadHTTPCustomHeaders),
|
||||
}
|
||||
o = append(o, opts...)
|
||||
|
||||
return o
|
||||
}
|
||||
Generated
Vendored
+43
-5
@@ -1,12 +1,10 @@
|
||||
# Benchmarks
|
||||
|
||||
## Name mangling utilities
|
||||
# Benchmarking name mangling utilities
|
||||
|
||||
```bash
|
||||
go test -bench XXX -run XXX -benchtime 30s
|
||||
```
|
||||
|
||||
### Benchmarks at b3e7a5386f996177e4808f11acb2aa93a0f660df
|
||||
## Benchmarks at `b3e7a5386f996177e4808f11acb2aa93a0f660df`
|
||||
|
||||
```
|
||||
goos: linux
|
||||
@@ -21,7 +19,7 @@ BenchmarkToXXXName/ToHumanNameLower-4 895334 40354 ns/op 10472 B/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-4 882441 40678 ns/op 10566 B/op 749 allocs/op
|
||||
```
|
||||
|
||||
### Benchmarks after PR #79
|
||||
## Benchmarks after PR #79
|
||||
|
||||
~ x10 performance improvement and ~ /100 memory allocations.
|
||||
|
||||
@@ -50,3 +48,43 @@ BenchmarkToXXXName/ToCommandName-16 32256634 1137 ns/op 147 B/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-16 18599661 1946 ns/op 92 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-16 17581353 2054 ns/op 105 B/op 6 allocs/op
|
||||
```
|
||||
|
||||
## Benchmarks at `d7d2d1b895f5b6747afaff312dd2a402e69e818b`
|
||||
|
||||
go1.24
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
BenchmarkToXXXName/ToGoName-16 19757858 1881 ns/op 42 B/op 5 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-16 17494111 2094 ns/op 74 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-16 28161226 1492 ns/op 158 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-16 23787333 1489 ns/op 158 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-16 17537257 2030 ns/op 103 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-16 16977453 2156 ns/op 105 B/op 6 allocs/op
|
||||
```
|
||||
|
||||
## Benchmarks after PR #106
|
||||
|
||||
Moving the scope of everything down to a struct allowed to reduce a bit garbage and pooling.
|
||||
|
||||
On top of that, ToGoName (and thus ToVarName) have been subject to a minor optimization, removing a few allocations.
|
||||
|
||||
Overall timings improve by ~ -10%.
|
||||
|
||||
go1.24
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag/mangling
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
BenchmarkToXXXName/ToGoName-16 22496130 1618 ns/op 31 B/op 3 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-16 22538068 1618 ns/op 33 B/op 3 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-16 27722977 1236 ns/op 105 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-16 27967395 1258 ns/op 105 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-16 18587901 1917 ns/op 103 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-16 17193208 2019 ns/op 108 B/op 7 allocs/op
|
||||
```
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package mangling provides name mangling capabilities.
|
||||
//
|
||||
// Name mangling is an important stage when generating code:
|
||||
// it helps construct safe program identifiers that abide by the language rules
|
||||
// and play along with linters.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// Suppose we get an object name taken from an API spec: "json_object",
|
||||
//
|
||||
// We may generate a legit go type name using [NameMangler.ToGoName]: "JsonObject".
|
||||
//
|
||||
// We may then locate this type in a source file named using [NameMangler.ToFileName]: "json_object.go".
|
||||
//
|
||||
// The methods exposed by the NameMangler are used to generate code in many different contexts, such as:
|
||||
//
|
||||
// - generating exported or unexported go identifiers from a JSON schema or an API spec
|
||||
// - generating file names
|
||||
// - generating human-readable comments for types and variables
|
||||
// - generating JSON-like API identifiers from go code
|
||||
// - ...
|
||||
package mangling
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// DefaultInitialisms returns all the initialisms configured by default for this package.
|
||||
//
|
||||
// # Motivation
|
||||
//
|
||||
// Common initialisms are acronyms for which the ordinary camel-casing rules are altered and
|
||||
// for which we retain the original case.
|
||||
//
|
||||
// This is largely specific to the go naming conventions enforced by golint (now revive).
|
||||
//
|
||||
// # Example
|
||||
//
|
||||
// In go, "id" is a good-looking identifier, but "Id" is not and "ID" is preferred
|
||||
// (notice that this stems only from conventions: the go compiler accepts all of these).
|
||||
//
|
||||
// Similarly, we may use "http", but not "Http". In this case, "HTTP" is preferred.
|
||||
//
|
||||
// # Reference and customization
|
||||
//
|
||||
// The default list of these casing-style exceptions is taken from the [github.com/mgechev/revive] linter for go:
|
||||
// https://github.com/mgechev/revive/blob/master/lint/name.go#L93
|
||||
//
|
||||
// There are a few additions to the original list, such as IPv4, IPv6 and OAI ("OpenAPI").
|
||||
//
|
||||
// For these additions, "IPv4" would be preferred to "Ipv4" or "IPV4", and "OAI" to "Oai"
|
||||
//
|
||||
// You may redefine this list entirely using the mangler option [WithInitialisms], or simply add extra definitions
|
||||
// using [WithAdditionalInitialisms].
|
||||
//
|
||||
// # Mixed-case and plurals
|
||||
//
|
||||
// Notice that initialisms are not necessarily fully upper-cased: a mixed-case initialism indicates the preferred casing.
|
||||
//
|
||||
// Obviously, lower-case only initialisms do not make a lot of sense: if lower-case only initialisms are added,
|
||||
// they will be considered fully capitalized.
|
||||
//
|
||||
// Plural forms use mixed case like "IDs". And so do values like "IPv4" or "IPv6".
|
||||
//
|
||||
// The [NameMangler] automatically detects simple plurals for words such as "IDs" or "APIs",
|
||||
// so you don't need to configure these variants.
|
||||
//
|
||||
// At this moment, it doesn't support pluralization of terms that ends with an 's' (or 'S'), since there is
|
||||
// no clear consensus on whether a word like DNS should be pluralized as DNSes or remain invariant.
|
||||
// The [NameMangler] consider those invariant. Therefore DNSs or DNSes are not recognized as plurals for DNS.
|
||||
//
|
||||
// Besids, we don't want to support pluralization of terms which would otherwise conflict with another one,
|
||||
// like "HTTPs" vs "HTTPS". All these should be considered invariant. Hence: "Https" matches "HTTPS" and
|
||||
// "HTTPSS" is "HTTPS" followed by "S".
|
||||
func DefaultInitialisms() []string {
|
||||
return []string{
|
||||
"ACL",
|
||||
"API",
|
||||
"ASCII",
|
||||
"CPU",
|
||||
"CSS",
|
||||
"DNS",
|
||||
"EOF",
|
||||
"GUID",
|
||||
"HTML",
|
||||
"HTTPS",
|
||||
"HTTP",
|
||||
"ID",
|
||||
"IP",
|
||||
"IPv4", // prefer the mixed case outcome IPv4 over the capitalized IPV4
|
||||
"IPv6", // prefer the mixed case outcome IPv6 over the capitalized IPV6
|
||||
"JSON",
|
||||
"LHS",
|
||||
"OAI",
|
||||
"QPS",
|
||||
"RAM",
|
||||
"RHS",
|
||||
"RPC",
|
||||
"SLA",
|
||||
"SMTP",
|
||||
"SQL",
|
||||
"SSH",
|
||||
"TCP",
|
||||
"TLS",
|
||||
"TTL",
|
||||
"UDP",
|
||||
"UI",
|
||||
"UID",
|
||||
"UUID",
|
||||
"URI",
|
||||
"URL",
|
||||
"UTF8",
|
||||
"VM",
|
||||
"XML",
|
||||
"XMPP",
|
||||
"XSRF",
|
||||
"XSS",
|
||||
}
|
||||
}
|
||||
|
||||
type indexOfInitialisms struct {
|
||||
initialismsCache
|
||||
|
||||
index map[string]struct{}
|
||||
}
|
||||
|
||||
func newIndexOfInitialisms() *indexOfInitialisms {
|
||||
return &indexOfInitialisms{
|
||||
index: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) add(words ...string) *indexOfInitialisms {
|
||||
for _, word := range words {
|
||||
// sanitization of injected words: trimmed from blanks, and must start with a letter
|
||||
trimmed := strings.TrimSpace(word)
|
||||
|
||||
firstRune, _ := utf8.DecodeRuneInString(trimmed)
|
||||
if !unicode.IsLetter(firstRune) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Initialisms are case-sensitive. This means that we support mixed-case words.
|
||||
// However, if specified as a lower-case string, the initialism should be fully capitalized.
|
||||
if trimmed == strings.ToLower(trimmed) {
|
||||
m.index[strings.ToUpper(trimmed)] = struct{}{}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
m.index[trimmed] = struct{}{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) sorted() []string {
|
||||
result := make([]string, 0, len(m.index))
|
||||
for k := range m.index {
|
||||
result = append(result, k)
|
||||
}
|
||||
sort.Sort(sort.Reverse(byInitialism(result)))
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *indexOfInitialisms) buildCache() {
|
||||
m.build(m.sorted(), m.pluralForm)
|
||||
}
|
||||
|
||||
// initialismsCache caches all needed pre-computed and converted initialism entries,
|
||||
// in the desired resolution order.
|
||||
type initialismsCache struct {
|
||||
initialisms []string
|
||||
initialismsRunes [][]rune
|
||||
initialismsUpperCased [][]rune // initialisms cached in their trimmed, upper-cased version
|
||||
initialismsPluralForm []pluralForm
|
||||
}
|
||||
|
||||
func (c *initialismsCache) build(in []string, pluralfunc func(string) pluralForm) {
|
||||
c.initialisms = in
|
||||
c.initialismsRunes = asRunes(c.initialisms)
|
||||
c.initialismsUpperCased = asUpperCased(c.initialisms)
|
||||
c.initialismsPluralForm = asPluralForms(c.initialisms, pluralfunc)
|
||||
}
|
||||
|
||||
// pluralForm denotes the kind of pluralization to be used for initialisms.
|
||||
//
|
||||
// At this moment, initialisms are either invariant or follow a simple plural form with an
|
||||
// extra (lower case) "s".
|
||||
type pluralForm uint8
|
||||
|
||||
const (
|
||||
notPlural pluralForm = iota
|
||||
invariantPlural
|
||||
simplePlural
|
||||
)
|
||||
|
||||
func (f pluralForm) String() string {
|
||||
switch f {
|
||||
case notPlural:
|
||||
return "notPlural"
|
||||
case invariantPlural:
|
||||
return "invariantPlural"
|
||||
case simplePlural:
|
||||
return "simplePlural"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
// pluralForm indicates how we want to pluralize a given initialism.
|
||||
//
|
||||
// Besides configured invariant forms (like HTTP and HTTPS),
|
||||
// an initialism is normally pluralized by adding a single 's', like in IDs.
|
||||
//
|
||||
// Initialisms ending with an 'S' or an 's' are configured as invariant (we don't
|
||||
// support plural forms like CSSes or DNSes, however the mechanism could be extended to
|
||||
// do just that).
|
||||
func (m *indexOfInitialisms) pluralForm(key string) pluralForm {
|
||||
if _, ok := m.index[key]; !ok {
|
||||
return notPlural
|
||||
}
|
||||
|
||||
if strings.HasSuffix(strings.ToUpper(key), "S") {
|
||||
return invariantPlural
|
||||
}
|
||||
|
||||
if _, ok := m.index[key+"s"]; ok {
|
||||
return invariantPlural
|
||||
}
|
||||
|
||||
if _, ok := m.index[key+"S"]; ok {
|
||||
return invariantPlural
|
||||
}
|
||||
|
||||
return simplePlural
|
||||
}
|
||||
|
||||
type byInitialism []string
|
||||
|
||||
func (s byInitialism) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s byInitialism) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Less specifies the order in which initialisms are prioritized:
|
||||
// 1. match longest first
|
||||
// 2. when equal length, match in reverse lexicographical order, lower case match comes first
|
||||
func (s byInitialism) Less(i, j int) bool {
|
||||
if len(s[i]) != len(s[j]) {
|
||||
return len(s[i]) < len(s[j])
|
||||
}
|
||||
|
||||
return s[i] < s[j]
|
||||
}
|
||||
|
||||
func asRunes(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(initialism)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func asUpperCased(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(upper(trim(initialism)))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// asPluralForms bakes an index of pluralization support.
|
||||
func asPluralForms(in []string, pluralFunc func(string) pluralForm) []pluralForm {
|
||||
out := make([]pluralForm, len(in))
|
||||
for i, initialism := range in {
|
||||
out[i] = pluralFunc(initialism)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type (
|
||||
lexemKind uint8
|
||||
|
||||
nameLexem struct {
|
||||
original string
|
||||
matchedInitialism string
|
||||
kind lexemKind
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
lexemKindCasualName lexemKind = iota
|
||||
lexemKindInitialismName
|
||||
)
|
||||
|
||||
func newInitialismNameLexem(original, matchedInitialism string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindInitialismName,
|
||||
original: original,
|
||||
matchedInitialism: matchedInitialism,
|
||||
}
|
||||
}
|
||||
|
||||
func newCasualNameLexem(original string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindCasualName,
|
||||
original: trim(original), // TODO: save on calls to trim
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTitleized writes the titleized lexeme to a bytes.Buffer.
|
||||
//
|
||||
// If the first letter cannot be capitalized, it doesn't write anything and return false,
|
||||
// so the caller may attempt some workaround strategy.
|
||||
func (l nameLexem) WriteTitleized(w *bytes.Buffer, alwaysUpper bool) bool {
|
||||
if l.kind == lexemKindInitialismName {
|
||||
w.WriteString(l.matchedInitialism)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if len(l.original) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(l.original) == 1 {
|
||||
// identifier is too short: casing will depend on the context
|
||||
firstByte := l.original[0]
|
||||
switch {
|
||||
case 'A' <= firstByte && firstByte <= 'Z':
|
||||
// safe
|
||||
w.WriteByte(firstByte)
|
||||
|
||||
return true
|
||||
case alwaysUpper && 'a' <= firstByte && firstByte <= 'z':
|
||||
w.WriteByte(firstByte - 'a' + 'A')
|
||||
|
||||
return true
|
||||
default:
|
||||
|
||||
// not a letter: skip and let the caller decide
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if firstByte := l.original[0]; firstByte < utf8.RuneSelf {
|
||||
// ASCII
|
||||
switch {
|
||||
case 'A' <= firstByte && firstByte <= 'Z':
|
||||
// already an upper case letter
|
||||
w.WriteString(l.original)
|
||||
|
||||
return true
|
||||
case 'a' <= firstByte && firstByte <= 'z':
|
||||
w.WriteByte(firstByte - 'a' + 'A')
|
||||
w.WriteString(l.original[1:])
|
||||
|
||||
return true
|
||||
default:
|
||||
// not a good candidate: doesn't start with a letter
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// unicode
|
||||
firstRune, idx := utf8.DecodeRuneInString(l.original)
|
||||
if !unicode.IsLetter(firstRune) || !unicode.IsUpper(unicode.ToUpper(firstRune)) {
|
||||
// not a good candidate: doesn't start with a letter
|
||||
// or a rune for which case doesn't make sense (e.g. East-Asian runes etc)
|
||||
return false
|
||||
}
|
||||
|
||||
rest := l.original[idx:]
|
||||
w.WriteRune(unicode.ToUpper(firstRune))
|
||||
w.WriteString(strings.ToLower(rest))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// WriteLower is like write titleized but it writes a lower-case version of the lexeme.
|
||||
//
|
||||
// Similarly, there is no writing if the casing of the first rune doesn't make sense.
|
||||
func (l nameLexem) WriteLower(w *bytes.Buffer, alwaysLower bool) bool {
|
||||
if l.kind == lexemKindInitialismName {
|
||||
w.WriteString(lower(l.matchedInitialism))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if len(l.original) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(l.original) == 1 {
|
||||
// identifier is too short: casing will depend on the context
|
||||
firstByte := l.original[0]
|
||||
switch {
|
||||
case 'a' <= firstByte && firstByte <= 'z':
|
||||
// safe
|
||||
w.WriteByte(firstByte)
|
||||
|
||||
return true
|
||||
case alwaysLower && 'A' <= firstByte && firstByte <= 'Z':
|
||||
w.WriteByte(firstByte - 'A' + 'a')
|
||||
|
||||
return true
|
||||
default:
|
||||
|
||||
// not a letter: skip and let the caller decide
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if firstByte := l.original[0]; firstByte < utf8.RuneSelf {
|
||||
// ASCII
|
||||
switch {
|
||||
case 'a' <= firstByte && firstByte <= 'z':
|
||||
// already a lower case letter
|
||||
w.WriteString(l.original)
|
||||
|
||||
return true
|
||||
case 'A' <= firstByte && firstByte <= 'Z':
|
||||
w.WriteByte(firstByte - 'A' + 'a')
|
||||
w.WriteString(l.original[1:])
|
||||
|
||||
return true
|
||||
default:
|
||||
// not a good candidate: doesn't start with a letter
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// unicode
|
||||
firstRune, idx := utf8.DecodeRuneInString(l.original)
|
||||
if !unicode.IsLetter(firstRune) || !unicode.IsLower(unicode.ToLower(firstRune)) {
|
||||
// not a good candidate: doesn't start with a letter
|
||||
// or a rune for which case doesn't make sense (e.g. East-Asian runes etc)
|
||||
return false
|
||||
}
|
||||
|
||||
rest := l.original[idx:]
|
||||
w.WriteRune(unicode.ToLower(firstRune))
|
||||
w.WriteString(rest)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (l nameLexem) GetOriginal() string {
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l nameLexem) IsInitialism() bool {
|
||||
return l.kind == lexemKindInitialismName
|
||||
}
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// NameMangler knows how to transform sentences or words into
|
||||
// identifiers that are a better fit in contexts such as:
|
||||
//
|
||||
// - unexported or exported go variable identifiers
|
||||
// - file names
|
||||
// - camel cased identifiers
|
||||
// - ...
|
||||
//
|
||||
// The [NameMangler] is safe for concurrent use, save for its [NameMangler.AddInitialisms] method,
|
||||
// which is not.
|
||||
//
|
||||
// # Known limitations
|
||||
//
|
||||
// At this moment, the [NameMangler] doesn't play well with "all caps" text:
|
||||
//
|
||||
// unless every single upper-cased word is declared as an initialism, capitalized words would generally
|
||||
// not be transformed with the expected result, e.g.
|
||||
//
|
||||
// ToFileName("THIS_IS_ALL_CAPS")
|
||||
//
|
||||
// yields the weird outcome
|
||||
//
|
||||
// "t_h_i_s_i_s_a_l_l_c_a_p_s"
|
||||
type NameMangler struct {
|
||||
options
|
||||
|
||||
index *indexOfInitialisms
|
||||
|
||||
splitter splitter
|
||||
splitterWithPostSplit splitter
|
||||
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// NewNameMangler builds a name mangler ready to convert strings.
|
||||
//
|
||||
// The default name mangler is configured with default common initialisms and all default options.
|
||||
func NewNameMangler(opts ...Option) NameMangler {
|
||||
m := NameMangler{
|
||||
options: optionsWithDefaults(opts),
|
||||
index: newIndexOfInitialisms(),
|
||||
}
|
||||
m.addInitialisms(m.commonInitialisms...)
|
||||
|
||||
// a splitter that returns matches lexemes as ready-to-assemble strings:
|
||||
// details of the lexemes are redeemed.
|
||||
m.splitter = newSplitter(
|
||||
withInitialismsCache(&m.index.initialismsCache),
|
||||
withReplaceFunc(m.replaceFunc),
|
||||
)
|
||||
|
||||
// a splitter that returns matches lexemes ready for post-processing
|
||||
m.splitterWithPostSplit = newSplitter(
|
||||
withInitialismsCache(&m.index.initialismsCache),
|
||||
withReplaceFunc(m.replaceFunc),
|
||||
withPostSplitInitialismCheck,
|
||||
)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// AddInitialisms declares extra initialisms to the mangler.
|
||||
//
|
||||
// It declares extra words as "initialisms" (i.e. words that won't be camel cased or titled cased),
|
||||
// on top of the existing list of common initialisms (such as ID, HTTP...).
|
||||
//
|
||||
// Added words must start with a (unicode) letter. If some don't, they are ignored.
|
||||
// Added words are either fully capitalized or mixed-cased. Lower-case only words are considered capitalized.
|
||||
//
|
||||
// It is typically used just after initializing the [NameMangler].
|
||||
//
|
||||
// When all initialisms are known at the time the mangler is initialized, it is preferable to
|
||||
// use [NewNameMangler] with the option [WithAdditionalInitialisms].
|
||||
//
|
||||
// Adding initialisms mutates the mangler and should not be carried out concurrently with other calls to the mangler.
|
||||
func (m *NameMangler) AddInitialisms(words ...string) {
|
||||
m.addInitialisms(words...)
|
||||
}
|
||||
|
||||
// Initialisms renders the list of initialisms supported by this mangler.
|
||||
func (m *NameMangler) Initialisms() []string {
|
||||
return m.index.initialisms
|
||||
}
|
||||
|
||||
// Camelize a single word.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// - "HELLO" and "hello" become "Hello".
|
||||
func (m NameMangler) Camelize(word string) string {
|
||||
ru := []rune(word)
|
||||
|
||||
switch len(ru) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return string(unicode.ToUpper(ru[0]))
|
||||
default:
|
||||
camelized := poolOfBuffers.BorrowBuffer(len(word))
|
||||
camelized.Grow(len(word))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(camelized)
|
||||
}()
|
||||
|
||||
camelized.WriteRune(unicode.ToUpper(ru[0]))
|
||||
for _, ru := range ru[1:] {
|
||||
camelized.WriteRune(unicode.ToLower(ru))
|
||||
}
|
||||
|
||||
return camelized.String()
|
||||
}
|
||||
}
|
||||
|
||||
// ToFileName generates a suitable snake-case file name from a sentence.
|
||||
//
|
||||
// It lower-cases everything with underscore (_) as a word separator.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - "Hello, Swagger" becomes "hello_swagger"
|
||||
// - "HelloSwagger" becomes "hello_swagger"
|
||||
func (m NameMangler) ToFileName(name string) string {
|
||||
inptr := m.split(name)
|
||||
in := *inptr
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, w := range in {
|
||||
out = append(out, lower(w))
|
||||
}
|
||||
poolOfStrings.RedeemStrings(inptr)
|
||||
|
||||
return strings.Join(out, "_")
|
||||
}
|
||||
|
||||
// ToCommandName generates a suitable CLI command name from a sentence.
|
||||
//
|
||||
// It lower-cases everything with dash (-) as a word separator.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - "Hello, Swagger" becomes "hello-swagger"
|
||||
// - "HelloSwagger" becomes "hello-swagger"
|
||||
func (m NameMangler) ToCommandName(name string) string {
|
||||
inptr := m.split(name)
|
||||
in := *inptr
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, w := range in {
|
||||
out = append(out, lower(w))
|
||||
}
|
||||
poolOfStrings.RedeemStrings(inptr)
|
||||
|
||||
return strings.Join(out, "-")
|
||||
}
|
||||
|
||||
// ToHumanNameLower represents a code name as a human-readable series of words.
|
||||
//
|
||||
// It lower-cases everything with blank space as a word separator.
|
||||
//
|
||||
// NOTE: parts recognized as initialisms just keep their original casing.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - "Hello, Swagger" becomes "hello swagger"
|
||||
// - "HelloSwagger" or "Hello-Swagger" become "hello swagger"
|
||||
func (m NameMangler) ToHumanNameLower(name string) string {
|
||||
s := m.splitterWithPostSplit
|
||||
in := s.split(name)
|
||||
out := make([]string, 0, len(*in))
|
||||
|
||||
for _, w := range *in {
|
||||
if !w.IsInitialism() {
|
||||
out = append(out, lower(w.GetOriginal()))
|
||||
} else {
|
||||
out = append(out, trim(w.GetOriginal()))
|
||||
}
|
||||
}
|
||||
|
||||
poolOfLexems.RedeemLexems(in)
|
||||
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
// ToHumanNameTitle represents a code name as a human-readable series of titleized words.
|
||||
//
|
||||
// It titleizes every word with blank space as a word separator.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - "hello, Swagger" becomes "Hello Swagger"
|
||||
// - "helloSwagger" becomes "Hello Swagger"
|
||||
func (m NameMangler) ToHumanNameTitle(name string) string {
|
||||
s := m.splitterWithPostSplit
|
||||
in := s.split(name)
|
||||
|
||||
out := make([]string, 0, len(*in))
|
||||
for _, w := range *in {
|
||||
original := trim(w.GetOriginal())
|
||||
if !w.IsInitialism() {
|
||||
out = append(out, m.Camelize(original))
|
||||
} else {
|
||||
out = append(out, original)
|
||||
}
|
||||
}
|
||||
poolOfLexems.RedeemLexems(in)
|
||||
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
// ToJSONName generates a camelized single-word version of a sentence.
|
||||
//
|
||||
// The output assembles every camelized word, but for the first word, which
|
||||
// is lower-cased.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// - "Hello_swagger" becomes "helloSwagger"
|
||||
func (m NameMangler) ToJSONName(name string) string {
|
||||
inptr := m.split(name)
|
||||
in := *inptr
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for i, w := range in {
|
||||
if i == 0 {
|
||||
out = append(out, lower(w))
|
||||
continue
|
||||
}
|
||||
out = append(out, m.Camelize(trim(w)))
|
||||
}
|
||||
|
||||
poolOfStrings.RedeemStrings(inptr)
|
||||
|
||||
return strings.Join(out, "")
|
||||
}
|
||||
|
||||
// ToVarName generates a legit unexported go variable name from a sentence.
|
||||
//
|
||||
// The generated name plays well with linters (see also [NameMangler.ToGoName]).
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - "Hello_swagger" becomes "helloSwagger"
|
||||
// - "Http_server" becomes "httpServer"
|
||||
//
|
||||
// This name applies the same rules as [NameMangler.ToGoName] (legit exported variable), save the
|
||||
// capitalization of the initial rune.
|
||||
//
|
||||
// Special case: when the initial part is a recognized as an initialism (like in the example above),
|
||||
// the full part is lower-cased.
|
||||
func (m NameMangler) ToVarName(name string) string {
|
||||
return m.goIdentifier(name, false)
|
||||
}
|
||||
|
||||
// ToGoName generates a legit exported go variable name from a sentence.
|
||||
//
|
||||
// The generated name plays well with most linters.
|
||||
//
|
||||
// ToGoName abides by the go "exported" symbol rule starting with an upper-case letter.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// - "hello_swagger" becomes "HelloSwagger"
|
||||
// - "Http_server" becomes "HTTPServer"
|
||||
//
|
||||
// # Edge cases
|
||||
//
|
||||
// Whenever the first rune is not eligible to upper case, a special prefix is prepended to the resulting name.
|
||||
// By default this is simply "X" and you may customize this behavior using the [WithGoNamePrefixFunc] option.
|
||||
//
|
||||
// This happens when the first rune is not a letter, e.g. a digit, or a symbol that has no word transliteration
|
||||
// (see also [WithReplaceFunc] about symbol transliterations),
|
||||
// as well as for most East Asian or Devanagari runes, for which there is no such concept as upper-case.
|
||||
//
|
||||
// # Linting
|
||||
//
|
||||
// [revive], the successor of golint is the reference linter.
|
||||
//
|
||||
// This means that [NameMangler.ToGoName] supports the initialisms that revive checks (see also [DefaultInitialisms]).
|
||||
//
|
||||
// At this moment, there is no attempt to transliterate unicode into ascii, meaning that some linters
|
||||
// (e.g. asciicheck, gosmopolitan) may croak on go identifiers generated from unicode input.
|
||||
//
|
||||
// [revive]: https://github.com/mgechev/revive
|
||||
func (m NameMangler) ToGoName(name string) string {
|
||||
return m.goIdentifier(name, true)
|
||||
}
|
||||
|
||||
func (m NameMangler) goIdentifier(name string, exported bool) string {
|
||||
s := m.splitterWithPostSplit
|
||||
lexems := s.split(name)
|
||||
defer func() {
|
||||
poolOfLexems.RedeemLexems(lexems)
|
||||
}()
|
||||
lexemes := *lexems
|
||||
|
||||
if len(lexemes) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
result := poolOfBuffers.BorrowBuffer(len(name))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(result)
|
||||
}()
|
||||
|
||||
firstPart := lexemes[0]
|
||||
if !exported {
|
||||
if ok := firstPart.WriteLower(result, true); !ok {
|
||||
// NOTE: an initialism as the first part is lower-cased: no longer generates stuff like hTTPxyz.
|
||||
//
|
||||
// same prefixing rule applied to unexported variable as to an exported one, so that we have consistent
|
||||
// names, whether the generated identifier is exported or not.
|
||||
result.WriteString(strings.ToLower(m.prefixFunc()(name)))
|
||||
result.WriteString(lexemes[0].GetOriginal())
|
||||
}
|
||||
} else {
|
||||
if ok := firstPart.WriteTitleized(result, true); !ok {
|
||||
// "repairs" a lexeme that doesn't start with a letter to become
|
||||
// the start a legit go name. The current strategy is very crude and simply adds a fixed prefix,
|
||||
// e.g. "X".
|
||||
// For instance "1_sesame_street" would be split into lexemes ["1", "sesame", "street"] and
|
||||
// the first one ("1") would result in something like "X1" (with the default prefix function).
|
||||
//
|
||||
// NOTE: no longer forcing the first part to be fully upper-cased
|
||||
result.WriteString(m.prefixFunc()(name))
|
||||
result.WriteString(lexemes[0].GetOriginal())
|
||||
}
|
||||
}
|
||||
|
||||
for _, lexem := range lexemes[1:] {
|
||||
// NOTE: no longer forcing initialism parts to be fully upper-cased:
|
||||
// * pluralized initialism preserve their trailing "s"
|
||||
// * mixed-cased initialisms, such as IPv4, are preserved
|
||||
if ok := lexem.WriteTitleized(result, false); !ok {
|
||||
// it's not titleized: perhaps it's too short, perhaps the first rune is not a letter.
|
||||
// write anyway
|
||||
result.WriteString(lexem.GetOriginal())
|
||||
}
|
||||
}
|
||||
|
||||
return result.String()
|
||||
}
|
||||
|
||||
func (m *NameMangler) addInitialisms(words ...string) {
|
||||
m.index.add(words...)
|
||||
m.index.buildCache()
|
||||
}
|
||||
|
||||
// split calls the inner splitter.
|
||||
func (m NameMangler) split(str string) *[]string {
|
||||
s := m.splitter
|
||||
lexems := s.split(str)
|
||||
result := poolOfStrings.BorrowStrings()
|
||||
|
||||
for _, lexem := range *lexems {
|
||||
*result = append(*result, lexem.GetOriginal())
|
||||
}
|
||||
poolOfLexems.RedeemLexems(lexems)
|
||||
|
||||
return result
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
type (
|
||||
// PrefixFunc defines a safeguard rule (that may depend on the input string), to prefix
|
||||
// a generated go name (in [NameMangler.ToGoName] and [NameMangler.ToVarName]).
|
||||
//
|
||||
// See [NameMangler.ToGoName] for more about which edge cases the prefix function covers.
|
||||
PrefixFunc func(string) string
|
||||
|
||||
// ReplaceFunc is a transliteration function to replace special runes by a word.
|
||||
ReplaceFunc func(r rune) (string, bool)
|
||||
|
||||
// Option to configure a [NameMangler].
|
||||
Option func(*options)
|
||||
|
||||
options struct {
|
||||
commonInitialisms []string
|
||||
|
||||
goNamePrefixFunc PrefixFunc
|
||||
goNamePrefixFuncPtr *PrefixFunc
|
||||
replaceFunc func(r rune) (string, bool)
|
||||
}
|
||||
)
|
||||
|
||||
func (o *options) prefixFunc() PrefixFunc {
|
||||
if o.goNamePrefixFuncPtr != nil && *o.goNamePrefixFuncPtr != nil {
|
||||
return *o.goNamePrefixFuncPtr
|
||||
}
|
||||
|
||||
return o.goNamePrefixFunc
|
||||
}
|
||||
|
||||
// WithGoNamePrefixFunc overrides the default prefix rule to safeguard generated go names.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// This helps convert "123" into "{prefix}123" (a very crude strategy indeed, but it works).
|
||||
//
|
||||
// See [github.com/go-swagger/go-swagger/generator.DefaultFuncMap] for an example.
|
||||
//
|
||||
// The prefix function is assumed to return a string that starts with an upper case letter.
|
||||
//
|
||||
// The default is to prefix with "X".
|
||||
//
|
||||
// See [NameMangler.ToGoName] for more about which edge cases the prefix function covers.
|
||||
func WithGoNamePrefixFunc(fn PrefixFunc) Option {
|
||||
return func(o *options) {
|
||||
o.goNamePrefixFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
// WithGoNamePrefixFuncPtr is like [WithGoNamePrefixFunc] but it specifies a pointer to a function.
|
||||
//
|
||||
// [WithGoNamePrefixFunc] should be preferred in most situations. This option should only serve the
|
||||
// purpose of handling special situations where the prefix function is not an internal variable
|
||||
// (e.g. an exported package global).
|
||||
//
|
||||
// [WithGoNamePrefixFuncPtr] supersedes [WithGoNamePrefixFunc] if it also specified.
|
||||
//
|
||||
// If the provided pointer is nil or points to a nil value, this option has no effect.
|
||||
//
|
||||
// The caller should ensure that no undesirable concurrent changes are applied to the function pointed to.
|
||||
func WithGoNamePrefixFuncPtr(ptr *PrefixFunc) Option {
|
||||
return func(o *options) {
|
||||
o.goNamePrefixFuncPtr = ptr
|
||||
}
|
||||
}
|
||||
|
||||
// WithInitialisms declares the initialisms this mangler supports.
|
||||
//
|
||||
// This supersedes any pre-loaded defaults (see [DefaultInitialisms] for more about what initialisms are).
|
||||
//
|
||||
// It declares words to be recognized as "initialisms" (i.e. words that won't be camel cased or titled cased).
|
||||
//
|
||||
// Words must start with a (unicode) letter. If some don't, they are ignored.
|
||||
// Words are either fully capitalized or mixed-cased. Lower-case only words are considered capitalized.
|
||||
func WithInitialisms(words ...string) Option {
|
||||
return func(o *options) {
|
||||
o.commonInitialisms = words
|
||||
}
|
||||
}
|
||||
|
||||
// WithAdditionalInitialisms adds new initialisms to the currently supported list (see [DefaultInitialisms]).
|
||||
//
|
||||
// The same sanitization rules apply as those described for [WithInitialisms].
|
||||
func WithAdditionalInitialisms(words ...string) Option {
|
||||
return func(o *options) {
|
||||
o.commonInitialisms = append(o.commonInitialisms, words...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithReplaceFunc specifies a custom transliteration function instead of the default.
|
||||
//
|
||||
// The default translates the following characters into words as follows:
|
||||
//
|
||||
// - '@' -> 'At'
|
||||
// - '&' -> 'And'
|
||||
// - '|' -> 'Pipe'
|
||||
// - '$' -> 'Dollar'
|
||||
// - '!' -> 'Bang'
|
||||
//
|
||||
// Notice that the outcome of a transliteration should always be titleized.
|
||||
func WithReplaceFunc(fn ReplaceFunc) Option {
|
||||
return func(o *options) {
|
||||
o.replaceFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
func defaultPrefixFunc(_ string) string {
|
||||
return "X"
|
||||
}
|
||||
|
||||
// defaultReplaceTable finds a word representation for special characters.
|
||||
func defaultReplaceTable(r rune) (string, bool) {
|
||||
switch r {
|
||||
case '@':
|
||||
return "At ", true
|
||||
case '&':
|
||||
return "And ", true
|
||||
case '|':
|
||||
return "Pipe ", true
|
||||
case '$':
|
||||
return "Dollar ", true
|
||||
case '!':
|
||||
return "Bang ", true
|
||||
case '-':
|
||||
return "", true
|
||||
case '_':
|
||||
return "", true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
func optionsWithDefaults(opts []Option) options {
|
||||
o := options{
|
||||
commonInitialisms: DefaultInitialisms(),
|
||||
goNamePrefixFunc: defaultPrefixFunc,
|
||||
replaceFunc: defaultReplaceTable,
|
||||
}
|
||||
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const maxAllocMatches = 8
|
||||
|
||||
type (
|
||||
// memory pools of temporary objects.
|
||||
//
|
||||
// These are used to recycle temporarily allocated objects
|
||||
// and relieve the GC from undue pressure.
|
||||
|
||||
matchesPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
buffersPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
lexemsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
stringsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// poolOfMatches holds temporary slices for recycling during the initialism match process
|
||||
poolOfMatches = matchesPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := make(initialismMatches, 0, maxAllocMatches)
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfBuffers = buffersPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfLexems = lexemsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := make([]nameLexem, 0, maxAllocMatches)
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfStrings = stringsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := make([]string, 0, maxAllocMatches)
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (p matchesPool) BorrowMatches() *initialismMatches {
|
||||
s := p.Get().(*initialismMatches)
|
||||
*s = (*s)[:0] // reset slice, keep allocated capacity
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p buffersPool) BorrowBuffer(size int) *bytes.Buffer {
|
||||
s := p.Get().(*bytes.Buffer)
|
||||
s.Reset()
|
||||
|
||||
if s.Cap() < size {
|
||||
s.Grow(size)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p lexemsPool) BorrowLexems() *[]nameLexem {
|
||||
s := p.Get().(*[]nameLexem)
|
||||
*s = (*s)[:0] // reset slice, keep allocated capacity
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p stringsPool) BorrowStrings() *[]string {
|
||||
s := p.Get().(*[]string)
|
||||
*s = (*s)[:0] // reset slice, keep allocated capacity
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p matchesPool) RedeemMatches(s *initialismMatches) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p buffersPool) RedeemBuffer(s *bytes.Buffer) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p lexemsPool) RedeemLexems(s *[]nameLexem) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p stringsPool) RedeemStrings(s *[]string) {
|
||||
p.Put(s)
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type splitterOption func(*splitter)
|
||||
|
||||
// withPostSplitInitialismCheck allows to catch initialisms after main split process
|
||||
func withPostSplitInitialismCheck(s *splitter) {
|
||||
s.postSplitInitialismCheck = true
|
||||
}
|
||||
|
||||
func withReplaceFunc(fn ReplaceFunc) func(*splitter) {
|
||||
return func(s *splitter) {
|
||||
s.replaceFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
func withInitialismsCache(c *initialismsCache) splitterOption {
|
||||
return func(s *splitter) {
|
||||
s.initialismsCache = c
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
initialismMatch struct {
|
||||
body []rune
|
||||
start, end int
|
||||
complete bool
|
||||
hasPlural pluralForm
|
||||
}
|
||||
initialismMatches []initialismMatch
|
||||
)
|
||||
|
||||
// String representation of a match, e.g. for debugging.
|
||||
func (m initialismMatch) String() string {
|
||||
return fmt.Sprintf("{body: %s (%d), start: %d, end; %d, complete: %t, hasPlural: %v}",
|
||||
string(m.body), len(m.body), m.start, m.end, m.complete, m.hasPlural,
|
||||
)
|
||||
}
|
||||
|
||||
func (m initialismMatch) isZero() bool {
|
||||
return m.start == 0 && m.end == 0
|
||||
}
|
||||
|
||||
type splitter struct {
|
||||
*initialismsCache
|
||||
|
||||
postSplitInitialismCheck bool
|
||||
replaceFunc ReplaceFunc
|
||||
}
|
||||
|
||||
func newSplitter(options ...splitterOption) splitter {
|
||||
var s splitter
|
||||
|
||||
for _, option := range options {
|
||||
option(&s)
|
||||
}
|
||||
|
||||
if s.replaceFunc == nil {
|
||||
s.replaceFunc = defaultReplaceTable
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s splitter) split(name string) *[]nameLexem {
|
||||
nameRunes := []rune(name)
|
||||
matches := s.gatherInitialismMatches(nameRunes)
|
||||
if matches == nil {
|
||||
return poolOfLexems.BorrowLexems()
|
||||
}
|
||||
|
||||
return s.mapMatchesToNameLexems(nameRunes, matches)
|
||||
}
|
||||
|
||||
func (s splitter) gatherInitialismMatches(nameRunes []rune) *initialismMatches {
|
||||
matches := poolOfMatches.BorrowMatches()
|
||||
const minLenInitialism = 1
|
||||
if len(nameRunes) < minLenInitialism+1 {
|
||||
// can't match initialism with 0 or 1 rune
|
||||
return matches
|
||||
}
|
||||
|
||||
// first iteration
|
||||
s.findMatches(matches, nameRunes, nameRunes[0], 0)
|
||||
|
||||
for i, currentRune := range nameRunes[1:] {
|
||||
currentRunePosition := i + 1
|
||||
// recycle allocations as we loop over runes
|
||||
// with such recycling, only 2 slices should be allocated per call
|
||||
// instead of o(n).
|
||||
//
|
||||
// BorrowMatches always yields slices with zero length (with some capacity)
|
||||
newMatches := poolOfMatches.BorrowMatches()
|
||||
|
||||
// check current initialism matches
|
||||
for _, match := range *matches {
|
||||
if keepCompleteMatch := match.complete; keepCompleteMatch {
|
||||
// the match is already complete: keep it then move on to the next match
|
||||
*newMatches = append(*newMatches, match)
|
||||
continue
|
||||
}
|
||||
|
||||
if currentRunePosition-match.start == len(match.body) {
|
||||
// unmatched: skip
|
||||
continue
|
||||
}
|
||||
|
||||
// 1. by construction of the matches, we can't have currentRunePosition - match.start < 0
|
||||
// because matches have been computed with their start <= currentRunePosition in the previous
|
||||
// iterations.
|
||||
// 2. by construction of the matches, we can't have currentRunePosition - match.start >= len(match.body)
|
||||
|
||||
currentMatchRune := match.body[currentRunePosition-match.start]
|
||||
if currentMatchRune != currentRune {
|
||||
// failed match, discard it then move on to the next match
|
||||
continue
|
||||
}
|
||||
|
||||
// try to complete the current match
|
||||
if currentRunePosition-match.start == len(match.body)-1 {
|
||||
// we are close: the next step is to check the symbol ahead
|
||||
// if it is a lowercase letter, then it is not the end of match
|
||||
// but the beginning of the next word.
|
||||
//
|
||||
// NOTE(fredbi): this heuristic sometimes leads to counterintuitive splits and
|
||||
// perhaps (not sure yet) we should check against case _alternance_.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// In the current version, in the sentence "IDS initialism", "ID" is recognized as an initialism,
|
||||
// leading to a split like "id_s_initialism" (or IDSInitialism),
|
||||
// whereas in the sentence "IDx initialism", it is not and produces something like
|
||||
// "i_d_x_initialism" (or IDxInitialism). The generated file name is not great.
|
||||
//
|
||||
// Both go identifiers are tolerated by linters.
|
||||
//
|
||||
// Notice that the slightly different input "IDs initialism" is correctly detected
|
||||
// as a pluralized initialism and produces something like "ids_initialism" (or IDsInitialism).
|
||||
|
||||
if currentRunePosition < len(nameRunes)-1 { // when before the last rune
|
||||
nextRune := nameRunes[currentRunePosition+1]
|
||||
|
||||
// recognize a plural form for this initialism (only simple english pluralization is supported).
|
||||
if nextRune == 's' && match.hasPlural == simplePlural {
|
||||
// detected a pluralized initialism
|
||||
match.body = append(match.body, nextRune)
|
||||
lookAhead := currentRunePosition + 1
|
||||
if lookAhead < len(nameRunes)-1 {
|
||||
nextRune = nameRunes[lookAhead+1]
|
||||
if newWord := unicode.IsLower(nextRune); newWord {
|
||||
// it is the start of a new word.
|
||||
// Match is only partial and the initialism is not recognized:
|
||||
// move on to the next match, but do not advance the rune position
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// this is a pluralized match: keep it
|
||||
currentRunePosition++
|
||||
match.complete = true
|
||||
match.hasPlural = simplePlural
|
||||
match.end = currentRunePosition
|
||||
*newMatches = append(*newMatches, match)
|
||||
|
||||
// match is complete: keep it then move on to the next match
|
||||
continue
|
||||
}
|
||||
|
||||
// other cases
|
||||
// example: invariant plural such as "TLS"
|
||||
if newWord := unicode.IsLower(nextRune); newWord {
|
||||
// it is the start of a new word
|
||||
// Match is only partial and the initialism is not recognized : move on
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
match.complete = true
|
||||
match.end = currentRunePosition
|
||||
}
|
||||
|
||||
// append the ongoing matching attempt: it is not necessarily complete, but was successful so far.
|
||||
// Let's see if it still matches on the next rune.
|
||||
*newMatches = append(*newMatches, match)
|
||||
}
|
||||
|
||||
s.findMatches(newMatches, nameRunes, currentRune, currentRunePosition)
|
||||
|
||||
poolOfMatches.RedeemMatches(matches)
|
||||
matches = newMatches
|
||||
}
|
||||
|
||||
// it is up to the caller to redeem this last slice
|
||||
return matches
|
||||
}
|
||||
|
||||
func (s splitter) findMatches(newMatches *initialismMatches, nameRunes []rune, currentRune rune, currentRunePosition int) {
|
||||
// check for new initialism matches, based on the first character
|
||||
for i, r := range s.initialismsRunes {
|
||||
if r[0] != currentRune {
|
||||
continue
|
||||
}
|
||||
|
||||
if currentRunePosition+len(r) > len(nameRunes) {
|
||||
continue // not eligible: would spilll over the initial string
|
||||
}
|
||||
|
||||
// possible matches: all initialisms starting with the current rune and that can fit the given string (nameRunes)
|
||||
*newMatches = append(*newMatches, initialismMatch{
|
||||
start: currentRunePosition,
|
||||
body: r,
|
||||
complete: false,
|
||||
hasPlural: s.initialismsPluralForm[i],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s splitter) mapMatchesToNameLexems(nameRunes []rune, matches *initialismMatches) *[]nameLexem {
|
||||
nameLexems := poolOfLexems.BorrowLexems()
|
||||
|
||||
var lastAcceptedMatch initialismMatch
|
||||
for _, match := range *matches {
|
||||
if !match.complete {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstMatch := lastAcceptedMatch.isZero(); firstMatch {
|
||||
s.appendBrokenDownCasualString(nameLexems, nameRunes[:match.start])
|
||||
*nameLexems = append(*nameLexems, s.breakInitialism(string(match.body)))
|
||||
|
||||
lastAcceptedMatch = match
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if overlappedMatch := match.start <= lastAcceptedMatch.end; overlappedMatch {
|
||||
continue
|
||||
}
|
||||
|
||||
middle := nameRunes[lastAcceptedMatch.end+1 : match.start]
|
||||
s.appendBrokenDownCasualString(nameLexems, middle)
|
||||
*nameLexems = append(*nameLexems, s.breakInitialism(string(match.body)))
|
||||
|
||||
lastAcceptedMatch = match
|
||||
}
|
||||
|
||||
// we have not found any accepted matches
|
||||
if lastAcceptedMatch.isZero() {
|
||||
*nameLexems = (*nameLexems)[:0]
|
||||
s.appendBrokenDownCasualString(nameLexems, nameRunes)
|
||||
} else if lastAcceptedMatch.end+1 != len(nameRunes) {
|
||||
rest := nameRunes[lastAcceptedMatch.end+1:]
|
||||
s.appendBrokenDownCasualString(nameLexems, rest)
|
||||
}
|
||||
|
||||
poolOfMatches.RedeemMatches(matches)
|
||||
|
||||
return nameLexems
|
||||
}
|
||||
|
||||
func (s splitter) breakInitialism(original string) nameLexem {
|
||||
return newInitialismNameLexem(original, original)
|
||||
}
|
||||
|
||||
func (s splitter) appendBrokenDownCasualString(segments *[]nameLexem, str []rune) {
|
||||
currentSegment := poolOfBuffers.BorrowBuffer(len(str)) // unlike strings.Builder, bytes.Buffer initial storage can reused
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(currentSegment)
|
||||
}()
|
||||
|
||||
addCasualNameLexem := func(original string) {
|
||||
*segments = append(*segments, newCasualNameLexem(original))
|
||||
}
|
||||
|
||||
addInitialismNameLexem := func(original, match string) {
|
||||
*segments = append(*segments, newInitialismNameLexem(original, match))
|
||||
}
|
||||
|
||||
var addNameLexem func(string)
|
||||
if s.postSplitInitialismCheck {
|
||||
addNameLexem = func(original string) {
|
||||
for i := range s.initialisms {
|
||||
if isEqualFoldIgnoreSpace(s.initialismsUpperCased[i], original) {
|
||||
addInitialismNameLexem(original, s.initialisms[i])
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
addCasualNameLexem(original)
|
||||
}
|
||||
} else {
|
||||
addNameLexem = addCasualNameLexem
|
||||
}
|
||||
|
||||
// NOTE: (performance). The few remaining non-amortized allocations
|
||||
// lay in the code below: using String() forces
|
||||
for _, rn := range str {
|
||||
if replace, found := s.replaceFunc(rn); found {
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
if replace != "" {
|
||||
addNameLexem(replace)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !unicode.In(rn, unicode.L, unicode.M, unicode.N, unicode.Pc) {
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if unicode.IsUpper(rn) {
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
}
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
currentSegment.WriteRune(rn)
|
||||
}
|
||||
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+4
-1
@@ -1,4 +1,7 @@
|
||||
package swag
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
import "unsafe"
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mangling
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Removes leading whitespaces
|
||||
func trim(str string) string { return strings.TrimSpace(str) }
|
||||
|
||||
// upper is strings.ToUpper() combined with trim
|
||||
func upper(str string) string {
|
||||
return strings.ToUpper(trim(str))
|
||||
}
|
||||
|
||||
// lower is strings.ToLower() combined with trim
|
||||
func lower(str string) string {
|
||||
return strings.ToLower(trim(str))
|
||||
}
|
||||
|
||||
// isEqualFoldIgnoreSpace is the same as strings.EqualFold, but
|
||||
// it ignores leading and trailing blank spaces in the compared
|
||||
// string.
|
||||
//
|
||||
// base is assumed to be composed of upper-cased runes, and be already
|
||||
// trimmed.
|
||||
//
|
||||
// This code is heavily inspired from strings.EqualFold.
|
||||
func isEqualFoldIgnoreSpace(base []rune, str string) bool {
|
||||
var i, baseIndex int
|
||||
// equivalent to b := []byte(str), but without data copy
|
||||
b := hackStringBytes(str)
|
||||
|
||||
for i < len(b) {
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// fast path for ASCII
|
||||
if c != ' ' && c != '\t' {
|
||||
break
|
||||
}
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if !unicode.IsSpace(r) {
|
||||
break
|
||||
}
|
||||
i += size
|
||||
}
|
||||
|
||||
if i >= len(b) {
|
||||
return len(base) == 0
|
||||
}
|
||||
|
||||
for _, baseRune := range base {
|
||||
if i >= len(b) {
|
||||
break
|
||||
}
|
||||
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// single byte rune case (ASCII)
|
||||
if baseRune >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
|
||||
baseChar := byte(baseRune)
|
||||
if c != baseChar && ((c < 'a') || (c > 'z') || (c-'a'+'A' != baseChar)) {
|
||||
return false
|
||||
}
|
||||
|
||||
baseIndex++
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if unicode.ToUpper(r) != baseRune {
|
||||
return false
|
||||
}
|
||||
baseIndex++
|
||||
i += size
|
||||
}
|
||||
|
||||
if baseIndex != len(base) {
|
||||
return false
|
||||
}
|
||||
|
||||
// all passed: now we should only have blanks
|
||||
for i < len(b) {
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// fast path for ASCII
|
||||
if c != ' ' && c != '\t' {
|
||||
return false
|
||||
}
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
|
||||
i += size
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package swag
|
||||
|
||||
import "github.com/go-openapi/swag/mangling"
|
||||
|
||||
// GoNamePrefixFunc sets an optional rule to prefix go names
|
||||
// which do not start with a letter.
|
||||
//
|
||||
// GoNamePrefixFunc should not be written to while concurrently using the other mangling functions of this package.
|
||||
//
|
||||
// Deprecated: use [mangling.WithGoNamePrefixFunc] instead.
|
||||
var GoNamePrefixFunc mangling.PrefixFunc
|
||||
|
||||
// swagNameMangler is a global instance of the name mangler specifically alloted
|
||||
// to support deprecated functions.
|
||||
var swagNameMangler = mangling.NewNameMangler(
|
||||
mangling.WithGoNamePrefixFuncPtr(&GoNamePrefixFunc),
|
||||
)
|
||||
|
||||
// AddInitialisms adds additional initialisms to the default list (see [mangling.DefaultInitialisms]).
|
||||
//
|
||||
// AddInitialisms is not safe to be called concurrently.
|
||||
//
|
||||
// Deprecated: use [mangling.WithAdditionalInitialisms] instead.
|
||||
func AddInitialisms(words ...string) {
|
||||
swagNameMangler.AddInitialisms(words...)
|
||||
}
|
||||
|
||||
// Camelize a single word.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.Camelize] instead.
|
||||
func Camelize(word string) string { return swagNameMangler.Camelize(word) }
|
||||
|
||||
// ToFileName lowercases and underscores a go type name.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.ToFileName] instead.
|
||||
func ToFileName(name string) string { return swagNameMangler.ToFileName(name) }
|
||||
|
||||
// ToCommandName lowercases and underscores a go type name.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.ToCommandName] instead.
|
||||
func ToCommandName(name string) string { return swagNameMangler.ToCommandName(name) }
|
||||
|
||||
// ToHumanNameLower represents a code name as a human series of words.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.ToHumanNameLower] instead.
|
||||
func ToHumanNameLower(name string) string { return swagNameMangler.ToHumanNameLower(name) }
|
||||
|
||||
// ToHumanNameTitle represents a code name as a human series of words with the first letters titleized.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.ToHumanNameTitle] instead.
|
||||
func ToHumanNameTitle(name string) string { return swagNameMangler.ToHumanNameTitle(name) }
|
||||
|
||||
// ToJSONName camel-cases a name which can be underscored or pascal-cased.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.ToJSONName] instead.
|
||||
func ToJSONName(name string) string { return swagNameMangler.ToJSONName(name) }
|
||||
|
||||
// ToVarName camel-cases a name which can be underscored or pascal-cased.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.ToVarName] instead.
|
||||
func ToVarName(name string) string { return swagNameMangler.ToVarName(name) }
|
||||
|
||||
// ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes.
|
||||
//
|
||||
// Deprecated: use [mangling.NameMangler.ToGoName] instead.
|
||||
func ToGoName(name string) string { return swagNameMangler.ToGoName(name) }
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type (
|
||||
lexemKind uint8
|
||||
|
||||
nameLexem struct {
|
||||
original string
|
||||
matchedInitialism string
|
||||
kind lexemKind
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
lexemKindCasualName lexemKind = iota
|
||||
lexemKindInitialismName
|
||||
)
|
||||
|
||||
func newInitialismNameLexem(original, matchedInitialism string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindInitialismName,
|
||||
original: original,
|
||||
matchedInitialism: matchedInitialism,
|
||||
}
|
||||
}
|
||||
|
||||
func newCasualNameLexem(original string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindCasualName,
|
||||
original: original,
|
||||
}
|
||||
}
|
||||
|
||||
func (l nameLexem) GetUnsafeGoName() string {
|
||||
if l.kind == lexemKindInitialismName {
|
||||
return l.matchedInitialism
|
||||
}
|
||||
|
||||
var (
|
||||
first rune
|
||||
rest string
|
||||
)
|
||||
|
||||
for i, orig := range l.original {
|
||||
if i == 0 {
|
||||
first = orig
|
||||
continue
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
rest = l.original[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(l.original) > 1 {
|
||||
b := poolOfBuffers.BorrowBuffer(utf8.UTFMax + len(rest))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(b)
|
||||
}()
|
||||
b.WriteRune(unicode.ToUpper(first))
|
||||
b.WriteString(lower(rest))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l nameLexem) GetOriginal() string {
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l nameLexem) IsInitialism() bool {
|
||||
return l.kind == lexemKindInitialismName
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// 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.
|
||||
|
||||
package swag
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// SplitHostPort splits a network address into a host and a port.
|
||||
// The port is -1 when there is no port to be found
|
||||
func SplitHostPort(addr string) (host string, port int, err error) {
|
||||
h, p, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return "", -1, err
|
||||
}
|
||||
if p == "" {
|
||||
return "", -1, &net.AddrError{Err: "missing port in address", Addr: addr}
|
||||
}
|
||||
|
||||
pi, err := strconv.Atoi(p)
|
||||
if err != nil {
|
||||
return "", -1, err
|
||||
}
|
||||
return h, pi, nil
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package netutils provides helpers for network-related tasks.
|
||||
package netutils
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user