mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-10-27 20:35:17 +00:00
vendor: Remove un-used vendored packages
With the infrakit plugin gone, remove it's dependencies. Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
## Hyperkit Infrakit plugin
|
||||
|
||||
There is a HyperKit instance plugin for infrakit in
|
||||
`src/cmd/infrakit-instance-hyperkit`. The instance plugin is capable
|
||||
to start/manage several hyperkit instances with with different
|
||||
configurations and LinuxKit configurations.
|
||||
|
||||
The plugin keeps state in a local directory (default
|
||||
`.infrakit/hyperkit-vms`) where each instance keeps some state in a
|
||||
sub-directory. The VM state directory can be specified at the kernel
|
||||
command line using the `--vm-dir` option.
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
make bin/infrakit-instance-hyperkit
|
||||
```
|
||||
|
||||
|
||||
## Quickstart
|
||||
|
||||
This is roughly based on the [infrakit tutorial](https://github.com/docker/infrakit/blob/master/docs/tutorial.md). You need to have the infrakit binaries in your path (or adjust the invocation of the commands below). To get the binaries, it's best to compile from source (checkout `https://github.com/docker/infrakit.git`, then `make` or `make build-in-container`). The add the `./build` directory to your path.
|
||||
|
||||
Start the default group plugin:
|
||||
```shell
|
||||
infrakit-group-default
|
||||
```
|
||||
and the vanilla flavour plugin:
|
||||
```shell
|
||||
infrakit-flavor-vanilla
|
||||
```
|
||||
|
||||
Then start the hyperkit plugin:
|
||||
```shell
|
||||
./bin/infrakit-instance-hyperkit
|
||||
```
|
||||
|
||||
Next, you can commit a new configuration. There is a sample infrakit config file in `hyperkit.json`. It assumes that you have a default LinuxKit build in the top-level directory. The `Moby` property needs to be of the form `<dir>/<prefix>` and assumes that the kernel and initrd images are called `<prefix>-bzImage` and `<prefix>-initrd.img` respectively (this is the convention used by the `moby` tool).
|
||||
```
|
||||
infrakit group commit hyperkit.json
|
||||
```
|
||||
|
||||
This will create a single hyperkit instance with its state stored in
|
||||
`~/.infrakit/hyperkit-vms`. There is a `tty` file which you can
|
||||
connect to with `screen` to access the VM.
|
||||
|
||||
If you kill the hyperkit process a new instance will be restarted. If
|
||||
you change the VM parameter in JSON file and commit the new config, a
|
||||
new VM will be created. f you change the `Size` parameter, multiple
|
||||
VMs will get started.
|
||||
19
vendor.conf
19
vendor.conf
@@ -1,24 +1,12 @@
|
||||
github.com/google/google-api-go-client 16ab375f94503bfa0d19db78e96bffbe1a34354f
|
||||
github.com/Masterminds/semver 312afcd0e81e5cf81fdc3cfd0e8504ae031521c8
|
||||
github.com/Masterminds/sprig 01a849f546a584d7b29bfee253e7db0aed44f7ba
|
||||
github.com/Sirupsen/logrus 10f801ebc38b33738c9d17d50860f484a0988ff5
|
||||
github.com/aokoli/goutils 9c37978a95bd5c709a15883b6242714ea6709e64
|
||||
github.com/armon/go-radix 4239b77079c7b5d1243b7b4736304ce8ddb6f0f2
|
||||
github.com/docker/docker 8d96619e5a367798cffcb740cfc41e0a505a5232
|
||||
github.com/docker/distribution 07f32ac1831ed0fc71960b7da5d6bb83cb6881b5
|
||||
github.com/docker/engine-api cf82c64276ebc2501e72b241f9fdc1e21e421743
|
||||
github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5
|
||||
github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
|
||||
github.com/docker/infrakit cb420e3e50ea60afe58538b1d3cab1cb14059433
|
||||
github.com/ghodss/yaml 0ca9ea5df5451ffdf184b4428c902747c2c11cd7
|
||||
github.com/golang/protobuf c9c7427a2a70d2eb3bafa0ab2dc163e45f143317
|
||||
github.com/googleapis/gax-go 8c5154c0fe5bf18cf649634d4c6df50897a32751
|
||||
github.com/gorilla/context 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
|
||||
github.com/gorilla/mux 599cba5e7b6137d46ddf58fb1765f5d928e69604
|
||||
github.com/gorilla/rpc 22c016f3df3febe0c1f6727598b6389507e03a18
|
||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
github.com/mattn/go-colorable d228849
|
||||
github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062
|
||||
github.com/moby/hyperkit 9b5f5fd848f0f5aedccb67a5a8cfa6787b8654f9
|
||||
github.com/opencontainers/runtime-spec d094a5c9c1997ab086197b57e9378fabed394d92
|
||||
@@ -26,11 +14,7 @@ github.com/pkg/errors ff09b135c25aae272398c51a07235b90a75aa4f0
|
||||
github.com/packethost/packngo 91d54000aa56874149d348a884ba083c41d38091
|
||||
github.com/rneugeba/iso9660wrap 4606f848a055435cdef85305960b0e1bb788d506
|
||||
github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a
|
||||
github.com/spf13/afero 9be650865eab0c12963d8753212f4f9c66cdcf12
|
||||
github.com/spf13/cobra 7be4beda01ec05d0b93d80b3facd2b6f44080d94
|
||||
github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
|
||||
github.com/surma/gocpio fcb68777e7dc4ea43ffce871b552c0d073c17495
|
||||
github.com/vaughan0/go-ini a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||
github.com/xeipuuv/gojsonpointer 6fe8760cad3569743d51ddbb243b26f8456742dc
|
||||
github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45
|
||||
github.com/xeipuuv/gojsonschema 702b404897d4364af44dc8dcabc9815947942325
|
||||
@@ -38,9 +22,6 @@ golang.org/x/crypto 573951cbe80bb6352881271bb276f48749eab6f4
|
||||
golang.org/x/net a6577fac2d73be281a500b310739095313165611
|
||||
golang.org/x/oauth2 1611bb46e67abc64a71ecc5c3ae67f1cbbc2b921
|
||||
golang.org/x/sys 99f16d856c9836c42d24e7ab64ea72916925fa97
|
||||
golang.org/x/text a263ba8
|
||||
google.golang.org/api 1202890e803f07684581b575fda809bf335a533f
|
||||
google.golang.org/grpc 0713829b980f4ddd276689a36235c5fcc82a21bf
|
||||
gopkg.in/inconshreveable/log15.v2 v2.11
|
||||
gopkg.in/tylerb/graceful.v1 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb
|
||||
gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a
|
||||
|
||||
20
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
20
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
@@ -1,20 +0,0 @@
|
||||
The Masterminds
|
||||
Copyright (C) 2014-2015, 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.
|
||||
163
vendor/github.com/Masterminds/semver/README.md
generated
vendored
163
vendor/github.com/Masterminds/semver/README.md
generated
vendored
@@ -1,163 +0,0 @@
|
||||
# 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://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)
|
||||
|
||||
## Parsing Semantic Versions
|
||||
|
||||
To parse a semantic version use the `NewVersion` function. For example,
|
||||
|
||||
```go
|
||||
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||
```
|
||||
|
||||
If there is an error the version wasn't parseable. 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](https://godoc.org/github.com/Masterminds/semver).
|
||||
|
||||
## Sorting Semantic Versions
|
||||
|
||||
A set of versions can be sorted using the [`sort`](https://golang.org/pkg/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
|
||||
|
||||
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 parseable.
|
||||
}
|
||||
|
||||
v, _ := semver.NewVersion("1.3")
|
||||
if err != nil {
|
||||
// Handle version not being parseable.
|
||||
}
|
||||
// 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 separated and comparisons. These are then separated by || separated 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
|
||||
|
||||
_Note, according to the Semantic Version specification pre-releases 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 without a pre-release value will skip pre-release versions.
|
||||
For example, `>1.2.3` will skip pre-releases when looking at a list of values
|
||||
while `>1.2.3-alpha.1` will evaluate pre-releases._
|
||||
|
||||
## 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 pack 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. 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`
|
||||
|
||||
# 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).
|
||||
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
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]
|
||||
}
|
||||
421
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
421
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
@@ -1,421 +0,0 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"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 {
|
||||
cs := strings.Split(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
|
||||
for _, o := range cs.constraints {
|
||||
joy := true
|
||||
for _, c := range o {
|
||||
if !c.check(v) {
|
||||
em := fmt.Errorf(c.msg, v, c.orig)
|
||||
e = append(e, em)
|
||||
joy = false
|
||||
}
|
||||
}
|
||||
|
||||
if joy {
|
||||
return true, []error{}
|
||||
}
|
||||
}
|
||||
|
||||
return false, e
|
||||
}
|
||||
|
||||
var constraintOps map[string]cfunc
|
||||
var constraintMsg map[string]string
|
||||
var constraintRegex *regexp.Regexp
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// An individual constraint
|
||||
type constraint struct {
|
||||
// The callback function for the restraint. It performs the logic for
|
||||
// the constraint.
|
||||
function cfunc
|
||||
|
||||
msg string
|
||||
|
||||
// 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
|
||||
|
||||
// When an x is used as part of the version (e.g., 1.x)
|
||||
minorDirty bool
|
||||
dirty bool
|
||||
}
|
||||
|
||||
// Check if a version meets the constraint
|
||||
func (c *constraint) check(v *Version) bool {
|
||||
return c.function(v, c)
|
||||
}
|
||||
|
||||
type cfunc func(v *Version, c *constraint) bool
|
||||
|
||||
func parseConstraint(c string) (*constraint, error) {
|
||||
m := constraintRegex.FindStringSubmatch(c)
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("improper constraint: %s", c)
|
||||
}
|
||||
|
||||
ver := m[2]
|
||||
orig := ver
|
||||
minorDirty := false
|
||||
dirty := false
|
||||
if isX(m[3]) {
|
||||
ver = "0.0.0"
|
||||
dirty = true
|
||||
} else if isX(strings.TrimPrefix(m[4], ".")) {
|
||||
minorDirty = true
|
||||
dirty = true
|
||||
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
|
||||
} else if isX(strings.TrimPrefix(m[5], ".")) {
|
||||
dirty = 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 := &constraint{
|
||||
function: constraintOps[m[1]],
|
||||
msg: constraintMsg[m[1]],
|
||||
con: con,
|
||||
orig: orig,
|
||||
minorDirty: minorDirty,
|
||||
dirty: dirty,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return !v.Equal(c.con)
|
||||
}
|
||||
|
||||
func constraintGreaterThan(v *Version, c *constraint) bool {
|
||||
|
||||
// An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease
|
||||
// exists. This that case.
|
||||
if !isNonZero(c.con) && isNonZero(v) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 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) == 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func constraintGreaterThanEqual(v *Version, c *constraint) bool {
|
||||
// An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease
|
||||
// exists. This that case.
|
||||
if !isNonZero(c.con) && isNonZero(v) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
c.msg = constraintMsg["~"]
|
||||
return constraintTilde(v, c)
|
||||
}
|
||||
|
||||
return v.Equal(c.con)
|
||||
}
|
||||
|
||||
// ^* --> (any)
|
||||
// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0
|
||||
// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0
|
||||
// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0
|
||||
// ^1.2.3 --> >=1.2.3, <2.0.0
|
||||
// ^1.2.0 --> >=1.2.0, <2.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
|
||||
}
|
||||
|
||||
if v.LessThan(c.con) {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.Major() != c.con.Major() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var constraintRangeRegex *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 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
|
||||
}
|
||||
|
||||
// Detect if a version is not zero (0.0.0)
|
||||
func isNonZero(v *Version) bool {
|
||||
if v.Major() != 0 || v.Minor() != 0 || v.Patch() != 0 || v.Prerelease() != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
To parse a semantic version use the `NewVersion` function. For example,
|
||||
|
||||
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||
|
||||
If there is an error the version wasn't parseable. 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
|
||||
|
||||
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 parseable.
|
||||
}
|
||||
|
||||
v, _ := semver.NewVersion("1.3")
|
||||
if err != nil {
|
||||
// Handle version not being parseable.
|
||||
}
|
||||
// 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 separated and comparisons. These are then separated by || separated 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
|
||||
|
||||
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 pack 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. 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`
|
||||
*/
|
||||
package semver
|
||||
375
vendor/github.com/Masterminds/semver/version.go
generated
vendored
375
vendor/github.com/Masterminds/semver/version.go
generated
vendored
@@ -1,375 +0,0 @@
|
||||
package semver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"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 validPrereleaseRegex *regexp.Regexp
|
||||
|
||||
var (
|
||||
// ErrInvalidSemVer is returned a version is found to be invalid when
|
||||
// being parsed.
|
||||
ErrInvalidSemVer = errors.New("Invalid Semantic Version")
|
||||
|
||||
// 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\-]+)*))?`
|
||||
|
||||
// ValidPrerelease is the regular expression which validates
|
||||
// both prerelease and metadata values.
|
||||
const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)`
|
||||
|
||||
// Version represents a single semantic version.
|
||||
type Version struct {
|
||||
major, minor, patch int64
|
||||
pre string
|
||||
metadata string
|
||||
original string
|
||||
}
|
||||
|
||||
func init() {
|
||||
versionRegex = regexp.MustCompile("^" + SemVerRegex + "$")
|
||||
validPrereleaseRegex = regexp.MustCompile(ValidPrerelease)
|
||||
}
|
||||
|
||||
// NewVersion parses a given version and returns an instance of Version or
|
||||
// an error if unable to parse the version.
|
||||
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 temp int64
|
||||
temp, err := strconv.ParseInt(m[1], 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||
}
|
||||
sv.major = temp
|
||||
|
||||
if m[2] != "" {
|
||||
temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||
}
|
||||
sv.minor = temp
|
||||
} else {
|
||||
sv.minor = 0
|
||||
}
|
||||
|
||||
if m[3] != "" {
|
||||
temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||
}
|
||||
sv.patch = temp
|
||||
} else {
|
||||
sv.patch = 0
|
||||
}
|
||||
|
||||
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
|
||||
// impelementation.
|
||||
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() int64 {
|
||||
return v.major
|
||||
}
|
||||
|
||||
// Minor returns the minor version.
|
||||
func (v *Version) Minor() int64 {
|
||||
return v.minor
|
||||
}
|
||||
|
||||
// Patch returns the patch version.
|
||||
func (v *Version) Patch() int64 {
|
||||
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 curent 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 'hypen' prefix.
|
||||
func (v Version) SetPrerelease(prerelease string) (Version, error) {
|
||||
vNext := v
|
||||
if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) {
|
||||
return vNext, ErrInvalidPrerelease
|
||||
}
|
||||
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 && !validPrereleaseRegex.MatchString(metadata) {
|
||||
return vNext, ErrInvalidMetadata
|
||||
}
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
func compareSegment(v, o int64) 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 o == "" {
|
||||
_, n := strconv.ParseInt(s, 10, 64)
|
||||
if n != nil {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
if s == "" {
|
||||
_, n := strconv.ParseInt(o, 10, 64)
|
||||
if n != nil {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
if s > o {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
20
vendor/github.com/Masterminds/sprig/LICENSE.txt
generated
vendored
20
vendor/github.com/Masterminds/sprig/LICENSE.txt
generated
vendored
@@ -1,20 +0,0 @@
|
||||
Sprig
|
||||
Copyright (C) 2013 Masterminds
|
||||
|
||||
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.
|
||||
311
vendor/github.com/Masterminds/sprig/README.md
generated
vendored
311
vendor/github.com/Masterminds/sprig/README.md
generated
vendored
@@ -1,311 +0,0 @@
|
||||
# Sprig: Template functions for Go templates
|
||||
|
||||
The Go language comes with a [built-in template
|
||||
language](http://golang.org/pkg/text/template/), but not
|
||||
very many template functions. This library provides a group of commonly
|
||||
used template functions.
|
||||
|
||||
It is inspired by the template functions found in
|
||||
[Twig](http://twig.sensiolabs.org/documentation).
|
||||
|
||||
[](https://travis-ci.org/Masterminds/sprig)
|
||||
|
||||
## Usage
|
||||
|
||||
API documentation is available [at GoDoc.org](http://godoc.org/github.com/Masterminds/sprig), but
|
||||
read on for standard usage.
|
||||
|
||||
### Load the Sprig library
|
||||
|
||||
To load the Sprig `FuncMap`:
|
||||
|
||||
```go
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/sprig"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
// This example illustrates that the FuncMap *must* be set before the
|
||||
// templates themselves are loaded.
|
||||
tpl := template.Must(
|
||||
template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html")
|
||||
)
|
||||
|
||||
|
||||
```
|
||||
|
||||
### Call the functions inside of templates
|
||||
|
||||
By convention, all functions are lowercase. This seems to follow the Go
|
||||
idiom for template functions (as opposed to template methods, which are
|
||||
TitleCase).
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
{{ "hello!" | upper | repeat 5 }}
|
||||
```
|
||||
|
||||
Produces:
|
||||
|
||||
```
|
||||
HELLO!HELLO!HELLO!HELLO!HELLO!
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### Date Functions
|
||||
|
||||
- date: Format a date, where a date is an integer type or a time.Time type, and
|
||||
format is a time.Format formatting string.
|
||||
- dateModify: Given a date, modify it with a duration: `date_modify "-1.5h" now`. If the duration doesn't
|
||||
parse, it returns the time unaltered. See `time.ParseDuration` for info on duration strings.
|
||||
- now: Current time.Time, for feeding into date-related functions.
|
||||
- htmlDate: Format a date for use in the value field of an HTML "date" form element.
|
||||
- dateInZone: Like date, but takes three arguments: format, timestamp,
|
||||
timezone.
|
||||
- htmlDateInZone: Like htmlDate, but takes two arguments: timestamp,
|
||||
timezone.
|
||||
|
||||
### String Functions
|
||||
|
||||
- trim: strings.TrimSpace
|
||||
- trimAll: strings.Trim, but with the argument order reversed `trimAll "$" "$5.00"` or `"$5.00 | trimAll "$"`
|
||||
- trimSuffix: strings.TrimSuffix, but with the argument order reversed `trimSuffix "-" "5-"`
|
||||
- trimPrefix: strings.TrimPrefix, but with the argument order reversed `trimPrefix "$" "$5"`
|
||||
- upper: strings.ToUpper
|
||||
- lower: strings.ToLower
|
||||
- title: strings.Title
|
||||
- repeat: strings.Repeat, but with the arguments switched: `repeat count str`. (This simplifies common pipelines)
|
||||
- substr: Given string, start, and length, return a substr.
|
||||
- nospace: Remove all spaces from a string. `h e l l o` becomes
|
||||
`hello`.
|
||||
- abbrev: Truncate a string with ellipses
|
||||
- trunc: Truncate a string (no suffix). `trunc 5 "Hello World"` yields "hello".
|
||||
- abbrevboth: Truncate both sides of a string with ellipses
|
||||
- untitle: Remove title case
|
||||
- intials: Given multiple words, return the first letter of each
|
||||
word
|
||||
- randAlphaNum: Generate a random alpha-numeric string
|
||||
- randAlpha: Generate a random alphabetic string
|
||||
- randAscii: Generate a random ASCII string, including symbols
|
||||
- randNumeric: Generate a random numeric string
|
||||
- wrap: Wrap text at the given column count
|
||||
- wrapWith: Wrap text at the given column count, and with the given
|
||||
string for a line terminator: `wrap 50 "\n\t" $string`
|
||||
- contains: strings.Contains, but with the arguments switched: `contains "cat" "uncatch"`. (This simplifies common pipelines)
|
||||
- hasPrefix: strings.hasPrefix, but with the arguments switched: `hasPrefix "cat" "catch"`.
|
||||
- hasSuffix: strings.hasSuffix, but with the arguments switched: `hasSuffix "cat" "ducat"`.
|
||||
- quote: Wrap strings in double quotes. `quote "a" "b"` returns `"a"
|
||||
"b"`
|
||||
- squote: Wrap strings in single quotes.
|
||||
- cat: Concatenate strings, separating them by spaces. `cat $a $b $c`.
|
||||
- indent: Indent a string using space characters. `indent 4 "foo\nbar"` produces " foo\n bar"
|
||||
- replace: Replace an old with a new in a string: `$name | replace " " "-"`
|
||||
- plural: Choose singular or plural based on length: `len $fish | plural
|
||||
"one anchovy" "many anchovies"`
|
||||
- uuidv4: Generate a UUID v4 string
|
||||
- sha256sum: Generate a hex encoded sha256 hash of the input
|
||||
- toString: Convert something to a string
|
||||
|
||||
### String Slice Functions:
|
||||
|
||||
- join: strings.Join, but as `join SEP SLICE`
|
||||
- split: strings.Split, but as `split SEP STRING`. The results are returned
|
||||
as a map with the indexes set to _N, where N is an integer starting from 0.
|
||||
Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`)
|
||||
- splitList: strings.Split, but as `split SEP STRING`. The results are returned
|
||||
as an array.
|
||||
- toStrings: convert a list to a list of strings. 'list 1 2 3 | toStrings' produces '["1" "2" "3"]'
|
||||
- sortAlpha: sort a list lexicographically.
|
||||
|
||||
### Integer Slice Functions:
|
||||
|
||||
- until: Given an integer, returns a slice of counting integers from 0 to one
|
||||
less than the given integer: `range $i, $e := until 5`
|
||||
- untilStep: Given start, stop, and step, return an integer slice starting at
|
||||
'start', stopping at `stop`, and incrementing by 'step'. This is the same
|
||||
as Python's long-form of 'range'.
|
||||
|
||||
### Conversions:
|
||||
|
||||
- atoi: Convert a string to an integer. 0 if the integer could not be parsed.
|
||||
- int: Convert a string or numeric to an int
|
||||
- int64: Convert a string or numeric to an int64
|
||||
- float64: Convert a string or numeric to a float64
|
||||
|
||||
### Defaults:
|
||||
|
||||
- default: Give a default value. Used like this: {{trim " "| default "empty"}}.
|
||||
Since trim produces an empty string, the default value is returned. For
|
||||
things with a length (strings, slices, maps), len(0) will trigger the default.
|
||||
For numbers, the value 0 will trigger the default. For booleans, false will
|
||||
trigger the default. For structs, the default is never returned (there is
|
||||
no clear empty condition). For everything else, nil value triggers a default.
|
||||
- empty: Returns true if the given value is the zero value for that
|
||||
type. Structs are always non-empty.
|
||||
- coalesce: Given a list of items, return the first non-empty one.
|
||||
This follows the same rules as 'empty'. `{{ coalesce .someVal 0 "hello" }}`
|
||||
will return `.someVal` if set, or else return "hello". The 0 is skipped
|
||||
because it is an empty value.
|
||||
- compact: Return a copy of a list with all of the empty values removed.
|
||||
`list 0 1 2 "" | compact` will return `[1 2]`
|
||||
|
||||
### OS:
|
||||
|
||||
- env: Read an environment variable.
|
||||
- expandenv: Expand all environment variables in a string.
|
||||
|
||||
### File Paths:
|
||||
- base: Return the last element of a path. https://golang.org/pkg/path#Base
|
||||
- dir: Remove the last element of a path. https://golang.org/pkg/path#Dir
|
||||
- clean: Clean a path to the shortest equivalent name. (e.g. remove "foo/.."
|
||||
from "foo/../bar.html") https://golang.org/pkg/path#Clean
|
||||
- ext: Get the extension for a file path: https://golang.org/pkg/path#Ext
|
||||
- isAbs: Returns true if a path is absolute: https://golang.org/pkg/path#IsAbs
|
||||
|
||||
### Encoding:
|
||||
|
||||
- b32enc: Encode a string into a Base32 string
|
||||
- b32dec: Decode a string from a Base32 string
|
||||
- b64enc: Encode a string into a Base64 string
|
||||
- b64dec: Decode a string from a Base64 string
|
||||
|
||||
### Data Structures:
|
||||
|
||||
- tuple: Takes an arbitrary list of items and returns a slice of items. Its
|
||||
tuple-ish properties are mainly gained through the template idiom, and not
|
||||
through an API provided here. WARNING: The implementation of tuple will
|
||||
change in the future.
|
||||
- list: An arbitrary ordered list of items. (This is prefered over tuple.)
|
||||
- dict: Takes a list of name/values and returns a map[string]interface{}.
|
||||
The first parameter is converted to a string and stored as a key, the
|
||||
second parameter is treated as the value. And so on, with odds as keys and
|
||||
evens as values. If the function call ends with an odd, the last key will
|
||||
be assigned the empty string. Non-string keys are converted to strings as
|
||||
follows: []byte are converted, fmt.Stringers will have String() called.
|
||||
errors will have Error() called. All others will be passed through
|
||||
fmt.Sprtinf("%v"). _dicts are unordered_.
|
||||
|
||||
List:
|
||||
|
||||
```
|
||||
{{$t := list 1 "a" "foo"}}
|
||||
{{index $t 2}}{{index $t 0 }}{{index $t 1}}
|
||||
{{/* Prints foo1a *}}
|
||||
```
|
||||
|
||||
Dict:
|
||||
```
|
||||
{{ $t := map "key1" "value1" "key2" "value2" }}
|
||||
{{ $t.key2 }}
|
||||
{{ /* Prints value2 *}}
|
||||
```
|
||||
|
||||
|
||||
### Lists Functions:
|
||||
|
||||
These are used to manipulate lists: `{{ list 1 2 3 | reverse | first }}`
|
||||
|
||||
- first: Get the first item in a 'list'. 'list 1 2 3 | first' prints '1'
|
||||
- last: Get the last item in a 'list': 'list 1 2 3 | last ' prints '3'
|
||||
- rest: Get all but the first item in a list: 'list 1 2 3 | rest' returns '[2 3]'
|
||||
- initial: Get all but the last item in a list: 'list 1 2 3 | initial' returns '[1 2]'
|
||||
- append: Add an item to the end of a list: 'append $list 4' adds '4' to the end of '$list'
|
||||
- prepend: Add an item to the beginning of a list: 'prepend $list 4' puts 4 at the beginning of the list.
|
||||
- reverse: Reverse the items in a list.
|
||||
- uniq: Remove duplicates from a list.
|
||||
- without: Return a list with the given values removed: 'without (list 1 2 3) 1' would return '[2 3]'
|
||||
- has: Return 'tru' if the item is found in the list: 'has "foo" $list' will return 'true' if the list contains "foo"
|
||||
|
||||
### Dict Functions:
|
||||
|
||||
These are used to manipulate dicts.
|
||||
|
||||
- set: Takes a dict, a key, and a value, and sets that key/value pair in
|
||||
the dict. `set $dict $key $value`. For convenience, it returns the dict,
|
||||
even though the dict was modified in place.
|
||||
- unset: Takes a dict and a key, and deletes that key/value pair from the
|
||||
dict. `unset $dict $key`. This returns the dict for convenience.
|
||||
- hasKey: Takes a dict and a key, and returns boolean true if the key is in
|
||||
the dict.
|
||||
- pluck: Given a key and one or more maps, get all of the values for that key.
|
||||
- keys: Get an array of all of the keys in a dict. Order is not guaranteed.
|
||||
- pick: Select just the given keys out of the dict, and return a new dict.
|
||||
- omit: Return a dict without the given keys.
|
||||
|
||||
### Reflection:
|
||||
|
||||
- typeOf: Takes an interface and returns a string representation of the type.
|
||||
For pointers, this will return a type prefixed with an asterisk(`*`). So
|
||||
a pointer to type `Foo` will be `*Foo`.
|
||||
- typeIs: Compares an interface with a string name, and returns true if they match.
|
||||
Note that a pointer will not match a reference. For example `*Foo` will not
|
||||
match `Foo`.
|
||||
- typeIsLike: returns true if the interface is of the given type, or
|
||||
is a pointer to the given type.
|
||||
- kindOf: Takes an interface and returns a string representation of its kind.
|
||||
- kindIs: Returns true if the given string matches the kind of the given interface.
|
||||
|
||||
Note: None of these can test whether or not something implements a given
|
||||
interface, since doing so would require compiling the interface in ahead of
|
||||
time.
|
||||
|
||||
|
||||
### Math Functions:
|
||||
|
||||
Integer functions will convert integers of any width to `int64`. If a
|
||||
string is passed in, functions will attempt to conver with
|
||||
`strconv.ParseInt(s, 1064)`. If this fails, the value will be treated as 0.
|
||||
|
||||
- add1: Increment an integer by 1
|
||||
- add: Sum integers. `add 1 2 3` renders `6`
|
||||
- sub: Subtract the second integer from the first
|
||||
- div: Divide the first integer by the second
|
||||
- mod: Module of first integer divided by second
|
||||
- mul: Multiply integers integers
|
||||
- max (biggest): Return the biggest of a series of integers. `max 1 2 3`
|
||||
returns `3`.
|
||||
- min: Return the smallest of a series of integers. `min 1 2 3` returns
|
||||
`1`.
|
||||
|
||||
### Cryptographic Functions:
|
||||
|
||||
- derivePassword: Derive a password from the given parameters according to the "Master Password" algorithm (http://masterpasswordapp.com/algorithm.html)
|
||||
Given parameters (in order) are:
|
||||
`counter` (starting with 1), `password_type` (maximum, long, medium, short, basic, or pin), `password`,
|
||||
`user`, and `site`. The following line generates a long password for the user "user" and with a master-password "password" on the site "example.com":
|
||||
```
|
||||
{{ derivePassword 1 "long" "password" "user" "example.com" }}
|
||||
```
|
||||
|
||||
## SemVer Functions:
|
||||
|
||||
These functions provide version parsing and comparisons for SemVer 2 version
|
||||
strings.
|
||||
|
||||
- semver: Parse a semantic version and return a Version object.
|
||||
- semverCompare: Compare a SemVer range to a particular version.
|
||||
|
||||
## Principles:
|
||||
|
||||
The following principles were used in deciding on which functions to add, and
|
||||
determining how to implement them.
|
||||
|
||||
- Template functions should be used to build layout. Therefore, the following
|
||||
types of operations are within the domain of template functions:
|
||||
- Formatting
|
||||
- Layout
|
||||
- Simple type conversions
|
||||
- Utilities that assist in handling common formatting and layout needs (e.g. arithmetic)
|
||||
- Template functions should not return errors unless there is no way to print
|
||||
a sensible value. For example, converting a string to an integer should not
|
||||
produce an error if conversion fails. Instead, it should display a default
|
||||
value that can be displayed.
|
||||
- Simple math is necessary for grid layouts, pagers, and so on. Complex math
|
||||
(anything other than arithmetic) should be done outside of templates.
|
||||
- Template functions only deal with the data passed into them. They never retrieve
|
||||
data from a source.
|
||||
- Finally, do not override core Go template functions.
|
||||
148
vendor/github.com/Masterminds/sprig/crypto.go
generated
vendored
148
vendor/github.com/Masterminds/sprig/crypto.go
generated
vendored
@@ -1,148 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
func sha256sum(input string) string {
|
||||
hash := sha256.Sum256([]byte(input))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// uuidv4 provides a safe and secure UUID v4 implementation
|
||||
func uuidv4() string {
|
||||
return fmt.Sprintf("%s", uuid.NewV4())
|
||||
}
|
||||
|
||||
var master_password_seed = "com.lyndir.masterpassword"
|
||||
|
||||
var password_type_templates = map[string][][]byte{
|
||||
"maximum": {[]byte("anoxxxxxxxxxxxxxxxxx"), []byte("axxxxxxxxxxxxxxxxxno")},
|
||||
"long": {[]byte("CvcvnoCvcvCvcv"), []byte("CvcvCvcvnoCvcv"), []byte("CvcvCvcvCvcvno"), []byte("CvccnoCvcvCvcv"), []byte("CvccCvcvnoCvcv"),
|
||||
[]byte("CvccCvcvCvcvno"), []byte("CvcvnoCvccCvcv"), []byte("CvcvCvccnoCvcv"), []byte("CvcvCvccCvcvno"), []byte("CvcvnoCvcvCvcc"),
|
||||
[]byte("CvcvCvcvnoCvcc"), []byte("CvcvCvcvCvccno"), []byte("CvccnoCvccCvcv"), []byte("CvccCvccnoCvcv"), []byte("CvccCvccCvcvno"),
|
||||
[]byte("CvcvnoCvccCvcc"), []byte("CvcvCvccnoCvcc"), []byte("CvcvCvccCvccno"), []byte("CvccnoCvcvCvcc"), []byte("CvccCvcvnoCvcc"),
|
||||
[]byte("CvccCvcvCvccno")},
|
||||
"medium": {[]byte("CvcnoCvc"), []byte("CvcCvcno")},
|
||||
"short": {[]byte("Cvcn")},
|
||||
"basic": {[]byte("aaanaaan"), []byte("aannaaan"), []byte("aaannaaa")},
|
||||
"pin": {[]byte("nnnn")},
|
||||
}
|
||||
|
||||
var template_characters = map[byte]string{
|
||||
'V': "AEIOU",
|
||||
'C': "BCDFGHJKLMNPQRSTVWXYZ",
|
||||
'v': "aeiou",
|
||||
'c': "bcdfghjklmnpqrstvwxyz",
|
||||
'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
|
||||
'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
|
||||
'n': "0123456789",
|
||||
'o': "@&%?,=[]_:-+*$#!'^~;()/.",
|
||||
'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
|
||||
}
|
||||
|
||||
func derivePassword(counter uint32, password_type, password, user, site string) string {
|
||||
var templates = password_type_templates[password_type]
|
||||
if templates == nil {
|
||||
return fmt.Sprintf("cannot find password template %s", password_type)
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(master_password_seed)
|
||||
binary.Write(&buffer, binary.BigEndian, uint32(len(user)))
|
||||
buffer.WriteString(user)
|
||||
|
||||
salt := buffer.Bytes()
|
||||
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 2, 64)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("failed to derive password: %s", err)
|
||||
}
|
||||
|
||||
buffer.Truncate(len(master_password_seed))
|
||||
binary.Write(&buffer, binary.BigEndian, uint32(len(site)))
|
||||
buffer.WriteString(site)
|
||||
binary.Write(&buffer, binary.BigEndian, counter)
|
||||
|
||||
var hmacv = hmac.New(sha256.New, key)
|
||||
hmacv.Write(buffer.Bytes())
|
||||
var seed = hmacv.Sum(nil)
|
||||
var temp = templates[int(seed[0])%len(templates)]
|
||||
|
||||
buffer.Truncate(0)
|
||||
for i, element := range temp {
|
||||
pass_chars := template_characters[element]
|
||||
pass_char := pass_chars[int(seed[i+1])%len(pass_chars)]
|
||||
buffer.WriteByte(pass_char)
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func generatePrivateKey(typ string) string {
|
||||
var priv interface{}
|
||||
var err error
|
||||
switch typ {
|
||||
case "", "rsa":
|
||||
// good enough for government work
|
||||
priv, err = rsa.GenerateKey(rand.Reader, 4096)
|
||||
case "dsa":
|
||||
key := new(dsa.PrivateKey)
|
||||
// again, good enough for government work
|
||||
if err = dsa.GenerateParameters(&key.Parameters, rand.Reader, dsa.L2048N256); err != nil {
|
||||
return fmt.Sprintf("failed to generate dsa params: %s", err)
|
||||
}
|
||||
err = dsa.GenerateKey(key, rand.Reader)
|
||||
priv = key
|
||||
case "ecdsa":
|
||||
// again, good enough for government work
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
default:
|
||||
return "Unknown type " + typ
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Sprintf("failed to generate private key: %s", err)
|
||||
}
|
||||
|
||||
return string(pem.EncodeToMemory(pemBlockForKey(priv)))
|
||||
}
|
||||
|
||||
type DSAKeyFormat struct {
|
||||
Version int
|
||||
P, Q, G, Y, X *big.Int
|
||||
}
|
||||
|
||||
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||
case *dsa.PrivateKey:
|
||||
val := DSAKeyFormat{
|
||||
P: k.P, Q: k.Q, G: k.G,
|
||||
Y: k.Y, X: k.X,
|
||||
}
|
||||
bytes, _ := asn1.Marshal(val)
|
||||
return &pem.Block{Type: "DSA PRIVATE KEY", Bytes: bytes}
|
||||
case *ecdsa.PrivateKey:
|
||||
b, _ := x509.MarshalECPrivateKey(k)
|
||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
53
vendor/github.com/Masterminds/sprig/date.go
generated
vendored
53
vendor/github.com/Masterminds/sprig/date.go
generated
vendored
@@ -1,53 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Given a format and a date, format the date string.
|
||||
//
|
||||
// Date can be a `time.Time` or an `int, int32, int64`.
|
||||
// In the later case, it is treated as seconds since UNIX
|
||||
// epoch.
|
||||
func date(fmt string, date interface{}) string {
|
||||
return dateInZone(fmt, date, "Local")
|
||||
}
|
||||
|
||||
func htmlDate(date interface{}) string {
|
||||
return dateInZone("2006-01-02", date, "Local")
|
||||
}
|
||||
|
||||
func htmlDateInZone(date interface{}, zone string) string {
|
||||
return dateInZone("2006-01-02", date, zone)
|
||||
}
|
||||
|
||||
func dateInZone(fmt string, date interface{}, zone string) string {
|
||||
var t time.Time
|
||||
switch date := date.(type) {
|
||||
default:
|
||||
t = time.Now()
|
||||
case time.Time:
|
||||
t = date
|
||||
case int64:
|
||||
t = time.Unix(date, 0)
|
||||
case int:
|
||||
t = time.Unix(int64(date), 0)
|
||||
case int32:
|
||||
t = time.Unix(int64(date), 0)
|
||||
}
|
||||
|
||||
loc, err := time.LoadLocation(zone)
|
||||
if err != nil {
|
||||
loc, _ = time.LoadLocation("UTC")
|
||||
}
|
||||
|
||||
return t.In(loc).Format(fmt)
|
||||
}
|
||||
|
||||
func dateModify(fmt string, date time.Time) time.Time {
|
||||
d, err := time.ParseDuration(fmt)
|
||||
if err != nil {
|
||||
return date
|
||||
}
|
||||
return date.Add(d)
|
||||
}
|
||||
62
vendor/github.com/Masterminds/sprig/defaults.go
generated
vendored
62
vendor/github.com/Masterminds/sprig/defaults.go
generated
vendored
@@ -1,62 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// dfault checks whether `given` is set, and returns default if not set.
|
||||
//
|
||||
// This returns `d` if `given` appears not to be set, and `given` otherwise.
|
||||
//
|
||||
// For numeric types 0 is unset.
|
||||
// For strings, maps, arrays, and slices, len() = 0 is considered unset.
|
||||
// For bool, false is unset.
|
||||
// Structs are never considered unset.
|
||||
//
|
||||
// For everything else, including pointers, a nil value is unset.
|
||||
func dfault(d interface{}, given ...interface{}) interface{} {
|
||||
|
||||
if empty(given) || empty(given[0]) {
|
||||
return d
|
||||
}
|
||||
return given[0]
|
||||
}
|
||||
|
||||
// empty returns true if the given value has the zero value for its type.
|
||||
func empty(given interface{}) bool {
|
||||
g := reflect.ValueOf(given)
|
||||
if !g.IsValid() {
|
||||
return true
|
||||
}
|
||||
|
||||
// Basically adapted from text/template.isTrue
|
||||
switch g.Kind() {
|
||||
default:
|
||||
return g.IsNil()
|
||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
||||
return g.Len() == 0
|
||||
case reflect.Bool:
|
||||
return g.Bool() == false
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return g.Complex() == 0
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return g.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return g.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return g.Float() == 0
|
||||
case reflect.Struct:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// coalesce returns the first non-empty value.
|
||||
func coalesce(v ...interface{}) interface{} {
|
||||
for _, val := range v {
|
||||
if !empty(val) {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
74
vendor/github.com/Masterminds/sprig/dict.go
generated
vendored
74
vendor/github.com/Masterminds/sprig/dict.go
generated
vendored
@@ -1,74 +0,0 @@
|
||||
package sprig
|
||||
|
||||
func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} {
|
||||
d[key] = value
|
||||
return d
|
||||
}
|
||||
|
||||
func unset(d map[string]interface{}, key string) map[string]interface{} {
|
||||
delete(d, key)
|
||||
return d
|
||||
}
|
||||
|
||||
func hasKey(d map[string]interface{}, key string) bool {
|
||||
_, ok := d[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func pluck(key string, d ...map[string]interface{}) []interface{} {
|
||||
res := []interface{}{}
|
||||
for _, dict := range d {
|
||||
if val, ok := dict[key]; ok {
|
||||
res = append(res, val)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func keys(dict map[string]interface{}) []string {
|
||||
k := []string{}
|
||||
for key := range dict {
|
||||
k = append(k, key)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func pick(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||
res := map[string]interface{}{}
|
||||
for _, k := range keys {
|
||||
if v, ok := dict[k]; ok {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func omit(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||
res := map[string]interface{}{}
|
||||
|
||||
omit := make(map[string]bool, len(keys))
|
||||
for _, k := range keys {
|
||||
omit[k] = true
|
||||
}
|
||||
|
||||
for k, v := range dict {
|
||||
if _, ok := omit[k]; !ok {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func dict(v ...interface{}) map[string]interface{} {
|
||||
dict := map[string]interface{}{}
|
||||
lenv := len(v)
|
||||
for i := 0; i < lenv; i += 2 {
|
||||
key := strval(v[i])
|
||||
if i+1 >= lenv {
|
||||
dict[key] = ""
|
||||
continue
|
||||
}
|
||||
dict[key] = v[i+1]
|
||||
}
|
||||
return dict
|
||||
}
|
||||
225
vendor/github.com/Masterminds/sprig/doc.go
generated
vendored
225
vendor/github.com/Masterminds/sprig/doc.go
generated
vendored
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
Sprig: Template functions for Go.
|
||||
|
||||
This package contains a number of utility functions for working with data
|
||||
inside of Go `html/template` and `text/template` files.
|
||||
|
||||
To add these functions, use the `template.Funcs()` method:
|
||||
|
||||
t := templates.New("foo").Funcs(sprig.FuncMap())
|
||||
|
||||
Note that you should add the function map before you parse any template files.
|
||||
|
||||
In several cases, Sprig reverses the order of arguments from the way they
|
||||
appear in the standard library. This is to make it easier to pipe
|
||||
arguments into functions.
|
||||
|
||||
Date Functions
|
||||
|
||||
- date FORMAT TIME: Format a date, where a date is an integer type or a time.Time type, and
|
||||
format is a time.Format formatting string.
|
||||
- dateModify: Given a date, modify it with a duration: `date_modify "-1.5h" now`. If the duration doesn't
|
||||
parse, it returns the time unaltered. See `time.ParseDuration` for info on duration strings.
|
||||
- now: Current time.Time, for feeding into date-related functions.
|
||||
- htmlDate TIME: Format a date for use in the value field of an HTML "date" form element.
|
||||
- dateInZone FORMAT TIME TZ: Like date, but takes three arguments: format, timestamp,
|
||||
timezone.
|
||||
- htmlDateInZone TIME TZ: Like htmlDate, but takes two arguments: timestamp,
|
||||
timezone.
|
||||
|
||||
String Functions
|
||||
|
||||
- abbrev: Truncate a string with ellipses. `abbrev 5 "hello world"` yields "he..."
|
||||
- abbrevboth: Abbreviate from both sides, yielding "...lo wo..."
|
||||
- trunc: Truncate a string (no suffix). `trunc 5 "Hello World"` yields "hello".
|
||||
- trim: strings.TrimSpace
|
||||
- trimAll: strings.Trim, but with the argument order reversed `trimAll "$" "$5.00"` or `"$5.00 | trimAll "$"`
|
||||
- trimSuffix: strings.TrimSuffix, but with the argument order reversed: `trimSuffix "-" "ends-with-"`
|
||||
- trimPrefix: strings.TrimPrefix, but with the argument order reversed `trimPrefix "$" "$5"`
|
||||
- upper: strings.ToUpper
|
||||
- lower: strings.ToLower
|
||||
- nospace: Remove all space characters from a string. `nospace "h e l l o"` becomes "hello"
|
||||
- title: strings.Title
|
||||
- untitle: Remove title casing
|
||||
- repeat: strings.Repeat, but with the arguments switched: `repeat count str`. (This simplifies common pipelines)
|
||||
- substr: Given string, start, and length, return a substr.
|
||||
- initials: Given a multi-word string, return the initials. `initials "Matt Butcher"` returns "MB"
|
||||
- randAlphaNum: Given a length, generate a random alphanumeric sequence
|
||||
- randAlpha: Given a length, generate an alphabetic string
|
||||
- randAscii: Given a length, generate a random ASCII string (symbols included)
|
||||
- randNumeric: Given a length, generate a string of digits.
|
||||
- wrap: Force a line wrap at the given width. `wrap 80 "imagine a longer string"`
|
||||
- wrapWith: Wrap a line at the given length, but using 'sep' instead of a newline. `wrapWith 50, "<br>", $html`
|
||||
- contains: strings.Contains, but with the arguments switched: `contains substr str`. (This simplifies common pipelines)
|
||||
- hasPrefix: strings.hasPrefix, but with the arguments switched
|
||||
- hasSuffix: strings.hasSuffix, but with the arguments switched
|
||||
- quote: Wrap string(s) in double quotation marks, escape the contents by adding '\' before '"'.
|
||||
- squote: Wrap string(s) in double quotation marks, does not escape content.
|
||||
- cat: Concatenate strings, separating them by spaces. `cat $a $b $c`.
|
||||
- indent: Indent a string using space characters. `indent 4 "foo\nbar"` produces " foo\n bar"
|
||||
- replace: Replace an old with a new in a string: `$name | replace " " "-"`
|
||||
- plural: Choose singular or plural based on length: `len $fish | plural "one anchovy" "many anchovies"`
|
||||
- sha256sum: Generate a hex encoded sha256 hash of the input
|
||||
- toString: Convert something to a string
|
||||
|
||||
String Slice Functions:
|
||||
|
||||
- join: strings.Join, but as `join SEP SLICE`
|
||||
- split: strings.Split, but as `split SEP STRING`. The results are returned
|
||||
as a map with the indexes set to _N, where N is an integer starting from 0.
|
||||
Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`)
|
||||
- splitList: strings.Split, but as `split SEP STRING`. The results are returned
|
||||
as an array.
|
||||
- toStrings: convert a list to a list of strings. 'list 1 2 3 | toStrings' produces '["1" "2" "3"]'
|
||||
- sortAlpha: sort a list lexicographically.
|
||||
|
||||
Integer Slice Functions:
|
||||
|
||||
- until: Given an integer, returns a slice of counting integers from 0 to one
|
||||
less than the given integer: `range $i, $e := until 5`
|
||||
- untilStep: Given start, stop, and step, return an integer slice starting at
|
||||
'start', stopping at `stop`, and incrementing by 'step. This is the same
|
||||
as Python's long-form of 'range'.
|
||||
|
||||
Conversions:
|
||||
|
||||
- atoi: Convert a string to an integer. 0 if the integer could not be parsed.
|
||||
- in64: Convert a string or another numeric type to an int64.
|
||||
- int: Convert a string or another numeric type to an int.
|
||||
- float64: Convert a string or another numeric type to a float64.
|
||||
|
||||
Defaults:
|
||||
|
||||
- default: Give a default value. Used like this: trim " "| default "empty".
|
||||
Since trim produces an empty string, the default value is returned. For
|
||||
things with a length (strings, slices, maps), len(0) will trigger the default.
|
||||
For numbers, the value 0 will trigger the default. For booleans, false will
|
||||
trigger the default. For structs, the default is never returned (there is
|
||||
no clear empty condition). For everything else, nil value triggers a default.
|
||||
- empty: Return true if the given value is the zero value for its type.
|
||||
Caveats: structs are always non-empty. This should match the behavior of
|
||||
{{if pipeline}}, but can be used inside of a pipeline.
|
||||
- coalesce: Given a list of items, return the first non-empty one.
|
||||
This follows the same rules as 'empty'. '{{ coalesce .someVal 0 "hello" }}`
|
||||
will return `.someVal` if set, or else return "hello". The 0 is skipped
|
||||
because it is an empty value.
|
||||
- compact: Return a copy of a list with all of the empty values removed.
|
||||
'list 0 1 2 "" | compact' will return '[1 2]'
|
||||
|
||||
OS:
|
||||
- env: Resolve an environment variable
|
||||
- expandenv: Expand a string through the environment
|
||||
|
||||
File Paths:
|
||||
- base: Return the last element of a path. https://golang.org/pkg/path#Base
|
||||
- dir: Remove the last element of a path. https://golang.org/pkg/path#Dir
|
||||
- clean: Clean a path to the shortest equivalent name. (e.g. remove "foo/.."
|
||||
from "foo/../bar.html") https://golang.org/pkg/path#Clean
|
||||
- ext: https://golang.org/pkg/path#Ext
|
||||
- isAbs: https://golang.org/pkg/path#IsAbs
|
||||
|
||||
Encoding:
|
||||
- b64enc: Base 64 encode a string.
|
||||
- b64dec: Base 64 decode a string.
|
||||
|
||||
Reflection:
|
||||
|
||||
- typeOf: Takes an interface and returns a string representation of the type.
|
||||
For pointers, this will return a type prefixed with an asterisk(`*`). So
|
||||
a pointer to type `Foo` will be `*Foo`.
|
||||
- typeIs: Compares an interface with a string name, and returns true if they match.
|
||||
Note that a pointer will not match a reference. For example `*Foo` will not
|
||||
match `Foo`.
|
||||
- typeIsLike: Compares an interface with a string name and returns true if
|
||||
the interface is that `name` or that `*name`. In other words, if the given
|
||||
value matches the given type or is a pointer to the given type, this returns
|
||||
true.
|
||||
- kindOf: Takes an interface and returns a string representation of its kind.
|
||||
- kindIs: Returns true if the given string matches the kind of the given interface.
|
||||
|
||||
Note: None of these can test whether or not something implements a given
|
||||
interface, since doing so would require compiling the interface in ahead of
|
||||
time.
|
||||
|
||||
Data Structures:
|
||||
|
||||
- tuple: Takes an arbitrary list of items and returns a slice of items. Its
|
||||
tuple-ish properties are mainly gained through the template idiom, and not
|
||||
through an API provided here. WARNING: The implementation of tuple will
|
||||
change in the future.
|
||||
- list: An arbitrary ordered list of items. (This is prefered over tuple.)
|
||||
- dict: Takes a list of name/values and returns a map[string]interface{}.
|
||||
The first parameter is converted to a string and stored as a key, the
|
||||
second parameter is treated as the value. And so on, with odds as keys and
|
||||
evens as values. If the function call ends with an odd, the last key will
|
||||
be assigned the empty string. Non-string keys are converted to strings as
|
||||
follows: []byte are converted, fmt.Stringers will have String() called.
|
||||
errors will have Error() called. All others will be passed through
|
||||
fmt.Sprtinf("%v").
|
||||
|
||||
Lists Functions:
|
||||
|
||||
These are used to manipulate lists: '{{ list 1 2 3 | reverse | first }}'
|
||||
|
||||
- first: Get the first item in a 'list'. 'list 1 2 3 | first' prints '1'
|
||||
- last: Get the last item in a 'list': 'list 1 2 3 | last ' prints '3'
|
||||
- rest: Get all but the first item in a list: 'list 1 2 3 | rest' returns '[2 3]'
|
||||
- initial: Get all but the last item in a list: 'list 1 2 3 | initial' returns '[1 2]'
|
||||
- append: Add an item to the end of a list: 'append $list 4' adds '4' to the end of '$list'
|
||||
- prepend: Add an item to the beginning of a list: 'prepend $list 4' puts 4 at the beginning of the list.
|
||||
- reverse: Reverse the items in a list.
|
||||
- uniq: Remove duplicates from a list.
|
||||
- without: Return a list with the given values removed: 'without (list 1 2 3) 1' would return '[2 3]'
|
||||
- has: Return 'true' if the item is found in the list: 'has "foo" $list' will return 'true' if the list contains "foo"
|
||||
|
||||
Dict Functions:
|
||||
|
||||
These are used to manipulate dicts.
|
||||
|
||||
- set: Takes a dict, a key, and a value, and sets that key/value pair in
|
||||
the dict. `set $dict $key $value`. For convenience, it returns the dict,
|
||||
even though the dict was modified in place.
|
||||
- unset: Takes a dict and a key, and deletes that key/value pair from the
|
||||
dict. `unset $dict $key`. This returns the dict for convenience.
|
||||
- hasKey: Takes a dict and a key, and returns boolean true if the key is in
|
||||
the dict.
|
||||
- pluck: Given a key and one or more maps, get all of the values for that key.
|
||||
- keys: Get an array of all of the keys in a dict.
|
||||
- pick: Select just the given keys out of the dict, and return a new dict.
|
||||
- omit: Return a dict without the given keys.
|
||||
|
||||
Math Functions:
|
||||
|
||||
Integer functions will convert integers of any width to `int64`. If a
|
||||
string is passed in, functions will attempt to convert with
|
||||
`strconv.ParseInt(s, 1064)`. If this fails, the value will be treated as 0.
|
||||
|
||||
- add1: Increment an integer by 1
|
||||
- add: Sum an arbitrary number of integers
|
||||
- sub: Subtract the second integer from the first
|
||||
- div: Divide the first integer by the second
|
||||
- mod: Module of first integer divided by second
|
||||
- mul: Multiply integers
|
||||
- max: Return the biggest of a series of one or more integers
|
||||
- min: Return the smallest of a series of one or more integers
|
||||
- biggest: DEPRECATED. Return the biggest of a series of one or more integers
|
||||
|
||||
Crypto Functions:
|
||||
|
||||
- genPrivateKey: Generate a private key for the given cryptosystem. If no
|
||||
argument is supplied, by default it will generate a private key using
|
||||
the RSA algorithm. Accepted values are `rsa`, `dsa`, and `ecdsa`.
|
||||
- derivePassword: Derive a password from the given parameters according to the ["Master Password" algorithm](http://masterpasswordapp.com/algorithm.html)
|
||||
Given parameters (in order) are:
|
||||
`counter` (starting with 1), `password_type` (maximum, long, medium, short, basic, or pin), `password`,
|
||||
`user`, and `site`
|
||||
|
||||
SemVer Functions:
|
||||
|
||||
These functions provide version parsing and comparisons for SemVer 2 version
|
||||
strings.
|
||||
|
||||
- semver: Parse a semantic version and return a Version object.
|
||||
- semverCompare: Compare a SemVer range to a particular version.
|
||||
*/
|
||||
package sprig
|
||||
250
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
250
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
@@ -1,250 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
ttemplate "text/template"
|
||||
"time"
|
||||
|
||||
util "github.com/aokoli/goutils"
|
||||
)
|
||||
|
||||
// Produce the function map.
|
||||
//
|
||||
// Use this to pass the functions into the template engine:
|
||||
//
|
||||
// tpl := template.New("foo").Funcs(sprig.FuncMap()))
|
||||
//
|
||||
func FuncMap() template.FuncMap {
|
||||
return HtmlFuncMap()
|
||||
}
|
||||
|
||||
// HermeticTextFuncMap returns a 'text/template'.FuncMap with only repeatable functions.
|
||||
func HermeticTxtFuncMap() ttemplate.FuncMap {
|
||||
r := TxtFuncMap()
|
||||
for _, name := range nonhermeticFunctions {
|
||||
delete(r, name)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions.
|
||||
func HermeticHtmlFuncMap() template.FuncMap {
|
||||
r := HtmlFuncMap()
|
||||
for _, name := range nonhermeticFunctions {
|
||||
delete(r, name)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// TextFuncMap returns a 'text/template'.FuncMap
|
||||
func TxtFuncMap() ttemplate.FuncMap {
|
||||
return ttemplate.FuncMap(GenericFuncMap())
|
||||
}
|
||||
|
||||
// HtmlFuncMap returns an 'html/template'.Funcmap
|
||||
func HtmlFuncMap() template.FuncMap {
|
||||
return template.FuncMap(GenericFuncMap())
|
||||
}
|
||||
|
||||
// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
|
||||
func GenericFuncMap() map[string]interface{} {
|
||||
gfm := make(map[string]interface{}, len(genericMap))
|
||||
for k, v := range genericMap {
|
||||
gfm[k] = v
|
||||
}
|
||||
return gfm
|
||||
}
|
||||
|
||||
// These functions are not guaranteed to evaluate to the same result for given input, because they
|
||||
// refer to the environemnt or global state.
|
||||
var nonhermeticFunctions = []string{
|
||||
// Date functions
|
||||
"date",
|
||||
"date_in_zone",
|
||||
"date_modify",
|
||||
"now",
|
||||
"htmlDate",
|
||||
"htmlDateInZone",
|
||||
"dateInZone",
|
||||
"dateModify",
|
||||
|
||||
// Strings
|
||||
"randAlphaNum",
|
||||
"randAlpha",
|
||||
"randAscii",
|
||||
"randNumeric",
|
||||
"uuidv4",
|
||||
|
||||
// OS
|
||||
"env",
|
||||
"expandenv",
|
||||
}
|
||||
|
||||
var genericMap = map[string]interface{}{
|
||||
"hello": func() string { return "Hello!" },
|
||||
|
||||
// Date functions
|
||||
"date": date,
|
||||
"date_in_zone": dateInZone,
|
||||
"date_modify": dateModify,
|
||||
"now": func() time.Time { return time.Now() },
|
||||
"htmlDate": htmlDate,
|
||||
"htmlDateInZone": htmlDateInZone,
|
||||
"dateInZone": dateInZone,
|
||||
"dateModify": dateModify,
|
||||
|
||||
// Strings
|
||||
"abbrev": abbrev,
|
||||
"abbrevboth": abbrevboth,
|
||||
"trunc": trunc,
|
||||
"trim": strings.TrimSpace,
|
||||
"upper": strings.ToUpper,
|
||||
"lower": strings.ToLower,
|
||||
"title": strings.Title,
|
||||
"untitle": untitle,
|
||||
"substr": substring,
|
||||
// Switch order so that "foo" | repeat 5
|
||||
"repeat": func(count int, str string) string { return strings.Repeat(str, count) },
|
||||
// Deprecated: Use trimAll.
|
||||
"trimall": func(a, b string) string { return strings.Trim(b, a) },
|
||||
// Switch order so that "$foo" | trimall "$"
|
||||
"trimAll": func(a, b string) string { return strings.Trim(b, a) },
|
||||
"trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
|
||||
"trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },
|
||||
"nospace": util.DeleteWhiteSpace,
|
||||
"initials": initials,
|
||||
"randAlphaNum": randAlphaNumeric,
|
||||
"randAlpha": randAlpha,
|
||||
"randAscii": randAscii,
|
||||
"randNumeric": randNumeric,
|
||||
"swapcase": util.SwapCase,
|
||||
"wrap": func(l int, s string) string { return util.Wrap(s, l) },
|
||||
"wrapWith": func(l int, sep, str string) string { return util.WrapCustom(str, l, sep, true) },
|
||||
// Switch order so that "foobar" | contains "foo"
|
||||
"contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
|
||||
"hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
|
||||
"hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
|
||||
"quote": quote,
|
||||
"squote": squote,
|
||||
"cat": cat,
|
||||
"indent": indent,
|
||||
"replace": replace,
|
||||
"plural": plural,
|
||||
"sha256sum": sha256sum,
|
||||
"toString": strval,
|
||||
|
||||
// Wrap Atoi to stop errors.
|
||||
"atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
|
||||
"int64": toInt64,
|
||||
"int": toInt,
|
||||
"float64": toFloat64,
|
||||
|
||||
//"gt": func(a, b int) bool {return a > b},
|
||||
//"gte": func(a, b int) bool {return a >= b},
|
||||
//"lt": func(a, b int) bool {return a < b},
|
||||
//"lte": func(a, b int) bool {return a <= b},
|
||||
|
||||
// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
|
||||
"split": split,
|
||||
"splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
|
||||
"toStrings": strslice,
|
||||
|
||||
"until": until,
|
||||
"untilStep": untilStep,
|
||||
|
||||
// VERY basic arithmetic.
|
||||
"add1": func(i interface{}) int64 { return toInt64(i) + 1 },
|
||||
"add": func(i ...interface{}) int64 {
|
||||
var a int64 = 0
|
||||
for _, b := range i {
|
||||
a += toInt64(b)
|
||||
}
|
||||
return a
|
||||
},
|
||||
"sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
|
||||
"div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
|
||||
"mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
|
||||
"mul": func(a interface{}, v ...interface{}) int64 {
|
||||
val := toInt64(a)
|
||||
for _, b := range v {
|
||||
val = val * toInt64(b)
|
||||
}
|
||||
return val
|
||||
},
|
||||
"biggest": max,
|
||||
"max": max,
|
||||
"min": min,
|
||||
|
||||
// string slices. Note that we reverse the order b/c that's better
|
||||
// for template processing.
|
||||
"join": join,
|
||||
"sortAlpha": sortAlpha,
|
||||
|
||||
// Defaults
|
||||
"default": dfault,
|
||||
"empty": empty,
|
||||
"coalesce": coalesce,
|
||||
"compact": compact,
|
||||
|
||||
// Reflection
|
||||
"typeOf": typeOf,
|
||||
"typeIs": typeIs,
|
||||
"typeIsLike": typeIsLike,
|
||||
"kindOf": kindOf,
|
||||
"kindIs": kindIs,
|
||||
|
||||
// OS:
|
||||
"env": func(s string) string { return os.Getenv(s) },
|
||||
"expandenv": func(s string) string { return os.ExpandEnv(s) },
|
||||
|
||||
// File Paths:
|
||||
"base": path.Base,
|
||||
"dir": path.Dir,
|
||||
"clean": path.Clean,
|
||||
"ext": path.Ext,
|
||||
"isAbs": path.IsAbs,
|
||||
|
||||
// Encoding:
|
||||
"b64enc": base64encode,
|
||||
"b64dec": base64decode,
|
||||
"b32enc": base32encode,
|
||||
"b32dec": base32decode,
|
||||
|
||||
// Data Structures:
|
||||
"tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
|
||||
"list": list,
|
||||
"dict": dict,
|
||||
"set": set,
|
||||
"unset": unset,
|
||||
"hasKey": hasKey,
|
||||
"pluck": pluck,
|
||||
"keys": keys,
|
||||
"pick": pick,
|
||||
"omit": omit,
|
||||
|
||||
"append": push, "push": push,
|
||||
"prepend": prepend,
|
||||
"first": first,
|
||||
"rest": rest,
|
||||
"last": last,
|
||||
"initial": initial,
|
||||
"reverse": reverse,
|
||||
"uniq": uniq,
|
||||
"without": without,
|
||||
"has": func(needle interface{}, haystack []interface{}) bool { return inList(haystack, needle) },
|
||||
|
||||
// Crypto:
|
||||
"genPrivateKey": generatePrivateKey,
|
||||
"derivePassword": derivePassword,
|
||||
|
||||
// UUIDs:
|
||||
"uuidv4": uuidv4,
|
||||
|
||||
// SemVer:
|
||||
"semver": semver,
|
||||
"semverCompare": semverCompare,
|
||||
}
|
||||
109
vendor/github.com/Masterminds/sprig/list.go
generated
vendored
109
vendor/github.com/Masterminds/sprig/list.go
generated
vendored
@@ -1,109 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func list(v ...interface{}) []interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func push(list []interface{}, v interface{}) []interface{} {
|
||||
return append(list, v)
|
||||
}
|
||||
|
||||
func prepend(list []interface{}, v interface{}) []interface{} {
|
||||
return append([]interface{}{v}, list...)
|
||||
}
|
||||
|
||||
func last(list []interface{}) interface{} {
|
||||
l := len(list)
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
return list[l-1]
|
||||
}
|
||||
|
||||
func first(list []interface{}) interface{} {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
return list[0]
|
||||
}
|
||||
|
||||
func rest(list []interface{}) []interface{} {
|
||||
if len(list) == 0 {
|
||||
return list
|
||||
}
|
||||
return list[1:]
|
||||
}
|
||||
|
||||
func initial(list []interface{}) []interface{} {
|
||||
l := len(list)
|
||||
if l == 0 {
|
||||
return list
|
||||
}
|
||||
return list[:l-1]
|
||||
}
|
||||
|
||||
func sortAlpha(list interface{}) []string {
|
||||
k := reflect.Indirect(reflect.ValueOf(list)).Kind()
|
||||
switch k {
|
||||
case reflect.Slice, reflect.Array:
|
||||
a := strslice(list)
|
||||
s := sort.StringSlice(a)
|
||||
s.Sort()
|
||||
return s
|
||||
}
|
||||
return []string{strval(list)}
|
||||
}
|
||||
|
||||
func reverse(v []interface{}) []interface{} {
|
||||
// We do not sort in place because the incomming array should not be altered.
|
||||
l := len(v)
|
||||
c := make([]interface{}, l)
|
||||
for i := 0; i < l; i++ {
|
||||
c[l-i-1] = v[i]
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func compact(list []interface{}) []interface{} {
|
||||
res := []interface{}{}
|
||||
for _, item := range list {
|
||||
if !empty(item) {
|
||||
res = append(res, item)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func uniq(list []interface{}) []interface{} {
|
||||
dest := []interface{}{}
|
||||
for _, item := range list {
|
||||
if !inList(dest, item) {
|
||||
dest = append(dest, item)
|
||||
}
|
||||
}
|
||||
return dest
|
||||
}
|
||||
|
||||
func inList(haystack []interface{}, needle interface{}) bool {
|
||||
for _, h := range haystack {
|
||||
if reflect.DeepEqual(needle, h) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func without(list []interface{}, omit ...interface{}) []interface{} {
|
||||
res := []interface{}{}
|
||||
for _, i := range list {
|
||||
if !inList(omit, i) {
|
||||
res = append(res, i)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
129
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
129
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
@@ -1,129 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// toFloat64 converts 64-bit floats
|
||||
func toFloat64(v interface{}) float64 {
|
||||
if str, ok := v.(string); ok {
|
||||
iv, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return iv
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(v))
|
||||
switch val.Kind() {
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return float64(val.Int())
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
return float64(val.Uint())
|
||||
case reflect.Uint, reflect.Uint64:
|
||||
return float64(val.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return val.Float()
|
||||
case reflect.Bool:
|
||||
if val.Bool() == true {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func toInt(v interface{}) int {
|
||||
//It's not optimal. Bud I don't want duplicate toInt64 code.
|
||||
return int(toInt64(v))
|
||||
}
|
||||
|
||||
// toInt64 converts integer types to 64-bit integers
|
||||
func toInt64(v interface{}) int64 {
|
||||
if str, ok := v.(string); ok {
|
||||
iv, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return iv
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(v))
|
||||
switch val.Kind() {
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return val.Int()
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
return int64(val.Uint())
|
||||
case reflect.Uint, reflect.Uint64:
|
||||
tv := val.Uint()
|
||||
if tv <= math.MaxInt64 {
|
||||
return int64(tv)
|
||||
}
|
||||
// TODO: What is the sensible thing to do here?
|
||||
return math.MaxInt64
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return int64(val.Float())
|
||||
case reflect.Bool:
|
||||
if val.Bool() == true {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func max(a interface{}, i ...interface{}) int64 {
|
||||
aa := toInt64(a)
|
||||
for _, b := range i {
|
||||
bb := toInt64(b)
|
||||
if bb > aa {
|
||||
aa = bb
|
||||
}
|
||||
}
|
||||
return aa
|
||||
}
|
||||
|
||||
func min(a interface{}, i ...interface{}) int64 {
|
||||
aa := toInt64(a)
|
||||
for _, b := range i {
|
||||
bb := toInt64(b)
|
||||
if bb < aa {
|
||||
aa = bb
|
||||
}
|
||||
}
|
||||
return aa
|
||||
}
|
||||
|
||||
func until(count int) []int {
|
||||
step := 1
|
||||
if count < 0 {
|
||||
step = -1
|
||||
}
|
||||
return untilStep(0, count, step)
|
||||
}
|
||||
|
||||
func untilStep(start, stop, step int) []int {
|
||||
v := []int{}
|
||||
|
||||
if stop < start {
|
||||
if step >= 0 {
|
||||
return v
|
||||
}
|
||||
for i := start; i > stop; i += step {
|
||||
v = append(v, i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
if step <= 0 {
|
||||
return v
|
||||
}
|
||||
for i := start; i < stop; i += step {
|
||||
v = append(v, i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
28
vendor/github.com/Masterminds/sprig/reflect.go
generated
vendored
28
vendor/github.com/Masterminds/sprig/reflect.go
generated
vendored
@@ -1,28 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// typeIs returns true if the src is the type named in target.
|
||||
func typeIs(target string, src interface{}) bool {
|
||||
return target == typeOf(src)
|
||||
}
|
||||
|
||||
func typeIsLike(target string, src interface{}) bool {
|
||||
t := typeOf(src)
|
||||
return target == t || "*"+target == t
|
||||
}
|
||||
|
||||
func typeOf(src interface{}) string {
|
||||
return fmt.Sprintf("%T", src)
|
||||
}
|
||||
|
||||
func kindIs(target string, src interface{}) bool {
|
||||
return target == kindOf(src)
|
||||
}
|
||||
|
||||
func kindOf(src interface{}) string {
|
||||
return reflect.ValueOf(src).Kind().String()
|
||||
}
|
||||
23
vendor/github.com/Masterminds/sprig/semver.go
generated
vendored
23
vendor/github.com/Masterminds/sprig/semver.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
sv2 "github.com/Masterminds/semver"
|
||||
)
|
||||
|
||||
func semverCompare(constraint, version string) (bool, error) {
|
||||
c, err := sv2.NewConstraint(constraint)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
v, err := sv2.NewVersion(version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return c.Check(v), nil
|
||||
}
|
||||
|
||||
func semver(version string) (*sv2.Version, error) {
|
||||
return sv2.NewVersion(version)
|
||||
}
|
||||
197
vendor/github.com/Masterminds/sprig/strings.go
generated
vendored
197
vendor/github.com/Masterminds/sprig/strings.go
generated
vendored
@@ -1,197 +0,0 @@
|
||||
package sprig
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
util "github.com/aokoli/goutils"
|
||||
)
|
||||
|
||||
func base64encode(v string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
|
||||
func base64decode(v string) string {
|
||||
data, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func base32encode(v string) string {
|
||||
return base32.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
|
||||
func base32decode(v string) string {
|
||||
data, err := base32.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func abbrev(width int, s string) string {
|
||||
if width < 4 {
|
||||
return s
|
||||
}
|
||||
r, _ := util.Abbreviate(s, width)
|
||||
return r
|
||||
}
|
||||
|
||||
func abbrevboth(left, right int, s string) string {
|
||||
if right < 4 || left > 0 && right < 7 {
|
||||
return s
|
||||
}
|
||||
r, _ := util.AbbreviateFull(s, left, right)
|
||||
return r
|
||||
}
|
||||
func initials(s string) string {
|
||||
// Wrap this just to eliminate the var args, which templates don't do well.
|
||||
return util.Initials(s)
|
||||
}
|
||||
|
||||
func randAlphaNumeric(count int) string {
|
||||
// It is not possible, it appears, to actually generate an error here.
|
||||
r, _ := util.RandomAlphaNumeric(count)
|
||||
return r
|
||||
}
|
||||
|
||||
func randAlpha(count int) string {
|
||||
r, _ := util.RandomAlphabetic(count)
|
||||
return r
|
||||
}
|
||||
|
||||
func randAscii(count int) string {
|
||||
r, _ := util.RandomAscii(count)
|
||||
return r
|
||||
}
|
||||
|
||||
func randNumeric(count int) string {
|
||||
r, _ := util.RandomNumeric(count)
|
||||
return r
|
||||
}
|
||||
|
||||
func untitle(str string) string {
|
||||
return util.Uncapitalize(str)
|
||||
}
|
||||
|
||||
func quote(str ...interface{}) string {
|
||||
out := make([]string, len(str))
|
||||
for i, s := range str {
|
||||
out[i] = fmt.Sprintf("%q", strval(s))
|
||||
}
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
func squote(str ...interface{}) string {
|
||||
out := make([]string, len(str))
|
||||
for i, s := range str {
|
||||
out[i] = fmt.Sprintf("'%v'", s)
|
||||
}
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
func cat(v ...interface{}) string {
|
||||
r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
|
||||
return fmt.Sprintf(r, v...)
|
||||
}
|
||||
|
||||
func indent(spaces int, v string) string {
|
||||
pad := strings.Repeat(" ", spaces)
|
||||
return pad + strings.Replace(v, "\n", "\n"+pad, -1)
|
||||
}
|
||||
|
||||
func replace(old, new, src string) string {
|
||||
return strings.Replace(src, old, new, -1)
|
||||
}
|
||||
|
||||
func plural(one, many string, count int) string {
|
||||
if count == 1 {
|
||||
return one
|
||||
}
|
||||
return many
|
||||
}
|
||||
|
||||
func strslice(v interface{}) []string {
|
||||
switch v := v.(type) {
|
||||
case []string:
|
||||
return v
|
||||
case []interface{}:
|
||||
l := len(v)
|
||||
b := make([]string, l)
|
||||
for i := 0; i < l; i++ {
|
||||
b[i] = strval(v[i])
|
||||
}
|
||||
return b
|
||||
default:
|
||||
val := reflect.ValueOf(v)
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
l := val.Len()
|
||||
b := make([]string, l)
|
||||
for i := 0; i < l; i++ {
|
||||
b[i] = strval(val.Index(i).Interface())
|
||||
}
|
||||
return b
|
||||
default:
|
||||
return []string{strval(v)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func strval(v interface{}) string {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return v
|
||||
case []byte:
|
||||
return string(v)
|
||||
case error:
|
||||
return v.Error()
|
||||
case fmt.Stringer:
|
||||
return v.String()
|
||||
default:
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func trunc(c int, s string) string {
|
||||
if len(s) <= c {
|
||||
return s
|
||||
}
|
||||
return s[0:c]
|
||||
}
|
||||
|
||||
func join(sep string, v interface{}) string {
|
||||
return strings.Join(strslice(v), sep)
|
||||
}
|
||||
|
||||
func split(sep, orig string) map[string]string {
|
||||
parts := strings.Split(orig, sep)
|
||||
res := make(map[string]string, len(parts))
|
||||
for i, v := range parts {
|
||||
res["_"+strconv.Itoa(i)] = v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// substring creates a substring of the given string.
|
||||
//
|
||||
// If start is < 0, this calls string[:length].
|
||||
//
|
||||
// If start is >= 0 and length < 0, this calls string[start:]
|
||||
//
|
||||
// Otherwise, this calls string[start, length].
|
||||
func substring(start, length int, s string) string {
|
||||
if start < 0 {
|
||||
return s[:length]
|
||||
}
|
||||
if length < 0 {
|
||||
return s[start:]
|
||||
}
|
||||
return s[start:length]
|
||||
}
|
||||
202
vendor/github.com/aokoli/goutils/LICENSE.txt
generated
vendored
202
vendor/github.com/aokoli/goutils/LICENSE.txt
generated
vendored
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
72
vendor/github.com/aokoli/goutils/README.md
generated
vendored
72
vendor/github.com/aokoli/goutils/README.md
generated
vendored
@@ -1,72 +0,0 @@
|
||||
GoUtils
|
||||
===========
|
||||
|
||||
GoUtils provides users with utility functions to manipulate strings in various ways. It is a Go implementation of some
|
||||
string manipulation libraries of Java Apache Commons. GoUtils includes the following Java Apache Commons classes:
|
||||
* WordUtils
|
||||
* RandomStringUtils
|
||||
* StringUtils (partial implementation)
|
||||
|
||||
## Installation
|
||||
If you have Go set up on your system, from the GOPATH directory within the command line/terminal, enter this:
|
||||
|
||||
go get github.com/aokoli/goutils
|
||||
|
||||
If you do not have Go set up on your system, please follow the [Go installation directions from the documenation](http://golang.org/doc/install), and then follow the instructions above to install GoUtils.
|
||||
|
||||
|
||||
## Documentation
|
||||
GoUtils doc is available here: [](https://godoc.org/github.com/aokoli/goutils)
|
||||
|
||||
|
||||
## Usage
|
||||
The code snippets below show examples of how to use GoUtils. Some functions return errors while others do not. The first instance below, which does not return an error, is the `Initials` function (located within the `wordutils.go` file).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aokoli/goutils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// EXAMPLE 1: A goutils function which returns no errors
|
||||
fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF"
|
||||
|
||||
}
|
||||
Some functions return errors mainly due to illegal arguements used as parameters. The code example below illustrates how to deal with function that returns an error. In this instance, the function is the `Random` function (located within the `randomstringutils.go` file).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aokoli/goutils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// EXAMPLE 2: A goutils function which returns an error
|
||||
rand1, err1 := goutils.Random (-1, 0, 0, true, true)
|
||||
|
||||
if err1 != nil {
|
||||
fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...)
|
||||
} else {
|
||||
fmt.Println(rand1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
## License
|
||||
GoUtils is licensed under the Apache License, Version 2.0. Please check the LICENSE.txt file or visit http://www.apache.org/licenses/LICENSE-2.0 for a copy of the license.
|
||||
|
||||
## Issue Reporting
|
||||
Make suggestions or report issues using the Git issue tracker: https://github.com/aokoli/goutils/issues
|
||||
|
||||
## Website
|
||||
* [GoUtils webpage](http://aokoli.github.io/goutils/)
|
||||
|
||||
## Mailing List
|
||||
Contact [okolialex@gmail.com](mailto:okolialex@mail.com) to be added to the mailing list. You will get updates on the
|
||||
status of the project and the potential direction it will be heading.
|
||||
|
||||
259
vendor/github.com/aokoli/goutils/randomstringutils.go
generated
vendored
259
vendor/github.com/aokoli/goutils/randomstringutils.go
generated
vendored
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Alexander Okoli
|
||||
|
||||
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 goutils
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
// Provides the time-based seed used to generate random #s
|
||||
var RANDOM = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
/*
|
||||
RandomNonAlphaNumeric creates a random string whose length is the number of characters specified.
|
||||
Characters will be chosen from the set of all characters (ASCII/Unicode values between 0 to 2,147,483,647 (math.MaxInt32)).
|
||||
|
||||
Parameter:
|
||||
count - the length of random string to create
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||
*/
|
||||
func RandomNonAlphaNumeric (count int) (string, error) {
|
||||
return RandomAlphaNumericCustom(count, false, false)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
RandomAscii creates a random string whose length is the number of characters specified.
|
||||
Characters will be chosen from the set of characters whose ASCII value is between 32 and 126 (inclusive).
|
||||
|
||||
Parameter:
|
||||
count - the length of random string to create
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||
*/
|
||||
func RandomAscii(count int) (string, error) {
|
||||
return Random(count, 32, 127, false, false)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
RandomNumeric creates a random string whose length is the number of characters specified.
|
||||
Characters will be chosen from the set of numeric characters.
|
||||
|
||||
Parameter:
|
||||
count - the length of random string to create
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||
*/
|
||||
func RandomNumeric (count int) (string, error) {
|
||||
return Random(count, 0, 0, false, true)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
RandomAlphabetic creates a random string whose length is the number of characters specified.
|
||||
Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
|
||||
|
||||
Parameters:
|
||||
count - the length of random string to create
|
||||
letters - if true, generated string may include alphabetic characters
|
||||
numbers - if true, generated string may include numeric characters
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||
*/
|
||||
func RandomAlphabetic (count int) (string, error) {
|
||||
return Random(count, 0, 0, true, false)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
RandomAlphaNumeric creates a random string whose length is the number of characters specified.
|
||||
Characters will be chosen from the set of alpha-numeric characters.
|
||||
|
||||
Parameter:
|
||||
count - the length of random string to create
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||
*/
|
||||
func RandomAlphaNumeric (count int) (string, error) {
|
||||
return Random(count, 0, 0, true, true)
|
||||
}
|
||||
|
||||
/*
|
||||
RandomAlphaNumericCustom creates a random string whose length is the number of characters specified.
|
||||
Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
|
||||
|
||||
Parameters:
|
||||
count - the length of random string to create
|
||||
letters - if true, generated string may include alphabetic characters
|
||||
numbers - if true, generated string may include numeric characters
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||
*/
|
||||
func RandomAlphaNumericCustom (count int, letters bool, numbers bool) (string, error) {
|
||||
return Random(count, 0, 0, letters, numbers)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Random creates a random string based on a variety of options, using default source of randomness.
|
||||
This method has exactly the same semantics as RandomSeed(int, int, int, bool, bool, []char, *rand.Rand), but
|
||||
instead of using an externally supplied source of randomness, it uses the internal *rand.Rand instance.
|
||||
|
||||
Parameters:
|
||||
count - the length of random string to create
|
||||
start - the position in set of chars (ASCII/Unicode int) to start at
|
||||
end - the position in set of chars (ASCII/Unicode int) to end before
|
||||
letters - if true, generated string may include alphabetic characters
|
||||
numbers - if true, generated string may include numeric characters
|
||||
chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||
*/
|
||||
func Random (count int, start int, end int, letters bool, numbers bool, chars ...rune) (string, error) {
|
||||
return RandomSeed (count, start, end, letters, numbers, chars, RANDOM)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
RandomSeed creates a random string based on a variety of options, using supplied source of randomness.
|
||||
If the parameters start and end are both 0, start and end are set to ' ' and 'z', the ASCII printable characters, will be used,
|
||||
unless letters and numbers are both false, in which case, start and end are set to 0 and math.MaxInt32, respectively.
|
||||
If chars is not nil, characters stored in chars that are between start and end are chosen.
|
||||
This method accepts a user-supplied *rand.Rand instance to use as a source of randomness. By seeding a single *rand.Rand instance
|
||||
with a fixed seed and using it for each call, the same random sequence of strings can be generated repeatedly and predictably.
|
||||
|
||||
Parameters:
|
||||
count - the length of random string to create
|
||||
start - the position in set of chars (ASCII/Unicode decimals) to start at
|
||||
end - the position in set of chars (ASCII/Unicode decimals) to end before
|
||||
letters - if true, generated string may include alphabetic characters
|
||||
numbers - if true, generated string may include numeric characters
|
||||
chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
|
||||
random - a source of randomness.
|
||||
|
||||
Returns:
|
||||
string - the random string
|
||||
error - an error stemming from invalid parameters: if count < 0; or the provided chars array is empty; or end <= start; or end > len(chars)
|
||||
*/
|
||||
func RandomSeed (count int, start int, end int, letters bool, numbers bool, chars []rune, random *rand.Rand) (string, error) {
|
||||
|
||||
if count == 0 {
|
||||
return "", nil
|
||||
} else if count < 0 {
|
||||
err := fmt.Errorf("randomstringutils illegal argument: Requested random string length %v is less than 0.", count) // equiv to err := errors.New("...")
|
||||
return "", err
|
||||
}
|
||||
if chars != nil && len(chars) == 0 {
|
||||
err := fmt.Errorf("randomstringutils illegal argument: The chars array must not be empty")
|
||||
return "", err
|
||||
}
|
||||
|
||||
if start == 0 && end == 0 {
|
||||
if chars != nil {
|
||||
end = len(chars)
|
||||
} else {
|
||||
if !letters && !numbers {
|
||||
end = math.MaxInt32
|
||||
} else {
|
||||
end = 'z' + 1
|
||||
start = ' '
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if end <= start {
|
||||
err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) must be greater than start (%v)", end, start)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if chars != nil && end > len(chars) {
|
||||
err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) cannot be greater than len(chars) (%v)", end, len(chars))
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
buffer := make([]rune, count)
|
||||
gap := end - start
|
||||
|
||||
|
||||
// high-surrogates range, (\uD800-\uDBFF) = 55296 - 56319
|
||||
// low-surrogates range, (\uDC00-\uDFFF) = 56320 - 57343
|
||||
|
||||
for count != 0 {
|
||||
count--
|
||||
var ch rune
|
||||
if chars == nil {
|
||||
ch = rune(random.Intn(gap) + start)
|
||||
} else {
|
||||
ch = chars[random.Intn(gap) + start]
|
||||
}
|
||||
|
||||
if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {
|
||||
if ch >= 56320 && ch <= 57343 { // low surrogate range
|
||||
if count == 0 {
|
||||
count++
|
||||
} else {
|
||||
// Insert low surrogate
|
||||
buffer[count] = ch
|
||||
count--
|
||||
// Insert high surrogate
|
||||
buffer[count] = rune(55296 + random.Intn(128))
|
||||
}
|
||||
} else if ch >= 55296 && ch <= 56191 { // High surrogates range (Partial)
|
||||
if count == 0 {
|
||||
count++
|
||||
} else {
|
||||
// Insert low surrogate
|
||||
buffer[count] = rune(56320 + random.Intn(128))
|
||||
count--
|
||||
// Insert high surrogate
|
||||
buffer[count] = ch
|
||||
}
|
||||
} else if ch >= 56192 && ch <= 56319 {
|
||||
// private high surrogate, skip it
|
||||
count++
|
||||
} else {
|
||||
// not one of the surrogates*
|
||||
buffer[count] = ch
|
||||
}
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return string(buffer), nil
|
||||
}
|
||||
232
vendor/github.com/aokoli/goutils/stringutils.go
generated
vendored
232
vendor/github.com/aokoli/goutils/stringutils.go
generated
vendored
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Alexander Okoli
|
||||
|
||||
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 goutils
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Typically returned by functions where a searched item cannot be found
|
||||
const INDEX_NOT_FOUND = -1
|
||||
|
||||
|
||||
/*
|
||||
Abbreviate abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "Now is the time for..."
|
||||
|
||||
Specifically, the algorithm is as follows:
|
||||
|
||||
- If str is less than maxWidth characters long, return it.
|
||||
- Else abbreviate it to (str[0:maxWidth - 3] + "...").
|
||||
- If maxWidth is less than 4, return an illegal argument error.
|
||||
- In no case will it return a string of length greater than maxWidth.
|
||||
|
||||
Parameters:
|
||||
str - the string to check
|
||||
maxWidth - maximum length of result string, must be at least 4
|
||||
|
||||
Returns:
|
||||
string - abbreviated string
|
||||
error - if the width is too small
|
||||
*/
|
||||
func Abbreviate (str string, maxWidth int) (string, error) {
|
||||
return AbbreviateFull(str, 0, maxWidth)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
AbbreviateFull abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "...is the time for..."
|
||||
This function works like Abbreviate(string, int), but allows you to specify a "left edge" offset. Note that this left edge is not
|
||||
necessarily going to be the leftmost character in the result, or the first character following the ellipses, but it will appear
|
||||
somewhere in the result.
|
||||
In no case will it return a string of length greater than maxWidth.
|
||||
|
||||
Parameters:
|
||||
str - the string to check
|
||||
offset - left edge of source string
|
||||
maxWidth - maximum length of result string, must be at least 4
|
||||
|
||||
Returns:
|
||||
string - abbreviated string
|
||||
error - if the width is too small
|
||||
*/
|
||||
func AbbreviateFull (str string, offset int, maxWidth int) (string, error) {
|
||||
if str == "" {
|
||||
return "", nil
|
||||
}
|
||||
if maxWidth < 4 {
|
||||
err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width is 4")
|
||||
return "", err
|
||||
}
|
||||
if len(str) <= maxWidth {
|
||||
return str, nil
|
||||
}
|
||||
if offset > len(str) {
|
||||
offset = len(str)
|
||||
}
|
||||
if len(str) - offset < (maxWidth - 3) { // 15 - 5 < 10 - 3 = 10 < 7
|
||||
offset = len(str) - (maxWidth - 3)
|
||||
}
|
||||
abrevMarker := "..."
|
||||
if offset <= 4 {
|
||||
return str[0:maxWidth - 3] + abrevMarker, nil// str.substring(0, maxWidth - 3) + abrevMarker;
|
||||
}
|
||||
if maxWidth < 7 {
|
||||
err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width with offset is 7")
|
||||
return "", err
|
||||
}
|
||||
if (offset + maxWidth - 3) < len(str) { // 5 + (10-3) < 15 = 12 < 15
|
||||
abrevStr, _ := Abbreviate(str[offset:len(str)], (maxWidth - 3))
|
||||
return abrevMarker + abrevStr, nil// abrevMarker + abbreviate(str.substring(offset), maxWidth - 3);
|
||||
}
|
||||
return abrevMarker + str[(len(str) - (maxWidth - 3)):len(str)], nil // abrevMarker + str.substring(str.length() - (maxWidth - 3));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
DeleteWhiteSpace deletes all whitespaces from a string as defined by unicode.IsSpace(rune).
|
||||
It returns the string without whitespaces.
|
||||
|
||||
Parameter:
|
||||
str - the string to delete whitespace from, may be nil
|
||||
|
||||
Returns:
|
||||
the string without whitespaces
|
||||
*/
|
||||
func DeleteWhiteSpace(str string) string {
|
||||
if str == "" {
|
||||
return str
|
||||
}
|
||||
sz := len(str)
|
||||
var chs bytes.Buffer
|
||||
count := 0
|
||||
for i := 0; i < sz; i++ {
|
||||
ch := rune(str[i])
|
||||
if !unicode.IsSpace(ch) {
|
||||
chs.WriteRune(ch)
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == sz {
|
||||
return str
|
||||
}
|
||||
return chs.String()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
IndexOfDifference compares two strings, and returns the index at which the strings begin to differ.
|
||||
|
||||
Parameters:
|
||||
str1 - the first string
|
||||
str2 - the second string
|
||||
|
||||
Returns:
|
||||
the index where str1 and str2 begin to differ; -1 if they are equal
|
||||
*/
|
||||
func IndexOfDifference(str1 string, str2 string) int {
|
||||
if str1 == str2 {
|
||||
return INDEX_NOT_FOUND
|
||||
}
|
||||
if IsEmpty(str1) || IsEmpty(str2) {
|
||||
return 0
|
||||
}
|
||||
var i int;
|
||||
for i = 0; i < len(str1) && i < len(str2); i++ {
|
||||
if rune(str1[i]) != rune(str2[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i < len(str2) || i < len(str1) {
|
||||
return i
|
||||
}
|
||||
return INDEX_NOT_FOUND
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
IsBlank checks if a string is whitespace or empty (""). Observe the following behavior:
|
||||
|
||||
goutils.IsBlank("") = true
|
||||
goutils.IsBlank(" ") = true
|
||||
goutils.IsBlank("bob") = false
|
||||
goutils.IsBlank(" bob ") = false
|
||||
|
||||
Parameter:
|
||||
str - the string to check
|
||||
|
||||
Returns:
|
||||
true - if the string is whitespace or empty ("")
|
||||
*/
|
||||
func IsBlank(str string) bool {
|
||||
strLen := len(str)
|
||||
if str == "" || strLen == 0 {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < strLen; i++ {
|
||||
if unicode.IsSpace(rune(str[i])) == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
IndexOf returns the index of the first instance of sub in str, with the search beginning from the
|
||||
index start point specified. -1 is returned if sub is not present in str.
|
||||
|
||||
An empty string ("") will return -1 (INDEX_NOT_FOUND). A negative start position is treated as zero.
|
||||
A start position greater than the string length returns -1.
|
||||
|
||||
Parameters:
|
||||
str - the string to check
|
||||
sub - the substring to find
|
||||
start - the start position; negative treated as zero
|
||||
|
||||
Returns:
|
||||
the first index where the sub string was found (always >= start)
|
||||
*/
|
||||
func IndexOf(str string, sub string, start int) int {
|
||||
|
||||
if (start < 0) {
|
||||
start = 0
|
||||
}
|
||||
|
||||
if len(str) < start {
|
||||
return INDEX_NOT_FOUND
|
||||
}
|
||||
|
||||
if IsEmpty(str) || IsEmpty(sub) {
|
||||
return INDEX_NOT_FOUND
|
||||
}
|
||||
|
||||
partialIndex := strings.Index(str[start:len(str)], sub)
|
||||
if partialIndex == -1 {
|
||||
return INDEX_NOT_FOUND
|
||||
}
|
||||
return partialIndex + start
|
||||
}
|
||||
|
||||
// IsEmpty checks if a string is empty (""). Returns true if empty, and false otherwise.
|
||||
func IsEmpty(str string) bool {
|
||||
return len(str) == 0
|
||||
}
|
||||
365
vendor/github.com/aokoli/goutils/wordutils.go
generated
vendored
365
vendor/github.com/aokoli/goutils/wordutils.go
generated
vendored
@@ -1,365 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Alexander Okoli
|
||||
|
||||
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 goutils provides utility functions to manipulate strings in various ways.
|
||||
The code snippets below show examples of how to use goutils. Some functions return
|
||||
errors while others do not, so usage would vary as a result.
|
||||
|
||||
Example:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aokoli/goutils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// EXAMPLE 1: A goutils function which returns no errors
|
||||
fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF"
|
||||
|
||||
|
||||
|
||||
// EXAMPLE 2: A goutils function which returns an error
|
||||
rand1, err1 := goutils.Random (-1, 0, 0, true, true)
|
||||
|
||||
if err1 != nil {
|
||||
fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...)
|
||||
} else {
|
||||
fmt.Println(rand1)
|
||||
}
|
||||
}
|
||||
*/
|
||||
package goutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// VERSION indicates the current version of goutils
|
||||
const VERSION = "1.0.0"
|
||||
|
||||
/*
|
||||
Wrap wraps a single line of text, identifying words by ' '.
|
||||
New lines will be separated by '\n'. Very long words, such as URLs will not be wrapped.
|
||||
Leading spaces on a new line are stripped. Trailing spaces are not stripped.
|
||||
|
||||
Parameters:
|
||||
str - the string to be word wrapped
|
||||
wrapLength - the column (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
|
||||
|
||||
Returns:
|
||||
a line with newlines inserted
|
||||
*/
|
||||
func Wrap (str string, wrapLength int) string {
|
||||
return WrapCustom (str, wrapLength, "", false)
|
||||
}
|
||||
|
||||
/*
|
||||
WrapCustom wraps a single line of text, identifying words by ' '.
|
||||
Leading spaces on a new line are stripped. Trailing spaces are not stripped.
|
||||
|
||||
Parameters:
|
||||
str - the string to be word wrapped
|
||||
wrapLength - the column number (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
|
||||
newLineStr - the string to insert for a new line, "" uses '\n'
|
||||
wrapLongWords - true if long words (such as URLs) should be wrapped
|
||||
|
||||
Returns:
|
||||
a line with newlines inserted
|
||||
*/
|
||||
func WrapCustom (str string, wrapLength int, newLineStr string, wrapLongWords bool) string {
|
||||
|
||||
if str == "" {
|
||||
return ""
|
||||
}
|
||||
if newLineStr == "" {
|
||||
newLineStr = "\n" // TODO Assumes "\n" is seperator. Explore SystemUtils.LINE_SEPARATOR from Apache Commons
|
||||
}
|
||||
if wrapLength < 1 {
|
||||
wrapLength = 1
|
||||
}
|
||||
|
||||
inputLineLength := len(str)
|
||||
offset := 0
|
||||
|
||||
var wrappedLine bytes.Buffer
|
||||
|
||||
for inputLineLength-offset > wrapLength {
|
||||
|
||||
if rune(str[offset]) == ' ' {
|
||||
offset++
|
||||
continue
|
||||
}
|
||||
|
||||
end := wrapLength + offset + 1
|
||||
spaceToWrapAt := strings.LastIndex(str[offset:end], " ") + offset
|
||||
|
||||
if spaceToWrapAt >= offset {
|
||||
// normal word (not longer than wrapLength)
|
||||
wrappedLine.WriteString(str[offset:spaceToWrapAt])
|
||||
wrappedLine.WriteString(newLineStr)
|
||||
offset = spaceToWrapAt + 1
|
||||
|
||||
} else {
|
||||
// long word or URL
|
||||
if wrapLongWords {
|
||||
end := wrapLength + offset
|
||||
// long words are wrapped one line at a time
|
||||
wrappedLine.WriteString(str[offset:end])
|
||||
wrappedLine.WriteString(newLineStr)
|
||||
offset += wrapLength
|
||||
} else {
|
||||
// long words aren't wrapped, just extended beyond limit
|
||||
end := wrapLength + offset
|
||||
spaceToWrapAt = strings.IndexRune(str[end:len(str)], ' ') + end
|
||||
if spaceToWrapAt >= 0 {
|
||||
wrappedLine.WriteString(str[offset:spaceToWrapAt])
|
||||
wrappedLine.WriteString(newLineStr)
|
||||
offset = spaceToWrapAt + 1
|
||||
} else {
|
||||
wrappedLine.WriteString(str[offset:len(str)])
|
||||
offset = inputLineLength
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wrappedLine.WriteString(str[offset:len(str)])
|
||||
|
||||
return wrappedLine.String()
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Capitalize capitalizes all the delimiter separated words in a string. Only the first letter of each word is changed.
|
||||
To convert the rest of each word to lowercase at the same time, use CapitalizeFully(str string, delimiters ...rune).
|
||||
The delimiters represent a set of characters understood to separate words. The first string character
|
||||
and the first non-delimiter character after a delimiter will be capitalized. A "" input string returns "".
|
||||
Capitalization uses the Unicode title case, normally equivalent to upper case.
|
||||
|
||||
Parameters:
|
||||
str - the string to capitalize
|
||||
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
|
||||
|
||||
Returns:
|
||||
capitalized string
|
||||
*/
|
||||
func Capitalize (str string, delimiters ...rune) string {
|
||||
|
||||
var delimLen int
|
||||
|
||||
if delimiters == nil {
|
||||
delimLen = -1
|
||||
} else {
|
||||
delimLen = len(delimiters)
|
||||
}
|
||||
|
||||
if str == "" || delimLen == 0 {
|
||||
return str;
|
||||
}
|
||||
|
||||
buffer := []rune(str)
|
||||
capitalizeNext := true
|
||||
for i := 0; i < len(buffer); i++ {
|
||||
ch := buffer[i]
|
||||
if isDelimiter(ch, delimiters...) {
|
||||
capitalizeNext = true
|
||||
} else if capitalizeNext {
|
||||
buffer[i] = unicode.ToTitle(ch)
|
||||
capitalizeNext = false
|
||||
}
|
||||
}
|
||||
return string(buffer)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
CapitalizeFully converts all the delimiter separated words in a string into capitalized words, that is each word is made up of a
|
||||
titlecase character and then a series of lowercase characters. The delimiters represent a set of characters understood
|
||||
to separate words. The first string character and the first non-delimiter character after a delimiter will be capitalized.
|
||||
Capitalization uses the Unicode title case, normally equivalent to upper case.
|
||||
|
||||
Parameters:
|
||||
str - the string to capitalize fully
|
||||
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
|
||||
|
||||
Returns:
|
||||
capitalized string
|
||||
*/
|
||||
func CapitalizeFully (str string, delimiters ...rune) string {
|
||||
|
||||
var delimLen int
|
||||
|
||||
if delimiters == nil {
|
||||
delimLen = -1
|
||||
} else {
|
||||
delimLen = len(delimiters)
|
||||
}
|
||||
|
||||
|
||||
if str == "" || delimLen == 0 {
|
||||
return str;
|
||||
}
|
||||
str = strings.ToLower(str)
|
||||
return Capitalize(str, delimiters...);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Uncapitalize uncapitalizes all the whitespace separated words in a string. Only the first letter of each word is changed.
|
||||
The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter
|
||||
character after a delimiter will be uncapitalized. Whitespace is defined by unicode.IsSpace(char).
|
||||
|
||||
Parameters:
|
||||
str - the string to uncapitalize fully
|
||||
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
|
||||
|
||||
Returns:
|
||||
uncapitalized string
|
||||
*/
|
||||
func Uncapitalize (str string, delimiters ...rune) string {
|
||||
|
||||
var delimLen int
|
||||
|
||||
if delimiters == nil {
|
||||
delimLen = -1
|
||||
} else {
|
||||
delimLen = len(delimiters)
|
||||
}
|
||||
|
||||
if str == "" || delimLen == 0 {
|
||||
return str;
|
||||
}
|
||||
|
||||
buffer := []rune(str)
|
||||
uncapitalizeNext := true // TODO Always makes capitalize/un apply to first char.
|
||||
for i := 0; i < len(buffer); i++ {
|
||||
ch := buffer[i]
|
||||
if isDelimiter(ch, delimiters...) {
|
||||
uncapitalizeNext = true
|
||||
} else if uncapitalizeNext {
|
||||
buffer[i] = unicode.ToLower(ch)
|
||||
uncapitalizeNext = false
|
||||
}
|
||||
}
|
||||
return string(buffer)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
SwapCase swaps the case of a string using a word based algorithm.
|
||||
|
||||
Conversion algorithm:
|
||||
|
||||
Upper case character converts to Lower case
|
||||
Title case character converts to Lower case
|
||||
Lower case character after Whitespace or at start converts to Title case
|
||||
Other Lower case character converts to Upper case
|
||||
Whitespace is defined by unicode.IsSpace(char).
|
||||
|
||||
Parameters:
|
||||
str - the string to swap case
|
||||
|
||||
Returns:
|
||||
the changed string
|
||||
*/
|
||||
func SwapCase(str string) string {
|
||||
if str == "" {
|
||||
return str
|
||||
}
|
||||
buffer := []rune(str)
|
||||
|
||||
whitespace := true
|
||||
|
||||
for i := 0; i < len(buffer); i++ {
|
||||
ch := buffer[i]
|
||||
if unicode.IsUpper(ch) {
|
||||
buffer[i] = unicode.ToLower(ch)
|
||||
whitespace = false
|
||||
} else if unicode.IsTitle(ch) {
|
||||
buffer[i] = unicode.ToLower(ch)
|
||||
whitespace = false
|
||||
} else if unicode.IsLower(ch) {
|
||||
if whitespace {
|
||||
buffer[i] = unicode.ToTitle(ch)
|
||||
whitespace = false
|
||||
} else {
|
||||
buffer[i] = unicode.ToUpper(ch)
|
||||
}
|
||||
} else {
|
||||
whitespace = unicode.IsSpace(ch)
|
||||
}
|
||||
}
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Initials extracts the initial letters from each word in the string. The first letter of the string and all first
|
||||
letters after the defined delimiters are returned as a new string. Their case is not changed. If the delimiters
|
||||
parameter is excluded, then Whitespace is used. Whitespace is defined by unicode.IsSpacea(char). An empty delimiter array returns an empty string.
|
||||
|
||||
Parameters:
|
||||
str - the string to get initials from
|
||||
delimiters - set of characters to determine words, exclusion of this parameter means whitespace would be delimeter
|
||||
Returns:
|
||||
string of initial letters
|
||||
*/
|
||||
func Initials(str string, delimiters ...rune) string {
|
||||
if str == "" {
|
||||
return str
|
||||
}
|
||||
if delimiters != nil && len(delimiters) == 0 {
|
||||
return ""
|
||||
}
|
||||
strLen := len(str)
|
||||
var buf bytes.Buffer
|
||||
lastWasGap := true
|
||||
for i := 0; i < strLen; i++ {
|
||||
ch := rune(str[i])
|
||||
|
||||
if isDelimiter(ch, delimiters...) {
|
||||
lastWasGap = true
|
||||
} else if lastWasGap {
|
||||
buf.WriteRune(ch)
|
||||
lastWasGap = false
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
||||
|
||||
// private function (lower case func name)
|
||||
func isDelimiter(ch rune, delimiters ...rune) bool {
|
||||
if delimiters == nil {
|
||||
return unicode.IsSpace(ch)
|
||||
}
|
||||
for _, delimiter := range delimiters {
|
||||
if ch == delimiter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
20
vendor/github.com/armon/go-radix/LICENSE
generated
vendored
20
vendor/github.com/armon/go-radix/LICENSE
generated
vendored
@@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Armon Dadgar
|
||||
|
||||
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.
|
||||
38
vendor/github.com/armon/go-radix/README.md
generated
vendored
38
vendor/github.com/armon/go-radix/README.md
generated
vendored
@@ -1,38 +0,0 @@
|
||||
go-radix [](https://travis-ci.org/armon/go-radix)
|
||||
=========
|
||||
|
||||
Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree).
|
||||
The package only provides a single `Tree` implementation, optimized for sparse nodes.
|
||||
|
||||
As a radix tree, it provides the following:
|
||||
* O(k) operations. In many cases, this can be faster than a hash table since
|
||||
the hash function is an O(k) operation, and hash tables have very poor cache locality.
|
||||
* Minimum / Maximum value lookups
|
||||
* Ordered iteration
|
||||
|
||||
For an immutable variant, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix).
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix).
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Below is a simple example of usage
|
||||
|
||||
```go
|
||||
// Create a tree
|
||||
r := radix.New()
|
||||
r.Insert("foo", 1)
|
||||
r.Insert("bar", 2)
|
||||
r.Insert("foobar", 2)
|
||||
|
||||
// Find the longest prefix match
|
||||
m, _, _ := r.LongestPrefix("foozip")
|
||||
if m != "foo" {
|
||||
panic("should be foo")
|
||||
}
|
||||
```
|
||||
|
||||
496
vendor/github.com/armon/go-radix/radix.go
generated
vendored
496
vendor/github.com/armon/go-radix/radix.go
generated
vendored
@@ -1,496 +0,0 @@
|
||||
package radix
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WalkFn is used when walking the tree. Takes a
|
||||
// key and value, returning if iteration should
|
||||
// be terminated.
|
||||
type WalkFn func(s string, v interface{}) bool
|
||||
|
||||
// leafNode is used to represent a value
|
||||
type leafNode struct {
|
||||
key string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
// edge is used to represent an edge node
|
||||
type edge struct {
|
||||
label byte
|
||||
node *node
|
||||
}
|
||||
|
||||
type node struct {
|
||||
// leaf is used to store possible leaf
|
||||
leaf *leafNode
|
||||
|
||||
// prefix is the common prefix we ignore
|
||||
prefix string
|
||||
|
||||
// Edges should be stored in-order for iteration.
|
||||
// We avoid a fully materialized slice to save memory,
|
||||
// since in most cases we expect to be sparse
|
||||
edges edges
|
||||
}
|
||||
|
||||
func (n *node) isLeaf() bool {
|
||||
return n.leaf != nil
|
||||
}
|
||||
|
||||
func (n *node) addEdge(e edge) {
|
||||
n.edges = append(n.edges, e)
|
||||
n.edges.Sort()
|
||||
}
|
||||
|
||||
func (n *node) replaceEdge(e edge) {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
return n.edges[i].label >= e.label
|
||||
})
|
||||
if idx < num && n.edges[idx].label == e.label {
|
||||
n.edges[idx].node = e.node
|
||||
return
|
||||
}
|
||||
panic("replacing missing edge")
|
||||
}
|
||||
|
||||
func (n *node) getEdge(label byte) *node {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
return n.edges[i].label >= label
|
||||
})
|
||||
if idx < num && n.edges[idx].label == label {
|
||||
return n.edges[idx].node
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) delEdge(label byte) {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
return n.edges[i].label >= label
|
||||
})
|
||||
if idx < num && n.edges[idx].label == label {
|
||||
copy(n.edges[idx:], n.edges[idx+1:])
|
||||
n.edges[len(n.edges)-1] = edge{}
|
||||
n.edges = n.edges[:len(n.edges)-1]
|
||||
}
|
||||
}
|
||||
|
||||
type edges []edge
|
||||
|
||||
func (e edges) Len() int {
|
||||
return len(e)
|
||||
}
|
||||
|
||||
func (e edges) Less(i, j int) bool {
|
||||
return e[i].label < e[j].label
|
||||
}
|
||||
|
||||
func (e edges) Swap(i, j int) {
|
||||
e[i], e[j] = e[j], e[i]
|
||||
}
|
||||
|
||||
func (e edges) Sort() {
|
||||
sort.Sort(e)
|
||||
}
|
||||
|
||||
// Tree implements a radix tree. This can be treated as a
|
||||
// Dictionary abstract data type. The main advantage over
|
||||
// a standard hash map is prefix-based lookups and
|
||||
// ordered iteration,
|
||||
type Tree struct {
|
||||
root *node
|
||||
size int
|
||||
}
|
||||
|
||||
// New returns an empty Tree
|
||||
func New() *Tree {
|
||||
return NewFromMap(nil)
|
||||
}
|
||||
|
||||
// NewFromMap returns a new tree containing the keys
|
||||
// from an existing map
|
||||
func NewFromMap(m map[string]interface{}) *Tree {
|
||||
t := &Tree{root: &node{}}
|
||||
for k, v := range m {
|
||||
t.Insert(k, v)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Len is used to return the number of elements in the tree
|
||||
func (t *Tree) Len() int {
|
||||
return t.size
|
||||
}
|
||||
|
||||
// longestPrefix finds the length of the shared prefix
|
||||
// of two strings
|
||||
func longestPrefix(k1, k2 string) int {
|
||||
max := len(k1)
|
||||
if l := len(k2); l < max {
|
||||
max = l
|
||||
}
|
||||
var i int
|
||||
for i = 0; i < max; i++ {
|
||||
if k1[i] != k2[i] {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Insert is used to add a newentry or update
|
||||
// an existing entry. Returns if updated.
|
||||
func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) {
|
||||
var parent *node
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Handle key exhaution
|
||||
if len(search) == 0 {
|
||||
if n.isLeaf() {
|
||||
old := n.leaf.val
|
||||
n.leaf.val = v
|
||||
return old, true
|
||||
}
|
||||
|
||||
n.leaf = &leafNode{
|
||||
key: s,
|
||||
val: v,
|
||||
}
|
||||
t.size++
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Look for the edge
|
||||
parent = n
|
||||
n = n.getEdge(search[0])
|
||||
|
||||
// No edge, create one
|
||||
if n == nil {
|
||||
e := edge{
|
||||
label: search[0],
|
||||
node: &node{
|
||||
leaf: &leafNode{
|
||||
key: s,
|
||||
val: v,
|
||||
},
|
||||
prefix: search,
|
||||
},
|
||||
}
|
||||
parent.addEdge(e)
|
||||
t.size++
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Determine longest prefix of the search key on match
|
||||
commonPrefix := longestPrefix(search, n.prefix)
|
||||
if commonPrefix == len(n.prefix) {
|
||||
search = search[commonPrefix:]
|
||||
continue
|
||||
}
|
||||
|
||||
// Split the node
|
||||
t.size++
|
||||
child := &node{
|
||||
prefix: search[:commonPrefix],
|
||||
}
|
||||
parent.replaceEdge(edge{
|
||||
label: search[0],
|
||||
node: child,
|
||||
})
|
||||
|
||||
// Restore the existing node
|
||||
child.addEdge(edge{
|
||||
label: n.prefix[commonPrefix],
|
||||
node: n,
|
||||
})
|
||||
n.prefix = n.prefix[commonPrefix:]
|
||||
|
||||
// Create a new leaf node
|
||||
leaf := &leafNode{
|
||||
key: s,
|
||||
val: v,
|
||||
}
|
||||
|
||||
// If the new key is a subset, add to to this node
|
||||
search = search[commonPrefix:]
|
||||
if len(search) == 0 {
|
||||
child.leaf = leaf
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Create a new edge for the node
|
||||
child.addEdge(edge{
|
||||
label: search[0],
|
||||
node: &node{
|
||||
leaf: leaf,
|
||||
prefix: search,
|
||||
},
|
||||
})
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Delete is used to delete a key, returning the previous
|
||||
// value and if it was deleted
|
||||
func (t *Tree) Delete(s string) (interface{}, bool) {
|
||||
var parent *node
|
||||
var label byte
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
if !n.isLeaf() {
|
||||
break
|
||||
}
|
||||
goto DELETE
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
parent = n
|
||||
label = search[0]
|
||||
n = n.getEdge(label)
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
|
||||
DELETE:
|
||||
// Delete the leaf
|
||||
leaf := n.leaf
|
||||
n.leaf = nil
|
||||
t.size--
|
||||
|
||||
// Check if we should delete this node from the parent
|
||||
if parent != nil && len(n.edges) == 0 {
|
||||
parent.delEdge(label)
|
||||
}
|
||||
|
||||
// Check if we should merge this node
|
||||
if n != t.root && len(n.edges) == 1 {
|
||||
n.mergeChild()
|
||||
}
|
||||
|
||||
// Check if we should merge the parent's other child
|
||||
if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() {
|
||||
parent.mergeChild()
|
||||
}
|
||||
|
||||
return leaf.val, true
|
||||
}
|
||||
|
||||
func (n *node) mergeChild() {
|
||||
e := n.edges[0]
|
||||
child := e.node
|
||||
n.prefix = n.prefix + child.prefix
|
||||
n.leaf = child.leaf
|
||||
n.edges = child.edges
|
||||
}
|
||||
|
||||
// Get is used to lookup a specific key, returning
|
||||
// the value and if it was found
|
||||
func (t *Tree) Get(s string) (interface{}, bool) {
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
if n.isLeaf() {
|
||||
return n.leaf.val, true
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// LongestPrefix is like Get, but instead of an
|
||||
// exact match, it will return the longest prefix match.
|
||||
func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) {
|
||||
var last *leafNode
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Look for a leaf node
|
||||
if n.isLeaf() {
|
||||
last = n.leaf
|
||||
}
|
||||
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if last != nil {
|
||||
return last.key, last.val, true
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
// Minimum is used to return the minimum value in the tree
|
||||
func (t *Tree) Minimum() (string, interface{}, bool) {
|
||||
n := t.root
|
||||
for {
|
||||
if n.isLeaf() {
|
||||
return n.leaf.key, n.leaf.val, true
|
||||
}
|
||||
if len(n.edges) > 0 {
|
||||
n = n.edges[0].node
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
// Maximum is used to return the maximum value in the tree
|
||||
func (t *Tree) Maximum() (string, interface{}, bool) {
|
||||
n := t.root
|
||||
for {
|
||||
if num := len(n.edges); num > 0 {
|
||||
n = n.edges[num-1].node
|
||||
continue
|
||||
}
|
||||
if n.isLeaf() {
|
||||
return n.leaf.key, n.leaf.val, true
|
||||
}
|
||||
break
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
// Walk is used to walk the tree
|
||||
func (t *Tree) Walk(fn WalkFn) {
|
||||
recursiveWalk(t.root, fn)
|
||||
}
|
||||
|
||||
// WalkPrefix is used to walk the tree under a prefix
|
||||
func (t *Tree) WalkPrefix(prefix string, fn WalkFn) {
|
||||
n := t.root
|
||||
search := prefix
|
||||
for {
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
recursiveWalk(n, fn)
|
||||
return
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
|
||||
} else if strings.HasPrefix(n.prefix, search) {
|
||||
// Child may be under our search prefix
|
||||
recursiveWalk(n, fn)
|
||||
return
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// WalkPath is used to walk the tree, but only visiting nodes
|
||||
// from the root down to a given leaf. Where WalkPrefix walks
|
||||
// all the entries *under* the given prefix, this walks the
|
||||
// entries *above* the given prefix.
|
||||
func (t *Tree) WalkPath(path string, fn WalkFn) {
|
||||
n := t.root
|
||||
search := path
|
||||
for {
|
||||
// Visit the leaf values if any
|
||||
if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recursiveWalk is used to do a pre-order walk of a node
|
||||
// recursively. Returns true if the walk should be aborted
|
||||
func recursiveWalk(n *node, fn WalkFn) bool {
|
||||
// Visit the leaf values if any
|
||||
if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Recurse on the children
|
||||
for _, e := range n.edges {
|
||||
if recursiveWalk(e.node, fn) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ToMap is used to walk the tree and convert it into a map
|
||||
func (t *Tree) ToMap() map[string]interface{} {
|
||||
out := make(map[string]interface{}, t.size)
|
||||
t.Walk(func(k string, v interface{}) bool {
|
||||
out[k] = v
|
||||
return false
|
||||
})
|
||||
return out
|
||||
}
|
||||
50
vendor/github.com/ghodss/yaml/LICENSE
generated
vendored
50
vendor/github.com/ghodss/yaml/LICENSE
generated
vendored
@@ -1,50 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Sam Ghods
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
121
vendor/github.com/ghodss/yaml/README.md
generated
vendored
121
vendor/github.com/ghodss/yaml/README.md
generated
vendored
@@ -1,121 +0,0 @@
|
||||
# YAML marshaling and unmarshaling support for Go
|
||||
|
||||
[](https://travis-ci.org/ghodss/yaml)
|
||||
|
||||
## Introduction
|
||||
|
||||
A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs.
|
||||
|
||||
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
|
||||
|
||||
## Compatibility
|
||||
|
||||
This package uses [go-yaml](https://github.com/go-yaml/yaml) and therefore supports [everything go-yaml supports](https://github.com/go-yaml/yaml#compatibility).
|
||||
|
||||
## Caveats
|
||||
|
||||
**Caveat #1:** When using `yaml.Marshal` and `yaml.Unmarshal`, binary data should NOT be preceded with the `!!binary` YAML tag. If you do, go-yaml will convert the binary data from base64 to native binary data, which is not compatible with JSON. You can still use binary in your YAML files though - just store them without the `!!binary` tag and decode the base64 in your code (e.g. in the custom JSON methods `MarshalJSON` and `UnmarshalJSON`). This also has the benefit that your YAML and your JSON binary data will be decoded exactly the same way. As an example:
|
||||
|
||||
```
|
||||
BAD:
|
||||
exampleKey: !!binary gIGC
|
||||
|
||||
GOOD:
|
||||
exampleKey: gIGC
|
||||
... and decode the base64 data in your code.
|
||||
```
|
||||
|
||||
**Caveat #2:** When using `YAMLToJSON` directly, maps with keys that are maps will result in an error since this is not supported by JSON. This error will occur in `Unmarshal` as well since you can't unmarshal map keys anyways since struct fields can't be keys.
|
||||
|
||||
## Installation and usage
|
||||
|
||||
To install, run:
|
||||
|
||||
```
|
||||
$ go get github.com/ghodss/yaml
|
||||
```
|
||||
|
||||
And import using:
|
||||
|
||||
```
|
||||
import "github.com/ghodss/yaml"
|
||||
```
|
||||
|
||||
Usage is very similar to the JSON library:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
type Person struct {
|
||||
Name string `json:"name"` // Affects YAML field names too.
|
||||
Age int `json:"age"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Marshal a Person struct to YAML.
|
||||
p := Person{"John", 30}
|
||||
y, err := yaml.Marshal(p)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(y))
|
||||
/* Output:
|
||||
age: 30
|
||||
name: John
|
||||
*/
|
||||
|
||||
// Unmarshal the YAML back into a Person struct.
|
||||
var p2 Person
|
||||
err = yaml.Unmarshal(y, &p2)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(p2)
|
||||
/* Output:
|
||||
{John 30}
|
||||
*/
|
||||
}
|
||||
```
|
||||
|
||||
`yaml.YAMLToJSON` and `yaml.JSONToYAML` methods are also available:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
func main() {
|
||||
j := []byte(`{"name": "John", "age": 30}`)
|
||||
y, err := yaml.JSONToYAML(j)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(y))
|
||||
/* Output:
|
||||
name: John
|
||||
age: 30
|
||||
*/
|
||||
j2, err := yaml.YAMLToJSON(y)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(j2))
|
||||
/* Output:
|
||||
{"age":30,"name":"John"}
|
||||
*/
|
||||
}
|
||||
```
|
||||
501
vendor/github.com/ghodss/yaml/fields.go
generated
vendored
501
vendor/github.com/ghodss/yaml/fields.go
generated
vendored
@@ -1,501 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// indirect walks down v allocating pointers as needed,
|
||||
// until it gets to a non-pointer.
|
||||
// if it encounters an Unmarshaler, indirect stops and returns that.
|
||||
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
|
||||
func indirect(v reflect.Value, decodingNull bool) (json.Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
|
||||
// If v is a named type and is addressable,
|
||||
// start with its address, so that if the type has pointer methods,
|
||||
// we find them.
|
||||
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
for {
|
||||
// Load value from interface, but only if the result will be
|
||||
// usefully addressable.
|
||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
e := v.Elem()
|
||||
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
|
||||
v = e
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Ptr {
|
||||
break
|
||||
}
|
||||
|
||||
if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() {
|
||||
break
|
||||
}
|
||||
if v.IsNil() {
|
||||
if v.CanSet() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
} else {
|
||||
v = reflect.New(v.Type().Elem())
|
||||
}
|
||||
}
|
||||
if v.Type().NumMethod() > 0 {
|
||||
if u, ok := v.Interface().(json.Unmarshaler); ok {
|
||||
return u, nil, reflect.Value{}
|
||||
}
|
||||
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
|
||||
return nil, u, reflect.Value{}
|
||||
}
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
return nil, nil, v
|
||||
}
|
||||
|
||||
// A field represents a single field found in a struct.
|
||||
type field struct {
|
||||
name string
|
||||
nameBytes []byte // []byte(name)
|
||||
equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent
|
||||
|
||||
tag bool
|
||||
index []int
|
||||
typ reflect.Type
|
||||
omitEmpty bool
|
||||
quoted bool
|
||||
}
|
||||
|
||||
func fillField(f field) field {
|
||||
f.nameBytes = []byte(f.name)
|
||||
f.equalFold = foldFunc(f.nameBytes)
|
||||
return f
|
||||
}
|
||||
|
||||
// byName sorts field by name, breaking ties with depth,
|
||||
// then breaking ties with "name came from json tag", then
|
||||
// breaking ties with index sequence.
|
||||
type byName []field
|
||||
|
||||
func (x byName) Len() int { return len(x) }
|
||||
|
||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byName) Less(i, j int) bool {
|
||||
if x[i].name != x[j].name {
|
||||
return x[i].name < x[j].name
|
||||
}
|
||||
if len(x[i].index) != len(x[j].index) {
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
if x[i].tag != x[j].tag {
|
||||
return x[i].tag
|
||||
}
|
||||
return byIndex(x).Less(i, j)
|
||||
}
|
||||
|
||||
// byIndex sorts field by index sequence.
|
||||
type byIndex []field
|
||||
|
||||
func (x byIndex) Len() int { return len(x) }
|
||||
|
||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byIndex) Less(i, j int) bool {
|
||||
for k, xik := range x[i].index {
|
||||
if k >= len(x[j].index) {
|
||||
return false
|
||||
}
|
||||
if xik != x[j].index[k] {
|
||||
return xik < x[j].index[k]
|
||||
}
|
||||
}
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
|
||||
// typeFields returns a list of fields that JSON should recognize for the given type.
|
||||
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
||||
// and then any reachable anonymous structs.
|
||||
func typeFields(t reflect.Type) []field {
|
||||
// Anonymous fields to explore at the current level and the next.
|
||||
current := []field{}
|
||||
next := []field{{typ: t}}
|
||||
|
||||
// Count of queued names for current level and the next.
|
||||
count := map[reflect.Type]int{}
|
||||
nextCount := map[reflect.Type]int{}
|
||||
|
||||
// Types already visited at an earlier level.
|
||||
visited := map[reflect.Type]bool{}
|
||||
|
||||
// Fields found.
|
||||
var fields []field
|
||||
|
||||
for len(next) > 0 {
|
||||
current, next = next, current[:0]
|
||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
||||
|
||||
for _, f := range current {
|
||||
if visited[f.typ] {
|
||||
continue
|
||||
}
|
||||
visited[f.typ] = true
|
||||
|
||||
// Scan f.typ for fields to include.
|
||||
for i := 0; i < f.typ.NumField(); i++ {
|
||||
sf := f.typ.Field(i)
|
||||
if sf.PkgPath != "" { // unexported
|
||||
continue
|
||||
}
|
||||
tag := sf.Tag.Get("json")
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
name, opts := parseTag(tag)
|
||||
if !isValidTag(name) {
|
||||
name = ""
|
||||
}
|
||||
index := make([]int, len(f.index)+1)
|
||||
copy(index, f.index)
|
||||
index[len(f.index)] = i
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
||||
// Follow pointer.
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
// Record found field and index sequence.
|
||||
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
||||
tagged := name != ""
|
||||
if name == "" {
|
||||
name = sf.Name
|
||||
}
|
||||
fields = append(fields, fillField(field{
|
||||
name: name,
|
||||
tag: tagged,
|
||||
index: index,
|
||||
typ: ft,
|
||||
omitEmpty: opts.Contains("omitempty"),
|
||||
quoted: opts.Contains("string"),
|
||||
}))
|
||||
if count[f.typ] > 1 {
|
||||
// If there were multiple instances, add a second,
|
||||
// so that the annihilation code will see a duplicate.
|
||||
// It only cares about the distinction between 1 or 2,
|
||||
// so don't bother generating any more copies.
|
||||
fields = append(fields, fields[len(fields)-1])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Record new anonymous struct to explore in next round.
|
||||
nextCount[ft]++
|
||||
if nextCount[ft] == 1 {
|
||||
next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(byName(fields))
|
||||
|
||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
||||
// except that fields with JSON tags are promoted.
|
||||
|
||||
// The fields are sorted in primary order of name, secondary order
|
||||
// of field index length. Loop over names; for each name, delete
|
||||
// hidden fields by choosing the one dominant field that survives.
|
||||
out := fields[:0]
|
||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of fields with the name of this first field.
|
||||
fi := fields[i]
|
||||
name := fi.name
|
||||
for advance = 1; i+advance < len(fields); advance++ {
|
||||
fj := fields[i+advance]
|
||||
if fj.name != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
if advance == 1 { // Only one field with this name
|
||||
out = append(out, fi)
|
||||
continue
|
||||
}
|
||||
dominant, ok := dominantField(fields[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
|
||||
fields = out
|
||||
sort.Sort(byIndex(fields))
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// dominantField looks through the fields, all of which are known to
|
||||
// have the same name, to find the single field that dominates the
|
||||
// others using Go's embedding rules, modified by the presence of
|
||||
// JSON tags. If there are multiple top-level fields, the boolean
|
||||
// will be false: This condition is an error in Go and we skip all
|
||||
// the fields.
|
||||
func dominantField(fields []field) (field, bool) {
|
||||
// The fields are sorted in increasing index-length order. The winner
|
||||
// must therefore be one with the shortest index length. Drop all
|
||||
// longer entries, which is easy: just truncate the slice.
|
||||
length := len(fields[0].index)
|
||||
tagged := -1 // Index of first tagged field.
|
||||
for i, f := range fields {
|
||||
if len(f.index) > length {
|
||||
fields = fields[:i]
|
||||
break
|
||||
}
|
||||
if f.tag {
|
||||
if tagged >= 0 {
|
||||
// Multiple tagged fields at the same level: conflict.
|
||||
// Return no field.
|
||||
return field{}, false
|
||||
}
|
||||
tagged = i
|
||||
}
|
||||
}
|
||||
if tagged >= 0 {
|
||||
return fields[tagged], true
|
||||
}
|
||||
// All remaining fields have the same length. If there's more than one,
|
||||
// we have a conflict (two fields named "X" at the same level) and we
|
||||
// return no field.
|
||||
if len(fields) > 1 {
|
||||
return field{}, false
|
||||
}
|
||||
return fields[0], true
|
||||
}
|
||||
|
||||
var fieldCache struct {
|
||||
sync.RWMutex
|
||||
m map[reflect.Type][]field
|
||||
}
|
||||
|
||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
||||
func cachedTypeFields(t reflect.Type) []field {
|
||||
fieldCache.RLock()
|
||||
f := fieldCache.m[t]
|
||||
fieldCache.RUnlock()
|
||||
if f != nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// Compute fields without lock.
|
||||
// Might duplicate effort but won't hold other computations back.
|
||||
f = typeFields(t)
|
||||
if f == nil {
|
||||
f = []field{}
|
||||
}
|
||||
|
||||
fieldCache.Lock()
|
||||
if fieldCache.m == nil {
|
||||
fieldCache.m = map[reflect.Type][]field{}
|
||||
}
|
||||
fieldCache.m[t] = f
|
||||
fieldCache.Unlock()
|
||||
return f
|
||||
}
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
||||
// Backslash and quote chars are reserved, but
|
||||
// otherwise any punctuation chars are allowed
|
||||
// in a tag name.
|
||||
default:
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const (
|
||||
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
|
||||
kelvin = '\u212a'
|
||||
smallLongEss = '\u017f'
|
||||
)
|
||||
|
||||
// foldFunc returns one of four different case folding equivalence
|
||||
// functions, from most general (and slow) to fastest:
|
||||
//
|
||||
// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8
|
||||
// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S')
|
||||
// 3) asciiEqualFold, no special, but includes non-letters (including _)
|
||||
// 4) simpleLetterEqualFold, no specials, no non-letters.
|
||||
//
|
||||
// The letters S and K are special because they map to 3 runes, not just 2:
|
||||
// * S maps to s and to U+017F 'ſ' Latin small letter long s
|
||||
// * k maps to K and to U+212A 'K' Kelvin sign
|
||||
// See http://play.golang.org/p/tTxjOc0OGo
|
||||
//
|
||||
// The returned function is specialized for matching against s and
|
||||
// should only be given s. It's not curried for performance reasons.
|
||||
func foldFunc(s []byte) func(s, t []byte) bool {
|
||||
nonLetter := false
|
||||
special := false // special letter
|
||||
for _, b := range s {
|
||||
if b >= utf8.RuneSelf {
|
||||
return bytes.EqualFold
|
||||
}
|
||||
upper := b & caseMask
|
||||
if upper < 'A' || upper > 'Z' {
|
||||
nonLetter = true
|
||||
} else if upper == 'K' || upper == 'S' {
|
||||
// See above for why these letters are special.
|
||||
special = true
|
||||
}
|
||||
}
|
||||
if special {
|
||||
return equalFoldRight
|
||||
}
|
||||
if nonLetter {
|
||||
return asciiEqualFold
|
||||
}
|
||||
return simpleLetterEqualFold
|
||||
}
|
||||
|
||||
// equalFoldRight is a specialization of bytes.EqualFold when s is
|
||||
// known to be all ASCII (including punctuation), but contains an 's',
|
||||
// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t.
|
||||
// See comments on foldFunc.
|
||||
func equalFoldRight(s, t []byte) bool {
|
||||
for _, sb := range s {
|
||||
if len(t) == 0 {
|
||||
return false
|
||||
}
|
||||
tb := t[0]
|
||||
if tb < utf8.RuneSelf {
|
||||
if sb != tb {
|
||||
sbUpper := sb & caseMask
|
||||
if 'A' <= sbUpper && sbUpper <= 'Z' {
|
||||
if sbUpper != tb&caseMask {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
t = t[1:]
|
||||
continue
|
||||
}
|
||||
// sb is ASCII and t is not. t must be either kelvin
|
||||
// sign or long s; sb must be s, S, k, or K.
|
||||
tr, size := utf8.DecodeRune(t)
|
||||
switch sb {
|
||||
case 's', 'S':
|
||||
if tr != smallLongEss {
|
||||
return false
|
||||
}
|
||||
case 'k', 'K':
|
||||
if tr != kelvin {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
t = t[size:]
|
||||
|
||||
}
|
||||
if len(t) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// asciiEqualFold is a specialization of bytes.EqualFold for use when
|
||||
// s is all ASCII (but may contain non-letters) and contains no
|
||||
// special-folding letters.
|
||||
// See comments on foldFunc.
|
||||
func asciiEqualFold(s, t []byte) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for i, sb := range s {
|
||||
tb := t[i]
|
||||
if sb == tb {
|
||||
continue
|
||||
}
|
||||
if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') {
|
||||
if sb&caseMask != tb&caseMask {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// simpleLetterEqualFold is a specialization of bytes.EqualFold for
|
||||
// use when s is all ASCII letters (no underscores, etc) and also
|
||||
// doesn't contain 'k', 'K', 's', or 'S'.
|
||||
// See comments on foldFunc.
|
||||
func simpleLetterEqualFold(s, t []byte) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for i, b := range s {
|
||||
if b&caseMask != t[i]&caseMask {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's "json"
|
||||
// tag, or the empty string. It does not include the leading comma.
|
||||
type tagOptions string
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// Contains reports whether a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
||||
277
vendor/github.com/ghodss/yaml/yaml.go
generated
vendored
277
vendor/github.com/ghodss/yaml/yaml.go
generated
vendored
@@ -1,277 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Marshals the object into JSON then converts JSON to YAML and returns the
|
||||
// YAML.
|
||||
func Marshal(o interface{}) ([]byte, error) {
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling into JSON: %v", err)
|
||||
}
|
||||
|
||||
y, err := JSONToYAML(j)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting JSON to YAML: %v", err)
|
||||
}
|
||||
|
||||
return y, nil
|
||||
}
|
||||
|
||||
// Converts YAML to JSON then uses JSON to unmarshal into an object.
|
||||
func Unmarshal(y []byte, o interface{}) error {
|
||||
vo := reflect.ValueOf(o)
|
||||
j, err := yamlToJSON(y, &vo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error converting YAML to JSON: %v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(j, o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error unmarshaling JSON: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert JSON to YAML.
|
||||
func JSONToYAML(j []byte) ([]byte, error) {
|
||||
// Convert the JSON to an object.
|
||||
var jsonObj interface{}
|
||||
// We are using yaml.Unmarshal here (instead of json.Unmarshal) because the
|
||||
// Go JSON library doesn't try to pick the right number type (int, float,
|
||||
// etc.) when unmarshalling to interface{}, it just picks float64
|
||||
// universally. go-yaml does go through the effort of picking the right
|
||||
// number type, so we can preserve number type throughout this process.
|
||||
err := yaml.Unmarshal(j, &jsonObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Marshal this object into YAML.
|
||||
return yaml.Marshal(jsonObj)
|
||||
}
|
||||
|
||||
// Convert YAML to JSON. Since JSON is a subset of YAML, passing JSON through
|
||||
// this method should be a no-op.
|
||||
//
|
||||
// Things YAML can do that are not supported by JSON:
|
||||
// * In YAML you can have binary and null keys in your maps. These are invalid
|
||||
// in JSON. (int and float keys are converted to strings.)
|
||||
// * Binary data in YAML with the !!binary tag is not supported. If you want to
|
||||
// use binary data with this library, encode the data as base64 as usual but do
|
||||
// not use the !!binary tag in your YAML. This will ensure the original base64
|
||||
// encoded data makes it all the way through to the JSON.
|
||||
func YAMLToJSON(y []byte) ([]byte, error) {
|
||||
return yamlToJSON(y, nil)
|
||||
}
|
||||
|
||||
func yamlToJSON(y []byte, jsonTarget *reflect.Value) ([]byte, error) {
|
||||
// Convert the YAML to an object.
|
||||
var yamlObj interface{}
|
||||
err := yaml.Unmarshal(y, &yamlObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// YAML objects are not completely compatible with JSON objects (e.g. you
|
||||
// can have non-string keys in YAML). So, convert the YAML-compatible object
|
||||
// to a JSON-compatible object, failing with an error if irrecoverable
|
||||
// incompatibilties happen along the way.
|
||||
jsonObj, err := convertToJSONableObject(yamlObj, jsonTarget)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert this object to JSON and return the data.
|
||||
return json.Marshal(jsonObj)
|
||||
}
|
||||
|
||||
func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (interface{}, error) {
|
||||
var err error
|
||||
|
||||
// Resolve jsonTarget to a concrete value (i.e. not a pointer or an
|
||||
// interface). We pass decodingNull as false because we're not actually
|
||||
// decoding into the value, we're just checking if the ultimate target is a
|
||||
// string.
|
||||
if jsonTarget != nil {
|
||||
ju, tu, pv := indirect(*jsonTarget, false)
|
||||
// We have a JSON or Text Umarshaler at this level, so we can't be trying
|
||||
// to decode into a string.
|
||||
if ju != nil || tu != nil {
|
||||
jsonTarget = nil
|
||||
} else {
|
||||
jsonTarget = &pv
|
||||
}
|
||||
}
|
||||
|
||||
// If yamlObj is a number or a boolean, check if jsonTarget is a string -
|
||||
// if so, coerce. Else return normal.
|
||||
// If yamlObj is a map or array, find the field that each key is
|
||||
// unmarshaling to, and when you recurse pass the reflect.Value for that
|
||||
// field back into this function.
|
||||
switch typedYAMLObj := yamlObj.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
// JSON does not support arbitrary keys in a map, so we must convert
|
||||
// these keys to strings.
|
||||
//
|
||||
// From my reading of go-yaml v2 (specifically the resolve function),
|
||||
// keys can only have the types string, int, int64, float64, binary
|
||||
// (unsupported), or null (unsupported).
|
||||
strMap := make(map[string]interface{})
|
||||
for k, v := range typedYAMLObj {
|
||||
// Resolve the key to a string first.
|
||||
var keyString string
|
||||
switch typedKey := k.(type) {
|
||||
case string:
|
||||
keyString = typedKey
|
||||
case int:
|
||||
keyString = strconv.Itoa(typedKey)
|
||||
case int64:
|
||||
// go-yaml will only return an int64 as a key if the system
|
||||
// architecture is 32-bit and the key's value is between 32-bit
|
||||
// and 64-bit. Otherwise the key type will simply be int.
|
||||
keyString = strconv.FormatInt(typedKey, 10)
|
||||
case float64:
|
||||
// Stolen from go-yaml to use the same conversion to string as
|
||||
// the go-yaml library uses to convert float to string when
|
||||
// Marshaling.
|
||||
s := strconv.FormatFloat(typedKey, 'g', -1, 32)
|
||||
switch s {
|
||||
case "+Inf":
|
||||
s = ".inf"
|
||||
case "-Inf":
|
||||
s = "-.inf"
|
||||
case "NaN":
|
||||
s = ".nan"
|
||||
}
|
||||
keyString = s
|
||||
case bool:
|
||||
if typedKey {
|
||||
keyString = "true"
|
||||
} else {
|
||||
keyString = "false"
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported map key of type: %s, key: %+#v, value: %+#v",
|
||||
reflect.TypeOf(k), k, v)
|
||||
}
|
||||
|
||||
// jsonTarget should be a struct or a map. If it's a struct, find
|
||||
// the field it's going to map to and pass its reflect.Value. If
|
||||
// it's a map, find the element type of the map and pass the
|
||||
// reflect.Value created from that type. If it's neither, just pass
|
||||
// nil - JSON conversion will error for us if it's a real issue.
|
||||
if jsonTarget != nil {
|
||||
t := *jsonTarget
|
||||
if t.Kind() == reflect.Struct {
|
||||
keyBytes := []byte(keyString)
|
||||
// Find the field that the JSON library would use.
|
||||
var f *field
|
||||
fields := cachedTypeFields(t.Type())
|
||||
for i := range fields {
|
||||
ff := &fields[i]
|
||||
if bytes.Equal(ff.nameBytes, keyBytes) {
|
||||
f = ff
|
||||
break
|
||||
}
|
||||
// Do case-insensitive comparison.
|
||||
if f == nil && ff.equalFold(ff.nameBytes, keyBytes) {
|
||||
f = ff
|
||||
}
|
||||
}
|
||||
if f != nil {
|
||||
// Find the reflect.Value of the most preferential
|
||||
// struct field.
|
||||
jtf := t.Field(f.index[0])
|
||||
strMap[keyString], err = convertToJSONableObject(v, &jtf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else if t.Kind() == reflect.Map {
|
||||
// Create a zero value of the map's element type to use as
|
||||
// the JSON target.
|
||||
jtv := reflect.Zero(t.Type().Elem())
|
||||
strMap[keyString], err = convertToJSONableObject(v, &jtv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
strMap[keyString], err = convertToJSONableObject(v, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return strMap, nil
|
||||
case []interface{}:
|
||||
// We need to recurse into arrays in case there are any
|
||||
// map[interface{}]interface{}'s inside and to convert any
|
||||
// numbers to strings.
|
||||
|
||||
// If jsonTarget is a slice (which it really should be), find the
|
||||
// thing it's going to map to. If it's not a slice, just pass nil
|
||||
// - JSON conversion will error for us if it's a real issue.
|
||||
var jsonSliceElemValue *reflect.Value
|
||||
if jsonTarget != nil {
|
||||
t := *jsonTarget
|
||||
if t.Kind() == reflect.Slice {
|
||||
// By default slices point to nil, but we need a reflect.Value
|
||||
// pointing to a value of the slice type, so we create one here.
|
||||
ev := reflect.Indirect(reflect.New(t.Type().Elem()))
|
||||
jsonSliceElemValue = &ev
|
||||
}
|
||||
}
|
||||
|
||||
// Make and use a new array.
|
||||
arr := make([]interface{}, len(typedYAMLObj))
|
||||
for i, v := range typedYAMLObj {
|
||||
arr[i], err = convertToJSONableObject(v, jsonSliceElemValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return arr, nil
|
||||
default:
|
||||
// If the target type is a string and the YAML type is a number,
|
||||
// convert the YAML type to a string.
|
||||
if jsonTarget != nil && (*jsonTarget).Kind() == reflect.String {
|
||||
// Based on my reading of go-yaml, it may return int, int64,
|
||||
// float64, or uint64.
|
||||
var s string
|
||||
switch typedVal := typedYAMLObj.(type) {
|
||||
case int:
|
||||
s = strconv.FormatInt(int64(typedVal), 10)
|
||||
case int64:
|
||||
s = strconv.FormatInt(typedVal, 10)
|
||||
case float64:
|
||||
s = strconv.FormatFloat(typedVal, 'g', -1, 32)
|
||||
case uint64:
|
||||
s = strconv.FormatUint(typedVal, 10)
|
||||
case bool:
|
||||
if typedVal {
|
||||
s = "true"
|
||||
} else {
|
||||
s = "false"
|
||||
}
|
||||
}
|
||||
if len(s) > 0 {
|
||||
yamlObj = interface{}(s)
|
||||
}
|
||||
}
|
||||
return yamlObj, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
27
vendor/github.com/gorilla/context/LICENSE
generated
vendored
27
vendor/github.com/gorilla/context/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
10
vendor/github.com/gorilla/context/README.md
generated
vendored
10
vendor/github.com/gorilla/context/README.md
generated
vendored
@@ -1,10 +0,0 @@
|
||||
context
|
||||
=======
|
||||
[](https://travis-ci.org/gorilla/context)
|
||||
|
||||
gorilla/context is a general purpose registry for global request variables.
|
||||
|
||||
> Note: gorilla/context, having been born well before `context.Context` existed, does not play well
|
||||
> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`.
|
||||
|
||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
|
||||
143
vendor/github.com/gorilla/context/context.go
generated
vendored
143
vendor/github.com/gorilla/context/context.go
generated
vendored
@@ -1,143 +0,0 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
mutex sync.RWMutex
|
||||
data = make(map[*http.Request]map[interface{}]interface{})
|
||||
datat = make(map[*http.Request]int64)
|
||||
)
|
||||
|
||||
// Set stores a value for a given key in a given request.
|
||||
func Set(r *http.Request, key, val interface{}) {
|
||||
mutex.Lock()
|
||||
if data[r] == nil {
|
||||
data[r] = make(map[interface{}]interface{})
|
||||
datat[r] = time.Now().Unix()
|
||||
}
|
||||
data[r][key] = val
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// Get returns a value stored for a given key in a given request.
|
||||
func Get(r *http.Request, key interface{}) interface{} {
|
||||
mutex.RLock()
|
||||
if ctx := data[r]; ctx != nil {
|
||||
value := ctx[key]
|
||||
mutex.RUnlock()
|
||||
return value
|
||||
}
|
||||
mutex.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOk returns stored value and presence state like multi-value return of map access.
|
||||
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
|
||||
mutex.RLock()
|
||||
if _, ok := data[r]; ok {
|
||||
value, ok := data[r][key]
|
||||
mutex.RUnlock()
|
||||
return value, ok
|
||||
}
|
||||
mutex.RUnlock()
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
|
||||
func GetAll(r *http.Request) map[interface{}]interface{} {
|
||||
mutex.RLock()
|
||||
if context, ok := data[r]; ok {
|
||||
result := make(map[interface{}]interface{}, len(context))
|
||||
for k, v := range context {
|
||||
result[k] = v
|
||||
}
|
||||
mutex.RUnlock()
|
||||
return result
|
||||
}
|
||||
mutex.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
|
||||
// the request was registered.
|
||||
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
|
||||
mutex.RLock()
|
||||
context, ok := data[r]
|
||||
result := make(map[interface{}]interface{}, len(context))
|
||||
for k, v := range context {
|
||||
result[k] = v
|
||||
}
|
||||
mutex.RUnlock()
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// Delete removes a value stored for a given key in a given request.
|
||||
func Delete(r *http.Request, key interface{}) {
|
||||
mutex.Lock()
|
||||
if data[r] != nil {
|
||||
delete(data[r], key)
|
||||
}
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// Clear removes all values stored for a given request.
|
||||
//
|
||||
// This is usually called by a handler wrapper to clean up request
|
||||
// variables at the end of a request lifetime. See ClearHandler().
|
||||
func Clear(r *http.Request) {
|
||||
mutex.Lock()
|
||||
clear(r)
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// clear is Clear without the lock.
|
||||
func clear(r *http.Request) {
|
||||
delete(data, r)
|
||||
delete(datat, r)
|
||||
}
|
||||
|
||||
// Purge removes request data stored for longer than maxAge, in seconds.
|
||||
// It returns the amount of requests removed.
|
||||
//
|
||||
// If maxAge <= 0, all request data is removed.
|
||||
//
|
||||
// This is only used for sanity check: in case context cleaning was not
|
||||
// properly set some request data can be kept forever, consuming an increasing
|
||||
// amount of memory. In case this is detected, Purge() must be called
|
||||
// periodically until the problem is fixed.
|
||||
func Purge(maxAge int) int {
|
||||
mutex.Lock()
|
||||
count := 0
|
||||
if maxAge <= 0 {
|
||||
count = len(data)
|
||||
data = make(map[*http.Request]map[interface{}]interface{})
|
||||
datat = make(map[*http.Request]int64)
|
||||
} else {
|
||||
min := time.Now().Unix() - int64(maxAge)
|
||||
for r := range data {
|
||||
if datat[r] < min {
|
||||
clear(r)
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex.Unlock()
|
||||
return count
|
||||
}
|
||||
|
||||
// ClearHandler wraps an http.Handler and clears request values at the end
|
||||
// of a request lifetime.
|
||||
func ClearHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer Clear(r)
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
88
vendor/github.com/gorilla/context/doc.go
generated
vendored
88
vendor/github.com/gorilla/context/doc.go
generated
vendored
@@ -1,88 +0,0 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package context stores values shared during a request lifetime.
|
||||
|
||||
Note: gorilla/context, having been born well before `context.Context` existed,
|
||||
does not play well > with the shallow copying of the request that
|
||||
[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext)
|
||||
(added to net/http Go 1.7 onwards) performs. You should either use *just*
|
||||
gorilla/context, or moving forward, the new `http.Request.Context()`.
|
||||
|
||||
For example, a router can set variables extracted from the URL and later
|
||||
application handlers can access those values, or it can be used to store
|
||||
sessions values to be saved at the end of a request. There are several
|
||||
others common uses.
|
||||
|
||||
The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
|
||||
|
||||
http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
|
||||
|
||||
Here's the basic usage: first define the keys that you will need. The key
|
||||
type is interface{} so a key can be of any type that supports equality.
|
||||
Here we define a key using a custom int type to avoid name collisions:
|
||||
|
||||
package foo
|
||||
|
||||
import (
|
||||
"github.com/gorilla/context"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
const MyKey key = 0
|
||||
|
||||
Then set a variable. Variables are bound to an http.Request object, so you
|
||||
need a request instance to set a value:
|
||||
|
||||
context.Set(r, MyKey, "bar")
|
||||
|
||||
The application can later access the variable using the same key you provided:
|
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// val is "bar".
|
||||
val := context.Get(r, foo.MyKey)
|
||||
|
||||
// returns ("bar", true)
|
||||
val, ok := context.GetOk(r, foo.MyKey)
|
||||
// ...
|
||||
}
|
||||
|
||||
And that's all about the basic usage. We discuss some other ideas below.
|
||||
|
||||
Any type can be stored in the context. To enforce a given type, make the key
|
||||
private and wrap Get() and Set() to accept and return values of a specific
|
||||
type:
|
||||
|
||||
type key int
|
||||
|
||||
const mykey key = 0
|
||||
|
||||
// GetMyKey returns a value for this package from the request values.
|
||||
func GetMyKey(r *http.Request) SomeType {
|
||||
if rv := context.Get(r, mykey); rv != nil {
|
||||
return rv.(SomeType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMyKey sets a value for this package in the request values.
|
||||
func SetMyKey(r *http.Request, val SomeType) {
|
||||
context.Set(r, mykey, val)
|
||||
}
|
||||
|
||||
Variables must be cleared at the end of a request, to remove all values
|
||||
that were stored. This can be done in an http.Handler, after a request was
|
||||
served. Just call Clear() passing the request:
|
||||
|
||||
context.Clear(r)
|
||||
|
||||
...or use ClearHandler(), which conveniently wraps an http.Handler to clear
|
||||
variables at the end of a request lifetime.
|
||||
|
||||
The Routers from the packages gorilla/mux and gorilla/pat call Clear()
|
||||
so if you are using either of them you don't need to clear the context manually.
|
||||
*/
|
||||
package context
|
||||
27
vendor/github.com/gorilla/mux/LICENSE
generated
vendored
27
vendor/github.com/gorilla/mux/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
340
vendor/github.com/gorilla/mux/README.md
generated
vendored
340
vendor/github.com/gorilla/mux/README.md
generated
vendored
@@ -1,340 +0,0 @@
|
||||
gorilla/mux
|
||||
===
|
||||
[](https://godoc.org/github.com/gorilla/mux)
|
||||
[](https://travis-ci.org/gorilla/mux)
|
||||
[](https://sourcegraph.com/github.com/gorilla/mux?badge)
|
||||
|
||||

|
||||
|
||||
http://www.gorillatoolkit.org/pkg/mux
|
||||
|
||||
Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
|
||||
their respective handler.
|
||||
|
||||
The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are:
|
||||
|
||||
* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
|
||||
* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
|
||||
* URL hosts and paths can have variables with an optional regular expression.
|
||||
* Registered URLs can be built, or "reversed", which helps maintaining references to resources.
|
||||
* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching.
|
||||
|
||||
---
|
||||
|
||||
* [Install](#install)
|
||||
* [Examples](#examples)
|
||||
* [Matching Routes](#matching-routes)
|
||||
* [Listing Routes](#listing-routes)
|
||||
* [Static Files](#static-files)
|
||||
* [Registered URLs](#registered-urls)
|
||||
* [Full Example](#full-example)
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
|
||||
With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain:
|
||||
|
||||
```sh
|
||||
go get -u github.com/gorilla/mux
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Let's start registering a couple of URL paths and handlers:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/", HomeHandler)
|
||||
r.HandleFunc("/products", ProductsHandler)
|
||||
r.HandleFunc("/articles", ArticlesHandler)
|
||||
http.Handle("/", r)
|
||||
}
|
||||
```
|
||||
|
||||
Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters.
|
||||
|
||||
Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example:
|
||||
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/products/{key}", ProductHandler)
|
||||
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
|
||||
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
|
||||
```
|
||||
|
||||
The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
|
||||
|
||||
```go
|
||||
func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Category: %v\n", vars["category"])
|
||||
}
|
||||
```
|
||||
|
||||
And this is all you need to know about the basic usage. More advanced options are explained below.
|
||||
|
||||
### Matching Routes
|
||||
|
||||
Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:
|
||||
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
// Only matches if domain is "www.example.com".
|
||||
r.Host("www.example.com")
|
||||
// Matches a dynamic subdomain.
|
||||
r.Host("{subdomain:[a-z]+}.domain.com")
|
||||
```
|
||||
|
||||
There are several other matchers that can be added. To match path prefixes:
|
||||
|
||||
```go
|
||||
r.PathPrefix("/products/")
|
||||
```
|
||||
|
||||
...or HTTP methods:
|
||||
|
||||
```go
|
||||
r.Methods("GET", "POST")
|
||||
```
|
||||
|
||||
...or URL schemes:
|
||||
|
||||
```go
|
||||
r.Schemes("https")
|
||||
```
|
||||
|
||||
...or header values:
|
||||
|
||||
```go
|
||||
r.Headers("X-Requested-With", "XMLHttpRequest")
|
||||
```
|
||||
|
||||
...or query values:
|
||||
|
||||
```go
|
||||
r.Queries("key", "value")
|
||||
```
|
||||
|
||||
...or to use a custom matcher function:
|
||||
|
||||
```go
|
||||
r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
|
||||
return r.ProtoMajor == 0
|
||||
})
|
||||
```
|
||||
|
||||
...and finally, it is possible to combine several matchers in a single route:
|
||||
|
||||
```go
|
||||
r.HandleFunc("/products", ProductsHandler).
|
||||
Host("www.example.com").
|
||||
Methods("GET").
|
||||
Schemes("http")
|
||||
```
|
||||
|
||||
Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting".
|
||||
|
||||
For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it:
|
||||
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
s := r.Host("www.example.com").Subrouter()
|
||||
```
|
||||
|
||||
Then register routes in the subrouter:
|
||||
|
||||
```go
|
||||
s.HandleFunc("/products/", ProductsHandler)
|
||||
s.HandleFunc("/products/{key}", ProductHandler)
|
||||
s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
|
||||
```
|
||||
|
||||
The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route.
|
||||
|
||||
Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter.
|
||||
|
||||
There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:
|
||||
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
s := r.PathPrefix("/products").Subrouter()
|
||||
// "/products/"
|
||||
s.HandleFunc("/", ProductsHandler)
|
||||
// "/products/{key}/"
|
||||
s.HandleFunc("/{key}/", ProductHandler)
|
||||
// "/products/{key}/details"
|
||||
s.HandleFunc("/{key}/details", ProductDetailsHandler)
|
||||
```
|
||||
|
||||
### Listing Routes
|
||||
|
||||
Routes on a mux can be listed using the Router.Walk method—useful for generating documentation:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/", handler)
|
||||
r.HandleFunc("/products", handler)
|
||||
r.HandleFunc("/articles", handler)
|
||||
r.HandleFunc("/articles/{id}", handler)
|
||||
r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
|
||||
t, err := route.GetPathTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(t)
|
||||
return nil
|
||||
})
|
||||
http.Handle("/", r)
|
||||
}
|
||||
```
|
||||
|
||||
### Static Files
|
||||
|
||||
Note that the path provided to `PathPrefix()` represents a "wildcard": calling
|
||||
`PathPrefix("/static/").Handler(...)` means that the handler will be passed any
|
||||
request that matches "/static/*". This makes it easy to serve static files with mux:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var dir string
|
||||
|
||||
flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
|
||||
flag.Parse()
|
||||
r := mux.NewRouter()
|
||||
|
||||
// This will serve files under http://localhost:8000/static/<filename>
|
||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: r,
|
||||
Addr: "127.0.0.1:8000",
|
||||
// Good practice: enforce timeouts for servers you create!
|
||||
WriteTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
log.Fatal(srv.ListenAndServe())
|
||||
}
|
||||
```
|
||||
|
||||
### Registered URLs
|
||||
|
||||
Now let's see how to build registered URLs.
|
||||
|
||||
Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example:
|
||||
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
Name("article")
|
||||
```
|
||||
|
||||
To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do:
|
||||
|
||||
```go
|
||||
url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||
```
|
||||
|
||||
...and the result will be a `url.URL` with the following path:
|
||||
|
||||
```
|
||||
"/articles/technology/42"
|
||||
```
|
||||
|
||||
This also works for host variables:
|
||||
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
r.Host("{subdomain}.domain.com").
|
||||
Path("/articles/{category}/{id:[0-9]+}").
|
||||
HandlerFunc(ArticleHandler).
|
||||
Name("article")
|
||||
|
||||
// url.String() will be "http://news.domain.com/articles/technology/42"
|
||||
url, err := r.Get("article").URL("subdomain", "news",
|
||||
"category", "technology",
|
||||
"id", "42")
|
||||
```
|
||||
|
||||
All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match.
|
||||
|
||||
Regex support also exists for matching Headers within a route. For example, we could do:
|
||||
|
||||
```go
|
||||
r.HeadersRegexp("Content-Type", "application/(text|json)")
|
||||
```
|
||||
|
||||
...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
|
||||
|
||||
There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
|
||||
|
||||
```go
|
||||
// "http://news.domain.com/"
|
||||
host, err := r.Get("article").URLHost("subdomain", "news")
|
||||
|
||||
// "/articles/technology/42"
|
||||
path, err := r.Get("article").URLPath("category", "technology", "id", "42")
|
||||
```
|
||||
|
||||
And if you use subrouters, host and path defined separately can be built as well:
|
||||
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
s := r.Host("{subdomain}.domain.com").Subrouter()
|
||||
s.Path("/articles/{category}/{id:[0-9]+}").
|
||||
HandlerFunc(ArticleHandler).
|
||||
Name("article")
|
||||
|
||||
// "http://news.domain.com/articles/technology/42"
|
||||
url, err := r.Get("article").URL("subdomain", "news",
|
||||
"category", "technology",
|
||||
"id", "42")
|
||||
```
|
||||
|
||||
## Full Example
|
||||
|
||||
Here's a complete, runnable example of a small `mux` based server:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"log"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func YourHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("Gorilla!\n"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := mux.NewRouter()
|
||||
// Routes consist of a path and a handler function.
|
||||
r.HandleFunc("/", YourHandler)
|
||||
|
||||
// Bind to a port and pass our router in
|
||||
log.Fatal(http.ListenAndServe(":8000", r))
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
BSD licensed. See the LICENSE file for details.
|
||||
26
vendor/github.com/gorilla/mux/context_gorilla.go
generated
vendored
26
vendor/github.com/gorilla/mux/context_gorilla.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// +build !go1.7
|
||||
|
||||
package mux
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/context"
|
||||
)
|
||||
|
||||
func contextGet(r *http.Request, key interface{}) interface{} {
|
||||
return context.Get(r, key)
|
||||
}
|
||||
|
||||
func contextSet(r *http.Request, key, val interface{}) *http.Request {
|
||||
if val == nil {
|
||||
return r
|
||||
}
|
||||
|
||||
context.Set(r, key, val)
|
||||
return r
|
||||
}
|
||||
|
||||
func contextClear(r *http.Request) {
|
||||
context.Clear(r)
|
||||
}
|
||||
24
vendor/github.com/gorilla/mux/context_native.go
generated
vendored
24
vendor/github.com/gorilla/mux/context_native.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
// +build go1.7
|
||||
|
||||
package mux
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func contextGet(r *http.Request, key interface{}) interface{} {
|
||||
return r.Context().Value(key)
|
||||
}
|
||||
|
||||
func contextSet(r *http.Request, key, val interface{}) *http.Request {
|
||||
if val == nil {
|
||||
return r
|
||||
}
|
||||
|
||||
return r.WithContext(context.WithValue(r.Context(), key, val))
|
||||
}
|
||||
|
||||
func contextClear(r *http.Request) {
|
||||
return
|
||||
}
|
||||
240
vendor/github.com/gorilla/mux/doc.go
generated
vendored
240
vendor/github.com/gorilla/mux/doc.go
generated
vendored
@@ -1,240 +0,0 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package mux implements a request router and dispatcher.
|
||||
|
||||
The name mux stands for "HTTP request multiplexer". Like the standard
|
||||
http.ServeMux, mux.Router matches incoming requests against a list of
|
||||
registered routes and calls a handler for the route that matches the URL
|
||||
or other conditions. The main features are:
|
||||
|
||||
* Requests can be matched based on URL host, path, path prefix, schemes,
|
||||
header and query values, HTTP methods or using custom matchers.
|
||||
* URL hosts and paths can have variables with an optional regular
|
||||
expression.
|
||||
* Registered URLs can be built, or "reversed", which helps maintaining
|
||||
references to resources.
|
||||
* Routes can be used as subrouters: nested routes are only tested if the
|
||||
parent route matches. This is useful to define groups of routes that
|
||||
share common conditions like a host, a path prefix or other repeated
|
||||
attributes. As a bonus, this optimizes request matching.
|
||||
* It implements the http.Handler interface so it is compatible with the
|
||||
standard http.ServeMux.
|
||||
|
||||
Let's start registering a couple of URL paths and handlers:
|
||||
|
||||
func main() {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/", HomeHandler)
|
||||
r.HandleFunc("/products", ProductsHandler)
|
||||
r.HandleFunc("/articles", ArticlesHandler)
|
||||
http.Handle("/", r)
|
||||
}
|
||||
|
||||
Here we register three routes mapping URL paths to handlers. This is
|
||||
equivalent to how http.HandleFunc() works: if an incoming request URL matches
|
||||
one of the paths, the corresponding handler is called passing
|
||||
(http.ResponseWriter, *http.Request) as parameters.
|
||||
|
||||
Paths can have variables. They are defined using the format {name} or
|
||||
{name:pattern}. If a regular expression pattern is not defined, the matched
|
||||
variable will be anything until the next slash. For example:
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/products/{key}", ProductHandler)
|
||||
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
|
||||
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
|
||||
|
||||
Groups can be used inside patterns, as long as they are non-capturing (?:re). For example:
|
||||
|
||||
r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler)
|
||||
|
||||
The names are used to create a map of route variables which can be retrieved
|
||||
calling mux.Vars():
|
||||
|
||||
vars := mux.Vars(request)
|
||||
category := vars["category"]
|
||||
|
||||
Note that if any capturing groups are present, mux will panic() during parsing. To prevent
|
||||
this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to
|
||||
"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably
|
||||
when capturing groups were present.
|
||||
|
||||
And this is all you need to know about the basic usage. More advanced options
|
||||
are explained below.
|
||||
|
||||
Routes can also be restricted to a domain or subdomain. Just define a host
|
||||
pattern to be matched. They can also have variables:
|
||||
|
||||
r := mux.NewRouter()
|
||||
// Only matches if domain is "www.example.com".
|
||||
r.Host("www.example.com")
|
||||
// Matches a dynamic subdomain.
|
||||
r.Host("{subdomain:[a-z]+}.domain.com")
|
||||
|
||||
There are several other matchers that can be added. To match path prefixes:
|
||||
|
||||
r.PathPrefix("/products/")
|
||||
|
||||
...or HTTP methods:
|
||||
|
||||
r.Methods("GET", "POST")
|
||||
|
||||
...or URL schemes:
|
||||
|
||||
r.Schemes("https")
|
||||
|
||||
...or header values:
|
||||
|
||||
r.Headers("X-Requested-With", "XMLHttpRequest")
|
||||
|
||||
...or query values:
|
||||
|
||||
r.Queries("key", "value")
|
||||
|
||||
...or to use a custom matcher function:
|
||||
|
||||
r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
|
||||
return r.ProtoMajor == 0
|
||||
})
|
||||
|
||||
...and finally, it is possible to combine several matchers in a single route:
|
||||
|
||||
r.HandleFunc("/products", ProductsHandler).
|
||||
Host("www.example.com").
|
||||
Methods("GET").
|
||||
Schemes("http")
|
||||
|
||||
Setting the same matching conditions again and again can be boring, so we have
|
||||
a way to group several routes that share the same requirements.
|
||||
We call it "subrouting".
|
||||
|
||||
For example, let's say we have several URLs that should only match when the
|
||||
host is "www.example.com". Create a route for that host and get a "subrouter"
|
||||
from it:
|
||||
|
||||
r := mux.NewRouter()
|
||||
s := r.Host("www.example.com").Subrouter()
|
||||
|
||||
Then register routes in the subrouter:
|
||||
|
||||
s.HandleFunc("/products/", ProductsHandler)
|
||||
s.HandleFunc("/products/{key}", ProductHandler)
|
||||
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||
|
||||
The three URL paths we registered above will only be tested if the domain is
|
||||
"www.example.com", because the subrouter is tested first. This is not
|
||||
only convenient, but also optimizes request matching. You can create
|
||||
subrouters combining any attribute matchers accepted by a route.
|
||||
|
||||
Subrouters can be used to create domain or path "namespaces": you define
|
||||
subrouters in a central place and then parts of the app can register its
|
||||
paths relatively to a given subrouter.
|
||||
|
||||
There's one more thing about subroutes. When a subrouter has a path prefix,
|
||||
the inner routes use it as base for their paths:
|
||||
|
||||
r := mux.NewRouter()
|
||||
s := r.PathPrefix("/products").Subrouter()
|
||||
// "/products/"
|
||||
s.HandleFunc("/", ProductsHandler)
|
||||
// "/products/{key}/"
|
||||
s.HandleFunc("/{key}/", ProductHandler)
|
||||
// "/products/{key}/details"
|
||||
s.HandleFunc("/{key}/details", ProductDetailsHandler)
|
||||
|
||||
Note that the path provided to PathPrefix() represents a "wildcard": calling
|
||||
PathPrefix("/static/").Handler(...) means that the handler will be passed any
|
||||
request that matches "/static/*". This makes it easy to serve static files with mux:
|
||||
|
||||
func main() {
|
||||
var dir string
|
||||
|
||||
flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
|
||||
flag.Parse()
|
||||
r := mux.NewRouter()
|
||||
|
||||
// This will serve files under http://localhost:8000/static/<filename>
|
||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: r,
|
||||
Addr: "127.0.0.1:8000",
|
||||
// Good practice: enforce timeouts for servers you create!
|
||||
WriteTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
log.Fatal(srv.ListenAndServe())
|
||||
}
|
||||
|
||||
Now let's see how to build registered URLs.
|
||||
|
||||
Routes can be named. All routes that define a name can have their URLs built,
|
||||
or "reversed". We define a name calling Name() on a route. For example:
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
Name("article")
|
||||
|
||||
To build a URL, get the route and call the URL() method, passing a sequence of
|
||||
key/value pairs for the route variables. For the previous route, we would do:
|
||||
|
||||
url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||
|
||||
...and the result will be a url.URL with the following path:
|
||||
|
||||
"/articles/technology/42"
|
||||
|
||||
This also works for host variables:
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.Host("{subdomain}.domain.com").
|
||||
Path("/articles/{category}/{id:[0-9]+}").
|
||||
HandlerFunc(ArticleHandler).
|
||||
Name("article")
|
||||
|
||||
// url.String() will be "http://news.domain.com/articles/technology/42"
|
||||
url, err := r.Get("article").URL("subdomain", "news",
|
||||
"category", "technology",
|
||||
"id", "42")
|
||||
|
||||
All variables defined in the route are required, and their values must
|
||||
conform to the corresponding patterns. These requirements guarantee that a
|
||||
generated URL will always match a registered route -- the only exception is
|
||||
for explicitly defined "build-only" routes which never match.
|
||||
|
||||
Regex support also exists for matching Headers within a route. For example, we could do:
|
||||
|
||||
r.HeadersRegexp("Content-Type", "application/(text|json)")
|
||||
|
||||
...and the route will match both requests with a Content-Type of `application/json` as well as
|
||||
`application/text`
|
||||
|
||||
There's also a way to build only the URL host or path for a route:
|
||||
use the methods URLHost() or URLPath() instead. For the previous route,
|
||||
we would do:
|
||||
|
||||
// "http://news.domain.com/"
|
||||
host, err := r.Get("article").URLHost("subdomain", "news")
|
||||
|
||||
// "/articles/technology/42"
|
||||
path, err := r.Get("article").URLPath("category", "technology", "id", "42")
|
||||
|
||||
And if you use subrouters, host and path defined separately can be built
|
||||
as well:
|
||||
|
||||
r := mux.NewRouter()
|
||||
s := r.Host("{subdomain}.domain.com").Subrouter()
|
||||
s.Path("/articles/{category}/{id:[0-9]+}").
|
||||
HandlerFunc(ArticleHandler).
|
||||
Name("article")
|
||||
|
||||
// "http://news.domain.com/articles/technology/42"
|
||||
url, err := r.Get("article").URL("subdomain", "news",
|
||||
"category", "technology",
|
||||
"id", "42")
|
||||
*/
|
||||
package mux
|
||||
542
vendor/github.com/gorilla/mux/mux.go
generated
vendored
542
vendor/github.com/gorilla/mux/mux.go
generated
vendored
@@ -1,542 +0,0 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewRouter returns a new router instance.
|
||||
func NewRouter() *Router {
|
||||
return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
|
||||
}
|
||||
|
||||
// Router registers routes to be matched and dispatches a handler.
|
||||
//
|
||||
// It implements the http.Handler interface, so it can be registered to serve
|
||||
// requests:
|
||||
//
|
||||
// var router = mux.NewRouter()
|
||||
//
|
||||
// func main() {
|
||||
// http.Handle("/", router)
|
||||
// }
|
||||
//
|
||||
// Or, for Google App Engine, register it in a init() function:
|
||||
//
|
||||
// func init() {
|
||||
// http.Handle("/", router)
|
||||
// }
|
||||
//
|
||||
// This will send all incoming requests to the router.
|
||||
type Router struct {
|
||||
// Configurable Handler to be used when no route matches.
|
||||
NotFoundHandler http.Handler
|
||||
// Parent route, if this is a subrouter.
|
||||
parent parentRoute
|
||||
// Routes to be matched, in order.
|
||||
routes []*Route
|
||||
// Routes by name for URL building.
|
||||
namedRoutes map[string]*Route
|
||||
// See Router.StrictSlash(). This defines the flag for new routes.
|
||||
strictSlash bool
|
||||
// See Router.SkipClean(). This defines the flag for new routes.
|
||||
skipClean bool
|
||||
// If true, do not clear the request context after handling the request.
|
||||
// This has no effect when go1.7+ is used, since the context is stored
|
||||
// on the request itself.
|
||||
KeepContext bool
|
||||
// see Router.UseEncodedPath(). This defines a flag for all routes.
|
||||
useEncodedPath bool
|
||||
}
|
||||
|
||||
// Match matches registered routes against the request.
|
||||
func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
|
||||
for _, route := range r.routes {
|
||||
if route.Match(req, match) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Closest match for a router (includes sub-routers)
|
||||
if r.NotFoundHandler != nil {
|
||||
match.Handler = r.NotFoundHandler
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ServeHTTP dispatches the handler registered in the matched route.
|
||||
//
|
||||
// When there is a match, the route variables can be retrieved calling
|
||||
// mux.Vars(request).
|
||||
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if !r.skipClean {
|
||||
path := req.URL.Path
|
||||
if r.useEncodedPath {
|
||||
path = getPath(req)
|
||||
}
|
||||
// Clean path to canonical form and redirect.
|
||||
if p := cleanPath(path); p != path {
|
||||
|
||||
// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
|
||||
// This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
|
||||
// http://code.google.com/p/go/issues/detail?id=5252
|
||||
url := *req.URL
|
||||
url.Path = p
|
||||
p = url.String()
|
||||
|
||||
w.Header().Set("Location", p)
|
||||
w.WriteHeader(http.StatusMovedPermanently)
|
||||
return
|
||||
}
|
||||
}
|
||||
var match RouteMatch
|
||||
var handler http.Handler
|
||||
if r.Match(req, &match) {
|
||||
handler = match.Handler
|
||||
req = setVars(req, match.Vars)
|
||||
req = setCurrentRoute(req, match.Route)
|
||||
}
|
||||
if handler == nil {
|
||||
handler = http.NotFoundHandler()
|
||||
}
|
||||
if !r.KeepContext {
|
||||
defer contextClear(req)
|
||||
}
|
||||
handler.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
// Get returns a route registered with the given name.
|
||||
func (r *Router) Get(name string) *Route {
|
||||
return r.getNamedRoutes()[name]
|
||||
}
|
||||
|
||||
// GetRoute returns a route registered with the given name. This method
|
||||
// was renamed to Get() and remains here for backwards compatibility.
|
||||
func (r *Router) GetRoute(name string) *Route {
|
||||
return r.getNamedRoutes()[name]
|
||||
}
|
||||
|
||||
// StrictSlash defines the trailing slash behavior for new routes. The initial
|
||||
// value is false.
|
||||
//
|
||||
// When true, if the route path is "/path/", accessing "/path" will redirect
|
||||
// to the former and vice versa. In other words, your application will always
|
||||
// see the path as specified in the route.
|
||||
//
|
||||
// When false, if the route path is "/path", accessing "/path/" will not match
|
||||
// this route and vice versa.
|
||||
//
|
||||
// Special case: when a route sets a path prefix using the PathPrefix() method,
|
||||
// strict slash is ignored for that route because the redirect behavior can't
|
||||
// be determined from a prefix alone. However, any subrouters created from that
|
||||
// route inherit the original StrictSlash setting.
|
||||
func (r *Router) StrictSlash(value bool) *Router {
|
||||
r.strictSlash = value
|
||||
return r
|
||||
}
|
||||
|
||||
// SkipClean defines the path cleaning behaviour for new routes. The initial
|
||||
// value is false. Users should be careful about which routes are not cleaned
|
||||
//
|
||||
// When true, if the route path is "/path//to", it will remain with the double
|
||||
// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
|
||||
//
|
||||
// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
|
||||
// become /fetch/http/xkcd.com/534
|
||||
func (r *Router) SkipClean(value bool) *Router {
|
||||
r.skipClean = value
|
||||
return r
|
||||
}
|
||||
|
||||
// UseEncodedPath tells the router to match the encoded original path
|
||||
// to the routes.
|
||||
// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
|
||||
// This behavior has the drawback of needing to match routes against
|
||||
// r.RequestURI instead of r.URL.Path. Any modifications (such as http.StripPrefix)
|
||||
// to r.URL.Path will not affect routing when this flag is on and thus may
|
||||
// induce unintended behavior.
|
||||
//
|
||||
// If not called, the router will match the unencoded path to the routes.
|
||||
// For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to"
|
||||
func (r *Router) UseEncodedPath() *Router {
|
||||
r.useEncodedPath = true
|
||||
return r
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// parentRoute
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// getNamedRoutes returns the map where named routes are registered.
|
||||
func (r *Router) getNamedRoutes() map[string]*Route {
|
||||
if r.namedRoutes == nil {
|
||||
if r.parent != nil {
|
||||
r.namedRoutes = r.parent.getNamedRoutes()
|
||||
} else {
|
||||
r.namedRoutes = make(map[string]*Route)
|
||||
}
|
||||
}
|
||||
return r.namedRoutes
|
||||
}
|
||||
|
||||
// getRegexpGroup returns regexp definitions from the parent route, if any.
|
||||
func (r *Router) getRegexpGroup() *routeRegexpGroup {
|
||||
if r.parent != nil {
|
||||
return r.parent.getRegexpGroup()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) buildVars(m map[string]string) map[string]string {
|
||||
if r.parent != nil {
|
||||
m = r.parent.buildVars(m)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Route factories
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// NewRoute registers an empty route.
|
||||
func (r *Router) NewRoute() *Route {
|
||||
route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath}
|
||||
r.routes = append(r.routes, route)
|
||||
return route
|
||||
}
|
||||
|
||||
// Handle registers a new route with a matcher for the URL path.
|
||||
// See Route.Path() and Route.Handler().
|
||||
func (r *Router) Handle(path string, handler http.Handler) *Route {
|
||||
return r.NewRoute().Path(path).Handler(handler)
|
||||
}
|
||||
|
||||
// HandleFunc registers a new route with a matcher for the URL path.
|
||||
// See Route.Path() and Route.HandlerFunc().
|
||||
func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
|
||||
*http.Request)) *Route {
|
||||
return r.NewRoute().Path(path).HandlerFunc(f)
|
||||
}
|
||||
|
||||
// Headers registers a new route with a matcher for request header values.
|
||||
// See Route.Headers().
|
||||
func (r *Router) Headers(pairs ...string) *Route {
|
||||
return r.NewRoute().Headers(pairs...)
|
||||
}
|
||||
|
||||
// Host registers a new route with a matcher for the URL host.
|
||||
// See Route.Host().
|
||||
func (r *Router) Host(tpl string) *Route {
|
||||
return r.NewRoute().Host(tpl)
|
||||
}
|
||||
|
||||
// MatcherFunc registers a new route with a custom matcher function.
|
||||
// See Route.MatcherFunc().
|
||||
func (r *Router) MatcherFunc(f MatcherFunc) *Route {
|
||||
return r.NewRoute().MatcherFunc(f)
|
||||
}
|
||||
|
||||
// Methods registers a new route with a matcher for HTTP methods.
|
||||
// See Route.Methods().
|
||||
func (r *Router) Methods(methods ...string) *Route {
|
||||
return r.NewRoute().Methods(methods...)
|
||||
}
|
||||
|
||||
// Path registers a new route with a matcher for the URL path.
|
||||
// See Route.Path().
|
||||
func (r *Router) Path(tpl string) *Route {
|
||||
return r.NewRoute().Path(tpl)
|
||||
}
|
||||
|
||||
// PathPrefix registers a new route with a matcher for the URL path prefix.
|
||||
// See Route.PathPrefix().
|
||||
func (r *Router) PathPrefix(tpl string) *Route {
|
||||
return r.NewRoute().PathPrefix(tpl)
|
||||
}
|
||||
|
||||
// Queries registers a new route with a matcher for URL query values.
|
||||
// See Route.Queries().
|
||||
func (r *Router) Queries(pairs ...string) *Route {
|
||||
return r.NewRoute().Queries(pairs...)
|
||||
}
|
||||
|
||||
// Schemes registers a new route with a matcher for URL schemes.
|
||||
// See Route.Schemes().
|
||||
func (r *Router) Schemes(schemes ...string) *Route {
|
||||
return r.NewRoute().Schemes(schemes...)
|
||||
}
|
||||
|
||||
// BuildVarsFunc registers a new route with a custom function for modifying
|
||||
// route variables before building a URL.
|
||||
func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
|
||||
return r.NewRoute().BuildVarsFunc(f)
|
||||
}
|
||||
|
||||
// Walk walks the router and all its sub-routers, calling walkFn for each route
|
||||
// in the tree. The routes are walked in the order they were added. Sub-routers
|
||||
// are explored depth-first.
|
||||
func (r *Router) Walk(walkFn WalkFunc) error {
|
||||
return r.walk(walkFn, []*Route{})
|
||||
}
|
||||
|
||||
// SkipRouter is used as a return value from WalkFuncs to indicate that the
|
||||
// router that walk is about to descend down to should be skipped.
|
||||
var SkipRouter = errors.New("skip this router")
|
||||
|
||||
// WalkFunc is the type of the function called for each route visited by Walk.
|
||||
// At every invocation, it is given the current route, and the current router,
|
||||
// and a list of ancestor routes that lead to the current route.
|
||||
type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
|
||||
|
||||
func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
|
||||
for _, t := range r.routes {
|
||||
if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
err := walkFn(t, r, ancestors)
|
||||
if err == SkipRouter {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sr := range t.matchers {
|
||||
if h, ok := sr.(*Router); ok {
|
||||
err := h.walk(walkFn, ancestors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if h, ok := t.handler.(*Router); ok {
|
||||
ancestors = append(ancestors, t)
|
||||
err := h.walk(walkFn, ancestors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ancestors = ancestors[:len(ancestors)-1]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Context
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// RouteMatch stores information about a matched route.
|
||||
type RouteMatch struct {
|
||||
Route *Route
|
||||
Handler http.Handler
|
||||
Vars map[string]string
|
||||
}
|
||||
|
||||
type contextKey int
|
||||
|
||||
const (
|
||||
varsKey contextKey = iota
|
||||
routeKey
|
||||
)
|
||||
|
||||
// Vars returns the route variables for the current request, if any.
|
||||
func Vars(r *http.Request) map[string]string {
|
||||
if rv := contextGet(r, varsKey); rv != nil {
|
||||
return rv.(map[string]string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CurrentRoute returns the matched route for the current request, if any.
|
||||
// This only works when called inside the handler of the matched route
|
||||
// because the matched route is stored in the request context which is cleared
|
||||
// after the handler returns, unless the KeepContext option is set on the
|
||||
// Router.
|
||||
func CurrentRoute(r *http.Request) *Route {
|
||||
if rv := contextGet(r, routeKey); rv != nil {
|
||||
return rv.(*Route)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setVars(r *http.Request, val interface{}) *http.Request {
|
||||
return contextSet(r, varsKey, val)
|
||||
}
|
||||
|
||||
func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
|
||||
return contextSet(r, routeKey, val)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// getPath returns the escaped path if possible; doing what URL.EscapedPath()
|
||||
// which was added in go1.5 does
|
||||
func getPath(req *http.Request) string {
|
||||
if req.RequestURI != "" {
|
||||
// Extract the path from RequestURI (which is escaped unlike URL.Path)
|
||||
// as detailed here as detailed in https://golang.org/pkg/net/url/#URL
|
||||
// for < 1.5 server side workaround
|
||||
// http://localhost/path/here?v=1 -> /path/here
|
||||
path := req.RequestURI
|
||||
path = strings.TrimPrefix(path, req.URL.Scheme+`://`)
|
||||
path = strings.TrimPrefix(path, req.URL.Host)
|
||||
if i := strings.LastIndex(path, "?"); i > -1 {
|
||||
path = path[:i]
|
||||
}
|
||||
if i := strings.LastIndex(path, "#"); i > -1 {
|
||||
path = path[:i]
|
||||
}
|
||||
return path
|
||||
}
|
||||
return req.URL.Path
|
||||
}
|
||||
|
||||
// cleanPath returns the canonical path for p, eliminating . and .. elements.
|
||||
// Borrowed from the net/http package.
|
||||
func cleanPath(p string) string {
|
||||
if p == "" {
|
||||
return "/"
|
||||
}
|
||||
if p[0] != '/' {
|
||||
p = "/" + p
|
||||
}
|
||||
np := path.Clean(p)
|
||||
// path.Clean removes trailing slash except for root;
|
||||
// put the trailing slash back if necessary.
|
||||
if p[len(p)-1] == '/' && np != "/" {
|
||||
np += "/"
|
||||
}
|
||||
|
||||
return np
|
||||
}
|
||||
|
||||
// uniqueVars returns an error if two slices contain duplicated strings.
|
||||
func uniqueVars(s1, s2 []string) error {
|
||||
for _, v1 := range s1 {
|
||||
for _, v2 := range s2 {
|
||||
if v1 == v2 {
|
||||
return fmt.Errorf("mux: duplicated route variable %q", v2)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkPairs returns the count of strings passed in, and an error if
|
||||
// the count is not an even number.
|
||||
func checkPairs(pairs ...string) (int, error) {
|
||||
length := len(pairs)
|
||||
if length%2 != 0 {
|
||||
return length, fmt.Errorf(
|
||||
"mux: number of parameters must be multiple of 2, got %v", pairs)
|
||||
}
|
||||
return length, nil
|
||||
}
|
||||
|
||||
// mapFromPairsToString converts variadic string parameters to a
|
||||
// string to string map.
|
||||
func mapFromPairsToString(pairs ...string) (map[string]string, error) {
|
||||
length, err := checkPairs(pairs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]string, length/2)
|
||||
for i := 0; i < length; i += 2 {
|
||||
m[pairs[i]] = pairs[i+1]
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// mapFromPairsToRegex converts variadic string paramers to a
|
||||
// string to regex map.
|
||||
func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
|
||||
length, err := checkPairs(pairs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]*regexp.Regexp, length/2)
|
||||
for i := 0; i < length; i += 2 {
|
||||
regex, err := regexp.Compile(pairs[i+1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[pairs[i]] = regex
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// matchInArray returns true if the given string value is in the array.
|
||||
func matchInArray(arr []string, value string) bool {
|
||||
for _, v := range arr {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// matchMapWithString returns true if the given key/value pairs exist in a given map.
|
||||
func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
|
||||
for k, v := range toCheck {
|
||||
// Check if key exists.
|
||||
if canonicalKey {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
}
|
||||
if values := toMatch[k]; values == nil {
|
||||
return false
|
||||
} else if v != "" {
|
||||
// If value was defined as an empty string we only check that the
|
||||
// key exists. Otherwise we also check for equality.
|
||||
valueExists := false
|
||||
for _, value := range values {
|
||||
if v == value {
|
||||
valueExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valueExists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
|
||||
// the given regex
|
||||
func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
|
||||
for k, v := range toCheck {
|
||||
// Check if key exists.
|
||||
if canonicalKey {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
}
|
||||
if values := toMatch[k]; values == nil {
|
||||
return false
|
||||
} else if v != nil {
|
||||
// If value was defined as an empty string we only check that the
|
||||
// key exists. Otherwise we also check for equality.
|
||||
valueExists := false
|
||||
for _, value := range values {
|
||||
if v.MatchString(value) {
|
||||
valueExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valueExists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
323
vendor/github.com/gorilla/mux/regexp.go
generated
vendored
323
vendor/github.com/gorilla/mux/regexp.go
generated
vendored
@@ -1,323 +0,0 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// newRouteRegexp parses a route template and returns a routeRegexp,
|
||||
// used to match a host, a path or a query string.
|
||||
//
|
||||
// It will extract named variables, assemble a regexp to be matched, create
|
||||
// a "reverse" template to build URLs and compile regexps to validate variable
|
||||
// values used in URL building.
|
||||
//
|
||||
// Previously we accepted only Python-like identifiers for variable
|
||||
// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
|
||||
// name and pattern can't be empty, and names can't contain a colon.
|
||||
func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash, useEncodedPath bool) (*routeRegexp, error) {
|
||||
// Check if it is well-formed.
|
||||
idxs, errBraces := braceIndices(tpl)
|
||||
if errBraces != nil {
|
||||
return nil, errBraces
|
||||
}
|
||||
// Backup the original.
|
||||
template := tpl
|
||||
// Now let's parse it.
|
||||
defaultPattern := "[^/]+"
|
||||
if matchQuery {
|
||||
defaultPattern = "[^?&]*"
|
||||
} else if matchHost {
|
||||
defaultPattern = "[^.]+"
|
||||
matchPrefix = false
|
||||
}
|
||||
// Only match strict slash if not matching
|
||||
if matchPrefix || matchHost || matchQuery {
|
||||
strictSlash = false
|
||||
}
|
||||
// Set a flag for strictSlash.
|
||||
endSlash := false
|
||||
if strictSlash && strings.HasSuffix(tpl, "/") {
|
||||
tpl = tpl[:len(tpl)-1]
|
||||
endSlash = true
|
||||
}
|
||||
varsN := make([]string, len(idxs)/2)
|
||||
varsR := make([]*regexp.Regexp, len(idxs)/2)
|
||||
pattern := bytes.NewBufferString("")
|
||||
pattern.WriteByte('^')
|
||||
reverse := bytes.NewBufferString("")
|
||||
var end int
|
||||
var err error
|
||||
for i := 0; i < len(idxs); i += 2 {
|
||||
// Set all values we are interested in.
|
||||
raw := tpl[end:idxs[i]]
|
||||
end = idxs[i+1]
|
||||
parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
|
||||
name := parts[0]
|
||||
patt := defaultPattern
|
||||
if len(parts) == 2 {
|
||||
patt = parts[1]
|
||||
}
|
||||
// Name or pattern can't be empty.
|
||||
if name == "" || patt == "" {
|
||||
return nil, fmt.Errorf("mux: missing name or pattern in %q",
|
||||
tpl[idxs[i]:end])
|
||||
}
|
||||
// Build the regexp pattern.
|
||||
fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt)
|
||||
|
||||
// Build the reverse template.
|
||||
fmt.Fprintf(reverse, "%s%%s", raw)
|
||||
|
||||
// Append variable name and compiled pattern.
|
||||
varsN[i/2] = name
|
||||
varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Add the remaining.
|
||||
raw := tpl[end:]
|
||||
pattern.WriteString(regexp.QuoteMeta(raw))
|
||||
if strictSlash {
|
||||
pattern.WriteString("[/]?")
|
||||
}
|
||||
if matchQuery {
|
||||
// Add the default pattern if the query value is empty
|
||||
if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
|
||||
pattern.WriteString(defaultPattern)
|
||||
}
|
||||
}
|
||||
if !matchPrefix {
|
||||
pattern.WriteByte('$')
|
||||
}
|
||||
reverse.WriteString(raw)
|
||||
if endSlash {
|
||||
reverse.WriteByte('/')
|
||||
}
|
||||
// Compile full regexp.
|
||||
reg, errCompile := regexp.Compile(pattern.String())
|
||||
if errCompile != nil {
|
||||
return nil, errCompile
|
||||
}
|
||||
|
||||
// Check for capturing groups which used to work in older versions
|
||||
if reg.NumSubexp() != len(idxs)/2 {
|
||||
panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) +
|
||||
"Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)")
|
||||
}
|
||||
|
||||
// Done!
|
||||
return &routeRegexp{
|
||||
template: template,
|
||||
matchHost: matchHost,
|
||||
matchQuery: matchQuery,
|
||||
strictSlash: strictSlash,
|
||||
useEncodedPath: useEncodedPath,
|
||||
regexp: reg,
|
||||
reverse: reverse.String(),
|
||||
varsN: varsN,
|
||||
varsR: varsR,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// routeRegexp stores a regexp to match a host or path and information to
|
||||
// collect and validate route variables.
|
||||
type routeRegexp struct {
|
||||
// The unmodified template.
|
||||
template string
|
||||
// True for host match, false for path or query string match.
|
||||
matchHost bool
|
||||
// True for query string match, false for path and host match.
|
||||
matchQuery bool
|
||||
// The strictSlash value defined on the route, but disabled if PathPrefix was used.
|
||||
strictSlash bool
|
||||
// Determines whether to use encoded path from getPath function or unencoded
|
||||
// req.URL.Path for path matching
|
||||
useEncodedPath bool
|
||||
// Expanded regexp.
|
||||
regexp *regexp.Regexp
|
||||
// Reverse template.
|
||||
reverse string
|
||||
// Variable names.
|
||||
varsN []string
|
||||
// Variable regexps (validators).
|
||||
varsR []*regexp.Regexp
|
||||
}
|
||||
|
||||
// Match matches the regexp against the URL host or path.
|
||||
func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
|
||||
if !r.matchHost {
|
||||
if r.matchQuery {
|
||||
return r.matchQueryString(req)
|
||||
}
|
||||
path := req.URL.Path
|
||||
if r.useEncodedPath {
|
||||
path = getPath(req)
|
||||
}
|
||||
return r.regexp.MatchString(path)
|
||||
}
|
||||
|
||||
return r.regexp.MatchString(getHost(req))
|
||||
}
|
||||
|
||||
// url builds a URL part using the given values.
|
||||
func (r *routeRegexp) url(values map[string]string) (string, error) {
|
||||
urlValues := make([]interface{}, len(r.varsN))
|
||||
for k, v := range r.varsN {
|
||||
value, ok := values[v]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("mux: missing route variable %q", v)
|
||||
}
|
||||
urlValues[k] = value
|
||||
}
|
||||
rv := fmt.Sprintf(r.reverse, urlValues...)
|
||||
if !r.regexp.MatchString(rv) {
|
||||
// The URL is checked against the full regexp, instead of checking
|
||||
// individual variables. This is faster but to provide a good error
|
||||
// message, we check individual regexps if the URL doesn't match.
|
||||
for k, v := range r.varsN {
|
||||
if !r.varsR[k].MatchString(values[v]) {
|
||||
return "", fmt.Errorf(
|
||||
"mux: variable %q doesn't match, expected %q", values[v],
|
||||
r.varsR[k].String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// getURLQuery returns a single query parameter from a request URL.
|
||||
// For a URL with foo=bar&baz=ding, we return only the relevant key
|
||||
// value pair for the routeRegexp.
|
||||
func (r *routeRegexp) getURLQuery(req *http.Request) string {
|
||||
if !r.matchQuery {
|
||||
return ""
|
||||
}
|
||||
templateKey := strings.SplitN(r.template, "=", 2)[0]
|
||||
for key, vals := range req.URL.Query() {
|
||||
if key == templateKey && len(vals) > 0 {
|
||||
return key + "=" + vals[0]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *routeRegexp) matchQueryString(req *http.Request) bool {
|
||||
return r.regexp.MatchString(r.getURLQuery(req))
|
||||
}
|
||||
|
||||
// braceIndices returns the first level curly brace indices from a string.
|
||||
// It returns an error in case of unbalanced braces.
|
||||
func braceIndices(s string) ([]int, error) {
|
||||
var level, idx int
|
||||
var idxs []int
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '{':
|
||||
if level++; level == 1 {
|
||||
idx = i
|
||||
}
|
||||
case '}':
|
||||
if level--; level == 0 {
|
||||
idxs = append(idxs, idx, i+1)
|
||||
} else if level < 0 {
|
||||
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
if level != 0 {
|
||||
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||
}
|
||||
return idxs, nil
|
||||
}
|
||||
|
||||
// varGroupName builds a capturing group name for the indexed variable.
|
||||
func varGroupName(idx int) string {
|
||||
return "v" + strconv.Itoa(idx)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// routeRegexpGroup
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// routeRegexpGroup groups the route matchers that carry variables.
|
||||
type routeRegexpGroup struct {
|
||||
host *routeRegexp
|
||||
path *routeRegexp
|
||||
queries []*routeRegexp
|
||||
}
|
||||
|
||||
// setMatch extracts the variables from the URL once a route matches.
|
||||
func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
|
||||
// Store host variables.
|
||||
if v.host != nil {
|
||||
host := getHost(req)
|
||||
matches := v.host.regexp.FindStringSubmatchIndex(host)
|
||||
if len(matches) > 0 {
|
||||
extractVars(host, matches, v.host.varsN, m.Vars)
|
||||
}
|
||||
}
|
||||
path := req.URL.Path
|
||||
if r.useEncodedPath {
|
||||
path = getPath(req)
|
||||
}
|
||||
// Store path variables.
|
||||
if v.path != nil {
|
||||
matches := v.path.regexp.FindStringSubmatchIndex(path)
|
||||
if len(matches) > 0 {
|
||||
extractVars(path, matches, v.path.varsN, m.Vars)
|
||||
// Check if we should redirect.
|
||||
if v.path.strictSlash {
|
||||
p1 := strings.HasSuffix(path, "/")
|
||||
p2 := strings.HasSuffix(v.path.template, "/")
|
||||
if p1 != p2 {
|
||||
u, _ := url.Parse(req.URL.String())
|
||||
if p1 {
|
||||
u.Path = u.Path[:len(u.Path)-1]
|
||||
} else {
|
||||
u.Path += "/"
|
||||
}
|
||||
m.Handler = http.RedirectHandler(u.String(), 301)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Store query string variables.
|
||||
for _, q := range v.queries {
|
||||
queryURL := q.getURLQuery(req)
|
||||
matches := q.regexp.FindStringSubmatchIndex(queryURL)
|
||||
if len(matches) > 0 {
|
||||
extractVars(queryURL, matches, q.varsN, m.Vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getHost tries its best to return the request host.
|
||||
func getHost(r *http.Request) string {
|
||||
if r.URL.IsAbs() {
|
||||
return r.URL.Host
|
||||
}
|
||||
host := r.Host
|
||||
// Slice off any port information.
|
||||
if i := strings.Index(host, ":"); i != -1 {
|
||||
host = host[:i]
|
||||
}
|
||||
return host
|
||||
|
||||
}
|
||||
|
||||
func extractVars(input string, matches []int, names []string, output map[string]string) {
|
||||
for i, name := range names {
|
||||
output[name] = input[matches[2*i+2]:matches[2*i+3]]
|
||||
}
|
||||
}
|
||||
636
vendor/github.com/gorilla/mux/route.go
generated
vendored
636
vendor/github.com/gorilla/mux/route.go
generated
vendored
@@ -1,636 +0,0 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Route stores information to match a request and build URLs.
|
||||
type Route struct {
|
||||
// Parent where the route was registered (a Router).
|
||||
parent parentRoute
|
||||
// Request handler for the route.
|
||||
handler http.Handler
|
||||
// List of matchers.
|
||||
matchers []matcher
|
||||
// Manager for the variables from host and path.
|
||||
regexp *routeRegexpGroup
|
||||
// If true, when the path pattern is "/path/", accessing "/path" will
|
||||
// redirect to the former and vice versa.
|
||||
strictSlash bool
|
||||
// If true, when the path pattern is "/path//to", accessing "/path//to"
|
||||
// will not redirect
|
||||
skipClean bool
|
||||
// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
|
||||
useEncodedPath bool
|
||||
// If true, this route never matches: it is only used to build URLs.
|
||||
buildOnly bool
|
||||
// The name used to build URLs.
|
||||
name string
|
||||
// Error resulted from building a route.
|
||||
err error
|
||||
|
||||
buildVarsFunc BuildVarsFunc
|
||||
}
|
||||
|
||||
func (r *Route) SkipClean() bool {
|
||||
return r.skipClean
|
||||
}
|
||||
|
||||
// Match matches the route against the request.
|
||||
func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||
if r.buildOnly || r.err != nil {
|
||||
return false
|
||||
}
|
||||
// Match everything.
|
||||
for _, m := range r.matchers {
|
||||
if matched := m.Match(req, match); !matched {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Yay, we have a match. Let's collect some info about it.
|
||||
if match.Route == nil {
|
||||
match.Route = r
|
||||
}
|
||||
if match.Handler == nil {
|
||||
match.Handler = r.handler
|
||||
}
|
||||
if match.Vars == nil {
|
||||
match.Vars = make(map[string]string)
|
||||
}
|
||||
// Set variables.
|
||||
if r.regexp != nil {
|
||||
r.regexp.setMatch(req, match, r)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Route attributes
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetError returns an error resulted from building the route, if any.
|
||||
func (r *Route) GetError() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// BuildOnly sets the route to never match: it is only used to build URLs.
|
||||
func (r *Route) BuildOnly() *Route {
|
||||
r.buildOnly = true
|
||||
return r
|
||||
}
|
||||
|
||||
// Handler --------------------------------------------------------------------
|
||||
|
||||
// Handler sets a handler for the route.
|
||||
func (r *Route) Handler(handler http.Handler) *Route {
|
||||
if r.err == nil {
|
||||
r.handler = handler
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// HandlerFunc sets a handler function for the route.
|
||||
func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
|
||||
return r.Handler(http.HandlerFunc(f))
|
||||
}
|
||||
|
||||
// GetHandler returns the handler for the route, if any.
|
||||
func (r *Route) GetHandler() http.Handler {
|
||||
return r.handler
|
||||
}
|
||||
|
||||
// Name -----------------------------------------------------------------------
|
||||
|
||||
// Name sets the name for the route, used to build URLs.
|
||||
// If the name was registered already it will be overwritten.
|
||||
func (r *Route) Name(name string) *Route {
|
||||
if r.name != "" {
|
||||
r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
|
||||
r.name, name)
|
||||
}
|
||||
if r.err == nil {
|
||||
r.name = name
|
||||
r.getNamedRoutes()[name] = r
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GetName returns the name for the route, if any.
|
||||
func (r *Route) GetName() string {
|
||||
return r.name
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Matchers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// matcher types try to match a request.
|
||||
type matcher interface {
|
||||
Match(*http.Request, *RouteMatch) bool
|
||||
}
|
||||
|
||||
// addMatcher adds a matcher to the route.
|
||||
func (r *Route) addMatcher(m matcher) *Route {
|
||||
if r.err == nil {
|
||||
r.matchers = append(r.matchers, m)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// addRegexpMatcher adds a host or path matcher and builder to a route.
|
||||
func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
r.regexp = r.getRegexpGroup()
|
||||
if !matchHost && !matchQuery {
|
||||
if len(tpl) > 0 && tpl[0] != '/' {
|
||||
return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
|
||||
}
|
||||
if r.regexp.path != nil {
|
||||
tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
|
||||
}
|
||||
}
|
||||
rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash, r.useEncodedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, q := range r.regexp.queries {
|
||||
if err = uniqueVars(rr.varsN, q.varsN); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if matchHost {
|
||||
if r.regexp.path != nil {
|
||||
if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
r.regexp.host = rr
|
||||
} else {
|
||||
if r.regexp.host != nil {
|
||||
if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if matchQuery {
|
||||
r.regexp.queries = append(r.regexp.queries, rr)
|
||||
} else {
|
||||
r.regexp.path = rr
|
||||
}
|
||||
}
|
||||
r.addMatcher(rr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Headers --------------------------------------------------------------------
|
||||
|
||||
// headerMatcher matches the request against header values.
|
||||
type headerMatcher map[string]string
|
||||
|
||||
func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||
return matchMapWithString(m, r.Header, true)
|
||||
}
|
||||
|
||||
// Headers adds a matcher for request header values.
|
||||
// It accepts a sequence of key/value pairs to be matched. For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Headers("Content-Type", "application/json",
|
||||
// "X-Requested-With", "XMLHttpRequest")
|
||||
//
|
||||
// The above route will only match if both request header values match.
|
||||
// If the value is an empty string, it will match any value if the key is set.
|
||||
func (r *Route) Headers(pairs ...string) *Route {
|
||||
if r.err == nil {
|
||||
var headers map[string]string
|
||||
headers, r.err = mapFromPairsToString(pairs...)
|
||||
return r.addMatcher(headerMatcher(headers))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// headerRegexMatcher matches the request against the route given a regex for the header
|
||||
type headerRegexMatcher map[string]*regexp.Regexp
|
||||
|
||||
func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||
return matchMapWithRegex(m, r.Header, true)
|
||||
}
|
||||
|
||||
// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
|
||||
// support. For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.HeadersRegexp("Content-Type", "application/(text|json)",
|
||||
// "X-Requested-With", "XMLHttpRequest")
|
||||
//
|
||||
// The above route will only match if both the request header matches both regular expressions.
|
||||
// It the value is an empty string, it will match any value if the key is set.
|
||||
func (r *Route) HeadersRegexp(pairs ...string) *Route {
|
||||
if r.err == nil {
|
||||
var headers map[string]*regexp.Regexp
|
||||
headers, r.err = mapFromPairsToRegex(pairs...)
|
||||
return r.addMatcher(headerRegexMatcher(headers))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Host -----------------------------------------------------------------------
|
||||
|
||||
// Host adds a matcher for the URL host.
|
||||
// It accepts a template with zero or more URL variables enclosed by {}.
|
||||
// Variables can define an optional regexp pattern to be matched:
|
||||
//
|
||||
// - {name} matches anything until the next dot.
|
||||
//
|
||||
// - {name:pattern} matches the given regexp pattern.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Host("www.example.com")
|
||||
// r.Host("{subdomain}.domain.com")
|
||||
// r.Host("{subdomain:[a-z]+}.domain.com")
|
||||
//
|
||||
// Variable names must be unique in a given route. They can be retrieved
|
||||
// calling mux.Vars(request).
|
||||
func (r *Route) Host(tpl string) *Route {
|
||||
r.err = r.addRegexpMatcher(tpl, true, false, false)
|
||||
return r
|
||||
}
|
||||
|
||||
// MatcherFunc ----------------------------------------------------------------
|
||||
|
||||
// MatcherFunc is the function signature used by custom matchers.
|
||||
type MatcherFunc func(*http.Request, *RouteMatch) bool
|
||||
|
||||
// Match returns the match for a given request.
|
||||
func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
|
||||
return m(r, match)
|
||||
}
|
||||
|
||||
// MatcherFunc adds a custom function to be used as request matcher.
|
||||
func (r *Route) MatcherFunc(f MatcherFunc) *Route {
|
||||
return r.addMatcher(f)
|
||||
}
|
||||
|
||||
// Methods --------------------------------------------------------------------
|
||||
|
||||
// methodMatcher matches the request against HTTP methods.
|
||||
type methodMatcher []string
|
||||
|
||||
func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||
return matchInArray(m, r.Method)
|
||||
}
|
||||
|
||||
// Methods adds a matcher for HTTP methods.
|
||||
// It accepts a sequence of one or more methods to be matched, e.g.:
|
||||
// "GET", "POST", "PUT".
|
||||
func (r *Route) Methods(methods ...string) *Route {
|
||||
for k, v := range methods {
|
||||
methods[k] = strings.ToUpper(v)
|
||||
}
|
||||
return r.addMatcher(methodMatcher(methods))
|
||||
}
|
||||
|
||||
// Path -----------------------------------------------------------------------
|
||||
|
||||
// Path adds a matcher for the URL path.
|
||||
// It accepts a template with zero or more URL variables enclosed by {}. The
|
||||
// template must start with a "/".
|
||||
// Variables can define an optional regexp pattern to be matched:
|
||||
//
|
||||
// - {name} matches anything until the next slash.
|
||||
//
|
||||
// - {name:pattern} matches the given regexp pattern.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Path("/products/").Handler(ProductsHandler)
|
||||
// r.Path("/products/{key}").Handler(ProductsHandler)
|
||||
// r.Path("/articles/{category}/{id:[0-9]+}").
|
||||
// Handler(ArticleHandler)
|
||||
//
|
||||
// Variable names must be unique in a given route. They can be retrieved
|
||||
// calling mux.Vars(request).
|
||||
func (r *Route) Path(tpl string) *Route {
|
||||
r.err = r.addRegexpMatcher(tpl, false, false, false)
|
||||
return r
|
||||
}
|
||||
|
||||
// PathPrefix -----------------------------------------------------------------
|
||||
|
||||
// PathPrefix adds a matcher for the URL path prefix. This matches if the given
|
||||
// template is a prefix of the full URL path. See Route.Path() for details on
|
||||
// the tpl argument.
|
||||
//
|
||||
// Note that it does not treat slashes specially ("/foobar/" will be matched by
|
||||
// the prefix "/foo") so you may want to use a trailing slash here.
|
||||
//
|
||||
// Also note that the setting of Router.StrictSlash() has no effect on routes
|
||||
// with a PathPrefix matcher.
|
||||
func (r *Route) PathPrefix(tpl string) *Route {
|
||||
r.err = r.addRegexpMatcher(tpl, false, true, false)
|
||||
return r
|
||||
}
|
||||
|
||||
// Query ----------------------------------------------------------------------
|
||||
|
||||
// Queries adds a matcher for URL query values.
|
||||
// It accepts a sequence of key/value pairs. Values may define variables.
|
||||
// For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
|
||||
//
|
||||
// The above route will only match if the URL contains the defined queries
|
||||
// values, e.g.: ?foo=bar&id=42.
|
||||
//
|
||||
// It the value is an empty string, it will match any value if the key is set.
|
||||
//
|
||||
// Variables can define an optional regexp pattern to be matched:
|
||||
//
|
||||
// - {name} matches anything until the next slash.
|
||||
//
|
||||
// - {name:pattern} matches the given regexp pattern.
|
||||
func (r *Route) Queries(pairs ...string) *Route {
|
||||
length := len(pairs)
|
||||
if length%2 != 0 {
|
||||
r.err = fmt.Errorf(
|
||||
"mux: number of parameters must be multiple of 2, got %v", pairs)
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < length; i += 2 {
|
||||
if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Schemes --------------------------------------------------------------------
|
||||
|
||||
// schemeMatcher matches the request against URL schemes.
|
||||
type schemeMatcher []string
|
||||
|
||||
func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||
return matchInArray(m, r.URL.Scheme)
|
||||
}
|
||||
|
||||
// Schemes adds a matcher for URL schemes.
|
||||
// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
|
||||
func (r *Route) Schemes(schemes ...string) *Route {
|
||||
for k, v := range schemes {
|
||||
schemes[k] = strings.ToLower(v)
|
||||
}
|
||||
return r.addMatcher(schemeMatcher(schemes))
|
||||
}
|
||||
|
||||
// BuildVarsFunc --------------------------------------------------------------
|
||||
|
||||
// BuildVarsFunc is the function signature used by custom build variable
|
||||
// functions (which can modify route variables before a route's URL is built).
|
||||
type BuildVarsFunc func(map[string]string) map[string]string
|
||||
|
||||
// BuildVarsFunc adds a custom function to be used to modify build variables
|
||||
// before a route's URL is built.
|
||||
func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
|
||||
r.buildVarsFunc = f
|
||||
return r
|
||||
}
|
||||
|
||||
// Subrouter ------------------------------------------------------------------
|
||||
|
||||
// Subrouter creates a subrouter for the route.
|
||||
//
|
||||
// It will test the inner routes only if the parent route matched. For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// s := r.Host("www.example.com").Subrouter()
|
||||
// s.HandleFunc("/products/", ProductsHandler)
|
||||
// s.HandleFunc("/products/{key}", ProductHandler)
|
||||
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||
//
|
||||
// Here, the routes registered in the subrouter won't be tested if the host
|
||||
// doesn't match.
|
||||
func (r *Route) Subrouter() *Router {
|
||||
router := &Router{parent: r, strictSlash: r.strictSlash}
|
||||
r.addMatcher(router)
|
||||
return router
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// URL building
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// URL builds a URL for the route.
|
||||
//
|
||||
// It accepts a sequence of key/value pairs for the route variables. For
|
||||
// example, given this route:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
// Name("article")
|
||||
//
|
||||
// ...a URL for it can be built using:
|
||||
//
|
||||
// url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||
//
|
||||
// ...which will return an url.URL with the following path:
|
||||
//
|
||||
// "/articles/technology/42"
|
||||
//
|
||||
// This also works for host variables:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Host("{subdomain}.domain.com").
|
||||
// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
// Name("article")
|
||||
//
|
||||
// // url.String() will be "http://news.domain.com/articles/technology/42"
|
||||
// url, err := r.Get("article").URL("subdomain", "news",
|
||||
// "category", "technology",
|
||||
// "id", "42")
|
||||
//
|
||||
// All variables defined in the route are required, and their values must
|
||||
// conform to the corresponding patterns.
|
||||
func (r *Route) URL(pairs ...string) (*url.URL, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
if r.regexp == nil {
|
||||
return nil, errors.New("mux: route doesn't have a host or path")
|
||||
}
|
||||
values, err := r.prepareVars(pairs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var scheme, host, path string
|
||||
if r.regexp.host != nil {
|
||||
// Set a default scheme.
|
||||
scheme = "http"
|
||||
if host, err = r.regexp.host.url(values); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if r.regexp.path != nil {
|
||||
if path, err = r.regexp.path.url(values); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &url.URL{
|
||||
Scheme: scheme,
|
||||
Host: host,
|
||||
Path: path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// URLHost builds the host part of the URL for a route. See Route.URL().
|
||||
//
|
||||
// The route must have a host defined.
|
||||
func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
if r.regexp == nil || r.regexp.host == nil {
|
||||
return nil, errors.New("mux: route doesn't have a host")
|
||||
}
|
||||
values, err := r.prepareVars(pairs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host, err := r.regexp.host.url(values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &url.URL{
|
||||
Scheme: "http",
|
||||
Host: host,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// URLPath builds the path part of the URL for a route. See Route.URL().
|
||||
//
|
||||
// The route must have a path defined.
|
||||
func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
if r.regexp == nil || r.regexp.path == nil {
|
||||
return nil, errors.New("mux: route doesn't have a path")
|
||||
}
|
||||
values, err := r.prepareVars(pairs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := r.regexp.path.url(values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &url.URL{
|
||||
Path: path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetPathTemplate returns the template used to build the
|
||||
// route match.
|
||||
// This is useful for building simple REST API documentation and for instrumentation
|
||||
// against third-party services.
|
||||
// An error will be returned if the route does not define a path.
|
||||
func (r *Route) GetPathTemplate() (string, error) {
|
||||
if r.err != nil {
|
||||
return "", r.err
|
||||
}
|
||||
if r.regexp == nil || r.regexp.path == nil {
|
||||
return "", errors.New("mux: route doesn't have a path")
|
||||
}
|
||||
return r.regexp.path.template, nil
|
||||
}
|
||||
|
||||
// GetHostTemplate returns the template used to build the
|
||||
// route match.
|
||||
// This is useful for building simple REST API documentation and for instrumentation
|
||||
// against third-party services.
|
||||
// An error will be returned if the route does not define a host.
|
||||
func (r *Route) GetHostTemplate() (string, error) {
|
||||
if r.err != nil {
|
||||
return "", r.err
|
||||
}
|
||||
if r.regexp == nil || r.regexp.host == nil {
|
||||
return "", errors.New("mux: route doesn't have a host")
|
||||
}
|
||||
return r.regexp.host.template, nil
|
||||
}
|
||||
|
||||
// prepareVars converts the route variable pairs into a map. If the route has a
|
||||
// BuildVarsFunc, it is invoked.
|
||||
func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
|
||||
m, err := mapFromPairsToString(pairs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.buildVars(m), nil
|
||||
}
|
||||
|
||||
func (r *Route) buildVars(m map[string]string) map[string]string {
|
||||
if r.parent != nil {
|
||||
m = r.parent.buildVars(m)
|
||||
}
|
||||
if r.buildVarsFunc != nil {
|
||||
m = r.buildVarsFunc(m)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// parentRoute
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// parentRoute allows routes to know about parent host and path definitions.
|
||||
type parentRoute interface {
|
||||
getNamedRoutes() map[string]*Route
|
||||
getRegexpGroup() *routeRegexpGroup
|
||||
buildVars(map[string]string) map[string]string
|
||||
}
|
||||
|
||||
// getNamedRoutes returns the map where named routes are registered.
|
||||
func (r *Route) getNamedRoutes() map[string]*Route {
|
||||
if r.parent == nil {
|
||||
// During tests router is not always set.
|
||||
r.parent = NewRouter()
|
||||
}
|
||||
return r.parent.getNamedRoutes()
|
||||
}
|
||||
|
||||
// getRegexpGroup returns regexp definitions from this route.
|
||||
func (r *Route) getRegexpGroup() *routeRegexpGroup {
|
||||
if r.regexp == nil {
|
||||
if r.parent == nil {
|
||||
// During tests router is not always set.
|
||||
r.parent = NewRouter()
|
||||
}
|
||||
regexp := r.parent.getRegexpGroup()
|
||||
if regexp == nil {
|
||||
r.regexp = new(routeRegexpGroup)
|
||||
} else {
|
||||
// Copy.
|
||||
r.regexp = &routeRegexpGroup{
|
||||
host: regexp.host,
|
||||
path: regexp.path,
|
||||
queries: regexp.queries,
|
||||
}
|
||||
}
|
||||
}
|
||||
return r.regexp
|
||||
}
|
||||
27
vendor/github.com/gorilla/rpc/LICENSE
generated
vendored
27
vendor/github.com/gorilla/rpc/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
7
vendor/github.com/gorilla/rpc/README.md
generated
vendored
7
vendor/github.com/gorilla/rpc/README.md
generated
vendored
@@ -1,7 +0,0 @@
|
||||
rpc
|
||||
===
|
||||
[](https://travis-ci.org/gorilla/rpc)
|
||||
|
||||
gorilla/rpc is a foundation for RPC over HTTP services, providing access to the exported methods of an object through HTTP requests.
|
||||
|
||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/rpc
|
||||
27
vendor/github.com/gorilla/rpc/v2/LICENSE
generated
vendored
27
vendor/github.com/gorilla/rpc/v2/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
6
vendor/github.com/gorilla/rpc/v2/README.md
generated
vendored
6
vendor/github.com/gorilla/rpc/v2/README.md
generated
vendored
@@ -1,6 +0,0 @@
|
||||
rpc
|
||||
===
|
||||
|
||||
gorilla/rpc is a foundation for RPC over HTTP services, providing access to the exported methods of an object through HTTP requests.
|
||||
|
||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/rpc
|
||||
90
vendor/github.com/gorilla/rpc/v2/compression_selector.go
generated
vendored
90
vendor/github.com/gorilla/rpc/v2/compression_selector.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// gzipWriter writes and closes the gzip writer.
|
||||
type gzipWriter struct {
|
||||
w *gzip.Writer
|
||||
}
|
||||
|
||||
func (gw *gzipWriter) Write(p []byte) (n int, err error) {
|
||||
defer gw.w.Close()
|
||||
return gw.w.Write(p)
|
||||
}
|
||||
|
||||
// gzipEncoder implements the gzip compressed http encoder.
|
||||
type gzipEncoder struct {
|
||||
}
|
||||
|
||||
func (enc *gzipEncoder) Encode(w http.ResponseWriter) io.Writer {
|
||||
w.Header().Set("Content-Encoding", "gzip")
|
||||
return &gzipWriter{gzip.NewWriter(w)}
|
||||
}
|
||||
|
||||
// flateWriter writes and closes the flate writer.
|
||||
type flateWriter struct {
|
||||
w *flate.Writer
|
||||
}
|
||||
|
||||
func (fw *flateWriter) Write(p []byte) (n int, err error) {
|
||||
defer fw.w.Close()
|
||||
return fw.w.Write(p)
|
||||
}
|
||||
|
||||
// flateEncoder implements the flate compressed http encoder.
|
||||
type flateEncoder struct {
|
||||
}
|
||||
|
||||
func (enc *flateEncoder) Encode(w http.ResponseWriter) io.Writer {
|
||||
fw, err := flate.NewWriter(w, flate.DefaultCompression)
|
||||
if err != nil {
|
||||
return w
|
||||
}
|
||||
w.Header().Set("Content-Encoding", "deflate")
|
||||
return &flateWriter{fw}
|
||||
}
|
||||
|
||||
// CompressionSelector generates the compressed http encoder.
|
||||
type CompressionSelector struct {
|
||||
}
|
||||
|
||||
// acceptedEnc returns the first compression type in "Accept-Encoding" header
|
||||
// field of the request.
|
||||
func acceptedEnc(req *http.Request) string {
|
||||
encHeader := req.Header.Get("Accept-Encoding")
|
||||
if encHeader == "" {
|
||||
return ""
|
||||
}
|
||||
encTypes := strings.FieldsFunc(encHeader, func(r rune) bool {
|
||||
return unicode.IsSpace(r) || r == ','
|
||||
})
|
||||
for _, enc := range encTypes {
|
||||
if enc == "gzip" || enc == "deflate" {
|
||||
return enc
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Select method selects the correct compression encoder based on http HEADER.
|
||||
func (_ *CompressionSelector) Select(r *http.Request) Encoder {
|
||||
switch acceptedEnc(r) {
|
||||
case "gzip":
|
||||
return &gzipEncoder{}
|
||||
case "flate":
|
||||
return &flateEncoder{}
|
||||
}
|
||||
return DefaultEncoder
|
||||
}
|
||||
81
vendor/github.com/gorilla/rpc/v2/doc.go
generated
vendored
81
vendor/github.com/gorilla/rpc/v2/doc.go
generated
vendored
@@ -1,81 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package gorilla/rpc is a foundation for RPC over HTTP services, providing
|
||||
access to the exported methods of an object through HTTP requests.
|
||||
|
||||
This package derives from the standard net/rpc package but uses a single HTTP
|
||||
request per call instead of persistent connections. Other differences
|
||||
compared to net/rpc:
|
||||
|
||||
- Multiple codecs can be registered in the same server.
|
||||
- A codec is chosen based on the "Content-Type" header from the request.
|
||||
- Service methods also receive http.Request as parameter.
|
||||
- This package can be used on Google App Engine.
|
||||
|
||||
Let's setup a server and register a codec and service:
|
||||
|
||||
import (
|
||||
"http"
|
||||
"github.com/gorilla/rpc/v2"
|
||||
"github.com/gorilla/rpc/v2/json"
|
||||
)
|
||||
|
||||
func init() {
|
||||
s := rpc.NewServer()
|
||||
s.RegisterCodec(json.NewCodec(), "application/json")
|
||||
s.RegisterService(new(HelloService), "")
|
||||
http.Handle("/rpc", s)
|
||||
}
|
||||
|
||||
This server handles requests to the "/rpc" path using a JSON codec.
|
||||
A codec is tied to a content type. In the example above, the JSON codec is
|
||||
registered to serve requests with "application/json" as the value for the
|
||||
"Content-Type" header. If the header includes a charset definition, it is
|
||||
ignored; only the media-type part is taken into account.
|
||||
|
||||
A service can be registered using a name. If the name is empty, like in the
|
||||
example above, it will be inferred from the service type.
|
||||
|
||||
That's all about the server setup. Now let's define a simple service:
|
||||
|
||||
type HelloArgs struct {
|
||||
Who string
|
||||
}
|
||||
|
||||
type HelloReply struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
type HelloService struct {}
|
||||
|
||||
func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
|
||||
reply.Message = "Hello, " + args.Who + "!"
|
||||
return nil
|
||||
}
|
||||
|
||||
The example above defines a service with a method "HelloService.Say" and
|
||||
the arguments and reply related to that method.
|
||||
|
||||
The service must be exported (begin with an upper case letter) or local
|
||||
(defined in the package registering the service).
|
||||
|
||||
When a service is registered, the server inspects the service methods
|
||||
and make available the ones that follow these rules:
|
||||
|
||||
- The method name is exported.
|
||||
- The method has three arguments: *http.Request, *args, *reply.
|
||||
- All three arguments are pointers.
|
||||
- The second and third arguments are exported or local.
|
||||
- The method has return type error.
|
||||
|
||||
All other methods are ignored.
|
||||
|
||||
Gorilla has packages with common RPC codecs. Check out their documentation:
|
||||
|
||||
JSON: http://gorilla-web.appspot.com/pkg/rpc/json
|
||||
*/
|
||||
package rpc
|
||||
43
vendor/github.com/gorilla/rpc/v2/encoder_selector.go
generated
vendored
43
vendor/github.com/gorilla/rpc/v2/encoder_selector.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Encoder interface contains the encoder for http response.
|
||||
// Eg. gzip, flate compressions.
|
||||
type Encoder interface {
|
||||
Encode(w http.ResponseWriter) io.Writer
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
}
|
||||
|
||||
func (_ *encoder) Encode(w http.ResponseWriter) io.Writer {
|
||||
return w
|
||||
}
|
||||
|
||||
var DefaultEncoder = &encoder{}
|
||||
|
||||
// EncoderSelector interface provides a way to select encoder using the http
|
||||
// request. Typically people can use this to check HEADER of the request and
|
||||
// figure out client capabilities.
|
||||
// Eg. "Accept-Encoding" tells about supported compressions.
|
||||
type EncoderSelector interface {
|
||||
Select(r *http.Request) Encoder
|
||||
}
|
||||
|
||||
type encoderSelector struct {
|
||||
}
|
||||
|
||||
func (_ *encoderSelector) Select(_ *http.Request) Encoder {
|
||||
return DefaultEncoder
|
||||
}
|
||||
|
||||
var DefaultEncoderSelector = &encoderSelector{}
|
||||
75
vendor/github.com/gorilla/rpc/v2/json2/client.go
generated
vendored
75
vendor/github.com/gorilla/rpc/v2/json2/client.go
generated
vendored
@@ -1,75 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Request and Response
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// clientRequest represents a JSON-RPC request sent by a client.
|
||||
type clientRequest struct {
|
||||
// JSON-RPC protocol.
|
||||
Version string `json:"jsonrpc"`
|
||||
|
||||
// A String containing the name of the method to be invoked.
|
||||
Method string `json:"method"`
|
||||
|
||||
// Object to pass as request parameter to the method.
|
||||
Params interface{} `json:"params"`
|
||||
|
||||
// The request id. This can be of any type. It is used to match the
|
||||
// response with the request that it is replying to.
|
||||
Id uint64 `json:"id"`
|
||||
}
|
||||
|
||||
// clientResponse represents a JSON-RPC response returned to a client.
|
||||
type clientResponse struct {
|
||||
Version string `json:"jsonrpc"`
|
||||
Result *json.RawMessage `json:"result"`
|
||||
Error *json.RawMessage `json:"error"`
|
||||
}
|
||||
|
||||
// EncodeClientRequest encodes parameters for a JSON-RPC client request.
|
||||
func EncodeClientRequest(method string, args interface{}) ([]byte, error) {
|
||||
c := &clientRequest{
|
||||
Version: "2.0",
|
||||
Method: method,
|
||||
Params: args,
|
||||
Id: uint64(rand.Int63()),
|
||||
}
|
||||
return json.Marshal(c)
|
||||
}
|
||||
|
||||
// DecodeClientResponse decodes the response body of a client request into
|
||||
// the interface reply.
|
||||
func DecodeClientResponse(r io.Reader, reply interface{}) error {
|
||||
var c clientResponse
|
||||
if err := json.NewDecoder(r).Decode(&c); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Error != nil {
|
||||
jsonErr := &Error{}
|
||||
if err := json.Unmarshal(*c.Error, jsonErr); err != nil {
|
||||
return &Error{
|
||||
Code: E_SERVER,
|
||||
Message: string(*c.Error),
|
||||
}
|
||||
}
|
||||
return jsonErr
|
||||
}
|
||||
|
||||
if c.Result == nil {
|
||||
return ErrNullResult
|
||||
}
|
||||
|
||||
return json.Unmarshal(*c.Result, reply)
|
||||
}
|
||||
39
vendor/github.com/gorilla/rpc/v2/json2/error.go
generated
vendored
39
vendor/github.com/gorilla/rpc/v2/json2/error.go
generated
vendored
@@ -1,39 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type ErrorCode int
|
||||
|
||||
const (
|
||||
E_PARSE ErrorCode = -32700
|
||||
E_INVALID_REQ ErrorCode = -32600
|
||||
E_NO_METHOD ErrorCode = -32601
|
||||
E_BAD_PARAMS ErrorCode = -32602
|
||||
E_INTERNAL ErrorCode = -32603
|
||||
E_SERVER ErrorCode = -32000
|
||||
)
|
||||
|
||||
var ErrNullResult = errors.New("result is null")
|
||||
|
||||
type Error struct {
|
||||
// A Number that indicates the error type that occurred.
|
||||
Code ErrorCode `json:"code"` /* required */
|
||||
|
||||
// A String providing a short description of the error.
|
||||
// The message SHOULD be limited to a concise single sentence.
|
||||
Message string `json:"message"` /* required */
|
||||
|
||||
// A Primitive or Structured value that contains additional information about the error.
|
||||
Data interface{} `json:"data"` /* optional */
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
202
vendor/github.com/gorilla/rpc/v2/json2/server.go
generated
vendored
202
vendor/github.com/gorilla/rpc/v2/json2/server.go
generated
vendored
@@ -1,202 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/rpc/v2"
|
||||
)
|
||||
|
||||
var null = json.RawMessage([]byte("null"))
|
||||
var Version = "2.0"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Request and Response
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// serverRequest represents a JSON-RPC request received by the server.
|
||||
type serverRequest struct {
|
||||
// JSON-RPC protocol.
|
||||
Version string `json:"jsonrpc"`
|
||||
|
||||
// A String containing the name of the method to be invoked.
|
||||
Method string `json:"method"`
|
||||
|
||||
// A Structured value to pass as arguments to the method.
|
||||
Params *json.RawMessage `json:"params"`
|
||||
|
||||
// The request id. MUST be a string, number or null.
|
||||
// Our implementation will not do type checking for id.
|
||||
// It will be copied as it is.
|
||||
Id *json.RawMessage `json:"id"`
|
||||
}
|
||||
|
||||
// serverResponse represents a JSON-RPC response returned by the server.
|
||||
type serverResponse struct {
|
||||
// JSON-RPC protocol.
|
||||
Version string `json:"jsonrpc"`
|
||||
|
||||
// The Object that was returned by the invoked method. This must be null
|
||||
// in case there was an error invoking the method.
|
||||
// As per spec the member will be omitted if there was an error.
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
|
||||
// An Error object if there was an error invoking the method. It must be
|
||||
// null if there was no error.
|
||||
// As per spec the member will be omitted if there was no error.
|
||||
Error *Error `json:"error,omitempty"`
|
||||
|
||||
// This must be the same id as the request it is responding to.
|
||||
Id *json.RawMessage `json:"id"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Codec
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// NewcustomCodec returns a new JSON Codec based on passed encoder selector.
|
||||
func NewCustomCodec(encSel rpc.EncoderSelector) *Codec {
|
||||
return &Codec{encSel: encSel}
|
||||
}
|
||||
|
||||
// NewCodec returns a new JSON Codec.
|
||||
func NewCodec() *Codec {
|
||||
return NewCustomCodec(rpc.DefaultEncoderSelector)
|
||||
}
|
||||
|
||||
// Codec creates a CodecRequest to process each request.
|
||||
type Codec struct {
|
||||
encSel rpc.EncoderSelector
|
||||
}
|
||||
|
||||
// NewRequest returns a CodecRequest.
|
||||
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
|
||||
return newCodecRequest(r, c.encSel.Select(r))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CodecRequest
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// newCodecRequest returns a new CodecRequest.
|
||||
func newCodecRequest(r *http.Request, encoder rpc.Encoder) rpc.CodecRequest {
|
||||
// Decode the request body and check if RPC method is valid.
|
||||
req := new(serverRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(req)
|
||||
if err != nil {
|
||||
err = &Error{
|
||||
Code: E_PARSE,
|
||||
Message: err.Error(),
|
||||
Data: req,
|
||||
}
|
||||
}
|
||||
if req.Version != Version {
|
||||
err = &Error{
|
||||
Code: E_INVALID_REQ,
|
||||
Message: "jsonrpc must be " + Version,
|
||||
Data: req,
|
||||
}
|
||||
}
|
||||
r.Body.Close()
|
||||
return &CodecRequest{request: req, err: err, encoder: encoder}
|
||||
}
|
||||
|
||||
// CodecRequest decodes and encodes a single request.
|
||||
type CodecRequest struct {
|
||||
request *serverRequest
|
||||
err error
|
||||
encoder rpc.Encoder
|
||||
}
|
||||
|
||||
// Method returns the RPC method for the current request.
|
||||
//
|
||||
// The method uses a dotted notation as in "Service.Method".
|
||||
func (c *CodecRequest) Method() (string, error) {
|
||||
if c.err == nil {
|
||||
return c.request.Method, nil
|
||||
}
|
||||
return "", c.err
|
||||
}
|
||||
|
||||
// ReadRequest fills the request object for the RPC method.
|
||||
//
|
||||
// ReadRequest parses request parameters in two supported forms in
|
||||
// accordance with http://www.jsonrpc.org/specification#parameter_structures
|
||||
//
|
||||
// by-position: params MUST be an Array, containing the
|
||||
// values in the Server expected order.
|
||||
//
|
||||
// by-name: params MUST be an Object, with member names
|
||||
// that match the Server expected parameter names. The
|
||||
// absence of expected names MAY result in an error being
|
||||
// generated. The names MUST match exactly, including
|
||||
// case, to the method's expected parameters.
|
||||
func (c *CodecRequest) ReadRequest(args interface{}) error {
|
||||
if c.err == nil && c.request.Params != nil {
|
||||
// Note: if c.request.Params is nil it's not an error, it's an optional member.
|
||||
// JSON params structured object. Unmarshal to the args object.
|
||||
if err := json.Unmarshal(*c.request.Params, args); err != nil {
|
||||
// Clearly JSON params is not a structured object,
|
||||
// fallback and attempt an unmarshal with JSON params as
|
||||
// array value and RPC params is struct. Unmarshal into
|
||||
// array containing the request struct.
|
||||
params := [1]interface{}{args}
|
||||
if err = json.Unmarshal(*c.request.Params, ¶ms); err != nil {
|
||||
c.err = &Error{
|
||||
Code: E_INVALID_REQ,
|
||||
Message: err.Error(),
|
||||
Data: c.request.Params,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.err
|
||||
}
|
||||
|
||||
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||||
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) {
|
||||
res := &serverResponse{
|
||||
Version: Version,
|
||||
Result: reply,
|
||||
Id: c.request.Id,
|
||||
}
|
||||
c.writeServerResponse(w, res)
|
||||
}
|
||||
|
||||
func (c *CodecRequest) WriteError(w http.ResponseWriter, status int, err error) {
|
||||
jsonErr, ok := err.(*Error)
|
||||
if !ok {
|
||||
jsonErr = &Error{
|
||||
Code: E_SERVER,
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
res := &serverResponse{
|
||||
Version: Version,
|
||||
Error: jsonErr,
|
||||
Id: c.request.Id,
|
||||
}
|
||||
c.writeServerResponse(w, res)
|
||||
}
|
||||
|
||||
func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, res *serverResponse) {
|
||||
// Id is null for notifications and they don't have a response.
|
||||
if c.request.Id != nil {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
encoder := json.NewEncoder(c.encoder.Encode(w))
|
||||
err := encoder.Encode(res)
|
||||
|
||||
// Not sure in which case will this happen. But seems harmless.
|
||||
if err != nil {
|
||||
rpc.WriteError(w, 400, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type EmptyResponse struct {
|
||||
}
|
||||
164
vendor/github.com/gorilla/rpc/v2/map.go
generated
vendored
164
vendor/github.com/gorilla/rpc/v2/map.go
generated
vendored
@@ -1,164 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
// Precompute the reflect.Type of error and http.Request
|
||||
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||
typeOfRequest = reflect.TypeOf((*http.Request)(nil)).Elem()
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// service
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type service struct {
|
||||
name string // name of service
|
||||
rcvr reflect.Value // receiver of methods for the service
|
||||
rcvrType reflect.Type // type of the receiver
|
||||
methods map[string]*serviceMethod // registered methods
|
||||
}
|
||||
|
||||
type serviceMethod struct {
|
||||
method reflect.Method // receiver method
|
||||
argsType reflect.Type // type of the request argument
|
||||
replyType reflect.Type // type of the response argument
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// serviceMap
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// serviceMap is a registry for services.
|
||||
type serviceMap struct {
|
||||
mutex sync.Mutex
|
||||
services map[string]*service
|
||||
}
|
||||
|
||||
// register adds a new service using reflection to extract its methods.
|
||||
func (m *serviceMap) register(rcvr interface{}, name string) error {
|
||||
// Setup service.
|
||||
s := &service{
|
||||
name: name,
|
||||
rcvr: reflect.ValueOf(rcvr),
|
||||
rcvrType: reflect.TypeOf(rcvr),
|
||||
methods: make(map[string]*serviceMethod),
|
||||
}
|
||||
if name == "" {
|
||||
s.name = reflect.Indirect(s.rcvr).Type().Name()
|
||||
if !isExported(s.name) {
|
||||
return fmt.Errorf("rpc: type %q is not exported", s.name)
|
||||
}
|
||||
}
|
||||
if s.name == "" {
|
||||
return fmt.Errorf("rpc: no service name for type %q",
|
||||
s.rcvrType.String())
|
||||
}
|
||||
// Setup methods.
|
||||
for i := 0; i < s.rcvrType.NumMethod(); i++ {
|
||||
method := s.rcvrType.Method(i)
|
||||
mtype := method.Type
|
||||
// Method must be exported.
|
||||
if method.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
// Method needs four ins: receiver, *http.Request, *args, *reply.
|
||||
if mtype.NumIn() != 4 {
|
||||
continue
|
||||
}
|
||||
// First argument must be a pointer and must be http.Request.
|
||||
reqType := mtype.In(1)
|
||||
if reqType.Kind() != reflect.Ptr || reqType.Elem() != typeOfRequest {
|
||||
continue
|
||||
}
|
||||
// Second argument must be a pointer and must be exported.
|
||||
args := mtype.In(2)
|
||||
if args.Kind() != reflect.Ptr || !isExportedOrBuiltin(args) {
|
||||
continue
|
||||
}
|
||||
// Third argument must be a pointer and must be exported.
|
||||
reply := mtype.In(3)
|
||||
if reply.Kind() != reflect.Ptr || !isExportedOrBuiltin(reply) {
|
||||
continue
|
||||
}
|
||||
// Method needs one out: error.
|
||||
if mtype.NumOut() != 1 {
|
||||
continue
|
||||
}
|
||||
if returnType := mtype.Out(0); returnType != typeOfError {
|
||||
continue
|
||||
}
|
||||
s.methods[method.Name] = &serviceMethod{
|
||||
method: method,
|
||||
argsType: args.Elem(),
|
||||
replyType: reply.Elem(),
|
||||
}
|
||||
}
|
||||
if len(s.methods) == 0 {
|
||||
return fmt.Errorf("rpc: %q has no exported methods of suitable type",
|
||||
s.name)
|
||||
}
|
||||
// Add to the map.
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
if m.services == nil {
|
||||
m.services = make(map[string]*service)
|
||||
} else if _, ok := m.services[s.name]; ok {
|
||||
return fmt.Errorf("rpc: service already defined: %q", s.name)
|
||||
}
|
||||
m.services[s.name] = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// get returns a registered service given a method name.
|
||||
//
|
||||
// The method name uses a dotted notation as in "Service.Method".
|
||||
func (m *serviceMap) get(method string) (*service, *serviceMethod, error) {
|
||||
parts := strings.Split(method, ".")
|
||||
if len(parts) != 2 {
|
||||
err := fmt.Errorf("rpc: service/method request ill-formed: %q", method)
|
||||
return nil, nil, err
|
||||
}
|
||||
m.mutex.Lock()
|
||||
service := m.services[parts[0]]
|
||||
m.mutex.Unlock()
|
||||
if service == nil {
|
||||
err := fmt.Errorf("rpc: can't find service %q", method)
|
||||
return nil, nil, err
|
||||
}
|
||||
serviceMethod := service.methods[parts[1]]
|
||||
if serviceMethod == nil {
|
||||
err := fmt.Errorf("rpc: can't find method %q", method)
|
||||
return nil, nil, err
|
||||
}
|
||||
return service, serviceMethod, nil
|
||||
}
|
||||
|
||||
// isExported returns true of a string is an exported (upper case) name.
|
||||
func isExported(name string) bool {
|
||||
rune, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(rune)
|
||||
}
|
||||
|
||||
// isExportedOrBuiltin returns true if a type is exported or a builtin.
|
||||
func isExportedOrBuiltin(t reflect.Type) bool {
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
// PkgPath will be non-empty even for an exported type,
|
||||
// so we need to check the type name as well.
|
||||
return isExported(t.Name()) || t.PkgPath() == ""
|
||||
}
|
||||
164
vendor/github.com/gorilla/rpc/v2/server.go
generated
vendored
164
vendor/github.com/gorilla/rpc/v2/server.go
generated
vendored
@@ -1,164 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Codec
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Codec creates a CodecRequest to process each request.
|
||||
type Codec interface {
|
||||
NewRequest(*http.Request) CodecRequest
|
||||
}
|
||||
|
||||
// CodecRequest decodes a request and encodes a response using a specific
|
||||
// serialization scheme.
|
||||
type CodecRequest interface {
|
||||
// Reads the request and returns the RPC method name.
|
||||
Method() (string, error)
|
||||
// Reads the request filling the RPC method args.
|
||||
ReadRequest(interface{}) error
|
||||
// Writes the response using the RPC method reply.
|
||||
WriteResponse(http.ResponseWriter, interface{})
|
||||
// Writes an error produced by the server.
|
||||
WriteError(w http.ResponseWriter, status int, err error)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Server
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// NewServer returns a new RPC server.
|
||||
func NewServer() *Server {
|
||||
return &Server{
|
||||
codecs: make(map[string]Codec),
|
||||
services: new(serviceMap),
|
||||
}
|
||||
}
|
||||
|
||||
// Server serves registered RPC services using registered codecs.
|
||||
type Server struct {
|
||||
codecs map[string]Codec
|
||||
services *serviceMap
|
||||
}
|
||||
|
||||
// RegisterCodec adds a new codec to the server.
|
||||
//
|
||||
// Codecs are defined to process a given serialization scheme, e.g., JSON or
|
||||
// XML. A codec is chosen based on the "Content-Type" header from the request,
|
||||
// excluding the charset definition.
|
||||
func (s *Server) RegisterCodec(codec Codec, contentType string) {
|
||||
s.codecs[strings.ToLower(contentType)] = codec
|
||||
}
|
||||
|
||||
// RegisterService adds a new service to the server.
|
||||
//
|
||||
// The name parameter is optional: if empty it will be inferred from
|
||||
// the receiver type name.
|
||||
//
|
||||
// Methods from the receiver will be extracted if these rules are satisfied:
|
||||
//
|
||||
// - The receiver is exported (begins with an upper case letter) or local
|
||||
// (defined in the package registering the service).
|
||||
// - The method name is exported.
|
||||
// - The method has three arguments: *http.Request, *args, *reply.
|
||||
// - All three arguments are pointers.
|
||||
// - The second and third arguments are exported or local.
|
||||
// - The method has return type error.
|
||||
//
|
||||
// All other methods are ignored.
|
||||
func (s *Server) RegisterService(receiver interface{}, name string) error {
|
||||
return s.services.register(receiver, name)
|
||||
}
|
||||
|
||||
// HasMethod returns true if the given method is registered.
|
||||
//
|
||||
// The method uses a dotted notation as in "Service.Method".
|
||||
func (s *Server) HasMethod(method string) bool {
|
||||
if _, _, err := s.services.get(method); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ServeHTTP
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
WriteError(w, 405, "rpc: POST method required, received "+r.Method)
|
||||
return
|
||||
}
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
idx := strings.Index(contentType, ";")
|
||||
if idx != -1 {
|
||||
contentType = contentType[:idx]
|
||||
}
|
||||
var codec Codec
|
||||
if contentType == "" && len(s.codecs) == 1 {
|
||||
// If Content-Type is not set and only one codec has been registered,
|
||||
// then default to that codec.
|
||||
for _, c := range s.codecs {
|
||||
codec = c
|
||||
}
|
||||
} else if codec = s.codecs[strings.ToLower(contentType)]; codec == nil {
|
||||
WriteError(w, 415, "rpc: unrecognized Content-Type: "+contentType)
|
||||
return
|
||||
}
|
||||
// Create a new codec request.
|
||||
codecReq := codec.NewRequest(r)
|
||||
// Get service method to be called.
|
||||
method, errMethod := codecReq.Method()
|
||||
if errMethod != nil {
|
||||
codecReq.WriteError(w, 400, errMethod)
|
||||
return
|
||||
}
|
||||
serviceSpec, methodSpec, errGet := s.services.get(method)
|
||||
if errGet != nil {
|
||||
codecReq.WriteError(w, 400, errGet)
|
||||
return
|
||||
}
|
||||
// Decode the args.
|
||||
args := reflect.New(methodSpec.argsType)
|
||||
if errRead := codecReq.ReadRequest(args.Interface()); errRead != nil {
|
||||
codecReq.WriteError(w, 400, errRead)
|
||||
return
|
||||
}
|
||||
// Call the service method.
|
||||
reply := reflect.New(methodSpec.replyType)
|
||||
errValue := methodSpec.method.Func.Call([]reflect.Value{
|
||||
serviceSpec.rcvr,
|
||||
reflect.ValueOf(r),
|
||||
args,
|
||||
reply,
|
||||
})
|
||||
// Cast the result to error if needed.
|
||||
var errResult error
|
||||
errInter := errValue[0].Interface()
|
||||
if errInter != nil {
|
||||
errResult = errInter.(error)
|
||||
}
|
||||
// Prevents Internet Explorer from MIME-sniffing a response away
|
||||
// from the declared content-type
|
||||
w.Header().Set("x-content-type-options", "nosniff")
|
||||
// Encode the response.
|
||||
if errResult == nil {
|
||||
codecReq.WriteResponse(w, reply.Interface())
|
||||
} else {
|
||||
codecReq.WriteError(w, 400, errResult)
|
||||
}
|
||||
}
|
||||
|
||||
func WriteError(w http.ResponseWriter, status int, msg string) {
|
||||
w.WriteHeader(status)
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
fmt.Fprint(w, msg)
|
||||
}
|
||||
13
vendor/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
13
vendor/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
@@ -1,13 +0,0 @@
|
||||
Copyright 2014 Alan Shreve
|
||||
|
||||
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.
|
||||
23
vendor/github.com/inconshreveable/mousetrap/README.md
generated
vendored
23
vendor/github.com/inconshreveable/mousetrap/README.md
generated
vendored
@@ -1,23 +0,0 @@
|
||||
# mousetrap
|
||||
|
||||
mousetrap is a tiny library that answers a single question.
|
||||
|
||||
On a Windows machine, was the process invoked by someone double clicking on
|
||||
the executable file while browsing in explorer?
|
||||
|
||||
### Motivation
|
||||
|
||||
Windows developers unfamiliar with command line tools will often "double-click"
|
||||
the executable for a tool. Because most CLI tools print the help and then exit
|
||||
when invoked without arguments, this is often very frustrating for those users.
|
||||
|
||||
mousetrap provides a way to detect these invocations so that you can provide
|
||||
more helpful behavior and instructions on how to run the CLI tool. To see what
|
||||
this looks like, both from an organizational and a technical perspective, see
|
||||
https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/
|
||||
|
||||
### The interface
|
||||
|
||||
The library exposes a single interface:
|
||||
|
||||
func StartedByExplorer() (bool)
|
||||
15
vendor/github.com/inconshreveable/mousetrap/trap_others.go
generated
vendored
15
vendor/github.com/inconshreveable/mousetrap/trap_others.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package mousetrap
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user
|
||||
// double-clicking on the executable from explorer.exe
|
||||
//
|
||||
// It is conservative and returns false if any of the internal calls fail.
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
//
|
||||
// On non-Windows platforms, it always returns false.
|
||||
func StartedByExplorer() bool {
|
||||
return false
|
||||
}
|
||||
98
vendor/github.com/inconshreveable/mousetrap/trap_windows.go
generated
vendored
98
vendor/github.com/inconshreveable/mousetrap/trap_windows.go
generated
vendored
@@ -1,98 +0,0 @@
|
||||
// +build windows
|
||||
// +build !go1.4
|
||||
|
||||
package mousetrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// defined by the Win32 API
|
||||
th32cs_snapprocess uintptr = 0x2
|
||||
)
|
||||
|
||||
var (
|
||||
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||
CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot")
|
||||
Process32First = kernel.MustFindProc("Process32FirstW")
|
||||
Process32Next = kernel.MustFindProc("Process32NextW")
|
||||
)
|
||||
|
||||
// ProcessEntry32 structure defined by the Win32 API
|
||||
type processEntry32 struct {
|
||||
dwSize uint32
|
||||
cntUsage uint32
|
||||
th32ProcessID uint32
|
||||
th32DefaultHeapID int
|
||||
th32ModuleID uint32
|
||||
cntThreads uint32
|
||||
th32ParentProcessID uint32
|
||||
pcPriClassBase int32
|
||||
dwFlags uint32
|
||||
szExeFile [syscall.MAX_PATH]uint16
|
||||
}
|
||||
|
||||
func getProcessEntry(pid int) (pe *processEntry32, err error) {
|
||||
snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0))
|
||||
if snapshot == uintptr(syscall.InvalidHandle) {
|
||||
err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1)
|
||||
return
|
||||
}
|
||||
defer syscall.CloseHandle(syscall.Handle(snapshot))
|
||||
|
||||
var processEntry processEntry32
|
||||
processEntry.dwSize = uint32(unsafe.Sizeof(processEntry))
|
||||
ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||
if ok == 0 {
|
||||
err = fmt.Errorf("Process32First: %v", e1)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if processEntry.th32ProcessID == uint32(pid) {
|
||||
pe = &processEntry
|
||||
return
|
||||
}
|
||||
|
||||
ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||
if ok == 0 {
|
||||
err = fmt.Errorf("Process32Next: %v", e1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getppid() (pid int, err error) {
|
||||
pe, err := getProcessEntry(os.Getpid())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pid = int(pe.th32ParentProcessID)
|
||||
return
|
||||
}
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||
// on the executable from explorer.exe
|
||||
//
|
||||
// It is conservative and returns false if any of the internal calls fail.
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
func StartedByExplorer() bool {
|
||||
ppid, err := getppid()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pe, err := getProcessEntry(ppid)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
name := syscall.UTF16ToString(pe.szExeFile[:])
|
||||
return name == "explorer.exe"
|
||||
}
|
||||
46
vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go
generated
vendored
46
vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go
generated
vendored
@@ -1,46 +0,0 @@
|
||||
// +build windows
|
||||
// +build go1.4
|
||||
|
||||
package mousetrap
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) {
|
||||
snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CloseHandle(snapshot)
|
||||
var procEntry syscall.ProcessEntry32
|
||||
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
|
||||
if err = syscall.Process32First(snapshot, &procEntry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
if procEntry.ProcessID == uint32(pid) {
|
||||
return &procEntry, nil
|
||||
}
|
||||
err = syscall.Process32Next(snapshot, &procEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||
// on the executable from explorer.exe
|
||||
//
|
||||
// It is conservative and returns false if any of the internal calls fail.
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
func StartedByExplorer() bool {
|
||||
pe, err := getProcessEntry(os.Getppid())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:])
|
||||
}
|
||||
13
vendor/github.com/jmespath/go-jmespath/LICENSE
generated
vendored
13
vendor/github.com/jmespath/go-jmespath/LICENSE
generated
vendored
@@ -1,13 +0,0 @@
|
||||
Copyright 2015 James Saryerwinnie
|
||||
|
||||
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.
|
||||
7
vendor/github.com/jmespath/go-jmespath/README.md
generated
vendored
7
vendor/github.com/jmespath/go-jmespath/README.md
generated
vendored
@@ -1,7 +0,0 @@
|
||||
# go-jmespath - A JMESPath implementation in Go
|
||||
|
||||
[](https://travis-ci.org/jmespath/go-jmespath)
|
||||
|
||||
|
||||
|
||||
See http://jmespath.org for more info.
|
||||
49
vendor/github.com/jmespath/go-jmespath/api.go
generated
vendored
49
vendor/github.com/jmespath/go-jmespath/api.go
generated
vendored
@@ -1,49 +0,0 @@
|
||||
package jmespath
|
||||
|
||||
import "strconv"
|
||||
|
||||
// JmesPath is the epresentation of a compiled JMES path query. A JmesPath is
|
||||
// safe for concurrent use by multiple goroutines.
|
||||
type JMESPath struct {
|
||||
ast ASTNode
|
||||
intr *treeInterpreter
|
||||
}
|
||||
|
||||
// Compile parses a JMESPath expression and returns, if successful, a JMESPath
|
||||
// object that can be used to match against data.
|
||||
func Compile(expression string) (*JMESPath, error) {
|
||||
parser := NewParser()
|
||||
ast, err := parser.Parse(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jmespath := &JMESPath{ast: ast, intr: newInterpreter()}
|
||||
return jmespath, nil
|
||||
}
|
||||
|
||||
// MustCompile is like Compile but panics if the expression cannot be parsed.
|
||||
// It simplifies safe initialization of global variables holding compiled
|
||||
// JMESPaths.
|
||||
func MustCompile(expression string) *JMESPath {
|
||||
jmespath, err := Compile(expression)
|
||||
if err != nil {
|
||||
panic(`jmespath: Compile(` + strconv.Quote(expression) + `): ` + err.Error())
|
||||
}
|
||||
return jmespath
|
||||
}
|
||||
|
||||
// Search evaluates a JMESPath expression against input data and returns the result.
|
||||
func (jp *JMESPath) Search(data interface{}) (interface{}, error) {
|
||||
return jp.intr.Execute(jp.ast, data)
|
||||
}
|
||||
|
||||
// Search evaluates a JMESPath expression against input data and returns the result.
|
||||
func Search(expression string, data interface{}) (interface{}, error) {
|
||||
intr := newInterpreter()
|
||||
parser := NewParser()
|
||||
ast, err := parser.Parse(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intr.Execute(ast, data)
|
||||
}
|
||||
16
vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
generated
vendored
16
vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
generated
vendored
@@ -1,16 +0,0 @@
|
||||
// generated by stringer -type astNodeType; DO NOT EDIT
|
||||
|
||||
package jmespath
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _astNodeType_name = "ASTEmptyASTComparatorASTCurrentNodeASTExpRefASTFunctionExpressionASTFieldASTFilterProjectionASTFlattenASTIdentityASTIndexASTIndexExpressionASTKeyValPairASTLiteralASTMultiSelectHashASTMultiSelectListASTOrExpressionASTAndExpressionASTNotExpressionASTPipeASTProjectionASTSubexpressionASTSliceASTValueProjection"
|
||||
|
||||
var _astNodeType_index = [...]uint16{0, 8, 21, 35, 44, 65, 73, 92, 102, 113, 121, 139, 152, 162, 180, 198, 213, 229, 245, 252, 265, 281, 289, 307}
|
||||
|
||||
func (i astNodeType) String() string {
|
||||
if i < 0 || i >= astNodeType(len(_astNodeType_index)-1) {
|
||||
return fmt.Sprintf("astNodeType(%d)", i)
|
||||
}
|
||||
return _astNodeType_name[_astNodeType_index[i]:_astNodeType_index[i+1]]
|
||||
}
|
||||
842
vendor/github.com/jmespath/go-jmespath/functions.go
generated
vendored
842
vendor/github.com/jmespath/go-jmespath/functions.go
generated
vendored
@@ -1,842 +0,0 @@
|
||||
package jmespath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type jpFunction func(arguments []interface{}) (interface{}, error)
|
||||
|
||||
type jpType string
|
||||
|
||||
const (
|
||||
jpUnknown jpType = "unknown"
|
||||
jpNumber jpType = "number"
|
||||
jpString jpType = "string"
|
||||
jpArray jpType = "array"
|
||||
jpObject jpType = "object"
|
||||
jpArrayNumber jpType = "array[number]"
|
||||
jpArrayString jpType = "array[string]"
|
||||
jpExpref jpType = "expref"
|
||||
jpAny jpType = "any"
|
||||
)
|
||||
|
||||
type functionEntry struct {
|
||||
name string
|
||||
arguments []argSpec
|
||||
handler jpFunction
|
||||
hasExpRef bool
|
||||
}
|
||||
|
||||
type argSpec struct {
|
||||
types []jpType
|
||||
variadic bool
|
||||
}
|
||||
|
||||
type byExprString struct {
|
||||
intr *treeInterpreter
|
||||
node ASTNode
|
||||
items []interface{}
|
||||
hasError bool
|
||||
}
|
||||
|
||||
func (a *byExprString) Len() int {
|
||||
return len(a.items)
|
||||
}
|
||||
func (a *byExprString) Swap(i, j int) {
|
||||
a.items[i], a.items[j] = a.items[j], a.items[i]
|
||||
}
|
||||
func (a *byExprString) Less(i, j int) bool {
|
||||
first, err := a.intr.Execute(a.node, a.items[i])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
ith, ok := first.(string)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
second, err := a.intr.Execute(a.node, a.items[j])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
jth, ok := second.(string)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
return ith < jth
|
||||
}
|
||||
|
||||
type byExprFloat struct {
|
||||
intr *treeInterpreter
|
||||
node ASTNode
|
||||
items []interface{}
|
||||
hasError bool
|
||||
}
|
||||
|
||||
func (a *byExprFloat) Len() int {
|
||||
return len(a.items)
|
||||
}
|
||||
func (a *byExprFloat) Swap(i, j int) {
|
||||
a.items[i], a.items[j] = a.items[j], a.items[i]
|
||||
}
|
||||
func (a *byExprFloat) Less(i, j int) bool {
|
||||
first, err := a.intr.Execute(a.node, a.items[i])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
ith, ok := first.(float64)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
second, err := a.intr.Execute(a.node, a.items[j])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
jth, ok := second.(float64)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
return ith < jth
|
||||
}
|
||||
|
||||
type functionCaller struct {
|
||||
functionTable map[string]functionEntry
|
||||
}
|
||||
|
||||
func newFunctionCaller() *functionCaller {
|
||||
caller := &functionCaller{}
|
||||
caller.functionTable = map[string]functionEntry{
|
||||
"length": {
|
||||
name: "length",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString, jpArray, jpObject}},
|
||||
},
|
||||
handler: jpfLength,
|
||||
},
|
||||
"starts_with": {
|
||||
name: "starts_with",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString}},
|
||||
{types: []jpType{jpString}},
|
||||
},
|
||||
handler: jpfStartsWith,
|
||||
},
|
||||
"abs": {
|
||||
name: "abs",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpNumber}},
|
||||
},
|
||||
handler: jpfAbs,
|
||||
},
|
||||
"avg": {
|
||||
name: "avg",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber}},
|
||||
},
|
||||
handler: jpfAvg,
|
||||
},
|
||||
"ceil": {
|
||||
name: "ceil",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpNumber}},
|
||||
},
|
||||
handler: jpfCeil,
|
||||
},
|
||||
"contains": {
|
||||
name: "contains",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray, jpString}},
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfContains,
|
||||
},
|
||||
"ends_with": {
|
||||
name: "ends_with",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString}},
|
||||
{types: []jpType{jpString}},
|
||||
},
|
||||
handler: jpfEndsWith,
|
||||
},
|
||||
"floor": {
|
||||
name: "floor",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpNumber}},
|
||||
},
|
||||
handler: jpfFloor,
|
||||
},
|
||||
"map": {
|
||||
name: "amp",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpExpref}},
|
||||
{types: []jpType{jpArray}},
|
||||
},
|
||||
handler: jpfMap,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"max": {
|
||||
name: "max",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber, jpArrayString}},
|
||||
},
|
||||
handler: jpfMax,
|
||||
},
|
||||
"merge": {
|
||||
name: "merge",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpObject}, variadic: true},
|
||||
},
|
||||
handler: jpfMerge,
|
||||
},
|
||||
"max_by": {
|
||||
name: "max_by",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray}},
|
||||
{types: []jpType{jpExpref}},
|
||||
},
|
||||
handler: jpfMaxBy,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"sum": {
|
||||
name: "sum",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber}},
|
||||
},
|
||||
handler: jpfSum,
|
||||
},
|
||||
"min": {
|
||||
name: "min",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber, jpArrayString}},
|
||||
},
|
||||
handler: jpfMin,
|
||||
},
|
||||
"min_by": {
|
||||
name: "min_by",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray}},
|
||||
{types: []jpType{jpExpref}},
|
||||
},
|
||||
handler: jpfMinBy,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"type": {
|
||||
name: "type",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfType,
|
||||
},
|
||||
"keys": {
|
||||
name: "keys",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpObject}},
|
||||
},
|
||||
handler: jpfKeys,
|
||||
},
|
||||
"values": {
|
||||
name: "values",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpObject}},
|
||||
},
|
||||
handler: jpfValues,
|
||||
},
|
||||
"sort": {
|
||||
name: "sort",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayString, jpArrayNumber}},
|
||||
},
|
||||
handler: jpfSort,
|
||||
},
|
||||
"sort_by": {
|
||||
name: "sort_by",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray}},
|
||||
{types: []jpType{jpExpref}},
|
||||
},
|
||||
handler: jpfSortBy,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"join": {
|
||||
name: "join",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString}},
|
||||
{types: []jpType{jpArrayString}},
|
||||
},
|
||||
handler: jpfJoin,
|
||||
},
|
||||
"reverse": {
|
||||
name: "reverse",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray, jpString}},
|
||||
},
|
||||
handler: jpfReverse,
|
||||
},
|
||||
"to_array": {
|
||||
name: "to_array",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfToArray,
|
||||
},
|
||||
"to_string": {
|
||||
name: "to_string",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfToString,
|
||||
},
|
||||
"to_number": {
|
||||
name: "to_number",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfToNumber,
|
||||
},
|
||||
"not_null": {
|
||||
name: "not_null",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}, variadic: true},
|
||||
},
|
||||
handler: jpfNotNull,
|
||||
},
|
||||
}
|
||||
return caller
|
||||
}
|
||||
|
||||
func (e *functionEntry) resolveArgs(arguments []interface{}) ([]interface{}, error) {
|
||||
if len(e.arguments) == 0 {
|
||||
return arguments, nil
|
||||
}
|
||||
if !e.arguments[len(e.arguments)-1].variadic {
|
||||
if len(e.arguments) != len(arguments) {
|
||||
return nil, errors.New("incorrect number of args")
|
||||
}
|
||||
for i, spec := range e.arguments {
|
||||
userArg := arguments[i]
|
||||
err := spec.typeCheck(userArg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return arguments, nil
|
||||
}
|
||||
if len(arguments) < len(e.arguments) {
|
||||
return nil, errors.New("Invalid arity.")
|
||||
}
|
||||
return arguments, nil
|
||||
}
|
||||
|
||||
func (a *argSpec) typeCheck(arg interface{}) error {
|
||||
for _, t := range a.types {
|
||||
switch t {
|
||||
case jpNumber:
|
||||
if _, ok := arg.(float64); ok {
|
||||
return nil
|
||||
}
|
||||
case jpString:
|
||||
if _, ok := arg.(string); ok {
|
||||
return nil
|
||||
}
|
||||
case jpArray:
|
||||
if isSliceType(arg) {
|
||||
return nil
|
||||
}
|
||||
case jpObject:
|
||||
if _, ok := arg.(map[string]interface{}); ok {
|
||||
return nil
|
||||
}
|
||||
case jpArrayNumber:
|
||||
if _, ok := toArrayNum(arg); ok {
|
||||
return nil
|
||||
}
|
||||
case jpArrayString:
|
||||
if _, ok := toArrayStr(arg); ok {
|
||||
return nil
|
||||
}
|
||||
case jpAny:
|
||||
return nil
|
||||
case jpExpref:
|
||||
if _, ok := arg.(expRef); ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Invalid type for: %v, expected: %#v", arg, a.types)
|
||||
}
|
||||
|
||||
func (f *functionCaller) CallFunction(name string, arguments []interface{}, intr *treeInterpreter) (interface{}, error) {
|
||||
entry, ok := f.functionTable[name]
|
||||
if !ok {
|
||||
return nil, errors.New("unknown function: " + name)
|
||||
}
|
||||
resolvedArgs, err := entry.resolveArgs(arguments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry.hasExpRef {
|
||||
var extra []interface{}
|
||||
extra = append(extra, intr)
|
||||
resolvedArgs = append(extra, resolvedArgs...)
|
||||
}
|
||||
return entry.handler(resolvedArgs)
|
||||
}
|
||||
|
||||
func jpfAbs(arguments []interface{}) (interface{}, error) {
|
||||
num := arguments[0].(float64)
|
||||
return math.Abs(num), nil
|
||||
}
|
||||
|
||||
func jpfLength(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0]
|
||||
if c, ok := arg.(string); ok {
|
||||
return float64(utf8.RuneCountInString(c)), nil
|
||||
} else if isSliceType(arg) {
|
||||
v := reflect.ValueOf(arg)
|
||||
return float64(v.Len()), nil
|
||||
} else if c, ok := arg.(map[string]interface{}); ok {
|
||||
return float64(len(c)), nil
|
||||
}
|
||||
return nil, errors.New("could not compute length()")
|
||||
}
|
||||
|
||||
func jpfStartsWith(arguments []interface{}) (interface{}, error) {
|
||||
search := arguments[0].(string)
|
||||
prefix := arguments[1].(string)
|
||||
return strings.HasPrefix(search, prefix), nil
|
||||
}
|
||||
|
||||
func jpfAvg(arguments []interface{}) (interface{}, error) {
|
||||
// We've already type checked the value so we can safely use
|
||||
// type assertions.
|
||||
args := arguments[0].([]interface{})
|
||||
length := float64(len(args))
|
||||
numerator := 0.0
|
||||
for _, n := range args {
|
||||
numerator += n.(float64)
|
||||
}
|
||||
return numerator / length, nil
|
||||
}
|
||||
func jpfCeil(arguments []interface{}) (interface{}, error) {
|
||||
val := arguments[0].(float64)
|
||||
return math.Ceil(val), nil
|
||||
}
|
||||
func jpfContains(arguments []interface{}) (interface{}, error) {
|
||||
search := arguments[0]
|
||||
el := arguments[1]
|
||||
if searchStr, ok := search.(string); ok {
|
||||
if elStr, ok := el.(string); ok {
|
||||
return strings.Index(searchStr, elStr) != -1, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
// Otherwise this is a generic contains for []interface{}
|
||||
general := search.([]interface{})
|
||||
for _, item := range general {
|
||||
if item == el {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func jpfEndsWith(arguments []interface{}) (interface{}, error) {
|
||||
search := arguments[0].(string)
|
||||
suffix := arguments[1].(string)
|
||||
return strings.HasSuffix(search, suffix), nil
|
||||
}
|
||||
func jpfFloor(arguments []interface{}) (interface{}, error) {
|
||||
val := arguments[0].(float64)
|
||||
return math.Floor(val), nil
|
||||
}
|
||||
func jpfMap(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
exp := arguments[1].(expRef)
|
||||
node := exp.ref
|
||||
arr := arguments[2].([]interface{})
|
||||
mapped := make([]interface{}, 0, len(arr))
|
||||
for _, value := range arr {
|
||||
current, err := intr.Execute(node, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapped = append(mapped, current)
|
||||
}
|
||||
return mapped, nil
|
||||
}
|
||||
func jpfMax(arguments []interface{}) (interface{}, error) {
|
||||
if items, ok := toArrayNum(arguments[0]); ok {
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item > best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
// Otherwise we're dealing with a max() of strings.
|
||||
items, _ := toArrayStr(arguments[0])
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item > best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
func jpfMerge(arguments []interface{}) (interface{}, error) {
|
||||
final := make(map[string]interface{})
|
||||
for _, m := range arguments {
|
||||
mapped := m.(map[string]interface{})
|
||||
for key, value := range mapped {
|
||||
final[key] = value
|
||||
}
|
||||
}
|
||||
return final, nil
|
||||
}
|
||||
func jpfMaxBy(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
arr := arguments[1].([]interface{})
|
||||
exp := arguments[2].(expRef)
|
||||
node := exp.ref
|
||||
if len(arr) == 0 {
|
||||
return nil, nil
|
||||
} else if len(arr) == 1 {
|
||||
return arr[0], nil
|
||||
}
|
||||
start, err := intr.Execute(node, arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t := start.(type) {
|
||||
case float64:
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(float64)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be number")
|
||||
}
|
||||
if current > bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
case string:
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be string")
|
||||
}
|
||||
if current > bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
default:
|
||||
return nil, errors.New("invalid type, must be number of string")
|
||||
}
|
||||
}
|
||||
func jpfSum(arguments []interface{}) (interface{}, error) {
|
||||
items, _ := toArrayNum(arguments[0])
|
||||
sum := 0.0
|
||||
for _, item := range items {
|
||||
sum += item
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func jpfMin(arguments []interface{}) (interface{}, error) {
|
||||
if items, ok := toArrayNum(arguments[0]); ok {
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item < best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
items, _ := toArrayStr(arguments[0])
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item < best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
|
||||
func jpfMinBy(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
arr := arguments[1].([]interface{})
|
||||
exp := arguments[2].(expRef)
|
||||
node := exp.ref
|
||||
if len(arr) == 0 {
|
||||
return nil, nil
|
||||
} else if len(arr) == 1 {
|
||||
return arr[0], nil
|
||||
}
|
||||
start, err := intr.Execute(node, arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t, ok := start.(float64); ok {
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(float64)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be number")
|
||||
}
|
||||
if current < bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
} else if t, ok := start.(string); ok {
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be string")
|
||||
}
|
||||
if current < bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
} else {
|
||||
return nil, errors.New("invalid type, must be number of string")
|
||||
}
|
||||
}
|
||||
func jpfType(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0]
|
||||
if _, ok := arg.(float64); ok {
|
||||
return "number", nil
|
||||
}
|
||||
if _, ok := arg.(string); ok {
|
||||
return "string", nil
|
||||
}
|
||||
if _, ok := arg.([]interface{}); ok {
|
||||
return "array", nil
|
||||
}
|
||||
if _, ok := arg.(map[string]interface{}); ok {
|
||||
return "object", nil
|
||||
}
|
||||
if arg == nil {
|
||||
return "null", nil
|
||||
}
|
||||
if arg == true || arg == false {
|
||||
return "boolean", nil
|
||||
}
|
||||
return nil, errors.New("unknown type")
|
||||
}
|
||||
func jpfKeys(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0].(map[string]interface{})
|
||||
collected := make([]interface{}, 0, len(arg))
|
||||
for key := range arg {
|
||||
collected = append(collected, key)
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
func jpfValues(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0].(map[string]interface{})
|
||||
collected := make([]interface{}, 0, len(arg))
|
||||
for _, value := range arg {
|
||||
collected = append(collected, value)
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
func jpfSort(arguments []interface{}) (interface{}, error) {
|
||||
if items, ok := toArrayNum(arguments[0]); ok {
|
||||
d := sort.Float64Slice(items)
|
||||
sort.Stable(d)
|
||||
final := make([]interface{}, len(d))
|
||||
for i, val := range d {
|
||||
final[i] = val
|
||||
}
|
||||
return final, nil
|
||||
}
|
||||
// Otherwise we're dealing with sort()'ing strings.
|
||||
items, _ := toArrayStr(arguments[0])
|
||||
d := sort.StringSlice(items)
|
||||
sort.Stable(d)
|
||||
final := make([]interface{}, len(d))
|
||||
for i, val := range d {
|
||||
final[i] = val
|
||||
}
|
||||
return final, nil
|
||||
}
|
||||
func jpfSortBy(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
arr := arguments[1].([]interface{})
|
||||
exp := arguments[2].(expRef)
|
||||
node := exp.ref
|
||||
if len(arr) == 0 {
|
||||
return arr, nil
|
||||
} else if len(arr) == 1 {
|
||||
return arr, nil
|
||||
}
|
||||
start, err := intr.Execute(node, arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := start.(float64); ok {
|
||||
sortable := &byExprFloat{intr, node, arr, false}
|
||||
sort.Stable(sortable)
|
||||
if sortable.hasError {
|
||||
return nil, errors.New("error in sort_by comparison")
|
||||
}
|
||||
return arr, nil
|
||||
} else if _, ok := start.(string); ok {
|
||||
sortable := &byExprString{intr, node, arr, false}
|
||||
sort.Stable(sortable)
|
||||
if sortable.hasError {
|
||||
return nil, errors.New("error in sort_by comparison")
|
||||
}
|
||||
return arr, nil
|
||||
} else {
|
||||
return nil, errors.New("invalid type, must be number of string")
|
||||
}
|
||||
}
|
||||
func jpfJoin(arguments []interface{}) (interface{}, error) {
|
||||
sep := arguments[0].(string)
|
||||
// We can't just do arguments[1].([]string), we have to
|
||||
// manually convert each item to a string.
|
||||
arrayStr := []string{}
|
||||
for _, item := range arguments[1].([]interface{}) {
|
||||
arrayStr = append(arrayStr, item.(string))
|
||||
}
|
||||
return strings.Join(arrayStr, sep), nil
|
||||
}
|
||||
func jpfReverse(arguments []interface{}) (interface{}, error) {
|
||||
if s, ok := arguments[0].(string); ok {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r), nil
|
||||
}
|
||||
items := arguments[0].([]interface{})
|
||||
length := len(items)
|
||||
reversed := make([]interface{}, length)
|
||||
for i, item := range items {
|
||||
reversed[length-(i+1)] = item
|
||||
}
|
||||
return reversed, nil
|
||||
}
|
||||
func jpfToArray(arguments []interface{}) (interface{}, error) {
|
||||
if _, ok := arguments[0].([]interface{}); ok {
|
||||
return arguments[0], nil
|
||||
}
|
||||
return arguments[:1:1], nil
|
||||
}
|
||||
func jpfToString(arguments []interface{}) (interface{}, error) {
|
||||
if v, ok := arguments[0].(string); ok {
|
||||
return v, nil
|
||||
}
|
||||
result, err := json.Marshal(arguments[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
func jpfToNumber(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0]
|
||||
if v, ok := arg.(float64); ok {
|
||||
return v, nil
|
||||
}
|
||||
if v, ok := arg.(string); ok {
|
||||
conv, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return conv, nil
|
||||
}
|
||||
if _, ok := arg.([]interface{}); ok {
|
||||
return nil, nil
|
||||
}
|
||||
if _, ok := arg.(map[string]interface{}); ok {
|
||||
return nil, nil
|
||||
}
|
||||
if arg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if arg == true || arg == false {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("unknown type")
|
||||
}
|
||||
func jpfNotNull(arguments []interface{}) (interface{}, error) {
|
||||
for _, arg := range arguments {
|
||||
if arg != nil {
|
||||
return arg, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
418
vendor/github.com/jmespath/go-jmespath/interpreter.go
generated
vendored
418
vendor/github.com/jmespath/go-jmespath/interpreter.go
generated
vendored
@@ -1,418 +0,0 @@
|
||||
package jmespath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
/* This is a tree based interpreter. It walks the AST and directly
|
||||
interprets the AST to search through a JSON document.
|
||||
*/
|
||||
|
||||
type treeInterpreter struct {
|
||||
fCall *functionCaller
|
||||
}
|
||||
|
||||
func newInterpreter() *treeInterpreter {
|
||||
interpreter := treeInterpreter{}
|
||||
interpreter.fCall = newFunctionCaller()
|
||||
return &interpreter
|
||||
}
|
||||
|
||||
type expRef struct {
|
||||
ref ASTNode
|
||||
}
|
||||
|
||||
// Execute takes an ASTNode and input data and interprets the AST directly.
|
||||
// It will produce the result of applying the JMESPath expression associated
|
||||
// with the ASTNode to the input data "value".
|
||||
func (intr *treeInterpreter) Execute(node ASTNode, value interface{}) (interface{}, error) {
|
||||
switch node.nodeType {
|
||||
case ASTComparator:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
right, err := intr.Execute(node.children[1], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch node.value {
|
||||
case tEQ:
|
||||
return objsEqual(left, right), nil
|
||||
case tNE:
|
||||
return !objsEqual(left, right), nil
|
||||
}
|
||||
leftNum, ok := left.(float64)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
rightNum, ok := right.(float64)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
switch node.value {
|
||||
case tGT:
|
||||
return leftNum > rightNum, nil
|
||||
case tGTE:
|
||||
return leftNum >= rightNum, nil
|
||||
case tLT:
|
||||
return leftNum < rightNum, nil
|
||||
case tLTE:
|
||||
return leftNum <= rightNum, nil
|
||||
}
|
||||
case ASTExpRef:
|
||||
return expRef{ref: node.children[0]}, nil
|
||||
case ASTFunctionExpression:
|
||||
resolvedArgs := []interface{}{}
|
||||
for _, arg := range node.children {
|
||||
current, err := intr.Execute(arg, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolvedArgs = append(resolvedArgs, current)
|
||||
}
|
||||
return intr.fCall.CallFunction(node.value.(string), resolvedArgs, intr)
|
||||
case ASTField:
|
||||
if m, ok := value.(map[string]interface{}); ok {
|
||||
key := node.value.(string)
|
||||
return m[key], nil
|
||||
}
|
||||
return intr.fieldFromStruct(node.value.(string), value)
|
||||
case ASTFilterProjection:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
sliceType, ok := left.([]interface{})
|
||||
if !ok {
|
||||
if isSliceType(left) {
|
||||
return intr.filterProjectionWithReflection(node, left)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
compareNode := node.children[2]
|
||||
collected := []interface{}{}
|
||||
for _, element := range sliceType {
|
||||
result, err := intr.Execute(compareNode, element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isFalse(result) {
|
||||
current, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
case ASTFlatten:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
sliceType, ok := left.([]interface{})
|
||||
if !ok {
|
||||
// If we can't type convert to []interface{}, there's
|
||||
// a chance this could still work via reflection if we're
|
||||
// dealing with user provided types.
|
||||
if isSliceType(left) {
|
||||
return intr.flattenWithReflection(left)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
flattened := []interface{}{}
|
||||
for _, element := range sliceType {
|
||||
if elementSlice, ok := element.([]interface{}); ok {
|
||||
flattened = append(flattened, elementSlice...)
|
||||
} else if isSliceType(element) {
|
||||
reflectFlat := []interface{}{}
|
||||
v := reflect.ValueOf(element)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
reflectFlat = append(reflectFlat, v.Index(i).Interface())
|
||||
}
|
||||
flattened = append(flattened, reflectFlat...)
|
||||
} else {
|
||||
flattened = append(flattened, element)
|
||||
}
|
||||
}
|
||||
return flattened, nil
|
||||
case ASTIdentity, ASTCurrentNode:
|
||||
return value, nil
|
||||
case ASTIndex:
|
||||
if sliceType, ok := value.([]interface{}); ok {
|
||||
index := node.value.(int)
|
||||
if index < 0 {
|
||||
index += len(sliceType)
|
||||
}
|
||||
if index < len(sliceType) && index >= 0 {
|
||||
return sliceType[index], nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
// Otherwise try via reflection.
|
||||
rv := reflect.ValueOf(value)
|
||||
if rv.Kind() == reflect.Slice {
|
||||
index := node.value.(int)
|
||||
if index < 0 {
|
||||
index += rv.Len()
|
||||
}
|
||||
if index < rv.Len() && index >= 0 {
|
||||
v := rv.Index(index)
|
||||
return v.Interface(), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
case ASTKeyValPair:
|
||||
return intr.Execute(node.children[0], value)
|
||||
case ASTLiteral:
|
||||
return node.value, nil
|
||||
case ASTMultiSelectHash:
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
collected := make(map[string]interface{})
|
||||
for _, child := range node.children {
|
||||
current, err := intr.Execute(child, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := child.value.(string)
|
||||
collected[key] = current
|
||||
}
|
||||
return collected, nil
|
||||
case ASTMultiSelectList:
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
collected := []interface{}{}
|
||||
for _, child := range node.children {
|
||||
current, err := intr.Execute(child, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
collected = append(collected, current)
|
||||
}
|
||||
return collected, nil
|
||||
case ASTOrExpression:
|
||||
matched, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isFalse(matched) {
|
||||
matched, err = intr.Execute(node.children[1], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return matched, nil
|
||||
case ASTAndExpression:
|
||||
matched, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isFalse(matched) {
|
||||
return matched, nil
|
||||
}
|
||||
return intr.Execute(node.children[1], value)
|
||||
case ASTNotExpression:
|
||||
matched, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isFalse(matched) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
case ASTPipe:
|
||||
result := value
|
||||
var err error
|
||||
for _, child := range node.children {
|
||||
result, err = intr.Execute(child, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
case ASTProjection:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sliceType, ok := left.([]interface{})
|
||||
if !ok {
|
||||
if isSliceType(left) {
|
||||
return intr.projectWithReflection(node, left)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
collected := []interface{}{}
|
||||
var current interface{}
|
||||
for _, element := range sliceType {
|
||||
current, err = intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
case ASTSubexpression, ASTIndexExpression:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intr.Execute(node.children[1], left)
|
||||
case ASTSlice:
|
||||
sliceType, ok := value.([]interface{})
|
||||
if !ok {
|
||||
if isSliceType(value) {
|
||||
return intr.sliceWithReflection(node, value)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
parts := node.value.([]*int)
|
||||
sliceParams := make([]sliceParam, 3)
|
||||
for i, part := range parts {
|
||||
if part != nil {
|
||||
sliceParams[i].Specified = true
|
||||
sliceParams[i].N = *part
|
||||
}
|
||||
}
|
||||
return slice(sliceType, sliceParams)
|
||||
case ASTValueProjection:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
mapType, ok := left.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
values := make([]interface{}, len(mapType))
|
||||
for _, value := range mapType {
|
||||
values = append(values, value)
|
||||
}
|
||||
collected := []interface{}{}
|
||||
for _, element := range values {
|
||||
current, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
return nil, errors.New("Unknown AST node: " + node.nodeType.String())
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) fieldFromStruct(key string, value interface{}) (interface{}, error) {
|
||||
rv := reflect.ValueOf(value)
|
||||
first, n := utf8.DecodeRuneInString(key)
|
||||
fieldName := string(unicode.ToUpper(first)) + key[n:]
|
||||
if rv.Kind() == reflect.Struct {
|
||||
v := rv.FieldByName(fieldName)
|
||||
if !v.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
return v.Interface(), nil
|
||||
} else if rv.Kind() == reflect.Ptr {
|
||||
// Handle multiple levels of indirection?
|
||||
if rv.IsNil() {
|
||||
return nil, nil
|
||||
}
|
||||
rv = rv.Elem()
|
||||
v := rv.FieldByName(fieldName)
|
||||
if !v.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
return v.Interface(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) flattenWithReflection(value interface{}) (interface{}, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
flattened := []interface{}{}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
if reflect.TypeOf(element).Kind() == reflect.Slice {
|
||||
// Then insert the contents of the element
|
||||
// slice into the flattened slice,
|
||||
// i.e flattened = append(flattened, mySlice...)
|
||||
elementV := reflect.ValueOf(element)
|
||||
for j := 0; j < elementV.Len(); j++ {
|
||||
flattened = append(
|
||||
flattened, elementV.Index(j).Interface())
|
||||
}
|
||||
} else {
|
||||
flattened = append(flattened, element)
|
||||
}
|
||||
}
|
||||
return flattened, nil
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) sliceWithReflection(node ASTNode, value interface{}) (interface{}, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
parts := node.value.([]*int)
|
||||
sliceParams := make([]sliceParam, 3)
|
||||
for i, part := range parts {
|
||||
if part != nil {
|
||||
sliceParams[i].Specified = true
|
||||
sliceParams[i].N = *part
|
||||
}
|
||||
}
|
||||
final := []interface{}{}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
final = append(final, element)
|
||||
}
|
||||
return slice(final, sliceParams)
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) filterProjectionWithReflection(node ASTNode, value interface{}) (interface{}, error) {
|
||||
compareNode := node.children[2]
|
||||
collected := []interface{}{}
|
||||
v := reflect.ValueOf(value)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
result, err := intr.Execute(compareNode, element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isFalse(result) {
|
||||
current, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) projectWithReflection(node ASTNode, value interface{}) (interface{}, error) {
|
||||
collected := []interface{}{}
|
||||
v := reflect.ValueOf(value)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
result, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result != nil {
|
||||
collected = append(collected, result)
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
420
vendor/github.com/jmespath/go-jmespath/lexer.go
generated
vendored
420
vendor/github.com/jmespath/go-jmespath/lexer.go
generated
vendored
@@ -1,420 +0,0 @@
|
||||
package jmespath
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type token struct {
|
||||
tokenType tokType
|
||||
value string
|
||||
position int
|
||||
length int
|
||||
}
|
||||
|
||||
type tokType int
|
||||
|
||||
const eof = -1
|
||||
|
||||
// Lexer contains information about the expression being tokenized.
|
||||
type Lexer struct {
|
||||
expression string // The expression provided by the user.
|
||||
currentPos int // The current position in the string.
|
||||
lastWidth int // The width of the current rune. This
|
||||
buf bytes.Buffer // Internal buffer used for building up values.
|
||||
}
|
||||
|
||||
// SyntaxError is the main error used whenever a lexing or parsing error occurs.
|
||||
type SyntaxError struct {
|
||||
msg string // Error message displayed to user
|
||||
Expression string // Expression that generated a SyntaxError
|
||||
Offset int // The location in the string where the error occurred
|
||||
}
|
||||
|
||||
func (e SyntaxError) Error() string {
|
||||
// In the future, it would be good to underline the specific
|
||||
// location where the error occurred.
|
||||
return "SyntaxError: " + e.msg
|
||||
}
|
||||
|
||||
// HighlightLocation will show where the syntax error occurred.
|
||||
// It will place a "^" character on a line below the expression
|
||||
// at the point where the syntax error occurred.
|
||||
func (e SyntaxError) HighlightLocation() string {
|
||||
return e.Expression + "\n" + strings.Repeat(" ", e.Offset) + "^"
|
||||
}
|
||||
|
||||
//go:generate stringer -type=tokType
|
||||
const (
|
||||
tUnknown tokType = iota
|
||||
tStar
|
||||
tDot
|
||||
tFilter
|
||||
tFlatten
|
||||
tLparen
|
||||
tRparen
|
||||
tLbracket
|
||||
tRbracket
|
||||
tLbrace
|
||||
tRbrace
|
||||
tOr
|
||||
tPipe
|
||||
tNumber
|
||||
tUnquotedIdentifier
|
||||
tQuotedIdentifier
|
||||
tComma
|
||||
tColon
|
||||
tLT
|
||||
tLTE
|
||||
tGT
|
||||
tGTE
|
||||
tEQ
|
||||
tNE
|
||||
tJSONLiteral
|
||||
tStringLiteral
|
||||
tCurrent
|
||||
tExpref
|
||||
tAnd
|
||||
tNot
|
||||
tEOF
|
||||
)
|
||||
|
||||
var basicTokens = map[rune]tokType{
|
||||
'.': tDot,
|
||||
'*': tStar,
|
||||
',': tComma,
|
||||
':': tColon,
|
||||
'{': tLbrace,
|
||||
'}': tRbrace,
|
||||
']': tRbracket, // tLbracket not included because it could be "[]"
|
||||
'(': tLparen,
|
||||
')': tRparen,
|
||||
'@': tCurrent,
|
||||
}
|
||||
|
||||
// Bit mask for [a-zA-Z_] shifted down 64 bits to fit in a single uint64.
|
||||
// When using this bitmask just be sure to shift the rune down 64 bits
|
||||
// before checking against identifierStartBits.
|
||||
const identifierStartBits uint64 = 576460745995190270
|
||||
|
||||
// Bit mask for [a-zA-Z0-9], 128 bits -> 2 uint64s.
|
||||
var identifierTrailingBits = [2]uint64{287948901175001088, 576460745995190270}
|
||||
|
||||
var whiteSpace = map[rune]bool{
|
||||
' ': true, '\t': true, '\n': true, '\r': true,
|
||||
}
|
||||
|
||||
func (t token) String() string {
|
||||
return fmt.Sprintf("Token{%+v, %s, %d, %d}",
|
||||
t.tokenType, t.value, t.position, t.length)
|
||||
}
|
||||
|
||||
// NewLexer creates a new JMESPath lexer.
|
||||
func NewLexer() *Lexer {
|
||||
lexer := Lexer{}
|
||||
return &lexer
|
||||
}
|
||||
|
||||
func (lexer *Lexer) next() rune {
|
||||
if lexer.currentPos >= len(lexer.expression) {
|
||||
lexer.lastWidth = 0
|
||||
return eof
|
||||
}
|
||||
r, w := utf8.DecodeRuneInString(lexer.expression[lexer.currentPos:])
|
||||
lexer.lastWidth = w
|
||||
lexer.currentPos += w
|
||||
return r
|
||||
}
|
||||
|
||||
func (lexer *Lexer) back() {
|
||||
lexer.currentPos -= lexer.lastWidth
|
||||
}
|
||||
|
||||
func (lexer *Lexer) peek() rune {
|
||||
t := lexer.next()
|
||||
lexer.back()
|
||||
return t
|
||||
}
|
||||
|
||||
// tokenize takes an expression and returns corresponding tokens.
|
||||
func (lexer *Lexer) tokenize(expression string) ([]token, error) {
|
||||
var tokens []token
|
||||
lexer.expression = expression
|
||||
lexer.currentPos = 0
|
||||
lexer.lastWidth = 0
|
||||
loop:
|
||||
for {
|
||||
r := lexer.next()
|
||||
if identifierStartBits&(1<<(uint64(r)-64)) > 0 {
|
||||
t := lexer.consumeUnquotedIdentifier()
|
||||
tokens = append(tokens, t)
|
||||
} else if val, ok := basicTokens[r]; ok {
|
||||
// Basic single char token.
|
||||
t := token{
|
||||
tokenType: val,
|
||||
value: string(r),
|
||||
position: lexer.currentPos - lexer.lastWidth,
|
||||
length: 1,
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '-' || (r >= '0' && r <= '9') {
|
||||
t := lexer.consumeNumber()
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '[' {
|
||||
t := lexer.consumeLBracket()
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '"' {
|
||||
t, err := lexer.consumeQuotedIdentifier()
|
||||
if err != nil {
|
||||
return tokens, err
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '\'' {
|
||||
t, err := lexer.consumeRawStringLiteral()
|
||||
if err != nil {
|
||||
return tokens, err
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '`' {
|
||||
t, err := lexer.consumeLiteral()
|
||||
if err != nil {
|
||||
return tokens, err
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '|' {
|
||||
t := lexer.matchOrElse(r, '|', tOr, tPipe)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '<' {
|
||||
t := lexer.matchOrElse(r, '=', tLTE, tLT)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '>' {
|
||||
t := lexer.matchOrElse(r, '=', tGTE, tGT)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '!' {
|
||||
t := lexer.matchOrElse(r, '=', tNE, tNot)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '=' {
|
||||
t := lexer.matchOrElse(r, '=', tEQ, tUnknown)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '&' {
|
||||
t := lexer.matchOrElse(r, '&', tAnd, tExpref)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == eof {
|
||||
break loop
|
||||
} else if _, ok := whiteSpace[r]; ok {
|
||||
// Ignore whitespace
|
||||
} else {
|
||||
return tokens, lexer.syntaxError(fmt.Sprintf("Unknown char: %s", strconv.QuoteRuneToASCII(r)))
|
||||
}
|
||||
}
|
||||
tokens = append(tokens, token{tEOF, "", len(lexer.expression), 0})
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
// Consume characters until the ending rune "r" is reached.
|
||||
// If the end of the expression is reached before seeing the
|
||||
// terminating rune "r", then an error is returned.
|
||||
// If no error occurs then the matching substring is returned.
|
||||
// The returned string will not include the ending rune.
|
||||
func (lexer *Lexer) consumeUntil(end rune) (string, error) {
|
||||
start := lexer.currentPos
|
||||
current := lexer.next()
|
||||
for current != end && current != eof {
|
||||
if current == '\\' && lexer.peek() != eof {
|
||||
lexer.next()
|
||||
}
|
||||
current = lexer.next()
|
||||
}
|
||||
if lexer.lastWidth == 0 {
|
||||
// Then we hit an EOF so we never reached the closing
|
||||
// delimiter.
|
||||
return "", SyntaxError{
|
||||
msg: "Unclosed delimiter: " + string(end),
|
||||
Expression: lexer.expression,
|
||||
Offset: len(lexer.expression),
|
||||
}
|
||||
}
|
||||
return lexer.expression[start : lexer.currentPos-lexer.lastWidth], nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeLiteral() (token, error) {
|
||||
start := lexer.currentPos
|
||||
value, err := lexer.consumeUntil('`')
|
||||
if err != nil {
|
||||
return token{}, err
|
||||
}
|
||||
value = strings.Replace(value, "\\`", "`", -1)
|
||||
return token{
|
||||
tokenType: tJSONLiteral,
|
||||
value: value,
|
||||
position: start,
|
||||
length: len(value),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeRawStringLiteral() (token, error) {
|
||||
start := lexer.currentPos
|
||||
currentIndex := start
|
||||
current := lexer.next()
|
||||
for current != '\'' && lexer.peek() != eof {
|
||||
if current == '\\' && lexer.peek() == '\'' {
|
||||
chunk := lexer.expression[currentIndex : lexer.currentPos-1]
|
||||
lexer.buf.WriteString(chunk)
|
||||
lexer.buf.WriteString("'")
|
||||
lexer.next()
|
||||
currentIndex = lexer.currentPos
|
||||
}
|
||||
current = lexer.next()
|
||||
}
|
||||
if lexer.lastWidth == 0 {
|
||||
// Then we hit an EOF so we never reached the closing
|
||||
// delimiter.
|
||||
return token{}, SyntaxError{
|
||||
msg: "Unclosed delimiter: '",
|
||||
Expression: lexer.expression,
|
||||
Offset: len(lexer.expression),
|
||||
}
|
||||
}
|
||||
if currentIndex < lexer.currentPos {
|
||||
lexer.buf.WriteString(lexer.expression[currentIndex : lexer.currentPos-1])
|
||||
}
|
||||
value := lexer.buf.String()
|
||||
// Reset the buffer so it can reused again.
|
||||
lexer.buf.Reset()
|
||||
return token{
|
||||
tokenType: tStringLiteral,
|
||||
value: value,
|
||||
position: start,
|
||||
length: len(value),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) syntaxError(msg string) SyntaxError {
|
||||
return SyntaxError{
|
||||
msg: msg,
|
||||
Expression: lexer.expression,
|
||||
Offset: lexer.currentPos - 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for a two char token, otherwise matches a single character
|
||||
// token. This is used whenever a two char token overlaps a single
|
||||
// char token, e.g. "||" -> tPipe, "|" -> tOr.
|
||||
func (lexer *Lexer) matchOrElse(first rune, second rune, matchedType tokType, singleCharType tokType) token {
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
nextRune := lexer.next()
|
||||
var t token
|
||||
if nextRune == second {
|
||||
t = token{
|
||||
tokenType: matchedType,
|
||||
value: string(first) + string(second),
|
||||
position: start,
|
||||
length: 2,
|
||||
}
|
||||
} else {
|
||||
lexer.back()
|
||||
t = token{
|
||||
tokenType: singleCharType,
|
||||
value: string(first),
|
||||
position: start,
|
||||
length: 1,
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeLBracket() token {
|
||||
// There's three options here:
|
||||
// 1. A filter expression "[?"
|
||||
// 2. A flatten operator "[]"
|
||||
// 3. A bare rbracket "["
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
nextRune := lexer.next()
|
||||
var t token
|
||||
if nextRune == '?' {
|
||||
t = token{
|
||||
tokenType: tFilter,
|
||||
value: "[?",
|
||||
position: start,
|
||||
length: 2,
|
||||
}
|
||||
} else if nextRune == ']' {
|
||||
t = token{
|
||||
tokenType: tFlatten,
|
||||
value: "[]",
|
||||
position: start,
|
||||
length: 2,
|
||||
}
|
||||
} else {
|
||||
t = token{
|
||||
tokenType: tLbracket,
|
||||
value: "[",
|
||||
position: start,
|
||||
length: 1,
|
||||
}
|
||||
lexer.back()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeQuotedIdentifier() (token, error) {
|
||||
start := lexer.currentPos
|
||||
value, err := lexer.consumeUntil('"')
|
||||
if err != nil {
|
||||
return token{}, err
|
||||
}
|
||||
var decoded string
|
||||
asJSON := []byte("\"" + value + "\"")
|
||||
if err := json.Unmarshal([]byte(asJSON), &decoded); err != nil {
|
||||
return token{}, err
|
||||
}
|
||||
return token{
|
||||
tokenType: tQuotedIdentifier,
|
||||
value: decoded,
|
||||
position: start - 1,
|
||||
length: len(decoded),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeUnquotedIdentifier() token {
|
||||
// Consume runes until we reach the end of an unquoted
|
||||
// identifier.
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
for {
|
||||
r := lexer.next()
|
||||
if r < 0 || r > 128 || identifierTrailingBits[uint64(r)/64]&(1<<(uint64(r)%64)) == 0 {
|
||||
lexer.back()
|
||||
break
|
||||
}
|
||||
}
|
||||
value := lexer.expression[start:lexer.currentPos]
|
||||
return token{
|
||||
tokenType: tUnquotedIdentifier,
|
||||
value: value,
|
||||
position: start,
|
||||
length: lexer.currentPos - start,
|
||||
}
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeNumber() token {
|
||||
// Consume runes until we reach something that's not a number.
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
for {
|
||||
r := lexer.next()
|
||||
if r < '0' || r > '9' {
|
||||
lexer.back()
|
||||
break
|
||||
}
|
||||
}
|
||||
value := lexer.expression[start:lexer.currentPos]
|
||||
return token{
|
||||
tokenType: tNumber,
|
||||
value: value,
|
||||
position: start,
|
||||
length: lexer.currentPos - start,
|
||||
}
|
||||
}
|
||||
603
vendor/github.com/jmespath/go-jmespath/parser.go
generated
vendored
603
vendor/github.com/jmespath/go-jmespath/parser.go
generated
vendored
@@ -1,603 +0,0 @@
|
||||
package jmespath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type astNodeType int
|
||||
|
||||
//go:generate stringer -type astNodeType
|
||||
const (
|
||||
ASTEmpty astNodeType = iota
|
||||
ASTComparator
|
||||
ASTCurrentNode
|
||||
ASTExpRef
|
||||
ASTFunctionExpression
|
||||
ASTField
|
||||
ASTFilterProjection
|
||||
ASTFlatten
|
||||
ASTIdentity
|
||||
ASTIndex
|
||||
ASTIndexExpression
|
||||
ASTKeyValPair
|
||||
ASTLiteral
|
||||
ASTMultiSelectHash
|
||||
ASTMultiSelectList
|
||||
ASTOrExpression
|
||||
ASTAndExpression
|
||||
ASTNotExpression
|
||||
ASTPipe
|
||||
ASTProjection
|
||||
ASTSubexpression
|
||||
ASTSlice
|
||||
ASTValueProjection
|
||||
)
|
||||
|
||||
// ASTNode represents the abstract syntax tree of a JMESPath expression.
|
||||
type ASTNode struct {
|
||||
nodeType astNodeType
|
||||
value interface{}
|
||||
children []ASTNode
|
||||
}
|
||||
|
||||
func (node ASTNode) String() string {
|
||||
return node.PrettyPrint(0)
|
||||
}
|
||||
|
||||
// PrettyPrint will pretty print the parsed AST.
|
||||
// The AST is an implementation detail and this pretty print
|
||||
// function is provided as a convenience method to help with
|
||||
// debugging. You should not rely on its output as the internal
|
||||
// structure of the AST may change at any time.
|
||||
func (node ASTNode) PrettyPrint(indent int) string {
|
||||
spaces := strings.Repeat(" ", indent)
|
||||
output := fmt.Sprintf("%s%s {\n", spaces, node.nodeType)
|
||||
nextIndent := indent + 2
|
||||
if node.value != nil {
|
||||
if converted, ok := node.value.(fmt.Stringer); ok {
|
||||
// Account for things like comparator nodes
|
||||
// that are enums with a String() method.
|
||||
output += fmt.Sprintf("%svalue: %s\n", strings.Repeat(" ", nextIndent), converted.String())
|
||||
} else {
|
||||
output += fmt.Sprintf("%svalue: %#v\n", strings.Repeat(" ", nextIndent), node.value)
|
||||
}
|
||||
}
|
||||
lastIndex := len(node.children)
|
||||
if lastIndex > 0 {
|
||||
output += fmt.Sprintf("%schildren: {\n", strings.Repeat(" ", nextIndent))
|
||||
childIndent := nextIndent + 2
|
||||
for _, elem := range node.children {
|
||||
output += elem.PrettyPrint(childIndent)
|
||||
}
|
||||
}
|
||||
output += fmt.Sprintf("%s}\n", spaces)
|
||||
return output
|
||||
}
|
||||
|
||||
var bindingPowers = map[tokType]int{
|
||||
tEOF: 0,
|
||||
tUnquotedIdentifier: 0,
|
||||
tQuotedIdentifier: 0,
|
||||
tRbracket: 0,
|
||||
tRparen: 0,
|
||||
tComma: 0,
|
||||
tRbrace: 0,
|
||||
tNumber: 0,
|
||||
tCurrent: 0,
|
||||
tExpref: 0,
|
||||
tColon: 0,
|
||||
tPipe: 1,
|
||||
tOr: 2,
|
||||
tAnd: 3,
|
||||
tEQ: 5,
|
||||
tLT: 5,
|
||||
tLTE: 5,
|
||||
tGT: 5,
|
||||
tGTE: 5,
|
||||
tNE: 5,
|
||||
tFlatten: 9,
|
||||
tStar: 20,
|
||||
tFilter: 21,
|
||||
tDot: 40,
|
||||
tNot: 45,
|
||||
tLbrace: 50,
|
||||
tLbracket: 55,
|
||||
tLparen: 60,
|
||||
}
|
||||
|
||||
// Parser holds state about the current expression being parsed.
|
||||
type Parser struct {
|
||||
expression string
|
||||
tokens []token
|
||||
index int
|
||||
}
|
||||
|
||||
// NewParser creates a new JMESPath parser.
|
||||
func NewParser() *Parser {
|
||||
p := Parser{}
|
||||
return &p
|
||||
}
|
||||
|
||||
// Parse will compile a JMESPath expression.
|
||||
func (p *Parser) Parse(expression string) (ASTNode, error) {
|
||||
lexer := NewLexer()
|
||||
p.expression = expression
|
||||
p.index = 0
|
||||
tokens, err := lexer.tokenize(expression)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
p.tokens = tokens
|
||||
parsed, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if p.current() != tEOF {
|
||||
return ASTNode{}, p.syntaxError(fmt.Sprintf(
|
||||
"Unexpected token at the end of the expresssion: %s", p.current()))
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseExpression(bindingPower int) (ASTNode, error) {
|
||||
var err error
|
||||
leftToken := p.lookaheadToken(0)
|
||||
p.advance()
|
||||
leftNode, err := p.nud(leftToken)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
currentToken := p.current()
|
||||
for bindingPower < bindingPowers[currentToken] {
|
||||
p.advance()
|
||||
leftNode, err = p.led(currentToken, leftNode)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
currentToken = p.current()
|
||||
}
|
||||
return leftNode, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseIndexExpression() (ASTNode, error) {
|
||||
if p.lookahead(0) == tColon || p.lookahead(1) == tColon {
|
||||
return p.parseSliceExpression()
|
||||
}
|
||||
indexStr := p.lookaheadToken(0).value
|
||||
parsedInt, err := strconv.Atoi(indexStr)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
indexNode := ASTNode{nodeType: ASTIndex, value: parsedInt}
|
||||
p.advance()
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return indexNode, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseSliceExpression() (ASTNode, error) {
|
||||
parts := []*int{nil, nil, nil}
|
||||
index := 0
|
||||
current := p.current()
|
||||
for current != tRbracket && index < 3 {
|
||||
if current == tColon {
|
||||
index++
|
||||
p.advance()
|
||||
} else if current == tNumber {
|
||||
parsedInt, err := strconv.Atoi(p.lookaheadToken(0).value)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
parts[index] = &parsedInt
|
||||
p.advance()
|
||||
} else {
|
||||
return ASTNode{}, p.syntaxError(
|
||||
"Expected tColon or tNumber" + ", received: " + p.current().String())
|
||||
}
|
||||
current = p.current()
|
||||
}
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTSlice,
|
||||
value: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) match(tokenType tokType) error {
|
||||
if p.current() == tokenType {
|
||||
p.advance()
|
||||
return nil
|
||||
}
|
||||
return p.syntaxError("Expected " + tokenType.String() + ", received: " + p.current().String())
|
||||
}
|
||||
|
||||
func (p *Parser) led(tokenType tokType, node ASTNode) (ASTNode, error) {
|
||||
switch tokenType {
|
||||
case tDot:
|
||||
if p.current() != tStar {
|
||||
right, err := p.parseDotRHS(bindingPowers[tDot])
|
||||
return ASTNode{
|
||||
nodeType: ASTSubexpression,
|
||||
children: []ASTNode{node, right},
|
||||
}, err
|
||||
}
|
||||
p.advance()
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tDot])
|
||||
return ASTNode{
|
||||
nodeType: ASTValueProjection,
|
||||
children: []ASTNode{node, right},
|
||||
}, err
|
||||
case tPipe:
|
||||
right, err := p.parseExpression(bindingPowers[tPipe])
|
||||
return ASTNode{nodeType: ASTPipe, children: []ASTNode{node, right}}, err
|
||||
case tOr:
|
||||
right, err := p.parseExpression(bindingPowers[tOr])
|
||||
return ASTNode{nodeType: ASTOrExpression, children: []ASTNode{node, right}}, err
|
||||
case tAnd:
|
||||
right, err := p.parseExpression(bindingPowers[tAnd])
|
||||
return ASTNode{nodeType: ASTAndExpression, children: []ASTNode{node, right}}, err
|
||||
case tLparen:
|
||||
name := node.value
|
||||
var args []ASTNode
|
||||
for p.current() != tRparen {
|
||||
expression, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if p.current() == tComma {
|
||||
if err := p.match(tComma); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
}
|
||||
args = append(args, expression)
|
||||
}
|
||||
if err := p.match(tRparen); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTFunctionExpression,
|
||||
value: name,
|
||||
children: args,
|
||||
}, nil
|
||||
case tFilter:
|
||||
return p.parseFilter(node)
|
||||
case tFlatten:
|
||||
left := ASTNode{nodeType: ASTFlatten, children: []ASTNode{node}}
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tFlatten])
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{left, right},
|
||||
}, err
|
||||
case tEQ, tNE, tGT, tGTE, tLT, tLTE:
|
||||
right, err := p.parseExpression(bindingPowers[tokenType])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTComparator,
|
||||
value: tokenType,
|
||||
children: []ASTNode{node, right},
|
||||
}, nil
|
||||
case tLbracket:
|
||||
tokenType := p.current()
|
||||
var right ASTNode
|
||||
var err error
|
||||
if tokenType == tNumber || tokenType == tColon {
|
||||
right, err = p.parseIndexExpression()
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.projectIfSlice(node, right)
|
||||
}
|
||||
// Otherwise this is a projection.
|
||||
if err := p.match(tStar); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
right, err = p.parseProjectionRHS(bindingPowers[tStar])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{node, right},
|
||||
}, nil
|
||||
}
|
||||
return ASTNode{}, p.syntaxError("Unexpected token: " + tokenType.String())
|
||||
}
|
||||
|
||||
func (p *Parser) nud(token token) (ASTNode, error) {
|
||||
switch token.tokenType {
|
||||
case tJSONLiteral:
|
||||
var parsed interface{}
|
||||
err := json.Unmarshal([]byte(token.value), &parsed)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTLiteral, value: parsed}, nil
|
||||
case tStringLiteral:
|
||||
return ASTNode{nodeType: ASTLiteral, value: token.value}, nil
|
||||
case tUnquotedIdentifier:
|
||||
return ASTNode{
|
||||
nodeType: ASTField,
|
||||
value: token.value,
|
||||
}, nil
|
||||
case tQuotedIdentifier:
|
||||
node := ASTNode{nodeType: ASTField, value: token.value}
|
||||
if p.current() == tLparen {
|
||||
return ASTNode{}, p.syntaxErrorToken("Can't have quoted identifier as function name.", token)
|
||||
}
|
||||
return node, nil
|
||||
case tStar:
|
||||
left := ASTNode{nodeType: ASTIdentity}
|
||||
var right ASTNode
|
||||
var err error
|
||||
if p.current() == tRbracket {
|
||||
right = ASTNode{nodeType: ASTIdentity}
|
||||
} else {
|
||||
right, err = p.parseProjectionRHS(bindingPowers[tStar])
|
||||
}
|
||||
return ASTNode{nodeType: ASTValueProjection, children: []ASTNode{left, right}}, err
|
||||
case tFilter:
|
||||
return p.parseFilter(ASTNode{nodeType: ASTIdentity})
|
||||
case tLbrace:
|
||||
return p.parseMultiSelectHash()
|
||||
case tFlatten:
|
||||
left := ASTNode{
|
||||
nodeType: ASTFlatten,
|
||||
children: []ASTNode{{nodeType: ASTIdentity}},
|
||||
}
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tFlatten])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTProjection, children: []ASTNode{left, right}}, nil
|
||||
case tLbracket:
|
||||
tokenType := p.current()
|
||||
//var right ASTNode
|
||||
if tokenType == tNumber || tokenType == tColon {
|
||||
right, err := p.parseIndexExpression()
|
||||
if err != nil {
|
||||
return ASTNode{}, nil
|
||||
}
|
||||
return p.projectIfSlice(ASTNode{nodeType: ASTIdentity}, right)
|
||||
} else if tokenType == tStar && p.lookahead(1) == tRbracket {
|
||||
p.advance()
|
||||
p.advance()
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tStar])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{{nodeType: ASTIdentity}, right},
|
||||
}, nil
|
||||
} else {
|
||||
return p.parseMultiSelectList()
|
||||
}
|
||||
case tCurrent:
|
||||
return ASTNode{nodeType: ASTCurrentNode}, nil
|
||||
case tExpref:
|
||||
expression, err := p.parseExpression(bindingPowers[tExpref])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTExpRef, children: []ASTNode{expression}}, nil
|
||||
case tNot:
|
||||
expression, err := p.parseExpression(bindingPowers[tNot])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTNotExpression, children: []ASTNode{expression}}, nil
|
||||
case tLparen:
|
||||
expression, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if err := p.match(tRparen); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return expression, nil
|
||||
case tEOF:
|
||||
return ASTNode{}, p.syntaxErrorToken("Incomplete expression", token)
|
||||
}
|
||||
|
||||
return ASTNode{}, p.syntaxErrorToken("Invalid token: "+token.tokenType.String(), token)
|
||||
}
|
||||
|
||||
func (p *Parser) parseMultiSelectList() (ASTNode, error) {
|
||||
var expressions []ASTNode
|
||||
for {
|
||||
expression, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
expressions = append(expressions, expression)
|
||||
if p.current() == tRbracket {
|
||||
break
|
||||
}
|
||||
err = p.match(tComma)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
}
|
||||
err := p.match(tRbracket)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTMultiSelectList,
|
||||
children: expressions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseMultiSelectHash() (ASTNode, error) {
|
||||
var children []ASTNode
|
||||
for {
|
||||
keyToken := p.lookaheadToken(0)
|
||||
if err := p.match(tUnquotedIdentifier); err != nil {
|
||||
if err := p.match(tQuotedIdentifier); err != nil {
|
||||
return ASTNode{}, p.syntaxError("Expected tQuotedIdentifier or tUnquotedIdentifier")
|
||||
}
|
||||
}
|
||||
keyName := keyToken.value
|
||||
err := p.match(tColon)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
value, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
node := ASTNode{
|
||||
nodeType: ASTKeyValPair,
|
||||
value: keyName,
|
||||
children: []ASTNode{value},
|
||||
}
|
||||
children = append(children, node)
|
||||
if p.current() == tComma {
|
||||
err := p.match(tComma)
|
||||
if err != nil {
|
||||
return ASTNode{}, nil
|
||||
}
|
||||
} else if p.current() == tRbrace {
|
||||
err := p.match(tRbrace)
|
||||
if err != nil {
|
||||
return ASTNode{}, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTMultiSelectHash,
|
||||
children: children,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) projectIfSlice(left ASTNode, right ASTNode) (ASTNode, error) {
|
||||
indexExpr := ASTNode{
|
||||
nodeType: ASTIndexExpression,
|
||||
children: []ASTNode{left, right},
|
||||
}
|
||||
if right.nodeType == ASTSlice {
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tStar])
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{indexExpr, right},
|
||||
}, err
|
||||
}
|
||||
return indexExpr, nil
|
||||
}
|
||||
func (p *Parser) parseFilter(node ASTNode) (ASTNode, error) {
|
||||
var right, condition ASTNode
|
||||
var err error
|
||||
condition, err = p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if p.current() == tFlatten {
|
||||
right = ASTNode{nodeType: ASTIdentity}
|
||||
} else {
|
||||
right, err = p.parseProjectionRHS(bindingPowers[tFilter])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return ASTNode{
|
||||
nodeType: ASTFilterProjection,
|
||||
children: []ASTNode{node, right, condition},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseDotRHS(bindingPower int) (ASTNode, error) {
|
||||
lookahead := p.current()
|
||||
if tokensOneOf([]tokType{tQuotedIdentifier, tUnquotedIdentifier, tStar}, lookahead) {
|
||||
return p.parseExpression(bindingPower)
|
||||
} else if lookahead == tLbracket {
|
||||
if err := p.match(tLbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.parseMultiSelectList()
|
||||
} else if lookahead == tLbrace {
|
||||
if err := p.match(tLbrace); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.parseMultiSelectHash()
|
||||
}
|
||||
return ASTNode{}, p.syntaxError("Expected identifier, lbracket, or lbrace")
|
||||
}
|
||||
|
||||
func (p *Parser) parseProjectionRHS(bindingPower int) (ASTNode, error) {
|
||||
current := p.current()
|
||||
if bindingPowers[current] < 10 {
|
||||
return ASTNode{nodeType: ASTIdentity}, nil
|
||||
} else if current == tLbracket {
|
||||
return p.parseExpression(bindingPower)
|
||||
} else if current == tFilter {
|
||||
return p.parseExpression(bindingPower)
|
||||
} else if current == tDot {
|
||||
err := p.match(tDot)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.parseDotRHS(bindingPower)
|
||||
} else {
|
||||
return ASTNode{}, p.syntaxError("Error")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) lookahead(number int) tokType {
|
||||
return p.lookaheadToken(number).tokenType
|
||||
}
|
||||
|
||||
func (p *Parser) current() tokType {
|
||||
return p.lookahead(0)
|
||||
}
|
||||
|
||||
func (p *Parser) lookaheadToken(number int) token {
|
||||
return p.tokens[p.index+number]
|
||||
}
|
||||
|
||||
func (p *Parser) advance() {
|
||||
p.index++
|
||||
}
|
||||
|
||||
func tokensOneOf(elements []tokType, token tokType) bool {
|
||||
for _, elem := range elements {
|
||||
if elem == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Parser) syntaxError(msg string) SyntaxError {
|
||||
return SyntaxError{
|
||||
msg: msg,
|
||||
Expression: p.expression,
|
||||
Offset: p.lookaheadToken(0).position,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a SyntaxError based on the provided token.
|
||||
// This differs from syntaxError() which creates a SyntaxError
|
||||
// based on the current lookahead token.
|
||||
func (p *Parser) syntaxErrorToken(msg string, t token) SyntaxError {
|
||||
return SyntaxError{
|
||||
msg: msg,
|
||||
Expression: p.expression,
|
||||
Offset: t.position,
|
||||
}
|
||||
}
|
||||
16
vendor/github.com/jmespath/go-jmespath/toktype_string.go
generated
vendored
16
vendor/github.com/jmespath/go-jmespath/toktype_string.go
generated
vendored
@@ -1,16 +0,0 @@
|
||||
// generated by stringer -type=tokType; DO NOT EDIT
|
||||
|
||||
package jmespath
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _tokType_name = "tUnknowntStartDottFiltertFlattentLparentRparentLbrackettRbrackettLbracetRbracetOrtPipetNumbertUnquotedIdentifiertQuotedIdentifiertCommatColontLTtLTEtGTtGTEtEQtNEtJSONLiteraltStringLiteraltCurrenttExpreftAndtNottEOF"
|
||||
|
||||
var _tokType_index = [...]uint8{0, 8, 13, 17, 24, 32, 39, 46, 55, 64, 71, 78, 81, 86, 93, 112, 129, 135, 141, 144, 148, 151, 155, 158, 161, 173, 187, 195, 202, 206, 210, 214}
|
||||
|
||||
func (i tokType) String() string {
|
||||
if i < 0 || i >= tokType(len(_tokType_index)-1) {
|
||||
return fmt.Sprintf("tokType(%d)", i)
|
||||
}
|
||||
return _tokType_name[_tokType_index[i]:_tokType_index[i+1]]
|
||||
}
|
||||
185
vendor/github.com/jmespath/go-jmespath/util.go
generated
vendored
185
vendor/github.com/jmespath/go-jmespath/util.go
generated
vendored
@@ -1,185 +0,0 @@
|
||||
package jmespath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// IsFalse determines if an object is false based on the JMESPath spec.
|
||||
// JMESPath defines false values to be any of:
|
||||
// - An empty string array, or hash.
|
||||
// - The boolean value false.
|
||||
// - nil
|
||||
func isFalse(value interface{}) bool {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
return !v
|
||||
case []interface{}:
|
||||
return len(v) == 0
|
||||
case map[string]interface{}:
|
||||
return len(v) == 0
|
||||
case string:
|
||||
return len(v) == 0
|
||||
case nil:
|
||||
return true
|
||||
}
|
||||
// Try the reflection cases before returning false.
|
||||
rv := reflect.ValueOf(value)
|
||||
switch rv.Kind() {
|
||||
case reflect.Struct:
|
||||
// A struct type will never be false, even if
|
||||
// all of its values are the zero type.
|
||||
return false
|
||||
case reflect.Slice, reflect.Map:
|
||||
return rv.Len() == 0
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return true
|
||||
}
|
||||
// If it's a pointer type, we'll try to deref the pointer
|
||||
// and evaluate the pointer value for isFalse.
|
||||
element := rv.Elem()
|
||||
return isFalse(element.Interface())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ObjsEqual is a generic object equality check.
|
||||
// It will take two arbitrary objects and recursively determine
|
||||
// if they are equal.
|
||||
func objsEqual(left interface{}, right interface{}) bool {
|
||||
return reflect.DeepEqual(left, right)
|
||||
}
|
||||
|
||||
// SliceParam refers to a single part of a slice.
|
||||
// A slice consists of a start, a stop, and a step, similar to
|
||||
// python slices.
|
||||
type sliceParam struct {
|
||||
N int
|
||||
Specified bool
|
||||
}
|
||||
|
||||
// Slice supports [start:stop:step] style slicing that's supported in JMESPath.
|
||||
func slice(slice []interface{}, parts []sliceParam) ([]interface{}, error) {
|
||||
computed, err := computeSliceParams(len(slice), parts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start, stop, step := computed[0], computed[1], computed[2]
|
||||
result := []interface{}{}
|
||||
if step > 0 {
|
||||
for i := start; i < stop; i += step {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
} else {
|
||||
for i := start; i > stop; i += step {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func computeSliceParams(length int, parts []sliceParam) ([]int, error) {
|
||||
var start, stop, step int
|
||||
if !parts[2].Specified {
|
||||
step = 1
|
||||
} else if parts[2].N == 0 {
|
||||
return nil, errors.New("Invalid slice, step cannot be 0")
|
||||
} else {
|
||||
step = parts[2].N
|
||||
}
|
||||
var stepValueNegative bool
|
||||
if step < 0 {
|
||||
stepValueNegative = true
|
||||
} else {
|
||||
stepValueNegative = false
|
||||
}
|
||||
|
||||
if !parts[0].Specified {
|
||||
if stepValueNegative {
|
||||
start = length - 1
|
||||
} else {
|
||||
start = 0
|
||||
}
|
||||
} else {
|
||||
start = capSlice(length, parts[0].N, step)
|
||||
}
|
||||
|
||||
if !parts[1].Specified {
|
||||
if stepValueNegative {
|
||||
stop = -1
|
||||
} else {
|
||||
stop = length
|
||||
}
|
||||
} else {
|
||||
stop = capSlice(length, parts[1].N, step)
|
||||
}
|
||||
return []int{start, stop, step}, nil
|
||||
}
|
||||
|
||||
func capSlice(length int, actual int, step int) int {
|
||||
if actual < 0 {
|
||||
actual += length
|
||||
if actual < 0 {
|
||||
if step < 0 {
|
||||
actual = -1
|
||||
} else {
|
||||
actual = 0
|
||||
}
|
||||
}
|
||||
} else if actual >= length {
|
||||
if step < 0 {
|
||||
actual = length - 1
|
||||
} else {
|
||||
actual = length
|
||||
}
|
||||
}
|
||||
return actual
|
||||
}
|
||||
|
||||
// ToArrayNum converts an empty interface type to a slice of float64.
|
||||
// If any element in the array cannot be converted, then nil is returned
|
||||
// along with a second value of false.
|
||||
func toArrayNum(data interface{}) ([]float64, bool) {
|
||||
// Is there a better way to do this with reflect?
|
||||
if d, ok := data.([]interface{}); ok {
|
||||
result := make([]float64, len(d))
|
||||
for i, el := range d {
|
||||
item, ok := el.(float64)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
result[i] = item
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ToArrayStr converts an empty interface type to a slice of strings.
|
||||
// If any element in the array cannot be converted, then nil is returned
|
||||
// along with a second value of false. If the input data could be entirely
|
||||
// converted, then the converted data, along with a second value of true,
|
||||
// will be returned.
|
||||
func toArrayStr(data interface{}) ([]string, bool) {
|
||||
// Is there a better way to do this with reflect?
|
||||
if d, ok := data.([]interface{}); ok {
|
||||
result := make([]string, len(d))
|
||||
for i, el := range d {
|
||||
item, ok := el.(string)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
result[i] = item
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func isSliceType(v interface{}) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
return reflect.TypeOf(v).Kind() == reflect.Slice
|
||||
}
|
||||
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Yasuhiro Matsumoto
|
||||
|
||||
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.
|
||||
43
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
43
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
@@ -1,43 +0,0 @@
|
||||
# go-colorable
|
||||
|
||||
Colorable writer for windows.
|
||||
|
||||
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
||||
This package is possible to handle escape sequence for ansi color on windows.
|
||||
|
||||
## Too Bad!
|
||||
|
||||

|
||||
|
||||
|
||||
## So Good!
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
||||
logrus.SetOutput(colorable.NewColorableStdout())
|
||||
|
||||
logrus.Info("succeeded")
|
||||
logrus.Warn("not correct")
|
||||
logrus.Error("something error")
|
||||
logrus.Fatal("panic")
|
||||
```
|
||||
|
||||
You can compile above code on non-windows OSs.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get github.com/mattn/go-colorable
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
|
||||
# Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
||||
27
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
27
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewColorable return new instance of Writer which handle escape sequence.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
||||
820
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
820
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
@@ -1,820 +0,0 @@
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
const (
|
||||
foregroundBlue = 0x1
|
||||
foregroundGreen = 0x2
|
||||
foregroundRed = 0x4
|
||||
foregroundIntensity = 0x8
|
||||
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
|
||||
backgroundBlue = 0x10
|
||||
backgroundGreen = 0x20
|
||||
backgroundRed = 0x40
|
||||
backgroundIntensity = 0x80
|
||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||
)
|
||||
|
||||
type wchar uint16
|
||||
type short int16
|
||||
type dword uint32
|
||||
type word uint16
|
||||
|
||||
type coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
|
||||
type smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
||||
|
||||
type consoleCursorInfo struct {
|
||||
size dword
|
||||
visible int32
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
||||
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
||||
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
out io.Writer
|
||||
handle syscall.Handle
|
||||
lastbuf bytes.Buffer
|
||||
oldattr word
|
||||
oldpos coord
|
||||
}
|
||||
|
||||
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
if isatty.IsTerminal(file.Fd()) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
handle := syscall.Handle(file.Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
|
||||
} else {
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return NewColorable(os.Stdout)
|
||||
}
|
||||
|
||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return NewColorable(os.Stderr)
|
||||
}
|
||||
|
||||
var color256 = map[int]int{
|
||||
0: 0x000000,
|
||||
1: 0x800000,
|
||||
2: 0x008000,
|
||||
3: 0x808000,
|
||||
4: 0x000080,
|
||||
5: 0x800080,
|
||||
6: 0x008080,
|
||||
7: 0xc0c0c0,
|
||||
8: 0x808080,
|
||||
9: 0xff0000,
|
||||
10: 0x00ff00,
|
||||
11: 0xffff00,
|
||||
12: 0x0000ff,
|
||||
13: 0xff00ff,
|
||||
14: 0x00ffff,
|
||||
15: 0xffffff,
|
||||
16: 0x000000,
|
||||
17: 0x00005f,
|
||||
18: 0x000087,
|
||||
19: 0x0000af,
|
||||
20: 0x0000d7,
|
||||
21: 0x0000ff,
|
||||
22: 0x005f00,
|
||||
23: 0x005f5f,
|
||||
24: 0x005f87,
|
||||
25: 0x005faf,
|
||||
26: 0x005fd7,
|
||||
27: 0x005fff,
|
||||
28: 0x008700,
|
||||
29: 0x00875f,
|
||||
30: 0x008787,
|
||||
31: 0x0087af,
|
||||
32: 0x0087d7,
|
||||
33: 0x0087ff,
|
||||
34: 0x00af00,
|
||||
35: 0x00af5f,
|
||||
36: 0x00af87,
|
||||
37: 0x00afaf,
|
||||
38: 0x00afd7,
|
||||
39: 0x00afff,
|
||||
40: 0x00d700,
|
||||
41: 0x00d75f,
|
||||
42: 0x00d787,
|
||||
43: 0x00d7af,
|
||||
44: 0x00d7d7,
|
||||
45: 0x00d7ff,
|
||||
46: 0x00ff00,
|
||||
47: 0x00ff5f,
|
||||
48: 0x00ff87,
|
||||
49: 0x00ffaf,
|
||||
50: 0x00ffd7,
|
||||
51: 0x00ffff,
|
||||
52: 0x5f0000,
|
||||
53: 0x5f005f,
|
||||
54: 0x5f0087,
|
||||
55: 0x5f00af,
|
||||
56: 0x5f00d7,
|
||||
57: 0x5f00ff,
|
||||
58: 0x5f5f00,
|
||||
59: 0x5f5f5f,
|
||||
60: 0x5f5f87,
|
||||
61: 0x5f5faf,
|
||||
62: 0x5f5fd7,
|
||||
63: 0x5f5fff,
|
||||
64: 0x5f8700,
|
||||
65: 0x5f875f,
|
||||
66: 0x5f8787,
|
||||
67: 0x5f87af,
|
||||
68: 0x5f87d7,
|
||||
69: 0x5f87ff,
|
||||
70: 0x5faf00,
|
||||
71: 0x5faf5f,
|
||||
72: 0x5faf87,
|
||||
73: 0x5fafaf,
|
||||
74: 0x5fafd7,
|
||||
75: 0x5fafff,
|
||||
76: 0x5fd700,
|
||||
77: 0x5fd75f,
|
||||
78: 0x5fd787,
|
||||
79: 0x5fd7af,
|
||||
80: 0x5fd7d7,
|
||||
81: 0x5fd7ff,
|
||||
82: 0x5fff00,
|
||||
83: 0x5fff5f,
|
||||
84: 0x5fff87,
|
||||
85: 0x5fffaf,
|
||||
86: 0x5fffd7,
|
||||
87: 0x5fffff,
|
||||
88: 0x870000,
|
||||
89: 0x87005f,
|
||||
90: 0x870087,
|
||||
91: 0x8700af,
|
||||
92: 0x8700d7,
|
||||
93: 0x8700ff,
|
||||
94: 0x875f00,
|
||||
95: 0x875f5f,
|
||||
96: 0x875f87,
|
||||
97: 0x875faf,
|
||||
98: 0x875fd7,
|
||||
99: 0x875fff,
|
||||
100: 0x878700,
|
||||
101: 0x87875f,
|
||||
102: 0x878787,
|
||||
103: 0x8787af,
|
||||
104: 0x8787d7,
|
||||
105: 0x8787ff,
|
||||
106: 0x87af00,
|
||||
107: 0x87af5f,
|
||||
108: 0x87af87,
|
||||
109: 0x87afaf,
|
||||
110: 0x87afd7,
|
||||
111: 0x87afff,
|
||||
112: 0x87d700,
|
||||
113: 0x87d75f,
|
||||
114: 0x87d787,
|
||||
115: 0x87d7af,
|
||||
116: 0x87d7d7,
|
||||
117: 0x87d7ff,
|
||||
118: 0x87ff00,
|
||||
119: 0x87ff5f,
|
||||
120: 0x87ff87,
|
||||
121: 0x87ffaf,
|
||||
122: 0x87ffd7,
|
||||
123: 0x87ffff,
|
||||
124: 0xaf0000,
|
||||
125: 0xaf005f,
|
||||
126: 0xaf0087,
|
||||
127: 0xaf00af,
|
||||
128: 0xaf00d7,
|
||||
129: 0xaf00ff,
|
||||
130: 0xaf5f00,
|
||||
131: 0xaf5f5f,
|
||||
132: 0xaf5f87,
|
||||
133: 0xaf5faf,
|
||||
134: 0xaf5fd7,
|
||||
135: 0xaf5fff,
|
||||
136: 0xaf8700,
|
||||
137: 0xaf875f,
|
||||
138: 0xaf8787,
|
||||
139: 0xaf87af,
|
||||
140: 0xaf87d7,
|
||||
141: 0xaf87ff,
|
||||
142: 0xafaf00,
|
||||
143: 0xafaf5f,
|
||||
144: 0xafaf87,
|
||||
145: 0xafafaf,
|
||||
146: 0xafafd7,
|
||||
147: 0xafafff,
|
||||
148: 0xafd700,
|
||||
149: 0xafd75f,
|
||||
150: 0xafd787,
|
||||
151: 0xafd7af,
|
||||
152: 0xafd7d7,
|
||||
153: 0xafd7ff,
|
||||
154: 0xafff00,
|
||||
155: 0xafff5f,
|
||||
156: 0xafff87,
|
||||
157: 0xafffaf,
|
||||
158: 0xafffd7,
|
||||
159: 0xafffff,
|
||||
160: 0xd70000,
|
||||
161: 0xd7005f,
|
||||
162: 0xd70087,
|
||||
163: 0xd700af,
|
||||
164: 0xd700d7,
|
||||
165: 0xd700ff,
|
||||
166: 0xd75f00,
|
||||
167: 0xd75f5f,
|
||||
168: 0xd75f87,
|
||||
169: 0xd75faf,
|
||||
170: 0xd75fd7,
|
||||
171: 0xd75fff,
|
||||
172: 0xd78700,
|
||||
173: 0xd7875f,
|
||||
174: 0xd78787,
|
||||
175: 0xd787af,
|
||||
176: 0xd787d7,
|
||||
177: 0xd787ff,
|
||||
178: 0xd7af00,
|
||||
179: 0xd7af5f,
|
||||
180: 0xd7af87,
|
||||
181: 0xd7afaf,
|
||||
182: 0xd7afd7,
|
||||
183: 0xd7afff,
|
||||
184: 0xd7d700,
|
||||
185: 0xd7d75f,
|
||||
186: 0xd7d787,
|
||||
187: 0xd7d7af,
|
||||
188: 0xd7d7d7,
|
||||
189: 0xd7d7ff,
|
||||
190: 0xd7ff00,
|
||||
191: 0xd7ff5f,
|
||||
192: 0xd7ff87,
|
||||
193: 0xd7ffaf,
|
||||
194: 0xd7ffd7,
|
||||
195: 0xd7ffff,
|
||||
196: 0xff0000,
|
||||
197: 0xff005f,
|
||||
198: 0xff0087,
|
||||
199: 0xff00af,
|
||||
200: 0xff00d7,
|
||||
201: 0xff00ff,
|
||||
202: 0xff5f00,
|
||||
203: 0xff5f5f,
|
||||
204: 0xff5f87,
|
||||
205: 0xff5faf,
|
||||
206: 0xff5fd7,
|
||||
207: 0xff5fff,
|
||||
208: 0xff8700,
|
||||
209: 0xff875f,
|
||||
210: 0xff8787,
|
||||
211: 0xff87af,
|
||||
212: 0xff87d7,
|
||||
213: 0xff87ff,
|
||||
214: 0xffaf00,
|
||||
215: 0xffaf5f,
|
||||
216: 0xffaf87,
|
||||
217: 0xffafaf,
|
||||
218: 0xffafd7,
|
||||
219: 0xffafff,
|
||||
220: 0xffd700,
|
||||
221: 0xffd75f,
|
||||
222: 0xffd787,
|
||||
223: 0xffd7af,
|
||||
224: 0xffd7d7,
|
||||
225: 0xffd7ff,
|
||||
226: 0xffff00,
|
||||
227: 0xffff5f,
|
||||
228: 0xffff87,
|
||||
229: 0xffffaf,
|
||||
230: 0xffffd7,
|
||||
231: 0xffffff,
|
||||
232: 0x080808,
|
||||
233: 0x121212,
|
||||
234: 0x1c1c1c,
|
||||
235: 0x262626,
|
||||
236: 0x303030,
|
||||
237: 0x3a3a3a,
|
||||
238: 0x444444,
|
||||
239: 0x4e4e4e,
|
||||
240: 0x585858,
|
||||
241: 0x626262,
|
||||
242: 0x6c6c6c,
|
||||
243: 0x767676,
|
||||
244: 0x808080,
|
||||
245: 0x8a8a8a,
|
||||
246: 0x949494,
|
||||
247: 0x9e9e9e,
|
||||
248: 0xa8a8a8,
|
||||
249: 0xb2b2b2,
|
||||
250: 0xbcbcbc,
|
||||
251: 0xc6c6c6,
|
||||
252: 0xd0d0d0,
|
||||
253: 0xdadada,
|
||||
254: 0xe4e4e4,
|
||||
255: 0xeeeeee,
|
||||
}
|
||||
|
||||
// Write write data on console
|
||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
er := bytes.NewReader(data)
|
||||
var bw [1]byte
|
||||
loop:
|
||||
for {
|
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
if r1 == 0 {
|
||||
break loop
|
||||
}
|
||||
|
||||
c1, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
bw[0] = c1
|
||||
w.out.Write(bw[:])
|
||||
continue
|
||||
}
|
||||
c2, err := er.ReadByte()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteByte(c1)
|
||||
break loop
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
w.lastbuf.WriteByte(c1)
|
||||
w.lastbuf.WriteByte(c2)
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var m byte
|
||||
for {
|
||||
c, err := er.ReadByte()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteByte(c1)
|
||||
w.lastbuf.WriteByte(c2)
|
||||
w.lastbuf.Write(buf.Bytes())
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
m = c
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
switch m {
|
||||
case 'A':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.y -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'B':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.y += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'C':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'D':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if n, err = strconv.Atoi(buf.String()); err == nil {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
}
|
||||
case 'E':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = 0
|
||||
csbi.cursorPosition.y += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'F':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = 0
|
||||
csbi.cursorPosition.y -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'G':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = short(n - 1)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'H':
|
||||
token := strings.Split(buf.String(), ";")
|
||||
if len(token) != 2 {
|
||||
continue
|
||||
}
|
||||
n1, err := strconv.Atoi(token[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
n2, err := strconv.Atoi(token[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
csbi.cursorPosition.x = short(n2 - 1)
|
||||
csbi.cursorPosition.y = short(n1 - 1)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'J':
|
||||
n, err := strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
var cursor coord
|
||||
switch n {
|
||||
case 0:
|
||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||
case 1:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||
case 2:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||
}
|
||||
var count, written dword
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'K':
|
||||
n, err := strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
var cursor coord
|
||||
switch n {
|
||||
case 0:
|
||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||
case 1:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||
case 2:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||
}
|
||||
var count, written dword
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'm':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
attr := csbi.attributes
|
||||
cs := buf.String()
|
||||
if cs == "" {
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
||||
continue
|
||||
}
|
||||
token := strings.Split(cs, ";")
|
||||
for i := 0; i < len(token); i++ {
|
||||
ns := token[i]
|
||||
if n, err = strconv.Atoi(ns); err == nil {
|
||||
switch {
|
||||
case n == 0 || n == 100:
|
||||
attr = w.oldattr
|
||||
case 1 <= n && n <= 5:
|
||||
attr |= foregroundIntensity
|
||||
case n == 7:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 22 == n || n == 25 || n == 25:
|
||||
attr |= foregroundIntensity
|
||||
case n == 27:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 30 <= n && n <= 37:
|
||||
attr &= backgroundMask
|
||||
if (n-30)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-30)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-30)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
case n == 38: // set foreground color.
|
||||
if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
|
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||
if n256foreAttr == nil {
|
||||
n256setup()
|
||||
}
|
||||
attr &= backgroundMask
|
||||
attr |= n256foreAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & backgroundMask)
|
||||
}
|
||||
case n == 39: // reset foreground color.
|
||||
attr &= backgroundMask
|
||||
attr |= w.oldattr & foregroundMask
|
||||
case 40 <= n && n <= 47:
|
||||
attr &= foregroundMask
|
||||
if (n-40)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-40)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-40)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
case n == 48: // set background color.
|
||||
if i < len(token)-2 && token[i+1] == "5" {
|
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||
if n256backAttr == nil {
|
||||
n256setup()
|
||||
}
|
||||
attr &= foregroundMask
|
||||
attr |= n256backAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & foregroundMask)
|
||||
}
|
||||
case n == 49: // reset foreground color.
|
||||
attr &= foregroundMask
|
||||
attr |= w.oldattr & backgroundMask
|
||||
case 90 <= n && n <= 97:
|
||||
attr = (attr & backgroundMask)
|
||||
attr |= foregroundIntensity
|
||||
if (n-90)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-90)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-90)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
case 100 <= n && n <= 107:
|
||||
attr = (attr & foregroundMask)
|
||||
attr |= backgroundIntensity
|
||||
if (n-100)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-100)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-100)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
}
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||
}
|
||||
}
|
||||
case 'h':
|
||||
cs := buf.String()
|
||||
if cs == "?25" {
|
||||
var ci consoleCursorInfo
|
||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
ci.visible = 1
|
||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
}
|
||||
case 'l':
|
||||
cs := buf.String()
|
||||
if cs == "?25" {
|
||||
var ci consoleCursorInfo
|
||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
ci.visible = 0
|
||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
}
|
||||
case 's':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
w.oldpos = csbi.cursorPosition
|
||||
case 'u':
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||
}
|
||||
}
|
||||
return len(data) - w.lastbuf.Len(), nil
|
||||
}
|
||||
|
||||
type consoleColor struct {
|
||||
rgb int
|
||||
red bool
|
||||
green bool
|
||||
blue bool
|
||||
intensity bool
|
||||
}
|
||||
|
||||
func (c consoleColor) foregroundAttr() (attr word) {
|
||||
if c.red {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if c.green {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if c.blue {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
if c.intensity {
|
||||
attr |= foregroundIntensity
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c consoleColor) backgroundAttr() (attr word) {
|
||||
if c.red {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if c.green {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if c.blue {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
if c.intensity {
|
||||
attr |= backgroundIntensity
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var color16 = []consoleColor{
|
||||
consoleColor{0x000000, false, false, false, false},
|
||||
consoleColor{0x000080, false, false, true, false},
|
||||
consoleColor{0x008000, false, true, false, false},
|
||||
consoleColor{0x008080, false, true, true, false},
|
||||
consoleColor{0x800000, true, false, false, false},
|
||||
consoleColor{0x800080, true, false, true, false},
|
||||
consoleColor{0x808000, true, true, false, false},
|
||||
consoleColor{0xc0c0c0, true, true, true, false},
|
||||
consoleColor{0x808080, false, false, false, true},
|
||||
consoleColor{0x0000ff, false, false, true, true},
|
||||
consoleColor{0x00ff00, false, true, false, true},
|
||||
consoleColor{0x00ffff, false, true, true, true},
|
||||
consoleColor{0xff0000, true, false, false, true},
|
||||
consoleColor{0xff00ff, true, false, true, true},
|
||||
consoleColor{0xffff00, true, true, false, true},
|
||||
consoleColor{0xffffff, true, true, true, true},
|
||||
}
|
||||
|
||||
type hsv struct {
|
||||
h, s, v float32
|
||||
}
|
||||
|
||||
func (a hsv) dist(b hsv) float32 {
|
||||
dh := a.h - b.h
|
||||
switch {
|
||||
case dh > 0.5:
|
||||
dh = 1 - dh
|
||||
case dh < -0.5:
|
||||
dh = -1 - dh
|
||||
}
|
||||
ds := a.s - b.s
|
||||
dv := a.v - b.v
|
||||
return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
|
||||
}
|
||||
|
||||
func toHSV(rgb int) hsv {
|
||||
r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
|
||||
float32((rgb&0x00FF00)>>8)/256.0,
|
||||
float32(rgb&0x0000FF)/256.0
|
||||
min, max := minmax3f(r, g, b)
|
||||
h := max - min
|
||||
if h > 0 {
|
||||
if max == r {
|
||||
h = (g - b) / h
|
||||
if h < 0 {
|
||||
h += 6
|
||||
}
|
||||
} else if max == g {
|
||||
h = 2 + (b-r)/h
|
||||
} else {
|
||||
h = 4 + (r-g)/h
|
||||
}
|
||||
}
|
||||
h /= 6.0
|
||||
s := max - min
|
||||
if max != 0 {
|
||||
s /= max
|
||||
}
|
||||
v := max
|
||||
return hsv{h: h, s: s, v: v}
|
||||
}
|
||||
|
||||
type hsvTable []hsv
|
||||
|
||||
func toHSVTable(rgbTable []consoleColor) hsvTable {
|
||||
t := make(hsvTable, len(rgbTable))
|
||||
for i, c := range rgbTable {
|
||||
t[i] = toHSV(c.rgb)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t hsvTable) find(rgb int) consoleColor {
|
||||
hsv := toHSV(rgb)
|
||||
n := 7
|
||||
l := float32(5.0)
|
||||
for i, p := range t {
|
||||
d := hsv.dist(p)
|
||||
if d < l {
|
||||
l, n = d, i
|
||||
}
|
||||
}
|
||||
return color16[n]
|
||||
}
|
||||
|
||||
func minmax3f(a, b, c float32) (min, max float32) {
|
||||
if a < b {
|
||||
if b < c {
|
||||
return a, c
|
||||
} else if a < c {
|
||||
return a, b
|
||||
} else {
|
||||
return c, b
|
||||
}
|
||||
} else {
|
||||
if a < c {
|
||||
return b, c
|
||||
} else if b < c {
|
||||
return b, a
|
||||
} else {
|
||||
return c, a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var n256foreAttr []word
|
||||
var n256backAttr []word
|
||||
|
||||
func n256setup() {
|
||||
n256foreAttr = make([]word, 256)
|
||||
n256backAttr = make([]word, 256)
|
||||
t := toHSVTable(color16)
|
||||
for i, rgb := range color256 {
|
||||
c := t.find(rgb)
|
||||
n256foreAttr[i] = c.foregroundAttr()
|
||||
n256backAttr[i] = c.backgroundAttr()
|
||||
}
|
||||
}
|
||||
61
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
61
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
@@ -1,61 +0,0 @@
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// NonColorable hold writer but remove escape sequence.
|
||||
type NonColorable struct {
|
||||
out io.Writer
|
||||
lastbuf bytes.Buffer
|
||||
}
|
||||
|
||||
// NewNonColorable return new instance of Writer which remove escape sequence from Writer.
|
||||
func NewNonColorable(w io.Writer) io.Writer {
|
||||
return &NonColorable{out: w}
|
||||
}
|
||||
|
||||
// Write write data on console
|
||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||
er := bytes.NewReader(data)
|
||||
var bw [1]byte
|
||||
loop:
|
||||
for {
|
||||
c1, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
bw[0] = c1
|
||||
w.out.Write(bw[:])
|
||||
continue
|
||||
}
|
||||
c2, err := er.ReadByte()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteByte(c1)
|
||||
break loop
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
w.lastbuf.WriteByte(c1)
|
||||
w.lastbuf.WriteByte(c2)
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for {
|
||||
c, err := er.ReadByte()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteByte(c1)
|
||||
w.lastbuf.WriteByte(c2)
|
||||
w.lastbuf.Write(buf.Bytes())
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
}
|
||||
return len(data) - w.lastbuf.Len(), nil
|
||||
}
|
||||
174
vendor/github.com/spf13/afero/LICENSE.txt
generated
vendored
174
vendor/github.com/spf13/afero/LICENSE.txt
generated
vendored
@@ -1,174 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
449
vendor/github.com/spf13/afero/README.md
generated
vendored
449
vendor/github.com/spf13/afero/README.md
generated
vendored
@@ -1,449 +0,0 @@
|
||||

|
||||
|
||||
A FileSystem Abstraction System for Go
|
||||
|
||||
[](https://travis-ci.org/spf13/afero) [](https://ci.appveyor.com/project/spf13/afero) [](https://godoc.org/github.com/spf13/afero) [](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
# Overview
|
||||
|
||||
Afero is an filesystem framework providing a simple, uniform and universal API
|
||||
interacting with any filesystem, as an abstraction layer providing interfaces,
|
||||
types and methods. Afero has an exceptionally clean interface and simple design
|
||||
without needless constructors or initialization methods.
|
||||
|
||||
Afero is also a library providing a base set of interoperable backend
|
||||
filesystems that make it easy to work with afero while retaining all the power
|
||||
and benefit of the os and ioutil packages.
|
||||
|
||||
Afero provides significant improvements over using the os package alone, most
|
||||
notably the ability to create mock and testing filesystems without relying on the disk.
|
||||
|
||||
It is suitable for use in a any situation where you would consider using the OS
|
||||
package as it provides an additional abstraction that makes it easy to use a
|
||||
memory backed file system during testing. It also adds support for the http
|
||||
filesystem for full interoperability.
|
||||
|
||||
|
||||
## Afero Features
|
||||
|
||||
* A single consistent API for accessing a variety of filesystems
|
||||
* Interoperation between a variety of file system types
|
||||
* A set of interfaces to encourage and enforce interoperability between backends
|
||||
* An atomic cross platform memory backed file system
|
||||
* Support for compositional (union) file systems by combining multiple file systems acting as one
|
||||
* Specialized backends which modify existing filesystems (Read Only, Regexp filtered)
|
||||
* A set of utility functions ported from io, ioutil & hugo to be afero aware
|
||||
|
||||
|
||||
# Using Afero
|
||||
|
||||
Afero is easy to use and easier to adopt.
|
||||
|
||||
A few different ways you could use Afero:
|
||||
|
||||
* Use the interfaces alone to define you own file system.
|
||||
* Wrap for the OS packages.
|
||||
* Define different filesystems for different parts of your application.
|
||||
* Use Afero for mock filesystems while testing
|
||||
|
||||
## Step 1: Install Afero
|
||||
|
||||
First use go get to install the latest version of the library.
|
||||
|
||||
$ go get github.com/spf13/afero
|
||||
|
||||
Next include Afero in your application.
|
||||
```go
|
||||
import "github.com/spf13/afero"
|
||||
```
|
||||
|
||||
## Step 2: Declare a backend
|
||||
|
||||
First define a package variable and set it to a pointer to a filesystem.
|
||||
```go
|
||||
var AppFs afero.Fs = afero.NewMemMapFs()
|
||||
|
||||
or
|
||||
|
||||
var AppFs afero.Fs = afero.NewOsFs()
|
||||
```
|
||||
It is important to note that if you repeat the composite literal you
|
||||
will be using a completely new and isolated filesystem. In the case of
|
||||
OsFs it will still use the same underlying filesystem but will reduce
|
||||
the ability to drop in other filesystems as desired.
|
||||
|
||||
## Step 3: Use it like you would the OS package
|
||||
|
||||
Throughout your application use any function and method like you normally
|
||||
would.
|
||||
|
||||
So if my application before had:
|
||||
```go
|
||||
os.Open('/tmp/foo')
|
||||
```
|
||||
We would replace it with a call to `AppFs.Open('/tmp/foo')`.
|
||||
|
||||
`AppFs` being the variable we defined above.
|
||||
|
||||
|
||||
## List of all available functions
|
||||
|
||||
File System Methods Available:
|
||||
```go
|
||||
Chmod(name string, mode os.FileMode) : error
|
||||
Chtimes(name string, atime time.Time, mtime time.Time) : error
|
||||
Create(name string) : File, error
|
||||
Mkdir(name string, perm os.FileMode) : error
|
||||
MkdirAll(path string, perm os.FileMode) : error
|
||||
Name() : string
|
||||
Open(name string) : File, error
|
||||
OpenFile(name string, flag int, perm os.FileMode) : File, error
|
||||
Remove(name string) : error
|
||||
RemoveAll(path string) : error
|
||||
Rename(oldname, newname string) : error
|
||||
Stat(name string) : os.FileInfo, error
|
||||
```
|
||||
File Interfaces and Methods Available:
|
||||
```go
|
||||
io.Closer
|
||||
io.Reader
|
||||
io.ReaderAt
|
||||
io.Seeker
|
||||
io.Writer
|
||||
io.WriterAt
|
||||
|
||||
Name() : string
|
||||
Readdir(count int) : []os.FileInfo, error
|
||||
Readdirnames(n int) : []string, error
|
||||
Stat() : os.FileInfo, error
|
||||
Sync() : error
|
||||
Truncate(size int64) : error
|
||||
WriteString(s string) : ret int, err error
|
||||
```
|
||||
In some applications it may make sense to define a new package that
|
||||
simply exports the file system variable for easy access from anywhere.
|
||||
|
||||
## Using Afero's utility functions
|
||||
|
||||
Afero provides a set of functions to make it easier to use the underlying file systems.
|
||||
These functions have been primarily ported from io & ioutil with some developed for Hugo.
|
||||
|
||||
The afero utilities support all afero compatible backends.
|
||||
|
||||
The list of utilities includes:
|
||||
|
||||
```go
|
||||
DirExists(path string) (bool, error)
|
||||
Exists(path string) (bool, error)
|
||||
FileContainsBytes(filename string, subslice []byte) (bool, error)
|
||||
GetTempDir(subPath string) string
|
||||
IsDir(path string) (bool, error)
|
||||
IsEmpty(path string) (bool, error)
|
||||
ReadDir(dirname string) ([]os.FileInfo, error)
|
||||
ReadFile(filename string) ([]byte, error)
|
||||
SafeWriteReader(path string, r io.Reader) (err error)
|
||||
TempDir(dir, prefix string) (name string, err error)
|
||||
TempFile(dir, prefix string) (f File, err error)
|
||||
Walk(root string, walkFn filepath.WalkFunc) error
|
||||
WriteFile(filename string, data []byte, perm os.FileMode) error
|
||||
WriteReader(path string, r io.Reader) (err error)
|
||||
```
|
||||
For a complete list see [Afero's GoDoc](https://godoc.org/github.com/spf13/afero)
|
||||
|
||||
They are available under two different approaches to use. You can either call
|
||||
them directly where the first parameter of each function will be the file
|
||||
system, or you can declare a new `Afero`, a custom type used to bind these
|
||||
functions as methods to a given filesystem.
|
||||
|
||||
### Calling utilities directly
|
||||
|
||||
```go
|
||||
fs := new(afero.MemMapFs)
|
||||
f, err := afero.TempFile(fs,"", "ioutil-test")
|
||||
|
||||
```
|
||||
|
||||
### Calling via Afero
|
||||
|
||||
```go
|
||||
fs := afero.NewMemMapFs
|
||||
afs := &Afero{Fs: fs}
|
||||
f, err := afs.TempFile("", "ioutil-test")
|
||||
```
|
||||
|
||||
## Using Afero for Testing
|
||||
|
||||
There is a large benefit to using a mock filesystem for testing. It has a
|
||||
completely blank state every time it is initialized and can be easily
|
||||
reproducible regardless of OS. You could create files to your heart’s content
|
||||
and the file access would be fast while also saving you from all the annoying
|
||||
issues with deleting temporary files, Windows file locking, etc. The MemMapFs
|
||||
backend is perfect for testing.
|
||||
|
||||
* Much faster than performing I/O operations on disk
|
||||
* Avoid security issues and permissions
|
||||
* Far more control. 'rm -rf /' with confidence
|
||||
* Test setup is far more easier to do
|
||||
* No test cleanup needed
|
||||
|
||||
One way to accomplish this is to define a variable as mentioned above.
|
||||
In your application this will be set to afero.NewOsFs() during testing you
|
||||
can set it to afero.NewMemMapFs().
|
||||
|
||||
It wouldn't be uncommon to have each test initialize a blank slate memory
|
||||
backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere
|
||||
appropriate in my application code. This approach ensures that Tests are order
|
||||
independent, with no test relying on the state left by an earlier test.
|
||||
|
||||
Then in my tests I would initialize a new MemMapFs for each test:
|
||||
```go
|
||||
func TestExist(t *testing.T) {
|
||||
appFS := afero.NewMemMapFs()
|
||||
// create test files and directories
|
||||
appFS.MkdirAll("src/a", 0755)
|
||||
afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644)
|
||||
afero.WriteFile(appFS, "src/c", []byte("file c"), 0644)
|
||||
name := "src/c"
|
||||
_, err := appFS.Stat(name)
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("file \"%s\" does not exist.\n", name)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Available Backends
|
||||
|
||||
## Operating System Native
|
||||
|
||||
### OsFs
|
||||
|
||||
The first is simply a wrapper around the native OS calls. This makes it
|
||||
very easy to use as all of the calls are the same as the existing OS
|
||||
calls. It also makes it trivial to have your code use the OS during
|
||||
operation and a mock filesystem during testing or as needed.
|
||||
|
||||
```go
|
||||
appfs := afero.NewOsFs()
|
||||
appfs.MkdirAll("src/a", 0755))
|
||||
```
|
||||
|
||||
## Memory Backed Storage
|
||||
|
||||
### MemMapFs
|
||||
|
||||
Afero also provides a fully atomic memory backed filesystem perfect for use in
|
||||
mocking and to speed up unnecessary disk io when persistence isn’t
|
||||
necessary. It is fully concurrent and will work within go routines
|
||||
safely.
|
||||
|
||||
```go
|
||||
mm := afero.NewMemMapFs()
|
||||
mm.MkdirAll("src/a", 0755))
|
||||
```
|
||||
|
||||
#### InMemoryFile
|
||||
|
||||
As part of MemMapFs, Afero also provides an atomic, fully concurrent memory
|
||||
backed file implementation. This can be used in other memory backed file
|
||||
systems with ease. Plans are to add a radix tree memory stored file
|
||||
system using InMemoryFile.
|
||||
|
||||
## Network Interfaces
|
||||
|
||||
### SftpFs
|
||||
|
||||
Afero has experimental support for secure file transfer protocol (sftp). Which can
|
||||
be used to perform file operations over a encrypted channel.
|
||||
|
||||
## Filtering Backends
|
||||
|
||||
### BasePathFs
|
||||
|
||||
The BasePathFs restricts all operations to a given path within an Fs.
|
||||
The given file name to the operations on this Fs will be prepended with
|
||||
the base path before calling the source Fs.
|
||||
|
||||
```go
|
||||
bp := afero.NewBasePathFs(afero.NewOsFs(), "/base/path")
|
||||
```
|
||||
|
||||
### ReadOnlyFs
|
||||
|
||||
A thin wrapper around the source Fs providing a read only view.
|
||||
|
||||
```go
|
||||
fs := afero.NewReadOnlyFs(afero.NewOsFs())
|
||||
_, err := fs.Create("/file.txt")
|
||||
// err = syscall.EPERM
|
||||
```
|
||||
|
||||
# RegexpFs
|
||||
|
||||
A filtered view on file names, any file NOT matching
|
||||
the passed regexp will be treated as non-existing.
|
||||
Files not matching the regexp provided will not be created.
|
||||
Directories are not filtered.
|
||||
|
||||
```go
|
||||
fs := afero.NewRegexpFs(afero.NewMemMapFs(), regexp.MustCompile(`\.txt$`))
|
||||
_, err := fs.Create("/file.html")
|
||||
// err = syscall.ENOENT
|
||||
```
|
||||
|
||||
### HttpFs
|
||||
|
||||
Afero provides an http compatible backend which can wrap any of the existing
|
||||
backends.
|
||||
|
||||
The Http package requires a slightly specific version of Open which
|
||||
returns an http.File type.
|
||||
|
||||
Afero provides an httpFs file system which satisfies this requirement.
|
||||
Any Afero FileSystem can be used as an httpFs.
|
||||
|
||||
```go
|
||||
httpFs := afero.NewHttpFs(<ExistingFS>)
|
||||
fileserver := http.FileServer(httpFs.Dir(<PATH>)))
|
||||
http.Handle("/", fileserver)
|
||||
```
|
||||
|
||||
## Composite Backends
|
||||
|
||||
Afero provides the ability have two filesystems (or more) act as a single
|
||||
file system.
|
||||
|
||||
### CacheOnReadFs
|
||||
|
||||
The CacheOnReadFs will lazily make copies of any accessed files from the base
|
||||
layer into the overlay. Subsequent reads will be pulled from the overlay
|
||||
directly permitting the request is within the cache duration of when it was
|
||||
created in the overlay.
|
||||
|
||||
If the base filesystem is writeable, any changes to files will be
|
||||
done first to the base, then to the overlay layer. Write calls to open file
|
||||
handles like `Write()` or `Truncate()` to the overlay first.
|
||||
|
||||
To writing files to the overlay only, you can use the overlay Fs directly (not
|
||||
via the union Fs).
|
||||
|
||||
Cache files in the layer for the given time.Duration, a cache duration of 0
|
||||
means "forever" meaning the file will not be re-requested from the base ever.
|
||||
|
||||
A read-only base will make the overlay also read-only but still copy files
|
||||
from the base to the overlay when they're not present (or outdated) in the
|
||||
caching layer.
|
||||
|
||||
```go
|
||||
base := afero.NewOsFs()
|
||||
layer := afero.NewMemMapFs()
|
||||
ufs := afero.NewCacheOnReadFs(base, layer, 100 * time.Second)
|
||||
```
|
||||
|
||||
### CopyOnWriteFs()
|
||||
|
||||
The CopyOnWriteFs is a read only base file system with a potentially
|
||||
writeable layer on top.
|
||||
|
||||
Read operations will first look in the overlay and if not found there, will
|
||||
serve the file from the base.
|
||||
|
||||
Changes to the file system will only be made in the overlay.
|
||||
|
||||
Any attempt to modify a file found only in the base will copy the file to the
|
||||
overlay layer before modification (including opening a file with a writable
|
||||
handle).
|
||||
|
||||
Removing and Renaming files present only in the base layer is not currently
|
||||
permitted. If a file is present in the base layer and the overlay, only the
|
||||
overlay will be removed/renamed.
|
||||
|
||||
```go
|
||||
base := afero.NewOsFs()
|
||||
roBase := afero.NewReadOnlyFs(base)
|
||||
ufs := afero.NewCopyOnWriteFs(roBase, afero.NewMemMapFs())
|
||||
|
||||
fh, _ = ufs.Create("/home/test/file2.txt")
|
||||
fh.WriteString("This is a test")
|
||||
fh.Close()
|
||||
```
|
||||
|
||||
In this example all write operations will only occur in memory (MemMapFs)
|
||||
leaving the base filesystem (OsFs) untouched.
|
||||
|
||||
|
||||
## Desired/possible backends
|
||||
|
||||
The following is a short list of possible backends we hope someone will
|
||||
implement:
|
||||
|
||||
* SSH
|
||||
* ZIP
|
||||
* TAR
|
||||
* S3
|
||||
|
||||
# About the project
|
||||
|
||||
## What's in the name
|
||||
|
||||
Afero comes from the latin roots Ad-Facere.
|
||||
|
||||
**"Ad"** is a prefix meaning "to".
|
||||
|
||||
**"Facere"** is a form of the root "faciō" making "make or do".
|
||||
|
||||
The literal meaning of afero is "to make" or "to do" which seems very fitting
|
||||
for a library that allows one to make files and directories and do things with them.
|
||||
|
||||
The English word that shares the same roots as Afero is "affair". Affair shares
|
||||
the same concept but as a noun it means "something that is made or done" or "an
|
||||
object of a particular type".
|
||||
|
||||
It's also nice that unlike some of my other libraries (hugo, cobra, viper) it
|
||||
Googles very well.
|
||||
|
||||
## Release Notes
|
||||
|
||||
* **0.10.0** 2015.12.10
|
||||
* Full compatibility with Windows
|
||||
* Introduction of afero utilities
|
||||
* Test suite rewritten to work cross platform
|
||||
* Normalize paths for MemMapFs
|
||||
* Adding Sync to the file interface
|
||||
* **Breaking Change** Walk and ReadDir have changed parameter order
|
||||
* Moving types used by MemMapFs to a subpackage
|
||||
* General bugfixes and improvements
|
||||
* **0.9.0** 2015.11.05
|
||||
* New Walk function similar to filepath.Walk
|
||||
* MemMapFs.OpenFile handles O_CREATE, O_APPEND, O_TRUNC
|
||||
* MemMapFs.Remove now really deletes the file
|
||||
* InMemoryFile.Readdir and Readdirnames work correctly
|
||||
* InMemoryFile functions lock it for concurrent access
|
||||
* Test suite improvements
|
||||
* **0.8.0** 2014.10.28
|
||||
* First public version
|
||||
* Interfaces feel ready for people to build using
|
||||
* Interfaces satisfy all known uses
|
||||
* MemMapFs passes the majority of the OS test suite
|
||||
* OsFs passes the majority of the OS test suite
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
|
||||
## Contributors
|
||||
|
||||
Names in no particular order:
|
||||
|
||||
* [spf13](https://github.com/spf13)
|
||||
* [jaqx0r](https://github.com/jaqx0r)
|
||||
* [mbertschler](https://github.com/mbertschler)
|
||||
* [xor-gate](https://github.com/xor-gate)
|
||||
|
||||
## License
|
||||
|
||||
Afero is released under the Apache 2.0 license. See
|
||||
[LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt)
|
||||
108
vendor/github.com/spf13/afero/afero.go
generated
vendored
108
vendor/github.com/spf13/afero/afero.go
generated
vendored
@@ -1,108 +0,0 @@
|
||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||
// Copyright 2013 tsuru authors. All rights reserved.
|
||||
//
|
||||
// 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 afero provides types and methods for interacting with the filesystem,
|
||||
// as an abstraction layer.
|
||||
|
||||
// Afero also provides a few implementations that are mostly interoperable. One that
|
||||
// uses the operating system filesystem, one that uses memory to store files
|
||||
// (cross platform) and an interface that should be implemented if you want to
|
||||
// provide your own filesystem.
|
||||
|
||||
package afero
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Afero struct {
|
||||
Fs
|
||||
}
|
||||
|
||||
// File represents a file in the filesystem.
|
||||
type File interface {
|
||||
io.Closer
|
||||
io.Reader
|
||||
io.ReaderAt
|
||||
io.Seeker
|
||||
io.Writer
|
||||
io.WriterAt
|
||||
|
||||
Name() string
|
||||
Readdir(count int) ([]os.FileInfo, error)
|
||||
Readdirnames(n int) ([]string, error)
|
||||
Stat() (os.FileInfo, error)
|
||||
Sync() error
|
||||
Truncate(size int64) error
|
||||
WriteString(s string) (ret int, err error)
|
||||
}
|
||||
|
||||
// Fs is the filesystem interface.
|
||||
//
|
||||
// Any simulated or real filesystem should implement this interface.
|
||||
type Fs interface {
|
||||
// Create creates a file in the filesystem, returning the file and an
|
||||
// error, if any happens.
|
||||
Create(name string) (File, error)
|
||||
|
||||
// Mkdir creates a directory in the filesystem, return an error if any
|
||||
// happens.
|
||||
Mkdir(name string, perm os.FileMode) error
|
||||
|
||||
// MkdirAll creates a directory path and all parents that does not exist
|
||||
// yet.
|
||||
MkdirAll(path string, perm os.FileMode) error
|
||||
|
||||
// Open opens a file, returning it or an error, if any happens.
|
||||
Open(name string) (File, error)
|
||||
|
||||
// OpenFile opens a file using the given flags and the given mode.
|
||||
OpenFile(name string, flag int, perm os.FileMode) (File, error)
|
||||
|
||||
// Remove removes a file identified by name, returning an error, if any
|
||||
// happens.
|
||||
Remove(name string) error
|
||||
|
||||
// RemoveAll removes a directory path and any children it contains. It
|
||||
// does not fail if the path does not exist (return nil).
|
||||
RemoveAll(path string) error
|
||||
|
||||
// Rename renames a file.
|
||||
Rename(oldname, newname string) error
|
||||
|
||||
// Stat returns a FileInfo describing the named file, or an error, if any
|
||||
// happens.
|
||||
Stat(name string) (os.FileInfo, error)
|
||||
|
||||
// The name of this FileSystem
|
||||
Name() string
|
||||
|
||||
//Chmod changes the mode of the named file to mode.
|
||||
Chmod(name string, mode os.FileMode) error
|
||||
|
||||
//Chtimes changes the access and modification times of the named file
|
||||
Chtimes(name string, atime time.Time, mtime time.Time) error
|
||||
}
|
||||
|
||||
var (
|
||||
ErrFileClosed = errors.New("File is closed")
|
||||
ErrOutOfRange = errors.New("Out of range")
|
||||
ErrTooLarge = errors.New("Too large")
|
||||
ErrFileNotFound = os.ErrNotExist
|
||||
ErrFileExists = os.ErrExist
|
||||
ErrDestinationExists = os.ErrExist
|
||||
)
|
||||
145
vendor/github.com/spf13/afero/basepath.go
generated
vendored
145
vendor/github.com/spf13/afero/basepath.go
generated
vendored
@@ -1,145 +0,0 @@
|
||||
package afero
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The BasePathFs restricts all operations to a given path within an Fs.
|
||||
// The given file name to the operations on this Fs will be prepended with
|
||||
// the base path before calling the base Fs.
|
||||
// Any file name (after filepath.Clean()) outside this base path will be
|
||||
// treated as non existing file.
|
||||
//
|
||||
// Note that it does not clean the error messages on return, so you may
|
||||
// reveal the real path on errors.
|
||||
type BasePathFs struct {
|
||||
source Fs
|
||||
path string
|
||||
}
|
||||
|
||||
func NewBasePathFs(source Fs, path string) Fs {
|
||||
return &BasePathFs{source: source, path: path}
|
||||
}
|
||||
|
||||
// on a file outside the base path it returns the given file name and an error,
|
||||
// else the given file with the base path prepended
|
||||
func (b *BasePathFs) RealPath(name string) (path string, err error) {
|
||||
if err := validateBasePathName(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bpath := filepath.Clean(b.path)
|
||||
path = filepath.Clean(filepath.Join(bpath, name))
|
||||
if !strings.HasPrefix(path, bpath) {
|
||||
return name, os.ErrNotExist
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func validateBasePathName(name string) error {
|
||||
if runtime.GOOS != "windows" {
|
||||
// Not much to do here;
|
||||
// the virtual file paths all look absolute on *nix.
|
||||
return nil
|
||||
}
|
||||
|
||||
// On Windows a common mistake would be to provide an absolute OS path
|
||||
// We could strip out the base part, but that would not be very portable.
|
||||
if filepath.IsAbs(name) {
|
||||
return &os.PathError{Op: "realPath", Path: name, Err: errors.New("got a real OS path instead of a virtual")}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return &os.PathError{Op: "chtimes", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return &os.PathError{Op: "chmod", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Name() string {
|
||||
return "BasePathFs"
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return nil, &os.PathError{Op: "stat", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Stat(name)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Rename(oldname, newname string) (err error) {
|
||||
if oldname, err = b.RealPath(oldname); err != nil {
|
||||
return &os.PathError{Op: "rename", Path: oldname, Err: err}
|
||||
}
|
||||
if newname, err = b.RealPath(newname); err != nil {
|
||||
return &os.PathError{Op: "rename", Path: newname, Err: err}
|
||||
}
|
||||
return b.source.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) RemoveAll(name string) (err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return &os.PathError{Op: "remove_all", Path: name, Err: err}
|
||||
}
|
||||
return b.source.RemoveAll(name)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Remove(name string) (err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return &os.PathError{Op: "remove", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Remove(name)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
|
||||
}
|
||||
return b.source.OpenFile(name, flag, mode)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Open(name string) (f File, err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Open(name)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Mkdir(name, mode)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: err}
|
||||
}
|
||||
return b.source.MkdirAll(name, mode)
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Create(name string) (f File, err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return nil, &os.PathError{Op: "create", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Create(name)
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4 noexpandtab nolist syn=go
|
||||
295
vendor/github.com/spf13/afero/cacheOnReadFs.go
generated
vendored
295
vendor/github.com/spf13/afero/cacheOnReadFs.go
generated
vendored
@@ -1,295 +0,0 @@
|
||||
package afero
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// If the cache duration is 0, cache time will be unlimited, i.e. once
|
||||
// a file is in the layer, the base will never be read again for this file.
|
||||
//
|
||||
// For cache times greater than 0, the modification time of a file is
|
||||
// checked. Note that a lot of file system implementations only allow a
|
||||
// resolution of a second for timestamps... or as the godoc for os.Chtimes()
|
||||
// states: "The underlying filesystem may truncate or round the values to a
|
||||
// less precise time unit."
|
||||
//
|
||||
// This caching union will forward all write calls also to the base file
|
||||
// system first. To prevent writing to the base Fs, wrap it in a read-only
|
||||
// filter - Note: this will also make the overlay read-only, for writing files
|
||||
// in the overlay, use the overlay Fs directly, not via the union Fs.
|
||||
type CacheOnReadFs struct {
|
||||
base Fs
|
||||
layer Fs
|
||||
cacheTime time.Duration
|
||||
}
|
||||
|
||||
func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
|
||||
return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
|
||||
}
|
||||
|
||||
type cacheState int
|
||||
|
||||
const (
|
||||
// not present in the overlay, unknown if it exists in the base:
|
||||
cacheMiss cacheState = iota
|
||||
// present in the overlay and in base, base file is newer:
|
||||
cacheStale
|
||||
// present in the overlay - with cache time == 0 it may exist in the base,
|
||||
// with cacheTime > 0 it exists in the base and is same age or newer in the
|
||||
// overlay
|
||||
cacheHit
|
||||
// happens if someone writes directly to the overlay without
|
||||
// going through this union
|
||||
cacheLocal
|
||||
)
|
||||
|
||||
func (u *CacheOnReadFs) cacheStatus(name string) (state cacheState, fi os.FileInfo, err error) {
|
||||
var lfi, bfi os.FileInfo
|
||||
lfi, err = u.layer.Stat(name)
|
||||
if err == nil {
|
||||
if u.cacheTime == 0 {
|
||||
return cacheHit, lfi, nil
|
||||
}
|
||||
if lfi.ModTime().Add(u.cacheTime).Before(time.Now()) {
|
||||
bfi, err = u.base.Stat(name)
|
||||
if err != nil {
|
||||
return cacheLocal, lfi, nil
|
||||
}
|
||||
if bfi.ModTime().After(lfi.ModTime()) {
|
||||
return cacheStale, bfi, nil
|
||||
}
|
||||
}
|
||||
return cacheHit, lfi, nil
|
||||
}
|
||||
|
||||
if err == syscall.ENOENT {
|
||||
return cacheMiss, nil, nil
|
||||
}
|
||||
var ok bool
|
||||
if err, ok = err.(*os.PathError); ok {
|
||||
if err == os.ErrNotExist {
|
||||
return cacheMiss, nil, nil
|
||||
}
|
||||
}
|
||||
return cacheMiss, nil, err
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) copyToLayer(name string) error {
|
||||
return copyToLayer(u.base, u.layer, name)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Chtimes(name string, atime, mtime time.Time) error {
|
||||
st, _, err := u.cacheStatus(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch st {
|
||||
case cacheLocal:
|
||||
case cacheHit:
|
||||
err = u.base.Chtimes(name, atime, mtime)
|
||||
case cacheStale, cacheMiss:
|
||||
if err := u.copyToLayer(name); err != nil {
|
||||
return err
|
||||
}
|
||||
err = u.base.Chtimes(name, atime, mtime)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.layer.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Chmod(name string, mode os.FileMode) error {
|
||||
st, _, err := u.cacheStatus(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch st {
|
||||
case cacheLocal:
|
||||
case cacheHit:
|
||||
err = u.base.Chmod(name, mode)
|
||||
case cacheStale, cacheMiss:
|
||||
if err := u.copyToLayer(name); err != nil {
|
||||
return err
|
||||
}
|
||||
err = u.base.Chmod(name, mode)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.layer.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Stat(name string) (os.FileInfo, error) {
|
||||
st, fi, err := u.cacheStatus(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch st {
|
||||
case cacheMiss:
|
||||
return u.base.Stat(name)
|
||||
default: // cacheStale has base, cacheHit and cacheLocal the layer os.FileInfo
|
||||
return fi, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Rename(oldname, newname string) error {
|
||||
st, _, err := u.cacheStatus(oldname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch st {
|
||||
case cacheLocal:
|
||||
case cacheHit:
|
||||
err = u.base.Rename(oldname, newname)
|
||||
case cacheStale, cacheMiss:
|
||||
if err := u.copyToLayer(oldname); err != nil {
|
||||
return err
|
||||
}
|
||||
err = u.base.Rename(oldname, newname)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.layer.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Remove(name string) error {
|
||||
st, _, err := u.cacheStatus(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch st {
|
||||
case cacheLocal:
|
||||
case cacheHit, cacheStale, cacheMiss:
|
||||
err = u.base.Remove(name)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.layer.Remove(name)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) RemoveAll(name string) error {
|
||||
st, _, err := u.cacheStatus(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch st {
|
||||
case cacheLocal:
|
||||
case cacheHit, cacheStale, cacheMiss:
|
||||
err = u.base.RemoveAll(name)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.layer.RemoveAll(name)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
st, _, err := u.cacheStatus(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch st {
|
||||
case cacheLocal, cacheHit:
|
||||
default:
|
||||
if err := u.copyToLayer(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
|
||||
bfi, err := u.base.OpenFile(name, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lfi, err := u.layer.OpenFile(name, flag, perm)
|
||||
if err != nil {
|
||||
bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
|
||||
return nil, err
|
||||
}
|
||||
return &UnionFile{base: bfi, layer: lfi}, nil
|
||||
}
|
||||
return u.layer.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Open(name string) (File, error) {
|
||||
st, fi, err := u.cacheStatus(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch st {
|
||||
case cacheLocal:
|
||||
return u.layer.Open(name)
|
||||
|
||||
case cacheMiss:
|
||||
bfi, err := u.base.Stat(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bfi.IsDir() {
|
||||
return u.base.Open(name)
|
||||
}
|
||||
if err := u.copyToLayer(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u.layer.Open(name)
|
||||
|
||||
case cacheStale:
|
||||
if !fi.IsDir() {
|
||||
if err := u.copyToLayer(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u.layer.Open(name)
|
||||
}
|
||||
case cacheHit:
|
||||
if !fi.IsDir() {
|
||||
return u.layer.Open(name)
|
||||
}
|
||||
}
|
||||
// the dirs from cacheHit, cacheStale fall down here:
|
||||
bfile, _ := u.base.Open(name)
|
||||
lfile, err := u.layer.Open(name)
|
||||
if err != nil && bfile == nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UnionFile{base: bfile, layer: lfile}, nil
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
|
||||
err := u.base.Mkdir(name, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.layer.MkdirAll(name, perm) // yes, MkdirAll... we cannot assume it exists in the cache
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Name() string {
|
||||
return "CacheOnReadFs"
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) MkdirAll(name string, perm os.FileMode) error {
|
||||
err := u.base.MkdirAll(name, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Create(name string) (File, error) {
|
||||
bfh, err := u.base.Create(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lfh, err := u.layer.Create(name)
|
||||
if err != nil {
|
||||
// oops, see comment about OS_TRUNC above, should we remove? then we have to
|
||||
// remember if the file did not exist before
|
||||
bfh.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &UnionFile{base: bfh, layer: lfh}, nil
|
||||
}
|
||||
22
vendor/github.com/spf13/afero/const_bsds.go
generated
vendored
22
vendor/github.com/spf13/afero/const_bsds.go
generated
vendored
@@ -1,22 +0,0 @@
|
||||
// Copyright © 2016 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// +build darwin openbsd freebsd netbsd dragonfly
|
||||
|
||||
package afero
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const BADFD = syscall.EBADF
|
||||
25
vendor/github.com/spf13/afero/const_win_unix.go
generated
vendored
25
vendor/github.com/spf13/afero/const_win_unix.go
generated
vendored
@@ -1,25 +0,0 @@
|
||||
// Copyright © 2016 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// 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.
|
||||
// +build !darwin
|
||||
// +build !openbsd
|
||||
// +build !freebsd
|
||||
// +build !dragonfly
|
||||
// +build !netbsd
|
||||
|
||||
package afero
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const BADFD = syscall.EBADFD
|
||||
253
vendor/github.com/spf13/afero/copyOnWriteFs.go
generated
vendored
253
vendor/github.com/spf13/afero/copyOnWriteFs.go
generated
vendored
@@ -1,253 +0,0 @@
|
||||
package afero
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The CopyOnWriteFs is a union filesystem: a read only base file system with
|
||||
// a possibly writeable layer on top. Changes to the file system will only
|
||||
// be made in the overlay: Changing an existing file in the base layer which
|
||||
// is not present in the overlay will copy the file to the overlay ("changing"
|
||||
// includes also calls to e.g. Chtimes() and Chmod()).
|
||||
//
|
||||
// Reading directories is currently only supported via Open(), not OpenFile().
|
||||
type CopyOnWriteFs struct {
|
||||
base Fs
|
||||
layer Fs
|
||||
}
|
||||
|
||||
func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
|
||||
return &CopyOnWriteFs{base: base, layer: layer}
|
||||
}
|
||||
|
||||
// Returns true if the file is not in the overlay
|
||||
func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
|
||||
if _, err := u.layer.Stat(name); err == nil {
|
||||
return false, nil
|
||||
}
|
||||
_, err := u.base.Stat(name)
|
||||
if err != nil {
|
||||
if oerr, ok := err.(*os.PathError); ok {
|
||||
if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
if err == syscall.ENOENT {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) copyToLayer(name string) error {
|
||||
return copyToLayer(u.base, u.layer, name)
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
|
||||
b, err := u.isBaseFile(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b {
|
||||
if err := u.copyToLayer(name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return u.layer.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
|
||||
b, err := u.isBaseFile(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b {
|
||||
if err := u.copyToLayer(name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return u.layer.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
|
||||
fi, err := u.layer.Stat(name)
|
||||
if err != nil {
|
||||
origErr := err
|
||||
if e, ok := err.(*os.PathError); ok {
|
||||
err = e.Err
|
||||
}
|
||||
if err == syscall.ENOENT || err == syscall.ENOTDIR {
|
||||
return u.base.Stat(name)
|
||||
}
|
||||
return nil, origErr
|
||||
}
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
// Renaming files present only in the base layer is not permitted
|
||||
func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
|
||||
b, err := u.isBaseFile(oldname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b {
|
||||
return syscall.EPERM
|
||||
}
|
||||
return u.layer.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
// Removing files present only in the base layer is not permitted. If
|
||||
// a file is present in the base layer and the overlay, only the overlay
|
||||
// will be removed.
|
||||
func (u *CopyOnWriteFs) Remove(name string) error {
|
||||
err := u.layer.Remove(name)
|
||||
switch err {
|
||||
case syscall.ENOENT:
|
||||
_, err = u.base.Stat(name)
|
||||
if err == nil {
|
||||
return syscall.EPERM
|
||||
}
|
||||
return syscall.ENOENT
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) RemoveAll(name string) error {
|
||||
err := u.layer.RemoveAll(name)
|
||||
switch err {
|
||||
case syscall.ENOENT:
|
||||
_, err = u.base.Stat(name)
|
||||
if err == nil {
|
||||
return syscall.EPERM
|
||||
}
|
||||
return syscall.ENOENT
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
b, err := u.isBaseFile(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
|
||||
if b {
|
||||
if err = u.copyToLayer(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u.layer.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
dir := filepath.Dir(name)
|
||||
isaDir, err := IsDir(u.base, dir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if isaDir {
|
||||
if err = u.layer.MkdirAll(dir, 0777); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u.layer.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
isaDir, err = IsDir(u.layer, dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isaDir {
|
||||
return u.layer.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
|
||||
}
|
||||
if b {
|
||||
return u.base.OpenFile(name, flag, perm)
|
||||
}
|
||||
return u.layer.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
// This function handles the 9 different possibilities caused
|
||||
// by the union which are the intersection of the following...
|
||||
// layer: doesn't exist, exists as a file, and exists as a directory
|
||||
// base: doesn't exist, exists as a file, and exists as a directory
|
||||
func (u *CopyOnWriteFs) Open(name string) (File, error) {
|
||||
// Since the overlay overrides the base we check that first
|
||||
b, err := u.isBaseFile(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If overlay doesn't exist, return the base (base state irrelevant)
|
||||
if b {
|
||||
return u.base.Open(name)
|
||||
}
|
||||
|
||||
// If overlay is a file, return it (base state irrelevant)
|
||||
dir, err := IsDir(u.layer, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dir {
|
||||
return u.layer.Open(name)
|
||||
}
|
||||
|
||||
// Overlay is a directory, base state now matters.
|
||||
// Base state has 3 states to check but 2 outcomes:
|
||||
// A. It's a file or non-readable in the base (return just the overlay)
|
||||
// B. It's an accessible directory in the base (return a UnionFile)
|
||||
|
||||
// If base is file or nonreadable, return overlay
|
||||
dir, err = IsDir(u.base, name)
|
||||
if !dir || err != nil {
|
||||
return u.layer.Open(name)
|
||||
}
|
||||
|
||||
// Both base & layer are directories
|
||||
// Return union file (if opens are without error)
|
||||
bfile, bErr := u.base.Open(name)
|
||||
lfile, lErr := u.layer.Open(name)
|
||||
|
||||
// If either have errors at this point something is very wrong. Return nil and the errors
|
||||
if bErr != nil || lErr != nil {
|
||||
return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
|
||||
}
|
||||
|
||||
return &UnionFile{base: bfile, layer: lfile}, nil
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
|
||||
dir, err := IsDir(u.base, name)
|
||||
if err != nil {
|
||||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
if dir {
|
||||
return syscall.EEXIST
|
||||
}
|
||||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) Name() string {
|
||||
return "CopyOnWriteFs"
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
|
||||
dir, err := IsDir(u.base, name)
|
||||
if err != nil {
|
||||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
if dir {
|
||||
return syscall.EEXIST
|
||||
}
|
||||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) Create(name string) (File, error) {
|
||||
return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
|
||||
}
|
||||
110
vendor/github.com/spf13/afero/httpFs.go
generated
vendored
110
vendor/github.com/spf13/afero/httpFs.go
generated
vendored
@@ -1,110 +0,0 @@
|
||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// 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 afero
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type httpDir struct {
|
||||
basePath string
|
||||
fs HttpFs
|
||||
}
|
||||
|
||||
func (d httpDir) Open(name string) (http.File, error) {
|
||||
if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
|
||||
strings.Contains(name, "\x00") {
|
||||
return nil, errors.New("http: invalid character in file path")
|
||||
}
|
||||
dir := string(d.basePath)
|
||||
if dir == "" {
|
||||
dir = "."
|
||||
}
|
||||
|
||||
f, err := d.fs.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
type HttpFs struct {
|
||||
source Fs
|
||||
}
|
||||
|
||||
func NewHttpFs(source Fs) *HttpFs {
|
||||
return &HttpFs{source: source}
|
||||
}
|
||||
|
||||
func (h HttpFs) Dir(s string) *httpDir {
|
||||
return &httpDir{basePath: s, fs: h}
|
||||
}
|
||||
|
||||
func (h HttpFs) Name() string { return "h HttpFs" }
|
||||
|
||||
func (h HttpFs) Create(name string) (File, error) {
|
||||
return h.source.Create(name)
|
||||
}
|
||||
|
||||
func (h HttpFs) Chmod(name string, mode os.FileMode) error {
|
||||
return h.source.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (h HttpFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return h.source.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
func (h HttpFs) Mkdir(name string, perm os.FileMode) error {
|
||||
return h.source.Mkdir(name, perm)
|
||||
}
|
||||
|
||||
func (h HttpFs) MkdirAll(path string, perm os.FileMode) error {
|
||||
return h.source.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
func (h HttpFs) Open(name string) (http.File, error) {
|
||||
f, err := h.source.Open(name)
|
||||
if err == nil {
|
||||
if httpfile, ok := f.(http.File); ok {
|
||||
return httpfile, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (h HttpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
return h.source.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
func (h HttpFs) Remove(name string) error {
|
||||
return h.source.Remove(name)
|
||||
}
|
||||
|
||||
func (h HttpFs) RemoveAll(path string) error {
|
||||
return h.source.RemoveAll(path)
|
||||
}
|
||||
|
||||
func (h HttpFs) Rename(oldname, newname string) error {
|
||||
return h.source.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
func (h HttpFs) Stat(name string) (os.FileInfo, error) {
|
||||
return h.source.Stat(name)
|
||||
}
|
||||
230
vendor/github.com/spf13/afero/ioutil.go
generated
vendored
230
vendor/github.com/spf13/afero/ioutil.go
generated
vendored
@@ -1,230 +0,0 @@
|
||||
// Copyright ©2015 The Go Authors
|
||||
// Copyright ©2015 Steve Francia <spf@spf13.com>
|
||||
//
|
||||
// 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 afero
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// byName implements sort.Interface.
|
||||
type byName []os.FileInfo
|
||||
|
||||
func (f byName) Len() int { return len(f) }
|
||||
func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
|
||||
func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||
|
||||
// ReadDir reads the directory named by dirname and returns
|
||||
// a list of sorted directory entries.
|
||||
func (a Afero) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
return ReadDir(a.Fs, dirname)
|
||||
}
|
||||
|
||||
func ReadDir(fs Fs, dirname string) ([]os.FileInfo, error) {
|
||||
f, err := fs.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Sort(byName(list))
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// ReadFile reads the file named by filename and returns the contents.
|
||||
// A successful call returns err == nil, not err == EOF. Because ReadFile
|
||||
// reads the whole file, it does not treat an EOF from Read as an error
|
||||
// to be reported.
|
||||
func (a Afero) ReadFile(filename string) ([]byte, error) {
|
||||
return ReadFile(a.Fs, filename)
|
||||
}
|
||||
|
||||
func ReadFile(fs Fs, filename string) ([]byte, error) {
|
||||
f, err := fs.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
// It's a good but not certain bet that FileInfo will tell us exactly how much to
|
||||
// read, so let's try it but be prepared for the answer to be wrong.
|
||||
var n int64
|
||||
|
||||
if fi, err := f.Stat(); err == nil {
|
||||
// Don't preallocate a huge buffer, just in case.
|
||||
if size := fi.Size(); size < 1e9 {
|
||||
n = size
|
||||
}
|
||||
}
|
||||
// As initial capacity for readAll, use n + a little extra in case Size is zero,
|
||||
// and to avoid another allocation after Read has filled the buffer. The readAll
|
||||
// call will read into its allocated internal buffer cheaply. If the size was
|
||||
// wrong, we'll either waste some space off the end or reallocate as needed, but
|
||||
// in the overwhelmingly common case we'll get it just right.
|
||||
return readAll(f, n+bytes.MinRead)
|
||||
}
|
||||
|
||||
// readAll reads from r until an error or EOF and returns the data it read
|
||||
// from the internal buffer allocated with a specified capacity.
|
||||
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, capacity))
|
||||
// If the buffer overflows, we will get bytes.ErrTooLarge.
|
||||
// Return that as an error. Any other panic remains.
|
||||
defer func() {
|
||||
e := recover()
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
|
||||
err = panicErr
|
||||
} else {
|
||||
panic(e)
|
||||
}
|
||||
}()
|
||||
_, err = buf.ReadFrom(r)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// ReadAll reads from r until an error or EOF and returns the data it read.
|
||||
// A successful call returns err == nil, not err == EOF. Because ReadAll is
|
||||
// defined to read from src until EOF, it does not treat an EOF from Read
|
||||
// as an error to be reported.
|
||||
func ReadAll(r io.Reader) ([]byte, error) {
|
||||
return readAll(r, bytes.MinRead)
|
||||
}
|
||||
|
||||
// WriteFile writes data to a file named by filename.
|
||||
// If the file does not exist, WriteFile creates it with permissions perm;
|
||||
// otherwise WriteFile truncates it before writing.
|
||||
func (a Afero) WriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||
return WriteFile(a.Fs, filename, data, perm)
|
||||
}
|
||||
|
||||
func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error {
|
||||
f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := f.Write(data)
|
||||
if err == nil && n < len(data) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err1 := f.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Random number state.
|
||||
// We generate random temporary file names so that there's a good
|
||||
// chance the file doesn't exist yet - keeps the number of tries in
|
||||
// TempFile to a minimum.
|
||||
var rand uint32
|
||||
var randmu sync.Mutex
|
||||
|
||||
func reseed() uint32 {
|
||||
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
|
||||
}
|
||||
|
||||
func nextSuffix() string {
|
||||
randmu.Lock()
|
||||
r := rand
|
||||
if r == 0 {
|
||||
r = reseed()
|
||||
}
|
||||
r = r*1664525 + 1013904223 // constants from Numerical Recipes
|
||||
rand = r
|
||||
randmu.Unlock()
|
||||
return strconv.Itoa(int(1e9 + r%1e9))[1:]
|
||||
}
|
||||
|
||||
// TempFile creates a new temporary file in the directory dir
|
||||
// with a name beginning with prefix, opens the file for reading
|
||||
// and writing, and returns the resulting *File.
|
||||
// If dir is the empty string, TempFile uses the default directory
|
||||
// for temporary files (see os.TempDir).
|
||||
// Multiple programs calling TempFile simultaneously
|
||||
// will not choose the same file. The caller can use f.Name()
|
||||
// to find the pathname of the file. It is the caller's responsibility
|
||||
// to remove the file when no longer needed.
|
||||
func (a Afero) TempFile(dir, prefix string) (f File, err error) {
|
||||
return TempFile(a.Fs, dir, prefix)
|
||||
}
|
||||
|
||||
func TempFile(fs Fs, dir, prefix string) (f File, err error) {
|
||||
if dir == "" {
|
||||
dir = os.TempDir()
|
||||
}
|
||||
|
||||
nconflict := 0
|
||||
for i := 0; i < 10000; i++ {
|
||||
name := filepath.Join(dir, prefix+nextSuffix())
|
||||
f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||
if os.IsExist(err) {
|
||||
if nconflict++; nconflict > 10 {
|
||||
randmu.Lock()
|
||||
rand = reseed()
|
||||
randmu.Unlock()
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TempDir creates a new temporary directory in the directory dir
|
||||
// with a name beginning with prefix and returns the path of the
|
||||
// new directory. If dir is the empty string, TempDir uses the
|
||||
// default directory for temporary files (see os.TempDir).
|
||||
// Multiple programs calling TempDir simultaneously
|
||||
// will not choose the same directory. It is the caller's responsibility
|
||||
// to remove the directory when no longer needed.
|
||||
func (a Afero) TempDir(dir, prefix string) (name string, err error) {
|
||||
return TempDir(a.Fs, dir, prefix)
|
||||
}
|
||||
func TempDir(fs Fs, dir, prefix string) (name string, err error) {
|
||||
if dir == "" {
|
||||
dir = os.TempDir()
|
||||
}
|
||||
|
||||
nconflict := 0
|
||||
for i := 0; i < 10000; i++ {
|
||||
try := filepath.Join(dir, prefix+nextSuffix())
|
||||
err = fs.Mkdir(try, 0700)
|
||||
if os.IsExist(err) {
|
||||
if nconflict++; nconflict > 10 {
|
||||
randmu.Lock()
|
||||
rand = reseed()
|
||||
randmu.Unlock()
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err == nil {
|
||||
name = try
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
37
vendor/github.com/spf13/afero/mem/dir.go
generated
vendored
37
vendor/github.com/spf13/afero/mem/dir.go
generated
vendored
@@ -1,37 +0,0 @@
|
||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// 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 mem
|
||||
|
||||
type Dir interface {
|
||||
Len() int
|
||||
Names() []string
|
||||
Files() []*FileData
|
||||
Add(*FileData)
|
||||
Remove(*FileData)
|
||||
}
|
||||
|
||||
func RemoveFromMemDir(dir *FileData, f *FileData) {
|
||||
dir.memDir.Remove(f)
|
||||
}
|
||||
|
||||
func AddToMemDir(dir *FileData, f *FileData) {
|
||||
dir.memDir.Add(f)
|
||||
}
|
||||
|
||||
func InitializeDir(d *FileData) {
|
||||
if d.memDir == nil {
|
||||
d.dir = true
|
||||
d.memDir = &DirMap{}
|
||||
}
|
||||
}
|
||||
43
vendor/github.com/spf13/afero/mem/dirmap.go
generated
vendored
43
vendor/github.com/spf13/afero/mem/dirmap.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// 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 mem
|
||||
|
||||
import "sort"
|
||||
|
||||
type DirMap map[string]*FileData
|
||||
|
||||
func (m DirMap) Len() int { return len(m) }
|
||||
func (m DirMap) Add(f *FileData) { m[f.name] = f }
|
||||
func (m DirMap) Remove(f *FileData) { delete(m, f.name) }
|
||||
func (m DirMap) Files() (files []*FileData) {
|
||||
for _, f := range m {
|
||||
files = append(files, f)
|
||||
}
|
||||
sort.Sort(filesSorter(files))
|
||||
return files
|
||||
}
|
||||
|
||||
// implement sort.Interface for []*FileData
|
||||
type filesSorter []*FileData
|
||||
|
||||
func (s filesSorter) Len() int { return len(s) }
|
||||
func (s filesSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s filesSorter) Less(i, j int) bool { return s[i].name < s[j].name }
|
||||
|
||||
func (m DirMap) Names() (names []string) {
|
||||
for x := range m {
|
||||
names = append(names, x)
|
||||
}
|
||||
return names
|
||||
}
|
||||
285
vendor/github.com/spf13/afero/mem/file.go
generated
vendored
285
vendor/github.com/spf13/afero/mem/file.go
generated
vendored
@@ -1,285 +0,0 @@
|
||||
// Copyright © 2015 Steve Francia <spf@spf13.com>.
|
||||
// Copyright 2013 tsuru authors. All rights reserved.
|
||||
//
|
||||
// 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 mem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
import "time"
|
||||
|
||||
const FilePathSeparator = string(filepath.Separator)
|
||||
|
||||
type File struct {
|
||||
// atomic requires 64-bit alignment for struct field access
|
||||
at int64
|
||||
readDirCount int64
|
||||
closed bool
|
||||
readOnly bool
|
||||
fileData *FileData
|
||||
}
|
||||
|
||||
func NewFileHandle(data *FileData) *File {
|
||||
return &File{fileData: data}
|
||||
}
|
||||
|
||||
func NewReadOnlyFileHandle(data *FileData) *File {
|
||||
return &File{fileData: data, readOnly: true}
|
||||
}
|
||||
|
||||
func (f File) Data() *FileData {
|
||||
return f.fileData
|
||||
}
|
||||
|
||||
type FileData struct {
|
||||
sync.Mutex
|
||||
name string
|
||||
data []byte
|
||||
memDir Dir
|
||||
dir bool
|
||||
mode os.FileMode
|
||||
modtime time.Time
|
||||
}
|
||||
|
||||
func (d *FileData) Name() string {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
return d.name
|
||||
}
|
||||
|
||||
func CreateFile(name string) *FileData {
|
||||
return &FileData{name: name, mode: os.ModeTemporary, modtime: time.Now()}
|
||||
}
|
||||
|
||||
func CreateDir(name string) *FileData {
|
||||
return &FileData{name: name, memDir: &DirMap{}, dir: true}
|
||||
}
|
||||
|
||||
func ChangeFileName(f *FileData, newname string) {
|
||||
f.name = newname
|
||||
}
|
||||
|
||||
func SetMode(f *FileData, mode os.FileMode) {
|
||||
f.mode = mode
|
||||
}
|
||||
|
||||
func SetModTime(f *FileData, mtime time.Time) {
|
||||
f.modtime = mtime
|
||||
}
|
||||
|
||||
func GetFileInfo(f *FileData) *FileInfo {
|
||||
return &FileInfo{f}
|
||||
}
|
||||
|
||||
func (f *File) Open() error {
|
||||
atomic.StoreInt64(&f.at, 0)
|
||||
atomic.StoreInt64(&f.readDirCount, 0)
|
||||
f.fileData.Lock()
|
||||
f.closed = false
|
||||
f.fileData.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Close() error {
|
||||
f.fileData.Lock()
|
||||
f.closed = true
|
||||
if !f.readOnly {
|
||||
SetModTime(f.fileData, time.Now())
|
||||
}
|
||||
f.fileData.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Name() string {
|
||||
return f.fileData.Name()
|
||||
}
|
||||
|
||||
func (f *File) Stat() (os.FileInfo, error) {
|
||||
return &FileInfo{f.fileData}, nil
|
||||
}
|
||||
|
||||
func (f *File) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
|
||||
var outLength int64
|
||||
|
||||
f.fileData.Lock()
|
||||
files := f.fileData.memDir.Files()[f.readDirCount:]
|
||||
if count > 0 {
|
||||
if len(files) < count {
|
||||
outLength = int64(len(files))
|
||||
} else {
|
||||
outLength = int64(count)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
err = io.EOF
|
||||
}
|
||||
} else {
|
||||
outLength = int64(len(files))
|
||||
}
|
||||
f.readDirCount += outLength
|
||||
f.fileData.Unlock()
|
||||
|
||||
res = make([]os.FileInfo, outLength)
|
||||
for i := range res {
|
||||
res[i] = &FileInfo{files[i]}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (f *File) Readdirnames(n int) (names []string, err error) {
|
||||
fi, err := f.Readdir(n)
|
||||
names = make([]string, len(fi))
|
||||
for i, f := range fi {
|
||||
_, names[i] = filepath.Split(f.Name())
|
||||
}
|
||||
return names, err
|
||||
}
|
||||
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
f.fileData.Lock()
|
||||
defer f.fileData.Unlock()
|
||||
if f.closed == true {
|
||||
return 0, ErrFileClosed
|
||||
}
|
||||
if len(b) > 0 && int(f.at) == len(f.fileData.data) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if len(f.fileData.data)-int(f.at) >= len(b) {
|
||||
n = len(b)
|
||||
} else {
|
||||
n = len(f.fileData.data) - int(f.at)
|
||||
}
|
||||
copy(b, f.fileData.data[f.at:f.at+int64(n)])
|
||||
atomic.AddInt64(&f.at, int64(n))
|
||||
return
|
||||
}
|
||||
|
||||
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
|
||||
atomic.StoreInt64(&f.at, off)
|
||||
return f.Read(b)
|
||||
}
|
||||
|
||||
func (f *File) Truncate(size int64) error {
|
||||
if f.closed == true {
|
||||
return ErrFileClosed
|
||||
}
|
||||
if f.readOnly {
|
||||
return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
|
||||
}
|
||||
if size < 0 {
|
||||
return ErrOutOfRange
|
||||
}
|
||||
if size > int64(len(f.fileData.data)) {
|
||||
diff := size - int64(len(f.fileData.data))
|
||||
f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...)
|
||||
} else {
|
||||
f.fileData.data = f.fileData.data[0:size]
|
||||
}
|
||||
SetModTime(f.fileData, time.Now())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) Seek(offset int64, whence int) (int64, error) {
|
||||
if f.closed == true {
|
||||
return 0, ErrFileClosed
|
||||
}
|
||||
switch whence {
|
||||
case 0:
|
||||
atomic.StoreInt64(&f.at, offset)
|
||||
case 1:
|
||||
atomic.AddInt64(&f.at, int64(offset))
|
||||
case 2:
|
||||
atomic.StoreInt64(&f.at, int64(len(f.fileData.data))+offset)
|
||||
}
|
||||
return f.at, nil
|
||||
}
|
||||
|
||||
func (f *File) Write(b []byte) (n int, err error) {
|
||||
if f.readOnly {
|
||||
return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")}
|
||||
}
|
||||
n = len(b)
|
||||
cur := atomic.LoadInt64(&f.at)
|
||||
f.fileData.Lock()
|
||||
defer f.fileData.Unlock()
|
||||
diff := cur - int64(len(f.fileData.data))
|
||||
var tail []byte
|
||||
if n+int(cur) < len(f.fileData.data) {
|
||||
tail = f.fileData.data[n+int(cur):]
|
||||
}
|
||||
if diff > 0 {
|
||||
f.fileData.data = append(bytes.Repeat([]byte{00}, int(diff)), b...)
|
||||
f.fileData.data = append(f.fileData.data, tail...)
|
||||
} else {
|
||||
f.fileData.data = append(f.fileData.data[:cur], b...)
|
||||
f.fileData.data = append(f.fileData.data, tail...)
|
||||
}
|
||||
SetModTime(f.fileData, time.Now())
|
||||
|
||||
atomic.StoreInt64(&f.at, int64(len(f.fileData.data)))
|
||||
return
|
||||
}
|
||||
|
||||
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
|
||||
atomic.StoreInt64(&f.at, off)
|
||||
return f.Write(b)
|
||||
}
|
||||
|
||||
func (f *File) WriteString(s string) (ret int, err error) {
|
||||
return f.Write([]byte(s))
|
||||
}
|
||||
|
||||
func (f *File) Info() *FileInfo {
|
||||
return &FileInfo{f.fileData}
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
*FileData
|
||||
}
|
||||
|
||||
// Implements os.FileInfo
|
||||
func (s *FileInfo) Name() string {
|
||||
_, name := filepath.Split(s.name)
|
||||
return name
|
||||
}
|
||||
func (s *FileInfo) Mode() os.FileMode { return s.mode }
|
||||
func (s *FileInfo) ModTime() time.Time { return s.modtime }
|
||||
func (s *FileInfo) IsDir() bool { return s.dir }
|
||||
func (s *FileInfo) Sys() interface{} { return nil }
|
||||
func (s *FileInfo) Size() int64 {
|
||||
if s.IsDir() {
|
||||
return int64(42)
|
||||
}
|
||||
return int64(len(s.data))
|
||||
}
|
||||
|
||||
var (
|
||||
ErrFileClosed = errors.New("File is closed")
|
||||
ErrOutOfRange = errors.New("Out of range")
|
||||
ErrTooLarge = errors.New("Too large")
|
||||
ErrFileNotFound = os.ErrNotExist
|
||||
ErrFileExists = os.ErrExist
|
||||
ErrDestinationExists = os.ErrExist
|
||||
)
|
||||
361
vendor/github.com/spf13/afero/memmap.go
generated
vendored
361
vendor/github.com/spf13/afero/memmap.go
generated
vendored
@@ -1,361 +0,0 @@
|
||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// 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 afero
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero/mem"
|
||||
)
|
||||
|
||||
type MemMapFs struct {
|
||||
mu sync.RWMutex
|
||||
data map[string]*mem.FileData
|
||||
init sync.Once
|
||||
}
|
||||
|
||||
func NewMemMapFs() Fs {
|
||||
return &MemMapFs{}
|
||||
}
|
||||
|
||||
func (m *MemMapFs) getData() map[string]*mem.FileData {
|
||||
m.init.Do(func() {
|
||||
m.data = make(map[string]*mem.FileData)
|
||||
// Root should always exist, right?
|
||||
// TODO: what about windows?
|
||||
m.data[FilePathSeparator] = mem.CreateDir(FilePathSeparator)
|
||||
})
|
||||
return m.data
|
||||
}
|
||||
|
||||
func (*MemMapFs) Name() string { return "MemMapFS" }
|
||||
|
||||
func (m *MemMapFs) Create(name string) (File, error) {
|
||||
name = normalizePath(name)
|
||||
m.mu.Lock()
|
||||
file := mem.CreateFile(name)
|
||||
m.getData()[name] = file
|
||||
m.registerWithParent(file)
|
||||
m.mu.Unlock()
|
||||
return mem.NewFileHandle(file), nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) unRegisterWithParent(fileName string) error {
|
||||
f, err := m.lockfreeOpen(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parent := m.findParent(f)
|
||||
if parent == nil {
|
||||
log.Panic("parent of ", f.Name(), " is nil")
|
||||
}
|
||||
mem.RemoveFromMemDir(parent, f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
|
||||
pdir, _ := filepath.Split(f.Name())
|
||||
pdir = filepath.Clean(pdir)
|
||||
pfile, err := m.lockfreeOpen(pdir)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return pfile
|
||||
}
|
||||
|
||||
func (m *MemMapFs) registerWithParent(f *mem.FileData) {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
parent := m.findParent(f)
|
||||
if parent == nil {
|
||||
pdir := filepath.Dir(filepath.Clean(f.Name()))
|
||||
err := m.lockfreeMkdir(pdir, 0777)
|
||||
if err != nil {
|
||||
//log.Println("Mkdir error:", err)
|
||||
return
|
||||
}
|
||||
parent, err = m.lockfreeOpen(pdir)
|
||||
if err != nil {
|
||||
//log.Println("Open after Mkdir error:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mem.InitializeDir(parent)
|
||||
mem.AddToMemDir(parent, f)
|
||||
}
|
||||
|
||||
func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
|
||||
name = normalizePath(name)
|
||||
x, ok := m.getData()[name]
|
||||
if ok {
|
||||
// Only return ErrFileExists if it's a file, not a directory.
|
||||
i := mem.FileInfo{FileData: x}
|
||||
if !i.IsDir() {
|
||||
return ErrFileExists
|
||||
}
|
||||
} else {
|
||||
item := mem.CreateDir(name)
|
||||
m.getData()[name] = item
|
||||
m.registerWithParent(item)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
|
||||
name = normalizePath(name)
|
||||
|
||||
m.mu.RLock()
|
||||
_, ok := m.getData()[name]
|
||||
m.mu.RUnlock()
|
||||
if ok {
|
||||
return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
item := mem.CreateDir(name)
|
||||
m.getData()[name] = item
|
||||
m.registerWithParent(item)
|
||||
m.mu.Unlock()
|
||||
|
||||
m.Chmod(name, perm)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error {
|
||||
err := m.Mkdir(path, perm)
|
||||
if err != nil {
|
||||
if err.(*os.PathError).Err == ErrFileExists {
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle some relative paths
|
||||
func normalizePath(path string) string {
|
||||
path = filepath.Clean(path)
|
||||
|
||||
switch path {
|
||||
case ".":
|
||||
return FilePathSeparator
|
||||
case "..":
|
||||
return FilePathSeparator
|
||||
default:
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MemMapFs) Open(name string) (File, error) {
|
||||
f, err := m.open(name)
|
||||
if f != nil {
|
||||
return mem.NewReadOnlyFileHandle(f), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (m *MemMapFs) openWrite(name string) (File, error) {
|
||||
f, err := m.open(name)
|
||||
if f != nil {
|
||||
return mem.NewFileHandle(f), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (m *MemMapFs) open(name string) (*mem.FileData, error) {
|
||||
name = normalizePath(name)
|
||||
|
||||
m.mu.RLock()
|
||||
f, ok := m.getData()[name]
|
||||
m.mu.RUnlock()
|
||||
if !ok {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: ErrFileNotFound}
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {
|
||||
name = normalizePath(name)
|
||||
f, ok := m.getData()[name]
|
||||
if ok {
|
||||
return f, nil
|
||||
} else {
|
||||
return nil, ErrFileNotFound
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
chmod := false
|
||||
file, err := m.openWrite(name)
|
||||
if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {
|
||||
file, err = m.Create(name)
|
||||
chmod = true
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if flag == os.O_RDONLY {
|
||||
file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data())
|
||||
}
|
||||
if flag&os.O_APPEND > 0 {
|
||||
_, err = file.Seek(0, os.SEEK_END)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 {
|
||||
err = file.Truncate(0)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if chmod {
|
||||
m.Chmod(name, perm)
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) Remove(name string) error {
|
||||
name = normalizePath(name)
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if _, ok := m.getData()[name]; ok {
|
||||
err := m.unRegisterWithParent(name)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "remove", Path: name, Err: err}
|
||||
}
|
||||
delete(m.getData(), name)
|
||||
} else {
|
||||
return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) RemoveAll(path string) error {
|
||||
path = normalizePath(path)
|
||||
m.mu.Lock()
|
||||
m.unRegisterWithParent(path)
|
||||
m.mu.Unlock()
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
for p, _ := range m.getData() {
|
||||
if strings.HasPrefix(p, path) {
|
||||
m.mu.RUnlock()
|
||||
m.mu.Lock()
|
||||
delete(m.getData(), p)
|
||||
m.mu.Unlock()
|
||||
m.mu.RLock()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) Rename(oldname, newname string) error {
|
||||
oldname = normalizePath(oldname)
|
||||
newname = normalizePath(newname)
|
||||
|
||||
if oldname == newname {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if _, ok := m.getData()[oldname]; ok {
|
||||
m.mu.RUnlock()
|
||||
m.mu.Lock()
|
||||
m.unRegisterWithParent(oldname)
|
||||
fileData := m.getData()[oldname]
|
||||
delete(m.getData(), oldname)
|
||||
mem.ChangeFileName(fileData, newname)
|
||||
m.getData()[newname] = fileData
|
||||
m.registerWithParent(fileData)
|
||||
m.mu.Unlock()
|
||||
m.mu.RLock()
|
||||
} else {
|
||||
return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {
|
||||
f, err := m.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fi := mem.GetFileInfo(f.(*mem.File).Data())
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {
|
||||
name = normalizePath(name)
|
||||
|
||||
m.mu.RLock()
|
||||
f, ok := m.getData()[name]
|
||||
m.mu.RUnlock()
|
||||
if !ok {
|
||||
return &os.PathError{Op: "chmod", Path: name, Err: ErrFileNotFound}
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
mem.SetMode(f, mode)
|
||||
m.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
name = normalizePath(name)
|
||||
|
||||
m.mu.RLock()
|
||||
f, ok := m.getData()[name]
|
||||
m.mu.RUnlock()
|
||||
if !ok {
|
||||
return &os.PathError{Op: "chtimes", Path: name, Err: ErrFileNotFound}
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
mem.SetModTime(f, mtime)
|
||||
m.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MemMapFs) List() {
|
||||
for _, x := range m.data {
|
||||
y := mem.FileInfo{FileData: x}
|
||||
fmt.Println(x.Name(), y.Size())
|
||||
}
|
||||
}
|
||||
|
||||
// func debugMemMapList(fs Fs) {
|
||||
// if x, ok := fs.(*MemMapFs); ok {
|
||||
// x.List()
|
||||
// }
|
||||
// }
|
||||
14
vendor/github.com/spf13/afero/memradix.go
generated
vendored
14
vendor/github.com/spf13/afero/memradix.go
generated
vendored
@@ -1,14 +0,0 @@
|
||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// 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 afero
|
||||
94
vendor/github.com/spf13/afero/os.go
generated
vendored
94
vendor/github.com/spf13/afero/os.go
generated
vendored
@@ -1,94 +0,0 @@
|
||||
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
||||
// Copyright 2013 tsuru authors. All rights reserved.
|
||||
//
|
||||
// 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 afero
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OsFs is a Fs implementation that uses functions provided by the os package.
|
||||
//
|
||||
// For details in any method, check the documentation of the os package
|
||||
// (http://golang.org/pkg/os/).
|
||||
type OsFs struct{}
|
||||
|
||||
func NewOsFs() Fs {
|
||||
return &OsFs{}
|
||||
}
|
||||
|
||||
func (OsFs) Name() string { return "OsFs" }
|
||||
|
||||
func (OsFs) Create(name string) (File, error) {
|
||||
f, e := os.Create(name)
|
||||
if f == nil {
|
||||
// while this looks strange, we need to return a bare nil (of type nil) not
|
||||
// a nil value of type *os.File or nil won't be nil
|
||||
return nil, e
|
||||
}
|
||||
return f, e
|
||||
}
|
||||
|
||||
func (OsFs) Mkdir(name string, perm os.FileMode) error {
|
||||
return os.Mkdir(name, perm)
|
||||
}
|
||||
|
||||
func (OsFs) MkdirAll(path string, perm os.FileMode) error {
|
||||
return os.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
func (OsFs) Open(name string) (File, error) {
|
||||
f, e := os.Open(name)
|
||||
if f == nil {
|
||||
// while this looks strange, we need to return a bare nil (of type nil) not
|
||||
// a nil value of type *os.File or nil won't be nil
|
||||
return nil, e
|
||||
}
|
||||
return f, e
|
||||
}
|
||||
|
||||
func (OsFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
f, e := os.OpenFile(name, flag, perm)
|
||||
if f == nil {
|
||||
// while this looks strange, we need to return a bare nil (of type nil) not
|
||||
// a nil value of type *os.File or nil won't be nil
|
||||
return nil, e
|
||||
}
|
||||
return f, e
|
||||
}
|
||||
|
||||
func (OsFs) Remove(name string) error {
|
||||
return os.Remove(name)
|
||||
}
|
||||
|
||||
func (OsFs) RemoveAll(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
func (OsFs) Rename(oldname, newname string) error {
|
||||
return os.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
func (OsFs) Stat(name string) (os.FileInfo, error) {
|
||||
return os.Stat(name)
|
||||
}
|
||||
|
||||
func (OsFs) Chmod(name string, mode os.FileMode) error {
|
||||
return os.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (OsFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return os.Chtimes(name, atime, mtime)
|
||||
}
|
||||
108
vendor/github.com/spf13/afero/path.go
generated
vendored
108
vendor/github.com/spf13/afero/path.go
generated
vendored
@@ -1,108 +0,0 @@
|
||||
// Copyright ©2015 The Go Authors
|
||||
// Copyright ©2015 Steve Francia <spf@spf13.com>
|
||||
//
|
||||
// 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 afero
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// readDirNames reads the directory named by dirname and returns
|
||||
// a sorted list of directory entries.
|
||||
// adapted from https://golang.org/src/path/filepath/path.go
|
||||
func readDirNames(fs Fs, dirname string) ([]string, error) {
|
||||
f, err := fs.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names, err := f.Readdirnames(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// walk recursively descends path, calling walkFn
|
||||
// adapted from https://golang.org/src/path/filepath/path.go
|
||||
func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
||||
err := walkFn(path, info, nil)
|
||||
if err != nil {
|
||||
if info.IsDir() && err == filepath.SkipDir {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
names, err := readDirNames(fs, path)
|
||||
if err != nil {
|
||||
return walkFn(path, info, err)
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
filename := filepath.Join(path, name)
|
||||
fileInfo, err := lstatIfOs(fs, filename)
|
||||
if err != nil {
|
||||
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = walk(fs, filename, fileInfo, walkFn)
|
||||
if err != nil {
|
||||
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// if the filesystem is OsFs use Lstat, else use fs.Stat
|
||||
func lstatIfOs(fs Fs, path string) (info os.FileInfo, err error) {
|
||||
_, ok := fs.(*OsFs)
|
||||
if ok {
|
||||
info, err = os.Lstat(path)
|
||||
} else {
|
||||
info, err = fs.Stat(path)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
||||
// directory in the tree, including root. All errors that arise visiting files
|
||||
// and directories are filtered by walkFn. The files are walked in lexical
|
||||
// order, which makes the output deterministic but means that for very
|
||||
// large directories Walk can be inefficient.
|
||||
// Walk does not follow symbolic links.
|
||||
|
||||
func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error {
|
||||
return Walk(a.Fs, root, walkFn)
|
||||
}
|
||||
|
||||
func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error {
|
||||
info, err := lstatIfOs(fs, root)
|
||||
if err != nil {
|
||||
return walkFn(root, nil, err)
|
||||
}
|
||||
return walk(fs, root, info, walkFn)
|
||||
}
|
||||
70
vendor/github.com/spf13/afero/readonlyfs.go
generated
vendored
70
vendor/github.com/spf13/afero/readonlyfs.go
generated
vendored
@@ -1,70 +0,0 @@
|
||||
package afero
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ReadOnlyFs struct {
|
||||
source Fs
|
||||
}
|
||||
|
||||
func NewReadOnlyFs(source Fs) Fs {
|
||||
return &ReadOnlyFs{source: source}
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) ReadDir(name string) ([]os.FileInfo, error) {
|
||||
return ReadDir(r.source, name)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Chtimes(n string, a, m time.Time) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Chmod(n string, m os.FileMode) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Name() string {
|
||||
return "ReadOnlyFilter"
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Stat(name string) (os.FileInfo, error) {
|
||||
return r.source.Stat(name)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Rename(o, n string) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) RemoveAll(p string) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Remove(n string) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
|
||||
return nil, syscall.EPERM
|
||||
}
|
||||
return r.source.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Open(n string) (File, error) {
|
||||
return r.source.Open(n)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Mkdir(n string, p os.FileMode) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) MkdirAll(n string, p os.FileMode) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Create(n string) (File, error) {
|
||||
return nil, syscall.EPERM
|
||||
}
|
||||
214
vendor/github.com/spf13/afero/regexpfs.go
generated
vendored
214
vendor/github.com/spf13/afero/regexpfs.go
generated
vendored
@@ -1,214 +0,0 @@
|
||||
package afero
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The RegexpFs filters files (not directories) by regular expression. Only
|
||||
// files matching the given regexp will be allowed, all others get a ENOENT error (
|
||||
// "No such file or directory").
|
||||
//
|
||||
type RegexpFs struct {
|
||||
re *regexp.Regexp
|
||||
source Fs
|
||||
}
|
||||
|
||||
func NewRegexpFs(source Fs, re *regexp.Regexp) Fs {
|
||||
return &RegexpFs{source: source, re: re}
|
||||
}
|
||||
|
||||
type RegexpFile struct {
|
||||
f File
|
||||
re *regexp.Regexp
|
||||
}
|
||||
|
||||
func (r *RegexpFs) matchesName(name string) error {
|
||||
if r.re == nil {
|
||||
return nil
|
||||
}
|
||||
if r.re.MatchString(name) {
|
||||
return nil
|
||||
}
|
||||
return syscall.ENOENT
|
||||
}
|
||||
|
||||
func (r *RegexpFs) dirOrMatches(name string) error {
|
||||
dir, err := IsDir(r.source, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dir {
|
||||
return nil
|
||||
}
|
||||
return r.matchesName(name)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Chtimes(name string, a, m time.Time) error {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Chtimes(name, a, m)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Chmod(name string, mode os.FileMode) error {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Name() string {
|
||||
return "RegexpFs"
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Stat(name string) (os.FileInfo, error) {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.source.Stat(name)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Rename(oldname, newname string) error {
|
||||
dir, err := IsDir(r.source, oldname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dir {
|
||||
return nil
|
||||
}
|
||||
if err := r.matchesName(oldname); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.matchesName(newname); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) RemoveAll(p string) error {
|
||||
dir, err := IsDir(r.source, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !dir {
|
||||
if err := r.matchesName(p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return r.source.RemoveAll(p)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Remove(name string) error {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Remove(name)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.source.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Open(name string) (File, error) {
|
||||
dir, err := IsDir(r.source, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dir {
|
||||
if err := r.matchesName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f, err := r.source.Open(name)
|
||||
return &RegexpFile{f: f, re: r.re}, nil
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Mkdir(n string, p os.FileMode) error {
|
||||
return r.source.Mkdir(n, p)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) MkdirAll(n string, p os.FileMode) error {
|
||||
return r.source.MkdirAll(n, p)
|
||||
}
|
||||
|
||||
func (r *RegexpFs) Create(name string) (File, error) {
|
||||
if err := r.matchesName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.source.Create(name)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Close() error {
|
||||
return f.f.Close()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Read(s []byte) (int, error) {
|
||||
return f.f.Read(s)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) ReadAt(s []byte, o int64) (int, error) {
|
||||
return f.f.ReadAt(s, o)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Seek(o int64, w int) (int64, error) {
|
||||
return f.f.Seek(o, w)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Write(s []byte) (int, error) {
|
||||
return f.f.Write(s)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) WriteAt(s []byte, o int64) (int, error) {
|
||||
return f.f.WriteAt(s, o)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Name() string {
|
||||
return f.f.Name()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Readdir(c int) (fi []os.FileInfo, err error) {
|
||||
var rfi []os.FileInfo
|
||||
rfi, err = f.f.Readdir(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, i := range rfi {
|
||||
if i.IsDir() || f.re.MatchString(i.Name()) {
|
||||
fi = append(fi, i)
|
||||
}
|
||||
}
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Readdirnames(c int) (n []string, err error) {
|
||||
fi, err := f.Readdir(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, s := range fi {
|
||||
n = append(n, s.Name())
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Stat() (os.FileInfo, error) {
|
||||
return f.f.Stat()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Sync() error {
|
||||
return f.f.Sync()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Truncate(s int64) error {
|
||||
return f.f.Truncate(s)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) WriteString(s string) (int, error) {
|
||||
return f.f.WriteString(s)
|
||||
}
|
||||
274
vendor/github.com/spf13/afero/unionFile.go
generated
vendored
274
vendor/github.com/spf13/afero/unionFile.go
generated
vendored
@@ -1,274 +0,0 @@
|
||||
package afero
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// The UnionFile implements the afero.File interface and will be returned
|
||||
// when reading a directory present at least in the overlay or opening a file
|
||||
// for writing.
|
||||
//
|
||||
// The calls to
|
||||
// Readdir() and Readdirnames() merge the file os.FileInfo / names from the
|
||||
// base and the overlay - for files present in both layers, only those
|
||||
// from the overlay will be used.
|
||||
//
|
||||
// When opening files for writing (Create() / OpenFile() with the right flags)
|
||||
// the operations will be done in both layers, starting with the overlay. A
|
||||
// successful read in the overlay will move the cursor position in the base layer
|
||||
// by the number of bytes read.
|
||||
type UnionFile struct {
|
||||
base File
|
||||
layer File
|
||||
off int
|
||||
files []os.FileInfo
|
||||
}
|
||||
|
||||
func (f *UnionFile) Close() error {
|
||||
// first close base, so we have a newer timestamp in the overlay. If we'd close
|
||||
// the overlay first, we'd get a cacheStale the next time we access this file
|
||||
// -> cache would be useless ;-)
|
||||
if f.base != nil {
|
||||
f.base.Close()
|
||||
}
|
||||
if f.layer != nil {
|
||||
return f.layer.Close()
|
||||
}
|
||||
return BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Read(s []byte) (int, error) {
|
||||
if f.layer != nil {
|
||||
n, err := f.layer.Read(s)
|
||||
if (err == nil || err == io.EOF) && f.base != nil {
|
||||
// advance the file position also in the base file, the next
|
||||
// call may be a write at this position (or a seek with SEEK_CUR)
|
||||
if _, seekErr := f.base.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
|
||||
// only overwrite err in case the seek fails: we need to
|
||||
// report an eventual io.EOF to the caller
|
||||
err = seekErr
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Read(s)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
|
||||
if f.layer != nil {
|
||||
n, err := f.layer.ReadAt(s, o)
|
||||
if (err == nil || err == io.EOF) && f.base != nil {
|
||||
_, err = f.base.Seek(o+int64(n), os.SEEK_SET)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.ReadAt(s, o)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
|
||||
if f.layer != nil {
|
||||
pos, err = f.layer.Seek(o, w)
|
||||
if (err == nil || err == io.EOF) && f.base != nil {
|
||||
_, err = f.base.Seek(o, w)
|
||||
}
|
||||
return pos, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Seek(o, w)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Write(s []byte) (n int, err error) {
|
||||
if f.layer != nil {
|
||||
n, err = f.layer.Write(s)
|
||||
if err == nil && f.base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
|
||||
_, err = f.base.Write(s)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Write(s)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
|
||||
if f.layer != nil {
|
||||
n, err = f.layer.WriteAt(s, o)
|
||||
if err == nil && f.base != nil {
|
||||
_, err = f.base.WriteAt(s, o)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.WriteAt(s, o)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Name() string {
|
||||
if f.layer != nil {
|
||||
return f.layer.Name()
|
||||
}
|
||||
return f.base.Name()
|
||||
}
|
||||
|
||||
// Readdir will weave the two directories together and
|
||||
// return a single view of the overlayed directories
|
||||
func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
|
||||
if f.off == 0 {
|
||||
var files = make(map[string]os.FileInfo)
|
||||
var rfi []os.FileInfo
|
||||
if f.layer != nil {
|
||||
rfi, err = f.layer.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, fi := range rfi {
|
||||
files[fi.Name()] = fi
|
||||
}
|
||||
}
|
||||
|
||||
if f.base != nil {
|
||||
rfi, err = f.base.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, fi := range rfi {
|
||||
if _, exists := files[fi.Name()]; !exists {
|
||||
files[fi.Name()] = fi
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, fi := range files {
|
||||
f.files = append(f.files, fi)
|
||||
}
|
||||
}
|
||||
if c == -1 {
|
||||
return f.files[f.off:], nil
|
||||
}
|
||||
defer func() { f.off += c }()
|
||||
return f.files[f.off:c], nil
|
||||
}
|
||||
|
||||
func (f *UnionFile) Readdirnames(c int) ([]string, error) {
|
||||
rfi, err := f.Readdir(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var names []string
|
||||
for _, fi := range rfi {
|
||||
names = append(names, fi.Name())
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (f *UnionFile) Stat() (os.FileInfo, error) {
|
||||
if f.layer != nil {
|
||||
return f.layer.Stat()
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Stat()
|
||||
}
|
||||
return nil, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Sync() (err error) {
|
||||
if f.layer != nil {
|
||||
err = f.layer.Sync()
|
||||
if err == nil && f.base != nil {
|
||||
err = f.base.Sync()
|
||||
}
|
||||
return err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Sync()
|
||||
}
|
||||
return BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Truncate(s int64) (err error) {
|
||||
if f.layer != nil {
|
||||
err = f.layer.Truncate(s)
|
||||
if err == nil && f.base != nil {
|
||||
err = f.base.Truncate(s)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Truncate(s)
|
||||
}
|
||||
return BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) WriteString(s string) (n int, err error) {
|
||||
if f.layer != nil {
|
||||
n, err = f.layer.WriteString(s)
|
||||
if err == nil && f.base != nil {
|
||||
_, err = f.base.WriteString(s)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.WriteString(s)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func copyToLayer(base Fs, layer Fs, name string) error {
|
||||
bfh, err := base.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer bfh.Close()
|
||||
|
||||
// First make sure the directory exists
|
||||
exists, err := Exists(layer, filepath.Dir(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create the file on the overlay
|
||||
lfh, err := layer.Create(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := io.Copy(lfh, bfh)
|
||||
if err != nil {
|
||||
// If anything fails, clean up the file
|
||||
layer.Remove(name)
|
||||
lfh.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
bfi, err := bfh.Stat()
|
||||
if err != nil || bfi.Size() != n {
|
||||
layer.Remove(name)
|
||||
lfh.Close()
|
||||
return syscall.EIO
|
||||
}
|
||||
|
||||
err = lfh.Close()
|
||||
if err != nil {
|
||||
layer.Remove(name)
|
||||
lfh.Close()
|
||||
return err
|
||||
}
|
||||
return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
|
||||
}
|
||||
331
vendor/github.com/spf13/afero/util.go
generated
vendored
331
vendor/github.com/spf13/afero/util.go
generated
vendored
@@ -1,331 +0,0 @@
|
||||
// Copyright ©2015 Steve Francia <spf@spf13.com>
|
||||
// Portions Copyright ©2015 The Hugo Authors
|
||||
// Portions Copyright 2016-present Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
|
||||
//
|
||||
// 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 afero
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// Filepath separator defined by os.Separator.
|
||||
const FilePathSeparator = string(filepath.Separator)
|
||||
|
||||
// Takes a reader and a path and writes the content
|
||||
func (a Afero) WriteReader(path string, r io.Reader) (err error) {
|
||||
return WriteReader(a.Fs, path, r)
|
||||
}
|
||||
|
||||
func WriteReader(fs Fs, path string, r io.Reader) (err error) {
|
||||
dir, _ := filepath.Split(path)
|
||||
ospath := filepath.FromSlash(dir)
|
||||
|
||||
if ospath != "" {
|
||||
err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
|
||||
if err != nil {
|
||||
if err != os.ErrExist {
|
||||
log.Panicln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file, err := fs.Create(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(file, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Same as WriteReader but checks to see if file/directory already exists.
|
||||
func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {
|
||||
return SafeWriteReader(a.Fs, path, r)
|
||||
}
|
||||
|
||||
func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
|
||||
dir, _ := filepath.Split(path)
|
||||
ospath := filepath.FromSlash(dir)
|
||||
|
||||
if ospath != "" {
|
||||
err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
exists, err := Exists(fs, path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("%v already exists", path)
|
||||
}
|
||||
|
||||
file, err := fs.Create(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(file, r)
|
||||
return
|
||||
}
|
||||
|
||||
func (a Afero) GetTempDir(subPath string) string {
|
||||
return GetTempDir(a.Fs, subPath)
|
||||
}
|
||||
|
||||
// GetTempDir returns the default temp directory with trailing slash
|
||||
// if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx
|
||||
func GetTempDir(fs Fs, subPath string) string {
|
||||
addSlash := func(p string) string {
|
||||
if FilePathSeparator != p[len(p)-1:] {
|
||||
p = p + FilePathSeparator
|
||||
}
|
||||
return p
|
||||
}
|
||||
dir := addSlash(os.TempDir())
|
||||
|
||||
if subPath != "" {
|
||||
// preserve windows backslash :-(
|
||||
if FilePathSeparator == "\\" {
|
||||
subPath = strings.Replace(subPath, "\\", "____", -1)
|
||||
}
|
||||
dir = dir + UnicodeSanitize((subPath))
|
||||
if FilePathSeparator == "\\" {
|
||||
dir = strings.Replace(dir, "____", "\\", -1)
|
||||
}
|
||||
|
||||
if exists, _ := Exists(fs, dir); exists {
|
||||
return addSlash(dir)
|
||||
}
|
||||
|
||||
err := fs.MkdirAll(dir, 0777)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dir = addSlash(dir)
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
// Rewrite string to remove non-standard path characters
|
||||
func UnicodeSanitize(s string) string {
|
||||
source := []rune(s)
|
||||
target := make([]rune, 0, len(source))
|
||||
|
||||
for _, r := range source {
|
||||
if unicode.IsLetter(r) ||
|
||||
unicode.IsDigit(r) ||
|
||||
unicode.IsMark(r) ||
|
||||
r == '.' ||
|
||||
r == '/' ||
|
||||
r == '\\' ||
|
||||
r == '_' ||
|
||||
r == '-' ||
|
||||
r == '%' ||
|
||||
r == ' ' ||
|
||||
r == '#' {
|
||||
target = append(target, r)
|
||||
}
|
||||
}
|
||||
|
||||
return string(target)
|
||||
}
|
||||
|
||||
// Transform characters with accents into plan forms
|
||||
func NeuterAccents(s string) string {
|
||||
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
|
||||
result, _, _ := transform.String(t, string(s))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func isMn(r rune) bool {
|
||||
return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
|
||||
}
|
||||
|
||||
func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
|
||||
return FileContainsBytes(a.Fs, filename, subslice)
|
||||
}
|
||||
|
||||
// Check if a file contains a specified byte slice.
|
||||
func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
|
||||
f, err := fs.Open(filename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return readerContainsAny(f, subslice), nil
|
||||
}
|
||||
|
||||
func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
|
||||
return FileContainsAnyBytes(a.Fs, filename, subslices)
|
||||
}
|
||||
|
||||
// Check if a file contains any of the specified byte slices.
|
||||
func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
|
||||
f, err := fs.Open(filename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return readerContainsAny(f, subslices...), nil
|
||||
}
|
||||
|
||||
// readerContains reports whether any of the subslices is within r.
|
||||
func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
|
||||
|
||||
if r == nil || len(subslices) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
largestSlice := 0
|
||||
|
||||
for _, sl := range subslices {
|
||||
if len(sl) > largestSlice {
|
||||
largestSlice = len(sl)
|
||||
}
|
||||
}
|
||||
|
||||
if largestSlice == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
bufflen := largestSlice * 4
|
||||
halflen := bufflen / 2
|
||||
buff := make([]byte, bufflen)
|
||||
var err error
|
||||
var n, i int
|
||||
|
||||
for {
|
||||
i++
|
||||
if i == 1 {
|
||||
n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
|
||||
} else {
|
||||
if i != 2 {
|
||||
// shift left to catch overlapping matches
|
||||
copy(buff[:], buff[halflen:])
|
||||
}
|
||||
n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
for _, sl := range subslices {
|
||||
if bytes.Contains(buff, sl) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a Afero) DirExists(path string) (bool, error) {
|
||||
return DirExists(a.Fs, path)
|
||||
}
|
||||
|
||||
// DirExists checks if a path exists and is a directory.
|
||||
func DirExists(fs Fs, path string) (bool, error) {
|
||||
fi, err := fs.Stat(path)
|
||||
if err == nil && fi.IsDir() {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (a Afero) IsDir(path string) (bool, error) {
|
||||
return IsDir(a.Fs, path)
|
||||
}
|
||||
|
||||
// IsDir checks if a given path is a directory.
|
||||
func IsDir(fs Fs, path string) (bool, error) {
|
||||
fi, err := fs.Stat(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return fi.IsDir(), nil
|
||||
}
|
||||
|
||||
func (a Afero) IsEmpty(path string) (bool, error) {
|
||||
return IsEmpty(a.Fs, path)
|
||||
}
|
||||
|
||||
// IsEmpty checks if a given file or directory is empty.
|
||||
func IsEmpty(fs Fs, path string) (bool, error) {
|
||||
if b, _ := Exists(fs, path); !b {
|
||||
return false, fmt.Errorf("%q path does not exist", path)
|
||||
}
|
||||
fi, err := fs.Stat(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
f, err := fs.Open(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
list, err := f.Readdir(-1)
|
||||
return len(list) == 0, nil
|
||||
}
|
||||
return fi.Size() == 0, nil
|
||||
}
|
||||
|
||||
func (a Afero) Exists(path string) (bool, error) {
|
||||
return Exists(a.Fs, path)
|
||||
}
|
||||
|
||||
// Check if a file or directory exists.
|
||||
func Exists(fs Fs, path string) (bool, error) {
|
||||
_, err := fs.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
|
||||
combinedPath := filepath.Join(basePathFs.path, relativePath)
|
||||
if parent, ok := basePathFs.source.(*BasePathFs); ok {
|
||||
return FullBaseFsPath(parent, combinedPath)
|
||||
}
|
||||
|
||||
return combinedPath
|
||||
}
|
||||
174
vendor/github.com/spf13/cobra/LICENSE.txt
generated
vendored
174
vendor/github.com/spf13/cobra/LICENSE.txt
generated
vendored
@@ -1,174 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
910
vendor/github.com/spf13/cobra/README.md
generated
vendored
910
vendor/github.com/spf13/cobra/README.md
generated
vendored
@@ -1,910 +0,0 @@
|
||||

|
||||
|
||||
Cobra is both a library for creating powerful modern CLI applications as well as a program to generate applications and command files.
|
||||
|
||||
Many of the most widely used Go projects are built using Cobra including:
|
||||
|
||||
* [Kubernetes](http://kubernetes.io/)
|
||||
* [Hugo](http://gohugo.io)
|
||||
* [rkt](https://github.com/coreos/rkt)
|
||||
* [etcd](https://github.com/coreos/etcd)
|
||||
* [Docker](https://github.com/docker/docker)
|
||||
* [Docker (distribution)](https://github.com/docker/distribution)
|
||||
* [OpenShift](https://www.openshift.com/)
|
||||
* [Delve](https://github.com/derekparker/delve)
|
||||
* [GopherJS](http://www.gopherjs.org/)
|
||||
* [CockroachDB](http://www.cockroachlabs.com/)
|
||||
* [Bleve](http://www.blevesearch.com/)
|
||||
* [ProjectAtomic (enterprise)](http://www.projectatomic.io/)
|
||||
* [Parse (CLI)](https://parse.com/)
|
||||
* [GiantSwarm's swarm](https://github.com/giantswarm/cli)
|
||||
* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
|
||||
|
||||
|
||||
[](https://travis-ci.org/spf13/cobra)
|
||||
[](https://circleci.com/gh/spf13/cobra)
|
||||
[](https://godoc.org/github.com/spf13/cobra)
|
||||
|
||||

|
||||
|
||||
# Overview
|
||||
|
||||
Cobra is a library providing a simple interface to create powerful modern CLI
|
||||
interfaces similar to git & go tools.
|
||||
|
||||
Cobra is also an application that will generate your application scaffolding to rapidly
|
||||
develop a Cobra-based application.
|
||||
|
||||
Cobra provides:
|
||||
* Easy subcommand-based CLIs: `app server`, `app fetch`, etc.
|
||||
* Fully POSIX-compliant flags (including short & long versions)
|
||||
* Nested subcommands
|
||||
* Global, local and cascading flags
|
||||
* Easy generation of applications & commands with `cobra create appname` & `cobra add cmdname`
|
||||
* Intelligent suggestions (`app srver`... did you mean `app server`?)
|
||||
* Automatic help generation for commands and flags
|
||||
* Automatic detailed help for `app help [command]`
|
||||
* Automatic help flag recognition of `-h`, `--help`, etc.
|
||||
* Automatically generated bash autocomplete for your application
|
||||
* Automatically generated man pages for your application
|
||||
* Command aliases so you can change things without breaking them
|
||||
* The flexibilty to define your own help, usage, etc.
|
||||
* Optional tight integration with [viper](http://github.com/spf13/viper) for 12-factor apps
|
||||
|
||||
Cobra has an exceptionally clean interface and simple design without needless
|
||||
constructors or initialization methods.
|
||||
|
||||
Applications built with Cobra commands are designed to be as user-friendly as
|
||||
possible. Flags can be placed before or after the command (as long as a
|
||||
confusing space isn’t provided). Both short and long flags can be used. A
|
||||
command need not even be fully typed. Help is automatically generated and
|
||||
available for the application or for a specific command using either the help
|
||||
command or the `--help` flag.
|
||||
|
||||
# Concepts
|
||||
|
||||
Cobra is built on a structure of commands, arguments & flags.
|
||||
|
||||
**Commands** represent actions, **Args** are things and **Flags** are modifiers for those actions.
|
||||
|
||||
The best applications will read like sentences when used. Users will know how
|
||||
to use the application because they will natively understand how to use it.
|
||||
|
||||
The pattern to follow is
|
||||
`APPNAME VERB NOUN --ADJECTIVE.`
|
||||
or
|
||||
`APPNAME COMMAND ARG --FLAG`
|
||||
|
||||
A few good real world examples may better illustrate this point.
|
||||
|
||||
In the following example, 'server' is a command, and 'port' is a flag:
|
||||
|
||||
> hugo server --port=1313
|
||||
|
||||
In this command we are telling Git to clone the url bare.
|
||||
|
||||
> git clone URL --bare
|
||||
|
||||
## Commands
|
||||
|
||||
Command is the central point of the application. Each interaction that
|
||||
the application supports will be contained in a Command. A command can
|
||||
have children commands and optionally run an action.
|
||||
|
||||
In the example above, 'server' is the command.
|
||||
|
||||
A Command has the following structure:
|
||||
|
||||
```go
|
||||
type Command struct {
|
||||
Use string // The one-line usage message.
|
||||
Short string // The short description shown in the 'help' output.
|
||||
Long string // The long message shown in the 'help <this-command>' output.
|
||||
Run func(cmd *Command, args []string) // Run runs the command.
|
||||
}
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
A Flag is a way to modify the behavior of a command. Cobra supports
|
||||
fully POSIX-compliant flags as well as the Go [flag package](https://golang.org/pkg/flag/).
|
||||
A Cobra command can define flags that persist through to children commands
|
||||
and flags that are only available to that command.
|
||||
|
||||
In the example above, 'port' is the flag.
|
||||
|
||||
Flag functionality is provided by the [pflag
|
||||
library](https://github.com/ogier/pflag), a fork of the flag standard library
|
||||
which maintains the same interface while adding POSIX compliance.
|
||||
|
||||
## Usage
|
||||
|
||||
Cobra works by creating a set of commands and then organizing them into a tree.
|
||||
The tree defines the structure of the application.
|
||||
|
||||
Once each command is defined with its corresponding flags, then the
|
||||
tree is assigned to the commander which is finally executed.
|
||||
|
||||
# Installing
|
||||
Using Cobra is easy. First, use `go get` to install the latest version
|
||||
of the library. This command will install the `cobra` generator executible
|
||||
along with the library:
|
||||
|
||||
> go get -v github.com/spf13/cobra/cobra
|
||||
|
||||
Next, include Cobra in your application:
|
||||
|
||||
```go
|
||||
import "github.com/spf13/cobra"
|
||||
```
|
||||
|
||||
# Getting Started
|
||||
|
||||
While you are welcome to provide your own organization, typically a Cobra based
|
||||
application will follow the following organizational structure.
|
||||
|
||||
```
|
||||
▾ appName/
|
||||
▾ cmd/
|
||||
add.go
|
||||
your.go
|
||||
commands.go
|
||||
here.go
|
||||
main.go
|
||||
```
|
||||
|
||||
In a Cobra app, typically the main.go file is very bare. It serves, one purpose, to initialize Cobra.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"{pathToYourApp}/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.RootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using the Cobra Generator
|
||||
|
||||
Cobra provides its own program that will create your application and add any
|
||||
commands you want. It's the easiest way to incorporate Cobra into your application.
|
||||
|
||||
In order to use the cobra command, compile it using the following command:
|
||||
|
||||
> go install github.com/spf13/cobra/cobra
|
||||
|
||||
This will create the cobra executable under your go path bin directory!
|
||||
|
||||
### cobra init
|
||||
|
||||
The `cobra init [yourApp]` command will create your initial application code
|
||||
for you. It is a very powerful application that will populate your program with
|
||||
the right structure so you can immediately enjoy all the benefits of Cobra. It
|
||||
will also automatically apply the license you specify to your application.
|
||||
|
||||
Cobra init is pretty smart. You can provide it a full path, or simply a path
|
||||
similar to what is expected in the import.
|
||||
|
||||
```
|
||||
cobra init github.com/spf13/newAppName
|
||||
```
|
||||
|
||||
### cobra add
|
||||
|
||||
Once an application is initialized Cobra can create additional commands for you.
|
||||
Let's say you created an app and you wanted the following commands for it:
|
||||
|
||||
* app serve
|
||||
* app config
|
||||
* app config create
|
||||
|
||||
In your project directory (where your main.go file is) you would run the following:
|
||||
|
||||
```
|
||||
cobra add serve
|
||||
cobra add config
|
||||
cobra add create -p 'configCmd'
|
||||
```
|
||||
|
||||
Once you have run these three commands you would have an app structure that would look like:
|
||||
|
||||
```
|
||||
▾ app/
|
||||
▾ cmd/
|
||||
serve.go
|
||||
config.go
|
||||
create.go
|
||||
main.go
|
||||
```
|
||||
|
||||
at this point you can run `go run main.go` and it would run your app. `go run
|
||||
main.go serve`, `go run main.go config`, `go run main.go config create` along
|
||||
with `go run main.go help serve`, etc would all work.
|
||||
|
||||
Obviously you haven't added your own code to these yet, the commands are ready
|
||||
for you to give them their tasks. Have fun.
|
||||
|
||||
### Configuring the cobra generator
|
||||
|
||||
The cobra generator will be easier to use if you provide a simple configuration
|
||||
file which will help you eliminate providing a bunch of repeated information in
|
||||
flags over and over.
|
||||
|
||||
An example ~/.cobra.yaml file:
|
||||
|
||||
```yaml
|
||||
author: Steve Francia <spf@spf13.com>
|
||||
license: MIT
|
||||
```
|
||||
|
||||
You can specify no license by setting `license` to `none` or you can specify
|
||||
a custom license:
|
||||
|
||||
```yaml
|
||||
license:
|
||||
header: This file is part of {{ .appName }}.
|
||||
text: |
|
||||
{{ .copyright }}
|
||||
|
||||
This is my license. There are many like it, but this one is mine.
|
||||
My license is my best friend. It is my life. I must master it as I must
|
||||
master my life.
|
||||
```
|
||||
|
||||
## Manually implementing Cobra
|
||||
|
||||
To manually implement cobra you need to create a bare main.go file and a RootCmd file.
|
||||
You will optionally provide additional commands as you see fit.
|
||||
|
||||
### Create the root command
|
||||
|
||||
The root command represents your binary itself.
|
||||
|
||||
|
||||
#### Manually create rootCmd
|
||||
|
||||
Cobra doesn't require any special constructors. Simply create your commands.
|
||||
|
||||
Ideally you place this in app/cmd/root.go:
|
||||
|
||||
```go
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "hugo",
|
||||
Short: "Hugo is a very fast static site generator",
|
||||
Long: `A Fast and Flexible Static Site Generator built with
|
||||
love by spf13 and friends in Go.
|
||||
Complete documentation is available at http://hugo.spf13.com`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Do Stuff Here
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
You will additionally define flags and handle configuration in your init() function.
|
||||
|
||||
for example cmd/root.go:
|
||||
|
||||
```go
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
|
||||
RootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/")
|
||||
RootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution")
|
||||
RootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)")
|
||||
RootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration")
|
||||
viper.BindPFlag("author", RootCmd.PersistentFlags().Lookup("author"))
|
||||
viper.BindPFlag("projectbase", RootCmd.PersistentFlags().Lookup("projectbase"))
|
||||
viper.BindPFlag("useViper", RootCmd.PersistentFlags().Lookup("viper"))
|
||||
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
|
||||
viper.SetDefault("license", "apache")
|
||||
}
|
||||
```
|
||||
|
||||
### Create your main.go
|
||||
|
||||
With the root command you need to have your main function execute it.
|
||||
Execute should be run on the root for clarity, though it can be called on any command.
|
||||
|
||||
In a Cobra app, typically the main.go file is very bare. It serves, one purpose, to initialize Cobra.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"{pathToYourApp}/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.RootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Create additional commands
|
||||
|
||||
Additional commands can be defined and typically are each given their own file
|
||||
inside of the cmd/ directory.
|
||||
|
||||
If you wanted to create a version command you would create cmd/version.go and
|
||||
populate it with the following:
|
||||
|
||||
```go
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version number of Hugo",
|
||||
Long: `All software has versions. This is Hugo's`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Attach command to its parent
|
||||
|
||||
|
||||
If you notice in the above example we attach the command to its parent. In
|
||||
this case the parent is the rootCmd. In this example we are attaching it to the
|
||||
root, but commands can be attached at any level.
|
||||
|
||||
```go
|
||||
RootCmd.AddCommand(versionCmd)
|
||||
```
|
||||
|
||||
### Remove a command from its parent
|
||||
|
||||
Removing a command is not a common action in simple programs, but it allows 3rd
|
||||
parties to customize an existing command tree.
|
||||
|
||||
In this example, we remove the existing `VersionCmd` command of an existing
|
||||
root command, and we replace it with our own version:
|
||||
|
||||
```go
|
||||
mainlib.RootCmd.RemoveCommand(mainlib.VersionCmd)
|
||||
mainlib.RootCmd.AddCommand(versionCmd)
|
||||
```
|
||||
|
||||
## Working with Flags
|
||||
|
||||
Flags provide modifiers to control how the action command operates.
|
||||
|
||||
### Assign flags to a command
|
||||
|
||||
Since the flags are defined and used in different locations, we need to
|
||||
define a variable outside with the correct scope to assign the flag to
|
||||
work with.
|
||||
|
||||
```go
|
||||
var Verbose bool
|
||||
var Source string
|
||||
```
|
||||
|
||||
There are two different approaches to assign a flag.
|
||||
|
||||
### Persistent Flags
|
||||
|
||||
A flag can be 'persistent' meaning that this flag will be available to the
|
||||
command it's assigned to as well as every command under that command. For
|
||||
global flags, assign a flag as a persistent flag on the root.
|
||||
|
||||
```go
|
||||
RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
|
||||
```
|
||||
|
||||
### Local Flags
|
||||
|
||||
A flag can also be assigned locally which will only apply to that specific command.
|
||||
|
||||
```go
|
||||
RootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
In the example below, we have defined three commands. Two are at the top level
|
||||
and one (cmdTimes) is a child of one of the top commands. In this case the root
|
||||
is not executable meaning that a subcommand is required. This is accomplished
|
||||
by not providing a 'Run' for the 'rootCmd'.
|
||||
|
||||
We have only defined one flag for a single command.
|
||||
|
||||
More documentation about flags is available at https://github.com/spf13/pflag
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var echoTimes int
|
||||
|
||||
var cmdPrint = &cobra.Command{
|
||||
Use: "print [string to print]",
|
||||
Short: "Print anything to the screen",
|
||||
Long: `print is for printing anything back to the screen.
|
||||
For many years people have printed back to the screen.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Print: " + strings.Join(args, " "))
|
||||
},
|
||||
}
|
||||
|
||||
var cmdEcho = &cobra.Command{
|
||||
Use: "echo [string to echo]",
|
||||
Short: "Echo anything to the screen",
|
||||
Long: `echo is for echoing anything back.
|
||||
Echo works a lot like print, except it has a child command.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Print: " + strings.Join(args, " "))
|
||||
},
|
||||
}
|
||||
|
||||
var cmdTimes = &cobra.Command{
|
||||
Use: "times [# times] [string to echo]",
|
||||
Short: "Echo anything to the screen more times",
|
||||
Long: `echo things multiple times back to the user by providing
|
||||
a count and a string.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
for i := 0; i < echoTimes; i++ {
|
||||
fmt.Println("Echo: " + strings.Join(args, " "))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "app"}
|
||||
rootCmd.AddCommand(cmdPrint, cmdEcho)
|
||||
cmdEcho.AddCommand(cmdTimes)
|
||||
rootCmd.Execute()
|
||||
}
|
||||
```
|
||||
|
||||
For a more complete example of a larger application, please checkout [Hugo](http://gohugo.io/).
|
||||
|
||||
## The Help Command
|
||||
|
||||
Cobra automatically adds a help command to your application when you have subcommands.
|
||||
This will be called when a user runs 'app help'. Additionally, help will also
|
||||
support all other commands as input. Say, for instance, you have a command called
|
||||
'create' without any additional configuration; Cobra will work when 'app help
|
||||
create' is called. Every command will automatically have the '--help' flag added.
|
||||
|
||||
### Example
|
||||
|
||||
The following output is automatically generated by Cobra. Nothing beyond the
|
||||
command and flag definitions are needed.
|
||||
|
||||
> hugo help
|
||||
|
||||
hugo is the main command, used to build your Hugo site.
|
||||
|
||||
Hugo is a Fast and Flexible Static Site Generator
|
||||
built with love by spf13 and friends in Go.
|
||||
|
||||
Complete documentation is available at http://gohugo.io/.
|
||||
|
||||
Usage:
|
||||
hugo [flags]
|
||||
hugo [command]
|
||||
|
||||
Available Commands:
|
||||
server Hugo runs its own webserver to render the files
|
||||
version Print the version number of Hugo
|
||||
config Print the site configuration
|
||||
check Check content in the source directory
|
||||
benchmark Benchmark hugo by building a site a number of times.
|
||||
convert Convert your content to different formats
|
||||
new Create new content for your site
|
||||
list Listing out various types of content
|
||||
undraft Undraft changes the content's draft status from 'True' to 'False'
|
||||
genautocomplete Generate shell autocompletion script for Hugo
|
||||
gendoc Generate Markdown documentation for the Hugo CLI.
|
||||
genman Generate man page for Hugo
|
||||
import Import your site from others.
|
||||
|
||||
Flags:
|
||||
-b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/
|
||||
-D, --buildDrafts[=false]: include content marked as draft
|
||||
-F, --buildFuture[=false]: include content with publishdate in the future
|
||||
--cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/
|
||||
--canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL
|
||||
--config="": config file (default is path/config.yaml|json|toml)
|
||||
-d, --destination="": filesystem path to write files to
|
||||
--disableRSS[=false]: Do not build RSS files
|
||||
--disableSitemap[=false]: Do not build Sitemap file
|
||||
--editor="": edit new content with this editor, if provided
|
||||
--ignoreCache[=false]: Ignores the cache directory for reading but still writes to it
|
||||
--log[=false]: Enable Logging
|
||||
--logFile="": Log File path (if set, logging enabled automatically)
|
||||
--noTimes[=false]: Don't sync modification time of files
|
||||
--pluralizeListTitles[=true]: Pluralize titles in lists using inflect
|
||||
--preserveTaxonomyNames[=false]: Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
-s, --source="": filesystem path to read files relative from
|
||||
--stepAnalysis[=false]: display memory and timing of different steps of the program
|
||||
-t, --theme="": theme to use (located in /themes/THEMENAME/)
|
||||
--uglyURLs[=false]: if true, use /filename.html instead of /filename/
|
||||
-v, --verbose[=false]: verbose output
|
||||
--verboseLog[=false]: verbose logging
|
||||
-w, --watch[=false]: watch filesystem for changes and recreate as needed
|
||||
|
||||
Use "hugo [command] --help" for more information about a command.
|
||||
|
||||
|
||||
Help is just a command like any other. There is no special logic or behavior
|
||||
around it. In fact, you can provide your own if you want.
|
||||
|
||||
### Defining your own help
|
||||
|
||||
You can provide your own Help command or your own template for the default command to use.
|
||||
|
||||
The default help command is
|
||||
|
||||
```go
|
||||
func (c *Command) initHelp() {
|
||||
if c.helpCommand == nil {
|
||||
c.helpCommand = &Command{
|
||||
Use: "help [command]",
|
||||
Short: "Help about any command",
|
||||
Long: `Help provides help for any command in the application.
|
||||
Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
||||
Run: c.HelpFunc(),
|
||||
}
|
||||
}
|
||||
c.AddCommand(c.helpCommand)
|
||||
}
|
||||
```
|
||||
|
||||
You can provide your own command, function or template through the following methods:
|
||||
|
||||
```go
|
||||
command.SetHelpCommand(cmd *Command)
|
||||
|
||||
command.SetHelpFunc(f func(*Command, []string))
|
||||
|
||||
command.SetHelpTemplate(s string)
|
||||
```
|
||||
|
||||
The latter two will also apply to any children commands.
|
||||
|
||||
## Usage
|
||||
|
||||
When the user provides an invalid flag or invalid command, Cobra responds by
|
||||
showing the user the 'usage'.
|
||||
|
||||
### Example
|
||||
You may recognize this from the help above. That's because the default help
|
||||
embeds the usage as part of its output.
|
||||
|
||||
Usage:
|
||||
hugo [flags]
|
||||
hugo [command]
|
||||
|
||||
Available Commands:
|
||||
server Hugo runs its own webserver to render the files
|
||||
version Print the version number of Hugo
|
||||
config Print the site configuration
|
||||
check Check content in the source directory
|
||||
benchmark Benchmark hugo by building a site a number of times.
|
||||
convert Convert your content to different formats
|
||||
new Create new content for your site
|
||||
list Listing out various types of content
|
||||
undraft Undraft changes the content's draft status from 'True' to 'False'
|
||||
genautocomplete Generate shell autocompletion script for Hugo
|
||||
gendoc Generate Markdown documentation for the Hugo CLI.
|
||||
genman Generate man page for Hugo
|
||||
import Import your site from others.
|
||||
|
||||
Flags:
|
||||
-b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/
|
||||
-D, --buildDrafts[=false]: include content marked as draft
|
||||
-F, --buildFuture[=false]: include content with publishdate in the future
|
||||
--cacheDir="": filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/
|
||||
--canonifyURLs[=false]: if true, all relative URLs will be canonicalized using baseURL
|
||||
--config="": config file (default is path/config.yaml|json|toml)
|
||||
-d, --destination="": filesystem path to write files to
|
||||
--disableRSS[=false]: Do not build RSS files
|
||||
--disableSitemap[=false]: Do not build Sitemap file
|
||||
--editor="": edit new content with this editor, if provided
|
||||
--ignoreCache[=false]: Ignores the cache directory for reading but still writes to it
|
||||
--log[=false]: Enable Logging
|
||||
--logFile="": Log File path (if set, logging enabled automatically)
|
||||
--noTimes[=false]: Don't sync modification time of files
|
||||
--pluralizeListTitles[=true]: Pluralize titles in lists using inflect
|
||||
--preserveTaxonomyNames[=false]: Preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
-s, --source="": filesystem path to read files relative from
|
||||
--stepAnalysis[=false]: display memory and timing of different steps of the program
|
||||
-t, --theme="": theme to use (located in /themes/THEMENAME/)
|
||||
--uglyURLs[=false]: if true, use /filename.html instead of /filename/
|
||||
-v, --verbose[=false]: verbose output
|
||||
--verboseLog[=false]: verbose logging
|
||||
-w, --watch[=false]: watch filesystem for changes and recreate as needed
|
||||
|
||||
### Defining your own usage
|
||||
You can provide your own usage function or template for Cobra to use.
|
||||
|
||||
The default usage function is:
|
||||
|
||||
```go
|
||||
return func(c *Command) error {
|
||||
err := tmpl(c.Out(), c.UsageTemplate(), c)
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
Like help, the function and template are overridable through public methods:
|
||||
|
||||
```go
|
||||
command.SetUsageFunc(f func(*Command) error)
|
||||
|
||||
command.SetUsageTemplate(s string)
|
||||
```
|
||||
|
||||
## PreRun or PostRun Hooks
|
||||
|
||||
It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order:
|
||||
|
||||
- `PersistentPreRun`
|
||||
- `PreRun`
|
||||
- `Run`
|
||||
- `PostRun`
|
||||
- `PersistentPostRun`
|
||||
|
||||
An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "root [sub]",
|
||||
Short: "My root command",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
|
||||
},
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd Run with args: %v\n", args)
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
|
||||
},
|
||||
}
|
||||
|
||||
var subCmd = &cobra.Command{
|
||||
Use: "sub [no options!]",
|
||||
Short: "My subcommand",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd PreRun with args: %v\n", args)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd Run with args: %v\n", args)
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd PostRun with args: %v\n", args)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args)
|
||||
},
|
||||
}
|
||||
|
||||
rootCmd.AddCommand(subCmd)
|
||||
|
||||
rootCmd.SetArgs([]string{""})
|
||||
_ = rootCmd.Execute()
|
||||
fmt.Print("\n")
|
||||
rootCmd.SetArgs([]string{"sub", "arg1", "arg2"})
|
||||
_ = rootCmd.Execute()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Alternative Error Handling
|
||||
|
||||
Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top,
|
||||
providing a way to handle the errors in one location. The current list of functions that return an error is:
|
||||
|
||||
* PersistentPreRunE
|
||||
* PreRunE
|
||||
* RunE
|
||||
* PostRunE
|
||||
* PersistentPostRunE
|
||||
|
||||
If you would like to silence the default `error` and `usage` output in favor of your own, you can set `SilenceUsage`
|
||||
and `SilenceErrors` to `true` on the command. A child command respects these flags if they are set on the parent
|
||||
command.
|
||||
|
||||
**Example Usage using RunE:**
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "hugo",
|
||||
Short: "Hugo is a very fast static site generator",
|
||||
Long: `A Fast and Flexible Static Site Generator built with
|
||||
love by spf13 and friends in Go.
|
||||
Complete documentation is available at http://hugo.spf13.com`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Do Stuff Here
|
||||
return errors.New("some random error")
|
||||
},
|
||||
}
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Suggestions when "unknown command" happens
|
||||
|
||||
Cobra will print automatic suggestions when "unknown command" errors happen. This allows Cobra to behave similarly to the `git` command when a typo happens. For example:
|
||||
|
||||
```
|
||||
$ hugo srever
|
||||
Error: unknown command "srever" for "hugo"
|
||||
|
||||
Did you mean this?
|
||||
server
|
||||
|
||||
Run 'hugo --help' for usage.
|
||||
```
|
||||
|
||||
Suggestions are automatic based on every subcommand registered and use an implementation of [Levenshtein distance](http://en.wikipedia.org/wiki/Levenshtein_distance). Every registered command that matches a minimum distance of 2 (ignoring case) will be displayed as a suggestion.
|
||||
|
||||
If you need to disable suggestions or tweak the string distance in your command, use:
|
||||
|
||||
```go
|
||||
command.DisableSuggestions = true
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```go
|
||||
command.SuggestionsMinimumDistance = 1
|
||||
```
|
||||
|
||||
You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but makes sense in your set of commands and for some which you don't want aliases. Example:
|
||||
|
||||
```
|
||||
$ kubectl remove
|
||||
Error: unknown command "remove" for "kubectl"
|
||||
|
||||
Did you mean this?
|
||||
delete
|
||||
|
||||
Run 'kubectl help' for usage.
|
||||
```
|
||||
|
||||
## Generating Markdown-formatted documentation for your command
|
||||
|
||||
Cobra can generate a Markdown-formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](doc/md_docs.md).
|
||||
|
||||
## Generating man pages for your command
|
||||
|
||||
Cobra can generate a man page based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Man Docs](doc/man_docs.md).
|
||||
|
||||
## Generating bash completions for your command
|
||||
|
||||
Cobra can generate a bash-completion file. If you add more information to your command, these completions can be amazingly powerful and flexible. Read more about it in [Bash Completions](bash_completions.md).
|
||||
|
||||
## Debugging
|
||||
|
||||
Cobra provides a ‘DebugFlags’ method on a command which, when called, will print
|
||||
out everything Cobra knows about the flags for each command.
|
||||
|
||||
### Example
|
||||
|
||||
```go
|
||||
command.DebugFlags()
|
||||
```
|
||||
|
||||
## Release Notes
|
||||
* **0.9.0** June 17, 2014
|
||||
* flags can appears anywhere in the args (provided they are unambiguous)
|
||||
* --help prints usage screen for app or command
|
||||
* Prefix matching for commands
|
||||
* Cleaner looking help and usage output
|
||||
* Extensive test suite
|
||||
* **0.8.0** Nov 5, 2013
|
||||
* Reworked interface to remove commander completely
|
||||
* Command now primary structure
|
||||
* No initialization needed
|
||||
* Usage & Help templates & functions definable at any level
|
||||
* Updated Readme
|
||||
* **0.7.0** Sept 24, 2013
|
||||
* Needs more eyes
|
||||
* Test suite
|
||||
* Support for automatic error messages
|
||||
* Support for help command
|
||||
* Support for printing to any io.Writer instead of os.Stderr
|
||||
* Support for persistent flags which cascade down tree
|
||||
* Ready for integration into Hugo
|
||||
* **0.1.0** Sept 3, 2013
|
||||
* Implement first draft
|
||||
|
||||
## Extensions
|
||||
|
||||
Libraries for extending Cobra:
|
||||
|
||||
* [cmdns](https://github.com/gosuri/cmdns): Enables name spacing a command's immediate children. It provides an alternative way to structure subcommands, similar to `heroku apps:create` and `ovrclk clusters:launch`.
|
||||
|
||||
## ToDo
|
||||
* Launch proper documentation site
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
|
||||
## Contributors
|
||||
|
||||
Names in no particular order:
|
||||
|
||||
* [spf13](https://github.com/spf13),
|
||||
[eparis](https://github.com/eparis),
|
||||
[bep](https://github.com/bep), and many more!
|
||||
|
||||
## License
|
||||
|
||||
Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt)
|
||||
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user