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:
Bruno Chauvet
2026-05-04 08:30:29 +03:00
committed by Ciprian Hacman
parent 63808794e6
commit ddfb2c48d6
279 changed files with 17780 additions and 29128 deletions
+181
View File
@@ -0,0 +1,181 @@
# git-cliff ~ configuration file
# https://git-cliff.org/docs/configuration
[changelog]
header = """
"""
footer = """
-----
**[{{ remote.github.repo }}]({{ self::remote_url() }}) license terms**
[![License][license-badge]][license-url]
[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg
[license-url]: {{ self::remote_url() }}/?tab=Apache-2.0-1-ov-file#readme
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
"""
body = """
{%- if version %}
## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{%- else %}
## [unreleased]
{%- endif %}
{%- if message %}
{%- raw %}\n{% endraw %}
{{ message }}
{%- raw %}\n{% endraw %}
{%- endif %}
{%- if version %}
{%- if previous.version %}
**Full Changelog**: <{{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}>
{%- endif %}
{%- else %}
{%- raw %}\n{% endraw %}
{%- endif %}
{%- if statistics %}{% if statistics.commit_count %}
{%- raw %}\n{% endraw %}
{{ statistics.commit_count }} commits in this release.
{%- raw %}\n{% endraw %}
{%- endif %}{% endif %}
-----
{%- for group, commits in commits | group_by(attribute="group") %}
{%- raw %}\n{% endraw %}
### {{ group | upper_first }}
{%- raw %}\n{% endraw %}
{%- for commit in commits %}
{%- if commit.remote.pr_title %}
{%- set commit_message = commit.remote.pr_title %}
{%- else %}
{%- set commit_message = commit.message %}
{%- endif %}
* {{ commit_message | split(pat="\n") | first | trim }}
{%- if commit.remote.username %}
{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }})
{%- endif %}
{%- if commit.remote.pr_number %}
{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }})
{%- endif %}
{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }})
{%- endfor %}
{%- endfor %}
{%- if github %}
{%- raw %}\n{% endraw -%}
{%- set all_contributors = github.contributors | length %}
{%- if github.contributors | filter(attribute="username", value="dependabot[bot]") | length < all_contributors %}
-----
### People who contributed to this release
{% endif %}
{%- for contributor in github.contributors | filter(attribute="username") | sort(attribute="username") %}
{%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %}
* [@{{ contributor.username }}](https://github.com/{{ contributor.username }})
{%- endif %}
{%- endfor %}
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
-----
{%- raw %}\n{% endraw %}
### New Contributors
{%- endif %}
{%- for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
{%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %}
* @{{ contributor.username }} made their first contribution
{%- if contributor.pr_number %}
in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
{%- endif %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- raw %}\n{% endraw %}
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
"""
# Remove leading and trailing whitespaces from the changelog's body.
trim = true
# Render body even when there are no releases to process.
render_always = true
# An array of regex based postprocessors to modify the changelog.
postprocessors = [
# Replace the placeholder <REPO> with a URL.
#{ pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" },
]
# output file path
# output = "test.md"
[git]
# Parse commits according to the conventional commits specification.
# See https://www.conventionalcommits.org
conventional_commits = false
# Exclude commits that do not match the conventional commits specification.
filter_unconventional = false
# Require all commits to be conventional.
# Takes precedence over filter_unconventional.
require_conventional = false
# Split commits on newlines, treating each line as an individual commit.
split_commits = false
# An array of regex based parsers to modify commit messages prior to further processing.
commit_preprocessors = [
# Replace issue numbers with link templates to be updated in `changelog.postprocessors`.
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
# Check spelling of the commit message using https://github.com/crate-ci/typos.
# If the spelling is incorrect, it will be fixed automatically.
#{ pattern = '.*', replace_command = 'typos --write-changes -' }
]
# Prevent commits that are breaking from being excluded by commit parsers.
protect_breaking_commits = false
# An array of regex based parsers for extracting data from the commit message.
# Assigns commits to groups.
# Optionally sets the commit's scope and can decide to exclude commits from further processing.
commit_parsers = [
{ message = "^[Cc]hore\\([Rr]elease\\): prepare for", skip = true },
{ message = "(^[Mm]erge)|([Mm]erge conflict)", skip = true },
{ field = "author.name", pattern = "dependabot*", group = "<!-- 0A -->Updates" },
{ message = "([Ss]ecurity)|([Vv]uln)", group = "<!-- 08 -->Security" },
{ body = "(.*[Ss]ecurity)|([Vv]uln)", group = "<!-- 08 -->Security" },
{ message = "([Cc]hore\\(lint\\))|(style)|(lint)|(codeql)|(golangci)", group = "<!-- 05 -->Code quality" },
{ message = "(^[Dd]oc)|((?i)readme)|(badge)|(typo)|(documentation)", group = "<!-- 03 -->Documentation" },
{ message = "(^[Ff]eat)|(^[Ee]nhancement)", group = "<!-- 00 -->Implemented enhancements" },
{ message = "(^ci)|(\\(ci\\))|(fixup\\s+ci)|(fix\\s+ci)|(license)|(example)", group = "<!-- 07 -->Miscellaneous tasks" },
{ message = "^test", group = "<!-- 06 -->Testing" },
{ message = "(^fix)|(panic)", group = "<!-- 01 -->Fixed bugs" },
{ message = "(^refact)|(rework)", group = "<!-- 02 -->Refactor" },
{ message = "(^[Pp]erf)|(performance)", group = "<!-- 04 -->Performance" },
{ message = "(^[Cc]hore)", group = "<!-- 07 -->Miscellaneous tasks" },
{ message = "^[Rr]evert", group = "<!-- 09 -->Reverted changes" },
{ message = "(upgrade.*?go)|(go\\s+version)", group = "<!-- 0A -->Updates" },
{ message = ".*", group = "<!-- 0B -->Other" },
]
# Exclude commits that are not matched by any commit parser.
filter_commits = false
# An array of link parsers for extracting external references, and turning them into URLs, using regex.
link_parsers = []
# Include only the tags that belong to the current branch.
use_branch_tags = false
# Order releases topologically instead of chronologically.
topo_order = false
# Order releases topologically instead of chronologically.
topo_order_commits = true
# Order of commits in each group/release within the changelog.
# Allowed values: newest, oldest
sort_commits = "newest"
# Process submodules commits
recurse_submodules = false
#[remote.github]
#owner = "go-openapi"
+6 -1
View File
@@ -1 +1,6 @@
secrets.yml
*.out
*.cov
.idea
.env
.mcp.json
.claude/
+61 -55
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,4 +1,3 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
+39
View File
@@ -0,0 +1,39 @@
Copyright 2015-2025 go-swagger maintainers
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
This software library, github.com/go-openapi/jsonpointer, includes software developed
by the go-swagger and go-openapi maintainers ("go-swagger maintainers").
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this software except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
This software is copied from, derived from, and inspired by other original software products.
It ships with copies of other software which license terms are recalled below.
The original software was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com).
github.com/sigh-399/jsonpointer
===========================
// SPDX-FileCopyrightText: Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
// SPDX-License-Identifier: Apache-2.0
Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+150 -11
View File
@@ -1,19 +1,158 @@
# gojsonpointer [![Build Status](https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer)
# jsonpointer
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer.svg)](https://pkg.go.dev/github.com/go-openapi/jsonpointer)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonpointer)](https://goreportcard.com/report/github.com/go-openapi/jsonpointer)
<!-- Badges: status -->
[![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vuln scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url]
<!-- Badges: release & docker images -->
<!-- Badges: code quality -->
<!-- Badges: license & compliance -->
[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url]
<!-- Badges: documentation & support -->
<!-- Badges: others & stats -->
[![GoDoc][godoc-badge]][godoc-url] [![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
View File
@@ -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
View File
@@ -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
View File
@@ -1,28 +1,7 @@
// Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// author sigu-399
// author-github https://github.com/sigu-399
// author-mail sigu.399@gmail.com
//
// repository-name jsonpointer
// repository-desc An implementation of JSON Pointer - Go language
//
// description Main and unique file.
//
// created 25-02-2013
// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
// Package jsonpointer provides a golang implementation for json pointers.
package jsonpointer
import (
@@ -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
View File
@@ -0,0 +1,181 @@
# git-cliff ~ configuration file
# https://git-cliff.org/docs/configuration
[changelog]
header = """
"""
footer = """
-----
**[{{ remote.github.repo }}]({{ self::remote_url() }}) license terms**
[![License][license-badge]][license-url]
[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg
[license-url]: {{ self::remote_url() }}/?tab=Apache-2.0-1-ov-file#readme
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
"""
body = """
{%- if version %}
## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{%- else %}
## [unreleased]
{%- endif %}
{%- if message %}
{%- raw %}\n{% endraw %}
{{ message }}
{%- raw %}\n{% endraw %}
{%- endif %}
{%- if version %}
{%- if previous.version %}
**Full Changelog**: <{{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}>
{%- endif %}
{%- else %}
{%- raw %}\n{% endraw %}
{%- endif %}
{%- if statistics %}{% if statistics.commit_count %}
{%- raw %}\n{% endraw %}
{{ statistics.commit_count }} commits in this release.
{%- raw %}\n{% endraw %}
{%- endif %}{% endif %}
-----
{%- for group, commits in commits | group_by(attribute="group") %}
{%- raw %}\n{% endraw %}
### {{ group | upper_first }}
{%- raw %}\n{% endraw %}
{%- for commit in commits %}
{%- if commit.remote.pr_title %}
{%- set commit_message = commit.remote.pr_title %}
{%- else %}
{%- set commit_message = commit.message %}
{%- endif %}
* {{ commit_message | split(pat="\n") | first | trim }}
{%- if commit.remote.username %}
{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }})
{%- endif %}
{%- if commit.remote.pr_number %}
{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }})
{%- endif %}
{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }})
{%- endfor %}
{%- endfor %}
{%- if github %}
{%- raw %}\n{% endraw -%}
{%- set all_contributors = github.contributors | length %}
{%- if github.contributors | filter(attribute="username", value="dependabot[bot]") | length < all_contributors %}
-----
### People who contributed to this release
{% endif %}
{%- for contributor in github.contributors | filter(attribute="username") | sort(attribute="username") %}
{%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %}
* [@{{ contributor.username }}](https://github.com/{{ contributor.username }})
{%- endif %}
{%- endfor %}
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
-----
{%- raw %}\n{% endraw %}
### New Contributors
{%- endif %}
{%- for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
{%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %}
* @{{ contributor.username }} made their first contribution
{%- if contributor.pr_number %}
in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
{%- endif %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- raw %}\n{% endraw %}
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
"""
# Remove leading and trailing whitespaces from the changelog's body.
trim = true
# Render body even when there are no releases to process.
render_always = true
# An array of regex based postprocessors to modify the changelog.
postprocessors = [
# Replace the placeholder <REPO> with a URL.
#{ pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" },
]
# output file path
# output = "test.md"
[git]
# Parse commits according to the conventional commits specification.
# See https://www.conventionalcommits.org
conventional_commits = false
# Exclude commits that do not match the conventional commits specification.
filter_unconventional = false
# Require all commits to be conventional.
# Takes precedence over filter_unconventional.
require_conventional = false
# Split commits on newlines, treating each line as an individual commit.
split_commits = false
# An array of regex based parsers to modify commit messages prior to further processing.
commit_preprocessors = [
# Replace issue numbers with link templates to be updated in `changelog.postprocessors`.
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
# Check spelling of the commit message using https://github.com/crate-ci/typos.
# If the spelling is incorrect, it will be fixed automatically.
#{ pattern = '.*', replace_command = 'typos --write-changes -' }
]
# Prevent commits that are breaking from being excluded by commit parsers.
protect_breaking_commits = false
# An array of regex based parsers for extracting data from the commit message.
# Assigns commits to groups.
# Optionally sets the commit's scope and can decide to exclude commits from further processing.
commit_parsers = [
{ message = "^[Cc]hore\\([Rr]elease\\): prepare for", skip = true },
{ message = "(^[Mm]erge)|([Mm]erge conflict)", skip = true },
{ field = "author.name", pattern = "dependabot*", group = "<!-- 0A -->Updates" },
{ message = "([Ss]ecurity)|([Vv]uln)", group = "<!-- 08 -->Security" },
{ body = "(.*[Ss]ecurity)|([Vv]uln)", group = "<!-- 08 -->Security" },
{ message = "([Cc]hore\\(lint\\))|(style)|(lint)|(codeql)|(golangci)", group = "<!-- 05 -->Code quality" },
{ message = "(^[Dd]oc)|((?i)readme)|(badge)|(typo)|(documentation)", group = "<!-- 03 -->Documentation" },
{ message = "(^[Ff]eat)|(^[Ee]nhancement)", group = "<!-- 00 -->Implemented enhancements" },
{ message = "(^ci)|(\\(ci\\))|(fixup\\s+ci)|(fix\\s+ci)|(license)|(example)", group = "<!-- 07 -->Miscellaneous tasks" },
{ message = "^test", group = "<!-- 06 -->Testing" },
{ message = "(^fix)|(panic)", group = "<!-- 01 -->Fixed bugs" },
{ message = "(^refact)|(rework)", group = "<!-- 02 -->Refactor" },
{ message = "(^[Pp]erf)|(performance)", group = "<!-- 04 -->Performance" },
{ message = "(^[Cc]hore)", group = "<!-- 07 -->Miscellaneous tasks" },
{ message = "^[Rr]evert", group = "<!-- 09 -->Reverted changes" },
{ message = "(upgrade.*?go)|(go\\s+version)", group = "<!-- 0A -->Updates" },
{ message = ".*", group = "<!-- 0B -->Other" },
]
# Exclude commits that are not matched by any commit parser.
filter_commits = false
# An array of link parsers for extracting external references, and turning them into URLs, using regex.
link_parsers = []
# Include only the tags that belong to the current branch.
use_branch_tags = false
# Order releases topologically instead of chronologically.
topo_order = false
# Order releases topologically instead of chronologically.
topo_order_commits = true
# Order of commits in each group/release within the changelog.
# Allowed values: newest, oldest
sort_commits = "newest"
# Process submodules commits
recurse_submodules = false
#[remote.github]
#owner = "go-openapi"
+26
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,39 @@
Copyright 2015-2025 go-swagger maintainers
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0
This software library, github.com/go-openapi/jsonpointer, includes software developed
by the go-swagger and go-openapi maintainers ("go-swagger maintainers").
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this software except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
This software is copied from, derived from, and inspired by other original software products.
It ships with copies of other software which license terms are recalled below.
The original software was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com).
github.com/sigh-399/jsonpointer
===========================
// SPDX-FileCopyrightText: Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
// SPDX-License-Identifier: Apache-2.0
Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+91 -7
View File
@@ -1,15 +1,99 @@
# gojsonreference [![Build Status](https://travis-ci.org/go-openapi/jsonreference.svg?branch=master)](https://travis-ci.org/go-openapi/jsonreference) [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
# jsonreference
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](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
View File
@@ -0,0 +1,19 @@
# Security Policy
This policy outlines the commitment and practices of the go-openapi maintainers regarding security.
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 0.22.x | :white_check_mark: |
## Reporting a vulnerability
If you become aware of a security vulnerability that affects the current repository,
please report it privately to the maintainers.
Please follow the instructions provided by github to
[Privately report a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability).
TL;DR: on Github, navigate to the project's "Security" tab then click on "Report a vulnerability".
+8 -3
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,4 @@
ignore:
- jsonutils/fixtures_test
- jsonutils/adapters/ifaces/mocks
- jsonutils/adapters/testintegration/benchmarks
+2
View File
@@ -3,3 +3,5 @@ vendor
Godeps
.idea
*.out
.mcp.json
.claude/
+73 -55
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,23 +1,224 @@
# Swag [![Build Status](https://github.com/go-openapi/swag/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/swag/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/swag)
# Swag
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/swag.svg)](https://pkg.go.dev/github.com/go-openapi/swag)
[![Go Report Card](https://goreportcard.com/badge/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
View File
@@ -0,0 +1,19 @@
# Security Policy
This policy outlines the commitment and practices of the go-openapi maintainers regarding security.
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 0.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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
}
@@ -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
View File
@@ -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() {}
@@ -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() {
}
@@ -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
@@ -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}
}
@@ -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
}
}
@@ -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)
}
}
@@ -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
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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())
}
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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