monitoring dashboard dependency vendor
Signed-off-by: junotx <junotx@126.com>
This commit is contained in:
21
vendor/github.com/MakeNowJust/heredoc/LICENSE
generated
vendored
Normal file
21
vendor/github.com/MakeNowJust/heredoc/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2017 TSUYUSATO Kitsune
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
53
vendor/github.com/MakeNowJust/heredoc/README.md
generated
vendored
Normal file
53
vendor/github.com/MakeNowJust/heredoc/README.md
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# heredoc [](https://circleci.com/gh/MakeNowJust/heredoc) [](https://gowalker.org/github.com/MakeNowJust/heredoc)
|
||||
|
||||
## About
|
||||
|
||||
Package heredoc provides the here-document with keeping indent.
|
||||
|
||||
## Install
|
||||
|
||||
```console
|
||||
$ go get github.com/MakeNowJust/heredoc
|
||||
```
|
||||
|
||||
## Import
|
||||
|
||||
```go
|
||||
// usual
|
||||
import "github.com/MakeNowJust/heredoc"
|
||||
// shortcuts
|
||||
import . "github.com/MakeNowJust/heredoc/dot"
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/MakeNowJust/heredoc/dot"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(D(`
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, ...
|
||||
`))
|
||||
// Output:
|
||||
// Lorem ipsum dolor sit amet, consectetur adipisicing elit,
|
||||
// sed do eiusmod tempor incididunt ut labore et dolore magna
|
||||
// aliqua. Ut enim ad minim veniam, ...
|
||||
//
|
||||
}
|
||||
```
|
||||
|
||||
## API Document
|
||||
|
||||
- [Go Walker - github.com/MakeNowJust/heredoc](https://gowalker.org/github.com/MakeNowJust/heredoc)
|
||||
- [Go Walker - github.com/MakeNowJust/heredoc/dot](https://gowalker.org/github.com/MakeNowJust/heredoc/dot)
|
||||
|
||||
## License
|
||||
|
||||
This software is released under the MIT License, see LICENSE.
|
||||
98
vendor/github.com/MakeNowJust/heredoc/heredoc.go
generated
vendored
Normal file
98
vendor/github.com/MakeNowJust/heredoc/heredoc.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2014-2017 TSUYUSATO Kitsune
|
||||
// This software is released under the MIT License.
|
||||
// http://opensource.org/licenses/mit-license.php
|
||||
|
||||
// Package heredoc provides creation of here-documents from raw strings.
|
||||
//
|
||||
// Golang supports raw-string syntax.
|
||||
// doc := `
|
||||
// Foo
|
||||
// Bar
|
||||
// `
|
||||
// But raw-string cannot recognize indentation. Thus such content is an indented string, equivalent to
|
||||
// "\n\tFoo\n\tBar\n"
|
||||
// I dont't want this!
|
||||
//
|
||||
// However this problem is solved by package heredoc.
|
||||
// doc := heredoc.Doc(`
|
||||
// Foo
|
||||
// Bar
|
||||
// `)
|
||||
// Is equivalent to
|
||||
// "Foo\nBar\n"
|
||||
package heredoc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const maxInt = int(^uint(0) >> 1)
|
||||
|
||||
// Doc returns un-indented string as here-document.
|
||||
func Doc(raw string) string {
|
||||
skipFirstLine := false
|
||||
if len(raw) > 0 && raw[0] == '\n' {
|
||||
raw = raw[1:]
|
||||
} else {
|
||||
skipFirstLine = true
|
||||
}
|
||||
|
||||
lines := strings.Split(raw, "\n")
|
||||
|
||||
minIndentSize := getMinIndent(lines, skipFirstLine)
|
||||
lines = removeIndentation(lines, minIndentSize, skipFirstLine)
|
||||
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// getMinIndent calculates the minimum indentation in lines, excluding empty lines.
|
||||
func getMinIndent(lines []string, skipFirstLine bool) int {
|
||||
minIndentSize := maxInt
|
||||
|
||||
for i, line := range lines {
|
||||
if i == 0 && skipFirstLine {
|
||||
continue
|
||||
}
|
||||
|
||||
indentSize := 0
|
||||
for _, r := range []rune(line) {
|
||||
if unicode.IsSpace(r) {
|
||||
indentSize += 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(line) == indentSize {
|
||||
if i == len(lines)-1 && indentSize < minIndentSize {
|
||||
lines[i] = ""
|
||||
}
|
||||
} else if indentSize < minIndentSize {
|
||||
minIndentSize = indentSize
|
||||
}
|
||||
}
|
||||
return minIndentSize
|
||||
}
|
||||
|
||||
// removeIndentation removes n characters from the front of each line in lines.
|
||||
// Skips first line if skipFirstLine is true, skips empty lines.
|
||||
func removeIndentation(lines []string, n int, skipFirstLine bool) []string {
|
||||
for i, line := range lines {
|
||||
if i == 0 && skipFirstLine {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(lines[i]) >= n {
|
||||
lines[i] = line[n:]
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// Docf returns unindented and formatted string as here-document.
|
||||
// Formatting is done as for fmt.Printf().
|
||||
func Docf(raw string, args ...interface{}) string {
|
||||
return fmt.Sprintf(Doc(raw), args...)
|
||||
}
|
||||
1
vendor/github.com/Masterminds/semver/v3/.gitignore
generated
vendored
Normal file
1
vendor/github.com/Masterminds/semver/v3/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_fuzz/
|
||||
26
vendor/github.com/Masterminds/semver/v3/.golangci.yml
generated
vendored
Normal file
26
vendor/github.com/Masterminds/semver/v3/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
run:
|
||||
deadline: 2m
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- deadcode
|
||||
- dupl
|
||||
- errcheck
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nakedret
|
||||
- structcheck
|
||||
- unused
|
||||
- varcheck
|
||||
|
||||
linters-settings:
|
||||
gofmt:
|
||||
simplify: true
|
||||
dupl:
|
||||
threshold: 400
|
||||
27
vendor/github.com/Masterminds/semver/v3/.travis.yml
generated
vendored
Normal file
27
vendor/github.com/Masterminds/semver/v3/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
language: go
|
||||
|
||||
# Testing and linting occuring via go modules does not really work well prior
|
||||
# to Go 1.12. This is what can happen with experiments.
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- tip
|
||||
|
||||
# Setting sudo access to false will let Travis CI use containers rather than
|
||||
# VMs to run the tests. For more details see:
|
||||
# - http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
||||
# - http://docs.travis-ci.com/user/workers/standard-infrastructure/
|
||||
sudo: false
|
||||
|
||||
script:
|
||||
- make lint
|
||||
- make test-cover
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/06e3328629952dabe3e0
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: never # options: [always|never|change] default: always
|
||||
166
vendor/github.com/Masterminds/semver/v3/CHANGELOG.md
generated
vendored
Normal file
166
vendor/github.com/Masterminds/semver/v3/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
# Changelog
|
||||
|
||||
## 3.0.1 (2019-09-13)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #125: Fixes issue with module path for v3
|
||||
|
||||
## 3.0.0 (2019-09-12)
|
||||
|
||||
This is a major release of the semver package which includes API changes. The Go
|
||||
API is compatible with ^1. The Go API was not changed because many people are using
|
||||
`go get` without Go modules for their applications and API breaking changes cause
|
||||
errors which we have or would need to support.
|
||||
|
||||
The changes in this release are the handling based on the data passed into the
|
||||
functions. These are described in the added and changed sections below.
|
||||
|
||||
### Added
|
||||
|
||||
- StrictNewVersion function. This is similar to NewVersion but will return an
|
||||
error if the version passed in is not a strict semantic version. For example,
|
||||
1.2.3 would pass but v1.2.3 or 1.2 would fail because they are not strictly
|
||||
speaking semantic versions. This function is faster, performs fewer operations,
|
||||
and uses fewer allocations than NewVersion.
|
||||
- Fuzzing has been performed on NewVersion, StrictNewVersion, and NewConstraint.
|
||||
The Makefile contains the operations used. For more information on you can start
|
||||
on Wikipedia at https://en.wikipedia.org/wiki/Fuzzing
|
||||
- Now using Go modules
|
||||
|
||||
### Changed
|
||||
|
||||
- NewVersion has proper prerelease and metadata validation with error messages
|
||||
to signal an issue with either of them
|
||||
- ^ now operates using a similar set of rules to npm/js and Rust/Cargo. If the
|
||||
version is >=1 the ^ ranges works the same as v1. For major versions of 0 the
|
||||
rules have changed. The minor version is treated as the stable version unless
|
||||
a patch is specified and then it is equivalent to =. One difference from npm/js
|
||||
is that prereleases there are only to a specific version (e.g. 1.2.3).
|
||||
Prereleases here look over multiple versions and follow semantic version
|
||||
ordering rules. This pattern now follows along with the expected and requested
|
||||
handling of this packaged by numerous users.
|
||||
|
||||
## 1.5.0 (2019-09-11)
|
||||
|
||||
### Added
|
||||
|
||||
- #103: Add basic fuzzing for `NewVersion()` (thanks @jesse-c)
|
||||
|
||||
### Changed
|
||||
|
||||
- #82: Clarify wildcard meaning in range constraints and update tests for it (thanks @greysteil)
|
||||
- #83: Clarify caret operator range for pre-1.0.0 dependencies (thanks @greysteil)
|
||||
- #72: Adding docs comment pointing to vert for a cli
|
||||
- #71: Update the docs on pre-release comparator handling
|
||||
- #89: Test with new go versions (thanks @thedevsaddam)
|
||||
- #87: Added $ to ValidPrerelease for better validation (thanks @jeremycarroll)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #78: Fix unchecked error in example code (thanks @ravron)
|
||||
- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
|
||||
- #97: Fixed copyright file for proper display on GitHub
|
||||
- #107: Fix handling prerelease when sorting alphanum and num
|
||||
- #109: Fixed where Validate sometimes returns wrong message on error
|
||||
|
||||
## 1.4.2 (2018-04-10)
|
||||
|
||||
### Changed
|
||||
|
||||
- #72: Updated the docs to point to vert for a console appliaction
|
||||
- #71: Update the docs on pre-release comparator handling
|
||||
|
||||
### Fixed
|
||||
|
||||
- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
|
||||
|
||||
## 1.4.1 (2018-04-02)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed #64: Fix pre-release precedence issue (thanks @uudashr)
|
||||
|
||||
## 1.4.0 (2017-10-04)
|
||||
|
||||
### Changed
|
||||
|
||||
- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill)
|
||||
|
||||
## 1.3.1 (2017-07-10)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed #57: number comparisons in prerelease sometimes inaccurate
|
||||
|
||||
## 1.3.0 (2017-05-02)
|
||||
|
||||
### Added
|
||||
|
||||
- #45: Added json (un)marshaling support (thanks @mh-cbon)
|
||||
- Stability marker. See https://masterminds.github.io/stability/
|
||||
|
||||
### Fixed
|
||||
|
||||
- #51: Fix handling of single digit tilde constraint (thanks @dgodd)
|
||||
|
||||
### Changed
|
||||
|
||||
- #55: The godoc icon moved from png to svg
|
||||
|
||||
## 1.2.3 (2017-04-03)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #46: Fixed 0.x.x and 0.0.x in constraints being treated as *
|
||||
|
||||
## Release 1.2.2 (2016-12-13)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #34: Fixed issue where hyphen range was not working with pre-release parsing.
|
||||
|
||||
## Release 1.2.1 (2016-11-28)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #24: Fixed edge case issue where constraint "> 0" does not handle "0.0.1-alpha"
|
||||
properly.
|
||||
|
||||
## Release 1.2.0 (2016-11-04)
|
||||
|
||||
### Added
|
||||
|
||||
- #20: Added MustParse function for versions (thanks @adamreese)
|
||||
- #15: Added increment methods on versions (thanks @mh-cbon)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Issue #21: Per the SemVer spec (section 9) a pre-release is unstable and
|
||||
might not satisfy the intended compatibility. The change here ignores pre-releases
|
||||
on constraint checks (e.g., ~ or ^) when a pre-release is not part of the
|
||||
constraint. For example, `^1.2.3` will ignore pre-releases while
|
||||
`^1.2.3-alpha` will include them.
|
||||
|
||||
## Release 1.1.1 (2016-06-30)
|
||||
|
||||
### Changed
|
||||
|
||||
- Issue #9: Speed up version comparison performance (thanks @sdboyer)
|
||||
- Issue #8: Added benchmarks (thanks @sdboyer)
|
||||
- Updated Go Report Card URL to new location
|
||||
- Updated Readme to add code snippet formatting (thanks @mh-cbon)
|
||||
- Updating tagging to v[SemVer] structure for compatibility with other tools.
|
||||
|
||||
## Release 1.1.0 (2016-03-11)
|
||||
|
||||
- Issue #2: Implemented validation to provide reasons a versions failed a
|
||||
constraint.
|
||||
|
||||
## Release 1.0.1 (2015-12-31)
|
||||
|
||||
- Fixed #1: * constraint failing on valid versions.
|
||||
|
||||
## Release 1.0.0 (2015-10-20)
|
||||
|
||||
- Initial release
|
||||
19
vendor/github.com/Masterminds/semver/v3/LICENSE.txt
generated
vendored
Normal file
19
vendor/github.com/Masterminds/semver/v3/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2014-2019, Matt Butcher and Matt Farina
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
37
vendor/github.com/Masterminds/semver/v3/Makefile
generated
vendored
Normal file
37
vendor/github.com/Masterminds/semver/v3/Makefile
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
GOPATH=$(shell go env GOPATH)
|
||||
GOLANGCI_LINT=$(GOPATH)/bin/golangci-lint
|
||||
GOFUZZBUILD = $(GOPATH)/bin/go-fuzz-build
|
||||
GOFUZZ = $(GOPATH)/bin/go-fuzz
|
||||
|
||||
.PHONY: lint
|
||||
lint: $(GOLANGCI_LINT)
|
||||
@echo "==> Linting codebase"
|
||||
@$(GOLANGCI_LINT) run
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@echo "==> Running tests"
|
||||
GO111MODULE=on go test -v
|
||||
|
||||
.PHONY: test-cover
|
||||
test-cover:
|
||||
@echo "==> Running Tests with coverage"
|
||||
GO111MODULE=on go test -cover .
|
||||
|
||||
.PHONY: fuzz
|
||||
fuzz: $(GOFUZZBUILD) $(GOFUZZ)
|
||||
@echo "==> Fuzz testing"
|
||||
$(GOFUZZBUILD)
|
||||
$(GOFUZZ) -workdir=_fuzz
|
||||
|
||||
$(GOLANGCI_LINT):
|
||||
# Install golangci-lint. The configuration for it is in the .golangci.yml
|
||||
# file in the root of the repository
|
||||
echo ${GOPATH}
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.17.1
|
||||
|
||||
$(GOFUZZBUILD):
|
||||
cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
$(GOFUZZ):
|
||||
cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-dep
|
||||
242
vendor/github.com/Masterminds/semver/v3/README.md
generated
vendored
Normal file
242
vendor/github.com/Masterminds/semver/v3/README.md
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
# SemVer
|
||||
|
||||
The `semver` package provides the ability to work with [Semantic Versions](http://semver.org) in Go. Specifically it provides the ability to:
|
||||
|
||||
* Parse semantic versions
|
||||
* Sort semantic versions
|
||||
* Check if a semantic version fits within a set of constraints
|
||||
* Optionally work with a `v` prefix
|
||||
|
||||
[](https://masterminds.github.io/stability/active.html)
|
||||
[](https://travis-ci.org/Masterminds/semver) [](https://ci.appveyor.com/project/mattfarina/semver/branch/master) [](https://godoc.org/github.com/Masterminds/semver) [](https://goreportcard.com/report/github.com/Masterminds/semver)
|
||||
|
||||
If you are looking for a command line tool for version comparisons please see
|
||||
[vert](https://github.com/Masterminds/vert) which uses this library.
|
||||
|
||||
## Package Versions
|
||||
|
||||
There are three major versions fo the `semver` package.
|
||||
|
||||
* 3.x.x is the new stable and active version. This version is focused on constraint
|
||||
compatibility for range handling in other tools from other languages. It has
|
||||
a similar API to the v1 releases. The development of this version is on the master
|
||||
branch. The documentation for this version is below.
|
||||
* 2.x was developed primarily for [dep](https://github.com/golang/dep). There are
|
||||
no tagged releases and the development was performed by [@sdboyer](https://github.com/sdboyer).
|
||||
There are API breaking changes from v1. This version lives on the [2.x branch](https://github.com/Masterminds/semver/tree/2.x).
|
||||
* 1.x.x is the most widely used version with numerous tagged releases. This is the
|
||||
previous stable and is still maintained for bug fixes. The development, to fix
|
||||
bugs, occurs on the release-1 branch. You can read the documentation [here](https://github.com/Masterminds/semver/blob/release-1/README.md).
|
||||
|
||||
## Parsing Semantic Versions
|
||||
|
||||
There are two functions that can parse semantic versions. The `StrictNewVersion`
|
||||
function only parses valid version 2 semantic versions as outlined in the
|
||||
specification. The `NewVersion` function attempts to coerce a version into a
|
||||
semantic version and parse it. For example, if there is a leading v or a version
|
||||
listed without all 3 parts (e.g. `v1.2`) it will attempt to coerce it into a valid
|
||||
semantic version (e.g., 1.2.0). In both cases a `Version` object is returned
|
||||
that can be sorted, compared, and used in constraints.
|
||||
|
||||
When parsing a version an error is returned if there is an issue parsing the
|
||||
version. For example,
|
||||
|
||||
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||
|
||||
The version object has methods to get the parts of the version, compare it to
|
||||
other versions, convert the version back into a string, and get the original
|
||||
string. Getting the original string is useful if the semantic version was coerced
|
||||
into a valid form.
|
||||
|
||||
## Sorting Semantic Versions
|
||||
|
||||
A set of versions can be sorted using the `sort` package from the standard library.
|
||||
For example,
|
||||
|
||||
```go
|
||||
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||
vs := make([]*semver.Version, len(raw))
|
||||
for i, r := range raw {
|
||||
v, err := semver.NewVersion(r)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing version: %s", err)
|
||||
}
|
||||
|
||||
vs[i] = v
|
||||
}
|
||||
|
||||
sort.Sort(semver.Collection(vs))
|
||||
```
|
||||
|
||||
## Checking Version Constraints
|
||||
|
||||
There are two methods for comparing versions. One uses comparison methods on
|
||||
`Version` instances and the other uses `Constraints`. There are some important
|
||||
differences to notes between these two methods of comparison.
|
||||
|
||||
1. When two versions are compared using functions such as `Compare`, `LessThan`,
|
||||
and others it will follow the specification and always include prereleases
|
||||
within the comparison. It will provide an answer that is valid with the
|
||||
comparison section of the spec at https://semver.org/#spec-item-11
|
||||
2. When constraint checking is used for checks or validation it will follow a
|
||||
different set of rules that are common for ranges with tools like npm/js
|
||||
and Rust/Cargo. This includes considering prereleases to be invalid if the
|
||||
ranges does not include one. If you want to have it include pre-releases a
|
||||
simple solution is to include `-0` in your range.
|
||||
3. Constraint ranges can have some complex rules including the shorthand use of
|
||||
~ and ^. For more details on those see the options below.
|
||||
|
||||
There are differences between the two methods or checking versions because the
|
||||
comparison methods on `Version` follow the specification while comparison ranges
|
||||
are not part of the specification. Different packages and tools have taken it
|
||||
upon themselves to come up with range rules. This has resulted in differences.
|
||||
For example, npm/js and Cargo/Rust follow similar patterns while PHP has a
|
||||
different pattern for ^. The comparison features in this package follow the
|
||||
npm/js and Cargo/Rust lead because applications using it have followed similar
|
||||
patters with their versions.
|
||||
|
||||
Checking a version against version constraints is one of the most featureful
|
||||
parts of the package.
|
||||
|
||||
```go
|
||||
c, err := semver.NewConstraint(">= 1.2.3")
|
||||
if err != nil {
|
||||
// Handle constraint not being parsable.
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion("1.3")
|
||||
if err != nil {
|
||||
// Handle version not being parsable.
|
||||
}
|
||||
// Check if the version meets the constraints. The a variable will be true.
|
||||
a := c.Check(v)
|
||||
```
|
||||
|
||||
### Basic Comparisons
|
||||
|
||||
There are two elements to the comparisons. First, a comparison string is a list
|
||||
of space or comma separated AND comparisons. These are then separated by || (OR)
|
||||
comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a
|
||||
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
||||
greater than or equal to 4.2.3.
|
||||
|
||||
The basic comparisons are:
|
||||
|
||||
* `=`: equal (aliased to no operator)
|
||||
* `!=`: not equal
|
||||
* `>`: greater than
|
||||
* `<`: less than
|
||||
* `>=`: greater than or equal to
|
||||
* `<=`: less than or equal to
|
||||
|
||||
### Working With Prerelease Versions
|
||||
|
||||
Pre-releases, for those not familiar with them, are used for software releases
|
||||
prior to stable or generally available releases. Examples of prereleases include
|
||||
development, alpha, beta, and release candidate releases. A prerelease may be
|
||||
a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the
|
||||
order of precedence, prereleases come before their associated releases. In this
|
||||
example `1.2.3-beta.1 < 1.2.3`.
|
||||
|
||||
According to the Semantic Version specification prereleases may not be
|
||||
API compliant with their release counterpart. It says,
|
||||
|
||||
> A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version.
|
||||
|
||||
SemVer comparisons using constraints without a prerelease comparator will skip
|
||||
prerelease versions. For example, `>=1.2.3` will skip prereleases when looking
|
||||
at a list of releases while `>=1.2.3-0` will evaluate and find prereleases.
|
||||
|
||||
The reason for the `0` as a pre-release version in the example comparison is
|
||||
because pre-releases can only contain ASCII alphanumerics and hyphens (along with
|
||||
`.` separators), per the spec. Sorting happens in ASCII sort order, again per the
|
||||
spec. The lowest character is a `0` in ASCII sort order
|
||||
(see an [ASCII Table](http://www.asciitable.com/))
|
||||
|
||||
Understanding ASCII sort ordering is important because A-Z comes before a-z. That
|
||||
means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case
|
||||
sensitivity doesn't apply here. This is due to ASCII sort ordering which is what
|
||||
the spec specifies.
|
||||
|
||||
### Hyphen Range Comparisons
|
||||
|
||||
There are multiple methods to handle ranges and the first is hyphens ranges.
|
||||
These look like:
|
||||
|
||||
* `1.2 - 1.4.5` which is equivalent to `>= 1.2 <= 1.4.5`
|
||||
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5`
|
||||
|
||||
### Wildcards In Comparisons
|
||||
|
||||
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
||||
for all comparison operators. When used on the `=` operator it falls
|
||||
back to the patch level comparison (see tilde below). For example,
|
||||
|
||||
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
||||
* `<= 2.x` is equivalent to `< 3`
|
||||
* `*` is equivalent to `>= 0.0.0`
|
||||
|
||||
### Tilde Range Comparisons (Patch)
|
||||
|
||||
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
||||
version is specified and major level changes when the minor number is missing.
|
||||
For example,
|
||||
|
||||
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
|
||||
* `~1` is equivalent to `>= 1, < 2`
|
||||
* `~2.3` is equivalent to `>= 2.3, < 2.4`
|
||||
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||
* `~1.x` is equivalent to `>= 1, < 2`
|
||||
|
||||
### Caret Range Comparisons (Major)
|
||||
|
||||
The caret (`^`) comparison operator is for major level changes once a stable
|
||||
(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts
|
||||
as the API stability level. This is useful when comparisons of API versions as a
|
||||
major change is API breaking. For example,
|
||||
|
||||
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
||||
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
||||
* `^2.3` is equivalent to `>= 2.3, < 3`
|
||||
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
||||
* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0`
|
||||
* `^0.2` is equivalent to `>=0.2.0 <0.3.0`
|
||||
* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4`
|
||||
* `^0.0` is equivalent to `>=0.0.0 <0.1.0`
|
||||
* `^0` is equivalent to `>=0.0.0 <1.0.0`
|
||||
|
||||
## Validation
|
||||
|
||||
In addition to testing a version against a constraint, a version can be validated
|
||||
against a constraint. When validation fails a slice of errors containing why a
|
||||
version didn't meet the constraint is returned. For example,
|
||||
|
||||
```go
|
||||
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
|
||||
if err != nil {
|
||||
// Handle constraint not being parseable.
|
||||
}
|
||||
|
||||
v, _ := semver.NewVersion("1.3")
|
||||
if err != nil {
|
||||
// Handle version not being parseable.
|
||||
}
|
||||
|
||||
// Validate a version against a constraint.
|
||||
a, msgs := c.Validate(v)
|
||||
// a is false
|
||||
for _, m := range msgs {
|
||||
fmt.Println(m)
|
||||
|
||||
// Loops over the errors which would read
|
||||
// "1.3 is greater than 1.2.3"
|
||||
// "1.3 is less than 1.4"
|
||||
}
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues)
|
||||
or [create a pull request](https://github.com/Masterminds/semver/pulls).
|
||||
17
vendor/github.com/Masterminds/semver/v3/appveyor.yml
generated
vendored
Normal file
17
vendor/github.com/Masterminds/semver/v3/appveyor.yml
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
version: build-{build}.{branch}
|
||||
|
||||
shallow_clone: true
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- go test -v
|
||||
|
||||
deploy: off
|
||||
24
vendor/github.com/Masterminds/semver/v3/collection.go
generated
vendored
Normal file
24
vendor/github.com/Masterminds/semver/v3/collection.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package semver
|
||||
|
||||
// Collection is a collection of Version instances and implements the sort
|
||||
// interface. See the sort package for more details.
|
||||
// https://golang.org/pkg/sort/
|
||||
type Collection []*Version
|
||||
|
||||
// Len returns the length of a collection. The number of Version instances
|
||||
// on the slice.
|
||||
func (c Collection) Len() int {
|
||||
return len(c)
|
||||
}
|
||||
|
||||
// Less is needed for the sort interface to compare two Version objects on the
|
||||
// slice. If checks if one is less than the other.
|
||||
func (c Collection) Less(i, j int) bool {
|
||||
return c[i].LessThan(c[j])
|
||||
}
|
||||
|
||||
// Swap is needed for the sort interface to replace the Version objects
|
||||
// at two different positions in the slice.
|
||||
func (c Collection) Swap(i, j int) {
|
||||
c[i], c[j] = c[j], c[i]
|
||||
}
|
||||
527
vendor/github.com/Masterminds/semver/v3/constraints.go
generated
vendored
Normal file
527
vendor/github.com/Masterminds/semver/v3/constraints.go
generated
vendored
Normal file
@@ -0,0 +1,527 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Constraints is one or more constraint that a semantic version can be
|
||||
// checked against.
|
||||
type Constraints struct {
|
||||
constraints [][]*constraint
|
||||
}
|
||||
|
||||
// NewConstraint returns a Constraints instance that a Version instance can
|
||||
// be checked against. If there is a parse error it will be returned.
|
||||
func NewConstraint(c string) (*Constraints, error) {
|
||||
|
||||
// Rewrite - ranges into a comparison operation.
|
||||
c = rewriteRange(c)
|
||||
|
||||
ors := strings.Split(c, "||")
|
||||
or := make([][]*constraint, len(ors))
|
||||
for k, v := range ors {
|
||||
|
||||
// TODO: Find a way to validate and fetch all the constraints in a simpler form
|
||||
|
||||
// Validate the segment
|
||||
if !validConstraintRegex.MatchString(v) {
|
||||
return nil, fmt.Errorf("improper constraint: %s", v)
|
||||
}
|
||||
|
||||
cs := findConstraintRegex.FindAllString(v, -1)
|
||||
if cs == nil {
|
||||
cs = append(cs, v)
|
||||
}
|
||||
result := make([]*constraint, len(cs))
|
||||
for i, s := range cs {
|
||||
pc, err := parseConstraint(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result[i] = pc
|
||||
}
|
||||
or[k] = result
|
||||
}
|
||||
|
||||
o := &Constraints{constraints: or}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// Check tests if a version satisfies the constraints.
|
||||
func (cs Constraints) Check(v *Version) bool {
|
||||
// loop over the ORs and check the inner ANDs
|
||||
for _, o := range cs.constraints {
|
||||
joy := true
|
||||
for _, c := range o {
|
||||
if !c.check(v) {
|
||||
joy = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if joy {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate checks if a version satisfies a constraint. If not a slice of
|
||||
// reasons for the failure are returned in addition to a bool.
|
||||
func (cs Constraints) Validate(v *Version) (bool, []error) {
|
||||
// loop over the ORs and check the inner ANDs
|
||||
var e []error
|
||||
|
||||
// Capture the prerelease message only once. When it happens the first time
|
||||
// this var is marked
|
||||
var prerelesase bool
|
||||
for _, o := range cs.constraints {
|
||||
joy := true
|
||||
for _, c := range o {
|
||||
// Before running the check handle the case there the version is
|
||||
// a prerelease and the check is not searching for prereleases.
|
||||
if c.con.pre == "" && v.pre != "" {
|
||||
if !prerelesase {
|
||||
em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
|
||||
e = append(e, em)
|
||||
prerelesase = true
|
||||
}
|
||||
joy = false
|
||||
|
||||
} else {
|
||||
|
||||
if !c.check(v) {
|
||||
em := fmt.Errorf(constraintMsg[c.origfunc], v, c.orig)
|
||||
e = append(e, em)
|
||||
joy = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if joy {
|
||||
return true, []error{}
|
||||
}
|
||||
}
|
||||
|
||||
return false, e
|
||||
}
|
||||
|
||||
func (cs Constraints) String() string {
|
||||
buf := make([]string, len(cs.constraints))
|
||||
var tmp bytes.Buffer
|
||||
|
||||
for k, v := range cs.constraints {
|
||||
tmp.Reset()
|
||||
vlen := len(v)
|
||||
for kk, c := range v {
|
||||
tmp.WriteString(c.string())
|
||||
|
||||
// Space separate the AND conditions
|
||||
if vlen > 1 && kk < vlen-1 {
|
||||
tmp.WriteString(" ")
|
||||
}
|
||||
}
|
||||
buf[k] = tmp.String()
|
||||
}
|
||||
|
||||
return strings.Join(buf, " || ")
|
||||
}
|
||||
|
||||
var constraintOps map[string]cfunc
|
||||
var constraintMsg map[string]string
|
||||
var constraintRegex *regexp.Regexp
|
||||
var constraintRangeRegex *regexp.Regexp
|
||||
|
||||
// Used to find individual constraints within a multi-constraint string
|
||||
var findConstraintRegex *regexp.Regexp
|
||||
|
||||
// Used to validate an segment of ANDs is valid
|
||||
var validConstraintRegex *regexp.Regexp
|
||||
|
||||
const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
|
||||
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||
|
||||
func init() {
|
||||
constraintOps = map[string]cfunc{
|
||||
"": constraintTildeOrEqual,
|
||||
"=": constraintTildeOrEqual,
|
||||
"!=": constraintNotEqual,
|
||||
">": constraintGreaterThan,
|
||||
"<": constraintLessThan,
|
||||
">=": constraintGreaterThanEqual,
|
||||
"=>": constraintGreaterThanEqual,
|
||||
"<=": constraintLessThanEqual,
|
||||
"=<": constraintLessThanEqual,
|
||||
"~": constraintTilde,
|
||||
"~>": constraintTilde,
|
||||
"^": constraintCaret,
|
||||
}
|
||||
|
||||
constraintMsg = map[string]string{
|
||||
"": "%s is not equal to %s",
|
||||
"=": "%s is not equal to %s",
|
||||
"!=": "%s is equal to %s",
|
||||
">": "%s is less than or equal to %s",
|
||||
"<": "%s is greater than or equal to %s",
|
||||
">=": "%s is less than %s",
|
||||
"=>": "%s is less than %s",
|
||||
"<=": "%s is greater than %s",
|
||||
"=<": "%s is greater than %s",
|
||||
"~": "%s does not have same major and minor version as %s",
|
||||
"~>": "%s does not have same major and minor version as %s",
|
||||
"^": "%s does not have same major version as %s",
|
||||
}
|
||||
|
||||
ops := make([]string, 0, len(constraintOps))
|
||||
for k := range constraintOps {
|
||||
ops = append(ops, regexp.QuoteMeta(k))
|
||||
}
|
||||
|
||||
constraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||
`^\s*(%s)\s*(%s)\s*$`,
|
||||
strings.Join(ops, "|"),
|
||||
cvRegex))
|
||||
|
||||
constraintRangeRegex = regexp.MustCompile(fmt.Sprintf(
|
||||
`\s*(%s)\s+-\s+(%s)\s*`,
|
||||
cvRegex, cvRegex))
|
||||
|
||||
findConstraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||
`(%s)\s*(%s)`,
|
||||
strings.Join(ops, "|"),
|
||||
cvRegex))
|
||||
|
||||
validConstraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||
`^(\s*(%s)\s*(%s)\s*\,?)+$`,
|
||||
strings.Join(ops, "|"),
|
||||
cvRegex))
|
||||
}
|
||||
|
||||
// An individual constraint
|
||||
type constraint struct {
|
||||
// The version used in the constraint check. For example, if a constraint
|
||||
// is '<= 2.0.0' the con a version instance representing 2.0.0.
|
||||
con *Version
|
||||
|
||||
// The original parsed version (e.g., 4.x from != 4.x)
|
||||
orig string
|
||||
|
||||
// The original operator for the constraint
|
||||
origfunc string
|
||||
|
||||
// When an x is used as part of the version (e.g., 1.x)
|
||||
minorDirty bool
|
||||
dirty bool
|
||||
patchDirty bool
|
||||
}
|
||||
|
||||
// Check if a version meets the constraint
|
||||
func (c *constraint) check(v *Version) bool {
|
||||
return constraintOps[c.origfunc](v, c)
|
||||
}
|
||||
|
||||
// String prints an individual constraint into a string
|
||||
func (c *constraint) string() string {
|
||||
return c.origfunc + c.orig
|
||||
}
|
||||
|
||||
type cfunc func(v *Version, c *constraint) bool
|
||||
|
||||
func parseConstraint(c string) (*constraint, error) {
|
||||
if len(c) > 0 {
|
||||
m := constraintRegex.FindStringSubmatch(c)
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("improper constraint: %s", c)
|
||||
}
|
||||
|
||||
cs := &constraint{
|
||||
orig: m[2],
|
||||
origfunc: m[1],
|
||||
}
|
||||
|
||||
ver := m[2]
|
||||
minorDirty := false
|
||||
patchDirty := false
|
||||
dirty := false
|
||||
if isX(m[3]) || m[3] == "" {
|
||||
ver = "0.0.0"
|
||||
dirty = true
|
||||
} else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" {
|
||||
minorDirty = true
|
||||
dirty = true
|
||||
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
|
||||
} else if isX(strings.TrimPrefix(m[5], ".")) || m[5] == "" {
|
||||
dirty = true
|
||||
patchDirty = true
|
||||
ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
|
||||
}
|
||||
|
||||
con, err := NewVersion(ver)
|
||||
if err != nil {
|
||||
|
||||
// The constraintRegex should catch any regex parsing errors. So,
|
||||
// we should never get here.
|
||||
return nil, errors.New("constraint Parser Error")
|
||||
}
|
||||
|
||||
cs.con = con
|
||||
cs.minorDirty = minorDirty
|
||||
cs.patchDirty = patchDirty
|
||||
cs.dirty = dirty
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// The rest is the special case where an empty string was passed in which
|
||||
// is equivalent to * or >=0.0.0
|
||||
con, err := StrictNewVersion("0.0.0")
|
||||
if err != nil {
|
||||
|
||||
// The constraintRegex should catch any regex parsing errors. So,
|
||||
// we should never get here.
|
||||
return nil, errors.New("constraint Parser Error")
|
||||
}
|
||||
|
||||
cs := &constraint{
|
||||
con: con,
|
||||
orig: c,
|
||||
origfunc: "",
|
||||
minorDirty: false,
|
||||
patchDirty: false,
|
||||
dirty: true,
|
||||
}
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// Constraint functions
|
||||
func constraintNotEqual(v *Version, c *constraint) bool {
|
||||
if c.dirty {
|
||||
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if c.con.Major() != v.Major() {
|
||||
return true
|
||||
}
|
||||
if c.con.Minor() != v.Minor() && !c.minorDirty {
|
||||
return true
|
||||
} else if c.minorDirty {
|
||||
return false
|
||||
} else if c.con.Patch() != v.Patch() && !c.patchDirty {
|
||||
return true
|
||||
} else if c.patchDirty {
|
||||
// Need to handle prereleases if present
|
||||
if v.Prerelease() != "" || c.con.Prerelease() != "" {
|
||||
return comparePrerelease(v.Prerelease(), c.con.Prerelease()) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return !v.Equal(c.con)
|
||||
}
|
||||
|
||||
func constraintGreaterThan(v *Version, c *constraint) bool {
|
||||
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !c.dirty {
|
||||
return v.Compare(c.con) == 1
|
||||
}
|
||||
|
||||
if v.Major() > c.con.Major() {
|
||||
return true
|
||||
} else if v.Major() < c.con.Major() {
|
||||
return false
|
||||
} else if c.minorDirty {
|
||||
// This is a range case such as >11. When the version is something like
|
||||
// 11.1.0 is it not > 11. For that we would need 12 or higher
|
||||
return false
|
||||
} else if c.patchDirty {
|
||||
// This is for ranges such as >11.1. A version of 11.1.1 is not greater
|
||||
// which one of 11.2.1 is greater
|
||||
return v.Minor() > c.con.Minor()
|
||||
}
|
||||
|
||||
// If we have gotten here we are not comparing pre-preleases and can use the
|
||||
// Compare function to accomplish that.
|
||||
return v.Compare(c.con) == 1
|
||||
}
|
||||
|
||||
func constraintLessThan(v *Version, c *constraint) bool {
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Compare(c.con) < 0
|
||||
}
|
||||
|
||||
func constraintGreaterThanEqual(v *Version, c *constraint) bool {
|
||||
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Compare(c.con) >= 0
|
||||
}
|
||||
|
||||
func constraintLessThanEqual(v *Version, c *constraint) bool {
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !c.dirty {
|
||||
return v.Compare(c.con) <= 0
|
||||
}
|
||||
|
||||
if v.Major() > c.con.Major() {
|
||||
return false
|
||||
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ~*, ~>* --> >= 0.0.0 (any)
|
||||
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
|
||||
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
|
||||
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
|
||||
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
|
||||
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
|
||||
func constraintTilde(v *Version, c *constraint) bool {
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.LessThan(c.con) {
|
||||
return false
|
||||
}
|
||||
|
||||
// ~0.0.0 is a special case where all constraints are accepted. It's
|
||||
// equivalent to >= 0.0.0.
|
||||
if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 &&
|
||||
!c.minorDirty && !c.patchDirty {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Major() != c.con.Major() {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.Minor() != c.con.Minor() && !c.minorDirty {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
|
||||
// it's a straight =
|
||||
func constraintTildeOrEqual(v *Version, c *constraint) bool {
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if c.dirty {
|
||||
return constraintTilde(v, c)
|
||||
}
|
||||
|
||||
return v.Equal(c.con)
|
||||
}
|
||||
|
||||
// ^* --> (any)
|
||||
// ^1.2.3 --> >=1.2.3 <2.0.0
|
||||
// ^1.2 --> >=1.2.0 <2.0.0
|
||||
// ^1 --> >=1.0.0 <2.0.0
|
||||
// ^0.2.3 --> >=0.2.3 <0.3.0
|
||||
// ^0.2 --> >=0.2.0 <0.3.0
|
||||
// ^0.0.3 --> >=0.0.3 <0.0.4
|
||||
// ^0.0 --> >=0.0.0 <0.1.0
|
||||
// ^0 --> >=0.0.0 <1.0.0
|
||||
func constraintCaret(v *Version, c *constraint) bool {
|
||||
// If there is a pre-release on the version but the constraint isn't looking
|
||||
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||
// more details.
|
||||
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// This less than handles prereleases
|
||||
if v.LessThan(c.con) {
|
||||
return false
|
||||
}
|
||||
|
||||
// ^ when the major > 0 is >=x.y.z < x+1
|
||||
if c.con.Major() > 0 || c.minorDirty {
|
||||
|
||||
// ^ has to be within a major range for > 0. Everything less than was
|
||||
// filtered out with the LessThan call above. This filters out those
|
||||
// that greater but not within the same major range.
|
||||
return v.Major() == c.con.Major()
|
||||
}
|
||||
|
||||
// ^ when the major is 0 and minor > 0 is >=0.y.z < 0.y+1
|
||||
// If the con Minor is > 0 it is not dirty
|
||||
if c.con.Minor() > 0 || c.patchDirty {
|
||||
return v.Minor() == c.con.Minor()
|
||||
}
|
||||
|
||||
// At this point the major is 0 and the minor is 0 and not dirty. The patch
|
||||
// is not dirty so we need to check if they are equal. If they are not equal
|
||||
return c.con.Patch() == v.Patch()
|
||||
}
|
||||
|
||||
func isX(x string) bool {
|
||||
switch x {
|
||||
case "x", "*", "X":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func rewriteRange(i string) string {
|
||||
m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
|
||||
if m == nil {
|
||||
return i
|
||||
}
|
||||
o := i
|
||||
for _, v := range m {
|
||||
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
|
||||
o = strings.Replace(o, v[0], t, 1)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
184
vendor/github.com/Masterminds/semver/v3/doc.go
generated
vendored
Normal file
184
vendor/github.com/Masterminds/semver/v3/doc.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go.
|
||||
|
||||
Specifically it provides the ability to:
|
||||
|
||||
* Parse semantic versions
|
||||
* Sort semantic versions
|
||||
* Check if a semantic version fits within a set of constraints
|
||||
* Optionally work with a `v` prefix
|
||||
|
||||
Parsing Semantic Versions
|
||||
|
||||
There are two functions that can parse semantic versions. The `StrictNewVersion`
|
||||
function only parses valid version 2 semantic versions as outlined in the
|
||||
specification. The `NewVersion` function attempts to coerce a version into a
|
||||
semantic version and parse it. For example, if there is a leading v or a version
|
||||
listed without all 3 parts (e.g. 1.2) it will attempt to coerce it into a valid
|
||||
semantic version (e.g., 1.2.0). In both cases a `Version` object is returned
|
||||
that can be sorted, compared, and used in constraints.
|
||||
|
||||
When parsing a version an optional error can be returned if there is an issue
|
||||
parsing the version. For example,
|
||||
|
||||
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||
|
||||
The version object has methods to get the parts of the version, compare it to
|
||||
other versions, convert the version back into a string, and get the original
|
||||
string. For more details please see the documentation
|
||||
at https://godoc.org/github.com/Masterminds/semver.
|
||||
|
||||
Sorting Semantic Versions
|
||||
|
||||
A set of versions can be sorted using the `sort` package from the standard library.
|
||||
For example,
|
||||
|
||||
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||
vs := make([]*semver.Version, len(raw))
|
||||
for i, r := range raw {
|
||||
v, err := semver.NewVersion(r)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing version: %s", err)
|
||||
}
|
||||
|
||||
vs[i] = v
|
||||
}
|
||||
|
||||
sort.Sort(semver.Collection(vs))
|
||||
|
||||
Checking Version Constraints and Comparing Versions
|
||||
|
||||
There are two methods for comparing versions. One uses comparison methods on
|
||||
`Version` instances and the other is using Constraints. There are some important
|
||||
differences to notes between these two methods of comparison.
|
||||
|
||||
1. When two versions are compared using functions such as `Compare`, `LessThan`,
|
||||
and others it will follow the specification and always include prereleases
|
||||
within the comparison. It will provide an answer valid with the comparison
|
||||
spec section at https://semver.org/#spec-item-11
|
||||
2. When constraint checking is used for checks or validation it will follow a
|
||||
different set of rules that are common for ranges with tools like npm/js
|
||||
and Rust/Cargo. This includes considering prereleases to be invalid if the
|
||||
ranges does not include on. If you want to have it include pre-releases a
|
||||
simple solution is to include `-0` in your range.
|
||||
3. Constraint ranges can have some complex rules including the shorthard use of
|
||||
~ and ^. For more details on those see the options below.
|
||||
|
||||
There are differences between the two methods or checking versions because the
|
||||
comparison methods on `Version` follow the specification while comparison ranges
|
||||
are not part of the specification. Different packages and tools have taken it
|
||||
upon themselves to come up with range rules. This has resulted in differences.
|
||||
For example, npm/js and Cargo/Rust follow similar patterns which PHP has a
|
||||
different pattern for ^. The comparison features in this package follow the
|
||||
npm/js and Cargo/Rust lead because applications using it have followed similar
|
||||
patters with their versions.
|
||||
|
||||
Checking a version against version constraints is one of the most featureful
|
||||
parts of the package.
|
||||
|
||||
c, err := semver.NewConstraint(">= 1.2.3")
|
||||
if err != nil {
|
||||
// Handle constraint not being parsable.
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion("1.3")
|
||||
if err != nil {
|
||||
// Handle version not being parsable.
|
||||
}
|
||||
// Check if the version meets the constraints. The a variable will be true.
|
||||
a := c.Check(v)
|
||||
|
||||
Basic Comparisons
|
||||
|
||||
There are two elements to the comparisons. First, a comparison string is a list
|
||||
of comma or space separated AND comparisons. These are then separated by || (OR)
|
||||
comparisons. For example, `">= 1.2 < 3.0.0 || >= 4.2.3"` is looking for a
|
||||
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
||||
greater than or equal to 4.2.3. This can also be written as
|
||||
`">= 1.2, < 3.0.0 || >= 4.2.3"`
|
||||
|
||||
The basic comparisons are:
|
||||
|
||||
* `=`: equal (aliased to no operator)
|
||||
* `!=`: not equal
|
||||
* `>`: greater than
|
||||
* `<`: less than
|
||||
* `>=`: greater than or equal to
|
||||
* `<=`: less than or equal to
|
||||
|
||||
Hyphen Range Comparisons
|
||||
|
||||
There are multiple methods to handle ranges and the first is hyphens ranges.
|
||||
These look like:
|
||||
|
||||
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
|
||||
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5`
|
||||
|
||||
Wildcards In Comparisons
|
||||
|
||||
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
||||
for all comparison operators. When used on the `=` operator it falls
|
||||
back to the tilde operation. For example,
|
||||
|
||||
* `1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
|
||||
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
||||
* `<= 2.x` is equivalent to `<= 3`
|
||||
* `*` is equivalent to `>= 0.0.0`
|
||||
|
||||
Tilde Range Comparisons (Patch)
|
||||
|
||||
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
||||
version is specified and major level changes when the minor number is missing.
|
||||
For example,
|
||||
|
||||
* `~1.2.3` is equivalent to `>= 1.2.3 < 1.3.0`
|
||||
* `~1` is equivalent to `>= 1, < 2`
|
||||
* `~2.3` is equivalent to `>= 2.3 < 2.4`
|
||||
* `~1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
|
||||
* `~1.x` is equivalent to `>= 1 < 2`
|
||||
|
||||
Caret Range Comparisons (Major)
|
||||
|
||||
The caret (`^`) comparison operator is for major level changes once a stable
|
||||
(1.0.0) release has occurred. Prior to a 1.0.0 release the minor versions acts
|
||||
as the API stability level. This is useful when comparisons of API versions as a
|
||||
major change is API breaking. For example,
|
||||
|
||||
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
||||
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
||||
* `^2.3` is equivalent to `>= 2.3, < 3`
|
||||
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
||||
* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0`
|
||||
* `^0.2` is equivalent to `>=0.2.0 <0.3.0`
|
||||
* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4`
|
||||
* `^0.0` is equivalent to `>=0.0.0 <0.1.0`
|
||||
* `^0` is equivalent to `>=0.0.0 <1.0.0`
|
||||
|
||||
Validation
|
||||
|
||||
In addition to testing a version against a constraint, a version can be validated
|
||||
against a constraint. When validation fails a slice of errors containing why a
|
||||
version didn't meet the constraint is returned. For example,
|
||||
|
||||
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
|
||||
if err != nil {
|
||||
// Handle constraint not being parseable.
|
||||
}
|
||||
|
||||
v, _ := semver.NewVersion("1.3")
|
||||
if err != nil {
|
||||
// Handle version not being parseable.
|
||||
}
|
||||
|
||||
// Validate a version against a constraint.
|
||||
a, msgs := c.Validate(v)
|
||||
// a is false
|
||||
for _, m := range msgs {
|
||||
fmt.Println(m)
|
||||
|
||||
// Loops over the errors which would read
|
||||
// "1.3 is greater than 1.2.3"
|
||||
// "1.3 is less than 1.4"
|
||||
}
|
||||
*/
|
||||
package semver
|
||||
22
vendor/github.com/Masterminds/semver/v3/fuzz.go
generated
vendored
Normal file
22
vendor/github.com/Masterminds/semver/v3/fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// +build gofuzz
|
||||
|
||||
package semver
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
d := string(data)
|
||||
|
||||
// Test NewVersion
|
||||
_, _ = NewVersion(d)
|
||||
|
||||
// Test StrictNewVersion
|
||||
_, _ = StrictNewVersion(d)
|
||||
|
||||
// Test NewConstraint
|
||||
_, _ = NewConstraint(d)
|
||||
|
||||
// The return value should be 0 normally, 1 if the priority in future tests
|
||||
// should be increased, and -1 if future tests should skip passing in that
|
||||
// data. We do not have a reason to change priority so 0 is always returned.
|
||||
// There are example tests that do this.
|
||||
return 0
|
||||
}
|
||||
3
vendor/github.com/Masterminds/semver/v3/go.mod
generated
vendored
Normal file
3
vendor/github.com/Masterminds/semver/v3/go.mod
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/Masterminds/semver/v3
|
||||
|
||||
go 1.12
|
||||
583
vendor/github.com/Masterminds/semver/v3/version.go
generated
vendored
Normal file
583
vendor/github.com/Masterminds/semver/v3/version.go
generated
vendored
Normal file
@@ -0,0 +1,583 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The compiled version of the regex created at init() is cached here so it
|
||||
// only needs to be created once.
|
||||
var versionRegex *regexp.Regexp
|
||||
|
||||
var (
|
||||
// ErrInvalidSemVer is returned a version is found to be invalid when
|
||||
// being parsed.
|
||||
ErrInvalidSemVer = errors.New("Invalid Semantic Version")
|
||||
|
||||
// ErrEmptyString is returned when an empty string is passed in for parsing.
|
||||
ErrEmptyString = errors.New("Version string empty")
|
||||
|
||||
// ErrInvalidCharacters is returned when invalid characters are found as
|
||||
// part of a version
|
||||
ErrInvalidCharacters = errors.New("Invalid characters in version")
|
||||
|
||||
// ErrSegmentStartsZero is returned when a version segment starts with 0.
|
||||
// This is invalid in SemVer.
|
||||
ErrSegmentStartsZero = errors.New("Version segment starts with 0")
|
||||
|
||||
// ErrInvalidMetadata is returned when the metadata is an invalid format
|
||||
ErrInvalidMetadata = errors.New("Invalid Metadata string")
|
||||
|
||||
// ErrInvalidPrerelease is returned when the pre-release is an invalid format
|
||||
ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
|
||||
)
|
||||
|
||||
// semVerRegex is the regular expression used to parse a semantic version.
|
||||
const semVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
|
||||
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||
|
||||
// Version represents a single semantic version.
|
||||
type Version struct {
|
||||
major, minor, patch uint64
|
||||
pre string
|
||||
metadata string
|
||||
original string
|
||||
}
|
||||
|
||||
func init() {
|
||||
versionRegex = regexp.MustCompile("^" + semVerRegex + "$")
|
||||
}
|
||||
|
||||
const num string = "0123456789"
|
||||
const allowed string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + num
|
||||
|
||||
// StrictNewVersion parses a given version and returns an instance of Version or
|
||||
// an error if unable to parse the version. Only parses valid semantic versions.
|
||||
// Performs checking that can find errors within the version.
|
||||
// If you want to coerce a version, such as 1 or 1.2, and perse that as the 1.x
|
||||
// releases of semver provided use the NewSemver() function.
|
||||
func StrictNewVersion(v string) (*Version, error) {
|
||||
// Parsing here does not use RegEx in order to increase performance and reduce
|
||||
// allocations.
|
||||
|
||||
if len(v) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
|
||||
// Split the parts into [0]major, [1]minor, and [2]patch,prerelease,build
|
||||
parts := strings.SplitN(v, ".", 3)
|
||||
if len(parts) != 3 {
|
||||
return nil, ErrInvalidSemVer
|
||||
}
|
||||
|
||||
sv := &Version{
|
||||
original: v,
|
||||
}
|
||||
|
||||
// check for prerelease or build metadata
|
||||
var extra []string
|
||||
if strings.ContainsAny(parts[2], "-+") {
|
||||
// Start with the build metadata first as it needs to be on the right
|
||||
extra = strings.SplitN(parts[2], "+", 2)
|
||||
if len(extra) > 1 {
|
||||
// build metadata found
|
||||
sv.metadata = extra[1]
|
||||
parts[2] = extra[0]
|
||||
}
|
||||
|
||||
extra = strings.SplitN(parts[2], "-", 2)
|
||||
if len(extra) > 1 {
|
||||
// prerelease found
|
||||
sv.pre = extra[1]
|
||||
parts[2] = extra[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the number segments are valid. This includes only having positive
|
||||
// numbers and no leading 0's.
|
||||
for _, p := range parts {
|
||||
if !containsOnly(p, num) {
|
||||
return nil, ErrInvalidCharacters
|
||||
}
|
||||
|
||||
if len(p) > 1 && p[0] == '0' {
|
||||
return nil, ErrSegmentStartsZero
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the major, minor, and patch elements onto the returned Version
|
||||
var err error
|
||||
sv.major, err = strconv.ParseUint(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sv.minor, err = strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sv.patch, err = strconv.ParseUint(parts[2], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No prerelease or build metadata found so returning now as a fastpath.
|
||||
if sv.pre == "" && sv.metadata == "" {
|
||||
return sv, nil
|
||||
}
|
||||
|
||||
if sv.pre != "" {
|
||||
if err = validatePrerelease(sv.pre); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if sv.metadata != "" {
|
||||
if err = validateMetadata(sv.metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return sv, nil
|
||||
}
|
||||
|
||||
// NewVersion parses a given version and returns an instance of Version or
|
||||
// an error if unable to parse the version. If the version is SemVer-ish it
|
||||
// attempts to convert it to SemVer. If you want to validate it was a strict
|
||||
// semantic version at parse time see StrictNewVersion().
|
||||
func NewVersion(v string) (*Version, error) {
|
||||
m := versionRegex.FindStringSubmatch(v)
|
||||
if m == nil {
|
||||
return nil, ErrInvalidSemVer
|
||||
}
|
||||
|
||||
sv := &Version{
|
||||
metadata: m[8],
|
||||
pre: m[5],
|
||||
original: v,
|
||||
}
|
||||
|
||||
var err error
|
||||
sv.major, err = strconv.ParseUint(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||
}
|
||||
|
||||
if m[2] != "" {
|
||||
sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||
}
|
||||
} else {
|
||||
sv.minor = 0
|
||||
}
|
||||
|
||||
if m[3] != "" {
|
||||
sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||
}
|
||||
} else {
|
||||
sv.patch = 0
|
||||
}
|
||||
|
||||
// Perform some basic due diligence on the extra parts to ensure they are
|
||||
// valid.
|
||||
|
||||
if sv.pre != "" {
|
||||
if err = validatePrerelease(sv.pre); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if sv.metadata != "" {
|
||||
if err = validateMetadata(sv.metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return sv, nil
|
||||
}
|
||||
|
||||
// MustParse parses a given version and panics on error.
|
||||
func MustParse(v string) *Version {
|
||||
sv, err := NewVersion(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sv
|
||||
}
|
||||
|
||||
// String converts a Version object to a string.
|
||||
// Note, if the original version contained a leading v this version will not.
|
||||
// See the Original() method to retrieve the original value. Semantic Versions
|
||||
// don't contain a leading v per the spec. Instead it's optional on
|
||||
// implementation.
|
||||
func (v Version) String() string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch)
|
||||
if v.pre != "" {
|
||||
fmt.Fprintf(&buf, "-%s", v.pre)
|
||||
}
|
||||
if v.metadata != "" {
|
||||
fmt.Fprintf(&buf, "+%s", v.metadata)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Original returns the original value passed in to be parsed.
|
||||
func (v *Version) Original() string {
|
||||
return v.original
|
||||
}
|
||||
|
||||
// Major returns the major version.
|
||||
func (v Version) Major() uint64 {
|
||||
return v.major
|
||||
}
|
||||
|
||||
// Minor returns the minor version.
|
||||
func (v Version) Minor() uint64 {
|
||||
return v.minor
|
||||
}
|
||||
|
||||
// Patch returns the patch version.
|
||||
func (v Version) Patch() uint64 {
|
||||
return v.patch
|
||||
}
|
||||
|
||||
// Prerelease returns the pre-release version.
|
||||
func (v Version) Prerelease() string {
|
||||
return v.pre
|
||||
}
|
||||
|
||||
// Metadata returns the metadata on the version.
|
||||
func (v Version) Metadata() string {
|
||||
return v.metadata
|
||||
}
|
||||
|
||||
// originalVPrefix returns the original 'v' prefix if any.
|
||||
func (v Version) originalVPrefix() string {
|
||||
|
||||
// Note, only lowercase v is supported as a prefix by the parser.
|
||||
if v.original != "" && v.original[:1] == "v" {
|
||||
return v.original[:1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IncPatch produces the next patch version.
|
||||
// If the current version does not have prerelease/metadata information,
|
||||
// it unsets metadata and prerelease values, increments patch number.
|
||||
// If the current version has any of prerelease or metadata information,
|
||||
// it unsets both values and keeps current patch value
|
||||
func (v Version) IncPatch() Version {
|
||||
vNext := v
|
||||
// according to http://semver.org/#spec-item-9
|
||||
// Pre-release versions have a lower precedence than the associated normal version.
|
||||
// according to http://semver.org/#spec-item-10
|
||||
// Build metadata SHOULD be ignored when determining version precedence.
|
||||
if v.pre != "" {
|
||||
vNext.metadata = ""
|
||||
vNext.pre = ""
|
||||
} else {
|
||||
vNext.metadata = ""
|
||||
vNext.pre = ""
|
||||
vNext.patch = v.patch + 1
|
||||
}
|
||||
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||
return vNext
|
||||
}
|
||||
|
||||
// IncMinor produces the next minor version.
|
||||
// Sets patch to 0.
|
||||
// Increments minor number.
|
||||
// Unsets metadata.
|
||||
// Unsets prerelease status.
|
||||
func (v Version) IncMinor() Version {
|
||||
vNext := v
|
||||
vNext.metadata = ""
|
||||
vNext.pre = ""
|
||||
vNext.patch = 0
|
||||
vNext.minor = v.minor + 1
|
||||
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||
return vNext
|
||||
}
|
||||
|
||||
// IncMajor produces the next major version.
|
||||
// Sets patch to 0.
|
||||
// Sets minor to 0.
|
||||
// Increments major number.
|
||||
// Unsets metadata.
|
||||
// Unsets prerelease status.
|
||||
func (v Version) IncMajor() Version {
|
||||
vNext := v
|
||||
vNext.metadata = ""
|
||||
vNext.pre = ""
|
||||
vNext.patch = 0
|
||||
vNext.minor = 0
|
||||
vNext.major = v.major + 1
|
||||
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||
return vNext
|
||||
}
|
||||
|
||||
// SetPrerelease defines the prerelease value.
|
||||
// Value must not include the required 'hyphen' prefix.
|
||||
func (v Version) SetPrerelease(prerelease string) (Version, error) {
|
||||
vNext := v
|
||||
if len(prerelease) > 0 {
|
||||
if err := validatePrerelease(prerelease); err != nil {
|
||||
return vNext, err
|
||||
}
|
||||
}
|
||||
vNext.pre = prerelease
|
||||
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||
return vNext, nil
|
||||
}
|
||||
|
||||
// SetMetadata defines metadata value.
|
||||
// Value must not include the required 'plus' prefix.
|
||||
func (v Version) SetMetadata(metadata string) (Version, error) {
|
||||
vNext := v
|
||||
if len(metadata) > 0 {
|
||||
if err := validateMetadata(metadata); err != nil {
|
||||
return vNext, err
|
||||
}
|
||||
}
|
||||
vNext.metadata = metadata
|
||||
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||
return vNext, nil
|
||||
}
|
||||
|
||||
// LessThan tests if one version is less than another one.
|
||||
func (v *Version) LessThan(o *Version) bool {
|
||||
return v.Compare(o) < 0
|
||||
}
|
||||
|
||||
// GreaterThan tests if one version is greater than another one.
|
||||
func (v *Version) GreaterThan(o *Version) bool {
|
||||
return v.Compare(o) > 0
|
||||
}
|
||||
|
||||
// Equal tests if two versions are equal to each other.
|
||||
// Note, versions can be equal with different metadata since metadata
|
||||
// is not considered part of the comparable version.
|
||||
func (v *Version) Equal(o *Version) bool {
|
||||
return v.Compare(o) == 0
|
||||
}
|
||||
|
||||
// Compare compares this version to another one. It returns -1, 0, or 1 if
|
||||
// the version smaller, equal, or larger than the other version.
|
||||
//
|
||||
// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is
|
||||
// lower than the version without a prerelease. Compare always takes into account
|
||||
// prereleases. If you want to work with ranges using typical range syntaxes that
|
||||
// skip prereleases if the range is not looking for them use constraints.
|
||||
func (v *Version) Compare(o *Version) int {
|
||||
// Compare the major, minor, and patch version for differences. If a
|
||||
// difference is found return the comparison.
|
||||
if d := compareSegment(v.Major(), o.Major()); d != 0 {
|
||||
return d
|
||||
}
|
||||
if d := compareSegment(v.Minor(), o.Minor()); d != 0 {
|
||||
return d
|
||||
}
|
||||
if d := compareSegment(v.Patch(), o.Patch()); d != 0 {
|
||||
return d
|
||||
}
|
||||
|
||||
// At this point the major, minor, and patch versions are the same.
|
||||
ps := v.pre
|
||||
po := o.Prerelease()
|
||||
|
||||
if ps == "" && po == "" {
|
||||
return 0
|
||||
}
|
||||
if ps == "" {
|
||||
return 1
|
||||
}
|
||||
if po == "" {
|
||||
return -1
|
||||
}
|
||||
|
||||
return comparePrerelease(ps, po)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements JSON.Unmarshaler interface.
|
||||
func (v *Version) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
temp, err := NewVersion(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.major = temp.major
|
||||
v.minor = temp.minor
|
||||
v.patch = temp.patch
|
||||
v.pre = temp.pre
|
||||
v.metadata = temp.metadata
|
||||
v.original = temp.original
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements JSON.Marshaler interface.
|
||||
func (v Version) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.String())
|
||||
}
|
||||
|
||||
func compareSegment(v, o uint64) int {
|
||||
if v < o {
|
||||
return -1
|
||||
}
|
||||
if v > o {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func comparePrerelease(v, o string) int {
|
||||
|
||||
// split the prelease versions by their part. The separator, per the spec,
|
||||
// is a .
|
||||
sparts := strings.Split(v, ".")
|
||||
oparts := strings.Split(o, ".")
|
||||
|
||||
// Find the longer length of the parts to know how many loop iterations to
|
||||
// go through.
|
||||
slen := len(sparts)
|
||||
olen := len(oparts)
|
||||
|
||||
l := slen
|
||||
if olen > slen {
|
||||
l = olen
|
||||
}
|
||||
|
||||
// Iterate over each part of the prereleases to compare the differences.
|
||||
for i := 0; i < l; i++ {
|
||||
// Since the lentgh of the parts can be different we need to create
|
||||
// a placeholder. This is to avoid out of bounds issues.
|
||||
stemp := ""
|
||||
if i < slen {
|
||||
stemp = sparts[i]
|
||||
}
|
||||
|
||||
otemp := ""
|
||||
if i < olen {
|
||||
otemp = oparts[i]
|
||||
}
|
||||
|
||||
d := comparePrePart(stemp, otemp)
|
||||
if d != 0 {
|
||||
return d
|
||||
}
|
||||
}
|
||||
|
||||
// Reaching here means two versions are of equal value but have different
|
||||
// metadata (the part following a +). They are not identical in string form
|
||||
// but the version comparison finds them to be equal.
|
||||
return 0
|
||||
}
|
||||
|
||||
func comparePrePart(s, o string) int {
|
||||
// Fastpath if they are equal
|
||||
if s == o {
|
||||
return 0
|
||||
}
|
||||
|
||||
// When s or o are empty we can use the other in an attempt to determine
|
||||
// the response.
|
||||
if s == "" {
|
||||
if o != "" {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if o == "" {
|
||||
if s != "" {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// When comparing strings "99" is greater than "103". To handle
|
||||
// cases like this we need to detect numbers and compare them. According
|
||||
// to the semver spec, numbers are always positive. If there is a - at the
|
||||
// start like -99 this is to be evaluated as an alphanum. numbers always
|
||||
// have precedence over alphanum. Parsing as Uints because negative numbers
|
||||
// are ignored.
|
||||
|
||||
oi, n1 := strconv.ParseUint(o, 10, 64)
|
||||
si, n2 := strconv.ParseUint(s, 10, 64)
|
||||
|
||||
// The case where both are strings compare the strings
|
||||
if n1 != nil && n2 != nil {
|
||||
if s > o {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
} else if n1 != nil {
|
||||
// o is a string and s is a number
|
||||
return -1
|
||||
} else if n2 != nil {
|
||||
// s is a string and o is a number
|
||||
return 1
|
||||
}
|
||||
// Both are numbers
|
||||
if si > oi {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
|
||||
}
|
||||
|
||||
// Like strings.ContainsAny but does an only instead of any.
|
||||
func containsOnly(s string, comp string) bool {
|
||||
return strings.IndexFunc(s, func(r rune) bool {
|
||||
return !strings.ContainsRune(comp, r)
|
||||
}) == -1
|
||||
}
|
||||
|
||||
// From the spec, "Identifiers MUST comprise only
|
||||
// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty.
|
||||
// Numeric identifiers MUST NOT include leading zeroes.". These segments can
|
||||
// be dot separated.
|
||||
func validatePrerelease(p string) error {
|
||||
eparts := strings.Split(p, ".")
|
||||
for _, p := range eparts {
|
||||
if containsOnly(p, num) {
|
||||
if len(p) > 1 && p[0] == '0' {
|
||||
return ErrSegmentStartsZero
|
||||
}
|
||||
} else if !containsOnly(p, allowed) {
|
||||
return ErrInvalidPrerelease
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// From the spec, "Build metadata MAY be denoted by
|
||||
// appending a plus sign and a series of dot separated identifiers immediately
|
||||
// following the patch or pre-release version. Identifiers MUST comprise only
|
||||
// ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty."
|
||||
func validateMetadata(m string) error {
|
||||
eparts := strings.Split(m, ".")
|
||||
for _, p := range eparts {
|
||||
if !containsOnly(p, allowed) {
|
||||
return ErrInvalidMetadata
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2016 Docker, Inc.
|
||||
Copyright The containerd Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
16
vendor/github.com/containerd/containerd/NOTICE
generated
vendored
Normal file
16
vendor/github.com/containerd/containerd/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
Docker
|
||||
Copyright 2012-2015 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
|
||||
Use and transfer of Docker may be subject to certain restrictions by the
|
||||
United States and other governments.
|
||||
It is your responsibility to ensure that your use and/or transfer does not
|
||||
violate applicable laws.
|
||||
|
||||
For more information, please see https://www.bis.doc.gov
|
||||
|
||||
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
||||
93
vendor/github.com/containerd/containerd/errdefs/errors.go
generated
vendored
Normal file
93
vendor/github.com/containerd/containerd/errdefs/errors.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 errdefs defines the common errors used throughout containerd
|
||||
// packages.
|
||||
//
|
||||
// Use with errors.Wrap and error.Wrapf to add context to an error.
|
||||
//
|
||||
// To detect an error class, use the IsXXX functions to tell whether an error
|
||||
// is of a certain type.
|
||||
//
|
||||
// The functions ToGRPC and FromGRPC can be used to map server-side and
|
||||
// client-side errors to the correct types.
|
||||
package errdefs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Definitions of common error types used throughout containerd. All containerd
|
||||
// errors returned by most packages will map into one of these errors classes.
|
||||
// Packages should return errors of these types when they want to instruct a
|
||||
// client to take a particular action.
|
||||
//
|
||||
// For the most part, we just try to provide local grpc errors. Most conditions
|
||||
// map very well to those defined by grpc.
|
||||
var (
|
||||
ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping.
|
||||
ErrInvalidArgument = errors.New("invalid argument")
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrAlreadyExists = errors.New("already exists")
|
||||
ErrFailedPrecondition = errors.New("failed precondition")
|
||||
ErrUnavailable = errors.New("unavailable")
|
||||
ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented
|
||||
)
|
||||
|
||||
// IsInvalidArgument returns true if the error is due to an invalid argument
|
||||
func IsInvalidArgument(err error) bool {
|
||||
return errors.Cause(err) == ErrInvalidArgument
|
||||
}
|
||||
|
||||
// IsNotFound returns true if the error is due to a missing object
|
||||
func IsNotFound(err error) bool {
|
||||
return errors.Cause(err) == ErrNotFound
|
||||
}
|
||||
|
||||
// IsAlreadyExists returns true if the error is due to an already existing
|
||||
// metadata item
|
||||
func IsAlreadyExists(err error) bool {
|
||||
return errors.Cause(err) == ErrAlreadyExists
|
||||
}
|
||||
|
||||
// IsFailedPrecondition returns true if an operation could not proceed to the
|
||||
// lack of a particular condition
|
||||
func IsFailedPrecondition(err error) bool {
|
||||
return errors.Cause(err) == ErrFailedPrecondition
|
||||
}
|
||||
|
||||
// IsUnavailable returns true if the error is due to a resource being unavailable
|
||||
func IsUnavailable(err error) bool {
|
||||
return errors.Cause(err) == ErrUnavailable
|
||||
}
|
||||
|
||||
// IsNotImplemented returns true if the error is due to not being implemented
|
||||
func IsNotImplemented(err error) bool {
|
||||
return errors.Cause(err) == ErrNotImplemented
|
||||
}
|
||||
|
||||
// IsCanceled returns true if the error is due to `context.Canceled`.
|
||||
func IsCanceled(err error) bool {
|
||||
return errors.Cause(err) == context.Canceled
|
||||
}
|
||||
|
||||
// IsDeadlineExceeded returns true if the error is due to
|
||||
// `context.DeadlineExceeded`.
|
||||
func IsDeadlineExceeded(err error) bool {
|
||||
return errors.Cause(err) == context.DeadlineExceeded
|
||||
}
|
||||
147
vendor/github.com/containerd/containerd/errdefs/grpc.go
generated
vendored
Normal file
147
vendor/github.com/containerd/containerd/errdefs/grpc.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
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 errdefs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// ToGRPC will attempt to map the backend containerd error into a grpc error,
|
||||
// using the original error message as a description.
|
||||
//
|
||||
// Further information may be extracted from certain errors depending on their
|
||||
// type.
|
||||
//
|
||||
// If the error is unmapped, the original error will be returned to be handled
|
||||
// by the regular grpc error handling stack.
|
||||
func ToGRPC(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isGRPCError(err) {
|
||||
// error has already been mapped to grpc
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case IsInvalidArgument(err):
|
||||
return status.Errorf(codes.InvalidArgument, err.Error())
|
||||
case IsNotFound(err):
|
||||
return status.Errorf(codes.NotFound, err.Error())
|
||||
case IsAlreadyExists(err):
|
||||
return status.Errorf(codes.AlreadyExists, err.Error())
|
||||
case IsFailedPrecondition(err):
|
||||
return status.Errorf(codes.FailedPrecondition, err.Error())
|
||||
case IsUnavailable(err):
|
||||
return status.Errorf(codes.Unavailable, err.Error())
|
||||
case IsNotImplemented(err):
|
||||
return status.Errorf(codes.Unimplemented, err.Error())
|
||||
case IsCanceled(err):
|
||||
return status.Errorf(codes.Canceled, err.Error())
|
||||
case IsDeadlineExceeded(err):
|
||||
return status.Errorf(codes.DeadlineExceeded, err.Error())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
|
||||
// and combining it with the target error string.
|
||||
//
|
||||
// This is equivalent to errors.ToGRPC(errors.Wrapf(err, format, args...))
|
||||
func ToGRPCf(err error, format string, args ...interface{}) error {
|
||||
return ToGRPC(errors.Wrapf(err, format, args...))
|
||||
}
|
||||
|
||||
// FromGRPC returns the underlying error from a grpc service based on the grpc error code
|
||||
func FromGRPC(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cls error // divide these into error classes, becomes the cause
|
||||
|
||||
switch code(err) {
|
||||
case codes.InvalidArgument:
|
||||
cls = ErrInvalidArgument
|
||||
case codes.AlreadyExists:
|
||||
cls = ErrAlreadyExists
|
||||
case codes.NotFound:
|
||||
cls = ErrNotFound
|
||||
case codes.Unavailable:
|
||||
cls = ErrUnavailable
|
||||
case codes.FailedPrecondition:
|
||||
cls = ErrFailedPrecondition
|
||||
case codes.Unimplemented:
|
||||
cls = ErrNotImplemented
|
||||
case codes.Canceled:
|
||||
cls = context.Canceled
|
||||
case codes.DeadlineExceeded:
|
||||
cls = context.DeadlineExceeded
|
||||
default:
|
||||
cls = ErrUnknown
|
||||
}
|
||||
|
||||
msg := rebaseMessage(cls, err)
|
||||
if msg != "" {
|
||||
err = errors.Wrap(cls, msg)
|
||||
} else {
|
||||
err = errors.WithStack(cls)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// rebaseMessage removes the repeats for an error at the end of an error
|
||||
// string. This will happen when taking an error over grpc then remapping it.
|
||||
//
|
||||
// Effectively, we just remove the string of cls from the end of err if it
|
||||
// appears there.
|
||||
func rebaseMessage(cls error, err error) string {
|
||||
desc := errDesc(err)
|
||||
clss := cls.Error()
|
||||
if desc == clss {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(desc, ": "+clss)
|
||||
}
|
||||
|
||||
func isGRPCError(err error) bool {
|
||||
_, ok := status.FromError(err)
|
||||
return ok
|
||||
}
|
||||
|
||||
func code(err error) codes.Code {
|
||||
if s, ok := status.FromError(err); ok {
|
||||
return s.Code()
|
||||
}
|
||||
return codes.Unknown
|
||||
}
|
||||
|
||||
func errDesc(err error) string {
|
||||
if s, ok := status.FromError(err); ok {
|
||||
return s.Message()
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
61
vendor/github.com/docker/docker/AUTHORS
generated
vendored
61
vendor/github.com/docker/docker/AUTHORS
generated
vendored
@@ -4,6 +4,7 @@
|
||||
Aanand Prasad <aanand.prasad@gmail.com>
|
||||
Aaron Davidson <aaron@databricks.com>
|
||||
Aaron Feng <aaron.feng@gmail.com>
|
||||
Aaron Hnatiw <aaron@griddio.com>
|
||||
Aaron Huslage <huslage@gmail.com>
|
||||
Aaron L. Xu <liker.xu@foxmail.com>
|
||||
Aaron Lehmann <aaron.lehmann@docker.com>
|
||||
@@ -17,6 +18,7 @@ Abhishek Chanda <abhishek.becs@gmail.com>
|
||||
Abhishek Sharma <abhishek@asharma.me>
|
||||
Abin Shahab <ashahab@altiscale.com>
|
||||
Adam Avilla <aavilla@yp.com>
|
||||
Adam Dobrawy <naczelnik@jawnosc.tk>
|
||||
Adam Eijdenberg <adam.eijdenberg@gmail.com>
|
||||
Adam Kunk <adam.kunk@tiaa-cref.org>
|
||||
Adam Miller <admiller@redhat.com>
|
||||
@@ -112,6 +114,7 @@ Anda Xu <anda.xu@docker.com>
|
||||
Anders Janmyr <anders@janmyr.com>
|
||||
Andre Dublin <81dublin@gmail.com>
|
||||
Andre Granovsky <robotciti@live.com>
|
||||
Andrea Denisse Gómez <crypto.andrea@protonmail.ch>
|
||||
Andrea Luzzardi <aluzzardi@gmail.com>
|
||||
Andrea Turli <andrea.turli@gmail.com>
|
||||
Andreas Elvers <andreas@work.de>
|
||||
@@ -176,8 +179,10 @@ Anusha Ragunathan <anusha.ragunathan@docker.com>
|
||||
apocas <petermdias@gmail.com>
|
||||
Arash Deshmeh <adeshmeh@ca.ibm.com>
|
||||
ArikaChen <eaglesora@gmail.com>
|
||||
Arko Dasgupta <arko.dasgupta@docker.com>
|
||||
Arnaud Lefebvre <a.lefebvre@outlook.fr>
|
||||
Arnaud Porterie <arnaud.porterie@docker.com>
|
||||
Arnaud Rebillout <arnaud.rebillout@collabora.com>
|
||||
Arthur Barr <arthur.barr@uk.ibm.com>
|
||||
Arthur Gautier <baloo@gandi.net>
|
||||
Artur Meyster <arthurfbi@yahoo.com>
|
||||
@@ -279,6 +284,7 @@ Carl Loa Odin <carlodin@gmail.com>
|
||||
Carl X. Su <bcbcarl@gmail.com>
|
||||
Carlo Mion <mion00@gmail.com>
|
||||
Carlos Alexandro Becker <caarlos0@gmail.com>
|
||||
Carlos de Paula <me@carlosedp.com>
|
||||
Carlos Sanchez <carlos@apache.org>
|
||||
Carol Fager-Higgins <carol.fager-higgins@docker.com>
|
||||
Cary <caryhartline@users.noreply.github.com>
|
||||
@@ -328,6 +334,7 @@ Chris Gibson <chris@chrisg.io>
|
||||
Chris Khoo <chris.khoo@gmail.com>
|
||||
Chris McKinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||
Chris McKinnel <chrismckinnel@gmail.com>
|
||||
Chris Price <chris.price@docker.com>
|
||||
Chris Seto <chriskseto@gmail.com>
|
||||
Chris Snow <chsnow123@gmail.com>
|
||||
Chris St. Pierre <chris.a.st.pierre@gmail.com>
|
||||
@@ -417,12 +424,14 @@ Daniel Norberg <dano@spotify.com>
|
||||
Daniel Nordberg <dnordberg@gmail.com>
|
||||
Daniel Robinson <gottagetmac@gmail.com>
|
||||
Daniel S <dan.streby@gmail.com>
|
||||
Daniel Sweet <danieljsweet@icloud.com>
|
||||
Daniel Von Fange <daniel@leancoder.com>
|
||||
Daniel Watkins <daniel@daniel-watkins.co.uk>
|
||||
Daniel X Moore <yahivin@gmail.com>
|
||||
Daniel YC Lin <dlin.tw@gmail.com>
|
||||
Daniel Zhang <jmzwcn@gmail.com>
|
||||
Danny Berger <dpb587@gmail.com>
|
||||
Danny Milosavljevic <dannym@scratchpost.org>
|
||||
Danny Yates <danny@codeaholics.org>
|
||||
Danyal Khaliq <danyal.khaliq@tenpearls.com>
|
||||
Darren Coxall <darren@darrencoxall.com>
|
||||
@@ -516,6 +525,8 @@ Dmitry Smirnov <onlyjob@member.fsf.org>
|
||||
Dmitry V. Krivenok <krivenok.dmitry@gmail.com>
|
||||
Dmitry Vorobev <dimahabr@gmail.com>
|
||||
Dolph Mathews <dolph.mathews@gmail.com>
|
||||
Dominic Tubach <dominic.tubach@to.com>
|
||||
Dominic Yin <yindongchao@inspur.com>
|
||||
Dominik Dingel <dingel@linux.vnet.ibm.com>
|
||||
Dominik Finkbeiner <finkes93@gmail.com>
|
||||
Dominik Honnef <dominik@honnef.co>
|
||||
@@ -584,6 +595,7 @@ Erik Weathers <erikdw@gmail.com>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
Erwin van der Koogh <info@erronis.nl>
|
||||
Ethan Bell <ebgamer29@gmail.com>
|
||||
Ethan Mosbaugh <ethan@replicated.com>
|
||||
Euan Kemp <euan.kemp@coreos.com>
|
||||
Eugen Krizo <eugen.krizo@gmail.com>
|
||||
Eugene Yakubovich <eugene.yakubovich@coreos.com>
|
||||
@@ -620,6 +632,7 @@ Fareed Dudhia <fareeddudhia@googlemail.com>
|
||||
Fathi Boudra <fathi.boudra@linaro.org>
|
||||
Federico Gimenez <fgimenez@coit.es>
|
||||
Felipe Oliveira <felipeweb.programador@gmail.com>
|
||||
Felipe Ruhland <felipe.ruhland@gmail.com>
|
||||
Felix Abecassis <fabecassis@nvidia.com>
|
||||
Felix Geisendörfer <felix@debuggable.com>
|
||||
Felix Hupfeld <felix@quobyte.com>
|
||||
@@ -654,6 +667,7 @@ Frank Groeneveld <frank@ivaldi.nl>
|
||||
Frank Herrmann <fgh@4gh.tv>
|
||||
Frank Macreery <frank@macreery.com>
|
||||
Frank Rosquin <frank.rosquin+github@gmail.com>
|
||||
frankyang <yyb196@gmail.com>
|
||||
Fred Lifton <fred.lifton@docker.com>
|
||||
Frederick F. Kautz IV <fkautz@redhat.com>
|
||||
Frederik Loeffert <frederik@zitrusmedia.de>
|
||||
@@ -701,6 +715,7 @@ Gleb M Borisov <borisov.gleb@gmail.com>
|
||||
Glyn Normington <gnormington@gopivotal.com>
|
||||
GoBella <caili_welcome@163.com>
|
||||
Goffert van Gool <goffert@phusion.nl>
|
||||
Goldwyn Rodrigues <rgoldwyn@suse.com>
|
||||
Gopikannan Venugopalsamy <gopikannan.venugopalsamy@gmail.com>
|
||||
Gosuke Miyashita <gosukenator@gmail.com>
|
||||
Gou Rao <gou@portworx.com>
|
||||
@@ -724,6 +739,7 @@ Guruprasad <lgp171188@gmail.com>
|
||||
Gustav Sinder <gustav.sinder@gmail.com>
|
||||
gwx296173 <gaojing3@huawei.com>
|
||||
Günter Zöchbauer <guenter@gzoechbauer.com>
|
||||
Haichao Yang <yang.haichao@zte.com.cn>
|
||||
haikuoliu <haikuo@amazon.com>
|
||||
Hakan Özler <hakan.ozler@kodcu.com>
|
||||
Hamish Hutchings <moredhel@aoeu.me>
|
||||
@@ -732,6 +748,7 @@ Hans Rødtang <hansrodtang@gmail.com>
|
||||
Hao Shu Wei <haosw@cn.ibm.com>
|
||||
Hao Zhang <21521210@zju.edu.cn>
|
||||
Harald Albers <github@albersweb.de>
|
||||
Harald Niesche <harald@niesche.de>
|
||||
Harley Laue <losinggeneration@gmail.com>
|
||||
Harold Cooper <hrldcpr@gmail.com>
|
||||
Harrison Turton <harrisonturton@gmail.com>
|
||||
@@ -751,9 +768,11 @@ Hobofan <goisser94@gmail.com>
|
||||
Hollie Teal <hollie@docker.com>
|
||||
Hong Xu <hong@topbug.net>
|
||||
Hongbin Lu <hongbin034@gmail.com>
|
||||
Hongxu Jia <hongxu.jia@windriver.com>
|
||||
hsinko <21551195@zju.edu.cn>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Hu Tao <hutao@cn.fujitsu.com>
|
||||
HuanHuan Ye <logindaveye@gmail.com>
|
||||
Huanzhong Zhang <zhanghuanzhong90@gmail.com>
|
||||
Huayi Zhang <irachex@gmail.com>
|
||||
Hugo Duncan <hugo@hugoduncan.org>
|
||||
@@ -897,6 +916,7 @@ Jie Luo <luo612@zju.edu.cn>
|
||||
Jihyun Hwang <jhhwang@telcoware.com>
|
||||
Jilles Oldenbeuving <ojilles@gmail.com>
|
||||
Jim Alateras <jima@comware.com.au>
|
||||
Jim Ehrismann <jim.ehrismann@docker.com>
|
||||
Jim Galasyn <jim.galasyn@docker.com>
|
||||
Jim Minter <jminter@redhat.com>
|
||||
Jim Perrin <jperrin@centos.org>
|
||||
@@ -934,7 +954,7 @@ John Feminella <jxf@jxf.me>
|
||||
John Gardiner Myers <jgmyers@proofpoint.com>
|
||||
John Gossman <johngos@microsoft.com>
|
||||
John Harris <john@johnharris.io>
|
||||
John Howard (VM) <John.Howard@microsoft.com>
|
||||
John Howard <github@lowenna.com>
|
||||
John Laswell <john.n.laswell@gmail.com>
|
||||
John Maguire <jmaguire@duosecurity.com>
|
||||
John Mulhausen <john@docker.com>
|
||||
@@ -948,6 +968,7 @@ John Willis <john.willis@docker.com>
|
||||
Jon Johnson <jonjohnson@google.com>
|
||||
Jon Surrell <jon.surrell@gmail.com>
|
||||
Jon Wedaman <jweede@gmail.com>
|
||||
Jonas Dohse <jonas@dohse.ch>
|
||||
Jonas Pfenniger <jonas@pfenniger.name>
|
||||
Jonathan A. Schweder <jonathanschweder@gmail.com>
|
||||
Jonathan A. Sternberg <jonathansternberg@gmail.com>
|
||||
@@ -1001,6 +1022,7 @@ Julio Montes <imc.coder@gmail.com>
|
||||
Jun-Ru Chang <jrjang@gmail.com>
|
||||
Jussi Nummelin <jussi.nummelin@gmail.com>
|
||||
Justas Brazauskas <brazauskasjustas@gmail.com>
|
||||
Justen Martin <jmart@the-coder.com>
|
||||
Justin Cormack <justin.cormack@docker.com>
|
||||
Justin Force <justin.force@gmail.com>
|
||||
Justin Menga <justin.menga@gmail.com>
|
||||
@@ -1009,6 +1031,7 @@ Justin Simonelis <justin.p.simonelis@gmail.com>
|
||||
Justin Terry <juterry@microsoft.com>
|
||||
Justyn Temme <justyntemme@gmail.com>
|
||||
Jyrki Puttonen <jyrkiput@gmail.com>
|
||||
Jérémy Leherpeur <amenophis@leherpeur.net>
|
||||
Jérôme Petazzoni <jerome.petazzoni@docker.com>
|
||||
Jörg Thalheim <joerg@higgsboson.tk>
|
||||
K. Heller <pestophagous@gmail.com>
|
||||
@@ -1046,6 +1069,7 @@ Ken Reese <krrgithub@gmail.com>
|
||||
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
|
||||
Kenjiro Nakayama <nakayamakenjiro@gmail.com>
|
||||
Kent Johnson <kentoj@gmail.com>
|
||||
Kenta Tada <Kenta.Tada@sony.com>
|
||||
Kevin "qwazerty" Houdebert <kevin.houdebert@gmail.com>
|
||||
Kevin Burke <kev@inburke.com>
|
||||
Kevin Clark <kevin.clark@gmail.com>
|
||||
@@ -1056,6 +1080,7 @@ Kevin Kern <kaiwentan@harmonycloud.cn>
|
||||
Kevin Menard <kevin@nirvdrum.com>
|
||||
Kevin Meredith <kevin.m.meredith@gmail.com>
|
||||
Kevin P. Kucharczyk <kevinkucharczyk@gmail.com>
|
||||
Kevin Parsons <kevpar@microsoft.com>
|
||||
Kevin Richardson <kevin@kevinrichardson.co>
|
||||
Kevin Shi <kshi@andrew.cmu.edu>
|
||||
Kevin Wallace <kevin@pentabarf.net>
|
||||
@@ -1146,6 +1171,7 @@ longliqiang88 <394564827@qq.com>
|
||||
Lorenz Leutgeb <lorenz.leutgeb@gmail.com>
|
||||
Lorenzo Fontana <fontanalorenz@gmail.com>
|
||||
Lotus Fenn <fenn.lotus@gmail.com>
|
||||
Louis Delossantos <ldelossa.ld@gmail.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
Luca Favatella <luca.favatella@erlang-solutions.com>
|
||||
Luca Marturana <lucamarturana@gmail.com>
|
||||
@@ -1154,15 +1180,18 @@ Luca-Bogdan Grigorescu <Luca-Bogdan Grigorescu>
|
||||
Lucas Chan <lucas-github@lucaschan.com>
|
||||
Lucas Chi <lucas@teacherspayteachers.com>
|
||||
Lucas Molas <lmolas@fundacionsadosky.org.ar>
|
||||
Lucas Silvestre <lukas.silvestre@gmail.com>
|
||||
Luciano Mores <leslau@gmail.com>
|
||||
Luis Martínez de Bartolomé Izquierdo <lmartinez@biicode.com>
|
||||
Luiz Svoboda <luizek@gmail.com>
|
||||
Lukas Heeren <lukas-heeren@hotmail.com>
|
||||
Lukas Waslowski <cr7pt0gr4ph7@gmail.com>
|
||||
lukaspustina <lukas.pustina@centerdevice.com>
|
||||
Lukasz Zajaczkowski <Lukasz.Zajaczkowski@ts.fujitsu.com>
|
||||
Luke Marsden <me@lukemarsden.net>
|
||||
Lyn <energylyn@zju.edu.cn>
|
||||
Lynda O'Leary <lyndaoleary29@gmail.com>
|
||||
lzhfromutsc <lzhfromustc@gmail.com>
|
||||
Lénaïc Huard <lhuard@amadeus.com>
|
||||
Ma Müller <mueller-ma@users.noreply.github.com>
|
||||
Ma Shimiao <mashimiao.fnst@cn.fujitsu.com>
|
||||
@@ -1296,6 +1325,7 @@ Michael Stapelberg <michael+gh@stapelberg.de>
|
||||
Michael Steinert <mike.steinert@gmail.com>
|
||||
Michael Thies <michaelthies78@gmail.com>
|
||||
Michael West <mwest@mdsol.com>
|
||||
Michael Zhao <michael.zhao@arm.com>
|
||||
Michal Fojtik <mfojtik@redhat.com>
|
||||
Michal Gebauer <mishak@mishak.net>
|
||||
Michal Jemala <michal.jemala@gmail.com>
|
||||
@@ -1380,6 +1410,7 @@ Neyazul Haque <nuhaque@gmail.com>
|
||||
Nghia Tran <nghia@google.com>
|
||||
Niall O'Higgins <niallo@unworkable.org>
|
||||
Nicholas E. Rabenau <nerab@gmx.at>
|
||||
Nick Adcock <nick.adcock@docker.com>
|
||||
Nick DeCoursin <n.decoursin@foodpanda.com>
|
||||
Nick Irvine <nfirvine@nfirvine.com>
|
||||
Nick Neisen <nwneisen@gmail.com>
|
||||
@@ -1418,6 +1449,7 @@ Nuutti Kotivuori <naked@iki.fi>
|
||||
nzwsch <hi@nzwsch.com>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
objectified <objectified@gmail.com>
|
||||
Odin Ugedal <odin@ugedal.com>
|
||||
Oguz Bilgic <fisyonet@gmail.com>
|
||||
Oh Jinkyun <tintypemolly@gmail.com>
|
||||
Ohad Schneider <ohadschn@users.noreply.github.com>
|
||||
@@ -1428,6 +1460,7 @@ Oliver Reason <oli@overrateddev.co>
|
||||
Olivier Gambier <dmp42@users.noreply.github.com>
|
||||
Olle Jonsson <olle.jonsson@gmail.com>
|
||||
Olli Janatuinen <olli.janatuinen@gmail.com>
|
||||
Olly Pomeroy <oppomeroy@gmail.com>
|
||||
Omri Shiv <Omri.Shiv@teradata.com>
|
||||
Oriol Francès <oriolfa@gmail.com>
|
||||
Oskar Niburski <oskarniburski@gmail.com>
|
||||
@@ -1437,6 +1470,7 @@ Ovidio Mallo <ovidio.mallo@gmail.com>
|
||||
Panagiotis Moustafellos <pmoust@elastic.co>
|
||||
Paolo G. Giarrusso <p.giarrusso@gmail.com>
|
||||
Pascal <pascalgn@users.noreply.github.com>
|
||||
Pascal Bach <pascal.bach@siemens.com>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Pascal Hartig <phartig@rdrei.net>
|
||||
Patrick Böänziger <patrick.baenziger@bsi-software.com>
|
||||
@@ -1461,6 +1495,7 @@ Paul Nasrat <pnasrat@gmail.com>
|
||||
Paul Weaver <pauweave@cisco.com>
|
||||
Paulo Ribeiro <paigr.io@gmail.com>
|
||||
Pavel Lobashov <ShockwaveNN@gmail.com>
|
||||
Pavel Matěja <pavel@verotel.cz>
|
||||
Pavel Pletenev <cpp.create@gmail.com>
|
||||
Pavel Pospisil <pospispa@gmail.com>
|
||||
Pavel Sutyrin <pavel.sutyrin@gmail.com>
|
||||
@@ -1572,6 +1607,7 @@ Riku Voipio <riku.voipio@linaro.org>
|
||||
Riley Guerin <rileytg.dev@gmail.com>
|
||||
Ritesh H Shukla <sritesh@vmware.com>
|
||||
Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
|
||||
Rob Gulewich <rgulewich@netflix.com>
|
||||
Rob Vesse <rvesse@dotnetrdf.org>
|
||||
Robert Bachmann <rb@robertbachmann.at>
|
||||
Robert Bittle <guywithnose@gmail.com>
|
||||
@@ -1580,11 +1616,13 @@ Robert Schneider <mail@shakeme.info>
|
||||
Robert Stern <lexandro2000@gmail.com>
|
||||
Robert Terhaar <rterhaar@atlanticdynamic.com>
|
||||
Robert Wallis <smilingrob@gmail.com>
|
||||
Robert Wang <robert@arctic.tw>
|
||||
Roberto G. Hashioka <roberto.hashioka@docker.com>
|
||||
Roberto Muñoz Fernández <robertomf@gmail.com>
|
||||
Robin Naundorf <r.naundorf@fh-muenster.de>
|
||||
Robin Schneider <ypid@riseup.net>
|
||||
Robin Speekenbrink <robin@kingsquare.nl>
|
||||
Robin Thoni <robin@rthoni.com>
|
||||
robpc <rpcann@gmail.com>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Rodrigo Vaz <rodrigo.vaz@gmail.com>
|
||||
@@ -1618,6 +1656,7 @@ Rozhnov Alexandr <nox73@ya.ru>
|
||||
Rudolph Gottesheim <r.gottesheim@loot.at>
|
||||
Rui Cao <ruicao@alauda.io>
|
||||
Rui Lopes <rgl@ruilopes.com>
|
||||
Ruilin Li <liruilin4@huawei.com>
|
||||
Runshen Zhu <runshen.zhu@gmail.com>
|
||||
Russ Magee <rmagee@gmail.com>
|
||||
Ryan Abrams <rdabrams@gmail.com>
|
||||
@@ -1656,6 +1695,7 @@ Sam J Sharpe <sam.sharpe@digital.cabinet-office.gov.uk>
|
||||
Sam Neirinck <sam@samneirinck.com>
|
||||
Sam Reis <sreis@atlassian.com>
|
||||
Sam Rijs <srijs@airpost.net>
|
||||
Sam Whited <sam@samwhited.com>
|
||||
Sambuddha Basu <sambuddhabasu1@gmail.com>
|
||||
Sami Wagiaalla <swagiaal@redhat.com>
|
||||
Samuel Andaya <samuel@andaya.net>
|
||||
@@ -1670,6 +1710,7 @@ sapphiredev <se.imas.kr@gmail.com>
|
||||
Sargun Dhillon <sargun@netflix.com>
|
||||
Sascha Andres <sascha.andres@outlook.com>
|
||||
Sascha Grunert <sgrunert@suse.com>
|
||||
SataQiu <qiushida@beyondcent.com>
|
||||
Satnam Singh <satnam@raintown.org>
|
||||
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
|
||||
Satoshi Tagomori <tagomoris@gmail.com>
|
||||
@@ -1718,6 +1759,7 @@ Shijun Qin <qinshijun16@mails.ucas.ac.cn>
|
||||
Shishir Mahajan <shishir.mahajan@redhat.com>
|
||||
Shoubhik Bose <sbose78@gmail.com>
|
||||
Shourya Sarcar <shourya.sarcar@gmail.com>
|
||||
Shu-Wai Chow <shu-wai.chow@seattlechildrens.org>
|
||||
shuai-z <zs.broccoli@gmail.com>
|
||||
Shukui Yang <yangshukui@huawei.com>
|
||||
Shuwei Hao <haosw@cn.ibm.com>
|
||||
@@ -1728,6 +1770,7 @@ Silas Sewell <silas@sewell.org>
|
||||
Silvan Jegen <s.jegen@gmail.com>
|
||||
Simão Reis <smnrsti@gmail.com>
|
||||
Simei He <hesimei@zju.edu.cn>
|
||||
Simon Barendse <simon.barendse@gmail.com>
|
||||
Simon Eskildsen <sirup@sirupsen.com>
|
||||
Simon Ferquel <simon.ferquel@docker.com>
|
||||
Simon Leinen <simon.leinen@gmail.com>
|
||||
@@ -1736,6 +1779,7 @@ Simon Taranto <simon.taranto@gmail.com>
|
||||
Simon Vikstrom <pullreq@devsn.se>
|
||||
Sindhu S <sindhus@live.in>
|
||||
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
|
||||
skanehira <sho19921005@gmail.com>
|
||||
Solganik Alexander <solganik@gmail.com>
|
||||
Solomon Hykes <solomon@docker.com>
|
||||
Song Gao <song@gao.io>
|
||||
@@ -1747,16 +1791,18 @@ Sridatta Thatipamala <sthatipamala@gmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Srini Brahmaroutu <srbrahma@us.ibm.com>
|
||||
Srinivasan Srivatsan <srinivasan.srivatsan@hpe.com>
|
||||
Staf Wagemakers <staf@wagemakers.be>
|
||||
Stanislav Bondarenko <stanislav.bondarenko@gmail.com>
|
||||
Steeve Morin <steeve.morin@gmail.com>
|
||||
Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
Stefan J. Wernli <swernli@microsoft.com>
|
||||
Stefan Praszalowicz <stefan@greplin.com>
|
||||
Stefan S. <tronicum@user.github.com>
|
||||
Stefan Scherer <scherer_stefan@icloud.com>
|
||||
Stefan Scherer <stefan.scherer@docker.com>
|
||||
Stefan Staudenmeyer <doerte@instana.com>
|
||||
Stefan Weil <sw@weilnetz.de>
|
||||
Stephan Spindler <shutefan@gmail.com>
|
||||
Stephen Benjamin <stephen@redhat.com>
|
||||
Stephen Crosby <stevecrozz@gmail.com>
|
||||
Stephen Day <stephen.day@docker.com>
|
||||
Stephen Drake <stephen@xenolith.net>
|
||||
@@ -1773,10 +1819,12 @@ Steven Iveson <sjiveson@outlook.com>
|
||||
Steven Merrill <steven.merrill@gmail.com>
|
||||
Steven Richards <steven@axiomzen.co>
|
||||
Steven Taylor <steven.taylor@me.com>
|
||||
Stig Larsson <stig@larsson.dev>
|
||||
Subhajit Ghosh <isubuz.g@gmail.com>
|
||||
Sujith Haridasan <sujith.h@gmail.com>
|
||||
Sun Gengze <690388648@qq.com>
|
||||
Sun Jianbo <wonderflow.sun@gmail.com>
|
||||
Sune Keller <sune.keller@gmail.com>
|
||||
Sunny Gogoi <indiasuny000@gmail.com>
|
||||
Suryakumar Sudar <surya.trunks@gmail.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au>
|
||||
@@ -1827,6 +1875,7 @@ Tianyi Wang <capkurmagati@gmail.com>
|
||||
Tibor Vass <teabee89@gmail.com>
|
||||
Tiffany Jernigan <tiffany.f.j@gmail.com>
|
||||
Tiffany Low <tiffany@box.com>
|
||||
Tim <elatllat@gmail.com>
|
||||
Tim Bart <tim@fewagainstmany.com>
|
||||
Tim Bosse <taim@bosboot.org>
|
||||
Tim Dettrick <t.dettrick@uq.edu.au>
|
||||
@@ -1912,6 +1961,7 @@ Victor Palma <palma.victor@gmail.com>
|
||||
Victor Vieux <victor.vieux@docker.com>
|
||||
Victoria Bialas <victoria.bialas@docker.com>
|
||||
Vijaya Kumar K <vijayak@caviumnetworks.com>
|
||||
Vikram bir Singh <vikrambir.singh@docker.com>
|
||||
Viktor Stanchev <me@viktorstanchev.com>
|
||||
Viktor Vojnovski <viktor.vojnovski@amadeus.com>
|
||||
VinayRaghavanKS <raghavan.vinay@gmail.com>
|
||||
@@ -1969,6 +2019,7 @@ Wenyu You <21551128@zju.edu.cn>
|
||||
Wenzhi Liang <wenzhi.liang@gmail.com>
|
||||
Wes Morgan <cap10morgan@gmail.com>
|
||||
Wewang Xiaorenfine <wang.xiaoren@zte.com.cn>
|
||||
Wiktor Kwapisiewicz <wiktor@metacode.biz>
|
||||
Will Dietz <w@wdtz.org>
|
||||
Will Rouesnel <w.rouesnel@gmail.com>
|
||||
Will Weaver <monkey@buildingbananas.com>
|
||||
@@ -1996,6 +2047,7 @@ xichengliudui <1693291525@qq.com>
|
||||
xiekeyang <xiekeyang@huawei.com>
|
||||
Ximo Guanter Gonzálbez <joaquin.guantergonzalbez@telefonica.com>
|
||||
Xinbo Weng <xihuanbo_0521@zju.edu.cn>
|
||||
Xinfeng Liu <xinfeng.liu@gmail.com>
|
||||
Xinzi Zhou <imdreamrunner@gmail.com>
|
||||
Xiuming Chen <cc@cxm.cc>
|
||||
Xuecong Liao <satorulogic@gmail.com>
|
||||
@@ -2010,6 +2062,7 @@ Yang Pengfei <yangpengfei4@huawei.com>
|
||||
yangchenliang <yangchenliang@huawei.com>
|
||||
Yanqiang Miao <miao.yanqiang@zte.com.cn>
|
||||
Yao Zaiyong <yaozaiyong@hotmail.com>
|
||||
Yash Murty <yashmurty@gmail.com>
|
||||
Yassine Tijani <yasstij11@gmail.com>
|
||||
Yasunori Mahata <nori@mahata.net>
|
||||
Yazhong Liu <yorkiefixer@gmail.com>
|
||||
@@ -2024,6 +2077,7 @@ Yongxin Li <yxli@alauda.io>
|
||||
Yongzhi Pan <panyongzhi@gmail.com>
|
||||
Yosef Fertel <yfertel@gmail.com>
|
||||
You-Sheng Yang (楊有勝) <vicamo@gmail.com>
|
||||
youcai <omegacoleman@gmail.com>
|
||||
Youcef YEKHLEF <yyekhlef@gmail.com>
|
||||
Yu Changchun <yuchangchun1@huawei.com>
|
||||
Yu Chengxia <yuchengxia@huawei.com>
|
||||
@@ -2060,6 +2114,7 @@ Zhoulin Xie <zhoulin.xie@daocloud.io>
|
||||
Zhu Guihua <zhugh.fnst@cn.fujitsu.com>
|
||||
Zhu Kunjia <zhu.kunjia@zte.com.cn>
|
||||
Zhuoyun Wei <wzyboy@wzyboy.org>
|
||||
Ziheng Liu <lzhfromustc@gmail.com>
|
||||
Zilin Du <zilin.du@gmail.com>
|
||||
zimbatm <zimbatm@zimbatm.com>
|
||||
Ziming Dong <bnudzm@foxmail.com>
|
||||
@@ -2068,7 +2123,7 @@ zmarouf <zeid.marouf@gmail.com>
|
||||
Zoltan Tombol <zoltan.tombol@gmail.com>
|
||||
Zou Yu <zouyu7@huawei.com>
|
||||
zqh <zqhxuyuan@gmail.com>
|
||||
Zuhayr Elahi <elahi.zuhayr@gmail.com>
|
||||
Zuhayr Elahi <zuhayr.elahi@docker.com>
|
||||
Zunayed Ali <zunayed@gmail.com>
|
||||
Álex González <agonzalezro@gmail.com>
|
||||
Álvaro Lázaro <alvaro.lazaro.g@gmail.com>
|
||||
|
||||
2
vendor/github.com/docker/docker/NOTICE
generated
vendored
2
vendor/github.com/docker/docker/NOTICE
generated
vendored
@@ -3,7 +3,7 @@ Copyright 2012-2017 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||
|
||||
This product contains software (https://github.com/kr/pty) developed
|
||||
This product contains software (https://github.com/creack/pty) developed
|
||||
by Keith Rarick, licensed under the MIT License.
|
||||
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
2
vendor/github.com/docker/docker/api/common.go
generated
vendored
2
vendor/github.com/docker/docker/api/common.go
generated
vendored
@@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// DefaultVersion of Current REST API
|
||||
DefaultVersion = "1.40"
|
||||
DefaultVersion = "1.41"
|
||||
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
|
||||
480
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
480
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
@@ -19,10 +19,10 @@ produces:
|
||||
consumes:
|
||||
- "application/json"
|
||||
- "text/plain"
|
||||
basePath: "/v1.40"
|
||||
basePath: "/v1.41"
|
||||
info:
|
||||
title: "Docker Engine API"
|
||||
version: "1.40"
|
||||
version: "1.41"
|
||||
x-logo:
|
||||
url: "https://docs.docker.com/images/logo-docker-main.png"
|
||||
description: |
|
||||
@@ -49,8 +49,8 @@ info:
|
||||
the URL is not supported by the daemon, a HTTP `400 Bad Request` error message
|
||||
is returned.
|
||||
|
||||
If you omit the version-prefix, the current version of the API (v1.40) is used.
|
||||
For example, calling `/info` is the same as calling `/v1.40/info`. Using the
|
||||
If you omit the version-prefix, the current version of the API (v1.41) is used.
|
||||
For example, calling `/info` is the same as calling `/v1.41/info`. Using the
|
||||
API without a version-prefix is deprecated and will be removed in a future release.
|
||||
|
||||
Engine releases in the near future should support this version of the API,
|
||||
@@ -618,6 +618,71 @@ definitions:
|
||||
description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit."
|
||||
type: "integer"
|
||||
|
||||
Health:
|
||||
description: |
|
||||
Health stores information about the container's healthcheck results.
|
||||
type: "object"
|
||||
properties:
|
||||
Status:
|
||||
description: |
|
||||
Status is one of `none`, `starting`, `healthy` or `unhealthy`
|
||||
|
||||
- "none" Indicates there is no healthcheck
|
||||
- "starting" Starting indicates that the container is not yet ready
|
||||
- "healthy" Healthy indicates that the container is running correctly
|
||||
- "unhealthy" Unhealthy indicates that the container has a problem
|
||||
type: "string"
|
||||
enum:
|
||||
- "none"
|
||||
- "starting"
|
||||
- "healthy"
|
||||
- "unhealthy"
|
||||
example: "healthy"
|
||||
FailingStreak:
|
||||
description: "FailingStreak is the number of consecutive failures"
|
||||
type: "integer"
|
||||
example: 0
|
||||
Log:
|
||||
type: "array"
|
||||
description: |
|
||||
Log contains the last few results (oldest first)
|
||||
items:
|
||||
x-nullable: true
|
||||
$ref: "#/definitions/HealthcheckResult"
|
||||
|
||||
HealthcheckResult:
|
||||
description: |
|
||||
HealthcheckResult stores information about a single run of a healthcheck probe
|
||||
type: "object"
|
||||
properties:
|
||||
Start:
|
||||
description: |
|
||||
Date and time at which this check started in
|
||||
[RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
|
||||
type: "string"
|
||||
format: "date-time"
|
||||
example: "2020-01-04T10:44:24.496525531Z"
|
||||
End:
|
||||
description: |
|
||||
Date and time at which this check ended in
|
||||
[RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
|
||||
type: "string"
|
||||
format: "dateTime"
|
||||
example: "2020-01-04T10:45:21.364524523Z"
|
||||
ExitCode:
|
||||
description: |
|
||||
ExitCode meanings:
|
||||
|
||||
- `0` healthy
|
||||
- `1` unhealthy
|
||||
- `2` reserved (considered unhealthy)
|
||||
- other values: error running probe
|
||||
type: "integer"
|
||||
example: 0
|
||||
Output:
|
||||
description: "Output from last check"
|
||||
type: "string"
|
||||
|
||||
HostConfig:
|
||||
description: "Container configuration that depends on the host we are running on"
|
||||
allOf:
|
||||
@@ -628,12 +693,44 @@ definitions:
|
||||
Binds:
|
||||
type: "array"
|
||||
description: |
|
||||
A list of volume bindings for this container. Each volume binding is a string in one of these forms:
|
||||
A list of volume bindings for this container. Each volume binding
|
||||
is a string in one of these forms:
|
||||
|
||||
- `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path.
|
||||
- `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path.
|
||||
- `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path.
|
||||
- `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path.
|
||||
- `host-src:container-dest[:options]` to bind-mount a host path
|
||||
into the container. Both `host-src`, and `container-dest` must
|
||||
be an _absolute_ path.
|
||||
- `volume-name:container-dest[:options]` to bind-mount a volume
|
||||
managed by a volume driver into the container. `container-dest`
|
||||
must be an _absolute_ path.
|
||||
|
||||
`options` is an optional, comma-delimited list of:
|
||||
|
||||
- `nocopy` disables automatic copying of data from the container
|
||||
path to the volume. The `nocopy` flag only applies to named volumes.
|
||||
- `[ro|rw]` mounts a volume read-only or read-write, respectively.
|
||||
If omitted or set to `rw`, volumes are mounted read-write.
|
||||
- `[z|Z]` applies SELinux labels to allow or deny multiple containers
|
||||
to read and write to the same volume.
|
||||
- `z`: a _shared_ content label is applied to the content. This
|
||||
label indicates that multiple containers can share the volume
|
||||
content, for both reading and writing.
|
||||
- `Z`: a _private unshared_ label is applied to the content.
|
||||
This label indicates that only the current container can use
|
||||
a private volume. Labeling systems such as SELinux require
|
||||
proper labels to be placed on volume content that is mounted
|
||||
into a container. Without a label, the security system can
|
||||
prevent a container's processes from using the content. By
|
||||
default, the labels set by the host operating system are not
|
||||
modified.
|
||||
- `[[r]shared|[r]slave|[r]private]` specifies mount
|
||||
[propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt).
|
||||
This only applies to bind-mounted volumes, not internal volumes
|
||||
or named volumes. Mount propagation requires the source mount
|
||||
point (the location where the source directory is mounted in the
|
||||
host operating system) to have the correct propagation properties.
|
||||
For shared volumes, the source mount point must be set to `shared`.
|
||||
For slave volumes, the mount must be set to either `shared` or
|
||||
`slave`.
|
||||
items:
|
||||
type: "string"
|
||||
ContainerIDFile:
|
||||
@@ -703,6 +800,19 @@ definitions:
|
||||
description: "A list of kernel capabilities to drop from the container. Conflicts with option 'Capabilities'"
|
||||
items:
|
||||
type: "string"
|
||||
CgroupnsMode:
|
||||
type: "string"
|
||||
enum:
|
||||
- "private"
|
||||
- "host"
|
||||
description: |
|
||||
cgroup namespace mode for the container. Possible values are:
|
||||
|
||||
- `"private"`: the container runs in its own private cgroup namespace
|
||||
- `"host"`: use the host system's cgroup namespace
|
||||
|
||||
If not specified, the daemon default is used, which can either be `"private"`
|
||||
or `"host"`, depending on daemon version, kernel support and configuration.
|
||||
Dns:
|
||||
type: "array"
|
||||
description: "A list of DNS servers for the container to use."
|
||||
@@ -2869,6 +2979,18 @@ definitions:
|
||||
type: "object"
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
# This option is not used by Windows containers
|
||||
Capabilities:
|
||||
type: "array"
|
||||
description: |
|
||||
A list of kernel capabilities to be available for container (this overrides the default set).
|
||||
items:
|
||||
type: "string"
|
||||
example:
|
||||
- "CAP_NET_RAW"
|
||||
- "CAP_SYS_ADMIN"
|
||||
- "CAP_SYS_CHROOT"
|
||||
- "CAP_SYSLOG"
|
||||
NetworkAttachmentSpec:
|
||||
description: |
|
||||
Read-only spec type for non-swarm containers attached to swarm overlay
|
||||
@@ -2970,16 +3092,10 @@ definitions:
|
||||
description: "Runtime is the type of runtime specified for the task executor."
|
||||
type: "string"
|
||||
Networks:
|
||||
description: "Specifies which networks the service should attach to."
|
||||
type: "array"
|
||||
items:
|
||||
type: "object"
|
||||
properties:
|
||||
Target:
|
||||
type: "string"
|
||||
Aliases:
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
$ref: "#/definitions/NetworkAttachmentConfig"
|
||||
LogDriver:
|
||||
description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified."
|
||||
type: "object"
|
||||
@@ -3067,6 +3183,12 @@ definitions:
|
||||
type: "integer"
|
||||
DesiredState:
|
||||
$ref: "#/definitions/TaskState"
|
||||
JobIteration:
|
||||
description: |
|
||||
If the Service this Task belongs to is a job-mode service, contains
|
||||
the JobIteration of the Service this Task was created for. Absent if
|
||||
the Task was created for a Replicated or Global Service.
|
||||
$ref: "#/definitions/ObjectVersion"
|
||||
example:
|
||||
ID: "0kzzo1i0y4jz6027t0k7aezc7"
|
||||
Version:
|
||||
@@ -3159,6 +3281,22 @@ definitions:
|
||||
format: "int64"
|
||||
Global:
|
||||
type: "object"
|
||||
ReplicatedJob:
|
||||
description: "The mode used for services with a finite number of tasks that run to a completed state."
|
||||
type: "object"
|
||||
properties:
|
||||
MaxConcurrent:
|
||||
description: "The maximum number of replicas to run simultaneously."
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
default: 1
|
||||
TotalCompletions:
|
||||
description: "The total number of replicas desired to reach the Completed state. If unset, will default to the value of MaxConcurrent"
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
GlobalJob:
|
||||
description: "The mode used for services which run a task to the completed state on each valid node."
|
||||
type: "object"
|
||||
UpdateConfig:
|
||||
description: "Specification for the update strategy of the service."
|
||||
type: "object"
|
||||
@@ -3225,17 +3363,11 @@ definitions:
|
||||
- "stop-first"
|
||||
- "start-first"
|
||||
Networks:
|
||||
description: "Array of network names or IDs to attach the service to."
|
||||
description: "Specifies which networks the service should attach to."
|
||||
type: "array"
|
||||
items:
|
||||
type: "object"
|
||||
properties:
|
||||
Target:
|
||||
type: "string"
|
||||
Aliases:
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
$ref: "#/definitions/NetworkAttachmentConfig"
|
||||
|
||||
EndpointSpec:
|
||||
$ref: "#/definitions/EndpointSpec"
|
||||
|
||||
@@ -3262,7 +3394,7 @@ definitions:
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
- "ingress" makes the target port accessible on on every node,
|
||||
- "ingress" makes the target port accessible on every node,
|
||||
regardless of whether there is a task for the service running on
|
||||
that node or not.
|
||||
- "host" bypasses the routing mesh and publish the port directly on
|
||||
@@ -3280,8 +3412,8 @@ definitions:
|
||||
type: "object"
|
||||
properties:
|
||||
Mode:
|
||||
description: "The mode of resolution to use for internal load balancing
|
||||
between tasks."
|
||||
description: |
|
||||
The mode of resolution to use for internal load balancing between tasks.
|
||||
type: "string"
|
||||
enum:
|
||||
- "vip"
|
||||
@@ -3344,6 +3476,58 @@ definitions:
|
||||
format: "dateTime"
|
||||
Message:
|
||||
type: "string"
|
||||
ServiceStatus:
|
||||
description: |
|
||||
The status of the service's tasks. Provided only when requested as
|
||||
part of a ServiceList operation.
|
||||
type: "object"
|
||||
properties:
|
||||
RunningTasks:
|
||||
description: "The number of tasks for the service currently in the Running state"
|
||||
type: "integer"
|
||||
format: "uint64"
|
||||
example: 7
|
||||
DesiredTasks:
|
||||
description: |
|
||||
The number of tasks for the service desired to be running.
|
||||
For replicated services, this is the replica count from the
|
||||
service spec. For global services, this is computed by taking
|
||||
count of all tasks for the service with a Desired State other
|
||||
than Shutdown.
|
||||
type: "integer"
|
||||
format: "uint64"
|
||||
example: 10
|
||||
CompletedTasks:
|
||||
description: |
|
||||
The number of tasks for a job that are in the Completed state.
|
||||
This field must be cross-referenced with the service type, as the
|
||||
value of 0 may mean the service is not in a job mode, or it may
|
||||
mean the job-mode service has no tasks yet Completed.
|
||||
type: "integer"
|
||||
format: "uint64"
|
||||
JobStatus:
|
||||
description: |
|
||||
The status of the service when it is in one of ReplicatedJob or
|
||||
GlobalJob modes. Absent on Replicated and Global mode services. The
|
||||
JobIteration is an ObjectVersion, but unlike the Service's version,
|
||||
does not need to be sent with an update request.
|
||||
type: "object"
|
||||
properties:
|
||||
JobIteration:
|
||||
description: |
|
||||
JobIteration is a value increased each time a Job is executed,
|
||||
successfully or otherwise. "Executed", in this case, means the
|
||||
job as a whole has been started, not that an individual Task has
|
||||
been launched. A job is "Executed" when its ServiceSpec is
|
||||
updated. JobIteration can be used to disambiguate Tasks belonging
|
||||
to different executions of a job. Though JobIteration will
|
||||
increase with each subsequent execution, it may not necessarily
|
||||
increase by 1, and so JobIteration should not be used to
|
||||
$ref: "#/definitions/ObjectVersion"
|
||||
LastExecution:
|
||||
description: "The last time, as observed by the server, that this job was started"
|
||||
type: "string"
|
||||
format: "dateTime"
|
||||
example:
|
||||
ID: "9mnpnzenvg8p8tdbtq4wvbkcz"
|
||||
Version:
|
||||
@@ -3609,6 +3793,70 @@ definitions:
|
||||
Spec:
|
||||
$ref: "#/definitions/ConfigSpec"
|
||||
|
||||
ContainerState:
|
||||
description: |
|
||||
ContainerState stores container's running state. It's part of ContainerJSONBase
|
||||
and will be returned by the "inspect" command.
|
||||
type: "object"
|
||||
properties:
|
||||
Status:
|
||||
description: |
|
||||
String representation of the container state. Can be one of "created",
|
||||
"running", "paused", "restarting", "removing", "exited", or "dead".
|
||||
type: "string"
|
||||
enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"]
|
||||
example: "running"
|
||||
Running:
|
||||
description: |
|
||||
Whether this container is running.
|
||||
|
||||
Note that a running container can be _paused_. The `Running` and `Paused`
|
||||
booleans are not mutually exclusive:
|
||||
|
||||
When pausing a container (on Linux), the freezer cgroup is used to suspend
|
||||
all processes in the container. Freezing the process requires the process to
|
||||
be running. As a result, paused containers are both `Running` _and_ `Paused`.
|
||||
|
||||
Use the `Status` field instead to determine if a container's state is "running".
|
||||
type: "boolean"
|
||||
example: true
|
||||
Paused:
|
||||
description: "Whether this container is paused."
|
||||
type: "boolean"
|
||||
example: false
|
||||
Restarting:
|
||||
description: "Whether this container is restarting."
|
||||
type: "boolean"
|
||||
example: false
|
||||
OOMKilled:
|
||||
description: "Whether this container has been killed because it ran out of memory."
|
||||
type: "boolean"
|
||||
example: false
|
||||
Dead:
|
||||
type: "boolean"
|
||||
example: false
|
||||
Pid:
|
||||
description: "The process ID of this container"
|
||||
type: "integer"
|
||||
example: 1234
|
||||
ExitCode:
|
||||
description: "The last exit code of this container"
|
||||
type: "integer"
|
||||
example: 0
|
||||
Error:
|
||||
type: "string"
|
||||
StartedAt:
|
||||
description: "The time when this container was last started."
|
||||
type: "string"
|
||||
example: "2020-01-06T09:06:59.461876391Z"
|
||||
FinishedAt:
|
||||
description: "The time when this container last exited."
|
||||
type: "string"
|
||||
example: "2020-01-06T09:07:59.461876391Z"
|
||||
Health:
|
||||
x-nullable: true
|
||||
$ref: "#/definitions/Health"
|
||||
|
||||
SystemInfo:
|
||||
type: "object"
|
||||
properties:
|
||||
@@ -3683,44 +3931,6 @@ definitions:
|
||||
on Windows.
|
||||
type: "string"
|
||||
example: "/var/lib/docker"
|
||||
SystemStatus:
|
||||
description: |
|
||||
Status information about this node (standalone Swarm API).
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: The information returned in this field is only propagated
|
||||
> by the Swarm standalone API, and is empty (`null`) when using
|
||||
> built-in swarm mode.
|
||||
type: "array"
|
||||
items:
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
example:
|
||||
- ["Role", "primary"]
|
||||
- ["State", "Healthy"]
|
||||
- ["Strategy", "spread"]
|
||||
- ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"]
|
||||
- ["Nodes", "2"]
|
||||
- [" swarm-agent-00", "192.168.99.102:2376"]
|
||||
- [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"]
|
||||
- [" └ Status", "Healthy"]
|
||||
- [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"]
|
||||
- [" └ Reserved CPUs", "0 / 1"]
|
||||
- [" └ Reserved Memory", "0 B / 1.021 GiB"]
|
||||
- [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"]
|
||||
- [" └ UpdatedAt", "2017-08-09T10:03:46Z"]
|
||||
- [" └ ServerVersion", "17.06.0-ce"]
|
||||
- [" swarm-manager", "192.168.99.101:2376"]
|
||||
- [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"]
|
||||
- [" └ Status", "Healthy"]
|
||||
- [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"]
|
||||
- [" └ Reserved CPUs", "0 / 1"]
|
||||
- [" └ Reserved Memory", "0 B / 1.021 GiB"]
|
||||
- [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"]
|
||||
- [" └ UpdatedAt", "2017-08-09T10:04:11Z"]
|
||||
- [" └ ServerVersion", "17.06.0-ce"]
|
||||
Plugins:
|
||||
$ref: "#/definitions/PluginsInfo"
|
||||
MemoryLimit:
|
||||
@@ -3827,6 +4037,17 @@ definitions:
|
||||
or "Windows Server 2016 Datacenter"
|
||||
type: "string"
|
||||
example: "Alpine Linux v3.5"
|
||||
OSVersion:
|
||||
description: |
|
||||
Version of the host's operating system
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
> **Note**: The information returned in this field, including its
|
||||
> very existence, and the formatting of values, should not be considered
|
||||
> stable, and may change without notice.
|
||||
type: "string"
|
||||
example: "16.04"
|
||||
OSType:
|
||||
description: |
|
||||
Generic type of the operating system of the host, as returned by the
|
||||
@@ -4407,6 +4628,24 @@ definitions:
|
||||
IP address and ports at which this node can be reached.
|
||||
type: "string"
|
||||
|
||||
NetworkAttachmentConfig:
|
||||
description: "Specifies how a service should be attached to a particular network."
|
||||
type: "object"
|
||||
properties:
|
||||
Target:
|
||||
description: "The target network for attachment. Must be a network name or ID."
|
||||
type: "string"
|
||||
Aliases:
|
||||
description: "Discoverable alternate names for the service on this network."
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
DriverOpts:
|
||||
description: "Driver attachment options for the network target"
|
||||
type: "object"
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
paths:
|
||||
/containers/json:
|
||||
get:
|
||||
@@ -4822,52 +5061,8 @@ paths:
|
||||
items:
|
||||
type: "string"
|
||||
State:
|
||||
description: "The state of the container."
|
||||
type: "object"
|
||||
properties:
|
||||
Status:
|
||||
description: |
|
||||
The status of the container. For example, `"running"` or `"exited"`.
|
||||
type: "string"
|
||||
enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"]
|
||||
Running:
|
||||
description: |
|
||||
Whether this container is running.
|
||||
|
||||
Note that a running container can be _paused_. The `Running` and `Paused`
|
||||
booleans are not mutually exclusive:
|
||||
|
||||
When pausing a container (on Linux), the cgroups freezer is used to suspend
|
||||
all processes in the container. Freezing the process requires the process to
|
||||
be running. As a result, paused containers are both `Running` _and_ `Paused`.
|
||||
|
||||
Use the `Status` field instead to determine if a container's state is "running".
|
||||
type: "boolean"
|
||||
Paused:
|
||||
description: "Whether this container is paused."
|
||||
type: "boolean"
|
||||
Restarting:
|
||||
description: "Whether this container is restarting."
|
||||
type: "boolean"
|
||||
OOMKilled:
|
||||
description: "Whether this container has been killed because it ran out of memory."
|
||||
type: "boolean"
|
||||
Dead:
|
||||
type: "boolean"
|
||||
Pid:
|
||||
description: "The process ID of this container"
|
||||
type: "integer"
|
||||
ExitCode:
|
||||
description: "The last exit code of this container"
|
||||
type: "integer"
|
||||
Error:
|
||||
type: "string"
|
||||
StartedAt:
|
||||
description: "The time when this container was last started."
|
||||
type: "string"
|
||||
FinishedAt:
|
||||
description: "The time when this container last exited."
|
||||
type: "string"
|
||||
x-nullable: true
|
||||
$ref: "#/definitions/ContainerState"
|
||||
Image:
|
||||
description: "The container's image"
|
||||
type: "string"
|
||||
@@ -4879,15 +5074,14 @@ paths:
|
||||
type: "string"
|
||||
LogPath:
|
||||
type: "string"
|
||||
Node:
|
||||
description: "TODO"
|
||||
type: "object"
|
||||
Name:
|
||||
type: "string"
|
||||
RestartCount:
|
||||
type: "integer"
|
||||
Driver:
|
||||
type: "string"
|
||||
Platform:
|
||||
type: "string"
|
||||
MountLabel:
|
||||
type: "string"
|
||||
ProcessLabel:
|
||||
@@ -4937,6 +5131,8 @@ paths:
|
||||
Domainname: ""
|
||||
Env:
|
||||
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
Healthcheck:
|
||||
Test: ["CMD-SHELL", "exit 0"]
|
||||
Hostname: "ba033ac44011"
|
||||
Image: "ubuntu"
|
||||
Labels:
|
||||
@@ -5048,6 +5244,14 @@ paths:
|
||||
Error: ""
|
||||
ExitCode: 9
|
||||
FinishedAt: "2015-01-06T15:47:32.080254511Z"
|
||||
Health:
|
||||
Status: "healthy"
|
||||
FailingStreak: 0
|
||||
Log:
|
||||
- Start: "2019-12-22T10:59:05.6385933Z"
|
||||
End: "2019-12-22T10:59:05.8078452Z"
|
||||
ExitCode: 0
|
||||
Output: ""
|
||||
OOMKilled: false
|
||||
Dead: false
|
||||
Paused: false
|
||||
@@ -5507,8 +5711,6 @@ paths:
|
||||
description: "no error"
|
||||
304:
|
||||
description: "container already started"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
404:
|
||||
description: "no such container"
|
||||
schema:
|
||||
@@ -5540,8 +5742,6 @@ paths:
|
||||
description: "no error"
|
||||
304:
|
||||
description: "container already stopped"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
404:
|
||||
description: "no such container"
|
||||
schema:
|
||||
@@ -5732,9 +5932,9 @@ paths:
|
||||
post:
|
||||
summary: "Pause a container"
|
||||
description: |
|
||||
Use the cgroups freezer to suspend all processes in a container.
|
||||
Use the freezer cgroup to suspend all processes in a container.
|
||||
|
||||
Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed.
|
||||
Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the freezer cgroup the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed.
|
||||
operationId: "ContainerPause"
|
||||
responses:
|
||||
204:
|
||||
@@ -6457,10 +6657,11 @@ paths:
|
||||
type: "string"
|
||||
- name: "networkmode"
|
||||
in: "query"
|
||||
description: "Sets the networking mode for the run commands during
|
||||
build. Supported standard values are: `bridge`, `host`, `none`, and
|
||||
`container:<name|id>`. Any other value is taken as a custom network's
|
||||
name to which this container should connect to."
|
||||
description: |
|
||||
Sets the networking mode for the run commands during build. Supported
|
||||
standard values are: `bridge`, `host`, `none`, and `container:<name|id>`.
|
||||
Any other value is taken as a custom network's name or ID to which this
|
||||
container should connect to.
|
||||
type: "string"
|
||||
- name: "Content-type"
|
||||
in: "header"
|
||||
@@ -6605,6 +6806,10 @@ paths:
|
||||
in: "query"
|
||||
description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled."
|
||||
type: "string"
|
||||
- name: "message"
|
||||
in: "query"
|
||||
description: "Set commit message for imported image."
|
||||
type: "string"
|
||||
- name: "inputImage"
|
||||
in: "body"
|
||||
description: "Image content if the value `-` has been specified in fromSrc query parameter"
|
||||
@@ -9283,6 +9488,10 @@ paths:
|
||||
- `label=<service label>`
|
||||
- `mode=["replicated"|"global"]`
|
||||
- `name=<service name>`
|
||||
- name: "status"
|
||||
in: "query"
|
||||
type: "boolean"
|
||||
description: "Include service status, with count of running and desired tasks"
|
||||
tags: ["Service"]
|
||||
/services/create:
|
||||
post:
|
||||
@@ -9549,17 +9758,19 @@ paths:
|
||||
type: "integer"
|
||||
- name: "registryAuthFrom"
|
||||
in: "query"
|
||||
description: |
|
||||
If the `X-Registry-Auth` header is not specified, this parameter
|
||||
indicates where to find registry authorization credentials.
|
||||
type: "string"
|
||||
description: "If the X-Registry-Auth header is not specified, this
|
||||
parameter indicates where to find registry authorization credentials. The
|
||||
valid values are `spec` and `previous-spec`."
|
||||
enum: ["spec", "previous-spec"]
|
||||
default: "spec"
|
||||
- name: "rollback"
|
||||
in: "query"
|
||||
description: |
|
||||
Set to this parameter to `previous` to cause a server-side rollback
|
||||
to the previous service spec. The supplied spec will be ignored in
|
||||
this case.
|
||||
type: "string"
|
||||
description: "Set to this parameter to `previous` to cause a
|
||||
server-side rollback to the previous service spec. The supplied spec will be
|
||||
ignored in this case."
|
||||
- name: "X-Registry-Auth"
|
||||
in: "header"
|
||||
description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)"
|
||||
@@ -10379,9 +10590,6 @@ paths:
|
||||
description: |
|
||||
Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities.
|
||||
|
||||
> **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental
|
||||
> features enabled. The specifications for this endpoint may still change in a future version of the API.
|
||||
|
||||
### Hijacking
|
||||
|
||||
This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection.
|
||||
@@ -10415,4 +10623,4 @@ paths:
|
||||
description: "server error"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
tags: ["Session (experimental)"]
|
||||
tags: ["Session"]
|
||||
|
||||
8
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
8
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
@@ -205,7 +205,7 @@ const (
|
||||
// BuilderV1 is the first generation builder in docker daemon
|
||||
BuilderV1 BuilderVersion = "1"
|
||||
// BuilderBuildKit is builder based on moby/buildkit project
|
||||
BuilderBuildKit = "2"
|
||||
BuilderBuildKit BuilderVersion = "2"
|
||||
)
|
||||
|
||||
// ImageBuildResponse holds information
|
||||
@@ -265,7 +265,7 @@ type ImagePullOptions struct {
|
||||
// if the privilege request fails.
|
||||
type RequestPrivilegeFunc func() (string, error)
|
||||
|
||||
//ImagePushOptions holds information to push images.
|
||||
// ImagePushOptions holds information to push images.
|
||||
type ImagePushOptions ImagePullOptions
|
||||
|
||||
// ImageRemoveOptions holds parameters to remove images.
|
||||
@@ -363,6 +363,10 @@ type ServiceUpdateOptions struct {
|
||||
// ServiceListOptions holds parameters to list services with.
|
||||
type ServiceListOptions struct {
|
||||
Filters filters.Args
|
||||
|
||||
// Status indicates whether the server should include the service task
|
||||
// count of running and desired tasks.
|
||||
Status bool
|
||||
}
|
||||
|
||||
// ServiceInspectOptions holds parameters related to the "service inspect"
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
34
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
34
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
@@ -7,9 +7,32 @@ import (
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// CgroupnsMode represents the cgroup namespace mode of the container
|
||||
type CgroupnsMode string
|
||||
|
||||
// IsPrivate indicates whether the container uses its own private cgroup namespace
|
||||
func (c CgroupnsMode) IsPrivate() bool {
|
||||
return c == "private"
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container shares the host's cgroup namespace
|
||||
func (c CgroupnsMode) IsHost() bool {
|
||||
return c == "host"
|
||||
}
|
||||
|
||||
// IsEmpty indicates whether the container cgroup namespace mode is unset
|
||||
func (c CgroupnsMode) IsEmpty() bool {
|
||||
return c == ""
|
||||
}
|
||||
|
||||
// Valid indicates whether the cgroup namespace mode is valid
|
||||
func (c CgroupnsMode) Valid() bool {
|
||||
return c.IsEmpty() || c.IsPrivate() || c.IsHost()
|
||||
}
|
||||
|
||||
// Isolation represents the isolation technology of a container. The supported
|
||||
// values are platform specific
|
||||
type Isolation string
|
||||
@@ -122,7 +145,7 @@ func (n NetworkMode) ConnectedContainer() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
//UserDefined indicates user-created network
|
||||
// UserDefined indicates user-created network
|
||||
func (n NetworkMode) UserDefined() string {
|
||||
if n.IsUserDefined() {
|
||||
return string(n)
|
||||
@@ -381,9 +404,10 @@ type HostConfig struct {
|
||||
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
|
||||
Capabilities []string `json:"Capabilities"` // List of kernel capabilities to be available for container (this overrides the default set)
|
||||
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||
CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container
|
||||
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||
ExtraHosts []string // List of extra hosts
|
||||
GroupAdd []string // List of additional groups that the container process will run as
|
||||
IpcMode IpcMode // IPC namespace to use for the container
|
||||
|
||||
6
vendor/github.com/docker/docker/api/types/error_response_ext.go
generated
vendored
Normal file
6
vendor/github.com/docker/docker/api/types/error_response_ext.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package types
|
||||
|
||||
// Error returns the error message
|
||||
func (e ErrorResponse) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
17
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
17
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
@@ -36,6 +36,15 @@ func NewArgs(initialArgs ...KeyValuePair) Args {
|
||||
return args
|
||||
}
|
||||
|
||||
// Keys returns all the keys in list of Args
|
||||
func (args Args) Keys() []string {
|
||||
keys := make([]string, 0, len(args.fields))
|
||||
for k := range args.fields {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte representation of the Args
|
||||
func (args Args) MarshalJSON() ([]byte, error) {
|
||||
if len(args.fields) == 0 {
|
||||
@@ -57,7 +66,7 @@ func ToJSON(a Args) (string, error) {
|
||||
// then the encoded format will use an older legacy format where the values are a
|
||||
// list of strings, instead of a set.
|
||||
//
|
||||
// Deprecated: Use ToJSON
|
||||
// Deprecated: do not use in any new code; use ToJSON instead
|
||||
func ToParamWithVersion(version string, a Args) (string, error) {
|
||||
if a.Len() == 0 {
|
||||
return "", nil
|
||||
@@ -145,7 +154,7 @@ func (args Args) Len() int {
|
||||
func (args Args) MatchKVList(key string, sources map[string]string) bool {
|
||||
fieldValues := args.fields[key]
|
||||
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
// do not filter if there is no filter set or cannot determine filter
|
||||
if len(fieldValues) == 0 {
|
||||
return true
|
||||
}
|
||||
@@ -191,7 +200,7 @@ func (args Args) Match(field, source string) bool {
|
||||
// ExactMatch returns true if the source matches exactly one of the values.
|
||||
func (args Args) ExactMatch(key, source string) bool {
|
||||
fieldValues, ok := args.fields[key]
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
// do not filter if there is no filter set or cannot determine filter
|
||||
if !ok || len(fieldValues) == 0 {
|
||||
return true
|
||||
}
|
||||
@@ -204,7 +213,7 @@ func (args Args) ExactMatch(key, source string) bool {
|
||||
// matches exactly the value.
|
||||
func (args Args) UniqueExactMatch(key, source string) bool {
|
||||
fieldValues := args.fields[key]
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
// do not filter if there is no filter set or cannot determine filter
|
||||
if len(fieldValues) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/image/image_history.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/image/image_history.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package image // import "github.com/docker/docker/api/types/image"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
2
vendor/github.com/docker/docker/api/types/network/network.go
generated
vendored
2
vendor/github.com/docker/docker/api/types/network/network.go
generated
vendored
@@ -13,7 +13,7 @@ type Address struct {
|
||||
// IPAM represents IP Address Management
|
||||
type IPAM struct {
|
||||
Driver string
|
||||
Options map[string]string //Per network IPAM driver options
|
||||
Options map[string]string // Per network IPAM driver options
|
||||
Config []IPAMConfig
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/docker/docker/api/types/registry/registry.go
generated
vendored
2
vendor/github.com/docker/docker/api/types/registry/registry.go
generated
vendored
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ServiceConfig stores daemon registry services configuration.
|
||||
|
||||
13
vendor/github.com/docker/docker/api/types/swarm/container.go
generated
vendored
13
vendor/github.com/docker/docker/api/types/swarm/container.go
generated
vendored
@@ -67,10 +67,11 @@ type ContainerSpec struct {
|
||||
// The format of extra hosts on swarmkit is specified in:
|
||||
// http://man7.org/linux/man-pages/man5/hosts.5.html
|
||||
// IP_address canonical_hostname [aliases...]
|
||||
Hosts []string `json:",omitempty"`
|
||||
DNSConfig *DNSConfig `json:",omitempty"`
|
||||
Secrets []*SecretReference `json:",omitempty"`
|
||||
Configs []*ConfigReference `json:",omitempty"`
|
||||
Isolation container.Isolation `json:",omitempty"`
|
||||
Sysctls map[string]string `json:",omitempty"`
|
||||
Hosts []string `json:",omitempty"`
|
||||
DNSConfig *DNSConfig `json:",omitempty"`
|
||||
Secrets []*SecretReference `json:",omitempty"`
|
||||
Configs []*ConfigReference `json:",omitempty"`
|
||||
Isolation container.Isolation `json:",omitempty"`
|
||||
Sysctls map[string]string `json:",omitempty"`
|
||||
Capabilities []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
110
vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.pb.go
generated
vendored
110
vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.pb.go
generated
vendored
@@ -1,6 +1,5 @@
|
||||
// Code generated by protoc-gen-gogo.
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: plugin.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package runtime is a generated protocol buffer package.
|
||||
@@ -38,6 +37,7 @@ type PluginSpec struct {
|
||||
Remote string `protobuf:"bytes,2,opt,name=remote,proto3" json:"remote,omitempty"`
|
||||
Privileges []*PluginPrivilege `protobuf:"bytes,3,rep,name=privileges" json:"privileges,omitempty"`
|
||||
Disabled bool `protobuf:"varint,4,opt,name=disabled,proto3" json:"disabled,omitempty"`
|
||||
Env []string `protobuf:"bytes,5,rep,name=env" json:"env,omitempty"`
|
||||
}
|
||||
|
||||
func (m *PluginSpec) Reset() { *m = PluginSpec{} }
|
||||
@@ -73,6 +73,13 @@ func (m *PluginSpec) GetDisabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *PluginSpec) GetEnv() []string {
|
||||
if m != nil {
|
||||
return m.Env
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PluginPrivilege describes a permission the user has to accept
|
||||
// upon installing a plugin.
|
||||
type PluginPrivilege struct {
|
||||
@@ -160,6 +167,21 @@ func (m *PluginSpec) MarshalTo(dAtA []byte) (int, error) {
|
||||
}
|
||||
i++
|
||||
}
|
||||
if len(m.Env) > 0 {
|
||||
for _, s := range m.Env {
|
||||
dAtA[i] = 0x2a
|
||||
i++
|
||||
l = len(s)
|
||||
for l >= 1<<7 {
|
||||
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
|
||||
l >>= 7
|
||||
i++
|
||||
}
|
||||
dAtA[i] = uint8(l)
|
||||
i++
|
||||
i += copy(dAtA[i:], s)
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -208,24 +230,6 @@ func (m *PluginPrivilege) MarshalTo(dAtA []byte) (int, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeFixed64Plugin(dAtA []byte, offset int, v uint64) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
dAtA[offset+4] = uint8(v >> 32)
|
||||
dAtA[offset+5] = uint8(v >> 40)
|
||||
dAtA[offset+6] = uint8(v >> 48)
|
||||
dAtA[offset+7] = uint8(v >> 56)
|
||||
return offset + 8
|
||||
}
|
||||
func encodeFixed32Plugin(dAtA []byte, offset int, v uint32) int {
|
||||
dAtA[offset] = uint8(v)
|
||||
dAtA[offset+1] = uint8(v >> 8)
|
||||
dAtA[offset+2] = uint8(v >> 16)
|
||||
dAtA[offset+3] = uint8(v >> 24)
|
||||
return offset + 4
|
||||
}
|
||||
func encodeVarintPlugin(dAtA []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
@@ -255,6 +259,12 @@ func (m *PluginSpec) Size() (n int) {
|
||||
if m.Disabled {
|
||||
n += 2
|
||||
}
|
||||
if len(m.Env) > 0 {
|
||||
for _, s := range m.Env {
|
||||
l = len(s)
|
||||
n += 1 + l + sovPlugin(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -429,6 +439,35 @@ func (m *PluginSpec) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.Disabled = bool(v != 0)
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowPlugin
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthPlugin
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Env = append(m.Env, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipPlugin(dAtA[iNdEx:])
|
||||
@@ -695,18 +734,21 @@ var (
|
||||
func init() { proto.RegisterFile("plugin.proto", fileDescriptorPlugin) }
|
||||
|
||||
var fileDescriptorPlugin = []byte{
|
||||
// 196 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0xc8, 0x29, 0x4d,
|
||||
0xcf, 0xcc, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x6a, 0x63, 0xe4, 0xe2, 0x0a, 0x00, 0x0b,
|
||||
0x04, 0x17, 0xa4, 0x26, 0x0b, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30,
|
||||
0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x62, 0x5c, 0x6c, 0x45, 0xa9, 0xb9, 0xf9, 0x25, 0xa9, 0x12,
|
||||
0x4c, 0x60, 0x51, 0x28, 0x4f, 0xc8, 0x80, 0x8b, 0xab, 0xa0, 0x28, 0xb3, 0x2c, 0x33, 0x27, 0x35,
|
||||
0x3d, 0xb5, 0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x40, 0x0f, 0x62, 0x58, 0x00, 0x4c,
|
||||
0x22, 0x08, 0x49, 0x8d, 0x90, 0x14, 0x17, 0x47, 0x4a, 0x66, 0x71, 0x62, 0x52, 0x4e, 0x6a, 0x8a,
|
||||
0x04, 0x8b, 0x02, 0xa3, 0x06, 0x47, 0x10, 0x9c, 0xaf, 0x14, 0xcb, 0xc5, 0x8f, 0xa6, 0x15, 0xab,
|
||||
0x63, 0x14, 0xb8, 0xb8, 0x53, 0x52, 0x8b, 0x93, 0x8b, 0x32, 0x0b, 0x4a, 0x32, 0xf3, 0xf3, 0xa0,
|
||||
0x2e, 0x42, 0x16, 0x12, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, 0x05, 0xbb, 0x88, 0x33,
|
||||
0x08, 0xc2, 0x71, 0xe2, 0x39, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4,
|
||||
0x18, 0x93, 0xd8, 0xc0, 0x9e, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x84, 0xad, 0x79,
|
||||
0x0c, 0x01, 0x00, 0x00,
|
||||
// 256 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x4d, 0x4b, 0xc3, 0x30,
|
||||
0x18, 0xc7, 0x89, 0xdd, 0xc6, 0xfa, 0x4c, 0x70, 0x04, 0x91, 0xe2, 0xa1, 0x94, 0x9d, 0x7a, 0x6a,
|
||||
0x45, 0x2f, 0x82, 0x37, 0x0f, 0x9e, 0x47, 0xbc, 0x09, 0x1e, 0xd2, 0xf6, 0xa1, 0x06, 0x9b, 0x17,
|
||||
0x92, 0xb4, 0xe2, 0x37, 0xf1, 0x23, 0x79, 0xf4, 0x23, 0x48, 0x3f, 0x89, 0x98, 0x75, 0x32, 0x64,
|
||||
0xa7, 0xff, 0x4b, 0xc2, 0x9f, 0x1f, 0x0f, 0x9c, 0x9a, 0xae, 0x6f, 0x85, 0x2a, 0x8c, 0xd5, 0x5e,
|
||||
0x6f, 0x3e, 0x08, 0xc0, 0x36, 0x14, 0x8f, 0x06, 0x6b, 0x4a, 0x61, 0xa6, 0xb8, 0xc4, 0x84, 0x64,
|
||||
0x24, 0x8f, 0x59, 0xf0, 0xf4, 0x02, 0x16, 0x16, 0xa5, 0xf6, 0x98, 0x9c, 0x84, 0x76, 0x4a, 0xf4,
|
||||
0x0a, 0xc0, 0x58, 0x31, 0x88, 0x0e, 0x5b, 0x74, 0x49, 0x94, 0x45, 0xf9, 0xea, 0x7a, 0x5d, 0xec,
|
||||
0xc6, 0xb6, 0xfb, 0x07, 0x76, 0xf0, 0x87, 0x5e, 0xc2, 0xb2, 0x11, 0x8e, 0x57, 0x1d, 0x36, 0xc9,
|
||||
0x2c, 0x23, 0xf9, 0x92, 0xfd, 0x65, 0xba, 0x86, 0x08, 0xd5, 0x90, 0xcc, 0xb3, 0x28, 0x8f, 0xd9,
|
||||
0xaf, 0xdd, 0x3c, 0xc3, 0xd9, 0xbf, 0xb1, 0xa3, 0x78, 0x19, 0xac, 0x1a, 0x74, 0xb5, 0x15, 0xc6,
|
||||
0x0b, 0xad, 0x26, 0xc6, 0xc3, 0x8a, 0x9e, 0xc3, 0x7c, 0xe0, 0x5d, 0x8f, 0x81, 0x31, 0x66, 0xbb,
|
||||
0x70, 0xff, 0xf0, 0x39, 0xa6, 0xe4, 0x6b, 0x4c, 0xc9, 0xf7, 0x98, 0x92, 0xa7, 0xdb, 0x56, 0xf8,
|
||||
0x97, 0xbe, 0x2a, 0x6a, 0x2d, 0xcb, 0x46, 0xd7, 0xaf, 0x68, 0xf7, 0xc2, 0x8d, 0x28, 0xfd, 0xbb,
|
||||
0x41, 0x57, 0xba, 0x37, 0x6e, 0x65, 0x69, 0x7b, 0xe5, 0x85, 0xc4, 0xbb, 0x49, 0xab, 0x45, 0x38,
|
||||
0xe4, 0xcd, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0xa8, 0xd9, 0x9b, 0x58, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
1
vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.proto
generated
vendored
1
vendor/github.com/docker/docker/api/types/swarm/runtime/plugin.proto
generated
vendored
@@ -9,6 +9,7 @@ message PluginSpec {
|
||||
string remote = 2;
|
||||
repeated PluginPrivilege privileges = 3;
|
||||
bool disabled = 4;
|
||||
repeated string env = 5;
|
||||
}
|
||||
|
||||
// PluginPrivilege describes a permission the user has to accept
|
||||
|
||||
82
vendor/github.com/docker/docker/api/types/swarm/service.go
generated
vendored
82
vendor/github.com/docker/docker/api/types/swarm/service.go
generated
vendored
@@ -10,6 +10,17 @@ type Service struct {
|
||||
PreviousSpec *ServiceSpec `json:",omitempty"`
|
||||
Endpoint Endpoint `json:",omitempty"`
|
||||
UpdateStatus *UpdateStatus `json:",omitempty"`
|
||||
|
||||
// ServiceStatus is an optional, extra field indicating the number of
|
||||
// desired and running tasks. It is provided primarily as a shortcut to
|
||||
// calculating these values client-side, which otherwise would require
|
||||
// listing all tasks for a service, an operation that could be
|
||||
// computation and network expensive.
|
||||
ServiceStatus *ServiceStatus `json:",omitempty"`
|
||||
|
||||
// JobStatus is the status of a Service which is in one of ReplicatedJob or
|
||||
// GlobalJob modes. It is absent on Replicated and Global services.
|
||||
JobStatus *JobStatus `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ServiceSpec represents the spec of a service.
|
||||
@@ -32,8 +43,10 @@ type ServiceSpec struct {
|
||||
|
||||
// ServiceMode represents the mode of a service.
|
||||
type ServiceMode struct {
|
||||
Replicated *ReplicatedService `json:",omitempty"`
|
||||
Global *GlobalService `json:",omitempty"`
|
||||
Replicated *ReplicatedService `json:",omitempty"`
|
||||
Global *GlobalService `json:",omitempty"`
|
||||
ReplicatedJob *ReplicatedJob `json:",omitempty"`
|
||||
GlobalJob *GlobalJob `json:",omitempty"`
|
||||
}
|
||||
|
||||
// UpdateState is the state of a service update.
|
||||
@@ -70,6 +83,32 @@ type ReplicatedService struct {
|
||||
// GlobalService is a kind of ServiceMode.
|
||||
type GlobalService struct{}
|
||||
|
||||
// ReplicatedJob is the a type of Service which executes a defined Tasks
|
||||
// in parallel until the specified number of Tasks have succeeded.
|
||||
type ReplicatedJob struct {
|
||||
// MaxConcurrent indicates the maximum number of Tasks that should be
|
||||
// executing simultaneously for this job at any given time. There may be
|
||||
// fewer Tasks that MaxConcurrent executing simultaneously; for example, if
|
||||
// there are fewer than MaxConcurrent tasks needed to reach
|
||||
// TotalCompletions.
|
||||
//
|
||||
// If this field is empty, it will default to a max concurrency of 1.
|
||||
MaxConcurrent *uint64 `json:",omitempty"`
|
||||
|
||||
// TotalCompletions is the total number of Tasks desired to run to
|
||||
// completion.
|
||||
//
|
||||
// If this field is empty, the value of MaxConcurrent will be used.
|
||||
TotalCompletions *uint64 `json:",omitempty"`
|
||||
}
|
||||
|
||||
// GlobalJob is the type of a Service which executes a Task on every Node
|
||||
// matching the Service's placement constraints. These tasks run to completion
|
||||
// and then exit.
|
||||
//
|
||||
// This type is deliberately empty.
|
||||
type GlobalJob struct{}
|
||||
|
||||
const (
|
||||
// UpdateFailureActionPause PAUSE
|
||||
UpdateFailureActionPause = "pause"
|
||||
@@ -122,3 +161,42 @@ type UpdateConfig struct {
|
||||
// started, or the new task is started before the old task is shut down.
|
||||
Order string
|
||||
}
|
||||
|
||||
// ServiceStatus represents the number of running tasks in a service and the
|
||||
// number of tasks desired to be running.
|
||||
type ServiceStatus struct {
|
||||
// RunningTasks is the number of tasks for the service actually in the
|
||||
// Running state
|
||||
RunningTasks uint64
|
||||
|
||||
// DesiredTasks is the number of tasks desired to be running by the
|
||||
// service. For replicated services, this is the replica count. For global
|
||||
// services, this is computed by taking the number of tasks with desired
|
||||
// state of not-Shutdown.
|
||||
DesiredTasks uint64
|
||||
|
||||
// CompletedTasks is the number of tasks in the state Completed, if this
|
||||
// service is in ReplicatedJob or GlobalJob mode. This field must be
|
||||
// cross-referenced with the service type, because the default value of 0
|
||||
// may mean that a service is not in a job mode, or it may mean that the
|
||||
// job has yet to complete any tasks.
|
||||
CompletedTasks uint64
|
||||
}
|
||||
|
||||
// JobStatus is the status of a job-type service.
|
||||
type JobStatus struct {
|
||||
// JobIteration is a value increased each time a Job is executed,
|
||||
// successfully or otherwise. "Executed", in this case, means the job as a
|
||||
// whole has been started, not that an individual Task has been launched. A
|
||||
// job is "Executed" when its ServiceSpec is updated. JobIteration can be
|
||||
// used to disambiguate Tasks belonging to different executions of a job.
|
||||
//
|
||||
// Though JobIteration will increase with each subsequent execution, it may
|
||||
// not necessarily increase by 1, and so JobIteration should not be used to
|
||||
// keep track of the number of times a job has been executed.
|
||||
JobIteration Version
|
||||
|
||||
// LastExecution is the time that the job was last executed, as observed by
|
||||
// Swarm manager.
|
||||
LastExecution time.Time `json:",omitempty"`
|
||||
}
|
||||
|
||||
6
vendor/github.com/docker/docker/api/types/swarm/task.go
generated
vendored
6
vendor/github.com/docker/docker/api/types/swarm/task.go
generated
vendored
@@ -56,6 +56,12 @@ type Task struct {
|
||||
DesiredState TaskState `json:",omitempty"`
|
||||
NetworksAttachments []NetworkAttachment `json:",omitempty"`
|
||||
GenericResources []GenericResource `json:",omitempty"`
|
||||
|
||||
// JobIteration is the JobIteration of the Service that this Task was
|
||||
// spawned from, if the Service is a ReplicatedJob or GlobalJob. This is
|
||||
// used to determine which Tasks belong to which run of the job. This field
|
||||
// is absent if the Service mode is Replicated or Global.
|
||||
JobIteration *Version `json:",omitempty"`
|
||||
}
|
||||
|
||||
// TaskSpec represents the spec of a task.
|
||||
|
||||
8
vendor/github.com/docker/docker/api/types/types.go
generated
vendored
8
vendor/github.com/docker/docker/api/types/types.go
generated
vendored
@@ -39,6 +39,7 @@ type ImageInspect struct {
|
||||
Author string
|
||||
Config *container.Config
|
||||
Architecture string
|
||||
Variant string `json:",omitempty"`
|
||||
Os string
|
||||
OsVersion string `json:",omitempty"`
|
||||
Size int64
|
||||
@@ -153,7 +154,7 @@ type Info struct {
|
||||
Images int
|
||||
Driver string
|
||||
DriverStatus [][2]string
|
||||
SystemStatus [][2]string
|
||||
SystemStatus [][2]string `json:",omitempty"` // SystemStatus is only propagated by the Swarm standalone API
|
||||
Plugins PluginsInfo
|
||||
MemoryLimit bool
|
||||
SwapLimit bool
|
||||
@@ -177,6 +178,7 @@ type Info struct {
|
||||
NEventsListener int
|
||||
KernelVersion string
|
||||
OperatingSystem string
|
||||
OSVersion string
|
||||
OSType string
|
||||
Architecture string
|
||||
IndexServerAddress string
|
||||
@@ -316,7 +318,7 @@ type ContainerState struct {
|
||||
}
|
||||
|
||||
// ContainerNode stores information about the node that a container
|
||||
// is running on. It's only available in Docker Swarm
|
||||
// is running on. It's only used by the Docker Swarm standalone API
|
||||
type ContainerNode struct {
|
||||
ID string
|
||||
IPAddress string `json:"IP"`
|
||||
@@ -340,7 +342,7 @@ type ContainerJSONBase struct {
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
LogPath string
|
||||
Node *ContainerNode `json:",omitempty"`
|
||||
Node *ContainerNode `json:",omitempty"` // Node is only propagated by Docker Swarm standalone API
|
||||
Name string
|
||||
RestartCount int
|
||||
Driver string
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/volume/volume_create.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/volume/volume_create.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package volume // import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
3
vendor/github.com/docker/docker/api/types/volume/volume_list.go
generated
vendored
3
vendor/github.com/docker/docker/api/types/volume/volume_list.go
generated
vendored
@@ -1,8 +1,7 @@
|
||||
package volume // import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DO NOT EDIT THIS FILE
|
||||
// This file was generated by `swagger generate operation`
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
3
vendor/github.com/docker/docker/client/client.go
generated
vendored
3
vendor/github.com/docker/docker/client/client.go
generated
vendored
@@ -252,7 +252,8 @@ func (cli *Client) DaemonHost() string {
|
||||
|
||||
// HTTPClient returns a copy of the HTTP client bound to the server
|
||||
func (cli *Client) HTTPClient() *http.Client {
|
||||
return &*cli.client
|
||||
c := *cli.client
|
||||
return &c
|
||||
}
|
||||
|
||||
// ParseHostURL parses a url string, validates the string is a host url, and
|
||||
|
||||
2
vendor/github.com/docker/docker/client/client_unix.go
generated
vendored
2
vendor/github.com/docker/docker/client/client_unix.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// +build linux freebsd openbsd darwin
|
||||
// +build linux freebsd openbsd darwin solaris illumos
|
||||
|
||||
package client // import "github.com/docker/docker/client"
|
||||
|
||||
|
||||
1
vendor/github.com/docker/docker/client/container_list.go
generated
vendored
1
vendor/github.com/docker/docker/client/container_list.go
generated
vendored
@@ -35,6 +35,7 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis
|
||||
}
|
||||
|
||||
if options.Filters.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||
|
||||
if err != nil {
|
||||
|
||||
1
vendor/github.com/docker/docker/client/events.go
generated
vendored
1
vendor/github.com/docker/docker/client/events.go
generated
vendored
@@ -90,6 +90,7 @@ func buildEventsQueryParams(cliVersion string, options types.EventsOptions) (url
|
||||
}
|
||||
|
||||
if options.Filters.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cliVersion, options.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
6
vendor/github.com/docker/docker/client/hijack.go
generated
vendored
6
vendor/github.com/docker/docker/client/hijack.go
generated
vendored
@@ -24,7 +24,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
|
||||
}
|
||||
|
||||
apiPath := cli.getAPIPath(ctx, path, query)
|
||||
req, err := http.NewRequest("POST", apiPath, bodyEncoded)
|
||||
req, err := http.NewRequest(http.MethodPost, apiPath, bodyEncoded)
|
||||
if err != nil {
|
||||
return types.HijackedResponse{}, err
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
|
||||
|
||||
// DialHijack returns a hijacked connection with negotiated protocol proto.
|
||||
func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) {
|
||||
req, err := http.NewRequest("POST", url, nil)
|
||||
req, err := http.NewRequest(http.MethodPost, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -87,6 +87,8 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
|
||||
|
||||
// Server hijacks the connection, error 'connection closed' expected
|
||||
resp, err := clientconn.Do(req)
|
||||
|
||||
//nolint:staticcheck // ignore SA1019 for connecting to old (pre go1.8) daemons
|
||||
if err != httputil.ErrPersistEOF {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
2
vendor/github.com/docker/docker/client/image_import.go
generated
vendored
2
vendor/github.com/docker/docker/client/image_import.go
generated
vendored
@@ -14,7 +14,7 @@ import (
|
||||
// It returns the JSON content in the response body.
|
||||
func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
|
||||
if ref != "" {
|
||||
//Check if the given image name can be resolved
|
||||
// Check if the given image name can be resolved
|
||||
if _, err := reference.ParseNormalizedNamed(ref); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
1
vendor/github.com/docker/docker/client/image_list.go
generated
vendored
1
vendor/github.com/docker/docker/client/image_list.go
generated
vendored
@@ -24,6 +24,7 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions
|
||||
}
|
||||
}
|
||||
if optionFilters.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, optionFilters)
|
||||
if err != nil {
|
||||
return images, err
|
||||
|
||||
13
vendor/github.com/docker/docker/client/image_push.go
generated
vendored
13
vendor/github.com/docker/docker/client/image_push.go
generated
vendored
@@ -25,15 +25,14 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im
|
||||
return nil, errors.New("cannot push a digest reference")
|
||||
}
|
||||
|
||||
tag := ""
|
||||
name := reference.FamiliarName(ref)
|
||||
|
||||
if nameTaggedRef, isNamedTagged := ref.(reference.NamedTagged); isNamedTagged {
|
||||
tag = nameTaggedRef.Tag()
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("tag", tag)
|
||||
if !options.All {
|
||||
ref = reference.TagNameOnly(ref)
|
||||
if tagged, ok := ref.(reference.Tagged); ok {
|
||||
query.Set("tag", tagged.Tag())
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
|
||||
if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
|
||||
|
||||
1
vendor/github.com/docker/docker/client/network_list.go
generated
vendored
1
vendor/github.com/docker/docker/client/network_list.go
generated
vendored
@@ -13,6 +13,7 @@ import (
|
||||
func (cli *Client) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
||||
query := url.Values{}
|
||||
if options.Filters.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
6
vendor/github.com/docker/docker/client/ping.go
generated
vendored
6
vendor/github.com/docker/docker/client/ping.go
generated
vendored
@@ -17,9 +17,9 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
||||
var ping types.Ping
|
||||
|
||||
// Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest()
|
||||
// because ping requests are used during API version negotiation, so we want
|
||||
// because ping requests are used during API version negotiation, so we want
|
||||
// to hit the non-versioned /_ping endpoint, not /v1.xx/_ping
|
||||
req, err := cli.buildRequest("HEAD", path.Join(cli.basePath, "/_ping"), nil, nil)
|
||||
req, err := cli.buildRequest(http.MethodHead, path.Join(cli.basePath, "/_ping"), nil, nil)
|
||||
if err != nil {
|
||||
return ping, err
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
||||
return ping, err
|
||||
}
|
||||
|
||||
req, err = cli.buildRequest("GET", path.Join(cli.basePath, "/_ping"), nil, nil)
|
||||
req, err = cli.buildRequest(http.MethodGet, path.Join(cli.basePath, "/_ping"), nil, nil)
|
||||
if err != nil {
|
||||
return ping, err
|
||||
}
|
||||
|
||||
1
vendor/github.com/docker/docker/client/plugin_list.go
generated
vendored
1
vendor/github.com/docker/docker/client/plugin_list.go
generated
vendored
@@ -15,6 +15,7 @@ func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.P
|
||||
query := url.Values{}
|
||||
|
||||
if filter.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, filter)
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
|
||||
31
vendor/github.com/docker/docker/client/request.go
generated
vendored
31
vendor/github.com/docker/docker/client/request.go
generated
vendored
@@ -29,12 +29,12 @@ type serverResponse struct {
|
||||
|
||||
// head sends an http request to the docker API using the method HEAD.
|
||||
func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
|
||||
return cli.sendRequest(ctx, "HEAD", path, query, nil, headers)
|
||||
return cli.sendRequest(ctx, http.MethodHead, path, query, nil, headers)
|
||||
}
|
||||
|
||||
// get sends an http request to the docker API using the method GET with a specific Go context.
|
||||
func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
|
||||
return cli.sendRequest(ctx, "GET", path, query, nil, headers)
|
||||
return cli.sendRequest(ctx, http.MethodGet, path, query, nil, headers)
|
||||
}
|
||||
|
||||
// post sends an http request to the docker API using the method POST with a specific Go context.
|
||||
@@ -43,30 +43,21 @@ func (cli *Client) post(ctx context.Context, path string, query url.Values, obj
|
||||
if err != nil {
|
||||
return serverResponse{}, err
|
||||
}
|
||||
return cli.sendRequest(ctx, "POST", path, query, body, headers)
|
||||
return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers)
|
||||
}
|
||||
|
||||
func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
|
||||
return cli.sendRequest(ctx, "POST", path, query, body, headers)
|
||||
}
|
||||
|
||||
// put sends an http request to the docker API using the method PUT.
|
||||
func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
|
||||
body, headers, err := encodeBody(obj, headers)
|
||||
if err != nil {
|
||||
return serverResponse{}, err
|
||||
}
|
||||
return cli.sendRequest(ctx, "PUT", path, query, body, headers)
|
||||
return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers)
|
||||
}
|
||||
|
||||
// putRaw sends an http request to the docker API using the method PUT.
|
||||
func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
|
||||
return cli.sendRequest(ctx, "PUT", path, query, body, headers)
|
||||
return cli.sendRequest(ctx, http.MethodPut, path, query, body, headers)
|
||||
}
|
||||
|
||||
// delete sends an http request to the docker API using the method DELETE.
|
||||
func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
|
||||
return cli.sendRequest(ctx, "DELETE", path, query, nil, headers)
|
||||
return cli.sendRequest(ctx, http.MethodDelete, path, query, nil, headers)
|
||||
}
|
||||
|
||||
type headers map[string][]string
|
||||
@@ -88,7 +79,7 @@ func encodeBody(obj interface{}, headers headers) (io.Reader, headers, error) {
|
||||
}
|
||||
|
||||
func (cli *Client) buildRequest(method, path string, body io.Reader, headers headers) (*http.Request, error) {
|
||||
expectedPayload := (method == "POST" || method == "PUT")
|
||||
expectedPayload := (method == http.MethodPost || method == http.MethodPut)
|
||||
if expectedPayload && body == nil {
|
||||
body = bytes.NewReader([]byte{})
|
||||
}
|
||||
@@ -178,7 +169,13 @@ func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResp
|
||||
// this is localised - for example in French the error would be
|
||||
// `open //./pipe/docker_engine: Le fichier spécifié est introuvable.`
|
||||
if strings.Contains(err.Error(), `open //./pipe/docker_engine`) {
|
||||
err = errors.New(err.Error() + " In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.")
|
||||
// Checks if client is running with elevated privileges
|
||||
if f, elevatedErr := os.Open("\\\\.\\PHYSICALDRIVE0"); elevatedErr == nil {
|
||||
err = errors.Wrap(err, "In the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect.")
|
||||
} else {
|
||||
f.Close()
|
||||
err = errors.Wrap(err, "This error may indicate that the docker daemon is not running.")
|
||||
}
|
||||
}
|
||||
|
||||
return serverResp, errors.Wrap(err, "error during connect")
|
||||
|
||||
2
vendor/github.com/docker/docker/client/service_create.go
generated
vendored
2
vendor/github.com/docker/docker/client/service_create.go
generated
vendored
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/opencontainers/go-digest"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
4
vendor/github.com/docker/docker/client/service_list.go
generated
vendored
4
vendor/github.com/docker/docker/client/service_list.go
generated
vendored
@@ -23,6 +23,10 @@ func (cli *Client) ServiceList(ctx context.Context, options types.ServiceListOpt
|
||||
query.Set("filters", filterJSON)
|
||||
}
|
||||
|
||||
if options.Status {
|
||||
query.Set("status", "true")
|
||||
}
|
||||
|
||||
resp, err := cli.get(ctx, "/services", query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
|
||||
1
vendor/github.com/docker/docker/client/volume_list.go
generated
vendored
1
vendor/github.com/docker/docker/client/volume_list.go
generated
vendored
@@ -15,6 +15,7 @@ func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volumet
|
||||
query := url.Values{}
|
||||
|
||||
if filter.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, filter)
|
||||
if err != nil {
|
||||
return volumes, err
|
||||
|
||||
33
vendor/github.com/docker/docker/errdefs/http_helpers.go
generated
vendored
33
vendor/github.com/docker/docker/errdefs/http_helpers.go
generated
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
containerderrors "github.com/containerd/containerd/errdefs"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -47,6 +48,10 @@ func GetHTTPErrorStatusCode(err error) int {
|
||||
if statusCode != http.StatusInternalServerError {
|
||||
return statusCode
|
||||
}
|
||||
statusCode = statusCodeFromContainerdError(err)
|
||||
if statusCode != http.StatusInternalServerError {
|
||||
return statusCode
|
||||
}
|
||||
statusCode = statusCodeFromDistributionError(err)
|
||||
if statusCode != http.StatusInternalServerError {
|
||||
return statusCode
|
||||
@@ -136,9 +141,6 @@ func statusCodeFromGRPCError(err error) int {
|
||||
case codes.Unavailable: // code 14
|
||||
return http.StatusServiceUnavailable
|
||||
default:
|
||||
if e, ok := err.(causer); ok {
|
||||
return statusCodeFromGRPCError(e.Cause())
|
||||
}
|
||||
// codes.Canceled(1)
|
||||
// codes.Unknown(2)
|
||||
// codes.DeadlineExceeded(4)
|
||||
@@ -163,10 +165,27 @@ func statusCodeFromDistributionError(err error) int {
|
||||
}
|
||||
case errcode.ErrorCoder:
|
||||
return errs.ErrorCode().Descriptor().HTTPStatusCode
|
||||
default:
|
||||
if e, ok := err.(causer); ok {
|
||||
return statusCodeFromDistributionError(e.Cause())
|
||||
}
|
||||
}
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
// statusCodeFromContainerdError returns status code for containerd errors when
|
||||
// consumed directly (not through gRPC)
|
||||
func statusCodeFromContainerdError(err error) int {
|
||||
switch {
|
||||
case containerderrors.IsInvalidArgument(err):
|
||||
return http.StatusBadRequest
|
||||
case containerderrors.IsNotFound(err):
|
||||
return http.StatusNotFound
|
||||
case containerderrors.IsAlreadyExists(err):
|
||||
return http.StatusConflict
|
||||
case containerderrors.IsFailedPrecondition(err):
|
||||
return http.StatusPreconditionFailed
|
||||
case containerderrors.IsUnavailable(err):
|
||||
return http.StatusServiceUnavailable
|
||||
case containerderrors.IsNotImplemented(err):
|
||||
return http.StatusNotImplemented
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/docker/pkg/term/term_windows.go
generated
vendored
2
vendor/github.com/docker/docker/pkg/term/term_windows.go
generated
vendored
@@ -7,7 +7,7 @@ import (
|
||||
"syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE
|
||||
|
||||
"github.com/Azure/go-ansiterm/winterm"
|
||||
"github.com/docker/docker/pkg/term/windows"
|
||||
windowsconsole "github.com/docker/docker/pkg/term/windows"
|
||||
)
|
||||
|
||||
// State holds the console mode for the terminal.
|
||||
|
||||
3
vendor/github.com/docker/docker/pkg/term/windows/windows.go
generated
vendored
3
vendor/github.com/docker/docker/pkg/term/windows/windows.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
// +build windows
|
||||
// These files implement ANSI-aware input and output streams for use by the Docker Windows client.
|
||||
// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create
|
||||
// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls.
|
||||
@@ -9,7 +10,7 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/Azure/go-ansiterm"
|
||||
ansiterm "github.com/Azure/go-ansiterm"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
@@ -21,5 +21,4 @@ _testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
||||
*~
|
||||
*.prof
|
||||
5
vendor/github.com/exponent-io/jsonpath/.travis.yml
generated
vendored
Normal file
5
vendor/github.com/exponent-io/jsonpath/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.5
|
||||
- tip
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Koding, Inc.
|
||||
Copyright (c) 2015 Exponent Labs LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
66
vendor/github.com/exponent-io/jsonpath/README.md
generated
vendored
Normal file
66
vendor/github.com/exponent-io/jsonpath/README.md
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
[](https://godoc.org/github.com/exponent-io/jsonpath)
|
||||
[](https://travis-ci.org/exponent-io/jsonpath)
|
||||
|
||||
# jsonpath
|
||||
|
||||
This package extends the [json.Decoder](https://golang.org/pkg/encoding/json/#Decoder) to support navigating a stream of JSON tokens. You should be able to use this extended Decoder places where a json.Decoder would have been used.
|
||||
|
||||
This Decoder has the following enhancements...
|
||||
* The [Scan](https://godoc.org/github.com/exponent-io/jsonpath/#Decoder.Scan) method supports scanning a JSON stream while extracting particular values along the way using [PathActions](https://godoc.org/github.com/exponent-io/jsonpath#PathActions).
|
||||
* The [SeekTo](https://godoc.org/github.com/exponent-io/jsonpath#Decoder.SeekTo) method supports seeking forward in a JSON token stream to a particular path.
|
||||
* The [Path](https://godoc.org/github.com/exponent-io/jsonpath#Decoder.Path) method returns the path of the most recently parsed token.
|
||||
* The [Token](https://godoc.org/github.com/exponent-io/jsonpath#Decoder.Token) method has been modified to distinguish between strings that are object keys and strings that are values. Object key strings are returned as the [KeyString](https://godoc.org/github.com/exponent-io/jsonpath#KeyString) type rather than a native string.
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u github.com/exponent-io/jsonpath
|
||||
|
||||
## Example Usage
|
||||
|
||||
#### SeekTo
|
||||
|
||||
```go
|
||||
import "github.com/exponent-io/jsonpath"
|
||||
|
||||
var j = []byte(`[
|
||||
{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
|
||||
{"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}}
|
||||
]`)
|
||||
|
||||
w := json.NewDecoder(bytes.NewReader(j))
|
||||
var v interface{}
|
||||
|
||||
w.SeekTo(1, "Point", "G")
|
||||
w.Decode(&v) // v is 218
|
||||
```
|
||||
|
||||
#### Scan with PathActions
|
||||
|
||||
```go
|
||||
var j = []byte(`{"colors":[
|
||||
{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10, "A": 58}},
|
||||
{"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255, "A": 231}}
|
||||
]}`)
|
||||
|
||||
var actions PathActions
|
||||
|
||||
// Extract the value at Point.A
|
||||
actions.Add(func(d *Decoder) error {
|
||||
var alpha int
|
||||
err := d.Decode(&alpha)
|
||||
fmt.Printf("Alpha: %v\n", alpha)
|
||||
return err
|
||||
}, "Point", "A")
|
||||
|
||||
w := NewDecoder(bytes.NewReader(j))
|
||||
w.SeekTo("colors", 0)
|
||||
|
||||
var ok = true
|
||||
var err error
|
||||
for ok {
|
||||
ok, err = w.Scan(&actions)
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
210
vendor/github.com/exponent-io/jsonpath/decoder.go
generated
vendored
Normal file
210
vendor/github.com/exponent-io/jsonpath/decoder.go
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
package jsonpath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
// KeyString is returned from Decoder.Token to represent each key in a JSON object value.
|
||||
type KeyString string
|
||||
|
||||
// Decoder extends the Go runtime's encoding/json.Decoder to support navigating in a stream of JSON tokens.
|
||||
type Decoder struct {
|
||||
json.Decoder
|
||||
|
||||
path JsonPath
|
||||
context jsonContext
|
||||
}
|
||||
|
||||
// NewDecoder creates a new instance of the extended JSON Decoder.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
return &Decoder{Decoder: *json.NewDecoder(r)}
|
||||
}
|
||||
|
||||
// SeekTo causes the Decoder to move forward to a given path in the JSON structure.
|
||||
//
|
||||
// The path argument must consist of strings or integers. Each string specifies an JSON object key, and
|
||||
// each integer specifies an index into a JSON array.
|
||||
//
|
||||
// Consider the JSON structure
|
||||
//
|
||||
// { "a": [0,"s",12e4,{"b":0,"v":35} ] }
|
||||
//
|
||||
// SeekTo("a",3,"v") will move to the value referenced by the "a" key in the current object,
|
||||
// followed by a move to the 4th value (index 3) in the array, followed by a move to the value at key "v".
|
||||
// In this example, a subsequent call to the decoder's Decode() would unmarshal the value 35.
|
||||
//
|
||||
// SeekTo returns a boolean value indicating whether a match was found.
|
||||
//
|
||||
// Decoder is intended to be used with a stream of tokens. As a result it navigates forward only.
|
||||
func (d *Decoder) SeekTo(path ...interface{}) (bool, error) {
|
||||
|
||||
if len(path) == 0 {
|
||||
return len(d.path) == 0, nil
|
||||
}
|
||||
last := len(path) - 1
|
||||
if i, ok := path[last].(int); ok {
|
||||
path[last] = i - 1
|
||||
}
|
||||
|
||||
for {
|
||||
if d.path.Equal(path) {
|
||||
return true, nil
|
||||
}
|
||||
_, err := d.Token()
|
||||
if err == io.EOF {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v. This is
|
||||
// equivalent to encoding/json.Decode().
|
||||
func (d *Decoder) Decode(v interface{}) error {
|
||||
switch d.context {
|
||||
case objValue:
|
||||
d.context = objKey
|
||||
break
|
||||
case arrValue:
|
||||
d.path.incTop()
|
||||
break
|
||||
}
|
||||
return d.Decoder.Decode(v)
|
||||
}
|
||||
|
||||
// Path returns a slice of string and/or int values representing the path from the root of the JSON object to the
|
||||
// position of the most-recently parsed token.
|
||||
func (d *Decoder) Path() JsonPath {
|
||||
p := make(JsonPath, len(d.path))
|
||||
copy(p, d.path)
|
||||
return p
|
||||
}
|
||||
|
||||
// Token is equivalent to the Token() method on json.Decoder. The primary difference is that it distinguishes
|
||||
// between strings that are keys and and strings that are values. String tokens that are object keys are returned as a
|
||||
// KeyString rather than as a native string.
|
||||
func (d *Decoder) Token() (json.Token, error) {
|
||||
t, err := d.Decoder.Token()
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
switch d.context {
|
||||
case objValue:
|
||||
d.context = objKey
|
||||
break
|
||||
case arrValue:
|
||||
d.path.incTop()
|
||||
break
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
switch t := t.(type) {
|
||||
case json.Delim:
|
||||
switch t {
|
||||
case json.Delim('{'):
|
||||
if d.context == arrValue {
|
||||
d.path.incTop()
|
||||
}
|
||||
d.path.push("")
|
||||
d.context = objKey
|
||||
break
|
||||
case json.Delim('}'):
|
||||
d.path.pop()
|
||||
d.context = d.path.inferContext()
|
||||
break
|
||||
case json.Delim('['):
|
||||
if d.context == arrValue {
|
||||
d.path.incTop()
|
||||
}
|
||||
d.path.push(-1)
|
||||
d.context = arrValue
|
||||
break
|
||||
case json.Delim(']'):
|
||||
d.path.pop()
|
||||
d.context = d.path.inferContext()
|
||||
break
|
||||
}
|
||||
case float64, json.Number, bool:
|
||||
switch d.context {
|
||||
case objValue:
|
||||
d.context = objKey
|
||||
break
|
||||
case arrValue:
|
||||
d.path.incTop()
|
||||
break
|
||||
}
|
||||
break
|
||||
case string:
|
||||
switch d.context {
|
||||
case objKey:
|
||||
d.path.nameTop(t)
|
||||
d.context = objValue
|
||||
return KeyString(t), err
|
||||
case objValue:
|
||||
d.context = objKey
|
||||
case arrValue:
|
||||
d.path.incTop()
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return t, err
|
||||
}
|
||||
|
||||
// Scan moves forward over the JSON stream consuming all the tokens at the current level (current object, current array)
|
||||
// invoking each matching PathAction along the way.
|
||||
//
|
||||
// Scan returns true if there are more contiguous values to scan (for example in an array).
|
||||
func (d *Decoder) Scan(ext *PathActions) (bool, error) {
|
||||
|
||||
rootPath := d.Path()
|
||||
|
||||
// If this is an array path, increment the root path in our local copy.
|
||||
if rootPath.inferContext() == arrValue {
|
||||
rootPath.incTop()
|
||||
}
|
||||
|
||||
for {
|
||||
// advance the token position
|
||||
_, err := d.Token()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
match:
|
||||
var relPath JsonPath
|
||||
|
||||
// capture the new JSON path
|
||||
path := d.Path()
|
||||
|
||||
if len(path) > len(rootPath) {
|
||||
// capture the path relative to where the scan started
|
||||
relPath = path[len(rootPath):]
|
||||
} else {
|
||||
// if the path is not longer than the root, then we are done with this scan
|
||||
// return boolean flag indicating if there are more items to scan at the same level
|
||||
return d.Decoder.More(), nil
|
||||
}
|
||||
|
||||
// match the relative path against the path actions
|
||||
if node := ext.node.match(relPath); node != nil {
|
||||
if node.action != nil {
|
||||
// we have a match so execute the action
|
||||
err = node.action(d)
|
||||
if err != nil {
|
||||
return d.Decoder.More(), err
|
||||
}
|
||||
// The action may have advanced the decoder. If we are in an array, advancing it further would
|
||||
// skip tokens. So, if we are scanning an array, jump to the top without advancing the token.
|
||||
if d.path.inferContext() == arrValue && d.Decoder.More() {
|
||||
goto match
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
vendor/github.com/exponent-io/jsonpath/path.go
generated
vendored
Normal file
67
vendor/github.com/exponent-io/jsonpath/path.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Extends the Go runtime's json.Decoder enabling navigation of a stream of json tokens.
|
||||
package jsonpath
|
||||
|
||||
import "fmt"
|
||||
|
||||
type jsonContext int
|
||||
|
||||
const (
|
||||
none jsonContext = iota
|
||||
objKey
|
||||
objValue
|
||||
arrValue
|
||||
)
|
||||
|
||||
// AnyIndex can be used in a pattern to match any array index.
|
||||
const AnyIndex = -2
|
||||
|
||||
// JsonPath is a slice of strings and/or integers. Each string specifies an JSON object key, and
|
||||
// each integer specifies an index into a JSON array.
|
||||
type JsonPath []interface{}
|
||||
|
||||
func (p *JsonPath) push(n interface{}) { *p = append(*p, n) }
|
||||
func (p *JsonPath) pop() { *p = (*p)[:len(*p)-1] }
|
||||
|
||||
// increment the index at the top of the stack (must be an array index)
|
||||
func (p *JsonPath) incTop() { (*p)[len(*p)-1] = (*p)[len(*p)-1].(int) + 1 }
|
||||
|
||||
// name the key at the top of the stack (must be an object key)
|
||||
func (p *JsonPath) nameTop(n string) { (*p)[len(*p)-1] = n }
|
||||
|
||||
// infer the context from the item at the top of the stack
|
||||
func (p *JsonPath) inferContext() jsonContext {
|
||||
if len(*p) == 0 {
|
||||
return none
|
||||
}
|
||||
t := (*p)[len(*p)-1]
|
||||
switch t.(type) {
|
||||
case string:
|
||||
return objKey
|
||||
case int:
|
||||
return arrValue
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid stack type %T", t))
|
||||
}
|
||||
}
|
||||
|
||||
// Equal tests for equality between two JsonPath types.
|
||||
func (p *JsonPath) Equal(o JsonPath) bool {
|
||||
if len(*p) != len(o) {
|
||||
return false
|
||||
}
|
||||
for i, v := range *p {
|
||||
if v != o[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *JsonPath) HasPrefix(o JsonPath) bool {
|
||||
for i, v := range o {
|
||||
if v != (*p)[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
61
vendor/github.com/exponent-io/jsonpath/pathaction.go
generated
vendored
Normal file
61
vendor/github.com/exponent-io/jsonpath/pathaction.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package jsonpath
|
||||
|
||||
// pathNode is used to construct a trie of paths to be matched
|
||||
type pathNode struct {
|
||||
matchOn interface{} // string, or integer
|
||||
childNodes []pathNode
|
||||
action DecodeAction
|
||||
}
|
||||
|
||||
// match climbs the trie to find a node that matches the given JSON path.
|
||||
func (n *pathNode) match(path JsonPath) *pathNode {
|
||||
var node *pathNode = n
|
||||
for _, ps := range path {
|
||||
found := false
|
||||
for i, n := range node.childNodes {
|
||||
if n.matchOn == ps {
|
||||
node = &node.childNodes[i]
|
||||
found = true
|
||||
break
|
||||
} else if _, ok := ps.(int); ok && n.matchOn == AnyIndex {
|
||||
node = &node.childNodes[i]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// PathActions represents a collection of DecodeAction functions that should be called at certain path positions
|
||||
// when scanning the JSON stream. PathActions can be created once and used many times in one or more JSON streams.
|
||||
type PathActions struct {
|
||||
node pathNode
|
||||
}
|
||||
|
||||
// DecodeAction handlers are called by the Decoder when scanning objects. See PathActions.Add for more detail.
|
||||
type DecodeAction func(d *Decoder) error
|
||||
|
||||
// Add specifies an action to call on the Decoder when the specified path is encountered.
|
||||
func (je *PathActions) Add(action DecodeAction, path ...interface{}) {
|
||||
|
||||
var node *pathNode = &je.node
|
||||
for _, ps := range path {
|
||||
found := false
|
||||
for i, n := range node.childNodes {
|
||||
if n.matchOn == ps {
|
||||
node = &node.childNodes[i]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
node.childNodes = append(node.childNodes, pathNode{matchOn: ps})
|
||||
node = &node.childNodes[len(node.childNodes)-1]
|
||||
}
|
||||
}
|
||||
node.action = action
|
||||
}
|
||||
@@ -1,3 +1 @@
|
||||
language: go
|
||||
go: 1.x
|
||||
|
||||
0
vendor/github.com/golang/mock/LICENSE → vendor/github.com/google/btree/LICENSE
generated
vendored
0
vendor/github.com/golang/mock/LICENSE → vendor/github.com/google/btree/LICENSE
generated
vendored
12
vendor/github.com/google/btree/README.md
generated
vendored
Normal file
12
vendor/github.com/google/btree/README.md
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# BTree implementation for Go
|
||||
|
||||

|
||||
|
||||
This package provides an in-memory B-Tree implementation for Go, useful as
|
||||
an ordered, mutable data structure.
|
||||
|
||||
The API is based off of the wonderful
|
||||
http://godoc.org/github.com/petar/GoLLRB/llrb, and is meant to allow btree to
|
||||
act as a drop-in replacement for gollrb trees.
|
||||
|
||||
See http://godoc.org/github.com/google/btree for documentation.
|
||||
890
vendor/github.com/google/btree/btree.go
generated
vendored
Normal file
890
vendor/github.com/google/btree/btree.go
generated
vendored
Normal file
@@ -0,0 +1,890 @@
|
||||
// Copyright 2014 Google Inc.
|
||||
//
|
||||
// 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 btree implements in-memory B-Trees of arbitrary degree.
|
||||
//
|
||||
// btree implements an in-memory B-Tree for use as an ordered data structure.
|
||||
// It is not meant for persistent storage solutions.
|
||||
//
|
||||
// It has a flatter structure than an equivalent red-black or other binary tree,
|
||||
// which in some cases yields better memory usage and/or performance.
|
||||
// See some discussion on the matter here:
|
||||
// http://google-opensource.blogspot.com/2013/01/c-containers-that-save-memory-and-time.html
|
||||
// Note, though, that this project is in no way related to the C++ B-Tree
|
||||
// implementation written about there.
|
||||
//
|
||||
// Within this tree, each node contains a slice of items and a (possibly nil)
|
||||
// slice of children. For basic numeric values or raw structs, this can cause
|
||||
// efficiency differences when compared to equivalent C++ template code that
|
||||
// stores values in arrays within the node:
|
||||
// * Due to the overhead of storing values as interfaces (each
|
||||
// value needs to be stored as the value itself, then 2 words for the
|
||||
// interface pointing to that value and its type), resulting in higher
|
||||
// memory use.
|
||||
// * Since interfaces can point to values anywhere in memory, values are
|
||||
// most likely not stored in contiguous blocks, resulting in a higher
|
||||
// number of cache misses.
|
||||
// These issues don't tend to matter, though, when working with strings or other
|
||||
// heap-allocated structures, since C++-equivalent structures also must store
|
||||
// pointers and also distribute their values across the heap.
|
||||
//
|
||||
// This implementation is designed to be a drop-in replacement to gollrb.LLRB
|
||||
// trees, (http://github.com/petar/gollrb), an excellent and probably the most
|
||||
// widely used ordered tree implementation in the Go ecosystem currently.
|
||||
// Its functions, therefore, exactly mirror those of
|
||||
// llrb.LLRB where possible. Unlike gollrb, though, we currently don't
|
||||
// support storing multiple equivalent values.
|
||||
package btree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Item represents a single object in the tree.
|
||||
type Item interface {
|
||||
// Less tests whether the current item is less than the given argument.
|
||||
//
|
||||
// This must provide a strict weak ordering.
|
||||
// If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only
|
||||
// hold one of either a or b in the tree).
|
||||
Less(than Item) bool
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultFreeListSize = 32
|
||||
)
|
||||
|
||||
var (
|
||||
nilItems = make(items, 16)
|
||||
nilChildren = make(children, 16)
|
||||
)
|
||||
|
||||
// FreeList represents a free list of btree nodes. By default each
|
||||
// BTree has its own FreeList, but multiple BTrees can share the same
|
||||
// FreeList.
|
||||
// Two Btrees using the same freelist are safe for concurrent write access.
|
||||
type FreeList struct {
|
||||
mu sync.Mutex
|
||||
freelist []*node
|
||||
}
|
||||
|
||||
// NewFreeList creates a new free list.
|
||||
// size is the maximum size of the returned free list.
|
||||
func NewFreeList(size int) *FreeList {
|
||||
return &FreeList{freelist: make([]*node, 0, size)}
|
||||
}
|
||||
|
||||
func (f *FreeList) newNode() (n *node) {
|
||||
f.mu.Lock()
|
||||
index := len(f.freelist) - 1
|
||||
if index < 0 {
|
||||
f.mu.Unlock()
|
||||
return new(node)
|
||||
}
|
||||
n = f.freelist[index]
|
||||
f.freelist[index] = nil
|
||||
f.freelist = f.freelist[:index]
|
||||
f.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// freeNode adds the given node to the list, returning true if it was added
|
||||
// and false if it was discarded.
|
||||
func (f *FreeList) freeNode(n *node) (out bool) {
|
||||
f.mu.Lock()
|
||||
if len(f.freelist) < cap(f.freelist) {
|
||||
f.freelist = append(f.freelist, n)
|
||||
out = true
|
||||
}
|
||||
f.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// ItemIterator allows callers of Ascend* to iterate in-order over portions of
|
||||
// the tree. When this function returns false, iteration will stop and the
|
||||
// associated Ascend* function will immediately return.
|
||||
type ItemIterator func(i Item) bool
|
||||
|
||||
// New creates a new B-Tree with the given degree.
|
||||
//
|
||||
// New(2), for example, will create a 2-3-4 tree (each node contains 1-3 items
|
||||
// and 2-4 children).
|
||||
func New(degree int) *BTree {
|
||||
return NewWithFreeList(degree, NewFreeList(DefaultFreeListSize))
|
||||
}
|
||||
|
||||
// NewWithFreeList creates a new B-Tree that uses the given node free list.
|
||||
func NewWithFreeList(degree int, f *FreeList) *BTree {
|
||||
if degree <= 1 {
|
||||
panic("bad degree")
|
||||
}
|
||||
return &BTree{
|
||||
degree: degree,
|
||||
cow: ©OnWriteContext{freelist: f},
|
||||
}
|
||||
}
|
||||
|
||||
// items stores items in a node.
|
||||
type items []Item
|
||||
|
||||
// insertAt inserts a value into the given index, pushing all subsequent values
|
||||
// forward.
|
||||
func (s *items) insertAt(index int, item Item) {
|
||||
*s = append(*s, nil)
|
||||
if index < len(*s) {
|
||||
copy((*s)[index+1:], (*s)[index:])
|
||||
}
|
||||
(*s)[index] = item
|
||||
}
|
||||
|
||||
// removeAt removes a value at a given index, pulling all subsequent values
|
||||
// back.
|
||||
func (s *items) removeAt(index int) Item {
|
||||
item := (*s)[index]
|
||||
copy((*s)[index:], (*s)[index+1:])
|
||||
(*s)[len(*s)-1] = nil
|
||||
*s = (*s)[:len(*s)-1]
|
||||
return item
|
||||
}
|
||||
|
||||
// pop removes and returns the last element in the list.
|
||||
func (s *items) pop() (out Item) {
|
||||
index := len(*s) - 1
|
||||
out = (*s)[index]
|
||||
(*s)[index] = nil
|
||||
*s = (*s)[:index]
|
||||
return
|
||||
}
|
||||
|
||||
// truncate truncates this instance at index so that it contains only the
|
||||
// first index items. index must be less than or equal to length.
|
||||
func (s *items) truncate(index int) {
|
||||
var toClear items
|
||||
*s, toClear = (*s)[:index], (*s)[index:]
|
||||
for len(toClear) > 0 {
|
||||
toClear = toClear[copy(toClear, nilItems):]
|
||||
}
|
||||
}
|
||||
|
||||
// find returns the index where the given item should be inserted into this
|
||||
// list. 'found' is true if the item already exists in the list at the given
|
||||
// index.
|
||||
func (s items) find(item Item) (index int, found bool) {
|
||||
i := sort.Search(len(s), func(i int) bool {
|
||||
return item.Less(s[i])
|
||||
})
|
||||
if i > 0 && !s[i-1].Less(item) {
|
||||
return i - 1, true
|
||||
}
|
||||
return i, false
|
||||
}
|
||||
|
||||
// children stores child nodes in a node.
|
||||
type children []*node
|
||||
|
||||
// insertAt inserts a value into the given index, pushing all subsequent values
|
||||
// forward.
|
||||
func (s *children) insertAt(index int, n *node) {
|
||||
*s = append(*s, nil)
|
||||
if index < len(*s) {
|
||||
copy((*s)[index+1:], (*s)[index:])
|
||||
}
|
||||
(*s)[index] = n
|
||||
}
|
||||
|
||||
// removeAt removes a value at a given index, pulling all subsequent values
|
||||
// back.
|
||||
func (s *children) removeAt(index int) *node {
|
||||
n := (*s)[index]
|
||||
copy((*s)[index:], (*s)[index+1:])
|
||||
(*s)[len(*s)-1] = nil
|
||||
*s = (*s)[:len(*s)-1]
|
||||
return n
|
||||
}
|
||||
|
||||
// pop removes and returns the last element in the list.
|
||||
func (s *children) pop() (out *node) {
|
||||
index := len(*s) - 1
|
||||
out = (*s)[index]
|
||||
(*s)[index] = nil
|
||||
*s = (*s)[:index]
|
||||
return
|
||||
}
|
||||
|
||||
// truncate truncates this instance at index so that it contains only the
|
||||
// first index children. index must be less than or equal to length.
|
||||
func (s *children) truncate(index int) {
|
||||
var toClear children
|
||||
*s, toClear = (*s)[:index], (*s)[index:]
|
||||
for len(toClear) > 0 {
|
||||
toClear = toClear[copy(toClear, nilChildren):]
|
||||
}
|
||||
}
|
||||
|
||||
// node is an internal node in a tree.
|
||||
//
|
||||
// It must at all times maintain the invariant that either
|
||||
// * len(children) == 0, len(items) unconstrained
|
||||
// * len(children) == len(items) + 1
|
||||
type node struct {
|
||||
items items
|
||||
children children
|
||||
cow *copyOnWriteContext
|
||||
}
|
||||
|
||||
func (n *node) mutableFor(cow *copyOnWriteContext) *node {
|
||||
if n.cow == cow {
|
||||
return n
|
||||
}
|
||||
out := cow.newNode()
|
||||
if cap(out.items) >= len(n.items) {
|
||||
out.items = out.items[:len(n.items)]
|
||||
} else {
|
||||
out.items = make(items, len(n.items), cap(n.items))
|
||||
}
|
||||
copy(out.items, n.items)
|
||||
// Copy children
|
||||
if cap(out.children) >= len(n.children) {
|
||||
out.children = out.children[:len(n.children)]
|
||||
} else {
|
||||
out.children = make(children, len(n.children), cap(n.children))
|
||||
}
|
||||
copy(out.children, n.children)
|
||||
return out
|
||||
}
|
||||
|
||||
func (n *node) mutableChild(i int) *node {
|
||||
c := n.children[i].mutableFor(n.cow)
|
||||
n.children[i] = c
|
||||
return c
|
||||
}
|
||||
|
||||
// split splits the given node at the given index. The current node shrinks,
|
||||
// and this function returns the item that existed at that index and a new node
|
||||
// containing all items/children after it.
|
||||
func (n *node) split(i int) (Item, *node) {
|
||||
item := n.items[i]
|
||||
next := n.cow.newNode()
|
||||
next.items = append(next.items, n.items[i+1:]...)
|
||||
n.items.truncate(i)
|
||||
if len(n.children) > 0 {
|
||||
next.children = append(next.children, n.children[i+1:]...)
|
||||
n.children.truncate(i + 1)
|
||||
}
|
||||
return item, next
|
||||
}
|
||||
|
||||
// maybeSplitChild checks if a child should be split, and if so splits it.
|
||||
// Returns whether or not a split occurred.
|
||||
func (n *node) maybeSplitChild(i, maxItems int) bool {
|
||||
if len(n.children[i].items) < maxItems {
|
||||
return false
|
||||
}
|
||||
first := n.mutableChild(i)
|
||||
item, second := first.split(maxItems / 2)
|
||||
n.items.insertAt(i, item)
|
||||
n.children.insertAt(i+1, second)
|
||||
return true
|
||||
}
|
||||
|
||||
// insert inserts an item into the subtree rooted at this node, making sure
|
||||
// no nodes in the subtree exceed maxItems items. Should an equivalent item be
|
||||
// be found/replaced by insert, it will be returned.
|
||||
func (n *node) insert(item Item, maxItems int) Item {
|
||||
i, found := n.items.find(item)
|
||||
if found {
|
||||
out := n.items[i]
|
||||
n.items[i] = item
|
||||
return out
|
||||
}
|
||||
if len(n.children) == 0 {
|
||||
n.items.insertAt(i, item)
|
||||
return nil
|
||||
}
|
||||
if n.maybeSplitChild(i, maxItems) {
|
||||
inTree := n.items[i]
|
||||
switch {
|
||||
case item.Less(inTree):
|
||||
// no change, we want first split node
|
||||
case inTree.Less(item):
|
||||
i++ // we want second split node
|
||||
default:
|
||||
out := n.items[i]
|
||||
n.items[i] = item
|
||||
return out
|
||||
}
|
||||
}
|
||||
return n.mutableChild(i).insert(item, maxItems)
|
||||
}
|
||||
|
||||
// get finds the given key in the subtree and returns it.
|
||||
func (n *node) get(key Item) Item {
|
||||
i, found := n.items.find(key)
|
||||
if found {
|
||||
return n.items[i]
|
||||
} else if len(n.children) > 0 {
|
||||
return n.children[i].get(key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// min returns the first item in the subtree.
|
||||
func min(n *node) Item {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
for len(n.children) > 0 {
|
||||
n = n.children[0]
|
||||
}
|
||||
if len(n.items) == 0 {
|
||||
return nil
|
||||
}
|
||||
return n.items[0]
|
||||
}
|
||||
|
||||
// max returns the last item in the subtree.
|
||||
func max(n *node) Item {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
for len(n.children) > 0 {
|
||||
n = n.children[len(n.children)-1]
|
||||
}
|
||||
if len(n.items) == 0 {
|
||||
return nil
|
||||
}
|
||||
return n.items[len(n.items)-1]
|
||||
}
|
||||
|
||||
// toRemove details what item to remove in a node.remove call.
|
||||
type toRemove int
|
||||
|
||||
const (
|
||||
removeItem toRemove = iota // removes the given item
|
||||
removeMin // removes smallest item in the subtree
|
||||
removeMax // removes largest item in the subtree
|
||||
)
|
||||
|
||||
// remove removes an item from the subtree rooted at this node.
|
||||
func (n *node) remove(item Item, minItems int, typ toRemove) Item {
|
||||
var i int
|
||||
var found bool
|
||||
switch typ {
|
||||
case removeMax:
|
||||
if len(n.children) == 0 {
|
||||
return n.items.pop()
|
||||
}
|
||||
i = len(n.items)
|
||||
case removeMin:
|
||||
if len(n.children) == 0 {
|
||||
return n.items.removeAt(0)
|
||||
}
|
||||
i = 0
|
||||
case removeItem:
|
||||
i, found = n.items.find(item)
|
||||
if len(n.children) == 0 {
|
||||
if found {
|
||||
return n.items.removeAt(i)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
panic("invalid type")
|
||||
}
|
||||
// If we get to here, we have children.
|
||||
if len(n.children[i].items) <= minItems {
|
||||
return n.growChildAndRemove(i, item, minItems, typ)
|
||||
}
|
||||
child := n.mutableChild(i)
|
||||
// Either we had enough items to begin with, or we've done some
|
||||
// merging/stealing, because we've got enough now and we're ready to return
|
||||
// stuff.
|
||||
if found {
|
||||
// The item exists at index 'i', and the child we've selected can give us a
|
||||
// predecessor, since if we've gotten here it's got > minItems items in it.
|
||||
out := n.items[i]
|
||||
// We use our special-case 'remove' call with typ=maxItem to pull the
|
||||
// predecessor of item i (the rightmost leaf of our immediate left child)
|
||||
// and set it into where we pulled the item from.
|
||||
n.items[i] = child.remove(nil, minItems, removeMax)
|
||||
return out
|
||||
}
|
||||
// Final recursive call. Once we're here, we know that the item isn't in this
|
||||
// node and that the child is big enough to remove from.
|
||||
return child.remove(item, minItems, typ)
|
||||
}
|
||||
|
||||
// growChildAndRemove grows child 'i' to make sure it's possible to remove an
|
||||
// item from it while keeping it at minItems, then calls remove to actually
|
||||
// remove it.
|
||||
//
|
||||
// Most documentation says we have to do two sets of special casing:
|
||||
// 1) item is in this node
|
||||
// 2) item is in child
|
||||
// In both cases, we need to handle the two subcases:
|
||||
// A) node has enough values that it can spare one
|
||||
// B) node doesn't have enough values
|
||||
// For the latter, we have to check:
|
||||
// a) left sibling has node to spare
|
||||
// b) right sibling has node to spare
|
||||
// c) we must merge
|
||||
// To simplify our code here, we handle cases #1 and #2 the same:
|
||||
// If a node doesn't have enough items, we make sure it does (using a,b,c).
|
||||
// We then simply redo our remove call, and the second time (regardless of
|
||||
// whether we're in case 1 or 2), we'll have enough items and can guarantee
|
||||
// that we hit case A.
|
||||
func (n *node) growChildAndRemove(i int, item Item, minItems int, typ toRemove) Item {
|
||||
if i > 0 && len(n.children[i-1].items) > minItems {
|
||||
// Steal from left child
|
||||
child := n.mutableChild(i)
|
||||
stealFrom := n.mutableChild(i - 1)
|
||||
stolenItem := stealFrom.items.pop()
|
||||
child.items.insertAt(0, n.items[i-1])
|
||||
n.items[i-1] = stolenItem
|
||||
if len(stealFrom.children) > 0 {
|
||||
child.children.insertAt(0, stealFrom.children.pop())
|
||||
}
|
||||
} else if i < len(n.items) && len(n.children[i+1].items) > minItems {
|
||||
// steal from right child
|
||||
child := n.mutableChild(i)
|
||||
stealFrom := n.mutableChild(i + 1)
|
||||
stolenItem := stealFrom.items.removeAt(0)
|
||||
child.items = append(child.items, n.items[i])
|
||||
n.items[i] = stolenItem
|
||||
if len(stealFrom.children) > 0 {
|
||||
child.children = append(child.children, stealFrom.children.removeAt(0))
|
||||
}
|
||||
} else {
|
||||
if i >= len(n.items) {
|
||||
i--
|
||||
}
|
||||
child := n.mutableChild(i)
|
||||
// merge with right child
|
||||
mergeItem := n.items.removeAt(i)
|
||||
mergeChild := n.children.removeAt(i + 1)
|
||||
child.items = append(child.items, mergeItem)
|
||||
child.items = append(child.items, mergeChild.items...)
|
||||
child.children = append(child.children, mergeChild.children...)
|
||||
n.cow.freeNode(mergeChild)
|
||||
}
|
||||
return n.remove(item, minItems, typ)
|
||||
}
|
||||
|
||||
type direction int
|
||||
|
||||
const (
|
||||
descend = direction(-1)
|
||||
ascend = direction(+1)
|
||||
)
|
||||
|
||||
// iterate provides a simple method for iterating over elements in the tree.
|
||||
//
|
||||
// When ascending, the 'start' should be less than 'stop' and when descending,
|
||||
// the 'start' should be greater than 'stop'. Setting 'includeStart' to true
|
||||
// will force the iterator to include the first item when it equals 'start',
|
||||
// thus creating a "greaterOrEqual" or "lessThanEqual" rather than just a
|
||||
// "greaterThan" or "lessThan" queries.
|
||||
func (n *node) iterate(dir direction, start, stop Item, includeStart bool, hit bool, iter ItemIterator) (bool, bool) {
|
||||
var ok, found bool
|
||||
var index int
|
||||
switch dir {
|
||||
case ascend:
|
||||
if start != nil {
|
||||
index, _ = n.items.find(start)
|
||||
}
|
||||
for i := index; i < len(n.items); i++ {
|
||||
if len(n.children) > 0 {
|
||||
if hit, ok = n.children[i].iterate(dir, start, stop, includeStart, hit, iter); !ok {
|
||||
return hit, false
|
||||
}
|
||||
}
|
||||
if !includeStart && !hit && start != nil && !start.Less(n.items[i]) {
|
||||
hit = true
|
||||
continue
|
||||
}
|
||||
hit = true
|
||||
if stop != nil && !n.items[i].Less(stop) {
|
||||
return hit, false
|
||||
}
|
||||
if !iter(n.items[i]) {
|
||||
return hit, false
|
||||
}
|
||||
}
|
||||
if len(n.children) > 0 {
|
||||
if hit, ok = n.children[len(n.children)-1].iterate(dir, start, stop, includeStart, hit, iter); !ok {
|
||||
return hit, false
|
||||
}
|
||||
}
|
||||
case descend:
|
||||
if start != nil {
|
||||
index, found = n.items.find(start)
|
||||
if !found {
|
||||
index = index - 1
|
||||
}
|
||||
} else {
|
||||
index = len(n.items) - 1
|
||||
}
|
||||
for i := index; i >= 0; i-- {
|
||||
if start != nil && !n.items[i].Less(start) {
|
||||
if !includeStart || hit || start.Less(n.items[i]) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(n.children) > 0 {
|
||||
if hit, ok = n.children[i+1].iterate(dir, start, stop, includeStart, hit, iter); !ok {
|
||||
return hit, false
|
||||
}
|
||||
}
|
||||
if stop != nil && !stop.Less(n.items[i]) {
|
||||
return hit, false // continue
|
||||
}
|
||||
hit = true
|
||||
if !iter(n.items[i]) {
|
||||
return hit, false
|
||||
}
|
||||
}
|
||||
if len(n.children) > 0 {
|
||||
if hit, ok = n.children[0].iterate(dir, start, stop, includeStart, hit, iter); !ok {
|
||||
return hit, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return hit, true
|
||||
}
|
||||
|
||||
// Used for testing/debugging purposes.
|
||||
func (n *node) print(w io.Writer, level int) {
|
||||
fmt.Fprintf(w, "%sNODE:%v\n", strings.Repeat(" ", level), n.items)
|
||||
for _, c := range n.children {
|
||||
c.print(w, level+1)
|
||||
}
|
||||
}
|
||||
|
||||
// BTree is an implementation of a B-Tree.
|
||||
//
|
||||
// BTree stores Item instances in an ordered structure, allowing easy insertion,
|
||||
// removal, and iteration.
|
||||
//
|
||||
// Write operations are not safe for concurrent mutation by multiple
|
||||
// goroutines, but Read operations are.
|
||||
type BTree struct {
|
||||
degree int
|
||||
length int
|
||||
root *node
|
||||
cow *copyOnWriteContext
|
||||
}
|
||||
|
||||
// copyOnWriteContext pointers determine node ownership... a tree with a write
|
||||
// context equivalent to a node's write context is allowed to modify that node.
|
||||
// A tree whose write context does not match a node's is not allowed to modify
|
||||
// it, and must create a new, writable copy (IE: it's a Clone).
|
||||
//
|
||||
// When doing any write operation, we maintain the invariant that the current
|
||||
// node's context is equal to the context of the tree that requested the write.
|
||||
// We do this by, before we descend into any node, creating a copy with the
|
||||
// correct context if the contexts don't match.
|
||||
//
|
||||
// Since the node we're currently visiting on any write has the requesting
|
||||
// tree's context, that node is modifiable in place. Children of that node may
|
||||
// not share context, but before we descend into them, we'll make a mutable
|
||||
// copy.
|
||||
type copyOnWriteContext struct {
|
||||
freelist *FreeList
|
||||
}
|
||||
|
||||
// Clone clones the btree, lazily. Clone should not be called concurrently,
|
||||
// but the original tree (t) and the new tree (t2) can be used concurrently
|
||||
// once the Clone call completes.
|
||||
//
|
||||
// The internal tree structure of b is marked read-only and shared between t and
|
||||
// t2. Writes to both t and t2 use copy-on-write logic, creating new nodes
|
||||
// whenever one of b's original nodes would have been modified. Read operations
|
||||
// should have no performance degredation. Write operations for both t and t2
|
||||
// will initially experience minor slow-downs caused by additional allocs and
|
||||
// copies due to the aforementioned copy-on-write logic, but should converge to
|
||||
// the original performance characteristics of the original tree.
|
||||
func (t *BTree) Clone() (t2 *BTree) {
|
||||
// Create two entirely new copy-on-write contexts.
|
||||
// This operation effectively creates three trees:
|
||||
// the original, shared nodes (old b.cow)
|
||||
// the new b.cow nodes
|
||||
// the new out.cow nodes
|
||||
cow1, cow2 := *t.cow, *t.cow
|
||||
out := *t
|
||||
t.cow = &cow1
|
||||
out.cow = &cow2
|
||||
return &out
|
||||
}
|
||||
|
||||
// maxItems returns the max number of items to allow per node.
|
||||
func (t *BTree) maxItems() int {
|
||||
return t.degree*2 - 1
|
||||
}
|
||||
|
||||
// minItems returns the min number of items to allow per node (ignored for the
|
||||
// root node).
|
||||
func (t *BTree) minItems() int {
|
||||
return t.degree - 1
|
||||
}
|
||||
|
||||
func (c *copyOnWriteContext) newNode() (n *node) {
|
||||
n = c.freelist.newNode()
|
||||
n.cow = c
|
||||
return
|
||||
}
|
||||
|
||||
type freeType int
|
||||
|
||||
const (
|
||||
ftFreelistFull freeType = iota // node was freed (available for GC, not stored in freelist)
|
||||
ftStored // node was stored in the freelist for later use
|
||||
ftNotOwned // node was ignored by COW, since it's owned by another one
|
||||
)
|
||||
|
||||
// freeNode frees a node within a given COW context, if it's owned by that
|
||||
// context. It returns what happened to the node (see freeType const
|
||||
// documentation).
|
||||
func (c *copyOnWriteContext) freeNode(n *node) freeType {
|
||||
if n.cow == c {
|
||||
// clear to allow GC
|
||||
n.items.truncate(0)
|
||||
n.children.truncate(0)
|
||||
n.cow = nil
|
||||
if c.freelist.freeNode(n) {
|
||||
return ftStored
|
||||
} else {
|
||||
return ftFreelistFull
|
||||
}
|
||||
} else {
|
||||
return ftNotOwned
|
||||
}
|
||||
}
|
||||
|
||||
// ReplaceOrInsert adds the given item to the tree. If an item in the tree
|
||||
// already equals the given one, it is removed from the tree and returned.
|
||||
// Otherwise, nil is returned.
|
||||
//
|
||||
// nil cannot be added to the tree (will panic).
|
||||
func (t *BTree) ReplaceOrInsert(item Item) Item {
|
||||
if item == nil {
|
||||
panic("nil item being added to BTree")
|
||||
}
|
||||
if t.root == nil {
|
||||
t.root = t.cow.newNode()
|
||||
t.root.items = append(t.root.items, item)
|
||||
t.length++
|
||||
return nil
|
||||
} else {
|
||||
t.root = t.root.mutableFor(t.cow)
|
||||
if len(t.root.items) >= t.maxItems() {
|
||||
item2, second := t.root.split(t.maxItems() / 2)
|
||||
oldroot := t.root
|
||||
t.root = t.cow.newNode()
|
||||
t.root.items = append(t.root.items, item2)
|
||||
t.root.children = append(t.root.children, oldroot, second)
|
||||
}
|
||||
}
|
||||
out := t.root.insert(item, t.maxItems())
|
||||
if out == nil {
|
||||
t.length++
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Delete removes an item equal to the passed in item from the tree, returning
|
||||
// it. If no such item exists, returns nil.
|
||||
func (t *BTree) Delete(item Item) Item {
|
||||
return t.deleteItem(item, removeItem)
|
||||
}
|
||||
|
||||
// DeleteMin removes the smallest item in the tree and returns it.
|
||||
// If no such item exists, returns nil.
|
||||
func (t *BTree) DeleteMin() Item {
|
||||
return t.deleteItem(nil, removeMin)
|
||||
}
|
||||
|
||||
// DeleteMax removes the largest item in the tree and returns it.
|
||||
// If no such item exists, returns nil.
|
||||
func (t *BTree) DeleteMax() Item {
|
||||
return t.deleteItem(nil, removeMax)
|
||||
}
|
||||
|
||||
func (t *BTree) deleteItem(item Item, typ toRemove) Item {
|
||||
if t.root == nil || len(t.root.items) == 0 {
|
||||
return nil
|
||||
}
|
||||
t.root = t.root.mutableFor(t.cow)
|
||||
out := t.root.remove(item, t.minItems(), typ)
|
||||
if len(t.root.items) == 0 && len(t.root.children) > 0 {
|
||||
oldroot := t.root
|
||||
t.root = t.root.children[0]
|
||||
t.cow.freeNode(oldroot)
|
||||
}
|
||||
if out != nil {
|
||||
t.length--
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// AscendRange calls the iterator for every value in the tree within the range
|
||||
// [greaterOrEqual, lessThan), until iterator returns false.
|
||||
func (t *BTree) AscendRange(greaterOrEqual, lessThan Item, iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(ascend, greaterOrEqual, lessThan, true, false, iterator)
|
||||
}
|
||||
|
||||
// AscendLessThan calls the iterator for every value in the tree within the range
|
||||
// [first, pivot), until iterator returns false.
|
||||
func (t *BTree) AscendLessThan(pivot Item, iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(ascend, nil, pivot, false, false, iterator)
|
||||
}
|
||||
|
||||
// AscendGreaterOrEqual calls the iterator for every value in the tree within
|
||||
// the range [pivot, last], until iterator returns false.
|
||||
func (t *BTree) AscendGreaterOrEqual(pivot Item, iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(ascend, pivot, nil, true, false, iterator)
|
||||
}
|
||||
|
||||
// Ascend calls the iterator for every value in the tree within the range
|
||||
// [first, last], until iterator returns false.
|
||||
func (t *BTree) Ascend(iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(ascend, nil, nil, false, false, iterator)
|
||||
}
|
||||
|
||||
// DescendRange calls the iterator for every value in the tree within the range
|
||||
// [lessOrEqual, greaterThan), until iterator returns false.
|
||||
func (t *BTree) DescendRange(lessOrEqual, greaterThan Item, iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(descend, lessOrEqual, greaterThan, true, false, iterator)
|
||||
}
|
||||
|
||||
// DescendLessOrEqual calls the iterator for every value in the tree within the range
|
||||
// [pivot, first], until iterator returns false.
|
||||
func (t *BTree) DescendLessOrEqual(pivot Item, iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(descend, pivot, nil, true, false, iterator)
|
||||
}
|
||||
|
||||
// DescendGreaterThan calls the iterator for every value in the tree within
|
||||
// the range (pivot, last], until iterator returns false.
|
||||
func (t *BTree) DescendGreaterThan(pivot Item, iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(descend, nil, pivot, false, false, iterator)
|
||||
}
|
||||
|
||||
// Descend calls the iterator for every value in the tree within the range
|
||||
// [last, first], until iterator returns false.
|
||||
func (t *BTree) Descend(iterator ItemIterator) {
|
||||
if t.root == nil {
|
||||
return
|
||||
}
|
||||
t.root.iterate(descend, nil, nil, false, false, iterator)
|
||||
}
|
||||
|
||||
// Get looks for the key item in the tree, returning it. It returns nil if
|
||||
// unable to find that item.
|
||||
func (t *BTree) Get(key Item) Item {
|
||||
if t.root == nil {
|
||||
return nil
|
||||
}
|
||||
return t.root.get(key)
|
||||
}
|
||||
|
||||
// Min returns the smallest item in the tree, or nil if the tree is empty.
|
||||
func (t *BTree) Min() Item {
|
||||
return min(t.root)
|
||||
}
|
||||
|
||||
// Max returns the largest item in the tree, or nil if the tree is empty.
|
||||
func (t *BTree) Max() Item {
|
||||
return max(t.root)
|
||||
}
|
||||
|
||||
// Has returns true if the given key is in the tree.
|
||||
func (t *BTree) Has(key Item) bool {
|
||||
return t.Get(key) != nil
|
||||
}
|
||||
|
||||
// Len returns the number of items currently in the tree.
|
||||
func (t *BTree) Len() int {
|
||||
return t.length
|
||||
}
|
||||
|
||||
// Clear removes all items from the btree. If addNodesToFreelist is true,
|
||||
// t's nodes are added to its freelist as part of this call, until the freelist
|
||||
// is full. Otherwise, the root node is simply dereferenced and the subtree
|
||||
// left to Go's normal GC processes.
|
||||
//
|
||||
// This can be much faster
|
||||
// than calling Delete on all elements, because that requires finding/removing
|
||||
// each element in the tree and updating the tree accordingly. It also is
|
||||
// somewhat faster than creating a new tree to replace the old one, because
|
||||
// nodes from the old tree are reclaimed into the freelist for use by the new
|
||||
// one, instead of being lost to the garbage collector.
|
||||
//
|
||||
// This call takes:
|
||||
// O(1): when addNodesToFreelist is false, this is a single operation.
|
||||
// O(1): when the freelist is already full, it breaks out immediately
|
||||
// O(freelist size): when the freelist is empty and the nodes are all owned
|
||||
// by this tree, nodes are added to the freelist until full.
|
||||
// O(tree size): when all nodes are owned by another tree, all nodes are
|
||||
// iterated over looking for nodes to add to the freelist, and due to
|
||||
// ownership, none are.
|
||||
func (t *BTree) Clear(addNodesToFreelist bool) {
|
||||
if t.root != nil && addNodesToFreelist {
|
||||
t.root.reset(t.cow)
|
||||
}
|
||||
t.root, t.length = nil, 0
|
||||
}
|
||||
|
||||
// reset returns a subtree to the freelist. It breaks out immediately if the
|
||||
// freelist is full, since the only benefit of iterating is to fill that
|
||||
// freelist up. Returns true if parent reset call should continue.
|
||||
func (n *node) reset(c *copyOnWriteContext) bool {
|
||||
for _, child := range n.children {
|
||||
if !child.reset(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return c.freeNode(n) != ftFreelistFull
|
||||
}
|
||||
|
||||
// Int implements the Item interface for integers.
|
||||
type Int int
|
||||
|
||||
// Less returns true if int(a) < int(b).
|
||||
func (a Int) Less(b Item) bool {
|
||||
return a < b.(Int)
|
||||
}
|
||||
19
vendor/github.com/gregjones/httpcache/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/gregjones/httpcache/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- master
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
fast_finish: true
|
||||
install:
|
||||
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d .)
|
||||
- go tool vet .
|
||||
- go test -v -race ./...
|
||||
7
vendor/github.com/gregjones/httpcache/LICENSE.txt
generated
vendored
Normal file
7
vendor/github.com/gregjones/httpcache/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright © 2012 Greg Jones (greg.jones@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
25
vendor/github.com/gregjones/httpcache/README.md
generated
vendored
Normal file
25
vendor/github.com/gregjones/httpcache/README.md
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
httpcache
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/gregjones/httpcache) [](https://godoc.org/github.com/gregjones/httpcache)
|
||||
|
||||
Package httpcache provides a http.RoundTripper implementation that works as a mostly [RFC 7234](https://tools.ietf.org/html/rfc7234) compliant cache for http responses.
|
||||
|
||||
It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client and not for a shared proxy).
|
||||
|
||||
Cache Backends
|
||||
--------------
|
||||
|
||||
- The built-in 'memory' cache stores responses in an in-memory map.
|
||||
- [`github.com/gregjones/httpcache/diskcache`](https://github.com/gregjones/httpcache/tree/master/diskcache) provides a filesystem-backed cache using the [diskv](https://github.com/peterbourgon/diskv) library.
|
||||
- [`github.com/gregjones/httpcache/memcache`](https://github.com/gregjones/httpcache/tree/master/memcache) provides memcache implementations, for both App Engine and 'normal' memcache servers.
|
||||
- [`sourcegraph.com/sourcegraph/s3cache`](https://sourcegraph.com/github.com/sourcegraph/s3cache) uses Amazon S3 for storage.
|
||||
- [`github.com/gregjones/httpcache/leveldbcache`](https://github.com/gregjones/httpcache/tree/master/leveldbcache) provides a filesystem-backed cache using [leveldb](https://github.com/syndtr/goleveldb/leveldb).
|
||||
- [`github.com/die-net/lrucache`](https://github.com/die-net/lrucache) provides an in-memory cache that will evict least-recently used entries.
|
||||
- [`github.com/die-net/lrucache/twotier`](https://github.com/die-net/lrucache/tree/master/twotier) allows caches to be combined, for example to use lrucache above with a persistent disk-cache.
|
||||
- [`github.com/birkelund/boltdbcache`](https://github.com/birkelund/boltdbcache) provides a BoltDB implementation (based on the [bbolt](https://github.com/coreos/bbolt) fork).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
- [MIT License](LICENSE.txt)
|
||||
61
vendor/github.com/gregjones/httpcache/diskcache/diskcache.go
generated
vendored
Normal file
61
vendor/github.com/gregjones/httpcache/diskcache/diskcache.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// Package diskcache provides an implementation of httpcache.Cache that uses the diskv package
|
||||
// to supplement an in-memory map with persistent storage
|
||||
//
|
||||
package diskcache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"github.com/peterbourgon/diskv"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Cache is an implementation of httpcache.Cache that supplements the in-memory map with persistent storage
|
||||
type Cache struct {
|
||||
d *diskv.Diskv
|
||||
}
|
||||
|
||||
// Get returns the response corresponding to key if present
|
||||
func (c *Cache) Get(key string) (resp []byte, ok bool) {
|
||||
key = keyToFilename(key)
|
||||
resp, err := c.d.Read(key)
|
||||
if err != nil {
|
||||
return []byte{}, false
|
||||
}
|
||||
return resp, true
|
||||
}
|
||||
|
||||
// Set saves a response to the cache as key
|
||||
func (c *Cache) Set(key string, resp []byte) {
|
||||
key = keyToFilename(key)
|
||||
c.d.WriteStream(key, bytes.NewReader(resp), true)
|
||||
}
|
||||
|
||||
// Delete removes the response with key from the cache
|
||||
func (c *Cache) Delete(key string) {
|
||||
key = keyToFilename(key)
|
||||
c.d.Erase(key)
|
||||
}
|
||||
|
||||
func keyToFilename(key string) string {
|
||||
h := md5.New()
|
||||
io.WriteString(h, key)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// New returns a new Cache that will store files in basePath
|
||||
func New(basePath string) *Cache {
|
||||
return &Cache{
|
||||
d: diskv.New(diskv.Options{
|
||||
BasePath: basePath,
|
||||
CacheSizeMax: 100 * 1024 * 1024, // 100MB
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewWithDiskv returns a new Cache using the provided Diskv as underlying
|
||||
// storage.
|
||||
func NewWithDiskv(d *diskv.Diskv) *Cache {
|
||||
return &Cache{d}
|
||||
}
|
||||
551
vendor/github.com/gregjones/httpcache/httpcache.go
generated
vendored
Normal file
551
vendor/github.com/gregjones/httpcache/httpcache.go
generated
vendored
Normal file
@@ -0,0 +1,551 @@
|
||||
// Package httpcache provides a http.RoundTripper implementation that works as a
|
||||
// mostly RFC-compliant cache for http responses.
|
||||
//
|
||||
// It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client
|
||||
// and not for a shared proxy).
|
||||
//
|
||||
package httpcache
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
stale = iota
|
||||
fresh
|
||||
transparent
|
||||
// XFromCache is the header added to responses that are returned from the cache
|
||||
XFromCache = "X-From-Cache"
|
||||
)
|
||||
|
||||
// A Cache interface is used by the Transport to store and retrieve responses.
|
||||
type Cache interface {
|
||||
// Get returns the []byte representation of a cached response and a bool
|
||||
// set to true if the value isn't empty
|
||||
Get(key string) (responseBytes []byte, ok bool)
|
||||
// Set stores the []byte representation of a response against a key
|
||||
Set(key string, responseBytes []byte)
|
||||
// Delete removes the value associated with the key
|
||||
Delete(key string)
|
||||
}
|
||||
|
||||
// cacheKey returns the cache key for req.
|
||||
func cacheKey(req *http.Request) string {
|
||||
if req.Method == http.MethodGet {
|
||||
return req.URL.String()
|
||||
} else {
|
||||
return req.Method + " " + req.URL.String()
|
||||
}
|
||||
}
|
||||
|
||||
// CachedResponse returns the cached http.Response for req if present, and nil
|
||||
// otherwise.
|
||||
func CachedResponse(c Cache, req *http.Request) (resp *http.Response, err error) {
|
||||
cachedVal, ok := c.Get(cacheKey(req))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(cachedVal)
|
||||
return http.ReadResponse(bufio.NewReader(b), req)
|
||||
}
|
||||
|
||||
// MemoryCache is an implemtation of Cache that stores responses in an in-memory map.
|
||||
type MemoryCache struct {
|
||||
mu sync.RWMutex
|
||||
items map[string][]byte
|
||||
}
|
||||
|
||||
// Get returns the []byte representation of the response and true if present, false if not
|
||||
func (c *MemoryCache) Get(key string) (resp []byte, ok bool) {
|
||||
c.mu.RLock()
|
||||
resp, ok = c.items[key]
|
||||
c.mu.RUnlock()
|
||||
return resp, ok
|
||||
}
|
||||
|
||||
// Set saves response resp to the cache with key
|
||||
func (c *MemoryCache) Set(key string, resp []byte) {
|
||||
c.mu.Lock()
|
||||
c.items[key] = resp
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// Delete removes key from the cache
|
||||
func (c *MemoryCache) Delete(key string) {
|
||||
c.mu.Lock()
|
||||
delete(c.items, key)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// NewMemoryCache returns a new Cache that will store items in an in-memory map
|
||||
func NewMemoryCache() *MemoryCache {
|
||||
c := &MemoryCache{items: map[string][]byte{}}
|
||||
return c
|
||||
}
|
||||
|
||||
// Transport is an implementation of http.RoundTripper that will return values from a cache
|
||||
// where possible (avoiding a network request) and will additionally add validators (etag/if-modified-since)
|
||||
// to repeated requests allowing servers to return 304 / Not Modified
|
||||
type Transport struct {
|
||||
// The RoundTripper interface actually used to make requests
|
||||
// If nil, http.DefaultTransport is used
|
||||
Transport http.RoundTripper
|
||||
Cache Cache
|
||||
// If true, responses returned from the cache will be given an extra header, X-From-Cache
|
||||
MarkCachedResponses bool
|
||||
}
|
||||
|
||||
// NewTransport returns a new Transport with the
|
||||
// provided Cache implementation and MarkCachedResponses set to true
|
||||
func NewTransport(c Cache) *Transport {
|
||||
return &Transport{Cache: c, MarkCachedResponses: true}
|
||||
}
|
||||
|
||||
// Client returns an *http.Client that caches responses.
|
||||
func (t *Transport) Client() *http.Client {
|
||||
return &http.Client{Transport: t}
|
||||
}
|
||||
|
||||
// varyMatches will return false unless all of the cached values for the headers listed in Vary
|
||||
// match the new request
|
||||
func varyMatches(cachedResp *http.Response, req *http.Request) bool {
|
||||
for _, header := range headerAllCommaSepValues(cachedResp.Header, "vary") {
|
||||
header = http.CanonicalHeaderKey(header)
|
||||
if header != "" && req.Header.Get(header) != cachedResp.Header.Get("X-Varied-"+header) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RoundTrip takes a Request and returns a Response
|
||||
//
|
||||
// If there is a fresh Response already in cache, then it will be returned without connecting to
|
||||
// the server.
|
||||
//
|
||||
// If there is a stale Response, then any validators it contains will be set on the new request
|
||||
// to give the server a chance to respond with NotModified. If this happens, then the cached Response
|
||||
// will be returned.
|
||||
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||
cacheKey := cacheKey(req)
|
||||
cacheable := (req.Method == "GET" || req.Method == "HEAD") && req.Header.Get("range") == ""
|
||||
var cachedResp *http.Response
|
||||
if cacheable {
|
||||
cachedResp, err = CachedResponse(t.Cache, req)
|
||||
} else {
|
||||
// Need to invalidate an existing value
|
||||
t.Cache.Delete(cacheKey)
|
||||
}
|
||||
|
||||
transport := t.Transport
|
||||
if transport == nil {
|
||||
transport = http.DefaultTransport
|
||||
}
|
||||
|
||||
if cacheable && cachedResp != nil && err == nil {
|
||||
if t.MarkCachedResponses {
|
||||
cachedResp.Header.Set(XFromCache, "1")
|
||||
}
|
||||
|
||||
if varyMatches(cachedResp, req) {
|
||||
// Can only use cached value if the new request doesn't Vary significantly
|
||||
freshness := getFreshness(cachedResp.Header, req.Header)
|
||||
if freshness == fresh {
|
||||
return cachedResp, nil
|
||||
}
|
||||
|
||||
if freshness == stale {
|
||||
var req2 *http.Request
|
||||
// Add validators if caller hasn't already done so
|
||||
etag := cachedResp.Header.Get("etag")
|
||||
if etag != "" && req.Header.Get("etag") == "" {
|
||||
req2 = cloneRequest(req)
|
||||
req2.Header.Set("if-none-match", etag)
|
||||
}
|
||||
lastModified := cachedResp.Header.Get("last-modified")
|
||||
if lastModified != "" && req.Header.Get("last-modified") == "" {
|
||||
if req2 == nil {
|
||||
req2 = cloneRequest(req)
|
||||
}
|
||||
req2.Header.Set("if-modified-since", lastModified)
|
||||
}
|
||||
if req2 != nil {
|
||||
req = req2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp, err = transport.RoundTrip(req)
|
||||
if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified {
|
||||
// Replace the 304 response with the one from cache, but update with some new headers
|
||||
endToEndHeaders := getEndToEndHeaders(resp.Header)
|
||||
for _, header := range endToEndHeaders {
|
||||
cachedResp.Header[header] = resp.Header[header]
|
||||
}
|
||||
resp = cachedResp
|
||||
} else if (err != nil || (cachedResp != nil && resp.StatusCode >= 500)) &&
|
||||
req.Method == "GET" && canStaleOnError(cachedResp.Header, req.Header) {
|
||||
// In case of transport failure and stale-if-error activated, returns cached content
|
||||
// when available
|
||||
return cachedResp, nil
|
||||
} else {
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
t.Cache.Delete(cacheKey)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reqCacheControl := parseCacheControl(req.Header)
|
||||
if _, ok := reqCacheControl["only-if-cached"]; ok {
|
||||
resp = newGatewayTimeoutResponse(req)
|
||||
} else {
|
||||
resp, err = transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cacheable && canStore(parseCacheControl(req.Header), parseCacheControl(resp.Header)) {
|
||||
for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") {
|
||||
varyKey = http.CanonicalHeaderKey(varyKey)
|
||||
fakeHeader := "X-Varied-" + varyKey
|
||||
reqValue := req.Header.Get(varyKey)
|
||||
if reqValue != "" {
|
||||
resp.Header.Set(fakeHeader, reqValue)
|
||||
}
|
||||
}
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
// Delay caching until EOF is reached.
|
||||
resp.Body = &cachingReadCloser{
|
||||
R: resp.Body,
|
||||
OnEOF: func(r io.Reader) {
|
||||
resp := *resp
|
||||
resp.Body = ioutil.NopCloser(r)
|
||||
respBytes, err := httputil.DumpResponse(&resp, true)
|
||||
if err == nil {
|
||||
t.Cache.Set(cacheKey, respBytes)
|
||||
}
|
||||
},
|
||||
}
|
||||
default:
|
||||
respBytes, err := httputil.DumpResponse(resp, true)
|
||||
if err == nil {
|
||||
t.Cache.Set(cacheKey, respBytes)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.Cache.Delete(cacheKey)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ErrNoDateHeader indicates that the HTTP headers contained no Date header.
|
||||
var ErrNoDateHeader = errors.New("no Date header")
|
||||
|
||||
// Date parses and returns the value of the Date header.
|
||||
func Date(respHeaders http.Header) (date time.Time, err error) {
|
||||
dateHeader := respHeaders.Get("date")
|
||||
if dateHeader == "" {
|
||||
err = ErrNoDateHeader
|
||||
return
|
||||
}
|
||||
|
||||
return time.Parse(time.RFC1123, dateHeader)
|
||||
}
|
||||
|
||||
type realClock struct{}
|
||||
|
||||
func (c *realClock) since(d time.Time) time.Duration {
|
||||
return time.Since(d)
|
||||
}
|
||||
|
||||
type timer interface {
|
||||
since(d time.Time) time.Duration
|
||||
}
|
||||
|
||||
var clock timer = &realClock{}
|
||||
|
||||
// getFreshness will return one of fresh/stale/transparent based on the cache-control
|
||||
// values of the request and the response
|
||||
//
|
||||
// fresh indicates the response can be returned
|
||||
// stale indicates that the response needs validating before it is returned
|
||||
// transparent indicates the response should not be used to fulfil the request
|
||||
//
|
||||
// Because this is only a private cache, 'public' and 'private' in cache-control aren't
|
||||
// signficant. Similarly, smax-age isn't used.
|
||||
func getFreshness(respHeaders, reqHeaders http.Header) (freshness int) {
|
||||
respCacheControl := parseCacheControl(respHeaders)
|
||||
reqCacheControl := parseCacheControl(reqHeaders)
|
||||
if _, ok := reqCacheControl["no-cache"]; ok {
|
||||
return transparent
|
||||
}
|
||||
if _, ok := respCacheControl["no-cache"]; ok {
|
||||
return stale
|
||||
}
|
||||
if _, ok := reqCacheControl["only-if-cached"]; ok {
|
||||
return fresh
|
||||
}
|
||||
|
||||
date, err := Date(respHeaders)
|
||||
if err != nil {
|
||||
return stale
|
||||
}
|
||||
currentAge := clock.since(date)
|
||||
|
||||
var lifetime time.Duration
|
||||
var zeroDuration time.Duration
|
||||
|
||||
// If a response includes both an Expires header and a max-age directive,
|
||||
// the max-age directive overrides the Expires header, even if the Expires header is more restrictive.
|
||||
if maxAge, ok := respCacheControl["max-age"]; ok {
|
||||
lifetime, err = time.ParseDuration(maxAge + "s")
|
||||
if err != nil {
|
||||
lifetime = zeroDuration
|
||||
}
|
||||
} else {
|
||||
expiresHeader := respHeaders.Get("Expires")
|
||||
if expiresHeader != "" {
|
||||
expires, err := time.Parse(time.RFC1123, expiresHeader)
|
||||
if err != nil {
|
||||
lifetime = zeroDuration
|
||||
} else {
|
||||
lifetime = expires.Sub(date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if maxAge, ok := reqCacheControl["max-age"]; ok {
|
||||
// the client is willing to accept a response whose age is no greater than the specified time in seconds
|
||||
lifetime, err = time.ParseDuration(maxAge + "s")
|
||||
if err != nil {
|
||||
lifetime = zeroDuration
|
||||
}
|
||||
}
|
||||
if minfresh, ok := reqCacheControl["min-fresh"]; ok {
|
||||
// the client wants a response that will still be fresh for at least the specified number of seconds.
|
||||
minfreshDuration, err := time.ParseDuration(minfresh + "s")
|
||||
if err == nil {
|
||||
currentAge = time.Duration(currentAge + minfreshDuration)
|
||||
}
|
||||
}
|
||||
|
||||
if maxstale, ok := reqCacheControl["max-stale"]; ok {
|
||||
// Indicates that the client is willing to accept a response that has exceeded its expiration time.
|
||||
// If max-stale is assigned a value, then the client is willing to accept a response that has exceeded
|
||||
// its expiration time by no more than the specified number of seconds.
|
||||
// If no value is assigned to max-stale, then the client is willing to accept a stale response of any age.
|
||||
//
|
||||
// Responses served only because of a max-stale value are supposed to have a Warning header added to them,
|
||||
// but that seems like a hassle, and is it actually useful? If so, then there needs to be a different
|
||||
// return-value available here.
|
||||
if maxstale == "" {
|
||||
return fresh
|
||||
}
|
||||
maxstaleDuration, err := time.ParseDuration(maxstale + "s")
|
||||
if err == nil {
|
||||
currentAge = time.Duration(currentAge - maxstaleDuration)
|
||||
}
|
||||
}
|
||||
|
||||
if lifetime > currentAge {
|
||||
return fresh
|
||||
}
|
||||
|
||||
return stale
|
||||
}
|
||||
|
||||
// Returns true if either the request or the response includes the stale-if-error
|
||||
// cache control extension: https://tools.ietf.org/html/rfc5861
|
||||
func canStaleOnError(respHeaders, reqHeaders http.Header) bool {
|
||||
respCacheControl := parseCacheControl(respHeaders)
|
||||
reqCacheControl := parseCacheControl(reqHeaders)
|
||||
|
||||
var err error
|
||||
lifetime := time.Duration(-1)
|
||||
|
||||
if staleMaxAge, ok := respCacheControl["stale-if-error"]; ok {
|
||||
if staleMaxAge != "" {
|
||||
lifetime, err = time.ParseDuration(staleMaxAge + "s")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if staleMaxAge, ok := reqCacheControl["stale-if-error"]; ok {
|
||||
if staleMaxAge != "" {
|
||||
lifetime, err = time.ParseDuration(staleMaxAge + "s")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if lifetime >= 0 {
|
||||
date, err := Date(respHeaders)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
currentAge := clock.since(date)
|
||||
if lifetime > currentAge {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getEndToEndHeaders(respHeaders http.Header) []string {
|
||||
// These headers are always hop-by-hop
|
||||
hopByHopHeaders := map[string]struct{}{
|
||||
"Connection": struct{}{},
|
||||
"Keep-Alive": struct{}{},
|
||||
"Proxy-Authenticate": struct{}{},
|
||||
"Proxy-Authorization": struct{}{},
|
||||
"Te": struct{}{},
|
||||
"Trailers": struct{}{},
|
||||
"Transfer-Encoding": struct{}{},
|
||||
"Upgrade": struct{}{},
|
||||
}
|
||||
|
||||
for _, extra := range strings.Split(respHeaders.Get("connection"), ",") {
|
||||
// any header listed in connection, if present, is also considered hop-by-hop
|
||||
if strings.Trim(extra, " ") != "" {
|
||||
hopByHopHeaders[http.CanonicalHeaderKey(extra)] = struct{}{}
|
||||
}
|
||||
}
|
||||
endToEndHeaders := []string{}
|
||||
for respHeader, _ := range respHeaders {
|
||||
if _, ok := hopByHopHeaders[respHeader]; !ok {
|
||||
endToEndHeaders = append(endToEndHeaders, respHeader)
|
||||
}
|
||||
}
|
||||
return endToEndHeaders
|
||||
}
|
||||
|
||||
func canStore(reqCacheControl, respCacheControl cacheControl) (canStore bool) {
|
||||
if _, ok := respCacheControl["no-store"]; ok {
|
||||
return false
|
||||
}
|
||||
if _, ok := reqCacheControl["no-store"]; ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func newGatewayTimeoutResponse(req *http.Request) *http.Response {
|
||||
var braw bytes.Buffer
|
||||
braw.WriteString("HTTP/1.1 504 Gateway Timeout\r\n\r\n")
|
||||
resp, err := http.ReadResponse(bufio.NewReader(&braw), req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
// (This function copyright goauth2 authors: https://code.google.com/p/goauth2)
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// shallow copy of the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// deep copy of the Header
|
||||
r2.Header = make(http.Header)
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = s
|
||||
}
|
||||
return r2
|
||||
}
|
||||
|
||||
type cacheControl map[string]string
|
||||
|
||||
func parseCacheControl(headers http.Header) cacheControl {
|
||||
cc := cacheControl{}
|
||||
ccHeader := headers.Get("Cache-Control")
|
||||
for _, part := range strings.Split(ccHeader, ",") {
|
||||
part = strings.Trim(part, " ")
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
if strings.ContainsRune(part, '=') {
|
||||
keyval := strings.Split(part, "=")
|
||||
cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",")
|
||||
} else {
|
||||
cc[part] = ""
|
||||
}
|
||||
}
|
||||
return cc
|
||||
}
|
||||
|
||||
// headerAllCommaSepValues returns all comma-separated values (each
|
||||
// with whitespace trimmed) for header name in headers. According to
|
||||
// Section 4.2 of the HTTP/1.1 spec
|
||||
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2),
|
||||
// values from multiple occurrences of a header should be concatenated, if
|
||||
// the header's value is a comma-separated list.
|
||||
func headerAllCommaSepValues(headers http.Header, name string) []string {
|
||||
var vals []string
|
||||
for _, val := range headers[http.CanonicalHeaderKey(name)] {
|
||||
fields := strings.Split(val, ",")
|
||||
for i, f := range fields {
|
||||
fields[i] = strings.TrimSpace(f)
|
||||
}
|
||||
vals = append(vals, fields...)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// cachingReadCloser is a wrapper around ReadCloser R that calls OnEOF
|
||||
// handler with a full copy of the content read from R when EOF is
|
||||
// reached.
|
||||
type cachingReadCloser struct {
|
||||
// Underlying ReadCloser.
|
||||
R io.ReadCloser
|
||||
// OnEOF is called with a copy of the content of R when EOF is reached.
|
||||
OnEOF func(io.Reader)
|
||||
|
||||
buf bytes.Buffer // buf stores a copy of the content of R.
|
||||
}
|
||||
|
||||
// Read reads the next len(p) bytes from R or until R is drained. The
|
||||
// return value n is the number of bytes read. If R has no data to
|
||||
// return, err is io.EOF and OnEOF is called with a full copy of what
|
||||
// has been read so far.
|
||||
func (r *cachingReadCloser) Read(p []byte) (n int, err error) {
|
||||
n, err = r.R.Read(p)
|
||||
r.buf.Write(p[:n])
|
||||
if err == io.EOF {
|
||||
r.OnEOF(bytes.NewReader(r.buf.Bytes()))
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *cachingReadCloser) Close() error {
|
||||
return r.R.Close()
|
||||
}
|
||||
|
||||
// NewMemoryCacheTransport returns a new Transport using the in-memory cache implementation
|
||||
func NewMemoryCacheTransport() *Transport {
|
||||
c := NewMemoryCache()
|
||||
t := NewTransport(c)
|
||||
return t
|
||||
}
|
||||
89
vendor/github.com/jszwec/csvutil/.gitignore
generated
vendored
Normal file
89
vendor/github.com/jszwec/csvutil/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
# Created by https://www.gitignore.io/api/osx,go,windows,linux
|
||||
|
||||
### OSX ###
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
|
||||
### Go ###
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
|
||||
### Windows ###
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
21
vendor/github.com/jszwec/csvutil/LICENSE
generated
vendored
Normal file
21
vendor/github.com/jszwec/csvutil/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Jacek Szwec
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
668
vendor/github.com/jszwec/csvutil/README.md
generated
vendored
Normal file
668
vendor/github.com/jszwec/csvutil/README.md
generated
vendored
Normal file
@@ -0,0 +1,668 @@
|
||||
csvutil [](https://pkg.go.dev/github.com/jszwec/csvutil?tab=doc)  [](https://goreportcard.com/report/github.com/jszwec/csvutil) [](https://codecov.io/gh/jszwec/csvutil)
|
||||
=================
|
||||
|
||||
<p align="center">
|
||||
<img style="float: right;" src="https://user-images.githubusercontent.com/3941256/33054906-52b4bc08-ce4a-11e7-9651-b70c5a47c921.png"/ width=200>
|
||||
</p>
|
||||
|
||||
Package csvutil provides fast, idiomatic, and dependency free mapping between CSV and Go (golang) values.
|
||||
|
||||
This package is not a CSV parser, it is based on the [Reader](https://godoc.org/github.com/jszwec/csvutil#Reader) and [Writer](https://godoc.org/github.com/jszwec/csvutil#Writer)
|
||||
interfaces which are implemented by eg. std Go (golang) [csv package](https://golang.org/pkg/encoding/csv). This gives a possibility
|
||||
of choosing any other CSV writer or reader which may be more performant.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
go get github.com/jszwec/csvutil
|
||||
|
||||
Requirements
|
||||
-------------
|
||||
|
||||
* Go1.7+
|
||||
|
||||
Index
|
||||
------
|
||||
|
||||
1. [Examples](#examples)
|
||||
1. [Unmarshal](#examples_unmarshal)
|
||||
2. [Marshal](#examples_marshal)
|
||||
3. [Unmarshal and metadata](#examples_unmarshal_and_metadata)
|
||||
4. [But my CSV file has no header...](#examples_but_my_csv_has_no_header)
|
||||
5. [Decoder.Map - data normalization](#examples_decoder_map)
|
||||
6. [Different separator/delimiter](#examples_different_separator)
|
||||
7. [Custom Types](#examples_custom_types)
|
||||
8. [Custom time.Time format](#examples_time_format)
|
||||
9. [Custom struct tags](#examples_struct_tags)
|
||||
10. [Slice and Map fields](#examples_slice_and_map_field)
|
||||
11. [Nested/Embedded structs](#examples_nested_structs)
|
||||
12. [Inline tag](#examples_inlined_structs)
|
||||
2. [Performance](#performance)
|
||||
1. [Unmarshal](#performance_unmarshal)
|
||||
2. [Marshal](#performance_marshal)
|
||||
|
||||
Example <a name="examples"></a>
|
||||
--------
|
||||
|
||||
### Unmarshal <a name="examples_unmarshal"></a>
|
||||
|
||||
Nice and easy Unmarshal is using the Go std [csv.Reader](https://golang.org/pkg/encoding/csv/#Reader) with its default options. Use [Decoder](https://godoc.org/github.com/jszwec/csvutil#Decoder) for streaming and more advanced use cases.
|
||||
|
||||
```go
|
||||
var csvInput = []byte(`
|
||||
name,age,CreatedAt
|
||||
jacek,26,2012-04-01T15:00:00Z
|
||||
john,,0001-01-01T00:00:00Z`,
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Name string `csv:"name"`
|
||||
Age int `csv:"age,omitempty"`
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
var users []User
|
||||
if err := csvutil.Unmarshal(csvInput, &users); err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
fmt.Printf("%+v\n", u)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {Name:jacek Age:26 CreatedAt:2012-04-01 15:00:00 +0000 UTC}
|
||||
// {Name:john Age:0 CreatedAt:0001-01-01 00:00:00 +0000 UTC}
|
||||
```
|
||||
|
||||
### Marshal <a name="examples_marshal"></a>
|
||||
|
||||
Marshal is using the Go std [csv.Writer](https://golang.org/pkg/encoding/csv/#Writer) with its default options. Use [Encoder](https://godoc.org/github.com/jszwec/csvutil#Encoder) for streaming or to use a different Writer.
|
||||
|
||||
```go
|
||||
type Address struct {
|
||||
City string
|
||||
Country string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Address
|
||||
Age int `csv:"age,omitempty"`
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
users := []User{
|
||||
{
|
||||
Name: "John",
|
||||
Address: Address{"Boston", "USA"},
|
||||
Age: 26,
|
||||
CreatedAt: time.Date(2010, 6, 2, 12, 0, 0, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
Name: "Alice",
|
||||
Address: Address{"SF", "USA"},
|
||||
},
|
||||
}
|
||||
|
||||
b, err := csvutil.Marshal(users)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
|
||||
// Output:
|
||||
// Name,City,Country,age,CreatedAt
|
||||
// John,Boston,USA,26,2010-06-02T12:00:00Z
|
||||
// Alice,SF,USA,,0001-01-01T00:00:00Z
|
||||
```
|
||||
|
||||
### Unmarshal and metadata <a name="examples_unmarshal_and_metadata"></a>
|
||||
|
||||
It may happen that your CSV input will not always have the same header. In addition
|
||||
to your base fields you may get extra metadata that you would still like to store.
|
||||
[Decoder](https://godoc.org/github.com/jszwec/csvutil#Decoder) provides
|
||||
[Unused](https://godoc.org/github.com/jszwec/csvutil#Decoder.Unused) method, which after each call to
|
||||
[Decode](https://godoc.org/github.com/jszwec/csvutil#Decoder.Decode) can report which header indexes
|
||||
were not used during decoding. Based on that, it is possible to handle and store all these extra values.
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
Name string `csv:"name"`
|
||||
City string `csv:"city"`
|
||||
Age int `csv:"age"`
|
||||
OtherData map[string]string `csv:"-"`
|
||||
}
|
||||
|
||||
csvReader := csv.NewReader(strings.NewReader(`
|
||||
name,age,city,zip
|
||||
alice,25,la,90005
|
||||
bob,30,ny,10005`))
|
||||
|
||||
dec, err := csvutil.NewDecoder(csvReader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
header := dec.Header()
|
||||
var users []User
|
||||
for {
|
||||
u := User{OtherData: make(map[string]string)}
|
||||
|
||||
if err := dec.Decode(&u); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, i := range dec.Unused() {
|
||||
u.OtherData[header[i]] = dec.Record()[i]
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
|
||||
fmt.Println(users)
|
||||
|
||||
// Output:
|
||||
// [{alice la 25 map[zip:90005]} {bob ny 30 map[zip:10005]}]
|
||||
```
|
||||
|
||||
### But my CSV file has no header... <a name="examples_but_my_csv_has_no_header"></a>
|
||||
|
||||
Some CSV files have no header, but if you know how it should look like, it is
|
||||
possible to define a struct and generate it. All that is left to do, is to pass
|
||||
it to a decoder.
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
ID int
|
||||
Name string
|
||||
Age int `csv:",omitempty"`
|
||||
City string
|
||||
}
|
||||
|
||||
csvReader := csv.NewReader(strings.NewReader(`
|
||||
1,John,27,la
|
||||
2,Bob,,ny`))
|
||||
|
||||
// in real application this should be done once in init function.
|
||||
userHeader, err := csvutil.Header(User{}, "csv")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dec, err := csvutil.NewDecoder(csvReader, userHeader...)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var users []User
|
||||
for {
|
||||
var u User
|
||||
if err := dec.Decode(&u); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
|
||||
fmt.Printf("%+v", users)
|
||||
|
||||
// Output:
|
||||
// [{ID:1 Name:John Age:27 City:la} {ID:2 Name:Bob Age:0 City:ny}]
|
||||
```
|
||||
|
||||
### Decoder.Map - data normalization <a name="examples_decoder_map"></a>
|
||||
|
||||
The Decoder's [Map](https://godoc.org/github.com/jszwec/csvutil#Decoder.Map) function is a powerful tool that can help clean up or normalize
|
||||
the incoming data before the actual decoding takes place.
|
||||
|
||||
Lets say we want to decode some floats and the csv input contains some NaN values, but these values are represented by the 'n/a' string. An attempt to decode 'n/a' into float will end up with error, because strconv.ParseFloat expects 'NaN'. Knowing that, we can implement a Map function that will normalize our 'n/a' string and turn it to 'NaN' only for float types.
|
||||
|
||||
```go
|
||||
dec, err := NewDecoder(r)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dec.Map = func(field, column string, v interface{}) string {
|
||||
if _, ok := v.(float64); ok && field == "n/a" {
|
||||
return "NaN"
|
||||
}
|
||||
return field
|
||||
}
|
||||
```
|
||||
|
||||
Now our float64 fields will be decoded properly into NaN. What about float32, float type aliases and other NaN formats? Look at the full example [here](https://gist.github.com/jszwec/2bb94f8f3612e0162eb16003701f727e).
|
||||
|
||||
### Different separator/delimiter <a name="examples_different_separator"></a>
|
||||
|
||||
Some files may use different value separators, for example TSV files would use `\t`. The following examples show how to set up a Decoder and Encoder for such use case.
|
||||
|
||||
#### Decoder:
|
||||
```go
|
||||
csvReader := csv.NewReader(r)
|
||||
csvReader.Comma = '\t'
|
||||
|
||||
dec, err := NewDecoder(csvReader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var users []User
|
||||
for {
|
||||
var u User
|
||||
if err := dec.Decode(&u); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Encoder:
|
||||
```go
|
||||
var buf bytes.Buffer
|
||||
|
||||
w := csv.NewWriter(&buf)
|
||||
w.Comma = '\t'
|
||||
enc := csvutil.NewEncoder(w)
|
||||
|
||||
for _, u := range users {
|
||||
if err := enc.Encode(u); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
if err := w.Error(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Types and Overrides <a name="examples_custom_types"></a>
|
||||
|
||||
There are multiple ways to customize or override your type's behavior.
|
||||
|
||||
1. a type implements [csvutil.Marshaler](https://pkg.go.dev/github.com/jszwec/csvutil#Marshaler) and/or [csvutil.Unmarshaler](https://pkg.go.dev/github.com/jszwec/csvutil#Unmarshaler)
|
||||
```go
|
||||
type Foo int64
|
||||
|
||||
func (f Foo) MarshalCSV() ([]byte, error) {
|
||||
return strconv.AppendInt(nil, int64(f), 16), nil
|
||||
}
|
||||
|
||||
func (f *Foo) UnmarshalCSV(data []byte) error {
|
||||
i, err := strconv.ParseInt(string(data), 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f = Foo(i)
|
||||
return nil
|
||||
}
|
||||
```
|
||||
2. a type implements [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler) and/or [encoding.TextMarshaler](https://golang.org/pkg/encoding/#TextMarshaler)
|
||||
```go
|
||||
type Foo int64
|
||||
|
||||
func (f Foo) MarshalText() ([]byte, error) {
|
||||
return strconv.AppendInt(nil, int64(f), 16), nil
|
||||
}
|
||||
|
||||
func (f *Foo) UnmarshalText(data []byte) error {
|
||||
i, err := strconv.ParseInt(string(data), 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f = Foo(i)
|
||||
return nil
|
||||
}
|
||||
```
|
||||
3. a type is registered using [Encoder.Register](https://pkg.go.dev/github.com/jszwec/csvutil#Encoder.Register) and/or [Decoder.Register](https://pkg.go.dev/github.com/jszwec/csvutil#Decoder.Register)
|
||||
```go
|
||||
type Foo int64
|
||||
|
||||
enc.Register(func(f Foo) ([]byte, error) {
|
||||
return strconv.AppendInt(nil, int64(f), 16), nil
|
||||
})
|
||||
|
||||
dec.Register(func(data []byte, f *Foo) error {
|
||||
v, err := strconv.ParseInt(string(data), 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f = Foo(v)
|
||||
return nil
|
||||
})
|
||||
```
|
||||
4. a type implements an interface that was registered using [Encoder.Register](https://pkg.go.dev/github.com/jszwec/csvutil#Encoder.Register) and/or [Decoder.Register](https://pkg.go.dev/github.com/jszwec/csvutil#Decoder.Register)
|
||||
```go
|
||||
type Foo int64
|
||||
|
||||
func (f Foo) String() string {
|
||||
return strconv.FormatInt(int64(f), 16)
|
||||
}
|
||||
|
||||
func (f *Foo) Scan(state fmt.ScanState, verb rune) error {
|
||||
// too long; look here: https://github.com/jszwec/csvutil/blob/master/example_decoder_register_test.go#L19
|
||||
}
|
||||
|
||||
enc.Register(func(s fmt.Stringer) ([]byte, error) {
|
||||
return []byte(s.String()), nil
|
||||
})
|
||||
|
||||
dec.Register(func(data []byte, s fmt.Scanner) error {
|
||||
_, err := fmt.Sscan(string(data), s)
|
||||
return err
|
||||
})
|
||||
```
|
||||
|
||||
The order of precedence for both Encoder and Decoder is:
|
||||
1. type is registered
|
||||
2. type implements an interface that was registered
|
||||
3. csvutil.{Un,M}arshaler
|
||||
4. encoding.Text{Un,M}arshaler
|
||||
|
||||
For more examples look [here](https://pkg.go.dev/github.com/jszwec/csvutil?readme=expanded#pkg-examples)
|
||||
|
||||
### Custom time.Time format <a name="examples_time_format"></a>
|
||||
|
||||
Type [time.Time](https://golang.org/pkg/time/#Time) can be used as is in the struct fields by both Decoder and Encoder
|
||||
due to the fact that both have builtin support for [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler) and [encoding.TextMarshaler](https://golang.org/pkg/encoding/#TextMarshaler). This means that by default
|
||||
Time has a specific format; look at [MarshalText](https://golang.org/pkg/time/#Time.MarshalText) and [UnmarshalText](https://golang.org/pkg/time/#Time.UnmarshalText). There are two ways to override it, which one you choose depends on your use case:
|
||||
|
||||
1. Via Register func (based on encoding/json)
|
||||
```go
|
||||
const format = "2006/01/02 15:04:05"
|
||||
|
||||
marshalTime := func(t time.Time) ([]byte, error) {
|
||||
return t.AppendFormat(nil, format), nil
|
||||
}
|
||||
|
||||
unmarshalTime := func(data []byte, t *time.Time) error {
|
||||
tt, err := time.Parse(format, string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = tt
|
||||
return nil
|
||||
}
|
||||
|
||||
enc := csvutil.NewEncoder(w)
|
||||
enc.Register(marshalTime)
|
||||
|
||||
dec, err := csvutil.NewDecoder(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.Register(unmarshalTime)
|
||||
```
|
||||
|
||||
2. With custom type:
|
||||
```go
|
||||
type Time struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
const format = "2006/01/02 15:04:05"
|
||||
|
||||
func (t Time) MarshalCSV() ([]byte, error) {
|
||||
var b [len(format)]byte
|
||||
return t.AppendFormat(b[:0], format), nil
|
||||
}
|
||||
|
||||
func (t *Time) UnmarshalCSV(data []byte) error {
|
||||
tt, err := time.Parse(format, string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = Time{Time: tt}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Custom struct tags <a name="examples_struct_tags"></a>
|
||||
|
||||
Like in other Go encoding packages struct field tags can be used to set
|
||||
custom names or options. By default encoders and decoders are looking at `csv` tag.
|
||||
However, this can be overriden by manually setting the Tag field.
|
||||
|
||||
```go
|
||||
type Foo struct {
|
||||
Bar int `custom:"bar"`
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
dec, err := csvutil.NewDecoder(r)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
dec.Tag = "custom"
|
||||
```
|
||||
|
||||
```go
|
||||
enc := csvutil.NewEncoder(w)
|
||||
enc.Tag = "custom"
|
||||
```
|
||||
|
||||
### Slice and Map fields <a name="examples_slice_and_map_field"></a>
|
||||
|
||||
There is no default encoding/decoding support for slice and map fields because there is no CSV spec for such values.
|
||||
In such case, it is recommended to create a custom type alias and implement Marshaler and Unmarshaler interfaces.
|
||||
Please note that slice and map aliases behave differently than aliases of other types - there is no need for type casting.
|
||||
|
||||
```go
|
||||
type Strings []string
|
||||
|
||||
func (s Strings) MarshalCSV() ([]byte, error) {
|
||||
return []byte(strings.Join(s, ",")), nil // strings.Join takes []string but it will also accept Strings
|
||||
}
|
||||
|
||||
type StringMap map[string]string
|
||||
|
||||
func (sm StringMap) MarshalCSV() ([]byte, error) {
|
||||
return []byte(fmt.Sprint(sm)), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
b, err := csvutil.Marshal([]struct {
|
||||
Strings Strings `csv:"strings"`
|
||||
Map StringMap `csv:"map"`
|
||||
}{
|
||||
{[]string{"a", "b"}, map[string]string{"a": "1"}}, // no type casting is required for slice and map aliases
|
||||
{Strings{"c", "d"}, StringMap{"b": "1"}},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
// Output:
|
||||
// strings,map
|
||||
// "a,b",map[a:1]
|
||||
// "c,d",map[b:1]
|
||||
}
|
||||
```
|
||||
|
||||
### Nested/Embedded structs <a name="examples_nested_structs"></a>
|
||||
|
||||
Both Encoder and Decoder support nested or embedded structs.
|
||||
|
||||
Playground: https://play.golang.org/p/ZySjdVkovbf
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jszwec/csvutil"
|
||||
)
|
||||
|
||||
type Address struct {
|
||||
Street string `csv:"street"`
|
||||
City string `csv:"city"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `csv:"name"`
|
||||
Address
|
||||
}
|
||||
|
||||
func main() {
|
||||
users := []User{
|
||||
{
|
||||
Name: "John",
|
||||
Address: Address{
|
||||
Street: "Boylston",
|
||||
City: "Boston",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b, err := csvutil.Marshal(users)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
var out []User
|
||||
if err := csvutil.Unmarshal(b, &out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", out)
|
||||
|
||||
// Output:
|
||||
//
|
||||
// name,street,city
|
||||
// John,Boylston,Boston
|
||||
//
|
||||
// [{Name:John Address:{Street:Boylston City:Boston}}]
|
||||
}
|
||||
```
|
||||
|
||||
### Inline tag <a name="examples_inlined_structs"></a>
|
||||
|
||||
Fields with inline tag behave similarly to embedded struct fields. However,
|
||||
it gives a possibility to specify the prefix for all underlying fields. This
|
||||
can be useful when one structure can define multiple CSV columns because they
|
||||
are different from each other only by a certain prefix. Look at the example below.
|
||||
|
||||
Playground: https://play.golang.org/p/jyEzeskSnj7
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jszwec/csvutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Address struct {
|
||||
Street string `csv:"street"`
|
||||
City string `csv:"city"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `csv:"name"`
|
||||
Address Address `csv:",inline"`
|
||||
HomeAddress Address `csv:"home_address_,inline"`
|
||||
WorkAddress Address `csv:"work_address_,inline"`
|
||||
Age int `csv:"age,omitempty"`
|
||||
}
|
||||
|
||||
users := []User{
|
||||
{
|
||||
Name: "John",
|
||||
Address: Address{"Washington", "Boston"},
|
||||
HomeAddress: Address{"Boylston", "Boston"},
|
||||
WorkAddress: Address{"River St", "Cambridge"},
|
||||
Age: 26,
|
||||
},
|
||||
}
|
||||
|
||||
b, err := csvutil.Marshal(users)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", b)
|
||||
|
||||
// Output:
|
||||
// name,street,city,home_address_street,home_address_city,work_address_street,work_address_city,age
|
||||
// John,Washington,Boston,Boylston,Boston,River St,Cambridge,26
|
||||
}
|
||||
```
|
||||
|
||||
Performance
|
||||
------------
|
||||
|
||||
csvutil provides the best encoding and decoding performance with small memory usage.
|
||||
|
||||
### Unmarshal <a name="performance_unmarshal"></a>
|
||||
|
||||
[benchmark code](https://gist.github.com/jszwec/e8515e741190454fa3494bcd3e1f100f)
|
||||
|
||||
#### csvutil:
|
||||
```
|
||||
BenchmarkUnmarshal/csvutil.Unmarshal/1_record-12 280696 4516 ns/op 7332 B/op 26 allocs/op
|
||||
BenchmarkUnmarshal/csvutil.Unmarshal/10_records-12 95750 11517 ns/op 8356 B/op 35 allocs/op
|
||||
BenchmarkUnmarshal/csvutil.Unmarshal/100_records-12 14997 83146 ns/op 18532 B/op 125 allocs/op
|
||||
BenchmarkUnmarshal/csvutil.Unmarshal/1000_records-12 1485 750143 ns/op 121094 B/op 1025 allocs/op
|
||||
BenchmarkUnmarshal/csvutil.Unmarshal/10000_records-12 154 7587205 ns/op 1136662 B/op 10025 allocs/op
|
||||
BenchmarkUnmarshal/csvutil.Unmarshal/100000_records-12 14 76126616 ns/op 11808744 B/op 100025 allocs/op
|
||||
```
|
||||
|
||||
#### gocsv:
|
||||
```
|
||||
BenchmarkUnmarshal/gocsv.Unmarshal/1_record-12 141330 7499 ns/op 7795 B/op 97 allocs/op
|
||||
BenchmarkUnmarshal/gocsv.Unmarshal/10_records-12 54252 21664 ns/op 13891 B/op 307 allocs/op
|
||||
BenchmarkUnmarshal/gocsv.Unmarshal/100_records-12 6920 159662 ns/op 72644 B/op 2380 allocs/op
|
||||
BenchmarkUnmarshal/gocsv.Unmarshal/1000_records-12 752 1556083 ns/op 650248 B/op 23083 allocs/op
|
||||
BenchmarkUnmarshal/gocsv.Unmarshal/10000_records-12 72 17086623 ns/op 7017469 B/op 230092 allocs/op
|
||||
BenchmarkUnmarshal/gocsv.Unmarshal/100000_records-12 7 163610749 ns/op 75004923 B/op 2300105 allocs/op
|
||||
```
|
||||
|
||||
#### easycsv:
|
||||
```
|
||||
BenchmarkUnmarshal/easycsv.ReadAll/1_record-12 101527 10662 ns/op 8855 B/op 81 allocs/op
|
||||
BenchmarkUnmarshal/easycsv.ReadAll/10_records-12 23325 51437 ns/op 24072 B/op 391 allocs/op
|
||||
BenchmarkUnmarshal/easycsv.ReadAll/100_records-12 2402 447296 ns/op 170538 B/op 3454 allocs/op
|
||||
BenchmarkUnmarshal/easycsv.ReadAll/1000_records-12 272 4370854 ns/op 1595683 B/op 34057 allocs/op
|
||||
BenchmarkUnmarshal/easycsv.ReadAll/10000_records-12 24 47502457 ns/op 18861808 B/op 340068 allocs/op
|
||||
BenchmarkUnmarshal/easycsv.ReadAll/100000_records-12 3 468974170 ns/op 189427066 B/op 3400082 allocs/op
|
||||
```
|
||||
|
||||
### Marshal <a name="performance_marshal"></a>
|
||||
|
||||
[benchmark code](https://gist.github.com/jszwec/31980321e1852ebb5615a44ccf374f17)
|
||||
|
||||
#### csvutil:
|
||||
```
|
||||
BenchmarkMarshal/csvutil.Marshal/1_record-12 279558 4390 ns/op 9952 B/op 12 allocs/op
|
||||
BenchmarkMarshal/csvutil.Marshal/10_records-12 82478 15608 ns/op 10800 B/op 21 allocs/op
|
||||
BenchmarkMarshal/csvutil.Marshal/100_records-12 10275 117288 ns/op 28208 B/op 112 allocs/op
|
||||
BenchmarkMarshal/csvutil.Marshal/1000_records-12 1075 1147473 ns/op 168508 B/op 1014 allocs/op
|
||||
BenchmarkMarshal/csvutil.Marshal/10000_records-12 100 11985382 ns/op 1525973 B/op 10017 allocs/op
|
||||
BenchmarkMarshal/csvutil.Marshal/100000_records-12 9 113640813 ns/op 22455873 B/op 100021 allocs/op
|
||||
```
|
||||
|
||||
#### gocsv:
|
||||
```
|
||||
BenchmarkMarshal/gocsv.Marshal/1_record-12 203052 6077 ns/op 5914 B/op 81 allocs/op
|
||||
BenchmarkMarshal/gocsv.Marshal/10_records-12 50132 24585 ns/op 9284 B/op 360 allocs/op
|
||||
BenchmarkMarshal/gocsv.Marshal/100_records-12 5480 212008 ns/op 51916 B/op 3151 allocs/op
|
||||
BenchmarkMarshal/gocsv.Marshal/1000_records-12 514 2053919 ns/op 444506 B/op 31053 allocs/op
|
||||
BenchmarkMarshal/gocsv.Marshal/10000_records-12 52 21066666 ns/op 4332377 B/op 310064 allocs/op
|
||||
BenchmarkMarshal/gocsv.Marshal/100000_records-12 5 207408929 ns/op 51169419 B/op 3100077 allocs/op
|
||||
```
|
||||
2
vendor/github.com/jszwec/csvutil/_config.yml
generated
vendored
Normal file
2
vendor/github.com/jszwec/csvutil/_config.yml
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
theme: jekyll-theme-cayman
|
||||
markdown: GFM
|
||||
178
vendor/github.com/jszwec/csvutil/cache.go
generated
vendored
Normal file
178
vendor/github.com/jszwec/csvutil/cache.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type field struct {
|
||||
name string
|
||||
baseType reflect.Type
|
||||
typ reflect.Type
|
||||
tag tag
|
||||
index []int
|
||||
}
|
||||
|
||||
type fields []field
|
||||
|
||||
func (fs fields) Len() int { return len(fs) }
|
||||
|
||||
func (fs fields) Swap(i, j int) { fs[i], fs[j] = fs[j], fs[i] }
|
||||
|
||||
func (fs fields) Less(i, j int) bool {
|
||||
for k, n := range fs[i].index {
|
||||
if n != fs[j].index[k] {
|
||||
return n < fs[j].index[k]
|
||||
}
|
||||
}
|
||||
return len(fs[i].index) < len(fs[j].index)
|
||||
}
|
||||
|
||||
type typeKey struct {
|
||||
tag string
|
||||
reflect.Type
|
||||
}
|
||||
|
||||
type fieldMap map[string]fields
|
||||
|
||||
func (m fieldMap) insert(f field) {
|
||||
fs, ok := m[f.name]
|
||||
if !ok {
|
||||
m[f.name] = append(fs, f)
|
||||
return
|
||||
}
|
||||
|
||||
// insert only fields with the shortest path.
|
||||
if len(fs[0].index) != len(f.index) {
|
||||
return
|
||||
}
|
||||
|
||||
// fields that are tagged have priority.
|
||||
if !f.tag.empty {
|
||||
m[f.name] = append([]field{f}, fs...)
|
||||
return
|
||||
}
|
||||
|
||||
m[f.name] = append(fs, f)
|
||||
}
|
||||
|
||||
func (m fieldMap) fields() fields {
|
||||
out := make(fields, 0, len(m))
|
||||
for _, v := range m {
|
||||
for i, f := range v {
|
||||
if f.tag.empty != v[0].tag.empty {
|
||||
v = v[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(v) > 1 {
|
||||
continue
|
||||
}
|
||||
out = append(out, v[0])
|
||||
}
|
||||
sort.Sort(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func buildFields(k typeKey) fields {
|
||||
type key struct {
|
||||
reflect.Type
|
||||
tag
|
||||
}
|
||||
|
||||
q := fields{{typ: k.Type}}
|
||||
visited := make(map[key]struct{})
|
||||
fm := make(fieldMap)
|
||||
|
||||
for len(q) > 0 {
|
||||
f := q[0]
|
||||
q = q[1:]
|
||||
|
||||
key := key{f.typ, f.tag}
|
||||
if _, ok := visited[key]; ok {
|
||||
continue
|
||||
}
|
||||
visited[key] = struct{}{}
|
||||
|
||||
depth := len(f.index)
|
||||
|
||||
numField := f.typ.NumField()
|
||||
for i := 0; i < numField; i++ {
|
||||
sf := f.typ.Field(i)
|
||||
|
||||
if sf.PkgPath != "" && !sf.Anonymous {
|
||||
// unexported field
|
||||
continue
|
||||
}
|
||||
|
||||
if sf.Anonymous {
|
||||
t := sf.Type
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if sf.PkgPath != "" && t.Kind() != reflect.Struct {
|
||||
// ignore embedded unexported non-struct fields.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
tag := parseTag(k.tag, sf)
|
||||
if tag.ignore {
|
||||
continue
|
||||
}
|
||||
if f.tag.prefix != "" {
|
||||
tag.prefix += f.tag.prefix
|
||||
}
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
newf := field{
|
||||
name: tag.prefix + tag.name,
|
||||
baseType: sf.Type,
|
||||
typ: ft,
|
||||
tag: tag,
|
||||
index: makeIndex(f.index, i),
|
||||
}
|
||||
|
||||
if sf.Anonymous && ft.Kind() == reflect.Struct && tag.empty {
|
||||
q = append(q, newf)
|
||||
continue
|
||||
}
|
||||
|
||||
if tag.inline && ft.Kind() == reflect.Struct {
|
||||
q = append(q, newf)
|
||||
continue
|
||||
}
|
||||
|
||||
fm.insert(newf)
|
||||
|
||||
// look for duplicate nodes on the same level. Nodes won't be
|
||||
// revisited, so write all fields for the current type now.
|
||||
for _, v := range q {
|
||||
if len(v.index) != depth {
|
||||
break
|
||||
}
|
||||
if v.typ == f.typ && v.tag.prefix == tag.prefix {
|
||||
// other nodes can have different path.
|
||||
fm.insert(field{
|
||||
name: tag.prefix + tag.name,
|
||||
baseType: sf.Type,
|
||||
typ: ft,
|
||||
tag: tag,
|
||||
index: makeIndex(v.index, i),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fm.fields()
|
||||
}
|
||||
|
||||
func makeIndex(index []int, v int) []int {
|
||||
out := make([]int, len(index), len(index)+1)
|
||||
copy(out, index)
|
||||
return append(out, v)
|
||||
}
|
||||
30
vendor/github.com/jszwec/csvutil/cache_go17.go
generated
vendored
Normal file
30
vendor/github.com/jszwec/csvutil/cache_go17.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// +build !go1.9
|
||||
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var fieldCache = struct {
|
||||
mtx sync.RWMutex
|
||||
m map[typeKey][]field
|
||||
}{m: make(map[typeKey][]field)}
|
||||
|
||||
func cachedFields(k typeKey) fields {
|
||||
fieldCache.mtx.RLock()
|
||||
fields, ok := fieldCache.m[k]
|
||||
fieldCache.mtx.RUnlock()
|
||||
|
||||
if ok {
|
||||
return fields
|
||||
}
|
||||
|
||||
fields = buildFields(k)
|
||||
|
||||
fieldCache.mtx.Lock()
|
||||
fieldCache.m[k] = fields
|
||||
fieldCache.mtx.Unlock()
|
||||
|
||||
return fields
|
||||
}
|
||||
18
vendor/github.com/jszwec/csvutil/cache_go19.go
generated
vendored
Normal file
18
vendor/github.com/jszwec/csvutil/cache_go19.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// +build go1.9
|
||||
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var fieldCache sync.Map // map[typeKey][]field
|
||||
|
||||
func cachedFields(k typeKey) fields {
|
||||
if v, ok := fieldCache.Load(k); ok {
|
||||
return v.(fields)
|
||||
}
|
||||
|
||||
v, _ := fieldCache.LoadOrStore(k, buildFields(k))
|
||||
return v.(fields)
|
||||
}
|
||||
223
vendor/github.com/jszwec/csvutil/csvutil.go
generated
vendored
Normal file
223
vendor/github.com/jszwec/csvutil/csvutil.go
generated
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const defaultTag = "csv"
|
||||
|
||||
var (
|
||||
_bytes = reflect.TypeOf(([]byte)(nil))
|
||||
_error = reflect.TypeOf((*error)(nil)).Elem()
|
||||
)
|
||||
|
||||
// Unmarshal parses the CSV-encoded data and stores the result in the slice or
|
||||
// the array pointed to by v. If v is nil or not a pointer to a struct slice or
|
||||
// struct array, Unmarshal returns an InvalidUnmarshalError.
|
||||
//
|
||||
// Unmarshal uses the std encoding/csv.Reader for parsing and csvutil.Decoder
|
||||
// for populating the struct elements in the provided slice. For exact decoding
|
||||
// rules look at the Decoder's documentation.
|
||||
//
|
||||
// The first line in data is treated as a header. Decoder will use it to map
|
||||
// csv columns to struct's fields.
|
||||
//
|
||||
// In case of success the provided slice will be reinitialized and its content
|
||||
// fully replaced with decoded data.
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
val := reflect.ValueOf(v)
|
||||
|
||||
if val.Kind() != reflect.Ptr || val.IsNil() {
|
||||
return &InvalidUnmarshalError{Type: reflect.TypeOf(v)}
|
||||
}
|
||||
|
||||
switch val.Type().Elem().Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
default:
|
||||
return &InvalidUnmarshalError{Type: val.Type()}
|
||||
}
|
||||
|
||||
typ := val.Type().Elem()
|
||||
|
||||
if walkType(typ.Elem()).Kind() != reflect.Struct {
|
||||
return &InvalidUnmarshalError{Type: val.Type()}
|
||||
}
|
||||
|
||||
dec, err := NewDecoder(newCSVReader(bytes.NewReader(data)))
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// for the array just call decodeArray directly; for slice values call the
|
||||
// optimized code for better performance.
|
||||
|
||||
if typ.Kind() == reflect.Array {
|
||||
return dec.decodeArray(val.Elem())
|
||||
}
|
||||
|
||||
c := countRecords(data)
|
||||
slice := reflect.MakeSlice(typ, c, c)
|
||||
|
||||
var i int
|
||||
for ; ; i++ {
|
||||
// just in case countRecords counts it wrong.
|
||||
if i >= c && i >= slice.Len() {
|
||||
slice = reflect.Append(slice, reflect.New(typ.Elem()).Elem())
|
||||
}
|
||||
|
||||
if err := dec.Decode(slice.Index(i).Addr().Interface()); err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
val.Elem().Set(slice.Slice3(0, i, i))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal returns the CSV encoding of slice or array v. If v is not a slice or
|
||||
// elements are not structs then Marshal returns InvalidMarshalError.
|
||||
//
|
||||
// Marshal uses the std encoding/csv.Writer with its default settings for csv
|
||||
// encoding.
|
||||
//
|
||||
// Marshal will always encode the CSV header even for the empty slice.
|
||||
//
|
||||
// For the exact encoding rules look at Encoder.Encode method.
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
val := walkValue(reflect.ValueOf(v))
|
||||
|
||||
if !val.IsValid() {
|
||||
return nil, &InvalidMarshalError{}
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return nil, &InvalidMarshalError{Type: reflect.ValueOf(v).Type()}
|
||||
}
|
||||
|
||||
typ := walkType(val.Type().Elem())
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return nil, &InvalidMarshalError{Type: reflect.ValueOf(v).Type()}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := csv.NewWriter(&buf)
|
||||
enc := NewEncoder(w)
|
||||
|
||||
if err := enc.encodeHeader(typ); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := enc.encodeArray(val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
if err := w.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func countRecords(s []byte) (n int) {
|
||||
var prev byte
|
||||
inQuote := false
|
||||
for {
|
||||
if len(s) == 0 && prev != '"' {
|
||||
return n
|
||||
}
|
||||
|
||||
i := bytes.IndexAny(s, "\n\"")
|
||||
if i == -1 {
|
||||
return n + 1
|
||||
}
|
||||
|
||||
switch s[i] {
|
||||
case '\n':
|
||||
if !inQuote && (i > 0 || prev == '"') {
|
||||
n++
|
||||
}
|
||||
case '"':
|
||||
inQuote = !inQuote
|
||||
}
|
||||
|
||||
prev = s[i]
|
||||
s = s[i+1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Header scans the provided struct type and generates a CSV header for it.
|
||||
//
|
||||
// Field names are written in the same order as struct fields are defined.
|
||||
// Embedded struct's fields are treated as if they were part of the outer struct.
|
||||
// Fields that are embedded types and that are tagged are treated like any
|
||||
// other field.
|
||||
//
|
||||
// Unexported fields and fields with tag "-" are ignored.
|
||||
//
|
||||
// Tagged fields have the priority over non tagged fields with the same name.
|
||||
//
|
||||
// Following the Go visibility rules if there are multiple fields with the same
|
||||
// name (tagged or not tagged) on the same level and choice between them is
|
||||
// ambiguous, then all these fields will be ignored.
|
||||
//
|
||||
// It is a good practice to call Header once for each type. The suitable place
|
||||
// for calling it is init function. Look at Decoder.DecodingDataWithNoHeader
|
||||
// example.
|
||||
//
|
||||
// If tag is left empty the default "csv" will be used.
|
||||
//
|
||||
// Header will return UnsupportedTypeError if the provided value is nil or is
|
||||
// not a struct.
|
||||
func Header(v interface{}, tag string) ([]string, error) {
|
||||
typ, err := valueType(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
tag = defaultTag
|
||||
}
|
||||
|
||||
fields := cachedFields(typeKey{tag, typ})
|
||||
h := make([]string, len(fields))
|
||||
for i, f := range fields {
|
||||
h[i] = f.name
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func valueType(v interface{}) (reflect.Type, error) {
|
||||
val := reflect.ValueOf(v)
|
||||
if !val.IsValid() {
|
||||
return nil, &UnsupportedTypeError{}
|
||||
}
|
||||
|
||||
loop:
|
||||
for {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
el := val.Elem()
|
||||
if !el.IsValid() {
|
||||
break loop
|
||||
}
|
||||
val = el
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
typ := walkType(val.Type())
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return nil, &UnsupportedTypeError{Type: typ}
|
||||
}
|
||||
return typ, nil
|
||||
}
|
||||
12
vendor/github.com/jszwec/csvutil/csvutil_go17.go
generated
vendored
Normal file
12
vendor/github.com/jszwec/csvutil/csvutil_go17.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !go1.9
|
||||
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
)
|
||||
|
||||
func newCSVReader(r io.Reader) *csv.Reader {
|
||||
return csv.NewReader(r)
|
||||
}
|
||||
14
vendor/github.com/jszwec/csvutil/csvutil_go19.go
generated
vendored
Normal file
14
vendor/github.com/jszwec/csvutil/csvutil_go19.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build go1.9
|
||||
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
)
|
||||
|
||||
func newCSVReader(r io.Reader) *csv.Reader {
|
||||
rr := csv.NewReader(r)
|
||||
rr.ReuseRecord = true
|
||||
return rr
|
||||
}
|
||||
247
vendor/github.com/jszwec/csvutil/decode.go
generated
vendored
Normal file
247
vendor/github.com/jszwec/csvutil/decode.go
generated
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
textUnmarshaler = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
csvUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
var intDecoders = map[int]decodeFunc{
|
||||
8: decodeIntN(8),
|
||||
16: decodeIntN(16),
|
||||
32: decodeIntN(32),
|
||||
64: decodeIntN(64),
|
||||
}
|
||||
|
||||
var uintDecoders = map[int]decodeFunc{
|
||||
8: decodeUintN(8),
|
||||
16: decodeUintN(16),
|
||||
32: decodeUintN(32),
|
||||
64: decodeUintN(64),
|
||||
}
|
||||
|
||||
var (
|
||||
decodeFloat32 = decodeFloatN(32)
|
||||
decodeFloat64 = decodeFloatN(64)
|
||||
)
|
||||
|
||||
type decodeFunc func(s string, v reflect.Value) error
|
||||
|
||||
func decodeFuncValue(f reflect.Value) decodeFunc {
|
||||
isIface := f.Type().In(1).Kind() == reflect.Interface
|
||||
|
||||
return func(s string, v reflect.Value) error {
|
||||
if isIface && v.Type().Kind() == reflect.Interface && v.IsNil() {
|
||||
return &UnmarshalTypeError{Value: s, Type: v.Type()}
|
||||
}
|
||||
|
||||
out := f.Call([]reflect.Value{
|
||||
reflect.ValueOf([]byte(s)),
|
||||
v,
|
||||
})
|
||||
err, _ := out[0].Interface().(error)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func decodeFuncValuePtr(f reflect.Value) decodeFunc {
|
||||
return func(s string, v reflect.Value) error {
|
||||
out := f.Call([]reflect.Value{
|
||||
reflect.ValueOf([]byte(s)),
|
||||
v.Addr(),
|
||||
})
|
||||
err, _ := out[0].Interface().(error)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func decodeString(s string, v reflect.Value) error {
|
||||
v.SetString(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeIntN(bits int) decodeFunc {
|
||||
return func(s string, v reflect.Value) error {
|
||||
n, err := strconv.ParseInt(s, 10, bits)
|
||||
if err != nil {
|
||||
return &UnmarshalTypeError{Value: s, Type: v.Type()}
|
||||
}
|
||||
v.SetInt(n)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func decodeUintN(bits int) decodeFunc {
|
||||
return func(s string, v reflect.Value) error {
|
||||
n, err := strconv.ParseUint(s, 10, bits)
|
||||
if err != nil {
|
||||
return &UnmarshalTypeError{Value: s, Type: v.Type()}
|
||||
}
|
||||
v.SetUint(n)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func decodeFloatN(bits int) decodeFunc {
|
||||
return func(s string, v reflect.Value) error {
|
||||
n, err := strconv.ParseFloat(s, bits)
|
||||
if err != nil {
|
||||
return &UnmarshalTypeError{Value: s, Type: v.Type()}
|
||||
}
|
||||
v.SetFloat(n)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func decodeBool(s string, v reflect.Value) error {
|
||||
b, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return &UnmarshalTypeError{Value: s, Type: v.Type()}
|
||||
}
|
||||
v.SetBool(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodePtrTextUnmarshaler(s string, v reflect.Value) error {
|
||||
return decodeTextUnmarshaler(s, v.Addr())
|
||||
}
|
||||
|
||||
func decodeTextUnmarshaler(s string, v reflect.Value) error {
|
||||
return v.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(s))
|
||||
}
|
||||
|
||||
func decodePtrFieldUnmarshaler(s string, v reflect.Value) error {
|
||||
return decodeFieldUnmarshaler(s, v.Addr())
|
||||
}
|
||||
|
||||
func decodeFieldUnmarshaler(s string, v reflect.Value) error {
|
||||
return v.Interface().(Unmarshaler).UnmarshalCSV([]byte(s))
|
||||
}
|
||||
|
||||
func decodePtr(typ reflect.Type, funcMap map[reflect.Type]reflect.Value, ifaceFuncs []reflect.Value) (decodeFunc, error) {
|
||||
next, err := decodeFn(typ.Elem(), funcMap, ifaceFuncs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(s string, v reflect.Value) error {
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
return next(s, v.Elem())
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeInterface(funcMap map[reflect.Type]reflect.Value, ifaceFuncs []reflect.Value) decodeFunc {
|
||||
return func(s string, v reflect.Value) error {
|
||||
if v.NumMethod() != 0 {
|
||||
return &UnmarshalTypeError{
|
||||
Value: s,
|
||||
Type: v.Type(),
|
||||
}
|
||||
}
|
||||
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
el := walkValue(v)
|
||||
if !el.CanSet() {
|
||||
if el.IsValid() {
|
||||
// we may get a value receiver unmarshalers or registered funcs
|
||||
// underneath the interface in which case we should call
|
||||
// Unmarshal/Registered func.
|
||||
typ := el.Type()
|
||||
if f, ok := funcMap[typ]; ok {
|
||||
return decodeFuncValue(f)(s, el)
|
||||
}
|
||||
for _, f := range ifaceFuncs {
|
||||
if typ.AssignableTo(f.Type().In(1)) {
|
||||
return decodeFuncValue(f)(s, el)
|
||||
}
|
||||
}
|
||||
if typ.Implements(csvUnmarshaler) {
|
||||
return decodeFieldUnmarshaler(s, el)
|
||||
}
|
||||
if typ.Implements(textUnmarshaler) {
|
||||
return decodeTextUnmarshaler(s, el)
|
||||
}
|
||||
}
|
||||
v.Set(reflect.ValueOf(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
fn, err := decodeFn(el.Type(), funcMap, ifaceFuncs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fn(s, el)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeBytes(s string, v reflect.Value) error {
|
||||
b, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetBytes(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeFn(typ reflect.Type, funcMap map[reflect.Type]reflect.Value, ifaceFuncs []reflect.Value) (decodeFunc, error) {
|
||||
if f, ok := funcMap[typ]; ok {
|
||||
return decodeFuncValue(f), nil
|
||||
}
|
||||
if f, ok := funcMap[reflect.PtrTo(typ)]; ok {
|
||||
return decodeFuncValuePtr(f), nil
|
||||
}
|
||||
|
||||
for _, f := range ifaceFuncs {
|
||||
argType := f.Type().In(1)
|
||||
if typ.AssignableTo(argType) {
|
||||
return decodeFuncValue(f), nil
|
||||
}
|
||||
if reflect.PtrTo(typ).AssignableTo(argType) {
|
||||
return decodeFuncValuePtr(f), nil
|
||||
}
|
||||
}
|
||||
|
||||
if reflect.PtrTo(typ).Implements(csvUnmarshaler) {
|
||||
return decodePtrFieldUnmarshaler, nil
|
||||
}
|
||||
if reflect.PtrTo(typ).Implements(textUnmarshaler) {
|
||||
return decodePtrTextUnmarshaler, nil
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
return decodePtr(typ, funcMap, ifaceFuncs)
|
||||
case reflect.Interface:
|
||||
return decodeInterface(funcMap, ifaceFuncs), nil
|
||||
case reflect.String:
|
||||
return decodeString, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return intDecoders[typ.Bits()], nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return uintDecoders[typ.Bits()], nil
|
||||
case reflect.Float32:
|
||||
return decodeFloat32, nil
|
||||
case reflect.Float64:
|
||||
return decodeFloat64, nil
|
||||
case reflect.Bool:
|
||||
return decodeBool, nil
|
||||
case reflect.Slice:
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
return decodeBytes, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, &UnsupportedTypeError{Type: typ}
|
||||
}
|
||||
487
vendor/github.com/jszwec/csvutil/decoder.go
generated
vendored
Normal file
487
vendor/github.com/jszwec/csvutil/decoder.go
generated
vendored
Normal file
@@ -0,0 +1,487 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type decField struct {
|
||||
columnIndex int
|
||||
field
|
||||
decodeFunc
|
||||
zero interface{}
|
||||
}
|
||||
|
||||
// A Decoder reads and decodes string records into structs.
|
||||
type Decoder struct {
|
||||
// Tag defines which key in the struct field's tag to scan for names and
|
||||
// options (Default: 'csv').
|
||||
Tag string
|
||||
|
||||
// If true, Decoder will return a MissingColumnsError if it discovers
|
||||
// that any of the columns are missing. This means that a CSV input
|
||||
// will be required to contain all columns that were defined in the
|
||||
// provided struct.
|
||||
DisallowMissingColumns bool
|
||||
|
||||
// If not nil, Map is a function that is called for each field in the csv
|
||||
// record before decoding the data. It allows mapping certain string values
|
||||
// for specific columns or types to a known format. Decoder calls Map with
|
||||
// the current column name (taken from header) and a zero non-pointer value
|
||||
// of a type to which it is going to decode data into. Implementations
|
||||
// should use type assertions to recognize the type.
|
||||
//
|
||||
// The good example of use case for Map is if NaN values are represented by
|
||||
// eg 'n/a' string, implementing a specific Map function for all floats
|
||||
// could map 'n/a' back into 'NaN' to allow successful decoding.
|
||||
//
|
||||
// Use Map with caution. If the requirements of column or type are not met
|
||||
// Map should return 'field', since it is the original value that was
|
||||
// read from the csv input, this would indicate no change.
|
||||
//
|
||||
// If struct field is an interface v will be of type string, unless the
|
||||
// struct field contains a settable pointer value - then v will be a zero
|
||||
// value of that type.
|
||||
//
|
||||
// Map must be set before the first call to Decode and not changed after it.
|
||||
Map func(field, col string, v interface{}) string
|
||||
|
||||
r Reader
|
||||
typeKey typeKey
|
||||
hmap map[string]int
|
||||
header []string
|
||||
record []string
|
||||
cache []decField
|
||||
unused []int
|
||||
funcMap map[reflect.Type]reflect.Value
|
||||
ifaceFuncs []reflect.Value
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
//
|
||||
// Decoder will match struct fields according to the given header.
|
||||
//
|
||||
// If header is empty NewDecoder will read one line and treat it as a header.
|
||||
//
|
||||
// Records coming from r must be of the same length as the header.
|
||||
//
|
||||
// NewDecoder may return io.EOF if there is no data in r and no header was
|
||||
// provided by the caller.
|
||||
func NewDecoder(r Reader, header ...string) (dec *Decoder, err error) {
|
||||
if len(header) == 0 {
|
||||
header, err = r.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
h := make([]string, len(header))
|
||||
copy(h, header)
|
||||
header = h
|
||||
|
||||
m := make(map[string]int, len(header))
|
||||
for i, h := range header {
|
||||
m[h] = i
|
||||
}
|
||||
|
||||
return &Decoder{
|
||||
r: r,
|
||||
header: header,
|
||||
hmap: m,
|
||||
unused: make([]int, 0, len(header)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Decode reads the next string record or records from its input and stores it
|
||||
// in the value pointed to by v which must be a pointer to a struct, struct slice
|
||||
// or struct array.
|
||||
//
|
||||
// Decode matches all exported struct fields based on the header. Struct fields
|
||||
// can be adjusted by using tags.
|
||||
//
|
||||
// The "omitempty" option specifies that the field should be omitted from
|
||||
// the decoding if record's field is an empty string.
|
||||
//
|
||||
// Examples of struct field tags and their meanings:
|
||||
// // Decode matches this field with "myName" header column.
|
||||
// Field int `csv:"myName"`
|
||||
//
|
||||
// // Decode matches this field with "Field" header column.
|
||||
// Field int
|
||||
//
|
||||
// // Decode matches this field with "myName" header column and decoding is not
|
||||
// // called if record's field is an empty string.
|
||||
// Field int `csv:"myName,omitempty"`
|
||||
//
|
||||
// // Decode matches this field with "Field" header column and decoding is not
|
||||
// // called if record's field is an empty string.
|
||||
// Field int `csv:",omitempty"`
|
||||
//
|
||||
// // Decode ignores this field.
|
||||
// Field int `csv:"-"`
|
||||
//
|
||||
// // Decode treats this field exactly as if it was an embedded field and
|
||||
// // matches header columns that start with "my_prefix_" to all fields of this
|
||||
// // type.
|
||||
// Field Struct `csv:"my_prefix_,inline"`
|
||||
//
|
||||
// // Decode treats this field exactly as if it was an embedded field.
|
||||
// Field Struct `csv:",inline"`
|
||||
//
|
||||
// By default decode looks for "csv" tag, but this can be changed by setting
|
||||
// Decoder.Tag field.
|
||||
//
|
||||
// To Decode into a custom type v must implement csvutil.Unmarshaler or
|
||||
// encoding.TextUnmarshaler.
|
||||
//
|
||||
// Anonymous struct fields with tags are treated like normal fields and they
|
||||
// must implement csvutil.Unmarshaler or encoding.TextUnmarshaler unless inline
|
||||
// tag is specified.
|
||||
//
|
||||
// Anonymous struct fields without tags are populated just as if they were
|
||||
// part of the main struct. However, fields in the main struct have bigger
|
||||
// priority and they are populated first. If main struct and anonymous struct
|
||||
// field have the same fields, the main struct's fields will be populated.
|
||||
//
|
||||
// Fields of type []byte expect the data to be base64 encoded strings.
|
||||
//
|
||||
// Float fields are decoded to NaN if a string value is 'NaN'. This check
|
||||
// is case insensitive.
|
||||
//
|
||||
// Interface fields are decoded to strings unless they contain settable pointer
|
||||
// value.
|
||||
//
|
||||
// Pointer fields are decoded to nil if a string value is empty.
|
||||
//
|
||||
// If v is a slice, Decode resets it and reads the input until EOF, storing all
|
||||
// decoded values in the given slice. Decode returns nil on EOF.
|
||||
//
|
||||
// If v is an array, Decode reads the input until EOF or until it decodes all
|
||||
// corresponding array elements. If the input contains less elements than the
|
||||
// array, the additional Go array elements are set to zero values. Decode
|
||||
// returns nil on EOF unless there were no records decoded.
|
||||
//
|
||||
// Fields with inline tags that have a non-empty prefix must not be cyclic
|
||||
// structures. Passing such values to Decode will result in an infinite loop.
|
||||
func (d *Decoder) Decode(v interface{}) (err error) {
|
||||
val := reflect.ValueOf(v)
|
||||
if val.Kind() != reflect.Ptr || val.IsNil() {
|
||||
return &InvalidDecodeError{Type: reflect.TypeOf(v)}
|
||||
}
|
||||
|
||||
elem := indirect(val.Elem())
|
||||
switch elem.Kind() {
|
||||
case reflect.Struct:
|
||||
return d.decodeStruct(elem)
|
||||
case reflect.Slice:
|
||||
return d.decodeSlice(elem)
|
||||
case reflect.Array:
|
||||
return d.decodeArray(elem)
|
||||
case reflect.Interface, reflect.Invalid:
|
||||
elem = walkValue(elem)
|
||||
if elem.Kind() != reflect.Invalid {
|
||||
return &InvalidDecodeError{Type: elem.Type()}
|
||||
}
|
||||
return &InvalidDecodeError{Type: val.Type()}
|
||||
default:
|
||||
return &InvalidDecodeError{Type: reflect.PtrTo(elem.Type())}
|
||||
}
|
||||
}
|
||||
|
||||
// Record returns the most recently read record. The slice is valid until the
|
||||
// next call to Decode.
|
||||
func (d *Decoder) Record() []string {
|
||||
return d.record
|
||||
}
|
||||
|
||||
// Header returns the first line that came from the reader, or returns the
|
||||
// defined header by the caller.
|
||||
func (d *Decoder) Header() []string {
|
||||
header := make([]string, len(d.header))
|
||||
copy(header, d.header)
|
||||
return header
|
||||
}
|
||||
|
||||
// Unused returns a list of column indexes that were not used during decoding
|
||||
// due to lack of matching struct field.
|
||||
func (d *Decoder) Unused() []int {
|
||||
if len(d.unused) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
indices := make([]int, len(d.unused))
|
||||
copy(indices, d.unused)
|
||||
return indices
|
||||
}
|
||||
|
||||
// Register registers a custom decoding function for a concrete type or interface.
|
||||
// The argument f must be of type:
|
||||
// func([]byte, T) error
|
||||
//
|
||||
// T must be a concrete type such as *time.Time, or interface that has at least one
|
||||
// method.
|
||||
//
|
||||
// During decoding, fields are matched by the concrete type first. If match is not
|
||||
// found then Decoder looks if field implements any of the registered interfaces
|
||||
// in order they were registered.
|
||||
//
|
||||
// Register panics if:
|
||||
// - f does not match the right signature
|
||||
// - f is an empty interface
|
||||
// - f was already registered
|
||||
//
|
||||
// Register is based on the encoding/json proposal:
|
||||
// https://github.com/golang/go/issues/5901.
|
||||
func (d *Decoder) Register(f interface{}) {
|
||||
v := reflect.ValueOf(f)
|
||||
typ := v.Type()
|
||||
|
||||
if typ.Kind() != reflect.Func ||
|
||||
typ.NumIn() != 2 || typ.NumOut() != 1 ||
|
||||
typ.In(0) != _bytes || typ.Out(0) != _error {
|
||||
panic("csvutil: func must be of type func([]byte, T) error")
|
||||
}
|
||||
|
||||
argType := typ.In(1)
|
||||
|
||||
if argType.Kind() == reflect.Interface && argType.NumMethod() == 0 {
|
||||
panic("csvutil: func argument type must not be an empty interface")
|
||||
}
|
||||
|
||||
if d.funcMap == nil {
|
||||
d.funcMap = make(map[reflect.Type]reflect.Value)
|
||||
}
|
||||
|
||||
if _, ok := d.funcMap[argType]; ok {
|
||||
panic("csvutil: func " + typ.String() + " already registered")
|
||||
}
|
||||
|
||||
d.funcMap[argType] = v
|
||||
|
||||
if argType.Kind() == reflect.Interface {
|
||||
d.ifaceFuncs = append(d.ifaceFuncs, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeSlice(slice reflect.Value) error {
|
||||
typ := slice.Type().Elem()
|
||||
if walkType(typ).Kind() != reflect.Struct {
|
||||
return &InvalidDecodeError{Type: reflect.PtrTo(slice.Type())}
|
||||
}
|
||||
|
||||
slice.SetLen(0)
|
||||
|
||||
var c int
|
||||
for ; ; c++ {
|
||||
v := reflect.New(typ)
|
||||
|
||||
err := d.decodeStruct(indirect(v))
|
||||
if err == io.EOF {
|
||||
if c == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// we want to ensure that we append this element to the slice even if it
|
||||
// was partially decoded due to error. This is how JSON pkg does it.
|
||||
slice.Set(reflect.Append(slice, v.Elem()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
slice.Set(slice.Slice3(0, c, c))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeArray(v reflect.Value) error {
|
||||
if walkType(v.Type().Elem()).Kind() != reflect.Struct {
|
||||
return &InvalidDecodeError{Type: reflect.PtrTo(v.Type())}
|
||||
}
|
||||
|
||||
l := v.Len()
|
||||
|
||||
var i int
|
||||
for ; i < l; i++ {
|
||||
if err := d.decodeStruct(indirect(v.Index(i))); err == io.EOF {
|
||||
if i == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
zero := reflect.Zero(v.Type().Elem())
|
||||
for i := i; i < l; i++ {
|
||||
v.Index(i).Set(zero)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeStruct(v reflect.Value) (err error) {
|
||||
d.record, err = d.r.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(d.record) != len(d.header) {
|
||||
return ErrFieldCount
|
||||
}
|
||||
|
||||
return d.unmarshal(d.record, v)
|
||||
}
|
||||
|
||||
func (d *Decoder) unmarshal(record []string, v reflect.Value) error {
|
||||
fields, err := d.fields(typeKey{d.tag(), v.Type()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fieldLoop:
|
||||
for _, f := range fields {
|
||||
isBlank := record[f.columnIndex] == ""
|
||||
if f.tag.omitEmpty && isBlank {
|
||||
continue
|
||||
}
|
||||
|
||||
fv := v
|
||||
for n, i := range f.index {
|
||||
fv = fv.Field(i)
|
||||
if fv.Kind() == reflect.Ptr {
|
||||
if fv.IsNil() {
|
||||
if isBlank && n == len(f.index)-1 { // ensure we are on the leaf.
|
||||
continue fieldLoop
|
||||
}
|
||||
// this can happen if a field is an unexported embedded
|
||||
// pointer type. In Go prior to 1.10 it was possible to
|
||||
// set such value because of a bug in the reflect package
|
||||
// https://github.com/golang/go/issues/21353
|
||||
if !fv.CanSet() {
|
||||
return errPtrUnexportedStruct(fv.Type())
|
||||
}
|
||||
fv.Set(reflect.New(fv.Type().Elem()))
|
||||
}
|
||||
|
||||
if isBlank && n == len(f.index)-1 { // ensure we are on the leaf.
|
||||
fv.Set(reflect.Zero(fv.Type()))
|
||||
continue fieldLoop
|
||||
}
|
||||
|
||||
if n != len(f.index)-1 {
|
||||
fv = fv.Elem() // walk pointer until we are on the the leaf.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s := record[f.columnIndex]
|
||||
if d.Map != nil && f.zero != nil {
|
||||
zero := f.zero
|
||||
if fv := walkPtr(fv); fv.Kind() == reflect.Interface && !fv.IsNil() {
|
||||
if v := walkValue(fv); v.CanSet() {
|
||||
zero = reflect.Zero(v.Type()).Interface()
|
||||
}
|
||||
}
|
||||
s = d.Map(s, d.header[f.columnIndex], zero)
|
||||
}
|
||||
|
||||
if err := f.decodeFunc(s, fv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) fields(k typeKey) ([]decField, error) {
|
||||
if k == d.typeKey {
|
||||
return d.cache, nil
|
||||
}
|
||||
|
||||
var (
|
||||
fields = cachedFields(k)
|
||||
decFields = make([]decField, 0, len(fields))
|
||||
used = make([]bool, len(d.header))
|
||||
missingCols []string
|
||||
)
|
||||
for _, f := range fields {
|
||||
i, ok := d.hmap[f.name]
|
||||
if !ok {
|
||||
if d.DisallowMissingColumns {
|
||||
missingCols = append(missingCols, f.name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
fn, err := decodeFn(f.baseType, d.funcMap, d.ifaceFuncs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
df := decField{
|
||||
columnIndex: i,
|
||||
field: f,
|
||||
decodeFunc: fn,
|
||||
}
|
||||
|
||||
if d.Map != nil {
|
||||
switch f.typ.Kind() {
|
||||
case reflect.Interface:
|
||||
df.zero = "" // interface values are decoded to strings
|
||||
default:
|
||||
df.zero = reflect.Zero(walkType(f.typ)).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
decFields = append(decFields, df)
|
||||
used[i] = true
|
||||
}
|
||||
|
||||
if len(missingCols) > 0 {
|
||||
return nil, &MissingColumnsError{
|
||||
Columns: missingCols,
|
||||
}
|
||||
}
|
||||
|
||||
d.unused = d.unused[:0]
|
||||
for i, b := range used {
|
||||
if !b {
|
||||
d.unused = append(d.unused, i)
|
||||
}
|
||||
}
|
||||
|
||||
d.cache, d.typeKey = decFields, k
|
||||
return d.cache, nil
|
||||
}
|
||||
|
||||
func (d *Decoder) tag() string {
|
||||
if d.Tag == "" {
|
||||
return defaultTag
|
||||
}
|
||||
return d.Tag
|
||||
}
|
||||
|
||||
func indirect(v reflect.Value) reflect.Value {
|
||||
for {
|
||||
switch v.Kind() {
|
||||
case reflect.Interface:
|
||||
if v.IsNil() {
|
||||
return v
|
||||
}
|
||||
e := v.Elem()
|
||||
if e.Kind() == reflect.Ptr && !e.IsNil() {
|
||||
v = e
|
||||
continue
|
||||
}
|
||||
return v
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
v = v.Elem()
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
6
vendor/github.com/jszwec/csvutil/doc.go
generated
vendored
Normal file
6
vendor/github.com/jszwec/csvutil/doc.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Package csvutil provides fast and idiomatic mapping between CSV and Go values.
|
||||
//
|
||||
// This package does not provide a CSV parser itself, it is based on the Reader and Writer
|
||||
// interfaces which are implemented by eg. std csv package. This gives a possibility
|
||||
// of choosing any other CSV writer or reader which may be more performant.
|
||||
package csvutil
|
||||
245
vendor/github.com/jszwec/csvutil/encode.go
generated
vendored
Normal file
245
vendor/github.com/jszwec/csvutil/encode.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
textMarshaler = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||
csvMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
var (
|
||||
encodeFloat32 = encodeFloatN(32)
|
||||
encodeFloat64 = encodeFloatN(64)
|
||||
)
|
||||
|
||||
type encodeFunc func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error)
|
||||
|
||||
func encodeFuncValue(fn reflect.Value) encodeFunc {
|
||||
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
out := fn.Call([]reflect.Value{v})
|
||||
err, _ := out[1].Interface().(error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(buf, out[0].Bytes()...), nil
|
||||
}
|
||||
}
|
||||
|
||||
func encodeFuncValuePtr(fn reflect.Value) encodeFunc {
|
||||
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
if !v.CanAddr() {
|
||||
fallback, err := encodeFn(v.Type(), false, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fallback(buf, v, omitempty)
|
||||
}
|
||||
|
||||
out := fn.Call([]reflect.Value{v.Addr()})
|
||||
err, _ := out[1].Interface().(error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(buf, out[0].Bytes()...), nil
|
||||
}
|
||||
}
|
||||
|
||||
func encodeString(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
return append(buf, v.String()...), nil
|
||||
}
|
||||
|
||||
func encodeInt(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
n := v.Int()
|
||||
if n == 0 && omitempty {
|
||||
return buf, nil
|
||||
}
|
||||
return strconv.AppendInt(buf, n, 10), nil
|
||||
}
|
||||
|
||||
func encodeUint(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
n := v.Uint()
|
||||
if n == 0 && omitempty {
|
||||
return buf, nil
|
||||
}
|
||||
return strconv.AppendUint(buf, n, 10), nil
|
||||
}
|
||||
|
||||
func encodeFloatN(bits int) encodeFunc {
|
||||
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
f := v.Float()
|
||||
if f == 0 && omitempty {
|
||||
return buf, nil
|
||||
}
|
||||
return strconv.AppendFloat(buf, f, 'G', -1, bits), nil
|
||||
}
|
||||
}
|
||||
|
||||
func encodeBool(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
t := v.Bool()
|
||||
if !t && omitempty {
|
||||
return buf, nil
|
||||
}
|
||||
return strconv.AppendBool(buf, t), nil
|
||||
}
|
||||
|
||||
func encodeInterface(funcMap map[reflect.Type]reflect.Value, funcs []reflect.Value) encodeFunc {
|
||||
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
if !v.IsValid() || v.IsNil() || !v.Elem().IsValid() {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
v = v.Elem()
|
||||
canAddr := v.Kind() == reflect.Ptr
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if v.IsNil() {
|
||||
return buf, nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
enc, err := encodeFn(v.Type(), canAddr, funcMap, funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return enc(buf, v, omitempty)
|
||||
}
|
||||
}
|
||||
|
||||
func encodePtrMarshaler(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
if v.CanAddr() {
|
||||
return encodeMarshaler(buf, v.Addr(), omitempty)
|
||||
}
|
||||
|
||||
fallback, err := encodeFn(v.Type(), false, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fallback(buf, v, omitempty)
|
||||
}
|
||||
|
||||
func encodeTextMarshaler(buf []byte, v reflect.Value, _ bool) ([]byte, error) {
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
b, err := v.Interface().(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: v.Type(), MarshalerType: "MarshalText", Err: err}
|
||||
}
|
||||
return append(buf, b...), nil
|
||||
}
|
||||
|
||||
func encodePtrTextMarshaler(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
if v.CanAddr() {
|
||||
return encodeTextMarshaler(buf, v.Addr(), omitempty)
|
||||
}
|
||||
|
||||
fallback, err := encodeFn(v.Type(), false, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fallback(buf, v, omitempty)
|
||||
}
|
||||
|
||||
func encodeMarshaler(buf []byte, v reflect.Value, _ bool) ([]byte, error) {
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
b, err := v.Interface().(Marshaler).MarshalCSV()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: v.Type(), MarshalerType: "MarshalCSV", Err: err}
|
||||
}
|
||||
return append(buf, b...), nil
|
||||
}
|
||||
|
||||
func encodePtr(typ reflect.Type, canAddr bool, funcMap map[reflect.Type]reflect.Value, funcs []reflect.Value) (encodeFunc, error) {
|
||||
next, err := encodeFn(typ.Elem(), canAddr, funcMap, funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(buf []byte, v reflect.Value, omitempty bool) ([]byte, error) {
|
||||
if v.IsNil() {
|
||||
return buf, nil
|
||||
}
|
||||
return next(buf, v.Elem(), omitempty)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodeBytes(buf []byte, v reflect.Value, _ bool) ([]byte, error) {
|
||||
data := v.Bytes()
|
||||
|
||||
l := len(buf)
|
||||
buf = append(buf, make([]byte, base64.StdEncoding.EncodedLen(len(data)))...)
|
||||
base64.StdEncoding.Encode(buf[l:], data)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func encodeFn(typ reflect.Type, canAddr bool, funcMap map[reflect.Type]reflect.Value, funcs []reflect.Value) (encodeFunc, error) {
|
||||
if v, ok := funcMap[typ]; ok {
|
||||
return encodeFuncValue(v), nil
|
||||
}
|
||||
|
||||
if v, ok := funcMap[reflect.PtrTo(typ)]; ok && canAddr {
|
||||
return encodeFuncValuePtr(v), nil
|
||||
}
|
||||
|
||||
for _, v := range funcs {
|
||||
argType := v.Type().In(0)
|
||||
if typ.AssignableTo(argType) {
|
||||
return encodeFuncValue(v), nil
|
||||
}
|
||||
|
||||
if canAddr && reflect.PtrTo(typ).AssignableTo(argType) {
|
||||
return encodeFuncValuePtr(v), nil
|
||||
}
|
||||
}
|
||||
|
||||
if typ.Implements(csvMarshaler) {
|
||||
return encodeMarshaler, nil
|
||||
}
|
||||
|
||||
if canAddr && reflect.PtrTo(typ).Implements(csvMarshaler) {
|
||||
return encodePtrMarshaler, nil
|
||||
}
|
||||
|
||||
if typ.Implements(textMarshaler) {
|
||||
return encodeTextMarshaler, nil
|
||||
}
|
||||
|
||||
if canAddr && reflect.PtrTo(typ).Implements(textMarshaler) {
|
||||
return encodePtrTextMarshaler, nil
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
return encodeString, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return encodeInt, nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return encodeUint, nil
|
||||
case reflect.Float32:
|
||||
return encodeFloat32, nil
|
||||
case reflect.Float64:
|
||||
return encodeFloat64, nil
|
||||
case reflect.Bool:
|
||||
return encodeBool, nil
|
||||
case reflect.Interface:
|
||||
return encodeInterface(funcMap, funcs), nil
|
||||
case reflect.Ptr:
|
||||
return encodePtr(typ, canAddr, funcMap, funcs)
|
||||
case reflect.Slice:
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
return encodeBytes, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, &UnsupportedTypeError{Type: typ}
|
||||
}
|
||||
356
vendor/github.com/jszwec/csvutil/encoder.go
generated
vendored
Normal file
356
vendor/github.com/jszwec/csvutil/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const defaultBufSize = 4096
|
||||
|
||||
type encField struct {
|
||||
field
|
||||
encodeFunc
|
||||
}
|
||||
|
||||
type encCache struct {
|
||||
fields []encField
|
||||
buf []byte
|
||||
index []int
|
||||
record []string
|
||||
}
|
||||
|
||||
func newEncCache(k typeKey, funcMap map[reflect.Type]reflect.Value, funcs []reflect.Value) (_ *encCache, err error) {
|
||||
fields := cachedFields(k)
|
||||
encFields := make([]encField, len(fields))
|
||||
|
||||
for i, f := range fields {
|
||||
fn, err := encodeFn(f.baseType, true, funcMap, funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encFields[i] = encField{
|
||||
field: f,
|
||||
encodeFunc: fn,
|
||||
}
|
||||
}
|
||||
return &encCache{
|
||||
fields: encFields,
|
||||
buf: make([]byte, 0, defaultBufSize),
|
||||
index: make([]int, len(encFields)),
|
||||
record: make([]string, len(encFields)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encoder writes structs CSV representations to the output stream.
|
||||
type Encoder struct {
|
||||
// Tag defines which key in the struct field's tag to scan for names and
|
||||
// options (Default: 'csv').
|
||||
Tag string
|
||||
|
||||
// If AutoHeader is true, a struct header is encoded during the first call
|
||||
// to Encode automatically (Default: true).
|
||||
AutoHeader bool
|
||||
|
||||
w Writer
|
||||
c *encCache
|
||||
noHeader bool
|
||||
typeKey typeKey
|
||||
funcMap map[reflect.Type]reflect.Value
|
||||
ifaceFuncs []reflect.Value
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w Writer) *Encoder {
|
||||
return &Encoder{
|
||||
w: w,
|
||||
noHeader: true,
|
||||
AutoHeader: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Register registers a custom encoding function for a concrete type or interface.
|
||||
// The argument f must be of type:
|
||||
// func(T) ([]byte, error)
|
||||
//
|
||||
// T must be a concrete type such as Foo or *Foo, or interface that has at
|
||||
// least one method.
|
||||
//
|
||||
// During encoding, fields are matched by the concrete type first. If match is not
|
||||
// found then Encoder looks if field implements any of the registered interfaces
|
||||
// in order they were registered.
|
||||
//
|
||||
// Register panics if:
|
||||
// - f does not match the right signature
|
||||
// - f is an empty interface
|
||||
// - f was already registered
|
||||
//
|
||||
// Register is based on the encoding/json proposal:
|
||||
// https://github.com/golang/go/issues/5901.
|
||||
func (e *Encoder) Register(f interface{}) {
|
||||
v := reflect.ValueOf(f)
|
||||
typ := v.Type()
|
||||
|
||||
if typ.Kind() != reflect.Func ||
|
||||
typ.NumIn() != 1 || typ.NumOut() != 2 ||
|
||||
typ.Out(0) != _bytes || typ.Out(1) != _error {
|
||||
panic("csvutil: func must be of type func(T) ([]byte, error)")
|
||||
}
|
||||
|
||||
argType := typ.In(0)
|
||||
|
||||
if argType.Kind() == reflect.Interface && argType.NumMethod() == 0 {
|
||||
panic("csvutil: func argument type must not be an empty interface")
|
||||
}
|
||||
|
||||
if e.funcMap == nil {
|
||||
e.funcMap = make(map[reflect.Type]reflect.Value)
|
||||
}
|
||||
|
||||
if _, ok := e.funcMap[argType]; ok {
|
||||
panic("csvutil: func " + typ.String() + " already registered")
|
||||
}
|
||||
|
||||
e.funcMap[argType] = v
|
||||
|
||||
if argType.Kind() == reflect.Interface {
|
||||
e.ifaceFuncs = append(e.ifaceFuncs, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Encode writes the CSV encoding of v to the output stream. The provided
|
||||
// argument v must be a struct, struct slice or struct array.
|
||||
//
|
||||
// Only the exported fields will be encoded.
|
||||
//
|
||||
// First call to Encode will write a header unless EncodeHeader was called first
|
||||
// or AutoHeader is false. Header names can be customized by using tags
|
||||
// ('csv' by default), otherwise original Field names are used.
|
||||
//
|
||||
// Header and fields are written in the same order as struct fields are defined.
|
||||
// Embedded struct's fields are treated as if they were part of the outer struct.
|
||||
// Fields that are embedded types and that are tagged are treated like any
|
||||
// other field, but they have to implement Marshaler or encoding.TextMarshaler
|
||||
// interfaces.
|
||||
//
|
||||
// Marshaler interface has the priority over encoding.TextMarshaler.
|
||||
//
|
||||
// Tagged fields have the priority over non tagged fields with the same name.
|
||||
//
|
||||
// Following the Go visibility rules if there are multiple fields with the same
|
||||
// name (tagged or not tagged) on the same level and choice between them is
|
||||
// ambiguous, then all these fields will be ignored.
|
||||
//
|
||||
// Nil values will be encoded as empty strings. Same will happen if 'omitempty'
|
||||
// tag is set, and the value is a default value like 0, false or nil interface.
|
||||
//
|
||||
// Bool types are encoded as 'true' or 'false'.
|
||||
//
|
||||
// Float types are encoded using strconv.FormatFloat with precision -1 and 'G'
|
||||
// format. NaN values are encoded as 'NaN' string.
|
||||
//
|
||||
// Fields of type []byte are being encoded as base64-encoded strings.
|
||||
//
|
||||
// Fields can be excluded from encoding by using '-' tag option.
|
||||
//
|
||||
// Examples of struct tags:
|
||||
//
|
||||
// // Field appears as 'myName' header in CSV encoding.
|
||||
// Field int `csv:"myName"`
|
||||
//
|
||||
// // Field appears as 'Field' header in CSV encoding.
|
||||
// Field int
|
||||
//
|
||||
// // Field appears as 'myName' header in CSV encoding and is an empty string
|
||||
// // if Field is 0.
|
||||
// Field int `csv:"myName,omitempty"`
|
||||
//
|
||||
// // Field appears as 'Field' header in CSV encoding and is an empty string
|
||||
// // if Field is 0.
|
||||
// Field int `csv:",omitempty"`
|
||||
//
|
||||
// // Encode ignores this field.
|
||||
// Field int `csv:"-"`
|
||||
//
|
||||
// // Encode treats this field exactly as if it was an embedded field and adds
|
||||
// // "my_prefix_" to each field's name.
|
||||
// Field Struct `csv:"my_prefix_,inline"`
|
||||
//
|
||||
// // Encode treats this field exactly as if it was an embedded field.
|
||||
// Field Struct `csv:",inline"`
|
||||
//
|
||||
// Fields with inline tags that have a non-empty prefix must not be cyclic
|
||||
// structures. Passing such values to Encode will result in an infinite loop.
|
||||
//
|
||||
// Encode doesn't flush data. The caller is responsible for calling Flush() if
|
||||
// the used Writer supports it.
|
||||
func (e *Encoder) Encode(v interface{}) error {
|
||||
return e.encode(reflect.ValueOf(v))
|
||||
}
|
||||
|
||||
// EncodeHeader writes the CSV header of the provided struct value to the output
|
||||
// stream. The provided argument v must be a struct value.
|
||||
//
|
||||
// The first Encode method call will not write header if EncodeHeader was called
|
||||
// before it. This method can be called in cases when a data set could be
|
||||
// empty, but header is desired.
|
||||
//
|
||||
// EncodeHeader is like Header function, but it works with the Encoder and writes
|
||||
// directly to the output stream. Look at Header documentation for the exact
|
||||
// header encoding rules.
|
||||
func (e *Encoder) EncodeHeader(v interface{}) error {
|
||||
typ, err := valueType(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.encodeHeader(typ)
|
||||
}
|
||||
|
||||
func (e *Encoder) encode(v reflect.Value) error {
|
||||
val := walkValue(v)
|
||||
|
||||
if !val.IsValid() {
|
||||
return &InvalidEncodeError{}
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Struct:
|
||||
return e.encodeStruct(val)
|
||||
case reflect.Array, reflect.Slice:
|
||||
if walkType(val.Type().Elem()).Kind() != reflect.Struct {
|
||||
return &InvalidEncodeError{v.Type()}
|
||||
}
|
||||
return e.encodeArray(val)
|
||||
default:
|
||||
return &InvalidEncodeError{v.Type()}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeStruct(v reflect.Value) error {
|
||||
if e.AutoHeader && e.noHeader {
|
||||
if err := e.encodeHeader(v.Type()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return e.marshal(v)
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeArray(v reflect.Value) error {
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
if err := e.encodeStruct(walkValue(v.Index(i))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeHeader(typ reflect.Type) error {
|
||||
fields, _, _, record, err := e.cache(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, f := range fields {
|
||||
record[i] = f.name
|
||||
}
|
||||
|
||||
if err := e.w.Write(record); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.noHeader = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) marshal(v reflect.Value) error {
|
||||
fields, buf, index, record, err := e.cache(v.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, f := range fields {
|
||||
v := walkIndex(v, f.index)
|
||||
|
||||
omitempty := f.tag.omitEmpty
|
||||
if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
// We should disable omitempty for pointer and interface values,
|
||||
// because if it's nil we will automatically encode it as an empty
|
||||
// string. However, the initialized pointer should not be affected,
|
||||
// even if it's a default value.
|
||||
omitempty = false
|
||||
}
|
||||
|
||||
if !v.IsValid() {
|
||||
index[i] = 0
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := f.encodeFunc(buf, v, omitempty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
index[i], buf = len(b)-len(buf), b
|
||||
}
|
||||
|
||||
out := string(buf)
|
||||
for i, n := range index {
|
||||
record[i], out = out[:n], out[n:]
|
||||
}
|
||||
e.c.buf = buf[:0]
|
||||
|
||||
return e.w.Write(record)
|
||||
}
|
||||
|
||||
func (e *Encoder) tag() string {
|
||||
if e.Tag == "" {
|
||||
return defaultTag
|
||||
}
|
||||
return e.Tag
|
||||
}
|
||||
|
||||
func (e *Encoder) cache(typ reflect.Type) ([]encField, []byte, []int, []string, error) {
|
||||
if k := (typeKey{e.tag(), typ}); k != e.typeKey {
|
||||
c, err := newEncCache(k, e.funcMap, e.ifaceFuncs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
e.c, e.typeKey = c, k
|
||||
}
|
||||
return e.c.fields, e.c.buf[:0], e.c.index, e.c.record, nil
|
||||
}
|
||||
|
||||
func walkIndex(v reflect.Value, index []int) reflect.Value {
|
||||
for _, i := range index {
|
||||
v = walkPtr(v)
|
||||
if !v.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Field(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func walkPtr(v reflect.Value) reflect.Value {
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func walkValue(v reflect.Value) reflect.Value {
|
||||
for {
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
v = v.Elem()
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func walkType(typ reflect.Type) reflect.Type {
|
||||
for typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
return typ
|
||||
}
|
||||
156
vendor/github.com/jszwec/csvutil/error.go
generated
vendored
Normal file
156
vendor/github.com/jszwec/csvutil/error.go
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ErrFieldCount is returned when header's length doesn't match the length of
|
||||
// the read record.
|
||||
var ErrFieldCount = errors.New("wrong number of fields in record")
|
||||
|
||||
// An UnmarshalTypeError describes a string value that was not appropriate for
|
||||
// a value of a specific Go type.
|
||||
type UnmarshalTypeError struct {
|
||||
Value string // string value
|
||||
Type reflect.Type // type of Go value it could not be assigned to
|
||||
}
|
||||
|
||||
func (e *UnmarshalTypeError) Error() string {
|
||||
return "csvutil: cannot unmarshal " + strconv.Quote(e.Value) + " into Go value of type " + e.Type.String()
|
||||
}
|
||||
|
||||
// An UnsupportedTypeError is returned when attempting to encode or decode
|
||||
// a value of an unsupported type.
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *UnsupportedTypeError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "csvutil: unsupported type: nil"
|
||||
}
|
||||
return "csvutil: unsupported type: " + e.Type.String()
|
||||
}
|
||||
|
||||
// An InvalidDecodeError describes an invalid argument passed to Decode.
|
||||
// (The argument to Decode must be a non-nil struct pointer)
|
||||
type InvalidDecodeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *InvalidDecodeError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "csvutil: Decode(nil)"
|
||||
}
|
||||
|
||||
if e.Type.Kind() != reflect.Ptr {
|
||||
return "csvutil: Decode(non-pointer " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
typ := walkType(e.Type)
|
||||
switch typ.Kind() {
|
||||
case reflect.Struct:
|
||||
case reflect.Slice, reflect.Array:
|
||||
if typ.Elem().Kind() != reflect.Struct {
|
||||
return "csvutil: Decode(invalid type " + e.Type.String() + ")"
|
||||
}
|
||||
default:
|
||||
return "csvutil: Decode(invalid type " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
return "csvutil: Decode(nil " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
|
||||
// (The argument to Unmarshal must be a non-nil slice of structs pointer)
|
||||
type InvalidUnmarshalError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *InvalidUnmarshalError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "csvutil: Unmarshal(nil)"
|
||||
}
|
||||
|
||||
if e.Type.Kind() != reflect.Ptr {
|
||||
return "csvutil: Unmarshal(non-pointer " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
return "csvutil: Unmarshal(invalid type " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
// InvalidEncodeError is returned by Encode when the provided value was invalid.
|
||||
type InvalidEncodeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *InvalidEncodeError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "csvutil: Encode(nil)"
|
||||
}
|
||||
return "csvutil: Encode(" + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
// InvalidMarshalError is returned by Marshal when the provided value was invalid.
|
||||
type InvalidMarshalError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *InvalidMarshalError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "csvutil: Marshal(nil)"
|
||||
}
|
||||
|
||||
if walkType(e.Type).Kind() == reflect.Slice {
|
||||
return "csvutil: Marshal(non struct slice " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
if walkType(e.Type).Kind() == reflect.Array {
|
||||
return "csvutil: Marshal(non struct array " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
return "csvutil: Marshal(invalid type " + e.Type.String() + ")"
|
||||
}
|
||||
|
||||
// MarshalerError is returned by Encoder when MarshalCSV or MarshalText returned
|
||||
// an error.
|
||||
type MarshalerError struct {
|
||||
Type reflect.Type
|
||||
MarshalerType string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *MarshalerError) Error() string {
|
||||
return "csvutil: error calling " + e.MarshalerType + " for type " + e.Type.String() + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// Unwrap implements Unwrap interface for errors package in Go1.13+.
|
||||
func (e *MarshalerError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func errPtrUnexportedStruct(typ reflect.Type) error {
|
||||
return fmt.Errorf("csvutil: cannot decode into a pointer to unexported struct: %s", typ)
|
||||
}
|
||||
|
||||
// MissingColumnsError is returned by Decoder only when DisallowMissingColumns
|
||||
// option was set to true. It contains a list of all missing columns.
|
||||
type MissingColumnsError struct {
|
||||
Columns []string
|
||||
}
|
||||
|
||||
func (e *MissingColumnsError) Error() string {
|
||||
var b bytes.Buffer
|
||||
b.WriteString("csvutil: missing columns: ")
|
||||
for i, c := range e.Columns {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
fmt.Fprintf(&b, "%q", c)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
3
vendor/github.com/jszwec/csvutil/go.mod
generated
vendored
Normal file
3
vendor/github.com/jszwec/csvutil/go.mod
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/jszwec/csvutil
|
||||
|
||||
go 1.13
|
||||
29
vendor/github.com/jszwec/csvutil/interface.go
generated
vendored
Normal file
29
vendor/github.com/jszwec/csvutil/interface.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package csvutil
|
||||
|
||||
// Reader provides the interface for reading a single CSV record.
|
||||
//
|
||||
// If there is no data left to be read, Read returns (nil, io.EOF).
|
||||
//
|
||||
// It is implemented by csv.Reader.
|
||||
type Reader interface {
|
||||
Read() ([]string, error)
|
||||
}
|
||||
|
||||
// Writer provides the interface for writing a single CSV record.
|
||||
//
|
||||
// It is implemented by csv.Writer.
|
||||
type Writer interface {
|
||||
Write([]string) error
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface implemented by types that can unmarshal
|
||||
// a single record's field description of themselves.
|
||||
type Unmarshaler interface {
|
||||
UnmarshalCSV([]byte) error
|
||||
}
|
||||
|
||||
// Marshaler is the interface implemented by types that can marshal themselves
|
||||
// into valid string.
|
||||
type Marshaler interface {
|
||||
MarshalCSV() ([]byte, error)
|
||||
}
|
||||
47
vendor/github.com/jszwec/csvutil/tag.go
generated
vendored
Normal file
47
vendor/github.com/jszwec/csvutil/tag.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type tag struct {
|
||||
name string
|
||||
prefix string
|
||||
empty bool
|
||||
omitEmpty bool
|
||||
ignore bool
|
||||
inline bool
|
||||
}
|
||||
|
||||
func parseTag(tagname string, field reflect.StructField) (t tag) {
|
||||
tags := strings.Split(field.Tag.Get(tagname), ",")
|
||||
if len(tags) == 1 && tags[0] == "" {
|
||||
t.name = field.Name
|
||||
t.empty = true
|
||||
return
|
||||
}
|
||||
|
||||
switch tags[0] {
|
||||
case "-":
|
||||
t.ignore = true
|
||||
return
|
||||
case "":
|
||||
t.name = field.Name
|
||||
default:
|
||||
t.name = tags[0]
|
||||
}
|
||||
|
||||
for _, tagOpt := range tags[1:] {
|
||||
switch tagOpt {
|
||||
case "omitempty":
|
||||
t.omitEmpty = true
|
||||
case "inline":
|
||||
if walkType(field.Type).Kind() == reflect.Struct {
|
||||
t.inline = true
|
||||
t.prefix = tags[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
21
vendor/github.com/mitchellh/go-wordwrap/LICENSE.md
generated
vendored
Normal file
21
vendor/github.com/mitchellh/go-wordwrap/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Mitchell Hashimoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
39
vendor/github.com/mitchellh/go-wordwrap/README.md
generated
vendored
Normal file
39
vendor/github.com/mitchellh/go-wordwrap/README.md
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# go-wordwrap
|
||||
|
||||
`go-wordwrap` (Golang package: `wordwrap`) is a package for Go that
|
||||
automatically wraps words into multiple lines. The primary use case for this
|
||||
is in formatting CLI output, but of course word wrapping is a generally useful
|
||||
thing to do.
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
Install using `go get github.com/mitchellh/go-wordwrap`.
|
||||
|
||||
Full documentation is available at
|
||||
http://godoc.org/github.com/mitchellh/go-wordwrap
|
||||
|
||||
Below is an example of its usage ignoring errors:
|
||||
|
||||
```go
|
||||
wrapped := wordwrap.WrapString("foo bar baz", 3)
|
||||
fmt.Println(wrapped)
|
||||
```
|
||||
|
||||
Would output:
|
||||
|
||||
```
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
```
|
||||
|
||||
## Word Wrap Algorithm
|
||||
|
||||
This library doesn't use any clever algorithm for word wrapping. The wrapping
|
||||
is actually very naive: whenever there is whitespace or an explicit linebreak.
|
||||
The goal of this library is for word wrapping CLI output, so the input is
|
||||
typically pretty well controlled human language. Because of this, the naive
|
||||
approach typically works just fine.
|
||||
|
||||
In the future, we'd like to make the algorithm more advanced. We would do
|
||||
so without breaking the API.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user