migrate to go modules
This commit is contained in:
parent
1dd3fba125
commit
cde96f18bc
63 changed files with 33 additions and 12542 deletions
56
Gopkg.lock
generated
56
Gopkg.lock
generated
|
@ -1,56 +0,0 @@
|
||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:5d72bbcc9c8667b11c3dc3cbe681c5a6f71e5096744c0bf7726ab5c6425d5dc4"
|
|
||||||
name = "github.com/BurntSushi/toml"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:f3793f8a708522400cef1dba23385e901aede5519f68971fd69938ef330b07a1"
|
|
||||||
name = "github.com/alecthomas/template"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"parse",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:fdd419e104ec26bb5bd63cc62637c640453ed2929a7453f3afadbd9a0223da66"
|
|
||||||
name = "github.com/alecthomas/units"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:9c94adb84080287919cda31447549e0ce8f8cc2067e19ce51537cf4572868d36"
|
|
||||||
name = "github.com/andrew-d/go-termutil"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "009166a695a2f516c749a26b4ac1f183d89aa336"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:22b2dee6f30bc8601f087449a2a819df8388e54e9547349c658f14d8f8c590f2"
|
|
||||||
name = "gopkg.in/alecthomas/kingpin.v2"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "947dcec5ba9c011838740e680966fd7087a71d0d"
|
|
||||||
version = "v2.2.6"
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
input-imports = [
|
|
||||||
"github.com/BurntSushi/toml",
|
|
||||||
"github.com/andrew-d/go-termutil",
|
|
||||||
"gopkg.in/alecthomas/kingpin.v2",
|
|
||||||
]
|
|
||||||
solver-name = "gps-cdcl"
|
|
||||||
solver-version = 1
|
|
16
Gopkg.toml
16
Gopkg.toml
|
@ -1,16 +0,0 @@
|
||||||
[prune]
|
|
||||||
go-tests = true
|
|
||||||
non-go = true
|
|
||||||
unused-packages = true
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/BurntSushi/toml"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/andrew-d/go-termutil"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "gopkg.in/alecthomas/kingpin.v2"
|
|
||||||
version = "2.2.6"
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016-2019 Martin Lindhe
|
Copyright (c) 2016-2021 Martin Lindhe
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -1,7 +1,2 @@
|
||||||
update-vendor:
|
|
||||||
rm -rf vendor
|
|
||||||
dep ensure
|
|
||||||
dep ensure -update
|
|
||||||
|
|
||||||
release:
|
release:
|
||||||
goreleaser --rm-dist
|
goreleaser --rm-dist
|
||||||
|
|
12
go.mod
Normal file
12
go.mod
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module github.com/martinlindhe/validtoml
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v0.3.1
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
|
||||||
|
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2
|
||||||
|
github.com/stretchr/testify v1.7.0 // indirect
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||||
|
)
|
20
go.sum
Normal file
20
go.sum
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZOs7ygH5BgQp4N+aYrZ2DNpWZ1KG3VOSOM=
|
||||||
|
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os=
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
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.
|
|
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
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.
|
|
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
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.
|
|
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
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.
|
|
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
|
@ -1,509 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func e(format string, args ...interface{}) error {
|
|
||||||
return fmt.Errorf("toml: "+format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaler is the interface implemented by objects that can unmarshal a
|
|
||||||
// TOML description of themselves.
|
|
||||||
type Unmarshaler interface {
|
|
||||||
UnmarshalTOML(interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
|
|
||||||
func Unmarshal(p []byte, v interface{}) error {
|
|
||||||
_, err := Decode(string(p), v)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primitive is a TOML value that hasn't been decoded into a Go value.
|
|
||||||
// When using the various `Decode*` functions, the type `Primitive` may
|
|
||||||
// be given to any value, and its decoding will be delayed.
|
|
||||||
//
|
|
||||||
// A `Primitive` value can be decoded using the `PrimitiveDecode` function.
|
|
||||||
//
|
|
||||||
// The underlying representation of a `Primitive` value is subject to change.
|
|
||||||
// Do not rely on it.
|
|
||||||
//
|
|
||||||
// N.B. Primitive values are still parsed, so using them will only avoid
|
|
||||||
// the overhead of reflection. They can be useful when you don't know the
|
|
||||||
// exact type of TOML data until run time.
|
|
||||||
type Primitive struct {
|
|
||||||
undecoded interface{}
|
|
||||||
context Key
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEPRECATED!
|
|
||||||
//
|
|
||||||
// Use MetaData.PrimitiveDecode instead.
|
|
||||||
func PrimitiveDecode(primValue Primitive, v interface{}) error {
|
|
||||||
md := MetaData{decoded: make(map[string]bool)}
|
|
||||||
return md.unify(primValue.undecoded, rvalue(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrimitiveDecode is just like the other `Decode*` functions, except it
|
|
||||||
// decodes a TOML value that has already been parsed. Valid primitive values
|
|
||||||
// can *only* be obtained from values filled by the decoder functions,
|
|
||||||
// including this method. (i.e., `v` may contain more `Primitive`
|
|
||||||
// values.)
|
|
||||||
//
|
|
||||||
// Meta data for primitive values is included in the meta data returned by
|
|
||||||
// the `Decode*` functions with one exception: keys returned by the Undecoded
|
|
||||||
// method will only reflect keys that were decoded. Namely, any keys hidden
|
|
||||||
// behind a Primitive will be considered undecoded. Executing this method will
|
|
||||||
// update the undecoded keys in the meta data. (See the example.)
|
|
||||||
func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
|
|
||||||
md.context = primValue.context
|
|
||||||
defer func() { md.context = nil }()
|
|
||||||
return md.unify(primValue.undecoded, rvalue(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode will decode the contents of `data` in TOML format into a pointer
|
|
||||||
// `v`.
|
|
||||||
//
|
|
||||||
// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
|
|
||||||
// used interchangeably.)
|
|
||||||
//
|
|
||||||
// TOML arrays of tables correspond to either a slice of structs or a slice
|
|
||||||
// of maps.
|
|
||||||
//
|
|
||||||
// TOML datetimes correspond to Go `time.Time` values.
|
|
||||||
//
|
|
||||||
// All other TOML types (float, string, int, bool and array) correspond
|
|
||||||
// to the obvious Go types.
|
|
||||||
//
|
|
||||||
// An exception to the above rules is if a type implements the
|
|
||||||
// encoding.TextUnmarshaler interface. In this case, any primitive TOML value
|
|
||||||
// (floats, strings, integers, booleans and datetimes) will be converted to
|
|
||||||
// a byte string and given to the value's UnmarshalText method. See the
|
|
||||||
// Unmarshaler example for a demonstration with time duration strings.
|
|
||||||
//
|
|
||||||
// Key mapping
|
|
||||||
//
|
|
||||||
// TOML keys can map to either keys in a Go map or field names in a Go
|
|
||||||
// struct. The special `toml` struct tag may be used to map TOML keys to
|
|
||||||
// struct fields that don't match the key name exactly. (See the example.)
|
|
||||||
// A case insensitive match to struct names will be tried if an exact match
|
|
||||||
// can't be found.
|
|
||||||
//
|
|
||||||
// The mapping between TOML values and Go values is loose. That is, there
|
|
||||||
// may exist TOML values that cannot be placed into your representation, and
|
|
||||||
// there may be parts of your representation that do not correspond to
|
|
||||||
// TOML values. This loose mapping can be made stricter by using the IsDefined
|
|
||||||
// and/or Undecoded methods on the MetaData returned.
|
|
||||||
//
|
|
||||||
// This decoder will not handle cyclic types. If a cyclic type is passed,
|
|
||||||
// `Decode` will not terminate.
|
|
||||||
func Decode(data string, v interface{}) (MetaData, error) {
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if rv.Kind() != reflect.Ptr {
|
|
||||||
return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
|
|
||||||
}
|
|
||||||
if rv.IsNil() {
|
|
||||||
return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
|
|
||||||
}
|
|
||||||
p, err := parse(data)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
md := MetaData{
|
|
||||||
p.mapping, p.types, p.ordered,
|
|
||||||
make(map[string]bool, len(p.ordered)), nil,
|
|
||||||
}
|
|
||||||
return md, md.unify(p.mapping, indirect(rv))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFile is just like Decode, except it will automatically read the
|
|
||||||
// contents of the file at `fpath` and decode it for you.
|
|
||||||
func DecodeFile(fpath string, v interface{}) (MetaData, error) {
|
|
||||||
bs, err := ioutil.ReadFile(fpath)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
return Decode(string(bs), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeReader is just like Decode, except it will consume all bytes
|
|
||||||
// from the reader and decode it for you.
|
|
||||||
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
|
|
||||||
bs, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
return Decode(string(bs), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unify performs a sort of type unification based on the structure of `rv`,
|
|
||||||
// which is the client representation.
|
|
||||||
//
|
|
||||||
// Any type mismatch produces an error. Finding a type that we don't know
|
|
||||||
// how to handle produces an unsupported type error.
|
|
||||||
func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
|
|
||||||
|
|
||||||
// Special case. Look for a `Primitive` value.
|
|
||||||
if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
|
|
||||||
// Save the undecoded data and the key context into the primitive
|
|
||||||
// value.
|
|
||||||
context := make(Key, len(md.context))
|
|
||||||
copy(context, md.context)
|
|
||||||
rv.Set(reflect.ValueOf(Primitive{
|
|
||||||
undecoded: data,
|
|
||||||
context: context,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Unmarshaler Interface support.
|
|
||||||
if rv.CanAddr() {
|
|
||||||
if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
|
|
||||||
return v.UnmarshalTOML(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Handle time.Time values specifically.
|
|
||||||
// TODO: Remove this code when we decide to drop support for Go 1.1.
|
|
||||||
// This isn't necessary in Go 1.2 because time.Time satisfies the encoding
|
|
||||||
// interfaces.
|
|
||||||
if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) {
|
|
||||||
return md.unifyDatetime(data, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Look for a value satisfying the TextUnmarshaler interface.
|
|
||||||
if v, ok := rv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return md.unifyText(data, v)
|
|
||||||
}
|
|
||||||
// BUG(burntsushi)
|
|
||||||
// The behavior here is incorrect whenever a Go type satisfies the
|
|
||||||
// encoding.TextUnmarshaler interface but also corresponds to a TOML
|
|
||||||
// hash or array. In particular, the unmarshaler should only be applied
|
|
||||||
// to primitive TOML values. But at this point, it will be applied to
|
|
||||||
// all kinds of values and produce an incorrect error whenever those values
|
|
||||||
// are hashes or arrays (including arrays of tables).
|
|
||||||
|
|
||||||
k := rv.Kind()
|
|
||||||
|
|
||||||
// laziness
|
|
||||||
if k >= reflect.Int && k <= reflect.Uint64 {
|
|
||||||
return md.unifyInt(data, rv)
|
|
||||||
}
|
|
||||||
switch k {
|
|
||||||
case reflect.Ptr:
|
|
||||||
elem := reflect.New(rv.Type().Elem())
|
|
||||||
err := md.unify(data, reflect.Indirect(elem))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rv.Set(elem)
|
|
||||||
return nil
|
|
||||||
case reflect.Struct:
|
|
||||||
return md.unifyStruct(data, rv)
|
|
||||||
case reflect.Map:
|
|
||||||
return md.unifyMap(data, rv)
|
|
||||||
case reflect.Array:
|
|
||||||
return md.unifyArray(data, rv)
|
|
||||||
case reflect.Slice:
|
|
||||||
return md.unifySlice(data, rv)
|
|
||||||
case reflect.String:
|
|
||||||
return md.unifyString(data, rv)
|
|
||||||
case reflect.Bool:
|
|
||||||
return md.unifyBool(data, rv)
|
|
||||||
case reflect.Interface:
|
|
||||||
// we only support empty interfaces.
|
|
||||||
if rv.NumMethod() > 0 {
|
|
||||||
return e("unsupported type %s", rv.Type())
|
|
||||||
}
|
|
||||||
return md.unifyAnything(data, rv)
|
|
||||||
case reflect.Float32:
|
|
||||||
fallthrough
|
|
||||||
case reflect.Float64:
|
|
||||||
return md.unifyFloat64(data, rv)
|
|
||||||
}
|
|
||||||
return e("unsupported type %s", rv.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
|
|
||||||
tmap, ok := mapping.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
if mapping == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return e("type mismatch for %s: expected table but found %T",
|
|
||||||
rv.Type().String(), mapping)
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, datum := range tmap {
|
|
||||||
var f *field
|
|
||||||
fields := cachedTypeFields(rv.Type())
|
|
||||||
for i := range fields {
|
|
||||||
ff := &fields[i]
|
|
||||||
if ff.name == key {
|
|
||||||
f = ff
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if f == nil && strings.EqualFold(ff.name, key) {
|
|
||||||
f = ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f != nil {
|
|
||||||
subv := rv
|
|
||||||
for _, i := range f.index {
|
|
||||||
subv = indirect(subv.Field(i))
|
|
||||||
}
|
|
||||||
if isUnifiable(subv) {
|
|
||||||
md.decoded[md.context.add(key).String()] = true
|
|
||||||
md.context = append(md.context, key)
|
|
||||||
if err := md.unify(datum, subv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md.context = md.context[0 : len(md.context)-1]
|
|
||||||
} else if f.name != "" {
|
|
||||||
// Bad user! No soup for you!
|
|
||||||
return e("cannot write unexported field %s.%s",
|
|
||||||
rv.Type().String(), f.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
|
|
||||||
tmap, ok := mapping.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
if tmap == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("map", mapping)
|
|
||||||
}
|
|
||||||
if rv.IsNil() {
|
|
||||||
rv.Set(reflect.MakeMap(rv.Type()))
|
|
||||||
}
|
|
||||||
for k, v := range tmap {
|
|
||||||
md.decoded[md.context.add(k).String()] = true
|
|
||||||
md.context = append(md.context, k)
|
|
||||||
|
|
||||||
rvkey := indirect(reflect.New(rv.Type().Key()))
|
|
||||||
rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
|
|
||||||
if err := md.unify(v, rvval); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md.context = md.context[0 : len(md.context)-1]
|
|
||||||
|
|
||||||
rvkey.SetString(k)
|
|
||||||
rv.SetMapIndex(rvkey, rvval)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
|
|
||||||
datav := reflect.ValueOf(data)
|
|
||||||
if datav.Kind() != reflect.Slice {
|
|
||||||
if !datav.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("slice", data)
|
|
||||||
}
|
|
||||||
sliceLen := datav.Len()
|
|
||||||
if sliceLen != rv.Len() {
|
|
||||||
return e("expected array length %d; got TOML array of length %d",
|
|
||||||
rv.Len(), sliceLen)
|
|
||||||
}
|
|
||||||
return md.unifySliceArray(datav, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
|
|
||||||
datav := reflect.ValueOf(data)
|
|
||||||
if datav.Kind() != reflect.Slice {
|
|
||||||
if !datav.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("slice", data)
|
|
||||||
}
|
|
||||||
n := datav.Len()
|
|
||||||
if rv.IsNil() || rv.Cap() < n {
|
|
||||||
rv.Set(reflect.MakeSlice(rv.Type(), n, n))
|
|
||||||
}
|
|
||||||
rv.SetLen(n)
|
|
||||||
return md.unifySliceArray(datav, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
|
|
||||||
sliceLen := data.Len()
|
|
||||||
for i := 0; i < sliceLen; i++ {
|
|
||||||
v := data.Index(i).Interface()
|
|
||||||
sliceval := indirect(rv.Index(i))
|
|
||||||
if err := md.unify(v, sliceval); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error {
|
|
||||||
if _, ok := data.(time.Time); ok {
|
|
||||||
rv.Set(reflect.ValueOf(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("time.Time", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
|
|
||||||
if s, ok := data.(string); ok {
|
|
||||||
rv.SetString(s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("string", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
|
|
||||||
if num, ok := data.(float64); ok {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Float32:
|
|
||||||
fallthrough
|
|
||||||
case reflect.Float64:
|
|
||||||
rv.SetFloat(num)
|
|
||||||
default:
|
|
||||||
panic("bug")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("float", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
|
|
||||||
if num, ok := data.(int64); ok {
|
|
||||||
if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Int, reflect.Int64:
|
|
||||||
// No bounds checking necessary.
|
|
||||||
case reflect.Int8:
|
|
||||||
if num < math.MinInt8 || num > math.MaxInt8 {
|
|
||||||
return e("value %d is out of range for int8", num)
|
|
||||||
}
|
|
||||||
case reflect.Int16:
|
|
||||||
if num < math.MinInt16 || num > math.MaxInt16 {
|
|
||||||
return e("value %d is out of range for int16", num)
|
|
||||||
}
|
|
||||||
case reflect.Int32:
|
|
||||||
if num < math.MinInt32 || num > math.MaxInt32 {
|
|
||||||
return e("value %d is out of range for int32", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rv.SetInt(num)
|
|
||||||
} else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
|
|
||||||
unum := uint64(num)
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
// No bounds checking necessary.
|
|
||||||
case reflect.Uint8:
|
|
||||||
if num < 0 || unum > math.MaxUint8 {
|
|
||||||
return e("value %d is out of range for uint8", num)
|
|
||||||
}
|
|
||||||
case reflect.Uint16:
|
|
||||||
if num < 0 || unum > math.MaxUint16 {
|
|
||||||
return e("value %d is out of range for uint16", num)
|
|
||||||
}
|
|
||||||
case reflect.Uint32:
|
|
||||||
if num < 0 || unum > math.MaxUint32 {
|
|
||||||
return e("value %d is out of range for uint32", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rv.SetUint(unum)
|
|
||||||
} else {
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("integer", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
|
|
||||||
if b, ok := data.(bool); ok {
|
|
||||||
rv.SetBool(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("boolean", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
|
|
||||||
rv.Set(reflect.ValueOf(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error {
|
|
||||||
var s string
|
|
||||||
switch sdata := data.(type) {
|
|
||||||
case TextMarshaler:
|
|
||||||
text, err := sdata.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s = string(text)
|
|
||||||
case fmt.Stringer:
|
|
||||||
s = sdata.String()
|
|
||||||
case string:
|
|
||||||
s = sdata
|
|
||||||
case bool:
|
|
||||||
s = fmt.Sprintf("%v", sdata)
|
|
||||||
case int64:
|
|
||||||
s = fmt.Sprintf("%d", sdata)
|
|
||||||
case float64:
|
|
||||||
s = fmt.Sprintf("%f", sdata)
|
|
||||||
default:
|
|
||||||
return badtype("primitive (string-like)", data)
|
|
||||||
}
|
|
||||||
if err := v.UnmarshalText([]byte(s)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// rvalue returns a reflect.Value of `v`. All pointers are resolved.
|
|
||||||
func rvalue(v interface{}) reflect.Value {
|
|
||||||
return indirect(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// indirect returns the value pointed to by a pointer.
|
|
||||||
// Pointers are followed until the value is not a pointer.
|
|
||||||
// New values are allocated for each nil pointer.
|
|
||||||
//
|
|
||||||
// An exception to this rule is if the value satisfies an interface of
|
|
||||||
// interest to us (like encoding.TextUnmarshaler).
|
|
||||||
func indirect(v reflect.Value) reflect.Value {
|
|
||||||
if v.Kind() != reflect.Ptr {
|
|
||||||
if v.CanSet() {
|
|
||||||
pv := v.Addr()
|
|
||||||
if _, ok := pv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
if v.IsNil() {
|
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
|
||||||
}
|
|
||||||
return indirect(reflect.Indirect(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func isUnifiable(rv reflect.Value) bool {
|
|
||||||
if rv.CanSet() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := rv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func badtype(expected string, data interface{}) error {
|
|
||||||
return e("cannot load TOML value of type %T into a Go %s", data, expected)
|
|
||||||
}
|
|
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
|
@ -1,121 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// MetaData allows access to meta information about TOML data that may not
|
|
||||||
// be inferrable via reflection. In particular, whether a key has been defined
|
|
||||||
// and the TOML type of a key.
|
|
||||||
type MetaData struct {
|
|
||||||
mapping map[string]interface{}
|
|
||||||
types map[string]tomlType
|
|
||||||
keys []Key
|
|
||||||
decoded map[string]bool
|
|
||||||
context Key // Used only during decoding.
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDefined returns true if the key given exists in the TOML data. The key
|
|
||||||
// should be specified hierarchially. e.g.,
|
|
||||||
//
|
|
||||||
// // access the TOML key 'a.b.c'
|
|
||||||
// IsDefined("a", "b", "c")
|
|
||||||
//
|
|
||||||
// IsDefined will return false if an empty key given. Keys are case sensitive.
|
|
||||||
func (md *MetaData) IsDefined(key ...string) bool {
|
|
||||||
if len(key) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash map[string]interface{}
|
|
||||||
var ok bool
|
|
||||||
var hashOrVal interface{} = md.mapping
|
|
||||||
for _, k := range key {
|
|
||||||
if hash, ok = hashOrVal.(map[string]interface{}); !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if hashOrVal, ok = hash[k]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns a string representation of the type of the key specified.
|
|
||||||
//
|
|
||||||
// Type will return the empty string if given an empty key or a key that
|
|
||||||
// does not exist. Keys are case sensitive.
|
|
||||||
func (md *MetaData) Type(key ...string) string {
|
|
||||||
fullkey := strings.Join(key, ".")
|
|
||||||
if typ, ok := md.types[fullkey]; ok {
|
|
||||||
return typ.typeString()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
|
|
||||||
// to get values of this type.
|
|
||||||
type Key []string
|
|
||||||
|
|
||||||
func (k Key) String() string {
|
|
||||||
return strings.Join(k, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) maybeQuotedAll() string {
|
|
||||||
var ss []string
|
|
||||||
for i := range k {
|
|
||||||
ss = append(ss, k.maybeQuoted(i))
|
|
||||||
}
|
|
||||||
return strings.Join(ss, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) maybeQuoted(i int) string {
|
|
||||||
quote := false
|
|
||||||
for _, c := range k[i] {
|
|
||||||
if !isBareKeyChar(c) {
|
|
||||||
quote = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if quote {
|
|
||||||
return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
|
|
||||||
}
|
|
||||||
return k[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) add(piece string) Key {
|
|
||||||
newKey := make(Key, len(k)+1)
|
|
||||||
copy(newKey, k)
|
|
||||||
newKey[len(k)] = piece
|
|
||||||
return newKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys returns a slice of every key in the TOML data, including key groups.
|
|
||||||
// Each key is itself a slice, where the first element is the top of the
|
|
||||||
// hierarchy and the last is the most specific.
|
|
||||||
//
|
|
||||||
// The list will have the same order as the keys appeared in the TOML data.
|
|
||||||
//
|
|
||||||
// All keys returned are non-empty.
|
|
||||||
func (md *MetaData) Keys() []Key {
|
|
||||||
return md.keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Undecoded returns all keys that have not been decoded in the order in which
|
|
||||||
// they appear in the original TOML document.
|
|
||||||
//
|
|
||||||
// This includes keys that haven't been decoded because of a Primitive value.
|
|
||||||
// Once the Primitive value is decoded, the keys will be considered decoded.
|
|
||||||
//
|
|
||||||
// Also note that decoding into an empty interface will result in no decoding,
|
|
||||||
// and so no keys will be considered decoded.
|
|
||||||
//
|
|
||||||
// In this sense, the Undecoded keys correspond to keys in the TOML document
|
|
||||||
// that do not have a concrete type in your representation.
|
|
||||||
func (md *MetaData) Undecoded() []Key {
|
|
||||||
undecoded := make([]Key, 0, len(md.keys))
|
|
||||||
for _, key := range md.keys {
|
|
||||||
if !md.decoded[key.String()] {
|
|
||||||
undecoded = append(undecoded, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undecoded
|
|
||||||
}
|
|
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
Package toml provides facilities for decoding and encoding TOML configuration
|
|
||||||
files via reflection. There is also support for delaying decoding with
|
|
||||||
the Primitive type, and querying the set of keys in a TOML document with the
|
|
||||||
MetaData type.
|
|
||||||
|
|
||||||
The specification implemented: https://github.com/toml-lang/toml
|
|
||||||
|
|
||||||
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
|
||||||
whether a file is a valid TOML document. It can also be used to print the
|
|
||||||
type of each key in a TOML document.
|
|
||||||
|
|
||||||
Testing
|
|
||||||
|
|
||||||
There are two important types of tests used for this package. The first is
|
|
||||||
contained inside '*_test.go' files and uses the standard Go unit testing
|
|
||||||
framework. These tests are primarily devoted to holistically testing the
|
|
||||||
decoder and encoder.
|
|
||||||
|
|
||||||
The second type of testing is used to verify the implementation's adherence
|
|
||||||
to the TOML specification. These tests have been factored into their own
|
|
||||||
project: https://github.com/BurntSushi/toml-test
|
|
||||||
|
|
||||||
The reason the tests are in a separate project is so that they can be used by
|
|
||||||
any implementation of TOML. Namely, it is language agnostic.
|
|
||||||
*/
|
|
||||||
package toml
|
|
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
|
@ -1,568 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tomlEncodeError struct{ error }
|
|
||||||
|
|
||||||
var (
|
|
||||||
errArrayMixedElementTypes = errors.New(
|
|
||||||
"toml: cannot encode array with mixed element types")
|
|
||||||
errArrayNilElement = errors.New(
|
|
||||||
"toml: cannot encode array with nil element")
|
|
||||||
errNonString = errors.New(
|
|
||||||
"toml: cannot encode a map with non-string key type")
|
|
||||||
errAnonNonStruct = errors.New(
|
|
||||||
"toml: cannot encode an anonymous field that is not a struct")
|
|
||||||
errArrayNoTable = errors.New(
|
|
||||||
"toml: TOML array element cannot contain a table")
|
|
||||||
errNoKey = errors.New(
|
|
||||||
"toml: top-level values must be Go maps or structs")
|
|
||||||
errAnything = errors.New("") // used in testing
|
|
||||||
)
|
|
||||||
|
|
||||||
var quotedReplacer = strings.NewReplacer(
|
|
||||||
"\t", "\\t",
|
|
||||||
"\n", "\\n",
|
|
||||||
"\r", "\\r",
|
|
||||||
"\"", "\\\"",
|
|
||||||
"\\", "\\\\",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encoder controls the encoding of Go values to a TOML document to some
|
|
||||||
// io.Writer.
|
|
||||||
//
|
|
||||||
// The indentation level can be controlled with the Indent field.
|
|
||||||
type Encoder struct {
|
|
||||||
// A single indentation level. By default it is two spaces.
|
|
||||||
Indent string
|
|
||||||
|
|
||||||
// hasWritten is whether we have written any output to w yet.
|
|
||||||
hasWritten bool
|
|
||||||
w *bufio.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
|
|
||||||
// given. By default, a single indentation level is 2 spaces.
|
|
||||||
func NewEncoder(w io.Writer) *Encoder {
|
|
||||||
return &Encoder{
|
|
||||||
w: bufio.NewWriter(w),
|
|
||||||
Indent: " ",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes a TOML representation of the Go value to the underlying
|
|
||||||
// io.Writer. If the value given cannot be encoded to a valid TOML document,
|
|
||||||
// then an error is returned.
|
|
||||||
//
|
|
||||||
// The mapping between Go values and TOML values should be precisely the same
|
|
||||||
// as for the Decode* functions. Similarly, the TextMarshaler interface is
|
|
||||||
// supported by encoding the resulting bytes as strings. (If you want to write
|
|
||||||
// arbitrary binary data then you will need to use something like base64 since
|
|
||||||
// TOML does not have any binary types.)
|
|
||||||
//
|
|
||||||
// When encoding TOML hashes (i.e., Go maps or structs), keys without any
|
|
||||||
// sub-hashes are encoded first.
|
|
||||||
//
|
|
||||||
// If a Go map is encoded, then its keys are sorted alphabetically for
|
|
||||||
// deterministic output. More control over this behavior may be provided if
|
|
||||||
// there is demand for it.
|
|
||||||
//
|
|
||||||
// Encoding Go values without a corresponding TOML representation---like map
|
|
||||||
// types with non-string keys---will cause an error to be returned. Similarly
|
|
||||||
// for mixed arrays/slices, arrays/slices with nil elements, embedded
|
|
||||||
// non-struct types and nested slices containing maps or structs.
|
|
||||||
// (e.g., [][]map[string]string is not allowed but []map[string]string is OK
|
|
||||||
// and so is []map[string][]string.)
|
|
||||||
func (enc *Encoder) Encode(v interface{}) error {
|
|
||||||
rv := eindirect(reflect.ValueOf(v))
|
|
||||||
if err := enc.safeEncode(Key([]string{}), rv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return enc.w.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if terr, ok := r.(tomlEncodeError); ok {
|
|
||||||
err = terr.error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
enc.encode(key, rv)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) encode(key Key, rv reflect.Value) {
|
|
||||||
// Special case. Time needs to be in ISO8601 format.
|
|
||||||
// Special case. If we can marshal the type to text, then we used that.
|
|
||||||
// Basically, this prevents the encoder for handling these types as
|
|
||||||
// generic structs (or whatever the underlying type of a TextMarshaler is).
|
|
||||||
switch rv.Interface().(type) {
|
|
||||||
case time.Time, TextMarshaler:
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
k := rv.Kind()
|
|
||||||
switch k {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
|
||||||
reflect.Uint64,
|
|
||||||
reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
|
|
||||||
enc.eArrayOfTables(key, rv)
|
|
||||||
} else {
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.encode(key, rv.Elem())
|
|
||||||
case reflect.Map:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.eTable(key, rv)
|
|
||||||
case reflect.Ptr:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.encode(key, rv.Elem())
|
|
||||||
case reflect.Struct:
|
|
||||||
enc.eTable(key, rv)
|
|
||||||
default:
|
|
||||||
panic(e("unsupported type for key '%s': %s", key, k))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eElement encodes any value that can be an array element (primitives and
|
|
||||||
// arrays).
|
|
||||||
func (enc *Encoder) eElement(rv reflect.Value) {
|
|
||||||
switch v := rv.Interface().(type) {
|
|
||||||
case time.Time:
|
|
||||||
// Special case time.Time as a primitive. Has to come before
|
|
||||||
// TextMarshaler below because time.Time implements
|
|
||||||
// encoding.TextMarshaler, but we need to always use UTC.
|
|
||||||
enc.wf(v.UTC().Format("2006-01-02T15:04:05Z"))
|
|
||||||
return
|
|
||||||
case TextMarshaler:
|
|
||||||
// Special case. Use text marshaler if it's available for this value.
|
|
||||||
if s, err := v.MarshalText(); err != nil {
|
|
||||||
encPanic(err)
|
|
||||||
} else {
|
|
||||||
enc.writeQuoted(string(s))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
enc.wf(strconv.FormatBool(rv.Bool()))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64:
|
|
||||||
enc.wf(strconv.FormatInt(rv.Int(), 10))
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
|
||||||
reflect.Uint32, reflect.Uint64:
|
|
||||||
enc.wf(strconv.FormatUint(rv.Uint(), 10))
|
|
||||||
case reflect.Float32:
|
|
||||||
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
|
|
||||||
case reflect.Float64:
|
|
||||||
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
enc.eArrayOrSliceElement(rv)
|
|
||||||
case reflect.Interface:
|
|
||||||
enc.eElement(rv.Elem())
|
|
||||||
case reflect.String:
|
|
||||||
enc.writeQuoted(rv.String())
|
|
||||||
default:
|
|
||||||
panic(e("unexpected primitive type: %s", rv.Kind()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// By the TOML spec, all floats must have a decimal with at least one
|
|
||||||
// number on either side.
|
|
||||||
func floatAddDecimal(fstr string) string {
|
|
||||||
if !strings.Contains(fstr, ".") {
|
|
||||||
return fstr + ".0"
|
|
||||||
}
|
|
||||||
return fstr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) writeQuoted(s string) {
|
|
||||||
enc.wf("\"%s\"", quotedReplacer.Replace(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
|
|
||||||
length := rv.Len()
|
|
||||||
enc.wf("[")
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
elem := rv.Index(i)
|
|
||||||
enc.eElement(elem)
|
|
||||||
if i != length-1 {
|
|
||||||
enc.wf(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enc.wf("]")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
|
||||||
if len(key) == 0 {
|
|
||||||
encPanic(errNoKey)
|
|
||||||
}
|
|
||||||
for i := 0; i < rv.Len(); i++ {
|
|
||||||
trv := rv.Index(i)
|
|
||||||
if isNil(trv) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
enc.newline()
|
|
||||||
enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
|
|
||||||
enc.newline()
|
|
||||||
enc.eMapOrStruct(key, trv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
if len(key) == 1 {
|
|
||||||
// Output an extra newline between top-level tables.
|
|
||||||
// (The newline isn't written if nothing else has been written though.)
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
if len(key) > 0 {
|
|
||||||
enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
enc.eMapOrStruct(key, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
|
|
||||||
switch rv := eindirect(rv); rv.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
enc.eMap(key, rv)
|
|
||||||
case reflect.Struct:
|
|
||||||
enc.eStruct(key, rv)
|
|
||||||
default:
|
|
||||||
panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eMap(key Key, rv reflect.Value) {
|
|
||||||
rt := rv.Type()
|
|
||||||
if rt.Key().Kind() != reflect.String {
|
|
||||||
encPanic(errNonString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort keys so that we have deterministic output. And write keys directly
|
|
||||||
// underneath this key first, before writing sub-structs or sub-maps.
|
|
||||||
var mapKeysDirect, mapKeysSub []string
|
|
||||||
for _, mapKey := range rv.MapKeys() {
|
|
||||||
k := mapKey.String()
|
|
||||||
if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
|
|
||||||
mapKeysSub = append(mapKeysSub, k)
|
|
||||||
} else {
|
|
||||||
mapKeysDirect = append(mapKeysDirect, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var writeMapKeys = func(mapKeys []string) {
|
|
||||||
sort.Strings(mapKeys)
|
|
||||||
for _, mapKey := range mapKeys {
|
|
||||||
mrv := rv.MapIndex(reflect.ValueOf(mapKey))
|
|
||||||
if isNil(mrv) {
|
|
||||||
// Don't write anything for nil fields.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enc.encode(key.add(mapKey), mrv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeMapKeys(mapKeysDirect)
|
|
||||||
writeMapKeys(mapKeysSub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
|
|
||||||
// Write keys for fields directly under this key first, because if we write
|
|
||||||
// a field that creates a new table, then all keys under it will be in that
|
|
||||||
// table (not the one we're writing here).
|
|
||||||
rt := rv.Type()
|
|
||||||
var fieldsDirect, fieldsSub [][]int
|
|
||||||
var addFields func(rt reflect.Type, rv reflect.Value, start []int)
|
|
||||||
addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
|
|
||||||
for i := 0; i < rt.NumField(); i++ {
|
|
||||||
f := rt.Field(i)
|
|
||||||
// skip unexported fields
|
|
||||||
if f.PkgPath != "" && !f.Anonymous {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
frv := rv.Field(i)
|
|
||||||
if f.Anonymous {
|
|
||||||
t := f.Type
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
// Treat anonymous struct fields with
|
|
||||||
// tag names as though they are not
|
|
||||||
// anonymous, like encoding/json does.
|
|
||||||
if getOptions(f.Tag).name == "" {
|
|
||||||
addFields(t, frv, f.Index)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
if t.Elem().Kind() == reflect.Struct &&
|
|
||||||
getOptions(f.Tag).name == "" {
|
|
||||||
if !frv.IsNil() {
|
|
||||||
addFields(t.Elem(), frv.Elem(), f.Index)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Fall through to the normal field encoding logic below
|
|
||||||
// for non-struct anonymous fields.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if typeIsHash(tomlTypeOfGo(frv)) {
|
|
||||||
fieldsSub = append(fieldsSub, append(start, f.Index...))
|
|
||||||
} else {
|
|
||||||
fieldsDirect = append(fieldsDirect, append(start, f.Index...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addFields(rt, rv, nil)
|
|
||||||
|
|
||||||
var writeFields = func(fields [][]int) {
|
|
||||||
for _, fieldIndex := range fields {
|
|
||||||
sft := rt.FieldByIndex(fieldIndex)
|
|
||||||
sf := rv.FieldByIndex(fieldIndex)
|
|
||||||
if isNil(sf) {
|
|
||||||
// Don't write anything for nil fields.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := getOptions(sft.Tag)
|
|
||||||
if opts.skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
keyName := sft.Name
|
|
||||||
if opts.name != "" {
|
|
||||||
keyName = opts.name
|
|
||||||
}
|
|
||||||
if opts.omitempty && isEmpty(sf) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if opts.omitzero && isZero(sf) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
enc.encode(key.add(keyName), sf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeFields(fieldsDirect)
|
|
||||||
writeFields(fieldsSub)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tomlTypeName returns the TOML type name of the Go value's type. It is
|
|
||||||
// used to determine whether the types of array elements are mixed (which is
|
|
||||||
// forbidden). If the Go value is nil, then it is illegal for it to be an array
|
|
||||||
// element, and valueIsNil is returned as true.
|
|
||||||
|
|
||||||
// Returns the TOML type of a Go value. The type may be `nil`, which means
|
|
||||||
// no concrete TOML type could be found.
|
|
||||||
func tomlTypeOfGo(rv reflect.Value) tomlType {
|
|
||||||
if isNil(rv) || !rv.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return tomlBool
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
|
||||||
reflect.Uint64:
|
|
||||||
return tomlInteger
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return tomlFloat
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if typeEqual(tomlHash, tomlArrayType(rv)) {
|
|
||||||
return tomlArrayHash
|
|
||||||
}
|
|
||||||
return tomlArray
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return tomlTypeOfGo(rv.Elem())
|
|
||||||
case reflect.String:
|
|
||||||
return tomlString
|
|
||||||
case reflect.Map:
|
|
||||||
return tomlHash
|
|
||||||
case reflect.Struct:
|
|
||||||
switch rv.Interface().(type) {
|
|
||||||
case time.Time:
|
|
||||||
return tomlDatetime
|
|
||||||
case TextMarshaler:
|
|
||||||
return tomlString
|
|
||||||
default:
|
|
||||||
return tomlHash
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unexpected reflect.Kind: " + rv.Kind().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tomlArrayType returns the element type of a TOML array. The type returned
|
|
||||||
// may be nil if it cannot be determined (e.g., a nil slice or a zero length
|
|
||||||
// slize). This function may also panic if it finds a type that cannot be
|
|
||||||
// expressed in TOML (such as nil elements, heterogeneous arrays or directly
|
|
||||||
// nested arrays of tables).
|
|
||||||
func tomlArrayType(rv reflect.Value) tomlType {
|
|
||||||
if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
firstType := tomlTypeOfGo(rv.Index(0))
|
|
||||||
if firstType == nil {
|
|
||||||
encPanic(errArrayNilElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
rvlen := rv.Len()
|
|
||||||
for i := 1; i < rvlen; i++ {
|
|
||||||
elem := rv.Index(i)
|
|
||||||
switch elemType := tomlTypeOfGo(elem); {
|
|
||||||
case elemType == nil:
|
|
||||||
encPanic(errArrayNilElement)
|
|
||||||
case !typeEqual(firstType, elemType):
|
|
||||||
encPanic(errArrayMixedElementTypes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we have a nested array, then we must make sure that the nested
|
|
||||||
// array contains ONLY primitives.
|
|
||||||
// This checks arbitrarily nested arrays.
|
|
||||||
if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
|
|
||||||
nest := tomlArrayType(eindirect(rv.Index(0)))
|
|
||||||
if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
|
|
||||||
encPanic(errArrayNoTable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstType
|
|
||||||
}
|
|
||||||
|
|
||||||
type tagOptions struct {
|
|
||||||
skip bool // "-"
|
|
||||||
name string
|
|
||||||
omitempty bool
|
|
||||||
omitzero bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOptions(tag reflect.StructTag) tagOptions {
|
|
||||||
t := tag.Get("toml")
|
|
||||||
if t == "-" {
|
|
||||||
return tagOptions{skip: true}
|
|
||||||
}
|
|
||||||
var opts tagOptions
|
|
||||||
parts := strings.Split(t, ",")
|
|
||||||
opts.name = parts[0]
|
|
||||||
for _, s := range parts[1:] {
|
|
||||||
switch s {
|
|
||||||
case "omitempty":
|
|
||||||
opts.omitempty = true
|
|
||||||
case "omitzero":
|
|
||||||
opts.omitzero = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func isZero(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return rv.Int() == 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
return rv.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return rv.Float() == 0.0
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEmpty(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
|
||||||
return rv.Len() == 0
|
|
||||||
case reflect.Bool:
|
|
||||||
return !rv.Bool()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) newline() {
|
|
||||||
if enc.hasWritten {
|
|
||||||
enc.wf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
|
|
||||||
if len(key) == 0 {
|
|
||||||
encPanic(errNoKey)
|
|
||||||
}
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
|
||||||
enc.eElement(val)
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) wf(format string, v ...interface{}) {
|
|
||||||
if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
|
|
||||||
encPanic(err)
|
|
||||||
}
|
|
||||||
enc.hasWritten = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) indentStr(key Key) string {
|
|
||||||
return strings.Repeat(enc.Indent, len(key)-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encPanic(err error) {
|
|
||||||
panic(tomlEncodeError{err})
|
|
||||||
}
|
|
||||||
|
|
||||||
func eindirect(v reflect.Value) reflect.Value {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return eindirect(v.Elem())
|
|
||||||
default:
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNil(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return rv.IsNil()
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func panicIfInvalidKey(key Key) {
|
|
||||||
for _, k := range key {
|
|
||||||
if len(k) == 0 {
|
|
||||||
encPanic(e("Key '%s' is not a valid table name. Key names "+
|
|
||||||
"cannot be empty.", key.maybeQuotedAll()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidKeyName(s string) bool {
|
|
||||||
return len(s) != 0
|
|
||||||
}
|
|
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
|
@ -1,19 +0,0 @@
|
||||||
// +build go1.2
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
// In order to support Go 1.1, we define our own TextMarshaler and
|
|
||||||
// TextUnmarshaler types. For Go 1.2+, we just alias them with the
|
|
||||||
// standard library interfaces.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
|
||||||
// so that Go 1.1 can be supported.
|
|
||||||
type TextMarshaler encoding.TextMarshaler
|
|
||||||
|
|
||||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
|
||||||
// here so that Go 1.1 can be supported.
|
|
||||||
type TextUnmarshaler encoding.TextUnmarshaler
|
|
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
// +build !go1.2
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
// These interfaces were introduced in Go 1.2, so we add them manually when
|
|
||||||
// compiling for Go 1.1.
|
|
||||||
|
|
||||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
|
||||||
// so that Go 1.1 can be supported.
|
|
||||||
type TextMarshaler interface {
|
|
||||||
MarshalText() (text []byte, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
|
||||||
// here so that Go 1.1 can be supported.
|
|
||||||
type TextUnmarshaler interface {
|
|
||||||
UnmarshalText(text []byte) error
|
|
||||||
}
|
|
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
|
@ -1,953 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type itemType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
itemError itemType = iota
|
|
||||||
itemNIL // used in the parser to indicate no type
|
|
||||||
itemEOF
|
|
||||||
itemText
|
|
||||||
itemString
|
|
||||||
itemRawString
|
|
||||||
itemMultilineString
|
|
||||||
itemRawMultilineString
|
|
||||||
itemBool
|
|
||||||
itemInteger
|
|
||||||
itemFloat
|
|
||||||
itemDatetime
|
|
||||||
itemArray // the start of an array
|
|
||||||
itemArrayEnd
|
|
||||||
itemTableStart
|
|
||||||
itemTableEnd
|
|
||||||
itemArrayTableStart
|
|
||||||
itemArrayTableEnd
|
|
||||||
itemKeyStart
|
|
||||||
itemCommentStart
|
|
||||||
itemInlineTableStart
|
|
||||||
itemInlineTableEnd
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
eof = 0
|
|
||||||
comma = ','
|
|
||||||
tableStart = '['
|
|
||||||
tableEnd = ']'
|
|
||||||
arrayTableStart = '['
|
|
||||||
arrayTableEnd = ']'
|
|
||||||
tableSep = '.'
|
|
||||||
keySep = '='
|
|
||||||
arrayStart = '['
|
|
||||||
arrayEnd = ']'
|
|
||||||
commentStart = '#'
|
|
||||||
stringStart = '"'
|
|
||||||
stringEnd = '"'
|
|
||||||
rawStringStart = '\''
|
|
||||||
rawStringEnd = '\''
|
|
||||||
inlineTableStart = '{'
|
|
||||||
inlineTableEnd = '}'
|
|
||||||
)
|
|
||||||
|
|
||||||
type stateFn func(lx *lexer) stateFn
|
|
||||||
|
|
||||||
type lexer struct {
|
|
||||||
input string
|
|
||||||
start int
|
|
||||||
pos int
|
|
||||||
line int
|
|
||||||
state stateFn
|
|
||||||
items chan item
|
|
||||||
|
|
||||||
// Allow for backing up up to three runes.
|
|
||||||
// This is necessary because TOML contains 3-rune tokens (""" and ''').
|
|
||||||
prevWidths [3]int
|
|
||||||
nprev int // how many of prevWidths are in use
|
|
||||||
// If we emit an eof, we can still back up, but it is not OK to call
|
|
||||||
// next again.
|
|
||||||
atEOF bool
|
|
||||||
|
|
||||||
// A stack of state functions used to maintain context.
|
|
||||||
// The idea is to reuse parts of the state machine in various places.
|
|
||||||
// For example, values can appear at the top level or within arbitrarily
|
|
||||||
// nested arrays. The last state on the stack is used after a value has
|
|
||||||
// been lexed. Similarly for comments.
|
|
||||||
stack []stateFn
|
|
||||||
}
|
|
||||||
|
|
||||||
type item struct {
|
|
||||||
typ itemType
|
|
||||||
val string
|
|
||||||
line int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) nextItem() item {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case item := <-lx.items:
|
|
||||||
return item
|
|
||||||
default:
|
|
||||||
lx.state = lx.state(lx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lex(input string) *lexer {
|
|
||||||
lx := &lexer{
|
|
||||||
input: input,
|
|
||||||
state: lexTop,
|
|
||||||
line: 1,
|
|
||||||
items: make(chan item, 10),
|
|
||||||
stack: make([]stateFn, 0, 10),
|
|
||||||
}
|
|
||||||
return lx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) push(state stateFn) {
|
|
||||||
lx.stack = append(lx.stack, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) pop() stateFn {
|
|
||||||
if len(lx.stack) == 0 {
|
|
||||||
return lx.errorf("BUG in lexer: no states to pop")
|
|
||||||
}
|
|
||||||
last := lx.stack[len(lx.stack)-1]
|
|
||||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
|
||||||
return last
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) current() string {
|
|
||||||
return lx.input[lx.start:lx.pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) emit(typ itemType) {
|
|
||||||
lx.items <- item{typ, lx.current(), lx.line}
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) emitTrim(typ itemType) {
|
|
||||||
lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line}
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) next() (r rune) {
|
|
||||||
if lx.atEOF {
|
|
||||||
panic("next called after EOF")
|
|
||||||
}
|
|
||||||
if lx.pos >= len(lx.input) {
|
|
||||||
lx.atEOF = true
|
|
||||||
return eof
|
|
||||||
}
|
|
||||||
|
|
||||||
if lx.input[lx.pos] == '\n' {
|
|
||||||
lx.line++
|
|
||||||
}
|
|
||||||
lx.prevWidths[2] = lx.prevWidths[1]
|
|
||||||
lx.prevWidths[1] = lx.prevWidths[0]
|
|
||||||
if lx.nprev < 3 {
|
|
||||||
lx.nprev++
|
|
||||||
}
|
|
||||||
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
|
|
||||||
lx.prevWidths[0] = w
|
|
||||||
lx.pos += w
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore skips over the pending input before this point.
|
|
||||||
func (lx *lexer) ignore() {
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup steps back one rune. Can be called only twice between calls to next.
|
|
||||||
func (lx *lexer) backup() {
|
|
||||||
if lx.atEOF {
|
|
||||||
lx.atEOF = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if lx.nprev < 1 {
|
|
||||||
panic("backed up too far")
|
|
||||||
}
|
|
||||||
w := lx.prevWidths[0]
|
|
||||||
lx.prevWidths[0] = lx.prevWidths[1]
|
|
||||||
lx.prevWidths[1] = lx.prevWidths[2]
|
|
||||||
lx.nprev--
|
|
||||||
lx.pos -= w
|
|
||||||
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
|
|
||||||
lx.line--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// accept consumes the next rune if it's equal to `valid`.
|
|
||||||
func (lx *lexer) accept(valid rune) bool {
|
|
||||||
if lx.next() == valid {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek returns but does not consume the next rune in the input.
|
|
||||||
func (lx *lexer) peek() rune {
|
|
||||||
r := lx.next()
|
|
||||||
lx.backup()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip ignores all input that matches the given predicate.
|
|
||||||
func (lx *lexer) skip(pred func(rune) bool) {
|
|
||||||
for {
|
|
||||||
r := lx.next()
|
|
||||||
if pred(r) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.ignore()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf stops all lexing by emitting an error and returning `nil`.
|
|
||||||
// Note that any value that is a character is escaped if it's a special
|
|
||||||
// character (newlines, tabs, etc.).
|
|
||||||
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
|
||||||
lx.items <- item{
|
|
||||||
itemError,
|
|
||||||
fmt.Sprintf(format, values...),
|
|
||||||
lx.line,
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTop consumes elements at the top level of TOML data.
|
|
||||||
func lexTop(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isWhitespace(r) || isNL(r) {
|
|
||||||
return lexSkip(lx, lexTop)
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case commentStart:
|
|
||||||
lx.push(lexTop)
|
|
||||||
return lexCommentStart
|
|
||||||
case tableStart:
|
|
||||||
return lexTableStart
|
|
||||||
case eof:
|
|
||||||
if lx.pos > lx.start {
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
}
|
|
||||||
lx.emit(itemEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, the only valid item can be a key, so we back up
|
|
||||||
// and let the key lexer do the rest.
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexTopEnd)
|
|
||||||
return lexKeyStart
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
|
|
||||||
// or a table.) It must see only whitespace, and will turn back to lexTop
|
|
||||||
// upon a newline. If it sees EOF, it will quit the lexer successfully.
|
|
||||||
func lexTopEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == commentStart:
|
|
||||||
// a comment will read to a newline for us.
|
|
||||||
lx.push(lexTop)
|
|
||||||
return lexCommentStart
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexTopEnd
|
|
||||||
case isNL(r):
|
|
||||||
lx.ignore()
|
|
||||||
return lexTop
|
|
||||||
case r == eof:
|
|
||||||
lx.emit(itemEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a top-level item to end with a newline, "+
|
|
||||||
"comment, or EOF, but got %q instead", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTable lexes the beginning of a table. Namely, it makes sure that
|
|
||||||
// it starts with a character other than '.' and ']'.
|
|
||||||
// It assumes that '[' has already been consumed.
|
|
||||||
// It also handles the case that this is an item in an array of tables.
|
|
||||||
// e.g., '[[name]]'.
|
|
||||||
func lexTableStart(lx *lexer) stateFn {
|
|
||||||
if lx.peek() == arrayTableStart {
|
|
||||||
lx.next()
|
|
||||||
lx.emit(itemArrayTableStart)
|
|
||||||
lx.push(lexArrayTableEnd)
|
|
||||||
} else {
|
|
||||||
lx.emit(itemTableStart)
|
|
||||||
lx.push(lexTableEnd)
|
|
||||||
}
|
|
||||||
return lexTableNameStart
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexTableEnd(lx *lexer) stateFn {
|
|
||||||
lx.emit(itemTableEnd)
|
|
||||||
return lexTopEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexArrayTableEnd(lx *lexer) stateFn {
|
|
||||||
if r := lx.next(); r != arrayTableEnd {
|
|
||||||
return lx.errorf("expected end of table array name delimiter %q, "+
|
|
||||||
"but got %q instead", arrayTableEnd, r)
|
|
||||||
}
|
|
||||||
lx.emit(itemArrayTableEnd)
|
|
||||||
return lexTopEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexTableNameStart(lx *lexer) stateFn {
|
|
||||||
lx.skip(isWhitespace)
|
|
||||||
switch r := lx.peek(); {
|
|
||||||
case r == tableEnd || r == eof:
|
|
||||||
return lx.errorf("unexpected end of table name " +
|
|
||||||
"(table names cannot be empty)")
|
|
||||||
case r == tableSep:
|
|
||||||
return lx.errorf("unexpected table separator " +
|
|
||||||
"(table names cannot be empty)")
|
|
||||||
case r == stringStart || r == rawStringStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.push(lexTableNameEnd)
|
|
||||||
return lexValue // reuse string lexing
|
|
||||||
default:
|
|
||||||
return lexBareTableName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBareTableName lexes the name of a table. It assumes that at least one
|
|
||||||
// valid character for the table has already been read.
|
|
||||||
func lexBareTableName(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isBareKeyChar(r) {
|
|
||||||
return lexBareTableName
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexTableNameEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTableNameEnd reads the end of a piece of a table name, optionally
|
|
||||||
// consuming whitespace.
|
|
||||||
func lexTableNameEnd(lx *lexer) stateFn {
|
|
||||||
lx.skip(isWhitespace)
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexTableNameEnd
|
|
||||||
case r == tableSep:
|
|
||||||
lx.ignore()
|
|
||||||
return lexTableNameStart
|
|
||||||
case r == tableEnd:
|
|
||||||
return lx.pop()
|
|
||||||
default:
|
|
||||||
return lx.errorf("expected '.' or ']' to end table name, "+
|
|
||||||
"but got %q instead", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexKeyStart consumes a key name up until the first non-whitespace character.
|
|
||||||
// lexKeyStart will ignore whitespace.
|
|
||||||
func lexKeyStart(lx *lexer) stateFn {
|
|
||||||
r := lx.peek()
|
|
||||||
switch {
|
|
||||||
case r == keySep:
|
|
||||||
return lx.errorf("unexpected key separator %q", keySep)
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
lx.next()
|
|
||||||
return lexSkip(lx, lexKeyStart)
|
|
||||||
case r == stringStart || r == rawStringStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemKeyStart)
|
|
||||||
lx.push(lexKeyEnd)
|
|
||||||
return lexValue // reuse string lexing
|
|
||||||
default:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemKeyStart)
|
|
||||||
return lexBareKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBareKey consumes the text of a bare key. Assumes that the first character
|
|
||||||
// (which is not whitespace) has not yet been consumed.
|
|
||||||
func lexBareKey(lx *lexer) stateFn {
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case isBareKeyChar(r):
|
|
||||||
return lexBareKey
|
|
||||||
case isWhitespace(r):
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexKeyEnd
|
|
||||||
case r == keySep:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexKeyEnd
|
|
||||||
default:
|
|
||||||
return lx.errorf("bare keys cannot contain %q", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexKeyEnd consumes the end of a key and trims whitespace (up to the key
|
|
||||||
// separator).
|
|
||||||
func lexKeyEnd(lx *lexer) stateFn {
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case r == keySep:
|
|
||||||
return lexSkip(lx, lexValue)
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexKeyEnd)
|
|
||||||
default:
|
|
||||||
return lx.errorf("expected key separator %q, but got %q instead",
|
|
||||||
keySep, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexValue starts the consumption of a value anywhere a value is expected.
|
|
||||||
// lexValue will ignore whitespace.
|
|
||||||
// After a value is lexed, the last state on the next is popped and returned.
|
|
||||||
func lexValue(lx *lexer) stateFn {
|
|
||||||
// We allow whitespace to precede a value, but NOT newlines.
|
|
||||||
// In array syntax, the array states are responsible for ignoring newlines.
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexValue)
|
|
||||||
case isDigit(r):
|
|
||||||
lx.backup() // avoid an extra state and use the same as above
|
|
||||||
return lexNumberOrDateStart
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case arrayStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemArray)
|
|
||||||
return lexArrayValue
|
|
||||||
case inlineTableStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemInlineTableStart)
|
|
||||||
return lexInlineTableValue
|
|
||||||
case stringStart:
|
|
||||||
if lx.accept(stringStart) {
|
|
||||||
if lx.accept(stringStart) {
|
|
||||||
lx.ignore() // Ignore """
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
lx.ignore() // ignore the '"'
|
|
||||||
return lexString
|
|
||||||
case rawStringStart:
|
|
||||||
if lx.accept(rawStringStart) {
|
|
||||||
if lx.accept(rawStringStart) {
|
|
||||||
lx.ignore() // Ignore """
|
|
||||||
return lexMultilineRawString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
lx.ignore() // ignore the "'"
|
|
||||||
return lexRawString
|
|
||||||
case '+', '-':
|
|
||||||
return lexNumberStart
|
|
||||||
case '.': // special error case, be kind to users
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
if unicode.IsLetter(r) {
|
|
||||||
// Be permissive here; lexBool will give a nice error if the
|
|
||||||
// user wrote something like
|
|
||||||
// x = foo
|
|
||||||
// (i.e. not 'true' or 'false' but is something else word-like.)
|
|
||||||
lx.backup()
|
|
||||||
return lexBool
|
|
||||||
}
|
|
||||||
return lx.errorf("expected value but found %q instead", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
|
|
||||||
// have already been consumed. All whitespace and newlines are ignored.
|
|
||||||
func lexArrayValue(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
return lexSkip(lx, lexArrayValue)
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexArrayValue)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
return lx.errorf("unexpected comma")
|
|
||||||
case r == arrayEnd:
|
|
||||||
// NOTE(caleb): The spec isn't clear about whether you can have
|
|
||||||
// a trailing comma or not, so we'll allow it.
|
|
||||||
return lexArrayEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexArrayValueEnd)
|
|
||||||
return lexValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayValueEnd consumes everything between the end of an array value and
|
|
||||||
// the next value (or the end of the array): it ignores whitespace and newlines
|
|
||||||
// and expects either a ',' or a ']'.
|
|
||||||
func lexArrayValueEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
return lexSkip(lx, lexArrayValueEnd)
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexArrayValueEnd)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
lx.ignore()
|
|
||||||
return lexArrayValue // move on to the next value
|
|
||||||
case r == arrayEnd:
|
|
||||||
return lexArrayEnd
|
|
||||||
}
|
|
||||||
return lx.errorf(
|
|
||||||
"expected a comma or array terminator %q, but got %q instead",
|
|
||||||
arrayEnd, r,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayEnd finishes the lexing of an array.
|
|
||||||
// It assumes that a ']' has just been consumed.
|
|
||||||
func lexArrayEnd(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemArrayEnd)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableValue consumes one key/value pair in an inline table.
|
|
||||||
// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
|
|
||||||
func lexInlineTableValue(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexInlineTableValue)
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("newlines not allowed within inline tables")
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexInlineTableValue)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
return lx.errorf("unexpected comma")
|
|
||||||
case r == inlineTableEnd:
|
|
||||||
return lexInlineTableEnd
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexInlineTableValueEnd)
|
|
||||||
return lexKeyStart
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableValueEnd consumes everything between the end of an inline table
|
|
||||||
// key/value pair and the next pair (or the end of the table):
|
|
||||||
// it ignores whitespace and expects either a ',' or a '}'.
|
|
||||||
func lexInlineTableValueEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexInlineTableValueEnd)
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("newlines not allowed within inline tables")
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexInlineTableValueEnd)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
lx.ignore()
|
|
||||||
return lexInlineTableValue
|
|
||||||
case r == inlineTableEnd:
|
|
||||||
return lexInlineTableEnd
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a comma or an inline table terminator %q, "+
|
|
||||||
"but got %q instead", inlineTableEnd, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableEnd finishes the lexing of an inline table.
|
|
||||||
// It assumes that a '}' has just been consumed.
|
|
||||||
func lexInlineTableEnd(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemInlineTableEnd)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexString consumes the inner contents of a string. It assumes that the
|
|
||||||
// beginning '"' has already been consumed and ignored.
|
|
||||||
func lexString(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("strings cannot contain newlines")
|
|
||||||
case r == '\\':
|
|
||||||
lx.push(lexString)
|
|
||||||
return lexStringEscape
|
|
||||||
case r == stringEnd:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemString)
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lexString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineString consumes the inner contents of a string. It assumes that
|
|
||||||
// the beginning '"""' has already been consumed and ignored.
|
|
||||||
func lexMultilineString(lx *lexer) stateFn {
|
|
||||||
switch lx.next() {
|
|
||||||
case eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case '\\':
|
|
||||||
return lexMultilineStringEscape
|
|
||||||
case stringEnd:
|
|
||||||
if lx.accept(stringEnd) {
|
|
||||||
if lx.accept(stringEnd) {
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemMultilineString)
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexRawString consumes a raw string. Nothing can be escaped in such a string.
|
|
||||||
// It assumes that the beginning "'" has already been consumed and ignored.
|
|
||||||
func lexRawString(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("strings cannot contain newlines")
|
|
||||||
case r == rawStringEnd:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemRawString)
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lexRawString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
|
|
||||||
// a string. It assumes that the beginning "'''" has already been consumed and
|
|
||||||
// ignored.
|
|
||||||
func lexMultilineRawString(lx *lexer) stateFn {
|
|
||||||
switch lx.next() {
|
|
||||||
case eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case rawStringEnd:
|
|
||||||
if lx.accept(rawStringEnd) {
|
|
||||||
if lx.accept(rawStringEnd) {
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemRawMultilineString)
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexMultilineRawString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineStringEscape consumes an escaped character. It assumes that the
|
|
||||||
// preceding '\\' has already been consumed.
|
|
||||||
func lexMultilineStringEscape(lx *lexer) stateFn {
|
|
||||||
// Handle the special case first:
|
|
||||||
if isNL(lx.next()) {
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexMultilineString)
|
|
||||||
return lexStringEscape(lx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexStringEscape(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch r {
|
|
||||||
case 'b':
|
|
||||||
fallthrough
|
|
||||||
case 't':
|
|
||||||
fallthrough
|
|
||||||
case 'n':
|
|
||||||
fallthrough
|
|
||||||
case 'f':
|
|
||||||
fallthrough
|
|
||||||
case 'r':
|
|
||||||
fallthrough
|
|
||||||
case '"':
|
|
||||||
fallthrough
|
|
||||||
case '\\':
|
|
||||||
return lx.pop()
|
|
||||||
case 'u':
|
|
||||||
return lexShortUnicodeEscape
|
|
||||||
case 'U':
|
|
||||||
return lexLongUnicodeEscape
|
|
||||||
}
|
|
||||||
return lx.errorf("invalid escape character %q; only the following "+
|
|
||||||
"escape characters are allowed: "+
|
|
||||||
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexShortUnicodeEscape(lx *lexer) stateFn {
|
|
||||||
var r rune
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
r = lx.next()
|
|
||||||
if !isHexadecimal(r) {
|
|
||||||
return lx.errorf(`expected four hexadecimal digits after '\u', `+
|
|
||||||
"but got %q instead", lx.current())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexLongUnicodeEscape(lx *lexer) stateFn {
|
|
||||||
var r rune
|
|
||||||
for i := 0; i < 8; i++ {
|
|
||||||
r = lx.next()
|
|
||||||
if !isHexadecimal(r) {
|
|
||||||
return lx.errorf(`expected eight hexadecimal digits after '\U', `+
|
|
||||||
"but got %q instead", lx.current())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberOrDateStart consumes either an integer, a float, or datetime.
|
|
||||||
func lexNumberOrDateStart(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumberOrDate
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
case '.':
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a digit but got %q", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberOrDate consumes either an integer, float or datetime.
|
|
||||||
func lexNumberOrDate(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumberOrDate
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '-':
|
|
||||||
return lexDatetime
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemInteger)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexDatetime consumes a Datetime, to a first approximation.
|
|
||||||
// The parser validates that it matches one of the accepted formats.
|
|
||||||
func lexDatetime(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexDatetime
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '-', 'T', ':', '.', 'Z', '+':
|
|
||||||
return lexDatetime
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemDatetime)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberStart consumes either an integer or a float. It assumes that a sign
|
|
||||||
// has already been read, but that *no* digits have been consumed.
|
|
||||||
// lexNumberStart will move to the appropriate integer or float states.
|
|
||||||
func lexNumberStart(lx *lexer) stateFn {
|
|
||||||
// We MUST see a digit. Even floats have to start with a digit.
|
|
||||||
r := lx.next()
|
|
||||||
if !isDigit(r) {
|
|
||||||
if r == '.' {
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a digit but got %q", r)
|
|
||||||
}
|
|
||||||
return lexNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumber consumes an integer or a float after seeing the first digit.
|
|
||||||
func lexNumber(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumber
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemInteger)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexFloat consumes the elements of a float. It allows any sequence of
|
|
||||||
// float-like characters, so floats emitted by the lexer are only a first
|
|
||||||
// approximation and must be validated by the parser.
|
|
||||||
func lexFloat(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_', '.', '-', '+', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemFloat)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBool consumes a bool string: 'true' or 'false.
|
|
||||||
func lexBool(lx *lexer) stateFn {
|
|
||||||
var rs []rune
|
|
||||||
for {
|
|
||||||
r := lx.next()
|
|
||||||
if !unicode.IsLetter(r) {
|
|
||||||
lx.backup()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rs = append(rs, r)
|
|
||||||
}
|
|
||||||
s := string(rs)
|
|
||||||
switch s {
|
|
||||||
case "true", "false":
|
|
||||||
lx.emit(itemBool)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lx.errorf("expected value but found %q instead", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexCommentStart begins the lexing of a comment. It will emit
|
|
||||||
// itemCommentStart and consume no characters, passing control to lexComment.
|
|
||||||
func lexCommentStart(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemCommentStart)
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
|
|
||||||
// It will consume *up to* the first newline character, and pass control
|
|
||||||
// back to the last state on the stack.
|
|
||||||
func lexComment(lx *lexer) stateFn {
|
|
||||||
r := lx.peek()
|
|
||||||
if isNL(r) || r == eof {
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.next()
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexSkip ignores all slurped input and moves on to the next state.
|
|
||||||
func lexSkip(lx *lexer, nextState stateFn) stateFn {
|
|
||||||
return func(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
return nextState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isWhitespace returns true if `r` is a whitespace character according
|
|
||||||
// to the spec.
|
|
||||||
func isWhitespace(r rune) bool {
|
|
||||||
return r == '\t' || r == ' '
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNL(r rune) bool {
|
|
||||||
return r == '\n' || r == '\r'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDigit(r rune) bool {
|
|
||||||
return r >= '0' && r <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHexadecimal(r rune) bool {
|
|
||||||
return (r >= '0' && r <= '9') ||
|
|
||||||
(r >= 'a' && r <= 'f') ||
|
|
||||||
(r >= 'A' && r <= 'F')
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBareKeyChar(r rune) bool {
|
|
||||||
return (r >= 'A' && r <= 'Z') ||
|
|
||||||
(r >= 'a' && r <= 'z') ||
|
|
||||||
(r >= '0' && r <= '9') ||
|
|
||||||
r == '_' ||
|
|
||||||
r == '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
func (itype itemType) String() string {
|
|
||||||
switch itype {
|
|
||||||
case itemError:
|
|
||||||
return "Error"
|
|
||||||
case itemNIL:
|
|
||||||
return "NIL"
|
|
||||||
case itemEOF:
|
|
||||||
return "EOF"
|
|
||||||
case itemText:
|
|
||||||
return "Text"
|
|
||||||
case itemString, itemRawString, itemMultilineString, itemRawMultilineString:
|
|
||||||
return "String"
|
|
||||||
case itemBool:
|
|
||||||
return "Bool"
|
|
||||||
case itemInteger:
|
|
||||||
return "Integer"
|
|
||||||
case itemFloat:
|
|
||||||
return "Float"
|
|
||||||
case itemDatetime:
|
|
||||||
return "DateTime"
|
|
||||||
case itemTableStart:
|
|
||||||
return "TableStart"
|
|
||||||
case itemTableEnd:
|
|
||||||
return "TableEnd"
|
|
||||||
case itemKeyStart:
|
|
||||||
return "KeyStart"
|
|
||||||
case itemArray:
|
|
||||||
return "Array"
|
|
||||||
case itemArrayEnd:
|
|
||||||
return "ArrayEnd"
|
|
||||||
case itemCommentStart:
|
|
||||||
return "CommentStart"
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (item item) String() string {
|
|
||||||
return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val)
|
|
||||||
}
|
|
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
|
@ -1,592 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type parser struct {
|
|
||||||
mapping map[string]interface{}
|
|
||||||
types map[string]tomlType
|
|
||||||
lx *lexer
|
|
||||||
|
|
||||||
// A list of keys in the order that they appear in the TOML data.
|
|
||||||
ordered []Key
|
|
||||||
|
|
||||||
// the full key for the current hash in scope
|
|
||||||
context Key
|
|
||||||
|
|
||||||
// the base key name for everything except hashes
|
|
||||||
currentKey string
|
|
||||||
|
|
||||||
// rough approximation of line number
|
|
||||||
approxLine int
|
|
||||||
|
|
||||||
// A map of 'key.group.names' to whether they were created implicitly.
|
|
||||||
implicits map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type parseError string
|
|
||||||
|
|
||||||
func (pe parseError) Error() string {
|
|
||||||
return string(pe)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(data string) (p *parser, err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
var ok bool
|
|
||||||
if err, ok = r.(parseError); ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
p = &parser{
|
|
||||||
mapping: make(map[string]interface{}),
|
|
||||||
types: make(map[string]tomlType),
|
|
||||||
lx: lex(data),
|
|
||||||
ordered: make([]Key, 0),
|
|
||||||
implicits: make(map[string]bool),
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
item := p.next()
|
|
||||||
if item.typ == itemEOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.topLevel(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) panicf(format string, v ...interface{}) {
|
|
||||||
msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
|
|
||||||
p.approxLine, p.current(), fmt.Sprintf(format, v...))
|
|
||||||
panic(parseError(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) next() item {
|
|
||||||
it := p.lx.nextItem()
|
|
||||||
if it.typ == itemError {
|
|
||||||
p.panicf("%s", it.val)
|
|
||||||
}
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) bug(format string, v ...interface{}) {
|
|
||||||
panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) expect(typ itemType) item {
|
|
||||||
it := p.next()
|
|
||||||
p.assertEqual(typ, it.typ)
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) assertEqual(expected, got itemType) {
|
|
||||||
if expected != got {
|
|
||||||
p.bug("Expected '%s' but got '%s'.", expected, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) topLevel(item item) {
|
|
||||||
switch item.typ {
|
|
||||||
case itemCommentStart:
|
|
||||||
p.approxLine = item.line
|
|
||||||
p.expect(itemText)
|
|
||||||
case itemTableStart:
|
|
||||||
kg := p.next()
|
|
||||||
p.approxLine = kg.line
|
|
||||||
|
|
||||||
var key Key
|
|
||||||
for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
|
|
||||||
key = append(key, p.keyString(kg))
|
|
||||||
}
|
|
||||||
p.assertEqual(itemTableEnd, kg.typ)
|
|
||||||
|
|
||||||
p.establishContext(key, false)
|
|
||||||
p.setType("", tomlHash)
|
|
||||||
p.ordered = append(p.ordered, key)
|
|
||||||
case itemArrayTableStart:
|
|
||||||
kg := p.next()
|
|
||||||
p.approxLine = kg.line
|
|
||||||
|
|
||||||
var key Key
|
|
||||||
for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
|
|
||||||
key = append(key, p.keyString(kg))
|
|
||||||
}
|
|
||||||
p.assertEqual(itemArrayTableEnd, kg.typ)
|
|
||||||
|
|
||||||
p.establishContext(key, true)
|
|
||||||
p.setType("", tomlArrayHash)
|
|
||||||
p.ordered = append(p.ordered, key)
|
|
||||||
case itemKeyStart:
|
|
||||||
kname := p.next()
|
|
||||||
p.approxLine = kname.line
|
|
||||||
p.currentKey = p.keyString(kname)
|
|
||||||
|
|
||||||
val, typ := p.value(p.next())
|
|
||||||
p.setValue(p.currentKey, val)
|
|
||||||
p.setType(p.currentKey, typ)
|
|
||||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
|
||||||
p.currentKey = ""
|
|
||||||
default:
|
|
||||||
p.bug("Unexpected type at top level: %s", item.typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets a string for a key (or part of a key in a table name).
|
|
||||||
func (p *parser) keyString(it item) string {
|
|
||||||
switch it.typ {
|
|
||||||
case itemText:
|
|
||||||
return it.val
|
|
||||||
case itemString, itemMultilineString,
|
|
||||||
itemRawString, itemRawMultilineString:
|
|
||||||
s, _ := p.value(it)
|
|
||||||
return s.(string)
|
|
||||||
default:
|
|
||||||
p.bug("Unexpected key type: %s", it.typ)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// value translates an expected value from the lexer into a Go value wrapped
|
|
||||||
// as an empty interface.
|
|
||||||
func (p *parser) value(it item) (interface{}, tomlType) {
|
|
||||||
switch it.typ {
|
|
||||||
case itemString:
|
|
||||||
return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
|
|
||||||
case itemMultilineString:
|
|
||||||
trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
|
|
||||||
return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
|
|
||||||
case itemRawString:
|
|
||||||
return it.val, p.typeOfPrimitive(it)
|
|
||||||
case itemRawMultilineString:
|
|
||||||
return stripFirstNewline(it.val), p.typeOfPrimitive(it)
|
|
||||||
case itemBool:
|
|
||||||
switch it.val {
|
|
||||||
case "true":
|
|
||||||
return true, p.typeOfPrimitive(it)
|
|
||||||
case "false":
|
|
||||||
return false, p.typeOfPrimitive(it)
|
|
||||||
}
|
|
||||||
p.bug("Expected boolean value, but got '%s'.", it.val)
|
|
||||||
case itemInteger:
|
|
||||||
if !numUnderscoresOK(it.val) {
|
|
||||||
p.panicf("Invalid integer %q: underscores must be surrounded by digits",
|
|
||||||
it.val)
|
|
||||||
}
|
|
||||||
val := strings.Replace(it.val, "_", "", -1)
|
|
||||||
num, err := strconv.ParseInt(val, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
// Distinguish integer values. Normally, it'd be a bug if the lexer
|
|
||||||
// provides an invalid integer, but it's possible that the number is
|
|
||||||
// out of range of valid values (which the lexer cannot determine).
|
|
||||||
// So mark the former as a bug but the latter as a legitimate user
|
|
||||||
// error.
|
|
||||||
if e, ok := err.(*strconv.NumError); ok &&
|
|
||||||
e.Err == strconv.ErrRange {
|
|
||||||
|
|
||||||
p.panicf("Integer '%s' is out of the range of 64-bit "+
|
|
||||||
"signed integers.", it.val)
|
|
||||||
} else {
|
|
||||||
p.bug("Expected integer value, but got '%s'.", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num, p.typeOfPrimitive(it)
|
|
||||||
case itemFloat:
|
|
||||||
parts := strings.FieldsFunc(it.val, func(r rune) bool {
|
|
||||||
switch r {
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
for _, part := range parts {
|
|
||||||
if !numUnderscoresOK(part) {
|
|
||||||
p.panicf("Invalid float %q: underscores must be "+
|
|
||||||
"surrounded by digits", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !numPeriodsOK(it.val) {
|
|
||||||
// As a special case, numbers like '123.' or '1.e2',
|
|
||||||
// which are valid as far as Go/strconv are concerned,
|
|
||||||
// must be rejected because TOML says that a fractional
|
|
||||||
// part consists of '.' followed by 1+ digits.
|
|
||||||
p.panicf("Invalid float %q: '.' must be followed "+
|
|
||||||
"by one or more digits", it.val)
|
|
||||||
}
|
|
||||||
val := strings.Replace(it.val, "_", "", -1)
|
|
||||||
num, err := strconv.ParseFloat(val, 64)
|
|
||||||
if err != nil {
|
|
||||||
if e, ok := err.(*strconv.NumError); ok &&
|
|
||||||
e.Err == strconv.ErrRange {
|
|
||||||
|
|
||||||
p.panicf("Float '%s' is out of the range of 64-bit "+
|
|
||||||
"IEEE-754 floating-point numbers.", it.val)
|
|
||||||
} else {
|
|
||||||
p.panicf("Invalid float value: %q", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num, p.typeOfPrimitive(it)
|
|
||||||
case itemDatetime:
|
|
||||||
var t time.Time
|
|
||||||
var ok bool
|
|
||||||
var err error
|
|
||||||
for _, format := range []string{
|
|
||||||
"2006-01-02T15:04:05Z07:00",
|
|
||||||
"2006-01-02T15:04:05",
|
|
||||||
"2006-01-02",
|
|
||||||
} {
|
|
||||||
t, err = time.ParseInLocation(format, it.val, time.Local)
|
|
||||||
if err == nil {
|
|
||||||
ok = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
p.panicf("Invalid TOML Datetime: %q.", it.val)
|
|
||||||
}
|
|
||||||
return t, p.typeOfPrimitive(it)
|
|
||||||
case itemArray:
|
|
||||||
array := make([]interface{}, 0)
|
|
||||||
types := make([]tomlType, 0)
|
|
||||||
|
|
||||||
for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
|
|
||||||
if it.typ == itemCommentStart {
|
|
||||||
p.expect(itemText)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val, typ := p.value(it)
|
|
||||||
array = append(array, val)
|
|
||||||
types = append(types, typ)
|
|
||||||
}
|
|
||||||
return array, p.typeOfArray(types)
|
|
||||||
case itemInlineTableStart:
|
|
||||||
var (
|
|
||||||
hash = make(map[string]interface{})
|
|
||||||
outerContext = p.context
|
|
||||||
outerKey = p.currentKey
|
|
||||||
)
|
|
||||||
|
|
||||||
p.context = append(p.context, p.currentKey)
|
|
||||||
p.currentKey = ""
|
|
||||||
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
|
|
||||||
if it.typ != itemKeyStart {
|
|
||||||
p.bug("Expected key start but instead found %q, around line %d",
|
|
||||||
it.val, p.approxLine)
|
|
||||||
}
|
|
||||||
if it.typ == itemCommentStart {
|
|
||||||
p.expect(itemText)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve key
|
|
||||||
k := p.next()
|
|
||||||
p.approxLine = k.line
|
|
||||||
kname := p.keyString(k)
|
|
||||||
|
|
||||||
// retrieve value
|
|
||||||
p.currentKey = kname
|
|
||||||
val, typ := p.value(p.next())
|
|
||||||
// make sure we keep metadata up to date
|
|
||||||
p.setType(kname, typ)
|
|
||||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
|
||||||
hash[kname] = val
|
|
||||||
}
|
|
||||||
p.context = outerContext
|
|
||||||
p.currentKey = outerKey
|
|
||||||
return hash, tomlHash
|
|
||||||
}
|
|
||||||
p.bug("Unexpected value type: %s", it.typ)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// numUnderscoresOK checks whether each underscore in s is surrounded by
|
|
||||||
// characters that are not underscores.
|
|
||||||
func numUnderscoresOK(s string) bool {
|
|
||||||
accept := false
|
|
||||||
for _, r := range s {
|
|
||||||
if r == '_' {
|
|
||||||
if !accept {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
accept = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
accept = true
|
|
||||||
}
|
|
||||||
return accept
|
|
||||||
}
|
|
||||||
|
|
||||||
// numPeriodsOK checks whether every period in s is followed by a digit.
|
|
||||||
func numPeriodsOK(s string) bool {
|
|
||||||
period := false
|
|
||||||
for _, r := range s {
|
|
||||||
if period && !isDigit(r) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
period = r == '.'
|
|
||||||
}
|
|
||||||
return !period
|
|
||||||
}
|
|
||||||
|
|
||||||
// establishContext sets the current context of the parser,
|
|
||||||
// where the context is either a hash or an array of hashes. Which one is
|
|
||||||
// set depends on the value of the `array` parameter.
|
|
||||||
//
|
|
||||||
// Establishing the context also makes sure that the key isn't a duplicate, and
|
|
||||||
// will create implicit hashes automatically.
|
|
||||||
func (p *parser) establishContext(key Key, array bool) {
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
// Always start at the top level and drill down for our context.
|
|
||||||
hashContext := p.mapping
|
|
||||||
keyContext := make(Key, 0)
|
|
||||||
|
|
||||||
// We only need implicit hashes for key[0:-1]
|
|
||||||
for _, k := range key[0 : len(key)-1] {
|
|
||||||
_, ok = hashContext[k]
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
|
|
||||||
// No key? Make an implicit hash and move on.
|
|
||||||
if !ok {
|
|
||||||
p.addImplicit(keyContext)
|
|
||||||
hashContext[k] = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the hash context is actually an array of tables, then set
|
|
||||||
// the hash context to the last element in that array.
|
|
||||||
//
|
|
||||||
// Otherwise, it better be a table, since this MUST be a key group (by
|
|
||||||
// virtue of it not being the last element in a key).
|
|
||||||
switch t := hashContext[k].(type) {
|
|
||||||
case []map[string]interface{}:
|
|
||||||
hashContext = t[len(t)-1]
|
|
||||||
case map[string]interface{}:
|
|
||||||
hashContext = t
|
|
||||||
default:
|
|
||||||
p.panicf("Key '%s' was already created as a hash.", keyContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.context = keyContext
|
|
||||||
if array {
|
|
||||||
// If this is the first element for this array, then allocate a new
|
|
||||||
// list of tables for it.
|
|
||||||
k := key[len(key)-1]
|
|
||||||
if _, ok := hashContext[k]; !ok {
|
|
||||||
hashContext[k] = make([]map[string]interface{}, 0, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a new table. But make sure the key hasn't already been used
|
|
||||||
// for something else.
|
|
||||||
if hash, ok := hashContext[k].([]map[string]interface{}); ok {
|
|
||||||
hashContext[k] = append(hash, make(map[string]interface{}))
|
|
||||||
} else {
|
|
||||||
p.panicf("Key '%s' was already created and cannot be used as "+
|
|
||||||
"an array.", keyContext)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.setValue(key[len(key)-1], make(map[string]interface{}))
|
|
||||||
}
|
|
||||||
p.context = append(p.context, key[len(key)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// setValue sets the given key to the given value in the current context.
|
|
||||||
// It will make sure that the key hasn't already been defined, account for
|
|
||||||
// implicit key groups.
|
|
||||||
func (p *parser) setValue(key string, value interface{}) {
|
|
||||||
var tmpHash interface{}
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
hash := p.mapping
|
|
||||||
keyContext := make(Key, 0)
|
|
||||||
for _, k := range p.context {
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
if tmpHash, ok = hash[k]; !ok {
|
|
||||||
p.bug("Context for key '%s' has not been established.", keyContext)
|
|
||||||
}
|
|
||||||
switch t := tmpHash.(type) {
|
|
||||||
case []map[string]interface{}:
|
|
||||||
// The context is a table of hashes. Pick the most recent table
|
|
||||||
// defined as the current hash.
|
|
||||||
hash = t[len(t)-1]
|
|
||||||
case map[string]interface{}:
|
|
||||||
hash = t
|
|
||||||
default:
|
|
||||||
p.bug("Expected hash to have type 'map[string]interface{}', but "+
|
|
||||||
"it has '%T' instead.", tmpHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyContext = append(keyContext, key)
|
|
||||||
|
|
||||||
if _, ok := hash[key]; ok {
|
|
||||||
// Typically, if the given key has already been set, then we have
|
|
||||||
// to raise an error since duplicate keys are disallowed. However,
|
|
||||||
// it's possible that a key was previously defined implicitly. In this
|
|
||||||
// case, it is allowed to be redefined concretely. (See the
|
|
||||||
// `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
|
|
||||||
//
|
|
||||||
// But we have to make sure to stop marking it as an implicit. (So that
|
|
||||||
// another redefinition provokes an error.)
|
|
||||||
//
|
|
||||||
// Note that since it has already been defined (as a hash), we don't
|
|
||||||
// want to overwrite it. So our business is done.
|
|
||||||
if p.isImplicit(keyContext) {
|
|
||||||
p.removeImplicit(keyContext)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we have a concrete key trying to override a previous
|
|
||||||
// key, which is *always* wrong.
|
|
||||||
p.panicf("Key '%s' has already been defined.", keyContext)
|
|
||||||
}
|
|
||||||
hash[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// setType sets the type of a particular value at a given key.
|
|
||||||
// It should be called immediately AFTER setValue.
|
|
||||||
//
|
|
||||||
// Note that if `key` is empty, then the type given will be applied to the
|
|
||||||
// current context (which is either a table or an array of tables).
|
|
||||||
func (p *parser) setType(key string, typ tomlType) {
|
|
||||||
keyContext := make(Key, 0, len(p.context)+1)
|
|
||||||
for _, k := range p.context {
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
}
|
|
||||||
if len(key) > 0 { // allow type setting for hashes
|
|
||||||
keyContext = append(keyContext, key)
|
|
||||||
}
|
|
||||||
p.types[keyContext.String()] = typ
|
|
||||||
}
|
|
||||||
|
|
||||||
// addImplicit sets the given Key as having been created implicitly.
|
|
||||||
func (p *parser) addImplicit(key Key) {
|
|
||||||
p.implicits[key.String()] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeImplicit stops tagging the given key as having been implicitly
|
|
||||||
// created.
|
|
||||||
func (p *parser) removeImplicit(key Key) {
|
|
||||||
p.implicits[key.String()] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// isImplicit returns true if the key group pointed to by the key was created
|
|
||||||
// implicitly.
|
|
||||||
func (p *parser) isImplicit(key Key) bool {
|
|
||||||
return p.implicits[key.String()]
|
|
||||||
}
|
|
||||||
|
|
||||||
// current returns the full key name of the current context.
|
|
||||||
func (p *parser) current() string {
|
|
||||||
if len(p.currentKey) == 0 {
|
|
||||||
return p.context.String()
|
|
||||||
}
|
|
||||||
if len(p.context) == 0 {
|
|
||||||
return p.currentKey
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s.%s", p.context, p.currentKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripFirstNewline(s string) string {
|
|
||||||
if len(s) == 0 || s[0] != '\n' {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripEscapedWhitespace(s string) string {
|
|
||||||
esc := strings.Split(s, "\\\n")
|
|
||||||
if len(esc) > 1 {
|
|
||||||
for i := 1; i < len(esc); i++ {
|
|
||||||
esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(esc, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) replaceEscapes(str string) string {
|
|
||||||
var replaced []rune
|
|
||||||
s := []byte(str)
|
|
||||||
r := 0
|
|
||||||
for r < len(s) {
|
|
||||||
if s[r] != '\\' {
|
|
||||||
c, size := utf8.DecodeRune(s[r:])
|
|
||||||
r += size
|
|
||||||
replaced = append(replaced, c)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r += 1
|
|
||||||
if r >= len(s) {
|
|
||||||
p.bug("Escape sequence at end of string.")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
switch s[r] {
|
|
||||||
default:
|
|
||||||
p.bug("Expected valid escape code after \\, but got %q.", s[r])
|
|
||||||
return ""
|
|
||||||
case 'b':
|
|
||||||
replaced = append(replaced, rune(0x0008))
|
|
||||||
r += 1
|
|
||||||
case 't':
|
|
||||||
replaced = append(replaced, rune(0x0009))
|
|
||||||
r += 1
|
|
||||||
case 'n':
|
|
||||||
replaced = append(replaced, rune(0x000A))
|
|
||||||
r += 1
|
|
||||||
case 'f':
|
|
||||||
replaced = append(replaced, rune(0x000C))
|
|
||||||
r += 1
|
|
||||||
case 'r':
|
|
||||||
replaced = append(replaced, rune(0x000D))
|
|
||||||
r += 1
|
|
||||||
case '"':
|
|
||||||
replaced = append(replaced, rune(0x0022))
|
|
||||||
r += 1
|
|
||||||
case '\\':
|
|
||||||
replaced = append(replaced, rune(0x005C))
|
|
||||||
r += 1
|
|
||||||
case 'u':
|
|
||||||
// At this point, we know we have a Unicode escape of the form
|
|
||||||
// `uXXXX` at [r, r+5). (Because the lexer guarantees this
|
|
||||||
// for us.)
|
|
||||||
escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
|
|
||||||
replaced = append(replaced, escaped)
|
|
||||||
r += 5
|
|
||||||
case 'U':
|
|
||||||
// At this point, we know we have a Unicode escape of the form
|
|
||||||
// `uXXXX` at [r, r+9). (Because the lexer guarantees this
|
|
||||||
// for us.)
|
|
||||||
escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
|
|
||||||
replaced = append(replaced, escaped)
|
|
||||||
r += 9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(replaced)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
|
|
||||||
s := string(bs)
|
|
||||||
hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
p.bug("Could not parse '%s' as a hexadecimal number, but the "+
|
|
||||||
"lexer claims it's OK: %s", s, err)
|
|
||||||
}
|
|
||||||
if !utf8.ValidRune(rune(hex)) {
|
|
||||||
p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
|
|
||||||
}
|
|
||||||
return rune(hex)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isStringType(ty itemType) bool {
|
|
||||||
return ty == itemString || ty == itemMultilineString ||
|
|
||||||
ty == itemRawString || ty == itemRawMultilineString
|
|
||||||
}
|
|
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
|
@ -1,91 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
// tomlType represents any Go type that corresponds to a TOML type.
|
|
||||||
// While the first draft of the TOML spec has a simplistic type system that
|
|
||||||
// probably doesn't need this level of sophistication, we seem to be militating
|
|
||||||
// toward adding real composite types.
|
|
||||||
type tomlType interface {
|
|
||||||
typeString() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeEqual accepts any two types and returns true if they are equal.
|
|
||||||
func typeEqual(t1, t2 tomlType) bool {
|
|
||||||
if t1 == nil || t2 == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return t1.typeString() == t2.typeString()
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeIsHash(t tomlType) bool {
|
|
||||||
return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tomlBaseType string
|
|
||||||
|
|
||||||
func (btype tomlBaseType) typeString() string {
|
|
||||||
return string(btype)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (btype tomlBaseType) String() string {
|
|
||||||
return btype.typeString()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tomlInteger tomlBaseType = "Integer"
|
|
||||||
tomlFloat tomlBaseType = "Float"
|
|
||||||
tomlDatetime tomlBaseType = "Datetime"
|
|
||||||
tomlString tomlBaseType = "String"
|
|
||||||
tomlBool tomlBaseType = "Bool"
|
|
||||||
tomlArray tomlBaseType = "Array"
|
|
||||||
tomlHash tomlBaseType = "Hash"
|
|
||||||
tomlArrayHash tomlBaseType = "ArrayHash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// typeOfPrimitive returns a tomlType of any primitive value in TOML.
|
|
||||||
// Primitive values are: Integer, Float, Datetime, String and Bool.
|
|
||||||
//
|
|
||||||
// Passing a lexer item other than the following will cause a BUG message
|
|
||||||
// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
|
|
||||||
func (p *parser) typeOfPrimitive(lexItem item) tomlType {
|
|
||||||
switch lexItem.typ {
|
|
||||||
case itemInteger:
|
|
||||||
return tomlInteger
|
|
||||||
case itemFloat:
|
|
||||||
return tomlFloat
|
|
||||||
case itemDatetime:
|
|
||||||
return tomlDatetime
|
|
||||||
case itemString:
|
|
||||||
return tomlString
|
|
||||||
case itemMultilineString:
|
|
||||||
return tomlString
|
|
||||||
case itemRawString:
|
|
||||||
return tomlString
|
|
||||||
case itemRawMultilineString:
|
|
||||||
return tomlString
|
|
||||||
case itemBool:
|
|
||||||
return tomlBool
|
|
||||||
}
|
|
||||||
p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeOfArray returns a tomlType for an array given a list of types of its
|
|
||||||
// values.
|
|
||||||
//
|
|
||||||
// In the current spec, if an array is homogeneous, then its type is always
|
|
||||||
// "Array". If the array is not homogeneous, an error is generated.
|
|
||||||
func (p *parser) typeOfArray(types []tomlType) tomlType {
|
|
||||||
// Empty arrays are cool.
|
|
||||||
if len(types) == 0 {
|
|
||||||
return tomlArray
|
|
||||||
}
|
|
||||||
|
|
||||||
theType := types[0]
|
|
||||||
for _, t := range types[1:] {
|
|
||||||
if !typeEqual(theType, t) {
|
|
||||||
p.panicf("Array contains values of type '%s' and '%s', but "+
|
|
||||||
"arrays must be homogeneous.", theType, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tomlArray
|
|
||||||
}
|
|
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
|
@ -1,242 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
// Struct field handling is adapted from code in encoding/json:
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the Go distribution.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A field represents a single field found in a struct.
|
|
||||||
type field struct {
|
|
||||||
name string // the name of the field (`toml` tag included)
|
|
||||||
tag bool // whether field has a `toml` tag
|
|
||||||
index []int // represents the depth of an anonymous field
|
|
||||||
typ reflect.Type // the type of the field
|
|
||||||
}
|
|
||||||
|
|
||||||
// byName sorts field by name, breaking ties with depth,
|
|
||||||
// then breaking ties with "name came from toml 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 TOML 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 != "" && !sf.Anonymous { // unexported
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
opts := getOptions(sf.Tag)
|
|
||||||
if opts.skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
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 opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
|
||||||
tagged := opts.name != ""
|
|
||||||
name := opts.name
|
|
||||||
if name == "" {
|
|
||||||
name = sf.Name
|
|
||||||
}
|
|
||||||
fields = append(fields, field{name, tagged, index, ft})
|
|
||||||
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 {
|
|
||||||
f := field{name: ft.Name(), index: index, typ: ft}
|
|
||||||
next = append(next, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(byName(fields))
|
|
||||||
|
|
||||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
|
||||||
// except that fields with TOML 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
|
|
||||||
// TOML 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
|
|
||||||
}
|
|
27
vendor/github.com/alecthomas/template/LICENSE
generated
vendored
27
vendor/github.com/alecthomas/template/LICENSE
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
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.
|
|
406
vendor/github.com/alecthomas/template/doc.go
generated
vendored
406
vendor/github.com/alecthomas/template/doc.go
generated
vendored
|
@ -1,406 +0,0 @@
|
||||||
// Copyright 2011 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 template implements data-driven templates for generating textual output.
|
|
||||||
|
|
||||||
To generate HTML output, see package html/template, which has the same interface
|
|
||||||
as this package but automatically secures HTML output against certain attacks.
|
|
||||||
|
|
||||||
Templates are executed by applying them to a data structure. Annotations in the
|
|
||||||
template refer to elements of the data structure (typically a field of a struct
|
|
||||||
or a key in a map) to control execution and derive values to be displayed.
|
|
||||||
Execution of the template walks the structure and sets the cursor, represented
|
|
||||||
by a period '.' and called "dot", to the value at the current location in the
|
|
||||||
structure as execution proceeds.
|
|
||||||
|
|
||||||
The input text for a template is UTF-8-encoded text in any format.
|
|
||||||
"Actions"--data evaluations or control structures--are delimited by
|
|
||||||
"{{" and "}}"; all text outside actions is copied to the output unchanged.
|
|
||||||
Actions may not span newlines, although comments can.
|
|
||||||
|
|
||||||
Once parsed, a template may be executed safely in parallel.
|
|
||||||
|
|
||||||
Here is a trivial example that prints "17 items are made of wool".
|
|
||||||
|
|
||||||
type Inventory struct {
|
|
||||||
Material string
|
|
||||||
Count uint
|
|
||||||
}
|
|
||||||
sweaters := Inventory{"wool", 17}
|
|
||||||
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
|
|
||||||
if err != nil { panic(err) }
|
|
||||||
err = tmpl.Execute(os.Stdout, sweaters)
|
|
||||||
if err != nil { panic(err) }
|
|
||||||
|
|
||||||
More intricate examples appear below.
|
|
||||||
|
|
||||||
Actions
|
|
||||||
|
|
||||||
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
|
|
||||||
data, defined in detail below.
|
|
||||||
|
|
||||||
*/
|
|
||||||
// {{/* a comment */}}
|
|
||||||
// A comment; discarded. May contain newlines.
|
|
||||||
// Comments do not nest and must start and end at the
|
|
||||||
// delimiters, as shown here.
|
|
||||||
/*
|
|
||||||
|
|
||||||
{{pipeline}}
|
|
||||||
The default textual representation of the value of the pipeline
|
|
||||||
is copied to the output.
|
|
||||||
|
|
||||||
{{if pipeline}} T1 {{end}}
|
|
||||||
If the value of the pipeline is empty, no output is generated;
|
|
||||||
otherwise, T1 is executed. The empty values are false, 0, any
|
|
||||||
nil pointer or interface value, and any array, slice, map, or
|
|
||||||
string of length zero.
|
|
||||||
Dot is unaffected.
|
|
||||||
|
|
||||||
{{if pipeline}} T1 {{else}} T0 {{end}}
|
|
||||||
If the value of the pipeline is empty, T0 is executed;
|
|
||||||
otherwise, T1 is executed. Dot is unaffected.
|
|
||||||
|
|
||||||
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
|
|
||||||
To simplify the appearance of if-else chains, the else action
|
|
||||||
of an if may include another if directly; the effect is exactly
|
|
||||||
the same as writing
|
|
||||||
{{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
|
|
||||||
|
|
||||||
{{range pipeline}} T1 {{end}}
|
|
||||||
The value of the pipeline must be an array, slice, map, or channel.
|
|
||||||
If the value of the pipeline has length zero, nothing is output;
|
|
||||||
otherwise, dot is set to the successive elements of the array,
|
|
||||||
slice, or map and T1 is executed. If the value is a map and the
|
|
||||||
keys are of basic type with a defined order ("comparable"), the
|
|
||||||
elements will be visited in sorted key order.
|
|
||||||
|
|
||||||
{{range pipeline}} T1 {{else}} T0 {{end}}
|
|
||||||
The value of the pipeline must be an array, slice, map, or channel.
|
|
||||||
If the value of the pipeline has length zero, dot is unaffected and
|
|
||||||
T0 is executed; otherwise, dot is set to the successive elements
|
|
||||||
of the array, slice, or map and T1 is executed.
|
|
||||||
|
|
||||||
{{template "name"}}
|
|
||||||
The template with the specified name is executed with nil data.
|
|
||||||
|
|
||||||
{{template "name" pipeline}}
|
|
||||||
The template with the specified name is executed with dot set
|
|
||||||
to the value of the pipeline.
|
|
||||||
|
|
||||||
{{with pipeline}} T1 {{end}}
|
|
||||||
If the value of the pipeline is empty, no output is generated;
|
|
||||||
otherwise, dot is set to the value of the pipeline and T1 is
|
|
||||||
executed.
|
|
||||||
|
|
||||||
{{with pipeline}} T1 {{else}} T0 {{end}}
|
|
||||||
If the value of the pipeline is empty, dot is unaffected and T0
|
|
||||||
is executed; otherwise, dot is set to the value of the pipeline
|
|
||||||
and T1 is executed.
|
|
||||||
|
|
||||||
Arguments
|
|
||||||
|
|
||||||
An argument is a simple value, denoted by one of the following.
|
|
||||||
|
|
||||||
- A boolean, string, character, integer, floating-point, imaginary
|
|
||||||
or complex constant in Go syntax. These behave like Go's untyped
|
|
||||||
constants, although raw strings may not span newlines.
|
|
||||||
- The keyword nil, representing an untyped Go nil.
|
|
||||||
- The character '.' (period):
|
|
||||||
.
|
|
||||||
The result is the value of dot.
|
|
||||||
- A variable name, which is a (possibly empty) alphanumeric string
|
|
||||||
preceded by a dollar sign, such as
|
|
||||||
$piOver2
|
|
||||||
or
|
|
||||||
$
|
|
||||||
The result is the value of the variable.
|
|
||||||
Variables are described below.
|
|
||||||
- The name of a field of the data, which must be a struct, preceded
|
|
||||||
by a period, such as
|
|
||||||
.Field
|
|
||||||
The result is the value of the field. Field invocations may be
|
|
||||||
chained:
|
|
||||||
.Field1.Field2
|
|
||||||
Fields can also be evaluated on variables, including chaining:
|
|
||||||
$x.Field1.Field2
|
|
||||||
- The name of a key of the data, which must be a map, preceded
|
|
||||||
by a period, such as
|
|
||||||
.Key
|
|
||||||
The result is the map element value indexed by the key.
|
|
||||||
Key invocations may be chained and combined with fields to any
|
|
||||||
depth:
|
|
||||||
.Field1.Key1.Field2.Key2
|
|
||||||
Although the key must be an alphanumeric identifier, unlike with
|
|
||||||
field names they do not need to start with an upper case letter.
|
|
||||||
Keys can also be evaluated on variables, including chaining:
|
|
||||||
$x.key1.key2
|
|
||||||
- The name of a niladic method of the data, preceded by a period,
|
|
||||||
such as
|
|
||||||
.Method
|
|
||||||
The result is the value of invoking the method with dot as the
|
|
||||||
receiver, dot.Method(). Such a method must have one return value (of
|
|
||||||
any type) or two return values, the second of which is an error.
|
|
||||||
If it has two and the returned error is non-nil, execution terminates
|
|
||||||
and an error is returned to the caller as the value of Execute.
|
|
||||||
Method invocations may be chained and combined with fields and keys
|
|
||||||
to any depth:
|
|
||||||
.Field1.Key1.Method1.Field2.Key2.Method2
|
|
||||||
Methods can also be evaluated on variables, including chaining:
|
|
||||||
$x.Method1.Field
|
|
||||||
- The name of a niladic function, such as
|
|
||||||
fun
|
|
||||||
The result is the value of invoking the function, fun(). The return
|
|
||||||
types and values behave as in methods. Functions and function
|
|
||||||
names are described below.
|
|
||||||
- A parenthesized instance of one the above, for grouping. The result
|
|
||||||
may be accessed by a field or map key invocation.
|
|
||||||
print (.F1 arg1) (.F2 arg2)
|
|
||||||
(.StructValuedMethod "arg").Field
|
|
||||||
|
|
||||||
Arguments may evaluate to any type; if they are pointers the implementation
|
|
||||||
automatically indirects to the base type when required.
|
|
||||||
If an evaluation yields a function value, such as a function-valued
|
|
||||||
field of a struct, the function is not invoked automatically, but it
|
|
||||||
can be used as a truth value for an if action and the like. To invoke
|
|
||||||
it, use the call function, defined below.
|
|
||||||
|
|
||||||
A pipeline is a possibly chained sequence of "commands". A command is a simple
|
|
||||||
value (argument) or a function or method call, possibly with multiple arguments:
|
|
||||||
|
|
||||||
Argument
|
|
||||||
The result is the value of evaluating the argument.
|
|
||||||
.Method [Argument...]
|
|
||||||
The method can be alone or the last element of a chain but,
|
|
||||||
unlike methods in the middle of a chain, it can take arguments.
|
|
||||||
The result is the value of calling the method with the
|
|
||||||
arguments:
|
|
||||||
dot.Method(Argument1, etc.)
|
|
||||||
functionName [Argument...]
|
|
||||||
The result is the value of calling the function associated
|
|
||||||
with the name:
|
|
||||||
function(Argument1, etc.)
|
|
||||||
Functions and function names are described below.
|
|
||||||
|
|
||||||
Pipelines
|
|
||||||
|
|
||||||
A pipeline may be "chained" by separating a sequence of commands with pipeline
|
|
||||||
characters '|'. In a chained pipeline, the result of the each command is
|
|
||||||
passed as the last argument of the following command. The output of the final
|
|
||||||
command in the pipeline is the value of the pipeline.
|
|
||||||
|
|
||||||
The output of a command will be either one value or two values, the second of
|
|
||||||
which has type error. If that second value is present and evaluates to
|
|
||||||
non-nil, execution terminates and the error is returned to the caller of
|
|
||||||
Execute.
|
|
||||||
|
|
||||||
Variables
|
|
||||||
|
|
||||||
A pipeline inside an action may initialize a variable to capture the result.
|
|
||||||
The initialization has syntax
|
|
||||||
|
|
||||||
$variable := pipeline
|
|
||||||
|
|
||||||
where $variable is the name of the variable. An action that declares a
|
|
||||||
variable produces no output.
|
|
||||||
|
|
||||||
If a "range" action initializes a variable, the variable is set to the
|
|
||||||
successive elements of the iteration. Also, a "range" may declare two
|
|
||||||
variables, separated by a comma:
|
|
||||||
|
|
||||||
range $index, $element := pipeline
|
|
||||||
|
|
||||||
in which case $index and $element are set to the successive values of the
|
|
||||||
array/slice index or map key and element, respectively. Note that if there is
|
|
||||||
only one variable, it is assigned the element; this is opposite to the
|
|
||||||
convention in Go range clauses.
|
|
||||||
|
|
||||||
A variable's scope extends to the "end" action of the control structure ("if",
|
|
||||||
"with", or "range") in which it is declared, or to the end of the template if
|
|
||||||
there is no such control structure. A template invocation does not inherit
|
|
||||||
variables from the point of its invocation.
|
|
||||||
|
|
||||||
When execution begins, $ is set to the data argument passed to Execute, that is,
|
|
||||||
to the starting value of dot.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
|
|
||||||
Here are some example one-line templates demonstrating pipelines and variables.
|
|
||||||
All produce the quoted word "output":
|
|
||||||
|
|
||||||
{{"\"output\""}}
|
|
||||||
A string constant.
|
|
||||||
{{`"output"`}}
|
|
||||||
A raw string constant.
|
|
||||||
{{printf "%q" "output"}}
|
|
||||||
A function call.
|
|
||||||
{{"output" | printf "%q"}}
|
|
||||||
A function call whose final argument comes from the previous
|
|
||||||
command.
|
|
||||||
{{printf "%q" (print "out" "put")}}
|
|
||||||
A parenthesized argument.
|
|
||||||
{{"put" | printf "%s%s" "out" | printf "%q"}}
|
|
||||||
A more elaborate call.
|
|
||||||
{{"output" | printf "%s" | printf "%q"}}
|
|
||||||
A longer chain.
|
|
||||||
{{with "output"}}{{printf "%q" .}}{{end}}
|
|
||||||
A with action using dot.
|
|
||||||
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
|
|
||||||
A with action that creates and uses a variable.
|
|
||||||
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
|
|
||||||
A with action that uses the variable in another action.
|
|
||||||
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
|
|
||||||
The same, but pipelined.
|
|
||||||
|
|
||||||
Functions
|
|
||||||
|
|
||||||
During execution functions are found in two function maps: first in the
|
|
||||||
template, then in the global function map. By default, no functions are defined
|
|
||||||
in the template but the Funcs method can be used to add them.
|
|
||||||
|
|
||||||
Predefined global functions are named as follows.
|
|
||||||
|
|
||||||
and
|
|
||||||
Returns the boolean AND of its arguments by returning the
|
|
||||||
first empty argument or the last argument, that is,
|
|
||||||
"and x y" behaves as "if x then y else x". All the
|
|
||||||
arguments are evaluated.
|
|
||||||
call
|
|
||||||
Returns the result of calling the first argument, which
|
|
||||||
must be a function, with the remaining arguments as parameters.
|
|
||||||
Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
|
|
||||||
Y is a func-valued field, map entry, or the like.
|
|
||||||
The first argument must be the result of an evaluation
|
|
||||||
that yields a value of function type (as distinct from
|
|
||||||
a predefined function such as print). The function must
|
|
||||||
return either one or two result values, the second of which
|
|
||||||
is of type error. If the arguments don't match the function
|
|
||||||
or the returned error value is non-nil, execution stops.
|
|
||||||
html
|
|
||||||
Returns the escaped HTML equivalent of the textual
|
|
||||||
representation of its arguments.
|
|
||||||
index
|
|
||||||
Returns the result of indexing its first argument by the
|
|
||||||
following arguments. Thus "index x 1 2 3" is, in Go syntax,
|
|
||||||
x[1][2][3]. Each indexed item must be a map, slice, or array.
|
|
||||||
js
|
|
||||||
Returns the escaped JavaScript equivalent of the textual
|
|
||||||
representation of its arguments.
|
|
||||||
len
|
|
||||||
Returns the integer length of its argument.
|
|
||||||
not
|
|
||||||
Returns the boolean negation of its single argument.
|
|
||||||
or
|
|
||||||
Returns the boolean OR of its arguments by returning the
|
|
||||||
first non-empty argument or the last argument, that is,
|
|
||||||
"or x y" behaves as "if x then x else y". All the
|
|
||||||
arguments are evaluated.
|
|
||||||
print
|
|
||||||
An alias for fmt.Sprint
|
|
||||||
printf
|
|
||||||
An alias for fmt.Sprintf
|
|
||||||
println
|
|
||||||
An alias for fmt.Sprintln
|
|
||||||
urlquery
|
|
||||||
Returns the escaped value of the textual representation of
|
|
||||||
its arguments in a form suitable for embedding in a URL query.
|
|
||||||
|
|
||||||
The boolean functions take any zero value to be false and a non-zero
|
|
||||||
value to be true.
|
|
||||||
|
|
||||||
There is also a set of binary comparison operators defined as
|
|
||||||
functions:
|
|
||||||
|
|
||||||
eq
|
|
||||||
Returns the boolean truth of arg1 == arg2
|
|
||||||
ne
|
|
||||||
Returns the boolean truth of arg1 != arg2
|
|
||||||
lt
|
|
||||||
Returns the boolean truth of arg1 < arg2
|
|
||||||
le
|
|
||||||
Returns the boolean truth of arg1 <= arg2
|
|
||||||
gt
|
|
||||||
Returns the boolean truth of arg1 > arg2
|
|
||||||
ge
|
|
||||||
Returns the boolean truth of arg1 >= arg2
|
|
||||||
|
|
||||||
For simpler multi-way equality tests, eq (only) accepts two or more
|
|
||||||
arguments and compares the second and subsequent to the first,
|
|
||||||
returning in effect
|
|
||||||
|
|
||||||
arg1==arg2 || arg1==arg3 || arg1==arg4 ...
|
|
||||||
|
|
||||||
(Unlike with || in Go, however, eq is a function call and all the
|
|
||||||
arguments will be evaluated.)
|
|
||||||
|
|
||||||
The comparison functions work on basic types only (or named basic
|
|
||||||
types, such as "type Celsius float32"). They implement the Go rules
|
|
||||||
for comparison of values, except that size and exact type are
|
|
||||||
ignored, so any integer value, signed or unsigned, may be compared
|
|
||||||
with any other integer value. (The arithmetic value is compared,
|
|
||||||
not the bit pattern, so all negative integers are less than all
|
|
||||||
unsigned integers.) However, as usual, one may not compare an int
|
|
||||||
with a float32 and so on.
|
|
||||||
|
|
||||||
Associated templates
|
|
||||||
|
|
||||||
Each template is named by a string specified when it is created. Also, each
|
|
||||||
template is associated with zero or more other templates that it may invoke by
|
|
||||||
name; such associations are transitive and form a name space of templates.
|
|
||||||
|
|
||||||
A template may use a template invocation to instantiate another associated
|
|
||||||
template; see the explanation of the "template" action above. The name must be
|
|
||||||
that of a template associated with the template that contains the invocation.
|
|
||||||
|
|
||||||
Nested template definitions
|
|
||||||
|
|
||||||
When parsing a template, another template may be defined and associated with the
|
|
||||||
template being parsed. Template definitions must appear at the top level of the
|
|
||||||
template, much like global variables in a Go program.
|
|
||||||
|
|
||||||
The syntax of such definitions is to surround each template declaration with a
|
|
||||||
"define" and "end" action.
|
|
||||||
|
|
||||||
The define action names the template being created by providing a string
|
|
||||||
constant. Here is a simple example:
|
|
||||||
|
|
||||||
`{{define "T1"}}ONE{{end}}
|
|
||||||
{{define "T2"}}TWO{{end}}
|
|
||||||
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
|
|
||||||
{{template "T3"}}`
|
|
||||||
|
|
||||||
This defines two templates, T1 and T2, and a third T3 that invokes the other two
|
|
||||||
when it is executed. Finally it invokes T3. If executed this template will
|
|
||||||
produce the text
|
|
||||||
|
|
||||||
ONE TWO
|
|
||||||
|
|
||||||
By construction, a template may reside in only one association. If it's
|
|
||||||
necessary to have a template addressable from multiple associations, the
|
|
||||||
template definition must be parsed multiple times to create distinct *Template
|
|
||||||
values, or must be copied with the Clone or AddParseTree method.
|
|
||||||
|
|
||||||
Parse may be called multiple times to assemble the various associated templates;
|
|
||||||
see the ParseFiles and ParseGlob functions and methods for simple ways to parse
|
|
||||||
related templates stored in files.
|
|
||||||
|
|
||||||
A template may be executed directly or through ExecuteTemplate, which executes
|
|
||||||
an associated template identified by name. To invoke our example above, we
|
|
||||||
might write,
|
|
||||||
|
|
||||||
err := tmpl.Execute(os.Stdout, "no data needed")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("execution failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
or to invoke a particular template explicitly by name,
|
|
||||||
|
|
||||||
err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("execution failed: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
package template
|
|
845
vendor/github.com/alecthomas/template/exec.go
generated
vendored
845
vendor/github.com/alecthomas/template/exec.go
generated
vendored
|
@ -1,845 +0,0 @@
|
||||||
// Copyright 2011 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 template
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alecthomas/template/parse"
|
|
||||||
)
|
|
||||||
|
|
||||||
// state represents the state of an execution. It's not part of the
|
|
||||||
// template so that multiple executions of the same template
|
|
||||||
// can execute in parallel.
|
|
||||||
type state struct {
|
|
||||||
tmpl *Template
|
|
||||||
wr io.Writer
|
|
||||||
node parse.Node // current node, for errors
|
|
||||||
vars []variable // push-down stack of variable values.
|
|
||||||
}
|
|
||||||
|
|
||||||
// variable holds the dynamic value of a variable such as $, $x etc.
|
|
||||||
type variable struct {
|
|
||||||
name string
|
|
||||||
value reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// push pushes a new variable on the stack.
|
|
||||||
func (s *state) push(name string, value reflect.Value) {
|
|
||||||
s.vars = append(s.vars, variable{name, value})
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark returns the length of the variable stack.
|
|
||||||
func (s *state) mark() int {
|
|
||||||
return len(s.vars)
|
|
||||||
}
|
|
||||||
|
|
||||||
// pop pops the variable stack up to the mark.
|
|
||||||
func (s *state) pop(mark int) {
|
|
||||||
s.vars = s.vars[0:mark]
|
|
||||||
}
|
|
||||||
|
|
||||||
// setVar overwrites the top-nth variable on the stack. Used by range iterations.
|
|
||||||
func (s *state) setVar(n int, value reflect.Value) {
|
|
||||||
s.vars[len(s.vars)-n].value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// varValue returns the value of the named variable.
|
|
||||||
func (s *state) varValue(name string) reflect.Value {
|
|
||||||
for i := s.mark() - 1; i >= 0; i-- {
|
|
||||||
if s.vars[i].name == name {
|
|
||||||
return s.vars[i].value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.errorf("undefined variable: %s", name)
|
|
||||||
return zero
|
|
||||||
}
|
|
||||||
|
|
||||||
var zero reflect.Value
|
|
||||||
|
|
||||||
// at marks the state to be on node n, for error reporting.
|
|
||||||
func (s *state) at(node parse.Node) {
|
|
||||||
s.node = node
|
|
||||||
}
|
|
||||||
|
|
||||||
// doublePercent returns the string with %'s replaced by %%, if necessary,
|
|
||||||
// so it can be used safely inside a Printf format string.
|
|
||||||
func doublePercent(str string) string {
|
|
||||||
if strings.Contains(str, "%") {
|
|
||||||
str = strings.Replace(str, "%", "%%", -1)
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf formats the error and terminates processing.
|
|
||||||
func (s *state) errorf(format string, args ...interface{}) {
|
|
||||||
name := doublePercent(s.tmpl.Name())
|
|
||||||
if s.node == nil {
|
|
||||||
format = fmt.Sprintf("template: %s: %s", name, format)
|
|
||||||
} else {
|
|
||||||
location, context := s.tmpl.ErrorContext(s.node)
|
|
||||||
format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format)
|
|
||||||
}
|
|
||||||
panic(fmt.Errorf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// errRecover is the handler that turns panics into returns from the top
|
|
||||||
// level of Parse.
|
|
||||||
func errRecover(errp *error) {
|
|
||||||
e := recover()
|
|
||||||
if e != nil {
|
|
||||||
switch err := e.(type) {
|
|
||||||
case runtime.Error:
|
|
||||||
panic(e)
|
|
||||||
case error:
|
|
||||||
*errp = err
|
|
||||||
default:
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteTemplate applies the template associated with t that has the given name
|
|
||||||
// to the specified data object and writes the output to wr.
|
|
||||||
// If an error occurs executing the template or writing its output,
|
|
||||||
// execution stops, but partial results may already have been written to
|
|
||||||
// the output writer.
|
|
||||||
// A template may be executed safely in parallel.
|
|
||||||
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
|
|
||||||
tmpl := t.tmpl[name]
|
|
||||||
if tmpl == nil {
|
|
||||||
return fmt.Errorf("template: no template %q associated with template %q", name, t.name)
|
|
||||||
}
|
|
||||||
return tmpl.Execute(wr, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute applies a parsed template to the specified data object,
|
|
||||||
// and writes the output to wr.
|
|
||||||
// If an error occurs executing the template or writing its output,
|
|
||||||
// execution stops, but partial results may already have been written to
|
|
||||||
// the output writer.
|
|
||||||
// A template may be executed safely in parallel.
|
|
||||||
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
|
|
||||||
defer errRecover(&err)
|
|
||||||
value := reflect.ValueOf(data)
|
|
||||||
state := &state{
|
|
||||||
tmpl: t,
|
|
||||||
wr: wr,
|
|
||||||
vars: []variable{{"$", value}},
|
|
||||||
}
|
|
||||||
t.init()
|
|
||||||
if t.Tree == nil || t.Root == nil {
|
|
||||||
var b bytes.Buffer
|
|
||||||
for name, tmpl := range t.tmpl {
|
|
||||||
if tmpl.Tree == nil || tmpl.Root == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b.Len() > 0 {
|
|
||||||
b.WriteString(", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&b, "%q", name)
|
|
||||||
}
|
|
||||||
var s string
|
|
||||||
if b.Len() > 0 {
|
|
||||||
s = "; defined templates are: " + b.String()
|
|
||||||
}
|
|
||||||
state.errorf("%q is an incomplete or empty template%s", t.Name(), s)
|
|
||||||
}
|
|
||||||
state.walk(value, t.Root)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk functions step through the major pieces of the template structure,
|
|
||||||
// generating output as they go.
|
|
||||||
func (s *state) walk(dot reflect.Value, node parse.Node) {
|
|
||||||
s.at(node)
|
|
||||||
switch node := node.(type) {
|
|
||||||
case *parse.ActionNode:
|
|
||||||
// Do not pop variables so they persist until next end.
|
|
||||||
// Also, if the action declares variables, don't print the result.
|
|
||||||
val := s.evalPipeline(dot, node.Pipe)
|
|
||||||
if len(node.Pipe.Decl) == 0 {
|
|
||||||
s.printValue(node, val)
|
|
||||||
}
|
|
||||||
case *parse.IfNode:
|
|
||||||
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
|
|
||||||
case *parse.ListNode:
|
|
||||||
for _, node := range node.Nodes {
|
|
||||||
s.walk(dot, node)
|
|
||||||
}
|
|
||||||
case *parse.RangeNode:
|
|
||||||
s.walkRange(dot, node)
|
|
||||||
case *parse.TemplateNode:
|
|
||||||
s.walkTemplate(dot, node)
|
|
||||||
case *parse.TextNode:
|
|
||||||
if _, err := s.wr.Write(node.Text); err != nil {
|
|
||||||
s.errorf("%s", err)
|
|
||||||
}
|
|
||||||
case *parse.WithNode:
|
|
||||||
s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
|
|
||||||
default:
|
|
||||||
s.errorf("unknown node: %s", node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// walkIfOrWith walks an 'if' or 'with' node. The two control structures
|
|
||||||
// are identical in behavior except that 'with' sets dot.
|
|
||||||
func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) {
|
|
||||||
defer s.pop(s.mark())
|
|
||||||
val := s.evalPipeline(dot, pipe)
|
|
||||||
truth, ok := isTrue(val)
|
|
||||||
if !ok {
|
|
||||||
s.errorf("if/with can't use %v", val)
|
|
||||||
}
|
|
||||||
if truth {
|
|
||||||
if typ == parse.NodeWith {
|
|
||||||
s.walk(val, list)
|
|
||||||
} else {
|
|
||||||
s.walk(dot, list)
|
|
||||||
}
|
|
||||||
} else if elseList != nil {
|
|
||||||
s.walk(dot, elseList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isTrue reports whether the value is 'true', in the sense of not the zero of its type,
|
|
||||||
// and whether the value has a meaningful truth value.
|
|
||||||
func isTrue(val reflect.Value) (truth, ok bool) {
|
|
||||||
if !val.IsValid() {
|
|
||||||
// Something like var x interface{}, never set. It's a form of nil.
|
|
||||||
return false, true
|
|
||||||
}
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
||||||
truth = val.Len() > 0
|
|
||||||
case reflect.Bool:
|
|
||||||
truth = val.Bool()
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
truth = val.Complex() != 0
|
|
||||||
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
|
|
||||||
truth = !val.IsNil()
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
truth = val.Int() != 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
truth = val.Float() != 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
truth = val.Uint() != 0
|
|
||||||
case reflect.Struct:
|
|
||||||
truth = true // Struct values are always true.
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return truth, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
|
||||||
s.at(r)
|
|
||||||
defer s.pop(s.mark())
|
|
||||||
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
|
|
||||||
// mark top of stack before any variables in the body are pushed.
|
|
||||||
mark := s.mark()
|
|
||||||
oneIteration := func(index, elem reflect.Value) {
|
|
||||||
// Set top var (lexically the second if there are two) to the element.
|
|
||||||
if len(r.Pipe.Decl) > 0 {
|
|
||||||
s.setVar(1, elem)
|
|
||||||
}
|
|
||||||
// Set next var (lexically the first if there are two) to the index.
|
|
||||||
if len(r.Pipe.Decl) > 1 {
|
|
||||||
s.setVar(2, index)
|
|
||||||
}
|
|
||||||
s.walk(elem, r.List)
|
|
||||||
s.pop(mark)
|
|
||||||
}
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if val.Len() == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
oneIteration(reflect.ValueOf(i), val.Index(i))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case reflect.Map:
|
|
||||||
if val.Len() == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for _, key := range sortKeys(val.MapKeys()) {
|
|
||||||
oneIteration(key, val.MapIndex(key))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case reflect.Chan:
|
|
||||||
if val.IsNil() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
i := 0
|
|
||||||
for ; ; i++ {
|
|
||||||
elem, ok := val.Recv()
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
oneIteration(reflect.ValueOf(i), elem)
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case reflect.Invalid:
|
|
||||||
break // An invalid value is likely a nil map, etc. and acts like an empty map.
|
|
||||||
default:
|
|
||||||
s.errorf("range can't iterate over %v", val)
|
|
||||||
}
|
|
||||||
if r.ElseList != nil {
|
|
||||||
s.walk(dot, r.ElseList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
|
|
||||||
s.at(t)
|
|
||||||
tmpl := s.tmpl.tmpl[t.Name]
|
|
||||||
if tmpl == nil {
|
|
||||||
s.errorf("template %q not defined", t.Name)
|
|
||||||
}
|
|
||||||
// Variables declared by the pipeline persist.
|
|
||||||
dot = s.evalPipeline(dot, t.Pipe)
|
|
||||||
newState := *s
|
|
||||||
newState.tmpl = tmpl
|
|
||||||
// No dynamic scoping: template invocations inherit no variables.
|
|
||||||
newState.vars = []variable{{"$", dot}}
|
|
||||||
newState.walk(dot, tmpl.Root)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eval functions evaluate pipelines, commands, and their elements and extract
|
|
||||||
// values from the data structure by examining fields, calling methods, and so on.
|
|
||||||
// The printing of those values happens only through walk functions.
|
|
||||||
|
|
||||||
// evalPipeline returns the value acquired by evaluating a pipeline. If the
|
|
||||||
// pipeline has a variable declaration, the variable will be pushed on the
|
|
||||||
// stack. Callers should therefore pop the stack after they are finished
|
|
||||||
// executing commands depending on the pipeline value.
|
|
||||||
func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) {
|
|
||||||
if pipe == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.at(pipe)
|
|
||||||
for _, cmd := range pipe.Cmds {
|
|
||||||
value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.
|
|
||||||
// If the object has type interface{}, dig down one level to the thing inside.
|
|
||||||
if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
|
|
||||||
value = reflect.ValueOf(value.Interface()) // lovely!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, variable := range pipe.Decl {
|
|
||||||
s.push(variable.Ident[0], value)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) notAFunction(args []parse.Node, final reflect.Value) {
|
|
||||||
if len(args) > 1 || final.IsValid() {
|
|
||||||
s.errorf("can't give argument to non-function %s", args[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value {
|
|
||||||
firstWord := cmd.Args[0]
|
|
||||||
switch n := firstWord.(type) {
|
|
||||||
case *parse.FieldNode:
|
|
||||||
return s.evalFieldNode(dot, n, cmd.Args, final)
|
|
||||||
case *parse.ChainNode:
|
|
||||||
return s.evalChainNode(dot, n, cmd.Args, final)
|
|
||||||
case *parse.IdentifierNode:
|
|
||||||
// Must be a function.
|
|
||||||
return s.evalFunction(dot, n, cmd, cmd.Args, final)
|
|
||||||
case *parse.PipeNode:
|
|
||||||
// Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored.
|
|
||||||
return s.evalPipeline(dot, n)
|
|
||||||
case *parse.VariableNode:
|
|
||||||
return s.evalVariableNode(dot, n, cmd.Args, final)
|
|
||||||
}
|
|
||||||
s.at(firstWord)
|
|
||||||
s.notAFunction(cmd.Args, final)
|
|
||||||
switch word := firstWord.(type) {
|
|
||||||
case *parse.BoolNode:
|
|
||||||
return reflect.ValueOf(word.True)
|
|
||||||
case *parse.DotNode:
|
|
||||||
return dot
|
|
||||||
case *parse.NilNode:
|
|
||||||
s.errorf("nil is not a command")
|
|
||||||
case *parse.NumberNode:
|
|
||||||
return s.idealConstant(word)
|
|
||||||
case *parse.StringNode:
|
|
||||||
return reflect.ValueOf(word.Text)
|
|
||||||
}
|
|
||||||
s.errorf("can't evaluate command %q", firstWord)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
// idealConstant is called to return the value of a number in a context where
|
|
||||||
// we don't know the type. In that case, the syntax of the number tells us
|
|
||||||
// its type, and we use Go rules to resolve. Note there is no such thing as
|
|
||||||
// a uint ideal constant in this situation - the value must be of int type.
|
|
||||||
func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
|
|
||||||
// These are ideal constants but we don't know the type
|
|
||||||
// and we have no context. (If it was a method argument,
|
|
||||||
// we'd know what we need.) The syntax guides us to some extent.
|
|
||||||
s.at(constant)
|
|
||||||
switch {
|
|
||||||
case constant.IsComplex:
|
|
||||||
return reflect.ValueOf(constant.Complex128) // incontrovertible.
|
|
||||||
case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0:
|
|
||||||
return reflect.ValueOf(constant.Float64)
|
|
||||||
case constant.IsInt:
|
|
||||||
n := int(constant.Int64)
|
|
||||||
if int64(n) != constant.Int64 {
|
|
||||||
s.errorf("%s overflows int", constant.Text)
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(n)
|
|
||||||
case constant.IsUint:
|
|
||||||
s.errorf("%s overflows int", constant.Text)
|
|
||||||
}
|
|
||||||
return zero
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHexConstant(s string) bool {
|
|
||||||
return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value {
|
|
||||||
s.at(field)
|
|
||||||
return s.evalFieldChain(dot, dot, field, field.Ident, args, final)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value {
|
|
||||||
s.at(chain)
|
|
||||||
// (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields.
|
|
||||||
pipe := s.evalArg(dot, nil, chain.Node)
|
|
||||||
if len(chain.Field) == 0 {
|
|
||||||
s.errorf("internal error: no fields in evalChainNode")
|
|
||||||
}
|
|
||||||
return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {
|
|
||||||
// $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.
|
|
||||||
s.at(variable)
|
|
||||||
value := s.varValue(variable.Ident[0])
|
|
||||||
if len(variable.Ident) == 1 {
|
|
||||||
s.notAFunction(args, final)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final)
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalFieldChain evaluates .X.Y.Z possibly followed by arguments.
|
|
||||||
// dot is the environment in which to evaluate arguments, while
|
|
||||||
// receiver is the value being walked along the chain.
|
|
||||||
func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value {
|
|
||||||
n := len(ident)
|
|
||||||
for i := 0; i < n-1; i++ {
|
|
||||||
receiver = s.evalField(dot, ident[i], node, nil, zero, receiver)
|
|
||||||
}
|
|
||||||
// Now if it's a method, it gets the arguments.
|
|
||||||
return s.evalField(dot, ident[n-1], node, args, final, receiver)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value {
|
|
||||||
s.at(node)
|
|
||||||
name := node.Ident
|
|
||||||
function, ok := findFunction(name, s.tmpl)
|
|
||||||
if !ok {
|
|
||||||
s.errorf("%q is not a defined function", name)
|
|
||||||
}
|
|
||||||
return s.evalCall(dot, function, cmd, name, args, final)
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalField evaluates an expression like (.Field) or (.Field arg1 arg2).
|
|
||||||
// The 'final' argument represents the return value from the preceding
|
|
||||||
// value of the pipeline, if any.
|
|
||||||
func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {
|
|
||||||
if !receiver.IsValid() {
|
|
||||||
return zero
|
|
||||||
}
|
|
||||||
typ := receiver.Type()
|
|
||||||
receiver, _ = indirect(receiver)
|
|
||||||
// Unless it's an interface, need to get to a value of type *T to guarantee
|
|
||||||
// we see all methods of T and *T.
|
|
||||||
ptr := receiver
|
|
||||||
if ptr.Kind() != reflect.Interface && ptr.CanAddr() {
|
|
||||||
ptr = ptr.Addr()
|
|
||||||
}
|
|
||||||
if method := ptr.MethodByName(fieldName); method.IsValid() {
|
|
||||||
return s.evalCall(dot, method, node, fieldName, args, final)
|
|
||||||
}
|
|
||||||
hasArgs := len(args) > 1 || final.IsValid()
|
|
||||||
// It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil.
|
|
||||||
receiver, isNil := indirect(receiver)
|
|
||||||
if isNil {
|
|
||||||
s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
|
|
||||||
}
|
|
||||||
switch receiver.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
tField, ok := receiver.Type().FieldByName(fieldName)
|
|
||||||
if ok {
|
|
||||||
field := receiver.FieldByIndex(tField.Index)
|
|
||||||
if tField.PkgPath != "" { // field is unexported
|
|
||||||
s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
|
|
||||||
}
|
|
||||||
// If it's a function, we must call it.
|
|
||||||
if hasArgs {
|
|
||||||
s.errorf("%s has arguments but cannot be invoked as function", fieldName)
|
|
||||||
}
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
s.errorf("%s is not a field of struct type %s", fieldName, typ)
|
|
||||||
case reflect.Map:
|
|
||||||
// If it's a map, attempt to use the field name as a key.
|
|
||||||
nameVal := reflect.ValueOf(fieldName)
|
|
||||||
if nameVal.Type().AssignableTo(receiver.Type().Key()) {
|
|
||||||
if hasArgs {
|
|
||||||
s.errorf("%s is not a method but has arguments", fieldName)
|
|
||||||
}
|
|
||||||
return receiver.MapIndex(nameVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.errorf("can't evaluate field %s in type %s", fieldName, typ)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
||||||
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
|
||||||
)
|
|
||||||
|
|
||||||
// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
|
|
||||||
// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
|
|
||||||
// as the function itself.
|
|
||||||
func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value {
|
|
||||||
if args != nil {
|
|
||||||
args = args[1:] // Zeroth arg is function name/node; not passed to function.
|
|
||||||
}
|
|
||||||
typ := fun.Type()
|
|
||||||
numIn := len(args)
|
|
||||||
if final.IsValid() {
|
|
||||||
numIn++
|
|
||||||
}
|
|
||||||
numFixed := len(args)
|
|
||||||
if typ.IsVariadic() {
|
|
||||||
numFixed = typ.NumIn() - 1 // last arg is the variadic one.
|
|
||||||
if numIn < numFixed {
|
|
||||||
s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
|
|
||||||
}
|
|
||||||
} else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() {
|
|
||||||
s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args))
|
|
||||||
}
|
|
||||||
if !goodFunc(typ) {
|
|
||||||
// TODO: This could still be a confusing error; maybe goodFunc should provide info.
|
|
||||||
s.errorf("can't call method/function %q with %d results", name, typ.NumOut())
|
|
||||||
}
|
|
||||||
// Build the arg list.
|
|
||||||
argv := make([]reflect.Value, numIn)
|
|
||||||
// Args must be evaluated. Fixed args first.
|
|
||||||
i := 0
|
|
||||||
for ; i < numFixed && i < len(args); i++ {
|
|
||||||
argv[i] = s.evalArg(dot, typ.In(i), args[i])
|
|
||||||
}
|
|
||||||
// Now the ... args.
|
|
||||||
if typ.IsVariadic() {
|
|
||||||
argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
|
|
||||||
for ; i < len(args); i++ {
|
|
||||||
argv[i] = s.evalArg(dot, argType, args[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add final value if necessary.
|
|
||||||
if final.IsValid() {
|
|
||||||
t := typ.In(typ.NumIn() - 1)
|
|
||||||
if typ.IsVariadic() {
|
|
||||||
t = t.Elem()
|
|
||||||
}
|
|
||||||
argv[i] = s.validateType(final, t)
|
|
||||||
}
|
|
||||||
result := fun.Call(argv)
|
|
||||||
// If we have an error that is not nil, stop execution and return that error to the caller.
|
|
||||||
if len(result) == 2 && !result[1].IsNil() {
|
|
||||||
s.at(node)
|
|
||||||
s.errorf("error calling %s: %s", name, result[1].Interface().(error))
|
|
||||||
}
|
|
||||||
return result[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
|
|
||||||
func canBeNil(typ reflect.Type) bool {
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateType guarantees that the value is valid and assignable to the type.
|
|
||||||
func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
|
|
||||||
if !value.IsValid() {
|
|
||||||
if typ == nil || canBeNil(typ) {
|
|
||||||
// An untyped nil interface{}. Accept as a proper nil value.
|
|
||||||
return reflect.Zero(typ)
|
|
||||||
}
|
|
||||||
s.errorf("invalid value; expected %s", typ)
|
|
||||||
}
|
|
||||||
if typ != nil && !value.Type().AssignableTo(typ) {
|
|
||||||
if value.Kind() == reflect.Interface && !value.IsNil() {
|
|
||||||
value = value.Elem()
|
|
||||||
if value.Type().AssignableTo(typ) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
// fallthrough
|
|
||||||
}
|
|
||||||
// Does one dereference or indirection work? We could do more, as we
|
|
||||||
// do with method receivers, but that gets messy and method receivers
|
|
||||||
// are much more constrained, so it makes more sense there than here.
|
|
||||||
// Besides, one is almost always all you need.
|
|
||||||
switch {
|
|
||||||
case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ):
|
|
||||||
value = value.Elem()
|
|
||||||
if !value.IsValid() {
|
|
||||||
s.errorf("dereference of nil pointer of type %s", typ)
|
|
||||||
}
|
|
||||||
case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
|
|
||||||
value = value.Addr()
|
|
||||||
default:
|
|
||||||
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value {
|
|
||||||
s.at(n)
|
|
||||||
switch arg := n.(type) {
|
|
||||||
case *parse.DotNode:
|
|
||||||
return s.validateType(dot, typ)
|
|
||||||
case *parse.NilNode:
|
|
||||||
if canBeNil(typ) {
|
|
||||||
return reflect.Zero(typ)
|
|
||||||
}
|
|
||||||
s.errorf("cannot assign nil to %s", typ)
|
|
||||||
case *parse.FieldNode:
|
|
||||||
return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ)
|
|
||||||
case *parse.VariableNode:
|
|
||||||
return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ)
|
|
||||||
case *parse.PipeNode:
|
|
||||||
return s.validateType(s.evalPipeline(dot, arg), typ)
|
|
||||||
case *parse.IdentifierNode:
|
|
||||||
return s.evalFunction(dot, arg, arg, nil, zero)
|
|
||||||
case *parse.ChainNode:
|
|
||||||
return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ)
|
|
||||||
}
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return s.evalBool(typ, n)
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
return s.evalComplex(typ, n)
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return s.evalFloat(typ, n)
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return s.evalInteger(typ, n)
|
|
||||||
case reflect.Interface:
|
|
||||||
if typ.NumMethod() == 0 {
|
|
||||||
return s.evalEmptyInterface(dot, n)
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
return s.evalString(typ, n)
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
return s.evalUnsignedInteger(typ, n)
|
|
||||||
}
|
|
||||||
s.errorf("can't handle %s for arg of type %s", n, typ)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value {
|
|
||||||
s.at(n)
|
|
||||||
if n, ok := n.(*parse.BoolNode); ok {
|
|
||||||
value := reflect.New(typ).Elem()
|
|
||||||
value.SetBool(n.True)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
s.errorf("expected bool; found %s", n)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value {
|
|
||||||
s.at(n)
|
|
||||||
if n, ok := n.(*parse.StringNode); ok {
|
|
||||||
value := reflect.New(typ).Elem()
|
|
||||||
value.SetString(n.Text)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
s.errorf("expected string; found %s", n)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value {
|
|
||||||
s.at(n)
|
|
||||||
if n, ok := n.(*parse.NumberNode); ok && n.IsInt {
|
|
||||||
value := reflect.New(typ).Elem()
|
|
||||||
value.SetInt(n.Int64)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
s.errorf("expected integer; found %s", n)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value {
|
|
||||||
s.at(n)
|
|
||||||
if n, ok := n.(*parse.NumberNode); ok && n.IsUint {
|
|
||||||
value := reflect.New(typ).Elem()
|
|
||||||
value.SetUint(n.Uint64)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
s.errorf("expected unsigned integer; found %s", n)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value {
|
|
||||||
s.at(n)
|
|
||||||
if n, ok := n.(*parse.NumberNode); ok && n.IsFloat {
|
|
||||||
value := reflect.New(typ).Elem()
|
|
||||||
value.SetFloat(n.Float64)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
s.errorf("expected float; found %s", n)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value {
|
|
||||||
if n, ok := n.(*parse.NumberNode); ok && n.IsComplex {
|
|
||||||
value := reflect.New(typ).Elem()
|
|
||||||
value.SetComplex(n.Complex128)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
s.errorf("expected complex; found %s", n)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value {
|
|
||||||
s.at(n)
|
|
||||||
switch n := n.(type) {
|
|
||||||
case *parse.BoolNode:
|
|
||||||
return reflect.ValueOf(n.True)
|
|
||||||
case *parse.DotNode:
|
|
||||||
return dot
|
|
||||||
case *parse.FieldNode:
|
|
||||||
return s.evalFieldNode(dot, n, nil, zero)
|
|
||||||
case *parse.IdentifierNode:
|
|
||||||
return s.evalFunction(dot, n, n, nil, zero)
|
|
||||||
case *parse.NilNode:
|
|
||||||
// NilNode is handled in evalArg, the only place that calls here.
|
|
||||||
s.errorf("evalEmptyInterface: nil (can't happen)")
|
|
||||||
case *parse.NumberNode:
|
|
||||||
return s.idealConstant(n)
|
|
||||||
case *parse.StringNode:
|
|
||||||
return reflect.ValueOf(n.Text)
|
|
||||||
case *parse.VariableNode:
|
|
||||||
return s.evalVariableNode(dot, n, nil, zero)
|
|
||||||
case *parse.PipeNode:
|
|
||||||
return s.evalPipeline(dot, n)
|
|
||||||
}
|
|
||||||
s.errorf("can't handle assignment of %s to empty interface argument", n)
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
|
|
||||||
// We indirect through pointers and empty interfaces (only) because
|
|
||||||
// non-empty interfaces have methods we might need.
|
|
||||||
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
|
|
||||||
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
|
|
||||||
if v.IsNil() {
|
|
||||||
return v, true
|
|
||||||
}
|
|
||||||
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// printValue writes the textual representation of the value to the output of
|
|
||||||
// the template.
|
|
||||||
func (s *state) printValue(n parse.Node, v reflect.Value) {
|
|
||||||
s.at(n)
|
|
||||||
iface, ok := printableValue(v)
|
|
||||||
if !ok {
|
|
||||||
s.errorf("can't print %s of type %s", n, v.Type())
|
|
||||||
}
|
|
||||||
fmt.Fprint(s.wr, iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
// printableValue returns the, possibly indirected, interface value inside v that
|
|
||||||
// is best for a call to formatted printer.
|
|
||||||
func printableValue(v reflect.Value) (interface{}, bool) {
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v, _ = indirect(v) // fmt.Fprint handles nil.
|
|
||||||
}
|
|
||||||
if !v.IsValid() {
|
|
||||||
return "<no value>", true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {
|
|
||||||
if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) {
|
|
||||||
v = v.Addr()
|
|
||||||
} else {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Chan, reflect.Func:
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v.Interface(), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types to help sort the keys in a map for reproducible output.
|
|
||||||
|
|
||||||
type rvs []reflect.Value
|
|
||||||
|
|
||||||
func (x rvs) Len() int { return len(x) }
|
|
||||||
func (x rvs) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
type rvInts struct{ rvs }
|
|
||||||
|
|
||||||
func (x rvInts) Less(i, j int) bool { return x.rvs[i].Int() < x.rvs[j].Int() }
|
|
||||||
|
|
||||||
type rvUints struct{ rvs }
|
|
||||||
|
|
||||||
func (x rvUints) Less(i, j int) bool { return x.rvs[i].Uint() < x.rvs[j].Uint() }
|
|
||||||
|
|
||||||
type rvFloats struct{ rvs }
|
|
||||||
|
|
||||||
func (x rvFloats) Less(i, j int) bool { return x.rvs[i].Float() < x.rvs[j].Float() }
|
|
||||||
|
|
||||||
type rvStrings struct{ rvs }
|
|
||||||
|
|
||||||
func (x rvStrings) Less(i, j int) bool { return x.rvs[i].String() < x.rvs[j].String() }
|
|
||||||
|
|
||||||
// sortKeys sorts (if it can) the slice of reflect.Values, which is a slice of map keys.
|
|
||||||
func sortKeys(v []reflect.Value) []reflect.Value {
|
|
||||||
if len(v) <= 1 {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
switch v[0].Kind() {
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
sort.Sort(rvFloats{v})
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
sort.Sort(rvInts{v})
|
|
||||||
case reflect.String:
|
|
||||||
sort.Sort(rvStrings{v})
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
sort.Sort(rvUints{v})
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
598
vendor/github.com/alecthomas/template/funcs.go
generated
vendored
598
vendor/github.com/alecthomas/template/funcs.go
generated
vendored
|
@ -1,598 +0,0 @@
|
||||||
// Copyright 2011 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 template
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FuncMap is the type of the map defining the mapping from names to functions.
|
|
||||||
// Each function must have either a single return value, or two return values of
|
|
||||||
// which the second has type error. In that case, if the second (error)
|
|
||||||
// return value evaluates to non-nil during execution, execution terminates and
|
|
||||||
// Execute returns that error.
|
|
||||||
type FuncMap map[string]interface{}
|
|
||||||
|
|
||||||
var builtins = FuncMap{
|
|
||||||
"and": and,
|
|
||||||
"call": call,
|
|
||||||
"html": HTMLEscaper,
|
|
||||||
"index": index,
|
|
||||||
"js": JSEscaper,
|
|
||||||
"len": length,
|
|
||||||
"not": not,
|
|
||||||
"or": or,
|
|
||||||
"print": fmt.Sprint,
|
|
||||||
"printf": fmt.Sprintf,
|
|
||||||
"println": fmt.Sprintln,
|
|
||||||
"urlquery": URLQueryEscaper,
|
|
||||||
|
|
||||||
// Comparisons
|
|
||||||
"eq": eq, // ==
|
|
||||||
"ge": ge, // >=
|
|
||||||
"gt": gt, // >
|
|
||||||
"le": le, // <=
|
|
||||||
"lt": lt, // <
|
|
||||||
"ne": ne, // !=
|
|
||||||
}
|
|
||||||
|
|
||||||
var builtinFuncs = createValueFuncs(builtins)
|
|
||||||
|
|
||||||
// createValueFuncs turns a FuncMap into a map[string]reflect.Value
|
|
||||||
func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
|
|
||||||
m := make(map[string]reflect.Value)
|
|
||||||
addValueFuncs(m, funcMap)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
|
|
||||||
func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
|
|
||||||
for name, fn := range in {
|
|
||||||
v := reflect.ValueOf(fn)
|
|
||||||
if v.Kind() != reflect.Func {
|
|
||||||
panic("value for " + name + " not a function")
|
|
||||||
}
|
|
||||||
if !goodFunc(v.Type()) {
|
|
||||||
panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut()))
|
|
||||||
}
|
|
||||||
out[name] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addFuncs adds to values the functions in funcs. It does no checking of the input -
|
|
||||||
// call addValueFuncs first.
|
|
||||||
func addFuncs(out, in FuncMap) {
|
|
||||||
for name, fn := range in {
|
|
||||||
out[name] = fn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// goodFunc checks that the function or method has the right result signature.
|
|
||||||
func goodFunc(typ reflect.Type) bool {
|
|
||||||
// We allow functions with 1 result or 2 results where the second is an error.
|
|
||||||
switch {
|
|
||||||
case typ.NumOut() == 1:
|
|
||||||
return true
|
|
||||||
case typ.NumOut() == 2 && typ.Out(1) == errorType:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// findFunction looks for a function in the template, and global map.
|
|
||||||
func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
|
|
||||||
if tmpl != nil && tmpl.common != nil {
|
|
||||||
if fn := tmpl.execFuncs[name]; fn.IsValid() {
|
|
||||||
return fn, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if fn := builtinFuncs[name]; fn.IsValid() {
|
|
||||||
return fn, true
|
|
||||||
}
|
|
||||||
return reflect.Value{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indexing.
|
|
||||||
|
|
||||||
// index returns the result of indexing its first argument by the following
|
|
||||||
// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
|
|
||||||
// indexed item must be a map, slice, or array.
|
|
||||||
func index(item interface{}, indices ...interface{}) (interface{}, error) {
|
|
||||||
v := reflect.ValueOf(item)
|
|
||||||
for _, i := range indices {
|
|
||||||
index := reflect.ValueOf(i)
|
|
||||||
var isNil bool
|
|
||||||
if v, isNil = indirect(v); isNil {
|
|
||||||
return nil, fmt.Errorf("index of nil pointer")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice, reflect.String:
|
|
||||||
var x int64
|
|
||||||
switch index.Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
x = index.Int()
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
x = int64(index.Uint())
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
|
|
||||||
}
|
|
||||||
if x < 0 || x >= int64(v.Len()) {
|
|
||||||
return nil, fmt.Errorf("index out of range: %d", x)
|
|
||||||
}
|
|
||||||
v = v.Index(int(x))
|
|
||||||
case reflect.Map:
|
|
||||||
if !index.IsValid() {
|
|
||||||
index = reflect.Zero(v.Type().Key())
|
|
||||||
}
|
|
||||||
if !index.Type().AssignableTo(v.Type().Key()) {
|
|
||||||
return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
|
|
||||||
}
|
|
||||||
if x := v.MapIndex(index); x.IsValid() {
|
|
||||||
v = x
|
|
||||||
} else {
|
|
||||||
v = reflect.Zero(v.Type().Elem())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("can't index item of type %s", v.Type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v.Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Length
|
|
||||||
|
|
||||||
// length returns the length of the item, with an error if it has no defined length.
|
|
||||||
func length(item interface{}) (int, error) {
|
|
||||||
v, isNil := indirect(reflect.ValueOf(item))
|
|
||||||
if isNil {
|
|
||||||
return 0, fmt.Errorf("len of nil pointer")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
|
|
||||||
return v.Len(), nil
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("len of type %s", v.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function invocation
|
|
||||||
|
|
||||||
// call returns the result of evaluating the first argument as a function.
|
|
||||||
// The function must return 1 result, or 2 results, the second of which is an error.
|
|
||||||
func call(fn interface{}, args ...interface{}) (interface{}, error) {
|
|
||||||
v := reflect.ValueOf(fn)
|
|
||||||
typ := v.Type()
|
|
||||||
if typ.Kind() != reflect.Func {
|
|
||||||
return nil, fmt.Errorf("non-function of type %s", typ)
|
|
||||||
}
|
|
||||||
if !goodFunc(typ) {
|
|
||||||
return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
|
|
||||||
}
|
|
||||||
numIn := typ.NumIn()
|
|
||||||
var dddType reflect.Type
|
|
||||||
if typ.IsVariadic() {
|
|
||||||
if len(args) < numIn-1 {
|
|
||||||
return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
|
|
||||||
}
|
|
||||||
dddType = typ.In(numIn - 1).Elem()
|
|
||||||
} else {
|
|
||||||
if len(args) != numIn {
|
|
||||||
return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argv := make([]reflect.Value, len(args))
|
|
||||||
for i, arg := range args {
|
|
||||||
value := reflect.ValueOf(arg)
|
|
||||||
// Compute the expected type. Clumsy because of variadics.
|
|
||||||
var argType reflect.Type
|
|
||||||
if !typ.IsVariadic() || i < numIn-1 {
|
|
||||||
argType = typ.In(i)
|
|
||||||
} else {
|
|
||||||
argType = dddType
|
|
||||||
}
|
|
||||||
if !value.IsValid() && canBeNil(argType) {
|
|
||||||
value = reflect.Zero(argType)
|
|
||||||
}
|
|
||||||
if !value.Type().AssignableTo(argType) {
|
|
||||||
return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
|
|
||||||
}
|
|
||||||
argv[i] = value
|
|
||||||
}
|
|
||||||
result := v.Call(argv)
|
|
||||||
if len(result) == 2 && !result[1].IsNil() {
|
|
||||||
return result[0].Interface(), result[1].Interface().(error)
|
|
||||||
}
|
|
||||||
return result[0].Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Boolean logic.
|
|
||||||
|
|
||||||
func truth(a interface{}) bool {
|
|
||||||
t, _ := isTrue(reflect.ValueOf(a))
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// and computes the Boolean AND of its arguments, returning
|
|
||||||
// the first false argument it encounters, or the last argument.
|
|
||||||
func and(arg0 interface{}, args ...interface{}) interface{} {
|
|
||||||
if !truth(arg0) {
|
|
||||||
return arg0
|
|
||||||
}
|
|
||||||
for i := range args {
|
|
||||||
arg0 = args[i]
|
|
||||||
if !truth(arg0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arg0
|
|
||||||
}
|
|
||||||
|
|
||||||
// or computes the Boolean OR of its arguments, returning
|
|
||||||
// the first true argument it encounters, or the last argument.
|
|
||||||
func or(arg0 interface{}, args ...interface{}) interface{} {
|
|
||||||
if truth(arg0) {
|
|
||||||
return arg0
|
|
||||||
}
|
|
||||||
for i := range args {
|
|
||||||
arg0 = args[i]
|
|
||||||
if truth(arg0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arg0
|
|
||||||
}
|
|
||||||
|
|
||||||
// not returns the Boolean negation of its argument.
|
|
||||||
func not(arg interface{}) (truth bool) {
|
|
||||||
truth, _ = isTrue(reflect.ValueOf(arg))
|
|
||||||
return !truth
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comparison.
|
|
||||||
|
|
||||||
// TODO: Perhaps allow comparison between signed and unsigned integers.
|
|
||||||
|
|
||||||
var (
|
|
||||||
errBadComparisonType = errors.New("invalid type for comparison")
|
|
||||||
errBadComparison = errors.New("incompatible types for comparison")
|
|
||||||
errNoComparison = errors.New("missing argument for comparison")
|
|
||||||
)
|
|
||||||
|
|
||||||
type kind int
|
|
||||||
|
|
||||||
const (
|
|
||||||
invalidKind kind = iota
|
|
||||||
boolKind
|
|
||||||
complexKind
|
|
||||||
intKind
|
|
||||||
floatKind
|
|
||||||
integerKind
|
|
||||||
stringKind
|
|
||||||
uintKind
|
|
||||||
)
|
|
||||||
|
|
||||||
func basicKind(v reflect.Value) (kind, error) {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return boolKind, nil
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return intKind, nil
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
return uintKind, nil
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return floatKind, nil
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
return complexKind, nil
|
|
||||||
case reflect.String:
|
|
||||||
return stringKind, nil
|
|
||||||
}
|
|
||||||
return invalidKind, errBadComparisonType
|
|
||||||
}
|
|
||||||
|
|
||||||
// eq evaluates the comparison a == b || a == c || ...
|
|
||||||
func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
|
|
||||||
v1 := reflect.ValueOf(arg1)
|
|
||||||
k1, err := basicKind(v1)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if len(arg2) == 0 {
|
|
||||||
return false, errNoComparison
|
|
||||||
}
|
|
||||||
for _, arg := range arg2 {
|
|
||||||
v2 := reflect.ValueOf(arg)
|
|
||||||
k2, err := basicKind(v2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
truth := false
|
|
||||||
if k1 != k2 {
|
|
||||||
// Special case: Can compare integer values regardless of type's sign.
|
|
||||||
switch {
|
|
||||||
case k1 == intKind && k2 == uintKind:
|
|
||||||
truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
|
|
||||||
case k1 == uintKind && k2 == intKind:
|
|
||||||
truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
|
|
||||||
default:
|
|
||||||
return false, errBadComparison
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch k1 {
|
|
||||||
case boolKind:
|
|
||||||
truth = v1.Bool() == v2.Bool()
|
|
||||||
case complexKind:
|
|
||||||
truth = v1.Complex() == v2.Complex()
|
|
||||||
case floatKind:
|
|
||||||
truth = v1.Float() == v2.Float()
|
|
||||||
case intKind:
|
|
||||||
truth = v1.Int() == v2.Int()
|
|
||||||
case stringKind:
|
|
||||||
truth = v1.String() == v2.String()
|
|
||||||
case uintKind:
|
|
||||||
truth = v1.Uint() == v2.Uint()
|
|
||||||
default:
|
|
||||||
panic("invalid kind")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if truth {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ne evaluates the comparison a != b.
|
|
||||||
func ne(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
// != is the inverse of ==.
|
|
||||||
equal, err := eq(arg1, arg2)
|
|
||||||
return !equal, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// lt evaluates the comparison a < b.
|
|
||||||
func lt(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
v1 := reflect.ValueOf(arg1)
|
|
||||||
k1, err := basicKind(v1)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
v2 := reflect.ValueOf(arg2)
|
|
||||||
k2, err := basicKind(v2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
truth := false
|
|
||||||
if k1 != k2 {
|
|
||||||
// Special case: Can compare integer values regardless of type's sign.
|
|
||||||
switch {
|
|
||||||
case k1 == intKind && k2 == uintKind:
|
|
||||||
truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint()
|
|
||||||
case k1 == uintKind && k2 == intKind:
|
|
||||||
truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int())
|
|
||||||
default:
|
|
||||||
return false, errBadComparison
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch k1 {
|
|
||||||
case boolKind, complexKind:
|
|
||||||
return false, errBadComparisonType
|
|
||||||
case floatKind:
|
|
||||||
truth = v1.Float() < v2.Float()
|
|
||||||
case intKind:
|
|
||||||
truth = v1.Int() < v2.Int()
|
|
||||||
case stringKind:
|
|
||||||
truth = v1.String() < v2.String()
|
|
||||||
case uintKind:
|
|
||||||
truth = v1.Uint() < v2.Uint()
|
|
||||||
default:
|
|
||||||
panic("invalid kind")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return truth, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// le evaluates the comparison <= b.
|
|
||||||
func le(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
// <= is < or ==.
|
|
||||||
lessThan, err := lt(arg1, arg2)
|
|
||||||
if lessThan || err != nil {
|
|
||||||
return lessThan, err
|
|
||||||
}
|
|
||||||
return eq(arg1, arg2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// gt evaluates the comparison a > b.
|
|
||||||
func gt(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
// > is the inverse of <=.
|
|
||||||
lessOrEqual, err := le(arg1, arg2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return !lessOrEqual, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ge evaluates the comparison a >= b.
|
|
||||||
func ge(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
// >= is the inverse of <.
|
|
||||||
lessThan, err := lt(arg1, arg2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return !lessThan, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTML escaping.
|
|
||||||
|
|
||||||
var (
|
|
||||||
htmlQuot = []byte(""") // shorter than """
|
|
||||||
htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5
|
|
||||||
htmlAmp = []byte("&")
|
|
||||||
htmlLt = []byte("<")
|
|
||||||
htmlGt = []byte(">")
|
|
||||||
)
|
|
||||||
|
|
||||||
// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
|
|
||||||
func HTMLEscape(w io.Writer, b []byte) {
|
|
||||||
last := 0
|
|
||||||
for i, c := range b {
|
|
||||||
var html []byte
|
|
||||||
switch c {
|
|
||||||
case '"':
|
|
||||||
html = htmlQuot
|
|
||||||
case '\'':
|
|
||||||
html = htmlApos
|
|
||||||
case '&':
|
|
||||||
html = htmlAmp
|
|
||||||
case '<':
|
|
||||||
html = htmlLt
|
|
||||||
case '>':
|
|
||||||
html = htmlGt
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
w.Write(b[last:i])
|
|
||||||
w.Write(html)
|
|
||||||
last = i + 1
|
|
||||||
}
|
|
||||||
w.Write(b[last:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
|
|
||||||
func HTMLEscapeString(s string) string {
|
|
||||||
// Avoid allocation if we can.
|
|
||||||
if strings.IndexAny(s, `'"&<>`) < 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
var b bytes.Buffer
|
|
||||||
HTMLEscape(&b, []byte(s))
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTMLEscaper returns the escaped HTML equivalent of the textual
|
|
||||||
// representation of its arguments.
|
|
||||||
func HTMLEscaper(args ...interface{}) string {
|
|
||||||
return HTMLEscapeString(evalArgs(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
// JavaScript escaping.
|
|
||||||
|
|
||||||
var (
|
|
||||||
jsLowUni = []byte(`\u00`)
|
|
||||||
hex = []byte("0123456789ABCDEF")
|
|
||||||
|
|
||||||
jsBackslash = []byte(`\\`)
|
|
||||||
jsApos = []byte(`\'`)
|
|
||||||
jsQuot = []byte(`\"`)
|
|
||||||
jsLt = []byte(`\x3C`)
|
|
||||||
jsGt = []byte(`\x3E`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
|
|
||||||
func JSEscape(w io.Writer, b []byte) {
|
|
||||||
last := 0
|
|
||||||
for i := 0; i < len(b); i++ {
|
|
||||||
c := b[i]
|
|
||||||
|
|
||||||
if !jsIsSpecial(rune(c)) {
|
|
||||||
// fast path: nothing to do
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
w.Write(b[last:i])
|
|
||||||
|
|
||||||
if c < utf8.RuneSelf {
|
|
||||||
// Quotes, slashes and angle brackets get quoted.
|
|
||||||
// Control characters get written as \u00XX.
|
|
||||||
switch c {
|
|
||||||
case '\\':
|
|
||||||
w.Write(jsBackslash)
|
|
||||||
case '\'':
|
|
||||||
w.Write(jsApos)
|
|
||||||
case '"':
|
|
||||||
w.Write(jsQuot)
|
|
||||||
case '<':
|
|
||||||
w.Write(jsLt)
|
|
||||||
case '>':
|
|
||||||
w.Write(jsGt)
|
|
||||||
default:
|
|
||||||
w.Write(jsLowUni)
|
|
||||||
t, b := c>>4, c&0x0f
|
|
||||||
w.Write(hex[t : t+1])
|
|
||||||
w.Write(hex[b : b+1])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Unicode rune.
|
|
||||||
r, size := utf8.DecodeRune(b[i:])
|
|
||||||
if unicode.IsPrint(r) {
|
|
||||||
w.Write(b[i : i+size])
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(w, "\\u%04X", r)
|
|
||||||
}
|
|
||||||
i += size - 1
|
|
||||||
}
|
|
||||||
last = i + 1
|
|
||||||
}
|
|
||||||
w.Write(b[last:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
|
|
||||||
func JSEscapeString(s string) string {
|
|
||||||
// Avoid allocation if we can.
|
|
||||||
if strings.IndexFunc(s, jsIsSpecial) < 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
var b bytes.Buffer
|
|
||||||
JSEscape(&b, []byte(s))
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func jsIsSpecial(r rune) bool {
|
|
||||||
switch r {
|
|
||||||
case '\\', '\'', '"', '<', '>':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return r < ' ' || utf8.RuneSelf <= r
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSEscaper returns the escaped JavaScript equivalent of the textual
|
|
||||||
// representation of its arguments.
|
|
||||||
func JSEscaper(args ...interface{}) string {
|
|
||||||
return JSEscapeString(evalArgs(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
// URLQueryEscaper returns the escaped value of the textual representation of
|
|
||||||
// its arguments in a form suitable for embedding in a URL query.
|
|
||||||
func URLQueryEscaper(args ...interface{}) string {
|
|
||||||
return url.QueryEscape(evalArgs(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalArgs formats the list of arguments into a string. It is therefore equivalent to
|
|
||||||
// fmt.Sprint(args...)
|
|
||||||
// except that each argument is indirected (if a pointer), as required,
|
|
||||||
// using the same rules as the default string evaluation during template
|
|
||||||
// execution.
|
|
||||||
func evalArgs(args []interface{}) string {
|
|
||||||
ok := false
|
|
||||||
var s string
|
|
||||||
// Fast path for simple common case.
|
|
||||||
if len(args) == 1 {
|
|
||||||
s, ok = args[0].(string)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
for i, arg := range args {
|
|
||||||
a, ok := printableValue(reflect.ValueOf(arg))
|
|
||||||
if ok {
|
|
||||||
args[i] = a
|
|
||||||
} // else left fmt do its thing
|
|
||||||
}
|
|
||||||
s = fmt.Sprint(args...)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
108
vendor/github.com/alecthomas/template/helper.go
generated
vendored
108
vendor/github.com/alecthomas/template/helper.go
generated
vendored
|
@ -1,108 +0,0 @@
|
||||||
// Copyright 2011 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.
|
|
||||||
|
|
||||||
// Helper functions to make constructing templates easier.
|
|
||||||
|
|
||||||
package template
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Functions and methods to parse templates.
|
|
||||||
|
|
||||||
// Must is a helper that wraps a call to a function returning (*Template, error)
|
|
||||||
// and panics if the error is non-nil. It is intended for use in variable
|
|
||||||
// initializations such as
|
|
||||||
// var t = template.Must(template.New("name").Parse("text"))
|
|
||||||
func Must(t *Template, err error) *Template {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFiles creates a new Template and parses the template definitions from
|
|
||||||
// the named files. The returned template's name will have the (base) name and
|
|
||||||
// (parsed) contents of the first file. There must be at least one file.
|
|
||||||
// If an error occurs, parsing stops and the returned *Template is nil.
|
|
||||||
func ParseFiles(filenames ...string) (*Template, error) {
|
|
||||||
return parseFiles(nil, filenames...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFiles parses the named files and associates the resulting templates with
|
|
||||||
// t. If an error occurs, parsing stops and the returned template is nil;
|
|
||||||
// otherwise it is t. There must be at least one file.
|
|
||||||
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
|
|
||||||
return parseFiles(t, filenames...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFiles is the helper for the method and function. If the argument
|
|
||||||
// template is nil, it is created from the first file.
|
|
||||||
func parseFiles(t *Template, filenames ...string) (*Template, error) {
|
|
||||||
if len(filenames) == 0 {
|
|
||||||
// Not really a problem, but be consistent.
|
|
||||||
return nil, fmt.Errorf("template: no files named in call to ParseFiles")
|
|
||||||
}
|
|
||||||
for _, filename := range filenames {
|
|
||||||
b, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s := string(b)
|
|
||||||
name := filepath.Base(filename)
|
|
||||||
// First template becomes return value if not already defined,
|
|
||||||
// and we use that one for subsequent New calls to associate
|
|
||||||
// all the templates together. Also, if this file has the same name
|
|
||||||
// as t, this file becomes the contents of t, so
|
|
||||||
// t, err := New(name).Funcs(xxx).ParseFiles(name)
|
|
||||||
// works. Otherwise we create a new template associated with t.
|
|
||||||
var tmpl *Template
|
|
||||||
if t == nil {
|
|
||||||
t = New(name)
|
|
||||||
}
|
|
||||||
if name == t.Name() {
|
|
||||||
tmpl = t
|
|
||||||
} else {
|
|
||||||
tmpl = t.New(name)
|
|
||||||
}
|
|
||||||
_, err = tmpl.Parse(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseGlob creates a new Template and parses the template definitions from the
|
|
||||||
// files identified by the pattern, which must match at least one file. The
|
|
||||||
// returned template will have the (base) name and (parsed) contents of the
|
|
||||||
// first file matched by the pattern. ParseGlob is equivalent to calling
|
|
||||||
// ParseFiles with the list of files matched by the pattern.
|
|
||||||
func ParseGlob(pattern string) (*Template, error) {
|
|
||||||
return parseGlob(nil, pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseGlob parses the template definitions in the files identified by the
|
|
||||||
// pattern and associates the resulting templates with t. The pattern is
|
|
||||||
// processed by filepath.Glob and must match at least one file. ParseGlob is
|
|
||||||
// equivalent to calling t.ParseFiles with the list of files matched by the
|
|
||||||
// pattern.
|
|
||||||
func (t *Template) ParseGlob(pattern string) (*Template, error) {
|
|
||||||
return parseGlob(t, pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseGlob is the implementation of the function and method ParseGlob.
|
|
||||||
func parseGlob(t *Template, pattern string) (*Template, error) {
|
|
||||||
filenames, err := filepath.Glob(pattern)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(filenames) == 0 {
|
|
||||||
return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
|
|
||||||
}
|
|
||||||
return parseFiles(t, filenames...)
|
|
||||||
}
|
|
556
vendor/github.com/alecthomas/template/parse/lex.go
generated
vendored
556
vendor/github.com/alecthomas/template/parse/lex.go
generated
vendored
|
@ -1,556 +0,0 @@
|
||||||
// Copyright 2011 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 parse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// item represents a token or text string returned from the scanner.
|
|
||||||
type item struct {
|
|
||||||
typ itemType // The type of this item.
|
|
||||||
pos Pos // The starting position, in bytes, of this item in the input string.
|
|
||||||
val string // The value of this item.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i item) String() string {
|
|
||||||
switch {
|
|
||||||
case i.typ == itemEOF:
|
|
||||||
return "EOF"
|
|
||||||
case i.typ == itemError:
|
|
||||||
return i.val
|
|
||||||
case i.typ > itemKeyword:
|
|
||||||
return fmt.Sprintf("<%s>", i.val)
|
|
||||||
case len(i.val) > 10:
|
|
||||||
return fmt.Sprintf("%.10q...", i.val)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%q", i.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// itemType identifies the type of lex items.
|
|
||||||
type itemType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
itemError itemType = iota // error occurred; value is text of error
|
|
||||||
itemBool // boolean constant
|
|
||||||
itemChar // printable ASCII character; grab bag for comma etc.
|
|
||||||
itemCharConstant // character constant
|
|
||||||
itemComplex // complex constant (1+2i); imaginary is just a number
|
|
||||||
itemColonEquals // colon-equals (':=') introducing a declaration
|
|
||||||
itemEOF
|
|
||||||
itemField // alphanumeric identifier starting with '.'
|
|
||||||
itemIdentifier // alphanumeric identifier not starting with '.'
|
|
||||||
itemLeftDelim // left action delimiter
|
|
||||||
itemLeftParen // '(' inside action
|
|
||||||
itemNumber // simple number, including imaginary
|
|
||||||
itemPipe // pipe symbol
|
|
||||||
itemRawString // raw quoted string (includes quotes)
|
|
||||||
itemRightDelim // right action delimiter
|
|
||||||
itemElideNewline // elide newline after right delim
|
|
||||||
itemRightParen // ')' inside action
|
|
||||||
itemSpace // run of spaces separating arguments
|
|
||||||
itemString // quoted string (includes quotes)
|
|
||||||
itemText // plain text
|
|
||||||
itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'
|
|
||||||
// Keywords appear after all the rest.
|
|
||||||
itemKeyword // used only to delimit the keywords
|
|
||||||
itemDot // the cursor, spelled '.'
|
|
||||||
itemDefine // define keyword
|
|
||||||
itemElse // else keyword
|
|
||||||
itemEnd // end keyword
|
|
||||||
itemIf // if keyword
|
|
||||||
itemNil // the untyped nil constant, easiest to treat as a keyword
|
|
||||||
itemRange // range keyword
|
|
||||||
itemTemplate // template keyword
|
|
||||||
itemWith // with keyword
|
|
||||||
)
|
|
||||||
|
|
||||||
var key = map[string]itemType{
|
|
||||||
".": itemDot,
|
|
||||||
"define": itemDefine,
|
|
||||||
"else": itemElse,
|
|
||||||
"end": itemEnd,
|
|
||||||
"if": itemIf,
|
|
||||||
"range": itemRange,
|
|
||||||
"nil": itemNil,
|
|
||||||
"template": itemTemplate,
|
|
||||||
"with": itemWith,
|
|
||||||
}
|
|
||||||
|
|
||||||
const eof = -1
|
|
||||||
|
|
||||||
// stateFn represents the state of the scanner as a function that returns the next state.
|
|
||||||
type stateFn func(*lexer) stateFn
|
|
||||||
|
|
||||||
// lexer holds the state of the scanner.
|
|
||||||
type lexer struct {
|
|
||||||
name string // the name of the input; used only for error reports
|
|
||||||
input string // the string being scanned
|
|
||||||
leftDelim string // start of action
|
|
||||||
rightDelim string // end of action
|
|
||||||
state stateFn // the next lexing function to enter
|
|
||||||
pos Pos // current position in the input
|
|
||||||
start Pos // start position of this item
|
|
||||||
width Pos // width of last rune read from input
|
|
||||||
lastPos Pos // position of most recent item returned by nextItem
|
|
||||||
items chan item // channel of scanned items
|
|
||||||
parenDepth int // nesting depth of ( ) exprs
|
|
||||||
}
|
|
||||||
|
|
||||||
// next returns the next rune in the input.
|
|
||||||
func (l *lexer) next() rune {
|
|
||||||
if int(l.pos) >= len(l.input) {
|
|
||||||
l.width = 0
|
|
||||||
return eof
|
|
||||||
}
|
|
||||||
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
|
|
||||||
l.width = Pos(w)
|
|
||||||
l.pos += l.width
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek returns but does not consume the next rune in the input.
|
|
||||||
func (l *lexer) peek() rune {
|
|
||||||
r := l.next()
|
|
||||||
l.backup()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup steps back one rune. Can only be called once per call of next.
|
|
||||||
func (l *lexer) backup() {
|
|
||||||
l.pos -= l.width
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit passes an item back to the client.
|
|
||||||
func (l *lexer) emit(t itemType) {
|
|
||||||
l.items <- item{t, l.start, l.input[l.start:l.pos]}
|
|
||||||
l.start = l.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore skips over the pending input before this point.
|
|
||||||
func (l *lexer) ignore() {
|
|
||||||
l.start = l.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// accept consumes the next rune if it's from the valid set.
|
|
||||||
func (l *lexer) accept(valid string) bool {
|
|
||||||
if strings.IndexRune(valid, l.next()) >= 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
l.backup()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// acceptRun consumes a run of runes from the valid set.
|
|
||||||
func (l *lexer) acceptRun(valid string) {
|
|
||||||
for strings.IndexRune(valid, l.next()) >= 0 {
|
|
||||||
}
|
|
||||||
l.backup()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lineNumber reports which line we're on, based on the position of
|
|
||||||
// the previous item returned by nextItem. Doing it this way
|
|
||||||
// means we don't have to worry about peek double counting.
|
|
||||||
func (l *lexer) lineNumber() int {
|
|
||||||
return 1 + strings.Count(l.input[:l.lastPos], "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf returns an error token and terminates the scan by passing
|
|
||||||
// back a nil pointer that will be the next state, terminating l.nextItem.
|
|
||||||
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
|
|
||||||
l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextItem returns the next item from the input.
|
|
||||||
func (l *lexer) nextItem() item {
|
|
||||||
item := <-l.items
|
|
||||||
l.lastPos = item.pos
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
|
|
||||||
// lex creates a new scanner for the input string.
|
|
||||||
func lex(name, input, left, right string) *lexer {
|
|
||||||
if left == "" {
|
|
||||||
left = leftDelim
|
|
||||||
}
|
|
||||||
if right == "" {
|
|
||||||
right = rightDelim
|
|
||||||
}
|
|
||||||
l := &lexer{
|
|
||||||
name: name,
|
|
||||||
input: input,
|
|
||||||
leftDelim: left,
|
|
||||||
rightDelim: right,
|
|
||||||
items: make(chan item),
|
|
||||||
}
|
|
||||||
go l.run()
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// run runs the state machine for the lexer.
|
|
||||||
func (l *lexer) run() {
|
|
||||||
for l.state = lexText; l.state != nil; {
|
|
||||||
l.state = l.state(l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// state functions
|
|
||||||
|
|
||||||
const (
|
|
||||||
leftDelim = "{{"
|
|
||||||
rightDelim = "}}"
|
|
||||||
leftComment = "/*"
|
|
||||||
rightComment = "*/"
|
|
||||||
)
|
|
||||||
|
|
||||||
// lexText scans until an opening action delimiter, "{{".
|
|
||||||
func lexText(l *lexer) stateFn {
|
|
||||||
for {
|
|
||||||
if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
|
|
||||||
if l.pos > l.start {
|
|
||||||
l.emit(itemText)
|
|
||||||
}
|
|
||||||
return lexLeftDelim
|
|
||||||
}
|
|
||||||
if l.next() == eof {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Correctly reached EOF.
|
|
||||||
if l.pos > l.start {
|
|
||||||
l.emit(itemText)
|
|
||||||
}
|
|
||||||
l.emit(itemEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexLeftDelim scans the left delimiter, which is known to be present.
|
|
||||||
func lexLeftDelim(l *lexer) stateFn {
|
|
||||||
l.pos += Pos(len(l.leftDelim))
|
|
||||||
if strings.HasPrefix(l.input[l.pos:], leftComment) {
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
l.emit(itemLeftDelim)
|
|
||||||
l.parenDepth = 0
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexComment scans a comment. The left comment marker is known to be present.
|
|
||||||
func lexComment(l *lexer) stateFn {
|
|
||||||
l.pos += Pos(len(leftComment))
|
|
||||||
i := strings.Index(l.input[l.pos:], rightComment)
|
|
||||||
if i < 0 {
|
|
||||||
return l.errorf("unclosed comment")
|
|
||||||
}
|
|
||||||
l.pos += Pos(i + len(rightComment))
|
|
||||||
if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
|
|
||||||
return l.errorf("comment ends before closing delimiter")
|
|
||||||
|
|
||||||
}
|
|
||||||
l.pos += Pos(len(l.rightDelim))
|
|
||||||
l.ignore()
|
|
||||||
return lexText
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexRightDelim scans the right delimiter, which is known to be present.
|
|
||||||
func lexRightDelim(l *lexer) stateFn {
|
|
||||||
l.pos += Pos(len(l.rightDelim))
|
|
||||||
l.emit(itemRightDelim)
|
|
||||||
if l.peek() == '\\' {
|
|
||||||
l.pos++
|
|
||||||
l.emit(itemElideNewline)
|
|
||||||
}
|
|
||||||
return lexText
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInsideAction scans the elements inside action delimiters.
|
|
||||||
func lexInsideAction(l *lexer) stateFn {
|
|
||||||
// Either number, quoted string, or identifier.
|
|
||||||
// Spaces separate arguments; runs of spaces turn into itemSpace.
|
|
||||||
// Pipe symbols separate and are emitted.
|
|
||||||
if strings.HasPrefix(l.input[l.pos:], l.rightDelim+"\\") || strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
|
|
||||||
if l.parenDepth == 0 {
|
|
||||||
return lexRightDelim
|
|
||||||
}
|
|
||||||
return l.errorf("unclosed left paren")
|
|
||||||
}
|
|
||||||
switch r := l.next(); {
|
|
||||||
case r == eof || isEndOfLine(r):
|
|
||||||
return l.errorf("unclosed action")
|
|
||||||
case isSpace(r):
|
|
||||||
return lexSpace
|
|
||||||
case r == ':':
|
|
||||||
if l.next() != '=' {
|
|
||||||
return l.errorf("expected :=")
|
|
||||||
}
|
|
||||||
l.emit(itemColonEquals)
|
|
||||||
case r == '|':
|
|
||||||
l.emit(itemPipe)
|
|
||||||
case r == '"':
|
|
||||||
return lexQuote
|
|
||||||
case r == '`':
|
|
||||||
return lexRawQuote
|
|
||||||
case r == '$':
|
|
||||||
return lexVariable
|
|
||||||
case r == '\'':
|
|
||||||
return lexChar
|
|
||||||
case r == '.':
|
|
||||||
// special look-ahead for ".field" so we don't break l.backup().
|
|
||||||
if l.pos < Pos(len(l.input)) {
|
|
||||||
r := l.input[l.pos]
|
|
||||||
if r < '0' || '9' < r {
|
|
||||||
return lexField
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fallthrough // '.' can start a number.
|
|
||||||
case r == '+' || r == '-' || ('0' <= r && r <= '9'):
|
|
||||||
l.backup()
|
|
||||||
return lexNumber
|
|
||||||
case isAlphaNumeric(r):
|
|
||||||
l.backup()
|
|
||||||
return lexIdentifier
|
|
||||||
case r == '(':
|
|
||||||
l.emit(itemLeftParen)
|
|
||||||
l.parenDepth++
|
|
||||||
return lexInsideAction
|
|
||||||
case r == ')':
|
|
||||||
l.emit(itemRightParen)
|
|
||||||
l.parenDepth--
|
|
||||||
if l.parenDepth < 0 {
|
|
||||||
return l.errorf("unexpected right paren %#U", r)
|
|
||||||
}
|
|
||||||
return lexInsideAction
|
|
||||||
case r <= unicode.MaxASCII && unicode.IsPrint(r):
|
|
||||||
l.emit(itemChar)
|
|
||||||
return lexInsideAction
|
|
||||||
default:
|
|
||||||
return l.errorf("unrecognized character in action: %#U", r)
|
|
||||||
}
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexSpace scans a run of space characters.
|
|
||||||
// One space has already been seen.
|
|
||||||
func lexSpace(l *lexer) stateFn {
|
|
||||||
for isSpace(l.peek()) {
|
|
||||||
l.next()
|
|
||||||
}
|
|
||||||
l.emit(itemSpace)
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexIdentifier scans an alphanumeric.
|
|
||||||
func lexIdentifier(l *lexer) stateFn {
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
switch r := l.next(); {
|
|
||||||
case isAlphaNumeric(r):
|
|
||||||
// absorb.
|
|
||||||
default:
|
|
||||||
l.backup()
|
|
||||||
word := l.input[l.start:l.pos]
|
|
||||||
if !l.atTerminator() {
|
|
||||||
return l.errorf("bad character %#U", r)
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case key[word] > itemKeyword:
|
|
||||||
l.emit(key[word])
|
|
||||||
case word[0] == '.':
|
|
||||||
l.emit(itemField)
|
|
||||||
case word == "true", word == "false":
|
|
||||||
l.emit(itemBool)
|
|
||||||
default:
|
|
||||||
l.emit(itemIdentifier)
|
|
||||||
}
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexField scans a field: .Alphanumeric.
|
|
||||||
// The . has been scanned.
|
|
||||||
func lexField(l *lexer) stateFn {
|
|
||||||
return lexFieldOrVariable(l, itemField)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexVariable scans a Variable: $Alphanumeric.
|
|
||||||
// The $ has been scanned.
|
|
||||||
func lexVariable(l *lexer) stateFn {
|
|
||||||
if l.atTerminator() { // Nothing interesting follows -> "$".
|
|
||||||
l.emit(itemVariable)
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
return lexFieldOrVariable(l, itemVariable)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexVariable scans a field or variable: [.$]Alphanumeric.
|
|
||||||
// The . or $ has been scanned.
|
|
||||||
func lexFieldOrVariable(l *lexer, typ itemType) stateFn {
|
|
||||||
if l.atTerminator() { // Nothing interesting follows -> "." or "$".
|
|
||||||
if typ == itemVariable {
|
|
||||||
l.emit(itemVariable)
|
|
||||||
} else {
|
|
||||||
l.emit(itemDot)
|
|
||||||
}
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
var r rune
|
|
||||||
for {
|
|
||||||
r = l.next()
|
|
||||||
if !isAlphaNumeric(r) {
|
|
||||||
l.backup()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !l.atTerminator() {
|
|
||||||
return l.errorf("bad character %#U", r)
|
|
||||||
}
|
|
||||||
l.emit(typ)
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// atTerminator reports whether the input is at valid termination character to
|
|
||||||
// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases
|
|
||||||
// like "$x+2" not being acceptable without a space, in case we decide one
|
|
||||||
// day to implement arithmetic.
|
|
||||||
func (l *lexer) atTerminator() bool {
|
|
||||||
r := l.peek()
|
|
||||||
if isSpace(r) || isEndOfLine(r) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case eof, '.', ',', '|', ':', ')', '(':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
|
|
||||||
// succeed but should fail) but only in extremely rare cases caused by willfully
|
|
||||||
// bad choice of delimiter.
|
|
||||||
if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexChar scans a character constant. The initial quote is already
|
|
||||||
// scanned. Syntax checking is done by the parser.
|
|
||||||
func lexChar(l *lexer) stateFn {
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
switch l.next() {
|
|
||||||
case '\\':
|
|
||||||
if r := l.next(); r != eof && r != '\n' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case eof, '\n':
|
|
||||||
return l.errorf("unterminated character constant")
|
|
||||||
case '\'':
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.emit(itemCharConstant)
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
|
|
||||||
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
|
|
||||||
// and "089" - but when it's wrong the input is invalid and the parser (via
|
|
||||||
// strconv) will notice.
|
|
||||||
func lexNumber(l *lexer) stateFn {
|
|
||||||
if !l.scanNumber() {
|
|
||||||
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
|
||||||
}
|
|
||||||
if sign := l.peek(); sign == '+' || sign == '-' {
|
|
||||||
// Complex: 1+2i. No spaces, must end in 'i'.
|
|
||||||
if !l.scanNumber() || l.input[l.pos-1] != 'i' {
|
|
||||||
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
|
||||||
}
|
|
||||||
l.emit(itemComplex)
|
|
||||||
} else {
|
|
||||||
l.emit(itemNumber)
|
|
||||||
}
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *lexer) scanNumber() bool {
|
|
||||||
// Optional leading sign.
|
|
||||||
l.accept("+-")
|
|
||||||
// Is it hex?
|
|
||||||
digits := "0123456789"
|
|
||||||
if l.accept("0") && l.accept("xX") {
|
|
||||||
digits = "0123456789abcdefABCDEF"
|
|
||||||
}
|
|
||||||
l.acceptRun(digits)
|
|
||||||
if l.accept(".") {
|
|
||||||
l.acceptRun(digits)
|
|
||||||
}
|
|
||||||
if l.accept("eE") {
|
|
||||||
l.accept("+-")
|
|
||||||
l.acceptRun("0123456789")
|
|
||||||
}
|
|
||||||
// Is it imaginary?
|
|
||||||
l.accept("i")
|
|
||||||
// Next thing mustn't be alphanumeric.
|
|
||||||
if isAlphaNumeric(l.peek()) {
|
|
||||||
l.next()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexQuote scans a quoted string.
|
|
||||||
func lexQuote(l *lexer) stateFn {
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
switch l.next() {
|
|
||||||
case '\\':
|
|
||||||
if r := l.next(); r != eof && r != '\n' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case eof, '\n':
|
|
||||||
return l.errorf("unterminated quoted string")
|
|
||||||
case '"':
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.emit(itemString)
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexRawQuote scans a raw quoted string.
|
|
||||||
func lexRawQuote(l *lexer) stateFn {
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
switch l.next() {
|
|
||||||
case eof, '\n':
|
|
||||||
return l.errorf("unterminated raw quoted string")
|
|
||||||
case '`':
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.emit(itemRawString)
|
|
||||||
return lexInsideAction
|
|
||||||
}
|
|
||||||
|
|
||||||
// isSpace reports whether r is a space character.
|
|
||||||
func isSpace(r rune) bool {
|
|
||||||
return r == ' ' || r == '\t'
|
|
||||||
}
|
|
||||||
|
|
||||||
// isEndOfLine reports whether r is an end-of-line character.
|
|
||||||
func isEndOfLine(r rune) bool {
|
|
||||||
return r == '\r' || r == '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
|
|
||||||
func isAlphaNumeric(r rune) bool {
|
|
||||||
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
|
|
||||||
}
|
|
834
vendor/github.com/alecthomas/template/parse/node.go
generated
vendored
834
vendor/github.com/alecthomas/template/parse/node.go
generated
vendored
|
@ -1,834 +0,0 @@
|
||||||
// Copyright 2011 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.
|
|
||||||
|
|
||||||
// Parse nodes.
|
|
||||||
|
|
||||||
package parse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var textFormat = "%s" // Changed to "%q" in tests for better error messages.
|
|
||||||
|
|
||||||
// A Node is an element in the parse tree. The interface is trivial.
|
|
||||||
// The interface contains an unexported method so that only
|
|
||||||
// types local to this package can satisfy it.
|
|
||||||
type Node interface {
|
|
||||||
Type() NodeType
|
|
||||||
String() string
|
|
||||||
// Copy does a deep copy of the Node and all its components.
|
|
||||||
// To avoid type assertions, some XxxNodes also have specialized
|
|
||||||
// CopyXxx methods that return *XxxNode.
|
|
||||||
Copy() Node
|
|
||||||
Position() Pos // byte position of start of node in full original input string
|
|
||||||
// tree returns the containing *Tree.
|
|
||||||
// It is unexported so all implementations of Node are in this package.
|
|
||||||
tree() *Tree
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeType identifies the type of a parse tree node.
|
|
||||||
type NodeType int
|
|
||||||
|
|
||||||
// Pos represents a byte position in the original input text from which
|
|
||||||
// this template was parsed.
|
|
||||||
type Pos int
|
|
||||||
|
|
||||||
func (p Pos) Position() Pos {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns itself and provides an easy default implementation
|
|
||||||
// for embedding in a Node. Embedded in all non-trivial Nodes.
|
|
||||||
func (t NodeType) Type() NodeType {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
NodeText NodeType = iota // Plain text.
|
|
||||||
NodeAction // A non-control action such as a field evaluation.
|
|
||||||
NodeBool // A boolean constant.
|
|
||||||
NodeChain // A sequence of field accesses.
|
|
||||||
NodeCommand // An element of a pipeline.
|
|
||||||
NodeDot // The cursor, dot.
|
|
||||||
nodeElse // An else action. Not added to tree.
|
|
||||||
nodeEnd // An end action. Not added to tree.
|
|
||||||
NodeField // A field or method name.
|
|
||||||
NodeIdentifier // An identifier; always a function name.
|
|
||||||
NodeIf // An if action.
|
|
||||||
NodeList // A list of Nodes.
|
|
||||||
NodeNil // An untyped nil constant.
|
|
||||||
NodeNumber // A numerical constant.
|
|
||||||
NodePipe // A pipeline of commands.
|
|
||||||
NodeRange // A range action.
|
|
||||||
NodeString // A string constant.
|
|
||||||
NodeTemplate // A template invocation action.
|
|
||||||
NodeVariable // A $ variable.
|
|
||||||
NodeWith // A with action.
|
|
||||||
)
|
|
||||||
|
|
||||||
// Nodes.
|
|
||||||
|
|
||||||
// ListNode holds a sequence of nodes.
|
|
||||||
type ListNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Nodes []Node // The element nodes in lexical order.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newList(pos Pos) *ListNode {
|
|
||||||
return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *ListNode) append(n Node) {
|
|
||||||
l.Nodes = append(l.Nodes, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *ListNode) tree() *Tree {
|
|
||||||
return l.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *ListNode) String() string {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
for _, n := range l.Nodes {
|
|
||||||
fmt.Fprint(b, n)
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *ListNode) CopyList() *ListNode {
|
|
||||||
if l == nil {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
n := l.tr.newList(l.Pos)
|
|
||||||
for _, elem := range l.Nodes {
|
|
||||||
n.append(elem.Copy())
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *ListNode) Copy() Node {
|
|
||||||
return l.CopyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextNode holds plain text.
|
|
||||||
type TextNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Text []byte // The text; may span newlines.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newText(pos Pos, text string) *TextNode {
|
|
||||||
return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TextNode) String() string {
|
|
||||||
return fmt.Sprintf(textFormat, t.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TextNode) tree() *Tree {
|
|
||||||
return t.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TextNode) Copy() Node {
|
|
||||||
return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PipeNode holds a pipeline with optional declaration
|
|
||||||
type PipeNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Line int // The line number in the input (deprecated; kept for compatibility)
|
|
||||||
Decl []*VariableNode // Variable declarations in lexical order.
|
|
||||||
Cmds []*CommandNode // The commands in lexical order.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode {
|
|
||||||
return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PipeNode) append(command *CommandNode) {
|
|
||||||
p.Cmds = append(p.Cmds, command)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PipeNode) String() string {
|
|
||||||
s := ""
|
|
||||||
if len(p.Decl) > 0 {
|
|
||||||
for i, v := range p.Decl {
|
|
||||||
if i > 0 {
|
|
||||||
s += ", "
|
|
||||||
}
|
|
||||||
s += v.String()
|
|
||||||
}
|
|
||||||
s += " := "
|
|
||||||
}
|
|
||||||
for i, c := range p.Cmds {
|
|
||||||
if i > 0 {
|
|
||||||
s += " | "
|
|
||||||
}
|
|
||||||
s += c.String()
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PipeNode) tree() *Tree {
|
|
||||||
return p.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PipeNode) CopyPipe() *PipeNode {
|
|
||||||
if p == nil {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
var decl []*VariableNode
|
|
||||||
for _, d := range p.Decl {
|
|
||||||
decl = append(decl, d.Copy().(*VariableNode))
|
|
||||||
}
|
|
||||||
n := p.tr.newPipeline(p.Pos, p.Line, decl)
|
|
||||||
for _, c := range p.Cmds {
|
|
||||||
n.append(c.Copy().(*CommandNode))
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PipeNode) Copy() Node {
|
|
||||||
return p.CopyPipe()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActionNode holds an action (something bounded by delimiters).
|
|
||||||
// Control actions have their own nodes; ActionNode represents simple
|
|
||||||
// ones such as field evaluations and parenthesized pipelines.
|
|
||||||
type ActionNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Line int // The line number in the input (deprecated; kept for compatibility)
|
|
||||||
Pipe *PipeNode // The pipeline in the action.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
|
|
||||||
return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ActionNode) String() string {
|
|
||||||
return fmt.Sprintf("{{%s}}", a.Pipe)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ActionNode) tree() *Tree {
|
|
||||||
return a.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ActionNode) Copy() Node {
|
|
||||||
return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommandNode holds a command (a pipeline inside an evaluating action).
|
|
||||||
type CommandNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Args []Node // Arguments in lexical order: Identifier, field, or constant.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newCommand(pos Pos) *CommandNode {
|
|
||||||
return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandNode) append(arg Node) {
|
|
||||||
c.Args = append(c.Args, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandNode) String() string {
|
|
||||||
s := ""
|
|
||||||
for i, arg := range c.Args {
|
|
||||||
if i > 0 {
|
|
||||||
s += " "
|
|
||||||
}
|
|
||||||
if arg, ok := arg.(*PipeNode); ok {
|
|
||||||
s += "(" + arg.String() + ")"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s += arg.String()
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandNode) tree() *Tree {
|
|
||||||
return c.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandNode) Copy() Node {
|
|
||||||
if c == nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
n := c.tr.newCommand(c.Pos)
|
|
||||||
for _, c := range c.Args {
|
|
||||||
n.append(c.Copy())
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdentifierNode holds an identifier.
|
|
||||||
type IdentifierNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Ident string // The identifier's name.
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIdentifier returns a new IdentifierNode with the given identifier name.
|
|
||||||
func NewIdentifier(ident string) *IdentifierNode {
|
|
||||||
return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature.
|
|
||||||
// Chained for convenience.
|
|
||||||
// TODO: fix one day?
|
|
||||||
func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
|
|
||||||
i.Pos = pos
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature.
|
|
||||||
// Chained for convenience.
|
|
||||||
// TODO: fix one day?
|
|
||||||
func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
|
|
||||||
i.tr = t
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *IdentifierNode) String() string {
|
|
||||||
return i.Ident
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *IdentifierNode) tree() *Tree {
|
|
||||||
return i.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *IdentifierNode) Copy() Node {
|
|
||||||
return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VariableNode holds a list of variable names, possibly with chained field
|
|
||||||
// accesses. The dollar sign is part of the (first) name.
|
|
||||||
type VariableNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Ident []string // Variable name and fields in lexical order.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
|
|
||||||
return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VariableNode) String() string {
|
|
||||||
s := ""
|
|
||||||
for i, id := range v.Ident {
|
|
||||||
if i > 0 {
|
|
||||||
s += "."
|
|
||||||
}
|
|
||||||
s += id
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VariableNode) tree() *Tree {
|
|
||||||
return v.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VariableNode) Copy() Node {
|
|
||||||
return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DotNode holds the special identifier '.'.
|
|
||||||
type DotNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newDot(pos Pos) *DotNode {
|
|
||||||
return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DotNode) Type() NodeType {
|
|
||||||
// Override method on embedded NodeType for API compatibility.
|
|
||||||
// TODO: Not really a problem; could change API without effect but
|
|
||||||
// api tool complains.
|
|
||||||
return NodeDot
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DotNode) String() string {
|
|
||||||
return "."
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DotNode) tree() *Tree {
|
|
||||||
return d.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DotNode) Copy() Node {
|
|
||||||
return d.tr.newDot(d.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NilNode holds the special identifier 'nil' representing an untyped nil constant.
|
|
||||||
type NilNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newNil(pos Pos) *NilNode {
|
|
||||||
return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NilNode) Type() NodeType {
|
|
||||||
// Override method on embedded NodeType for API compatibility.
|
|
||||||
// TODO: Not really a problem; could change API without effect but
|
|
||||||
// api tool complains.
|
|
||||||
return NodeNil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NilNode) String() string {
|
|
||||||
return "nil"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NilNode) tree() *Tree {
|
|
||||||
return n.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NilNode) Copy() Node {
|
|
||||||
return n.tr.newNil(n.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FieldNode holds a field (identifier starting with '.').
|
|
||||||
// The names may be chained ('.x.y').
|
|
||||||
// The period is dropped from each ident.
|
|
||||||
type FieldNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Ident []string // The identifiers in lexical order.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newField(pos Pos, ident string) *FieldNode {
|
|
||||||
return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FieldNode) String() string {
|
|
||||||
s := ""
|
|
||||||
for _, id := range f.Ident {
|
|
||||||
s += "." + id
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FieldNode) tree() *Tree {
|
|
||||||
return f.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FieldNode) Copy() Node {
|
|
||||||
return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.').
|
|
||||||
// The names may be chained ('.x.y').
|
|
||||||
// The periods are dropped from each ident.
|
|
||||||
type ChainNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Node Node
|
|
||||||
Field []string // The identifiers in lexical order.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newChain(pos Pos, node Node) *ChainNode {
|
|
||||||
return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds the named field (which should start with a period) to the end of the chain.
|
|
||||||
func (c *ChainNode) Add(field string) {
|
|
||||||
if len(field) == 0 || field[0] != '.' {
|
|
||||||
panic("no dot in field")
|
|
||||||
}
|
|
||||||
field = field[1:] // Remove leading dot.
|
|
||||||
if field == "" {
|
|
||||||
panic("empty field")
|
|
||||||
}
|
|
||||||
c.Field = append(c.Field, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ChainNode) String() string {
|
|
||||||
s := c.Node.String()
|
|
||||||
if _, ok := c.Node.(*PipeNode); ok {
|
|
||||||
s = "(" + s + ")"
|
|
||||||
}
|
|
||||||
for _, field := range c.Field {
|
|
||||||
s += "." + field
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ChainNode) tree() *Tree {
|
|
||||||
return c.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ChainNode) Copy() Node {
|
|
||||||
return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolNode holds a boolean constant.
|
|
||||||
type BoolNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
True bool // The value of the boolean constant.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
|
|
||||||
return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BoolNode) String() string {
|
|
||||||
if b.True {
|
|
||||||
return "true"
|
|
||||||
}
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BoolNode) tree() *Tree {
|
|
||||||
return b.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BoolNode) Copy() Node {
|
|
||||||
return b.tr.newBool(b.Pos, b.True)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumberNode holds a number: signed or unsigned integer, float, or complex.
|
|
||||||
// The value is parsed and stored under all the types that can represent the value.
|
|
||||||
// This simulates in a small amount of code the behavior of Go's ideal constants.
|
|
||||||
type NumberNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
IsInt bool // Number has an integral value.
|
|
||||||
IsUint bool // Number has an unsigned integral value.
|
|
||||||
IsFloat bool // Number has a floating-point value.
|
|
||||||
IsComplex bool // Number is complex.
|
|
||||||
Int64 int64 // The signed integer value.
|
|
||||||
Uint64 uint64 // The unsigned integer value.
|
|
||||||
Float64 float64 // The floating-point value.
|
|
||||||
Complex128 complex128 // The complex value.
|
|
||||||
Text string // The original textual representation from the input.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
|
|
||||||
n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
|
|
||||||
switch typ {
|
|
||||||
case itemCharConstant:
|
|
||||||
rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if tail != "'" {
|
|
||||||
return nil, fmt.Errorf("malformed character constant: %s", text)
|
|
||||||
}
|
|
||||||
n.Int64 = int64(rune)
|
|
||||||
n.IsInt = true
|
|
||||||
n.Uint64 = uint64(rune)
|
|
||||||
n.IsUint = true
|
|
||||||
n.Float64 = float64(rune) // odd but those are the rules.
|
|
||||||
n.IsFloat = true
|
|
||||||
return n, nil
|
|
||||||
case itemComplex:
|
|
||||||
// fmt.Sscan can parse the pair, so let it do the work.
|
|
||||||
if _, err := fmt.Sscan(text, &n.Complex128); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
n.IsComplex = true
|
|
||||||
n.simplifyComplex()
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
// Imaginary constants can only be complex unless they are zero.
|
|
||||||
if len(text) > 0 && text[len(text)-1] == 'i' {
|
|
||||||
f, err := strconv.ParseFloat(text[:len(text)-1], 64)
|
|
||||||
if err == nil {
|
|
||||||
n.IsComplex = true
|
|
||||||
n.Complex128 = complex(0, f)
|
|
||||||
n.simplifyComplex()
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Do integer test first so we get 0x123 etc.
|
|
||||||
u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below.
|
|
||||||
if err == nil {
|
|
||||||
n.IsUint = true
|
|
||||||
n.Uint64 = u
|
|
||||||
}
|
|
||||||
i, err := strconv.ParseInt(text, 0, 64)
|
|
||||||
if err == nil {
|
|
||||||
n.IsInt = true
|
|
||||||
n.Int64 = i
|
|
||||||
if i == 0 {
|
|
||||||
n.IsUint = true // in case of -0.
|
|
||||||
n.Uint64 = u
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If an integer extraction succeeded, promote the float.
|
|
||||||
if n.IsInt {
|
|
||||||
n.IsFloat = true
|
|
||||||
n.Float64 = float64(n.Int64)
|
|
||||||
} else if n.IsUint {
|
|
||||||
n.IsFloat = true
|
|
||||||
n.Float64 = float64(n.Uint64)
|
|
||||||
} else {
|
|
||||||
f, err := strconv.ParseFloat(text, 64)
|
|
||||||
if err == nil {
|
|
||||||
n.IsFloat = true
|
|
||||||
n.Float64 = f
|
|
||||||
// If a floating-point extraction succeeded, extract the int if needed.
|
|
||||||
if !n.IsInt && float64(int64(f)) == f {
|
|
||||||
n.IsInt = true
|
|
||||||
n.Int64 = int64(f)
|
|
||||||
}
|
|
||||||
if !n.IsUint && float64(uint64(f)) == f {
|
|
||||||
n.IsUint = true
|
|
||||||
n.Uint64 = uint64(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !n.IsInt && !n.IsUint && !n.IsFloat {
|
|
||||||
return nil, fmt.Errorf("illegal number syntax: %q", text)
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// simplifyComplex pulls out any other types that are represented by the complex number.
|
|
||||||
// These all require that the imaginary part be zero.
|
|
||||||
func (n *NumberNode) simplifyComplex() {
|
|
||||||
n.IsFloat = imag(n.Complex128) == 0
|
|
||||||
if n.IsFloat {
|
|
||||||
n.Float64 = real(n.Complex128)
|
|
||||||
n.IsInt = float64(int64(n.Float64)) == n.Float64
|
|
||||||
if n.IsInt {
|
|
||||||
n.Int64 = int64(n.Float64)
|
|
||||||
}
|
|
||||||
n.IsUint = float64(uint64(n.Float64)) == n.Float64
|
|
||||||
if n.IsUint {
|
|
||||||
n.Uint64 = uint64(n.Float64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NumberNode) String() string {
|
|
||||||
return n.Text
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NumberNode) tree() *Tree {
|
|
||||||
return n.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NumberNode) Copy() Node {
|
|
||||||
nn := new(NumberNode)
|
|
||||||
*nn = *n // Easy, fast, correct.
|
|
||||||
return nn
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringNode holds a string constant. The value has been "unquoted".
|
|
||||||
type StringNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Quoted string // The original text of the string, with quotes.
|
|
||||||
Text string // The string, after quote processing.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
|
|
||||||
return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StringNode) String() string {
|
|
||||||
return s.Quoted
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StringNode) tree() *Tree {
|
|
||||||
return s.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StringNode) Copy() Node {
|
|
||||||
return s.tr.newString(s.Pos, s.Quoted, s.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endNode represents an {{end}} action.
|
|
||||||
// It does not appear in the final parse tree.
|
|
||||||
type endNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newEnd(pos Pos) *endNode {
|
|
||||||
return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *endNode) String() string {
|
|
||||||
return "{{end}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *endNode) tree() *Tree {
|
|
||||||
return e.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *endNode) Copy() Node {
|
|
||||||
return e.tr.newEnd(e.Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// elseNode represents an {{else}} action. Does not appear in the final tree.
|
|
||||||
type elseNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Line int // The line number in the input (deprecated; kept for compatibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newElse(pos Pos, line int) *elseNode {
|
|
||||||
return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *elseNode) Type() NodeType {
|
|
||||||
return nodeElse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *elseNode) String() string {
|
|
||||||
return "{{else}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *elseNode) tree() *Tree {
|
|
||||||
return e.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *elseNode) Copy() Node {
|
|
||||||
return e.tr.newElse(e.Pos, e.Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BranchNode is the common representation of if, range, and with.
|
|
||||||
type BranchNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Line int // The line number in the input (deprecated; kept for compatibility)
|
|
||||||
Pipe *PipeNode // The pipeline to be evaluated.
|
|
||||||
List *ListNode // What to execute if the value is non-empty.
|
|
||||||
ElseList *ListNode // What to execute if the value is empty (nil if absent).
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BranchNode) String() string {
|
|
||||||
name := ""
|
|
||||||
switch b.NodeType {
|
|
||||||
case NodeIf:
|
|
||||||
name = "if"
|
|
||||||
case NodeRange:
|
|
||||||
name = "range"
|
|
||||||
case NodeWith:
|
|
||||||
name = "with"
|
|
||||||
default:
|
|
||||||
panic("unknown branch type")
|
|
||||||
}
|
|
||||||
if b.ElseList != nil {
|
|
||||||
return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BranchNode) tree() *Tree {
|
|
||||||
return b.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BranchNode) Copy() Node {
|
|
||||||
switch b.NodeType {
|
|
||||||
case NodeIf:
|
|
||||||
return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
|
|
||||||
case NodeRange:
|
|
||||||
return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
|
|
||||||
case NodeWith:
|
|
||||||
return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
|
|
||||||
default:
|
|
||||||
panic("unknown branch type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IfNode represents an {{if}} action and its commands.
|
|
||||||
type IfNode struct {
|
|
||||||
BranchNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
|
|
||||||
return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *IfNode) Copy() Node {
|
|
||||||
return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
|
|
||||||
}
|
|
||||||
|
|
||||||
// RangeNode represents a {{range}} action and its commands.
|
|
||||||
type RangeNode struct {
|
|
||||||
BranchNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
|
|
||||||
return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RangeNode) Copy() Node {
|
|
||||||
return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithNode represents a {{with}} action and its commands.
|
|
||||||
type WithNode struct {
|
|
||||||
BranchNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
|
|
||||||
return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WithNode) Copy() Node {
|
|
||||||
return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TemplateNode represents a {{template}} action.
|
|
||||||
type TemplateNode struct {
|
|
||||||
NodeType
|
|
||||||
Pos
|
|
||||||
tr *Tree
|
|
||||||
Line int // The line number in the input (deprecated; kept for compatibility)
|
|
||||||
Name string // The name of the template (unquoted).
|
|
||||||
Pipe *PipeNode // The command to evaluate as dot for the template.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
|
|
||||||
return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TemplateNode) String() string {
|
|
||||||
if t.Pipe == nil {
|
|
||||||
return fmt.Sprintf("{{template %q}}", t.Name)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TemplateNode) tree() *Tree {
|
|
||||||
return t.tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TemplateNode) Copy() Node {
|
|
||||||
return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
|
|
||||||
}
|
|
700
vendor/github.com/alecthomas/template/parse/parse.go
generated
vendored
700
vendor/github.com/alecthomas/template/parse/parse.go
generated
vendored
|
@ -1,700 +0,0 @@
|
||||||
// Copyright 2011 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 parse builds parse trees for templates as defined by text/template
|
|
||||||
// and html/template. Clients should use those packages to construct templates
|
|
||||||
// rather than this one, which provides shared internal data structures not
|
|
||||||
// intended for general use.
|
|
||||||
package parse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tree is the representation of a single parsed template.
|
|
||||||
type Tree struct {
|
|
||||||
Name string // name of the template represented by the tree.
|
|
||||||
ParseName string // name of the top-level template during parsing, for error messages.
|
|
||||||
Root *ListNode // top-level root of the tree.
|
|
||||||
text string // text parsed to create the template (or its parent)
|
|
||||||
// Parsing only; cleared after parse.
|
|
||||||
funcs []map[string]interface{}
|
|
||||||
lex *lexer
|
|
||||||
token [3]item // three-token lookahead for parser.
|
|
||||||
peekCount int
|
|
||||||
vars []string // variables defined at the moment.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a copy of the Tree. Any parsing state is discarded.
|
|
||||||
func (t *Tree) Copy() *Tree {
|
|
||||||
if t == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Tree{
|
|
||||||
Name: t.Name,
|
|
||||||
ParseName: t.ParseName,
|
|
||||||
Root: t.Root.CopyList(),
|
|
||||||
text: t.text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse returns a map from template name to parse.Tree, created by parsing the
|
|
||||||
// templates described in the argument string. The top-level template will be
|
|
||||||
// given the specified name. If an error is encountered, parsing stops and an
|
|
||||||
// empty map is returned with the error.
|
|
||||||
func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
|
|
||||||
treeSet = make(map[string]*Tree)
|
|
||||||
t := New(name)
|
|
||||||
t.text = text
|
|
||||||
_, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// next returns the next token.
|
|
||||||
func (t *Tree) next() item {
|
|
||||||
if t.peekCount > 0 {
|
|
||||||
t.peekCount--
|
|
||||||
} else {
|
|
||||||
t.token[0] = t.lex.nextItem()
|
|
||||||
}
|
|
||||||
return t.token[t.peekCount]
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup backs the input stream up one token.
|
|
||||||
func (t *Tree) backup() {
|
|
||||||
t.peekCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup2 backs the input stream up two tokens.
|
|
||||||
// The zeroth token is already there.
|
|
||||||
func (t *Tree) backup2(t1 item) {
|
|
||||||
t.token[1] = t1
|
|
||||||
t.peekCount = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup3 backs the input stream up three tokens
|
|
||||||
// The zeroth token is already there.
|
|
||||||
func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
|
|
||||||
t.token[1] = t1
|
|
||||||
t.token[2] = t2
|
|
||||||
t.peekCount = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek returns but does not consume the next token.
|
|
||||||
func (t *Tree) peek() item {
|
|
||||||
if t.peekCount > 0 {
|
|
||||||
return t.token[t.peekCount-1]
|
|
||||||
}
|
|
||||||
t.peekCount = 1
|
|
||||||
t.token[0] = t.lex.nextItem()
|
|
||||||
return t.token[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextNonSpace returns the next non-space token.
|
|
||||||
func (t *Tree) nextNonSpace() (token item) {
|
|
||||||
for {
|
|
||||||
token = t.next()
|
|
||||||
if token.typ != itemSpace {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
// peekNonSpace returns but does not consume the next non-space token.
|
|
||||||
func (t *Tree) peekNonSpace() (token item) {
|
|
||||||
for {
|
|
||||||
token = t.next()
|
|
||||||
if token.typ != itemSpace {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.backup()
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parsing.
|
|
||||||
|
|
||||||
// New allocates a new parse tree with the given name.
|
|
||||||
func New(name string, funcs ...map[string]interface{}) *Tree {
|
|
||||||
return &Tree{
|
|
||||||
Name: name,
|
|
||||||
funcs: funcs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorContext returns a textual representation of the location of the node in the input text.
|
|
||||||
// The receiver is only used when the node does not have a pointer to the tree inside,
|
|
||||||
// which can occur in old code.
|
|
||||||
func (t *Tree) ErrorContext(n Node) (location, context string) {
|
|
||||||
pos := int(n.Position())
|
|
||||||
tree := n.tree()
|
|
||||||
if tree == nil {
|
|
||||||
tree = t
|
|
||||||
}
|
|
||||||
text := tree.text[:pos]
|
|
||||||
byteNum := strings.LastIndex(text, "\n")
|
|
||||||
if byteNum == -1 {
|
|
||||||
byteNum = pos // On first line.
|
|
||||||
} else {
|
|
||||||
byteNum++ // After the newline.
|
|
||||||
byteNum = pos - byteNum
|
|
||||||
}
|
|
||||||
lineNum := 1 + strings.Count(text, "\n")
|
|
||||||
context = n.String()
|
|
||||||
if len(context) > 20 {
|
|
||||||
context = fmt.Sprintf("%.20s...", context)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf formats the error and terminates processing.
|
|
||||||
func (t *Tree) errorf(format string, args ...interface{}) {
|
|
||||||
t.Root = nil
|
|
||||||
format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
|
|
||||||
panic(fmt.Errorf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// error terminates processing.
|
|
||||||
func (t *Tree) error(err error) {
|
|
||||||
t.errorf("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// expect consumes the next token and guarantees it has the required type.
|
|
||||||
func (t *Tree) expect(expected itemType, context string) item {
|
|
||||||
token := t.nextNonSpace()
|
|
||||||
if token.typ != expected {
|
|
||||||
t.unexpected(token, context)
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
// expectOneOf consumes the next token and guarantees it has one of the required types.
|
|
||||||
func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
|
|
||||||
token := t.nextNonSpace()
|
|
||||||
if token.typ != expected1 && token.typ != expected2 {
|
|
||||||
t.unexpected(token, context)
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
// unexpected complains about the token and terminates processing.
|
|
||||||
func (t *Tree) unexpected(token item, context string) {
|
|
||||||
t.errorf("unexpected %s in %s", token, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// recover is the handler that turns panics into returns from the top level of Parse.
|
|
||||||
func (t *Tree) recover(errp *error) {
|
|
||||||
e := recover()
|
|
||||||
if e != nil {
|
|
||||||
if _, ok := e.(runtime.Error); ok {
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
if t != nil {
|
|
||||||
t.stopParse()
|
|
||||||
}
|
|
||||||
*errp = e.(error)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// startParse initializes the parser, using the lexer.
|
|
||||||
func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) {
|
|
||||||
t.Root = nil
|
|
||||||
t.lex = lex
|
|
||||||
t.vars = []string{"$"}
|
|
||||||
t.funcs = funcs
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopParse terminates parsing.
|
|
||||||
func (t *Tree) stopParse() {
|
|
||||||
t.lex = nil
|
|
||||||
t.vars = nil
|
|
||||||
t.funcs = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses the template definition string to construct a representation of
|
|
||||||
// the template for execution. If either action delimiter string is empty, the
|
|
||||||
// default ("{{" or "}}") is used. Embedded template definitions are added to
|
|
||||||
// the treeSet map.
|
|
||||||
func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
|
|
||||||
defer t.recover(&err)
|
|
||||||
t.ParseName = t.Name
|
|
||||||
t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim))
|
|
||||||
t.text = text
|
|
||||||
t.parse(treeSet)
|
|
||||||
t.add(treeSet)
|
|
||||||
t.stopParse()
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// add adds tree to the treeSet.
|
|
||||||
func (t *Tree) add(treeSet map[string]*Tree) {
|
|
||||||
tree := treeSet[t.Name]
|
|
||||||
if tree == nil || IsEmptyTree(tree.Root) {
|
|
||||||
treeSet[t.Name] = t
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !IsEmptyTree(t.Root) {
|
|
||||||
t.errorf("template: multiple definition of template %q", t.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmptyTree reports whether this tree (node) is empty of everything but space.
|
|
||||||
func IsEmptyTree(n Node) bool {
|
|
||||||
switch n := n.(type) {
|
|
||||||
case nil:
|
|
||||||
return true
|
|
||||||
case *ActionNode:
|
|
||||||
case *IfNode:
|
|
||||||
case *ListNode:
|
|
||||||
for _, node := range n.Nodes {
|
|
||||||
if !IsEmptyTree(node) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case *RangeNode:
|
|
||||||
case *TemplateNode:
|
|
||||||
case *TextNode:
|
|
||||||
return len(bytes.TrimSpace(n.Text)) == 0
|
|
||||||
case *WithNode:
|
|
||||||
default:
|
|
||||||
panic("unknown node: " + n.String())
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse is the top-level parser for a template, essentially the same
|
|
||||||
// as itemList except it also parses {{define}} actions.
|
|
||||||
// It runs to EOF.
|
|
||||||
func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
|
|
||||||
t.Root = t.newList(t.peek().pos)
|
|
||||||
for t.peek().typ != itemEOF {
|
|
||||||
if t.peek().typ == itemLeftDelim {
|
|
||||||
delim := t.next()
|
|
||||||
if t.nextNonSpace().typ == itemDefine {
|
|
||||||
newT := New("definition") // name will be updated once we know it.
|
|
||||||
newT.text = t.text
|
|
||||||
newT.ParseName = t.ParseName
|
|
||||||
newT.startParse(t.funcs, t.lex)
|
|
||||||
newT.parseDefinition(treeSet)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.backup2(delim)
|
|
||||||
}
|
|
||||||
n := t.textOrAction()
|
|
||||||
if n.Type() == nodeEnd {
|
|
||||||
t.errorf("unexpected %s", n)
|
|
||||||
}
|
|
||||||
t.Root.append(n)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseDefinition parses a {{define}} ... {{end}} template definition and
|
|
||||||
// installs the definition in the treeSet map. The "define" keyword has already
|
|
||||||
// been scanned.
|
|
||||||
func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
|
|
||||||
const context = "define clause"
|
|
||||||
name := t.expectOneOf(itemString, itemRawString, context)
|
|
||||||
var err error
|
|
||||||
t.Name, err = strconv.Unquote(name.val)
|
|
||||||
if err != nil {
|
|
||||||
t.error(err)
|
|
||||||
}
|
|
||||||
t.expect(itemRightDelim, context)
|
|
||||||
var end Node
|
|
||||||
t.Root, end = t.itemList()
|
|
||||||
if end.Type() != nodeEnd {
|
|
||||||
t.errorf("unexpected %s in %s", end, context)
|
|
||||||
}
|
|
||||||
t.add(treeSet)
|
|
||||||
t.stopParse()
|
|
||||||
}
|
|
||||||
|
|
||||||
// itemList:
|
|
||||||
// textOrAction*
|
|
||||||
// Terminates at {{end}} or {{else}}, returned separately.
|
|
||||||
func (t *Tree) itemList() (list *ListNode, next Node) {
|
|
||||||
list = t.newList(t.peekNonSpace().pos)
|
|
||||||
for t.peekNonSpace().typ != itemEOF {
|
|
||||||
n := t.textOrAction()
|
|
||||||
switch n.Type() {
|
|
||||||
case nodeEnd, nodeElse:
|
|
||||||
return list, n
|
|
||||||
}
|
|
||||||
list.append(n)
|
|
||||||
}
|
|
||||||
t.errorf("unexpected EOF")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// textOrAction:
|
|
||||||
// text | action
|
|
||||||
func (t *Tree) textOrAction() Node {
|
|
||||||
switch token := t.nextNonSpace(); token.typ {
|
|
||||||
case itemElideNewline:
|
|
||||||
return t.elideNewline()
|
|
||||||
case itemText:
|
|
||||||
return t.newText(token.pos, token.val)
|
|
||||||
case itemLeftDelim:
|
|
||||||
return t.action()
|
|
||||||
default:
|
|
||||||
t.unexpected(token, "input")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// elideNewline:
|
|
||||||
// Remove newlines trailing rightDelim if \\ is present.
|
|
||||||
func (t *Tree) elideNewline() Node {
|
|
||||||
token := t.peek()
|
|
||||||
if token.typ != itemText {
|
|
||||||
t.unexpected(token, "input")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
t.next()
|
|
||||||
stripped := strings.TrimLeft(token.val, "\n\r")
|
|
||||||
diff := len(token.val) - len(stripped)
|
|
||||||
if diff > 0 {
|
|
||||||
// This is a bit nasty. We mutate the token in-place to remove
|
|
||||||
// preceding newlines.
|
|
||||||
token.pos += Pos(diff)
|
|
||||||
token.val = stripped
|
|
||||||
}
|
|
||||||
return t.newText(token.pos, token.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action:
|
|
||||||
// control
|
|
||||||
// command ("|" command)*
|
|
||||||
// Left delim is past. Now get actions.
|
|
||||||
// First word could be a keyword such as range.
|
|
||||||
func (t *Tree) action() (n Node) {
|
|
||||||
switch token := t.nextNonSpace(); token.typ {
|
|
||||||
case itemElse:
|
|
||||||
return t.elseControl()
|
|
||||||
case itemEnd:
|
|
||||||
return t.endControl()
|
|
||||||
case itemIf:
|
|
||||||
return t.ifControl()
|
|
||||||
case itemRange:
|
|
||||||
return t.rangeControl()
|
|
||||||
case itemTemplate:
|
|
||||||
return t.templateControl()
|
|
||||||
case itemWith:
|
|
||||||
return t.withControl()
|
|
||||||
}
|
|
||||||
t.backup()
|
|
||||||
// Do not pop variables; they persist until "end".
|
|
||||||
return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pipeline:
|
|
||||||
// declarations? command ('|' command)*
|
|
||||||
func (t *Tree) pipeline(context string) (pipe *PipeNode) {
|
|
||||||
var decl []*VariableNode
|
|
||||||
pos := t.peekNonSpace().pos
|
|
||||||
// Are there declarations?
|
|
||||||
for {
|
|
||||||
if v := t.peekNonSpace(); v.typ == itemVariable {
|
|
||||||
t.next()
|
|
||||||
// Since space is a token, we need 3-token look-ahead here in the worst case:
|
|
||||||
// in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
|
|
||||||
// argument variable rather than a declaration. So remember the token
|
|
||||||
// adjacent to the variable so we can push it back if necessary.
|
|
||||||
tokenAfterVariable := t.peek()
|
|
||||||
if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
|
|
||||||
t.nextNonSpace()
|
|
||||||
variable := t.newVariable(v.pos, v.val)
|
|
||||||
decl = append(decl, variable)
|
|
||||||
t.vars = append(t.vars, v.val)
|
|
||||||
if next.typ == itemChar && next.val == "," {
|
|
||||||
if context == "range" && len(decl) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.errorf("too many declarations in %s", context)
|
|
||||||
}
|
|
||||||
} else if tokenAfterVariable.typ == itemSpace {
|
|
||||||
t.backup3(v, tokenAfterVariable)
|
|
||||||
} else {
|
|
||||||
t.backup2(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
pipe = t.newPipeline(pos, t.lex.lineNumber(), decl)
|
|
||||||
for {
|
|
||||||
switch token := t.nextNonSpace(); token.typ {
|
|
||||||
case itemRightDelim, itemRightParen:
|
|
||||||
if len(pipe.Cmds) == 0 {
|
|
||||||
t.errorf("missing value for %s", context)
|
|
||||||
}
|
|
||||||
if token.typ == itemRightParen {
|
|
||||||
t.backup()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
|
|
||||||
itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
|
|
||||||
t.backup()
|
|
||||||
pipe.append(t.command())
|
|
||||||
default:
|
|
||||||
t.unexpected(token, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
|
|
||||||
defer t.popVars(len(t.vars))
|
|
||||||
line = t.lex.lineNumber()
|
|
||||||
pipe = t.pipeline(context)
|
|
||||||
var next Node
|
|
||||||
list, next = t.itemList()
|
|
||||||
switch next.Type() {
|
|
||||||
case nodeEnd: //done
|
|
||||||
case nodeElse:
|
|
||||||
if allowElseIf {
|
|
||||||
// Special case for "else if". If the "else" is followed immediately by an "if",
|
|
||||||
// the elseControl will have left the "if" token pending. Treat
|
|
||||||
// {{if a}}_{{else if b}}_{{end}}
|
|
||||||
// as
|
|
||||||
// {{if a}}_{{else}}{{if b}}_{{end}}{{end}}.
|
|
||||||
// To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}}
|
|
||||||
// is assumed. This technique works even for long if-else-if chains.
|
|
||||||
// TODO: Should we allow else-if in with and range?
|
|
||||||
if t.peek().typ == itemIf {
|
|
||||||
t.next() // Consume the "if" token.
|
|
||||||
elseList = t.newList(next.Position())
|
|
||||||
elseList.append(t.ifControl())
|
|
||||||
// Do not consume the next item - only one {{end}} required.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseList, next = t.itemList()
|
|
||||||
if next.Type() != nodeEnd {
|
|
||||||
t.errorf("expected end; found %s", next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pipe.Position(), line, pipe, list, elseList
|
|
||||||
}
|
|
||||||
|
|
||||||
// If:
|
|
||||||
// {{if pipeline}} itemList {{end}}
|
|
||||||
// {{if pipeline}} itemList {{else}} itemList {{end}}
|
|
||||||
// If keyword is past.
|
|
||||||
func (t *Tree) ifControl() Node {
|
|
||||||
return t.newIf(t.parseControl(true, "if"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range:
|
|
||||||
// {{range pipeline}} itemList {{end}}
|
|
||||||
// {{range pipeline}} itemList {{else}} itemList {{end}}
|
|
||||||
// Range keyword is past.
|
|
||||||
func (t *Tree) rangeControl() Node {
|
|
||||||
return t.newRange(t.parseControl(false, "range"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// With:
|
|
||||||
// {{with pipeline}} itemList {{end}}
|
|
||||||
// {{with pipeline}} itemList {{else}} itemList {{end}}
|
|
||||||
// If keyword is past.
|
|
||||||
func (t *Tree) withControl() Node {
|
|
||||||
return t.newWith(t.parseControl(false, "with"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// End:
|
|
||||||
// {{end}}
|
|
||||||
// End keyword is past.
|
|
||||||
func (t *Tree) endControl() Node {
|
|
||||||
return t.newEnd(t.expect(itemRightDelim, "end").pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Else:
|
|
||||||
// {{else}}
|
|
||||||
// Else keyword is past.
|
|
||||||
func (t *Tree) elseControl() Node {
|
|
||||||
// Special case for "else if".
|
|
||||||
peek := t.peekNonSpace()
|
|
||||||
if peek.typ == itemIf {
|
|
||||||
// We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
|
|
||||||
return t.newElse(peek.pos, t.lex.lineNumber())
|
|
||||||
}
|
|
||||||
return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Template:
|
|
||||||
// {{template stringValue pipeline}}
|
|
||||||
// Template keyword is past. The name must be something that can evaluate
|
|
||||||
// to a string.
|
|
||||||
func (t *Tree) templateControl() Node {
|
|
||||||
var name string
|
|
||||||
token := t.nextNonSpace()
|
|
||||||
switch token.typ {
|
|
||||||
case itemString, itemRawString:
|
|
||||||
s, err := strconv.Unquote(token.val)
|
|
||||||
if err != nil {
|
|
||||||
t.error(err)
|
|
||||||
}
|
|
||||||
name = s
|
|
||||||
default:
|
|
||||||
t.unexpected(token, "template invocation")
|
|
||||||
}
|
|
||||||
var pipe *PipeNode
|
|
||||||
if t.nextNonSpace().typ != itemRightDelim {
|
|
||||||
t.backup()
|
|
||||||
// Do not pop variables; they persist until "end".
|
|
||||||
pipe = t.pipeline("template")
|
|
||||||
}
|
|
||||||
return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
// command:
|
|
||||||
// operand (space operand)*
|
|
||||||
// space-separated arguments up to a pipeline character or right delimiter.
|
|
||||||
// we consume the pipe character but leave the right delim to terminate the action.
|
|
||||||
func (t *Tree) command() *CommandNode {
|
|
||||||
cmd := t.newCommand(t.peekNonSpace().pos)
|
|
||||||
for {
|
|
||||||
t.peekNonSpace() // skip leading spaces.
|
|
||||||
operand := t.operand()
|
|
||||||
if operand != nil {
|
|
||||||
cmd.append(operand)
|
|
||||||
}
|
|
||||||
switch token := t.next(); token.typ {
|
|
||||||
case itemSpace:
|
|
||||||
continue
|
|
||||||
case itemError:
|
|
||||||
t.errorf("%s", token.val)
|
|
||||||
case itemRightDelim, itemRightParen:
|
|
||||||
t.backup()
|
|
||||||
case itemPipe:
|
|
||||||
default:
|
|
||||||
t.errorf("unexpected %s in operand; missing space?", token)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if len(cmd.Args) == 0 {
|
|
||||||
t.errorf("empty command")
|
|
||||||
}
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// operand:
|
|
||||||
// term .Field*
|
|
||||||
// An operand is a space-separated component of a command,
|
|
||||||
// a term possibly followed by field accesses.
|
|
||||||
// A nil return means the next item is not an operand.
|
|
||||||
func (t *Tree) operand() Node {
|
|
||||||
node := t.term()
|
|
||||||
if node == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if t.peek().typ == itemField {
|
|
||||||
chain := t.newChain(t.peek().pos, node)
|
|
||||||
for t.peek().typ == itemField {
|
|
||||||
chain.Add(t.next().val)
|
|
||||||
}
|
|
||||||
// Compatibility with original API: If the term is of type NodeField
|
|
||||||
// or NodeVariable, just put more fields on the original.
|
|
||||||
// Otherwise, keep the Chain node.
|
|
||||||
// TODO: Switch to Chains always when we can.
|
|
||||||
switch node.Type() {
|
|
||||||
case NodeField:
|
|
||||||
node = t.newField(chain.Position(), chain.String())
|
|
||||||
case NodeVariable:
|
|
||||||
node = t.newVariable(chain.Position(), chain.String())
|
|
||||||
default:
|
|
||||||
node = chain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
// term:
|
|
||||||
// literal (number, string, nil, boolean)
|
|
||||||
// function (identifier)
|
|
||||||
// .
|
|
||||||
// .Field
|
|
||||||
// $
|
|
||||||
// '(' pipeline ')'
|
|
||||||
// A term is a simple "expression".
|
|
||||||
// A nil return means the next item is not a term.
|
|
||||||
func (t *Tree) term() Node {
|
|
||||||
switch token := t.nextNonSpace(); token.typ {
|
|
||||||
case itemError:
|
|
||||||
t.errorf("%s", token.val)
|
|
||||||
case itemIdentifier:
|
|
||||||
if !t.hasFunction(token.val) {
|
|
||||||
t.errorf("function %q not defined", token.val)
|
|
||||||
}
|
|
||||||
return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
|
|
||||||
case itemDot:
|
|
||||||
return t.newDot(token.pos)
|
|
||||||
case itemNil:
|
|
||||||
return t.newNil(token.pos)
|
|
||||||
case itemVariable:
|
|
||||||
return t.useVar(token.pos, token.val)
|
|
||||||
case itemField:
|
|
||||||
return t.newField(token.pos, token.val)
|
|
||||||
case itemBool:
|
|
||||||
return t.newBool(token.pos, token.val == "true")
|
|
||||||
case itemCharConstant, itemComplex, itemNumber:
|
|
||||||
number, err := t.newNumber(token.pos, token.val, token.typ)
|
|
||||||
if err != nil {
|
|
||||||
t.error(err)
|
|
||||||
}
|
|
||||||
return number
|
|
||||||
case itemLeftParen:
|
|
||||||
pipe := t.pipeline("parenthesized pipeline")
|
|
||||||
if token := t.next(); token.typ != itemRightParen {
|
|
||||||
t.errorf("unclosed right paren: unexpected %s", token)
|
|
||||||
}
|
|
||||||
return pipe
|
|
||||||
case itemString, itemRawString:
|
|
||||||
s, err := strconv.Unquote(token.val)
|
|
||||||
if err != nil {
|
|
||||||
t.error(err)
|
|
||||||
}
|
|
||||||
return t.newString(token.pos, token.val, s)
|
|
||||||
}
|
|
||||||
t.backup()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasFunction reports if a function name exists in the Tree's maps.
|
|
||||||
func (t *Tree) hasFunction(name string) bool {
|
|
||||||
for _, funcMap := range t.funcs {
|
|
||||||
if funcMap == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if funcMap[name] != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// popVars trims the variable list to the specified length
|
|
||||||
func (t *Tree) popVars(n int) {
|
|
||||||
t.vars = t.vars[:n]
|
|
||||||
}
|
|
||||||
|
|
||||||
// useVar returns a node for a variable reference. It errors if the
|
|
||||||
// variable is not defined.
|
|
||||||
func (t *Tree) useVar(pos Pos, name string) Node {
|
|
||||||
v := t.newVariable(pos, name)
|
|
||||||
for _, varName := range t.vars {
|
|
||||||
if varName == v.Ident[0] {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.errorf("undefined variable %q", v.Ident[0])
|
|
||||||
return nil
|
|
||||||
}
|
|
218
vendor/github.com/alecthomas/template/template.go
generated
vendored
218
vendor/github.com/alecthomas/template/template.go
generated
vendored
|
@ -1,218 +0,0 @@
|
||||||
// Copyright 2011 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 template
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/alecthomas/template/parse"
|
|
||||||
)
|
|
||||||
|
|
||||||
// common holds the information shared by related templates.
|
|
||||||
type common struct {
|
|
||||||
tmpl map[string]*Template
|
|
||||||
// We use two maps, one for parsing and one for execution.
|
|
||||||
// This separation makes the API cleaner since it doesn't
|
|
||||||
// expose reflection to the client.
|
|
||||||
parseFuncs FuncMap
|
|
||||||
execFuncs map[string]reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Template is the representation of a parsed template. The *parse.Tree
|
|
||||||
// field is exported only for use by html/template and should be treated
|
|
||||||
// as unexported by all other clients.
|
|
||||||
type Template struct {
|
|
||||||
name string
|
|
||||||
*parse.Tree
|
|
||||||
*common
|
|
||||||
leftDelim string
|
|
||||||
rightDelim string
|
|
||||||
}
|
|
||||||
|
|
||||||
// New allocates a new template with the given name.
|
|
||||||
func New(name string) *Template {
|
|
||||||
return &Template{
|
|
||||||
name: name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the template.
|
|
||||||
func (t *Template) Name() string {
|
|
||||||
return t.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// New allocates a new template associated with the given one and with the same
|
|
||||||
// delimiters. The association, which is transitive, allows one template to
|
|
||||||
// invoke another with a {{template}} action.
|
|
||||||
func (t *Template) New(name string) *Template {
|
|
||||||
t.init()
|
|
||||||
return &Template{
|
|
||||||
name: name,
|
|
||||||
common: t.common,
|
|
||||||
leftDelim: t.leftDelim,
|
|
||||||
rightDelim: t.rightDelim,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Template) init() {
|
|
||||||
if t.common == nil {
|
|
||||||
t.common = new(common)
|
|
||||||
t.tmpl = make(map[string]*Template)
|
|
||||||
t.parseFuncs = make(FuncMap)
|
|
||||||
t.execFuncs = make(map[string]reflect.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a duplicate of the template, including all associated
|
|
||||||
// templates. The actual representation is not copied, but the name space of
|
|
||||||
// associated templates is, so further calls to Parse in the copy will add
|
|
||||||
// templates to the copy but not to the original. Clone can be used to prepare
|
|
||||||
// common templates and use them with variant definitions for other templates
|
|
||||||
// by adding the variants after the clone is made.
|
|
||||||
func (t *Template) Clone() (*Template, error) {
|
|
||||||
nt := t.copy(nil)
|
|
||||||
nt.init()
|
|
||||||
nt.tmpl[t.name] = nt
|
|
||||||
for k, v := range t.tmpl {
|
|
||||||
if k == t.name { // Already installed.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// The associated templates share nt's common structure.
|
|
||||||
tmpl := v.copy(nt.common)
|
|
||||||
nt.tmpl[k] = tmpl
|
|
||||||
}
|
|
||||||
for k, v := range t.parseFuncs {
|
|
||||||
nt.parseFuncs[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range t.execFuncs {
|
|
||||||
nt.execFuncs[k] = v
|
|
||||||
}
|
|
||||||
return nt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy returns a shallow copy of t, with common set to the argument.
|
|
||||||
func (t *Template) copy(c *common) *Template {
|
|
||||||
nt := New(t.name)
|
|
||||||
nt.Tree = t.Tree
|
|
||||||
nt.common = c
|
|
||||||
nt.leftDelim = t.leftDelim
|
|
||||||
nt.rightDelim = t.rightDelim
|
|
||||||
return nt
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddParseTree creates a new template with the name and parse tree
|
|
||||||
// and associates it with t.
|
|
||||||
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
|
|
||||||
if t.common != nil && t.tmpl[name] != nil {
|
|
||||||
return nil, fmt.Errorf("template: redefinition of template %q", name)
|
|
||||||
}
|
|
||||||
nt := t.New(name)
|
|
||||||
nt.Tree = tree
|
|
||||||
t.tmpl[name] = nt
|
|
||||||
return nt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Templates returns a slice of the templates associated with t, including t
|
|
||||||
// itself.
|
|
||||||
func (t *Template) Templates() []*Template {
|
|
||||||
if t.common == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Return a slice so we don't expose the map.
|
|
||||||
m := make([]*Template, 0, len(t.tmpl))
|
|
||||||
for _, v := range t.tmpl {
|
|
||||||
m = append(m, v)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delims sets the action delimiters to the specified strings, to be used in
|
|
||||||
// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
|
|
||||||
// definitions will inherit the settings. An empty delimiter stands for the
|
|
||||||
// corresponding default: {{ or }}.
|
|
||||||
// The return value is the template, so calls can be chained.
|
|
||||||
func (t *Template) Delims(left, right string) *Template {
|
|
||||||
t.leftDelim = left
|
|
||||||
t.rightDelim = right
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Funcs adds the elements of the argument map to the template's function map.
|
|
||||||
// It panics if a value in the map is not a function with appropriate return
|
|
||||||
// type. However, it is legal to overwrite elements of the map. The return
|
|
||||||
// value is the template, so calls can be chained.
|
|
||||||
func (t *Template) Funcs(funcMap FuncMap) *Template {
|
|
||||||
t.init()
|
|
||||||
addValueFuncs(t.execFuncs, funcMap)
|
|
||||||
addFuncs(t.parseFuncs, funcMap)
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup returns the template with the given name that is associated with t,
|
|
||||||
// or nil if there is no such template.
|
|
||||||
func (t *Template) Lookup(name string) *Template {
|
|
||||||
if t.common == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return t.tmpl[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses a string into a template. Nested template definitions will be
|
|
||||||
// associated with the top-level template t. Parse may be called multiple times
|
|
||||||
// to parse definitions of templates to associate with t. It is an error if a
|
|
||||||
// resulting template is non-empty (contains content other than template
|
|
||||||
// definitions) and would replace a non-empty template with the same name.
|
|
||||||
// (In multiple calls to Parse with the same receiver template, only one call
|
|
||||||
// can contain text other than space, comments, and template definitions.)
|
|
||||||
func (t *Template) Parse(text string) (*Template, error) {
|
|
||||||
t.init()
|
|
||||||
trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Add the newly parsed trees, including the one for t, into our common structure.
|
|
||||||
for name, tree := range trees {
|
|
||||||
// If the name we parsed is the name of this template, overwrite this template.
|
|
||||||
// The associate method checks it's not a redefinition.
|
|
||||||
tmpl := t
|
|
||||||
if name != t.name {
|
|
||||||
tmpl = t.New(name)
|
|
||||||
}
|
|
||||||
// Even if t == tmpl, we need to install it in the common.tmpl map.
|
|
||||||
if replace, err := t.associate(tmpl, tree); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if replace {
|
|
||||||
tmpl.Tree = tree
|
|
||||||
}
|
|
||||||
tmpl.leftDelim = t.leftDelim
|
|
||||||
tmpl.rightDelim = t.rightDelim
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// associate installs the new template into the group of templates associated
|
|
||||||
// with t. It is an error to reuse a name except to overwrite an empty
|
|
||||||
// template. The two are already known to share the common structure.
|
|
||||||
// The boolean return value reports wither to store this tree as t.Tree.
|
|
||||||
func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) {
|
|
||||||
if new.common != t.common {
|
|
||||||
panic("internal error: associate not common")
|
|
||||||
}
|
|
||||||
name := new.name
|
|
||||||
if old := t.tmpl[name]; old != nil {
|
|
||||||
oldIsEmpty := parse.IsEmptyTree(old.Root)
|
|
||||||
newIsEmpty := parse.IsEmptyTree(tree.Root)
|
|
||||||
if newIsEmpty {
|
|
||||||
// Whether old is empty or not, new is empty; no reason to replace old.
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if !oldIsEmpty {
|
|
||||||
return false, fmt.Errorf("template: redefinition of template %q", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.tmpl[name] = new
|
|
||||||
return true, nil
|
|
||||||
}
|
|
19
vendor/github.com/alecthomas/units/COPYING
generated
vendored
19
vendor/github.com/alecthomas/units/COPYING
generated
vendored
|
@ -1,19 +0,0 @@
|
||||||
Copyright (C) 2014 Alec Thomas
|
|
||||||
|
|
||||||
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.
|
|
83
vendor/github.com/alecthomas/units/bytes.go
generated
vendored
83
vendor/github.com/alecthomas/units/bytes.go
generated
vendored
|
@ -1,83 +0,0 @@
|
||||||
package units
|
|
||||||
|
|
||||||
// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte,
|
|
||||||
// etc.).
|
|
||||||
type Base2Bytes int64
|
|
||||||
|
|
||||||
// Base-2 byte units.
|
|
||||||
const (
|
|
||||||
Kibibyte Base2Bytes = 1024
|
|
||||||
KiB = Kibibyte
|
|
||||||
Mebibyte = Kibibyte * 1024
|
|
||||||
MiB = Mebibyte
|
|
||||||
Gibibyte = Mebibyte * 1024
|
|
||||||
GiB = Gibibyte
|
|
||||||
Tebibyte = Gibibyte * 1024
|
|
||||||
TiB = Tebibyte
|
|
||||||
Pebibyte = Tebibyte * 1024
|
|
||||||
PiB = Pebibyte
|
|
||||||
Exbibyte = Pebibyte * 1024
|
|
||||||
EiB = Exbibyte
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bytesUnitMap = MakeUnitMap("iB", "B", 1024)
|
|
||||||
oldBytesUnitMap = MakeUnitMap("B", "B", 1024)
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB
|
|
||||||
// and KiB are both 1024.
|
|
||||||
func ParseBase2Bytes(s string) (Base2Bytes, error) {
|
|
||||||
n, err := ParseUnit(s, bytesUnitMap)
|
|
||||||
if err != nil {
|
|
||||||
n, err = ParseUnit(s, oldBytesUnitMap)
|
|
||||||
}
|
|
||||||
return Base2Bytes(n), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Base2Bytes) String() string {
|
|
||||||
return ToString(int64(b), 1024, "iB", "B")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
metricBytesUnitMap = MakeUnitMap("B", "B", 1000)
|
|
||||||
)
|
|
||||||
|
|
||||||
// MetricBytes are SI byte units (1000 bytes in a kilobyte).
|
|
||||||
type MetricBytes SI
|
|
||||||
|
|
||||||
// SI base-10 byte units.
|
|
||||||
const (
|
|
||||||
Kilobyte MetricBytes = 1000
|
|
||||||
KB = Kilobyte
|
|
||||||
Megabyte = Kilobyte * 1000
|
|
||||||
MB = Megabyte
|
|
||||||
Gigabyte = Megabyte * 1000
|
|
||||||
GB = Gigabyte
|
|
||||||
Terabyte = Gigabyte * 1000
|
|
||||||
TB = Terabyte
|
|
||||||
Petabyte = Terabyte * 1000
|
|
||||||
PB = Petabyte
|
|
||||||
Exabyte = Petabyte * 1000
|
|
||||||
EB = Exabyte
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes.
|
|
||||||
func ParseMetricBytes(s string) (MetricBytes, error) {
|
|
||||||
n, err := ParseUnit(s, metricBytesUnitMap)
|
|
||||||
return MetricBytes(n), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m MetricBytes) String() string {
|
|
||||||
return ToString(int64(m), 1000, "B", "B")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseStrictBytes supports both iB and B suffixes for base 2 and metric,
|
|
||||||
// respectively. That is, KiB represents 1024 and KB represents 1000.
|
|
||||||
func ParseStrictBytes(s string) (int64, error) {
|
|
||||||
n, err := ParseUnit(s, bytesUnitMap)
|
|
||||||
if err != nil {
|
|
||||||
n, err = ParseUnit(s, metricBytesUnitMap)
|
|
||||||
}
|
|
||||||
return int64(n), err
|
|
||||||
}
|
|
13
vendor/github.com/alecthomas/units/doc.go
generated
vendored
13
vendor/github.com/alecthomas/units/doc.go
generated
vendored
|
@ -1,13 +0,0 @@
|
||||||
// Package units provides helpful unit multipliers and functions for Go.
|
|
||||||
//
|
|
||||||
// The goal of this package is to have functionality similar to the time [1] package.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// [1] http://golang.org/pkg/time/
|
|
||||||
//
|
|
||||||
// It allows for code like this:
|
|
||||||
//
|
|
||||||
// n, err := ParseBase2Bytes("1KB")
|
|
||||||
// // n == 1024
|
|
||||||
// n = units.Mebibyte * 512
|
|
||||||
package units
|
|
26
vendor/github.com/alecthomas/units/si.go
generated
vendored
26
vendor/github.com/alecthomas/units/si.go
generated
vendored
|
@ -1,26 +0,0 @@
|
||||||
package units
|
|
||||||
|
|
||||||
// SI units.
|
|
||||||
type SI int64
|
|
||||||
|
|
||||||
// SI unit multiples.
|
|
||||||
const (
|
|
||||||
Kilo SI = 1000
|
|
||||||
Mega = Kilo * 1000
|
|
||||||
Giga = Mega * 1000
|
|
||||||
Tera = Giga * 1000
|
|
||||||
Peta = Tera * 1000
|
|
||||||
Exa = Peta * 1000
|
|
||||||
)
|
|
||||||
|
|
||||||
func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 {
|
|
||||||
return map[string]float64{
|
|
||||||
shortSuffix: 1,
|
|
||||||
"K" + suffix: float64(scale),
|
|
||||||
"M" + suffix: float64(scale * scale),
|
|
||||||
"G" + suffix: float64(scale * scale * scale),
|
|
||||||
"T" + suffix: float64(scale * scale * scale * scale),
|
|
||||||
"P" + suffix: float64(scale * scale * scale * scale * scale),
|
|
||||||
"E" + suffix: float64(scale * scale * scale * scale * scale * scale),
|
|
||||||
}
|
|
||||||
}
|
|
138
vendor/github.com/alecthomas/units/util.go
generated
vendored
138
vendor/github.com/alecthomas/units/util.go
generated
vendored
|
@ -1,138 +0,0 @@
|
||||||
package units
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
siUnits = []string{"", "K", "M", "G", "T", "P", "E"}
|
|
||||||
)
|
|
||||||
|
|
||||||
func ToString(n int64, scale int64, suffix, baseSuffix string) string {
|
|
||||||
mn := len(siUnits)
|
|
||||||
out := make([]string, mn)
|
|
||||||
for i, m := range siUnits {
|
|
||||||
if n%scale != 0 || i == 0 && n == 0 {
|
|
||||||
s := suffix
|
|
||||||
if i == 0 {
|
|
||||||
s = baseSuffix
|
|
||||||
}
|
|
||||||
out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s)
|
|
||||||
}
|
|
||||||
n /= scale
|
|
||||||
if n == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(out, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123
|
|
||||||
var errLeadingInt = errors.New("units: bad [0-9]*") // never printed
|
|
||||||
|
|
||||||
// leadingInt consumes the leading [0-9]* from s.
|
|
||||||
func leadingInt(s string) (x int64, rem string, err error) {
|
|
||||||
i := 0
|
|
||||||
for ; i < len(s); i++ {
|
|
||||||
c := s[i]
|
|
||||||
if c < '0' || c > '9' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if x >= (1<<63-10)/10 {
|
|
||||||
// overflow
|
|
||||||
return 0, "", errLeadingInt
|
|
||||||
}
|
|
||||||
x = x*10 + int64(c) - '0'
|
|
||||||
}
|
|
||||||
return x, s[i:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseUnit(s string, unitMap map[string]float64) (int64, error) {
|
|
||||||
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
|
|
||||||
orig := s
|
|
||||||
f := float64(0)
|
|
||||||
neg := false
|
|
||||||
|
|
||||||
// Consume [-+]?
|
|
||||||
if s != "" {
|
|
||||||
c := s[0]
|
|
||||||
if c == '-' || c == '+' {
|
|
||||||
neg = c == '-'
|
|
||||||
s = s[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Special case: if all that is left is "0", this is zero.
|
|
||||||
if s == "0" {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
if s == "" {
|
|
||||||
return 0, errors.New("units: invalid " + orig)
|
|
||||||
}
|
|
||||||
for s != "" {
|
|
||||||
g := float64(0) // this element of the sequence
|
|
||||||
|
|
||||||
var x int64
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// The next character must be [0-9.]
|
|
||||||
if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
|
|
||||||
return 0, errors.New("units: invalid " + orig)
|
|
||||||
}
|
|
||||||
// Consume [0-9]*
|
|
||||||
pl := len(s)
|
|
||||||
x, s, err = leadingInt(s)
|
|
||||||
if err != nil {
|
|
||||||
return 0, errors.New("units: invalid " + orig)
|
|
||||||
}
|
|
||||||
g = float64(x)
|
|
||||||
pre := pl != len(s) // whether we consumed anything before a period
|
|
||||||
|
|
||||||
// Consume (\.[0-9]*)?
|
|
||||||
post := false
|
|
||||||
if s != "" && s[0] == '.' {
|
|
||||||
s = s[1:]
|
|
||||||
pl := len(s)
|
|
||||||
x, s, err = leadingInt(s)
|
|
||||||
if err != nil {
|
|
||||||
return 0, errors.New("units: invalid " + orig)
|
|
||||||
}
|
|
||||||
scale := 1.0
|
|
||||||
for n := pl - len(s); n > 0; n-- {
|
|
||||||
scale *= 10
|
|
||||||
}
|
|
||||||
g += float64(x) / scale
|
|
||||||
post = pl != len(s)
|
|
||||||
}
|
|
||||||
if !pre && !post {
|
|
||||||
// no digits (e.g. ".s" or "-.s")
|
|
||||||
return 0, errors.New("units: invalid " + orig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume unit.
|
|
||||||
i := 0
|
|
||||||
for ; i < len(s); i++ {
|
|
||||||
c := s[i]
|
|
||||||
if c == '.' || ('0' <= c && c <= '9') {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u := s[:i]
|
|
||||||
s = s[i:]
|
|
||||||
unit, ok := unitMap[u]
|
|
||||||
if !ok {
|
|
||||||
return 0, errors.New("units: unknown unit " + u + " in " + orig)
|
|
||||||
}
|
|
||||||
|
|
||||||
f += g * unit
|
|
||||||
}
|
|
||||||
|
|
||||||
if neg {
|
|
||||||
f = -f
|
|
||||||
}
|
|
||||||
if f < float64(-1<<63) || f > float64(1<<63-1) {
|
|
||||||
return 0, errors.New("units: overflow parsing unit")
|
|
||||||
}
|
|
||||||
return int64(f), nil
|
|
||||||
}
|
|
21
vendor/github.com/andrew-d/go-termutil/LICENSE
generated
vendored
21
vendor/github.com/andrew-d/go-termutil/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013-2014 Andrew Dunham
|
|
||||||
|
|
||||||
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.
|
|
8
vendor/github.com/andrew-d/go-termutil/const_bsd.go
generated
vendored
8
vendor/github.com/andrew-d/go-termutil/const_bsd.go
generated
vendored
|
@ -1,8 +0,0 @@
|
||||||
// +build darwin freebsd
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
const ioctlReadTermios = syscall.TIOCGETA
|
|
||||||
const ioctlWriteTermios = syscall.TIOCSETA
|
|
8
vendor/github.com/andrew-d/go-termutil/const_linux.go
generated
vendored
8
vendor/github.com/andrew-d/go-termutil/const_linux.go
generated
vendored
|
@ -1,8 +0,0 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
const ioctlReadTermios = syscall.TCGETS
|
|
||||||
const ioctlWriteTermios = syscall.TCSETS
|
|
7
vendor/github.com/andrew-d/go-termutil/getpass.go
generated
vendored
7
vendor/github.com/andrew-d/go-termutil/getpass.go
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
// +build !windows,!linux,!darwin,!freebsd
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
func GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
90
vendor/github.com/andrew-d/go-termutil/getpass_nix.go
generated
vendored
90
vendor/github.com/andrew-d/go-termutil/getpass_nix.go
generated
vendored
|
@ -1,90 +0,0 @@
|
||||||
// +build linux darwin freebsd
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error) {
|
|
||||||
// Firstly, print the prompt.
|
|
||||||
written := 0
|
|
||||||
buf := []byte(prompt)
|
|
||||||
for written < len(prompt) {
|
|
||||||
n, err := syscall.Write(int(prompt_fd), buf[written:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
written += n
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a newline after we're done, since it won't be echoed when the
|
|
||||||
// user presses 'Enter'.
|
|
||||||
defer syscall.Write(int(prompt_fd), []byte("\n"))
|
|
||||||
|
|
||||||
// Get the current state of the terminal
|
|
||||||
var oldState syscall.Termios
|
|
||||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL,
|
|
||||||
uintptr(input_fd),
|
|
||||||
ioctlReadTermios,
|
|
||||||
uintptr(unsafe.Pointer(&oldState)),
|
|
||||||
0, 0, 0); err != 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn off echo and write the new state.
|
|
||||||
newState := oldState
|
|
||||||
newState.Lflag &^= syscall.ECHO
|
|
||||||
newState.Lflag |= syscall.ICANON | syscall.ISIG
|
|
||||||
newState.Iflag |= syscall.ICRNL
|
|
||||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL,
|
|
||||||
uintptr(input_fd),
|
|
||||||
ioctlWriteTermios,
|
|
||||||
uintptr(unsafe.Pointer(&newState)),
|
|
||||||
0, 0, 0); err != 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regardless of how we exit, we need to restore the old state.
|
|
||||||
defer func() {
|
|
||||||
syscall.Syscall6(syscall.SYS_IOCTL,
|
|
||||||
uintptr(input_fd),
|
|
||||||
ioctlWriteTermios,
|
|
||||||
uintptr(unsafe.Pointer(&oldState)),
|
|
||||||
0, 0, 0)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Read in increments of 16 bytes.
|
|
||||||
var readBuf [16]byte
|
|
||||||
var ret []byte
|
|
||||||
for {
|
|
||||||
n, err := syscall.Read(int(input_fd), readBuf[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
if len(ret) == 0 {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim the trailing newline.
|
|
||||||
if readBuf[n-1] == '\n' {
|
|
||||||
n--
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = append(ret, readBuf[:n]...)
|
|
||||||
if n < len(readBuf) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
59
vendor/github.com/andrew-d/go-termutil/getpass_windows.go
generated
vendored
59
vendor/github.com/andrew-d/go-termutil/getpass_windows.go
generated
vendored
|
@ -1,59 +0,0 @@
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"io"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
f_getwch uintptr // wint_t _getwch(void)
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
f_getwch = syscall.MustLoadDLL("msvcrt.dll").MustFindProc("_getwch").Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPass(prompt string, prompt_fd, input_fd uintptr) ([]byte, error) {
|
|
||||||
// Firstly, print the prompt.
|
|
||||||
written := 0
|
|
||||||
buf := []byte(prompt)
|
|
||||||
for written < len(prompt) {
|
|
||||||
n, err := syscall.Write(syscall.Handle(prompt_fd), buf[written:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
written += n
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a newline after we're done, since it won't be echoed when the
|
|
||||||
// user presses 'Enter'.
|
|
||||||
defer syscall.Write(syscall.Handle(prompt_fd), []byte("\r\n"))
|
|
||||||
|
|
||||||
// Read the characters
|
|
||||||
var chars []uint16
|
|
||||||
for {
|
|
||||||
ret, _, _ := syscall.Syscall(f_getwch, 0, 0, 0, 0)
|
|
||||||
if ret == 0x000A || ret == 0x000D {
|
|
||||||
break
|
|
||||||
} else if ret == 0x0003 {
|
|
||||||
return nil, errors.New("Input has been interrupted by user.")
|
|
||||||
} else if ret == 0x0008 {
|
|
||||||
chars = chars[0:len(chars)-2]
|
|
||||||
} else {
|
|
||||||
chars = append(chars, uint16(ret))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to string...
|
|
||||||
s := syscall.UTF16ToString(chars)
|
|
||||||
|
|
||||||
// ... and back to UTF-8 bytes.
|
|
||||||
return []byte(s), nil
|
|
||||||
}
|
|
14
vendor/github.com/andrew-d/go-termutil/isatty-c.go
generated
vendored
14
vendor/github.com/andrew-d/go-termutil/isatty-c.go
generated
vendored
|
@ -1,14 +0,0 @@
|
||||||
// +build !windows,!linux,!darwin,!freebsd,cgo
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <unistd.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
func Isatty(fd uintptr) bool {
|
|
||||||
return int(C.isatty(C.int(fd))) != 0
|
|
||||||
}
|
|
7
vendor/github.com/andrew-d/go-termutil/isatty.go
generated
vendored
7
vendor/github.com/andrew-d/go-termutil/isatty.go
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
// +build !windows,!linux,!darwin,!freebsd,!cgo
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
func Isatty(fd uintptr) bool {
|
|
||||||
panic("Not implemented")
|
|
||||||
}
|
|
20
vendor/github.com/andrew-d/go-termutil/isatty_nix.go
generated
vendored
20
vendor/github.com/andrew-d/go-termutil/isatty_nix.go
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
// +build linux darwin freebsd
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Isatty(fd uintptr) bool {
|
|
||||||
var termios syscall.Termios
|
|
||||||
|
|
||||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd,
|
|
||||||
uintptr(ioctlReadTermios),
|
|
||||||
uintptr(unsafe.Pointer(&termios)),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0)
|
|
||||||
return err == 0
|
|
||||||
}
|
|
34
vendor/github.com/andrew-d/go-termutil/isatty_windows.go
generated
vendored
34
vendor/github.com/andrew-d/go-termutil/isatty_windows.go
generated
vendored
|
@ -1,34 +0,0 @@
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package termutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
kernel32 = syscall.MustLoadDLL("kernel32.dll")
|
|
||||||
fGetConsoleMode = kernel32.MustFindProc("GetConsoleMode")
|
|
||||||
)
|
|
||||||
|
|
||||||
func Isatty(fd uintptr) bool {
|
|
||||||
var x uint32
|
|
||||||
return getConsoleMode(syscall.Handle(fd), &x) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getConsoleMode(hConsoleHandle syscall.Handle, lpMode *uint32) error {
|
|
||||||
ret, _, err := syscall.Syscall(fGetConsoleMode.Addr(), 2,
|
|
||||||
uintptr(hConsoleHandle),
|
|
||||||
uintptr(unsafe.Pointer(lpMode)),
|
|
||||||
0)
|
|
||||||
|
|
||||||
if int(ret) == 0 {
|
|
||||||
if err != 0 {
|
|
||||||
return error(err)
|
|
||||||
} else {
|
|
||||||
return syscall.EINVAL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
19
vendor/gopkg.in/alecthomas/kingpin.v2/COPYING
generated
vendored
19
vendor/gopkg.in/alecthomas/kingpin.v2/COPYING
generated
vendored
|
@ -1,19 +0,0 @@
|
||||||
Copyright (C) 2014 Alec Thomas
|
|
||||||
|
|
||||||
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.
|
|
42
vendor/gopkg.in/alecthomas/kingpin.v2/actions.go
generated
vendored
42
vendor/gopkg.in/alecthomas/kingpin.v2/actions.go
generated
vendored
|
@ -1,42 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
// Action callback executed at various stages after all values are populated.
|
|
||||||
// The application, commands, arguments and flags all have corresponding
|
|
||||||
// actions.
|
|
||||||
type Action func(*ParseContext) error
|
|
||||||
|
|
||||||
type actionMixin struct {
|
|
||||||
actions []Action
|
|
||||||
preActions []Action
|
|
||||||
}
|
|
||||||
|
|
||||||
type actionApplier interface {
|
|
||||||
applyActions(*ParseContext) error
|
|
||||||
applyPreActions(*ParseContext) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *actionMixin) addAction(action Action) {
|
|
||||||
a.actions = append(a.actions, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *actionMixin) addPreAction(action Action) {
|
|
||||||
a.preActions = append(a.preActions, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *actionMixin) applyActions(context *ParseContext) error {
|
|
||||||
for _, action := range a.actions {
|
|
||||||
if err := action(context); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *actionMixin) applyPreActions(context *ParseContext) error {
|
|
||||||
for _, preAction := range a.preActions {
|
|
||||||
if err := preAction(context); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
688
vendor/gopkg.in/alecthomas/kingpin.v2/app.go
generated
vendored
688
vendor/gopkg.in/alecthomas/kingpin.v2/app.go
generated
vendored
|
@ -1,688 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrCommandNotSpecified = fmt.Errorf("command not specified")
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
envarTransformRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]+`)
|
|
||||||
)
|
|
||||||
|
|
||||||
type ApplicationValidator func(*Application) error
|
|
||||||
|
|
||||||
// An Application contains the definitions of flags, arguments and commands
|
|
||||||
// for an application.
|
|
||||||
type Application struct {
|
|
||||||
cmdMixin
|
|
||||||
initialized bool
|
|
||||||
|
|
||||||
Name string
|
|
||||||
Help string
|
|
||||||
|
|
||||||
author string
|
|
||||||
version string
|
|
||||||
errorWriter io.Writer // Destination for errors.
|
|
||||||
usageWriter io.Writer // Destination for usage
|
|
||||||
usageTemplate string
|
|
||||||
validator ApplicationValidator
|
|
||||||
terminate func(status int) // See Terminate()
|
|
||||||
noInterspersed bool // can flags be interspersed with args (or must they come first)
|
|
||||||
defaultEnvars bool
|
|
||||||
completion bool
|
|
||||||
|
|
||||||
// Help flag. Exposed for user customisation.
|
|
||||||
HelpFlag *FlagClause
|
|
||||||
// Help command. Exposed for user customisation. May be nil.
|
|
||||||
HelpCommand *CmdClause
|
|
||||||
// Version flag. Exposed for user customisation. May be nil.
|
|
||||||
VersionFlag *FlagClause
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new Kingpin application instance.
|
|
||||||
func New(name, help string) *Application {
|
|
||||||
a := &Application{
|
|
||||||
Name: name,
|
|
||||||
Help: help,
|
|
||||||
errorWriter: os.Stderr, // Left for backwards compatibility purposes.
|
|
||||||
usageWriter: os.Stderr,
|
|
||||||
usageTemplate: DefaultUsageTemplate,
|
|
||||||
terminate: os.Exit,
|
|
||||||
}
|
|
||||||
a.flagGroup = newFlagGroup()
|
|
||||||
a.argGroup = newArgGroup()
|
|
||||||
a.cmdGroup = newCmdGroup(a)
|
|
||||||
a.HelpFlag = a.Flag("help", "Show context-sensitive help (also try --help-long and --help-man).")
|
|
||||||
a.HelpFlag.Bool()
|
|
||||||
a.Flag("help-long", "Generate long help.").Hidden().PreAction(a.generateLongHelp).Bool()
|
|
||||||
a.Flag("help-man", "Generate a man page.").Hidden().PreAction(a.generateManPage).Bool()
|
|
||||||
a.Flag("completion-bash", "Output possible completions for the given args.").Hidden().BoolVar(&a.completion)
|
|
||||||
a.Flag("completion-script-bash", "Generate completion script for bash.").Hidden().PreAction(a.generateBashCompletionScript).Bool()
|
|
||||||
a.Flag("completion-script-zsh", "Generate completion script for ZSH.").Hidden().PreAction(a.generateZSHCompletionScript).Bool()
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) generateLongHelp(c *ParseContext) error {
|
|
||||||
a.Writer(os.Stdout)
|
|
||||||
if err := a.UsageForContextWithTemplate(c, 2, LongHelpTemplate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.terminate(0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) generateManPage(c *ParseContext) error {
|
|
||||||
a.Writer(os.Stdout)
|
|
||||||
if err := a.UsageForContextWithTemplate(c, 2, ManPageTemplate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.terminate(0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) generateBashCompletionScript(c *ParseContext) error {
|
|
||||||
a.Writer(os.Stdout)
|
|
||||||
if err := a.UsageForContextWithTemplate(c, 2, BashCompletionTemplate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.terminate(0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) generateZSHCompletionScript(c *ParseContext) error {
|
|
||||||
a.Writer(os.Stdout)
|
|
||||||
if err := a.UsageForContextWithTemplate(c, 2, ZshCompletionTemplate); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.terminate(0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultEnvars configures all flags (that do not already have an associated
|
|
||||||
// envar) to use a default environment variable in the form "<app>_<flag>".
|
|
||||||
//
|
|
||||||
// For example, if the application is named "foo" and a flag is named "bar-
|
|
||||||
// waz" the environment variable: "FOO_BAR_WAZ".
|
|
||||||
func (a *Application) DefaultEnvars() *Application {
|
|
||||||
a.defaultEnvars = true
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Terminate specifies the termination handler. Defaults to os.Exit(status).
|
|
||||||
// If nil is passed, a no-op function will be used.
|
|
||||||
func (a *Application) Terminate(terminate func(int)) *Application {
|
|
||||||
if terminate == nil {
|
|
||||||
terminate = func(int) {}
|
|
||||||
}
|
|
||||||
a.terminate = terminate
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writer specifies the writer to use for usage and errors. Defaults to os.Stderr.
|
|
||||||
// DEPRECATED: See ErrorWriter and UsageWriter.
|
|
||||||
func (a *Application) Writer(w io.Writer) *Application {
|
|
||||||
a.errorWriter = w
|
|
||||||
a.usageWriter = w
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorWriter sets the io.Writer to use for errors.
|
|
||||||
func (a *Application) ErrorWriter(w io.Writer) *Application {
|
|
||||||
a.errorWriter = w
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// UsageWriter sets the io.Writer to use for errors.
|
|
||||||
func (a *Application) UsageWriter(w io.Writer) *Application {
|
|
||||||
a.usageWriter = w
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// UsageTemplate specifies the text template to use when displaying usage
|
|
||||||
// information. The default is UsageTemplate.
|
|
||||||
func (a *Application) UsageTemplate(template string) *Application {
|
|
||||||
a.usageTemplate = template
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate sets a validation function to run when parsing.
|
|
||||||
func (a *Application) Validate(validator ApplicationValidator) *Application {
|
|
||||||
a.validator = validator
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseContext parses the given command line and returns the fully populated
|
|
||||||
// ParseContext.
|
|
||||||
func (a *Application) ParseContext(args []string) (*ParseContext, error) {
|
|
||||||
return a.parseContext(false, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) parseContext(ignoreDefault bool, args []string) (*ParseContext, error) {
|
|
||||||
if err := a.init(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
context := tokenize(args, ignoreDefault)
|
|
||||||
err := parse(context, a)
|
|
||||||
return context, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses command-line arguments. It returns the selected command and an
|
|
||||||
// error. The selected command will be a space separated subcommand, if
|
|
||||||
// subcommands have been configured.
|
|
||||||
//
|
|
||||||
// This will populate all flag and argument values, call all callbacks, and so
|
|
||||||
// on.
|
|
||||||
func (a *Application) Parse(args []string) (command string, err error) {
|
|
||||||
|
|
||||||
context, parseErr := a.ParseContext(args)
|
|
||||||
selected := []string{}
|
|
||||||
var setValuesErr error
|
|
||||||
|
|
||||||
if context == nil {
|
|
||||||
// Since we do not throw error immediately, there could be a case
|
|
||||||
// where a context returns nil. Protect against that.
|
|
||||||
return "", parseErr
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = a.setDefaults(context); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
selected, setValuesErr = a.setValues(context)
|
|
||||||
|
|
||||||
if err = a.applyPreActions(context, !a.completion); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.completion {
|
|
||||||
a.generateBashCompletion(context)
|
|
||||||
a.terminate(0)
|
|
||||||
} else {
|
|
||||||
if parseErr != nil {
|
|
||||||
return "", parseErr
|
|
||||||
}
|
|
||||||
|
|
||||||
a.maybeHelp(context)
|
|
||||||
if !context.EOL() {
|
|
||||||
return "", fmt.Errorf("unexpected argument '%s'", context.Peek())
|
|
||||||
}
|
|
||||||
|
|
||||||
if setValuesErr != nil {
|
|
||||||
return "", setValuesErr
|
|
||||||
}
|
|
||||||
|
|
||||||
command, err = a.execute(context, selected)
|
|
||||||
if err == ErrCommandNotSpecified {
|
|
||||||
a.writeUsage(context, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return command, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) writeUsage(context *ParseContext, err error) {
|
|
||||||
if err != nil {
|
|
||||||
a.Errorf("%s", err)
|
|
||||||
}
|
|
||||||
if err := a.UsageForContext(context); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
a.terminate(1)
|
|
||||||
} else {
|
|
||||||
a.terminate(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) maybeHelp(context *ParseContext) {
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if flag, ok := element.Clause.(*FlagClause); ok && flag == a.HelpFlag {
|
|
||||||
// Re-parse the command-line ignoring defaults, so that help works correctly.
|
|
||||||
context, _ = a.parseContext(true, context.rawArgs)
|
|
||||||
a.writeUsage(context, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version adds a --version flag for displaying the application version.
|
|
||||||
func (a *Application) Version(version string) *Application {
|
|
||||||
a.version = version
|
|
||||||
a.VersionFlag = a.Flag("version", "Show application version.").PreAction(func(*ParseContext) error {
|
|
||||||
fmt.Fprintln(a.usageWriter, version)
|
|
||||||
a.terminate(0)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
a.VersionFlag.Bool()
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Author sets the author output by some help templates.
|
|
||||||
func (a *Application) Author(author string) *Application {
|
|
||||||
a.author = author
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action callback to call when all values are populated and parsing is
|
|
||||||
// complete, but before any command, flag or argument actions.
|
|
||||||
//
|
|
||||||
// All Action() callbacks are called in the order they are encountered on the
|
|
||||||
// command line.
|
|
||||||
func (a *Application) Action(action Action) *Application {
|
|
||||||
a.addAction(action)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action called after parsing completes but before validation and execution.
|
|
||||||
func (a *Application) PreAction(action Action) *Application {
|
|
||||||
a.addPreAction(action)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command adds a new top-level command.
|
|
||||||
func (a *Application) Command(name, help string) *CmdClause {
|
|
||||||
return a.addCommand(name, help)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interspersed control if flags can be interspersed with positional arguments
|
|
||||||
//
|
|
||||||
// true (the default) means that they can, false means that all the flags must appear before the first positional arguments.
|
|
||||||
func (a *Application) Interspersed(interspersed bool) *Application {
|
|
||||||
a.noInterspersed = !interspersed
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) defaultEnvarPrefix() string {
|
|
||||||
if a.defaultEnvars {
|
|
||||||
return a.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) init() error {
|
|
||||||
if a.initialized {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if a.cmdGroup.have() && a.argGroup.have() {
|
|
||||||
return fmt.Errorf("can't mix top-level Arg()s with Command()s")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have subcommands, add a help command at the top-level.
|
|
||||||
if a.cmdGroup.have() {
|
|
||||||
var command []string
|
|
||||||
a.HelpCommand = a.Command("help", "Show help.").PreAction(func(context *ParseContext) error {
|
|
||||||
a.Usage(command)
|
|
||||||
a.terminate(0)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
a.HelpCommand.Arg("command", "Show help on command.").StringsVar(&command)
|
|
||||||
// Make help first command.
|
|
||||||
l := len(a.commandOrder)
|
|
||||||
a.commandOrder = append(a.commandOrder[l-1:l], a.commandOrder[:l-1]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := a.flagGroup.init(a.defaultEnvarPrefix()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := a.cmdGroup.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := a.argGroup.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, cmd := range a.commands {
|
|
||||||
if err := cmd.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flagGroups := []*flagGroup{a.flagGroup}
|
|
||||||
for _, cmd := range a.commandOrder {
|
|
||||||
if err := checkDuplicateFlags(cmd, flagGroups); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a.initialized = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively check commands for duplicate flags.
|
|
||||||
func checkDuplicateFlags(current *CmdClause, flagGroups []*flagGroup) error {
|
|
||||||
// Check for duplicates.
|
|
||||||
for _, flags := range flagGroups {
|
|
||||||
for _, flag := range current.flagOrder {
|
|
||||||
if flag.shorthand != 0 {
|
|
||||||
if _, ok := flags.short[string(flag.shorthand)]; ok {
|
|
||||||
return fmt.Errorf("duplicate short flag -%c", flag.shorthand)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := flags.long[flag.name]; ok {
|
|
||||||
return fmt.Errorf("duplicate long flag --%s", flag.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flagGroups = append(flagGroups, current.flagGroup)
|
|
||||||
// Check subcommands.
|
|
||||||
for _, subcmd := range current.commandOrder {
|
|
||||||
if err := checkDuplicateFlags(subcmd, flagGroups); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) execute(context *ParseContext, selected []string) (string, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if err = a.validateRequired(context); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = a.applyValidators(context); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = a.applyActions(context); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
command := strings.Join(selected, " ")
|
|
||||||
if command == "" && a.cmdGroup.have() {
|
|
||||||
return "", ErrCommandNotSpecified
|
|
||||||
}
|
|
||||||
return command, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) setDefaults(context *ParseContext) error {
|
|
||||||
flagElements := map[string]*ParseElement{}
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if flag, ok := element.Clause.(*FlagClause); ok {
|
|
||||||
if flag.name == "help" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
flagElements[flag.name] = element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
argElements := map[string]*ParseElement{}
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if arg, ok := element.Clause.(*ArgClause); ok {
|
|
||||||
argElements[arg.name] = element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check required flags and set defaults.
|
|
||||||
for _, flag := range context.flags.long {
|
|
||||||
if flagElements[flag.name] == nil {
|
|
||||||
if err := flag.setDefault(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arg := range context.arguments.args {
|
|
||||||
if argElements[arg.name] == nil {
|
|
||||||
if err := arg.setDefault(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) validateRequired(context *ParseContext) error {
|
|
||||||
flagElements := map[string]*ParseElement{}
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if flag, ok := element.Clause.(*FlagClause); ok {
|
|
||||||
flagElements[flag.name] = element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
argElements := map[string]*ParseElement{}
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if arg, ok := element.Clause.(*ArgClause); ok {
|
|
||||||
argElements[arg.name] = element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check required flags and set defaults.
|
|
||||||
for _, flag := range context.flags.long {
|
|
||||||
if flagElements[flag.name] == nil {
|
|
||||||
// Check required flags were provided.
|
|
||||||
if flag.needsValue() {
|
|
||||||
return fmt.Errorf("required flag --%s not provided", flag.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arg := range context.arguments.args {
|
|
||||||
if argElements[arg.name] == nil {
|
|
||||||
if arg.needsValue() {
|
|
||||||
return fmt.Errorf("required argument '%s' not provided", arg.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) setValues(context *ParseContext) (selected []string, err error) {
|
|
||||||
// Set all arg and flag values.
|
|
||||||
var (
|
|
||||||
lastCmd *CmdClause
|
|
||||||
flagSet = map[string]struct{}{}
|
|
||||||
)
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
switch clause := element.Clause.(type) {
|
|
||||||
case *FlagClause:
|
|
||||||
if _, ok := flagSet[clause.name]; ok {
|
|
||||||
if v, ok := clause.value.(repeatableFlag); !ok || !v.IsCumulative() {
|
|
||||||
return nil, fmt.Errorf("flag '%s' cannot be repeated", clause.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = clause.value.Set(*element.Value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
flagSet[clause.name] = struct{}{}
|
|
||||||
|
|
||||||
case *ArgClause:
|
|
||||||
if err = clause.value.Set(*element.Value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case *CmdClause:
|
|
||||||
if clause.validator != nil {
|
|
||||||
if err = clause.validator(clause); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selected = append(selected, clause.name)
|
|
||||||
lastCmd = clause
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastCmd != nil && len(lastCmd.commands) > 0 {
|
|
||||||
return nil, fmt.Errorf("must select a subcommand of '%s'", lastCmd.FullCommand())
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) applyValidators(context *ParseContext) (err error) {
|
|
||||||
// Call command validation functions.
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if cmd, ok := element.Clause.(*CmdClause); ok && cmd.validator != nil {
|
|
||||||
if err = cmd.validator(cmd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.validator != nil {
|
|
||||||
err = a.validator(a)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) applyPreActions(context *ParseContext, dispatch bool) error {
|
|
||||||
if err := a.actionMixin.applyPreActions(context); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Dispatch to actions.
|
|
||||||
if dispatch {
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if applier, ok := element.Clause.(actionApplier); ok {
|
|
||||||
if err := applier.applyPreActions(context); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) applyActions(context *ParseContext) error {
|
|
||||||
if err := a.actionMixin.applyActions(context); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Dispatch to actions.
|
|
||||||
for _, element := range context.Elements {
|
|
||||||
if applier, ok := element.Clause.(actionApplier); ok {
|
|
||||||
if err := applier.applyActions(context); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf prints an error message to w in the format "<appname>: error: <message>".
|
|
||||||
func (a *Application) Errorf(format string, args ...interface{}) {
|
|
||||||
fmt.Fprintf(a.errorWriter, a.Name+": error: "+format+"\n", args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf writes a formatted error to w then terminates with exit status 1.
|
|
||||||
func (a *Application) Fatalf(format string, args ...interface{}) {
|
|
||||||
a.Errorf(format, args...)
|
|
||||||
a.terminate(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalUsage prints an error message followed by usage information, then
|
|
||||||
// exits with a non-zero status.
|
|
||||||
func (a *Application) FatalUsage(format string, args ...interface{}) {
|
|
||||||
a.Errorf(format, args...)
|
|
||||||
// Force usage to go to error output.
|
|
||||||
a.usageWriter = a.errorWriter
|
|
||||||
a.Usage([]string{})
|
|
||||||
a.terminate(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalUsageContext writes a printf formatted error message to w, then usage
|
|
||||||
// information for the given ParseContext, before exiting.
|
|
||||||
func (a *Application) FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
|
|
||||||
a.Errorf(format, args...)
|
|
||||||
if err := a.UsageForContext(context); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
a.terminate(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalIfError prints an error and exits if err is not nil. The error is printed
|
|
||||||
// with the given formatted string, if any.
|
|
||||||
func (a *Application) FatalIfError(err error, format string, args ...interface{}) {
|
|
||||||
if err != nil {
|
|
||||||
prefix := ""
|
|
||||||
if format != "" {
|
|
||||||
prefix = fmt.Sprintf(format, args...) + ": "
|
|
||||||
}
|
|
||||||
a.Errorf(prefix+"%s", err)
|
|
||||||
a.terminate(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) completionOptions(context *ParseContext) []string {
|
|
||||||
args := context.rawArgs
|
|
||||||
|
|
||||||
var (
|
|
||||||
currArg string
|
|
||||||
prevArg string
|
|
||||||
target cmdMixin
|
|
||||||
)
|
|
||||||
|
|
||||||
numArgs := len(args)
|
|
||||||
if numArgs > 1 {
|
|
||||||
args = args[1:]
|
|
||||||
currArg = args[len(args)-1]
|
|
||||||
}
|
|
||||||
if numArgs > 2 {
|
|
||||||
prevArg = args[len(args)-2]
|
|
||||||
}
|
|
||||||
|
|
||||||
target = a.cmdMixin
|
|
||||||
if context.SelectedCommand != nil {
|
|
||||||
// A subcommand was in use. We will use it as the target
|
|
||||||
target = context.SelectedCommand.cmdMixin
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currArg != "" && strings.HasPrefix(currArg, "--")) || strings.HasPrefix(prevArg, "--") {
|
|
||||||
// Perform completion for A flag. The last/current argument started with "-"
|
|
||||||
var (
|
|
||||||
flagName string // The name of a flag if given (could be half complete)
|
|
||||||
flagValue string // The value assigned to a flag (if given) (could be half complete)
|
|
||||||
)
|
|
||||||
|
|
||||||
if strings.HasPrefix(prevArg, "--") && !strings.HasPrefix(currArg, "--") {
|
|
||||||
// Matches: ./myApp --flag value
|
|
||||||
// Wont Match: ./myApp --flag --
|
|
||||||
flagName = prevArg[2:] // Strip the "--"
|
|
||||||
flagValue = currArg
|
|
||||||
} else if strings.HasPrefix(currArg, "--") {
|
|
||||||
// Matches: ./myApp --flag --
|
|
||||||
// Matches: ./myApp --flag somevalue --
|
|
||||||
// Matches: ./myApp --
|
|
||||||
flagName = currArg[2:] // Strip the "--"
|
|
||||||
}
|
|
||||||
|
|
||||||
options, flagMatched, valueMatched := target.FlagCompletion(flagName, flagValue)
|
|
||||||
if valueMatched {
|
|
||||||
// Value Matched. Show cmdCompletions
|
|
||||||
return target.CmdCompletion(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add top level flags if we're not at the top level and no match was found.
|
|
||||||
if context.SelectedCommand != nil && !flagMatched {
|
|
||||||
topOptions, topFlagMatched, topValueMatched := a.FlagCompletion(flagName, flagValue)
|
|
||||||
if topValueMatched {
|
|
||||||
// Value Matched. Back to cmdCompletions
|
|
||||||
return target.CmdCompletion(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
if topFlagMatched {
|
|
||||||
// Top level had a flag which matched the input. Return it's options.
|
|
||||||
options = topOptions
|
|
||||||
} else {
|
|
||||||
// Add top level flags
|
|
||||||
options = append(options, topOptions...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform completion for sub commands and arguments.
|
|
||||||
return target.CmdCompletion(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) generateBashCompletion(context *ParseContext) {
|
|
||||||
options := a.completionOptions(context)
|
|
||||||
fmt.Printf("%s", strings.Join(options, "\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func envarTransform(name string) string {
|
|
||||||
return strings.ToUpper(envarTransformRegexp.ReplaceAllString(name, "_"))
|
|
||||||
}
|
|
184
vendor/gopkg.in/alecthomas/kingpin.v2/args.go
generated
vendored
184
vendor/gopkg.in/alecthomas/kingpin.v2/args.go
generated
vendored
|
@ -1,184 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type argGroup struct {
|
|
||||||
args []*ArgClause
|
|
||||||
}
|
|
||||||
|
|
||||||
func newArgGroup() *argGroup {
|
|
||||||
return &argGroup{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *argGroup) have() bool {
|
|
||||||
return len(a.args) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArg gets an argument definition.
|
|
||||||
//
|
|
||||||
// This allows existing arguments to be modified after definition but before parsing. Useful for
|
|
||||||
// modular applications.
|
|
||||||
func (a *argGroup) GetArg(name string) *ArgClause {
|
|
||||||
for _, arg := range a.args {
|
|
||||||
if arg.name == name {
|
|
||||||
return arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *argGroup) Arg(name, help string) *ArgClause {
|
|
||||||
arg := newArg(name, help)
|
|
||||||
a.args = append(a.args, arg)
|
|
||||||
return arg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *argGroup) init() error {
|
|
||||||
required := 0
|
|
||||||
seen := map[string]struct{}{}
|
|
||||||
previousArgMustBeLast := false
|
|
||||||
for i, arg := range a.args {
|
|
||||||
if previousArgMustBeLast {
|
|
||||||
return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name)
|
|
||||||
}
|
|
||||||
if arg.consumesRemainder() {
|
|
||||||
previousArgMustBeLast = true
|
|
||||||
}
|
|
||||||
if _, ok := seen[arg.name]; ok {
|
|
||||||
return fmt.Errorf("duplicate argument '%s'", arg.name)
|
|
||||||
}
|
|
||||||
seen[arg.name] = struct{}{}
|
|
||||||
if arg.required && required != i {
|
|
||||||
return fmt.Errorf("required arguments found after non-required")
|
|
||||||
}
|
|
||||||
if arg.required {
|
|
||||||
required++
|
|
||||||
}
|
|
||||||
if err := arg.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArgClause struct {
|
|
||||||
actionMixin
|
|
||||||
parserMixin
|
|
||||||
completionsMixin
|
|
||||||
envarMixin
|
|
||||||
name string
|
|
||||||
help string
|
|
||||||
defaultValues []string
|
|
||||||
required bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newArg(name, help string) *ArgClause {
|
|
||||||
a := &ArgClause{
|
|
||||||
name: name,
|
|
||||||
help: help,
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgClause) setDefault() error {
|
|
||||||
if a.HasEnvarValue() {
|
|
||||||
if v, ok := a.value.(remainderArg); !ok || !v.IsCumulative() {
|
|
||||||
// Use the value as-is
|
|
||||||
return a.value.Set(a.GetEnvarValue())
|
|
||||||
}
|
|
||||||
for _, value := range a.GetSplitEnvarValue() {
|
|
||||||
if err := a.value.Set(value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a.defaultValues) > 0 {
|
|
||||||
for _, defaultValue := range a.defaultValues {
|
|
||||||
if err := a.value.Set(defaultValue); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgClause) needsValue() bool {
|
|
||||||
haveDefault := len(a.defaultValues) > 0
|
|
||||||
return a.required && !(haveDefault || a.HasEnvarValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgClause) consumesRemainder() bool {
|
|
||||||
if r, ok := a.value.(remainderArg); ok {
|
|
||||||
return r.IsCumulative()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required arguments must be input by the user. They can not have a Default() value provided.
|
|
||||||
func (a *ArgClause) Required() *ArgClause {
|
|
||||||
a.required = true
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default values for this argument. They *must* be parseable by the value of the argument.
|
|
||||||
func (a *ArgClause) Default(values ...string) *ArgClause {
|
|
||||||
a.defaultValues = values
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Envar overrides the default value(s) for a flag from an environment variable,
|
|
||||||
// if it is set. Several default values can be provided by using new lines to
|
|
||||||
// separate them.
|
|
||||||
func (a *ArgClause) Envar(name string) *ArgClause {
|
|
||||||
a.envar = name
|
|
||||||
a.noEnvar = false
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoEnvar forces environment variable defaults to be disabled for this flag.
|
|
||||||
// Most useful in conjunction with app.DefaultEnvars().
|
|
||||||
func (a *ArgClause) NoEnvar() *ArgClause {
|
|
||||||
a.envar = ""
|
|
||||||
a.noEnvar = true
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgClause) Action(action Action) *ArgClause {
|
|
||||||
a.addAction(action)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgClause) PreAction(action Action) *ArgClause {
|
|
||||||
a.addPreAction(action)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// HintAction registers a HintAction (function) for the arg to provide completions
|
|
||||||
func (a *ArgClause) HintAction(action HintAction) *ArgClause {
|
|
||||||
a.addHintAction(action)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// HintOptions registers any number of options for the flag to provide completions
|
|
||||||
func (a *ArgClause) HintOptions(options ...string) *ArgClause {
|
|
||||||
a.addHintAction(func() []string {
|
|
||||||
return options
|
|
||||||
})
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgClause) init() error {
|
|
||||||
if a.required && len(a.defaultValues) > 0 {
|
|
||||||
return fmt.Errorf("required argument '%s' with unusable default value", a.name)
|
|
||||||
}
|
|
||||||
if a.value == nil {
|
|
||||||
return fmt.Errorf("no parser defined for arg '%s'", a.name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
274
vendor/gopkg.in/alecthomas/kingpin.v2/cmd.go
generated
vendored
274
vendor/gopkg.in/alecthomas/kingpin.v2/cmd.go
generated
vendored
|
@ -1,274 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type cmdMixin struct {
|
|
||||||
*flagGroup
|
|
||||||
*argGroup
|
|
||||||
*cmdGroup
|
|
||||||
actionMixin
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdCompletion returns completion options for arguments, if that's where
|
|
||||||
// parsing left off, or commands if there aren't any unsatisfied args.
|
|
||||||
func (c *cmdMixin) CmdCompletion(context *ParseContext) []string {
|
|
||||||
var options []string
|
|
||||||
|
|
||||||
// Count args already satisfied - we won't complete those, and add any
|
|
||||||
// default commands' alternatives, since they weren't listed explicitly
|
|
||||||
// and the user may want to explicitly list something else.
|
|
||||||
argsSatisfied := 0
|
|
||||||
for _, el := range context.Elements {
|
|
||||||
switch clause := el.Clause.(type) {
|
|
||||||
case *ArgClause:
|
|
||||||
if el.Value != nil && *el.Value != "" {
|
|
||||||
argsSatisfied++
|
|
||||||
}
|
|
||||||
case *CmdClause:
|
|
||||||
options = append(options, clause.completionAlts...)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if argsSatisfied < len(c.argGroup.args) {
|
|
||||||
// Since not all args have been satisfied, show options for the current one
|
|
||||||
options = append(options, c.argGroup.args[argsSatisfied].resolveCompletions()...)
|
|
||||||
} else {
|
|
||||||
// If all args are satisfied, then go back to completing commands
|
|
||||||
for _, cmd := range c.cmdGroup.commandOrder {
|
|
||||||
if !cmd.hidden {
|
|
||||||
options = append(options, cmd.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdMixin) FlagCompletion(flagName string, flagValue string) (choices []string, flagMatch bool, optionMatch bool) {
|
|
||||||
// Check if flagName matches a known flag.
|
|
||||||
// If it does, show the options for the flag
|
|
||||||
// Otherwise, show all flags
|
|
||||||
|
|
||||||
options := []string{}
|
|
||||||
|
|
||||||
for _, flag := range c.flagGroup.flagOrder {
|
|
||||||
// Loop through each flag and determine if a match exists
|
|
||||||
if flag.name == flagName {
|
|
||||||
// User typed entire flag. Need to look for flag options.
|
|
||||||
options = flag.resolveCompletions()
|
|
||||||
if len(options) == 0 {
|
|
||||||
// No Options to Choose From, Assume Match.
|
|
||||||
return options, true, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop options to find if the user specified value matches
|
|
||||||
isPrefix := false
|
|
||||||
matched := false
|
|
||||||
|
|
||||||
for _, opt := range options {
|
|
||||||
if flagValue == opt {
|
|
||||||
matched = true
|
|
||||||
} else if strings.HasPrefix(opt, flagValue) {
|
|
||||||
isPrefix = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matched Flag Directly
|
|
||||||
// Flag Value Not Prefixed, and Matched Directly
|
|
||||||
return options, true, !isPrefix && matched
|
|
||||||
}
|
|
||||||
|
|
||||||
if !flag.hidden {
|
|
||||||
options = append(options, "--"+flag.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No Flag directly matched.
|
|
||||||
return options, false, false
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type cmdGroup struct {
|
|
||||||
app *Application
|
|
||||||
parent *CmdClause
|
|
||||||
commands map[string]*CmdClause
|
|
||||||
commandOrder []*CmdClause
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdGroup) defaultSubcommand() *CmdClause {
|
|
||||||
for _, cmd := range c.commandOrder {
|
|
||||||
if cmd.isDefault {
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdGroup) cmdNames() []string {
|
|
||||||
names := make([]string, 0, len(c.commandOrder))
|
|
||||||
for _, cmd := range c.commandOrder {
|
|
||||||
names = append(names, cmd.name)
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArg gets a command definition.
|
|
||||||
//
|
|
||||||
// This allows existing commands to be modified after definition but before parsing. Useful for
|
|
||||||
// modular applications.
|
|
||||||
func (c *cmdGroup) GetCommand(name string) *CmdClause {
|
|
||||||
return c.commands[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCmdGroup(app *Application) *cmdGroup {
|
|
||||||
return &cmdGroup{
|
|
||||||
app: app,
|
|
||||||
commands: make(map[string]*CmdClause),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdGroup) flattenedCommands() (out []*CmdClause) {
|
|
||||||
for _, cmd := range c.commandOrder {
|
|
||||||
if len(cmd.commands) == 0 {
|
|
||||||
out = append(out, cmd)
|
|
||||||
}
|
|
||||||
out = append(out, cmd.flattenedCommands()...)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdGroup) addCommand(name, help string) *CmdClause {
|
|
||||||
cmd := newCommand(c.app, name, help)
|
|
||||||
c.commands[name] = cmd
|
|
||||||
c.commandOrder = append(c.commandOrder, cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdGroup) init() error {
|
|
||||||
seen := map[string]bool{}
|
|
||||||
if c.defaultSubcommand() != nil && !c.have() {
|
|
||||||
return fmt.Errorf("default subcommand %q provided but no subcommands defined", c.defaultSubcommand().name)
|
|
||||||
}
|
|
||||||
defaults := []string{}
|
|
||||||
for _, cmd := range c.commandOrder {
|
|
||||||
if cmd.isDefault {
|
|
||||||
defaults = append(defaults, cmd.name)
|
|
||||||
}
|
|
||||||
if seen[cmd.name] {
|
|
||||||
return fmt.Errorf("duplicate command %q", cmd.name)
|
|
||||||
}
|
|
||||||
seen[cmd.name] = true
|
|
||||||
for _, alias := range cmd.aliases {
|
|
||||||
if seen[alias] {
|
|
||||||
return fmt.Errorf("alias duplicates existing command %q", alias)
|
|
||||||
}
|
|
||||||
c.commands[alias] = cmd
|
|
||||||
}
|
|
||||||
if err := cmd.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(defaults) > 1 {
|
|
||||||
return fmt.Errorf("more than one default subcommand exists: %s", strings.Join(defaults, ", "))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdGroup) have() bool {
|
|
||||||
return len(c.commands) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type CmdClauseValidator func(*CmdClause) error
|
|
||||||
|
|
||||||
// A CmdClause is a single top-level command. It encapsulates a set of flags
|
|
||||||
// and either subcommands or positional arguments.
|
|
||||||
type CmdClause struct {
|
|
||||||
cmdMixin
|
|
||||||
app *Application
|
|
||||||
name string
|
|
||||||
aliases []string
|
|
||||||
help string
|
|
||||||
isDefault bool
|
|
||||||
validator CmdClauseValidator
|
|
||||||
hidden bool
|
|
||||||
completionAlts []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCommand(app *Application, name, help string) *CmdClause {
|
|
||||||
c := &CmdClause{
|
|
||||||
app: app,
|
|
||||||
name: name,
|
|
||||||
help: help,
|
|
||||||
}
|
|
||||||
c.flagGroup = newFlagGroup()
|
|
||||||
c.argGroup = newArgGroup()
|
|
||||||
c.cmdGroup = newCmdGroup(app)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an Alias for this command.
|
|
||||||
func (c *CmdClause) Alias(name string) *CmdClause {
|
|
||||||
c.aliases = append(c.aliases, name)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate sets a validation function to run when parsing.
|
|
||||||
func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause {
|
|
||||||
c.validator = validator
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdClause) FullCommand() string {
|
|
||||||
out := []string{c.name}
|
|
||||||
for p := c.parent; p != nil; p = p.parent {
|
|
||||||
out = append([]string{p.name}, out...)
|
|
||||||
}
|
|
||||||
return strings.Join(out, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command adds a new sub-command.
|
|
||||||
func (c *CmdClause) Command(name, help string) *CmdClause {
|
|
||||||
cmd := c.addCommand(name, help)
|
|
||||||
cmd.parent = c
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default makes this command the default if commands don't match.
|
|
||||||
func (c *CmdClause) Default() *CmdClause {
|
|
||||||
c.isDefault = true
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdClause) Action(action Action) *CmdClause {
|
|
||||||
c.addAction(action)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdClause) PreAction(action Action) *CmdClause {
|
|
||||||
c.addPreAction(action)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdClause) init() error {
|
|
||||||
if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c.argGroup.have() && c.cmdGroup.have() {
|
|
||||||
return fmt.Errorf("can't mix Arg()s with Command()s")
|
|
||||||
}
|
|
||||||
if err := c.argGroup.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := c.cmdGroup.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdClause) Hidden() *CmdClause {
|
|
||||||
c.hidden = true
|
|
||||||
return c
|
|
||||||
}
|
|
33
vendor/gopkg.in/alecthomas/kingpin.v2/completions.go
generated
vendored
33
vendor/gopkg.in/alecthomas/kingpin.v2/completions.go
generated
vendored
|
@ -1,33 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
// HintAction is a function type who is expected to return a slice of possible
|
|
||||||
// command line arguments.
|
|
||||||
type HintAction func() []string
|
|
||||||
type completionsMixin struct {
|
|
||||||
hintActions []HintAction
|
|
||||||
builtinHintActions []HintAction
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *completionsMixin) addHintAction(action HintAction) {
|
|
||||||
a.hintActions = append(a.hintActions, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow adding of HintActions which are added internally, ie, EnumVar
|
|
||||||
func (a *completionsMixin) addHintActionBuiltin(action HintAction) {
|
|
||||||
a.builtinHintActions = append(a.builtinHintActions, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *completionsMixin) resolveCompletions() []string {
|
|
||||||
var hints []string
|
|
||||||
|
|
||||||
options := a.builtinHintActions
|
|
||||||
if len(a.hintActions) > 0 {
|
|
||||||
// User specified their own hintActions. Use those instead.
|
|
||||||
options = a.hintActions
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, hintAction := range options {
|
|
||||||
hints = append(hints, hintAction()...)
|
|
||||||
}
|
|
||||||
return hints
|
|
||||||
}
|
|
68
vendor/gopkg.in/alecthomas/kingpin.v2/doc.go
generated
vendored
68
vendor/gopkg.in/alecthomas/kingpin.v2/doc.go
generated
vendored
|
@ -1,68 +0,0 @@
|
||||||
// Package kingpin provides command line interfaces like this:
|
|
||||||
//
|
|
||||||
// $ chat
|
|
||||||
// usage: chat [<flags>] <command> [<flags>] [<args> ...]
|
|
||||||
//
|
|
||||||
// Flags:
|
|
||||||
// --debug enable debug mode
|
|
||||||
// --help Show help.
|
|
||||||
// --server=127.0.0.1 server address
|
|
||||||
//
|
|
||||||
// Commands:
|
|
||||||
// help <command>
|
|
||||||
// Show help for a command.
|
|
||||||
//
|
|
||||||
// post [<flags>] <channel>
|
|
||||||
// Post a message to a channel.
|
|
||||||
//
|
|
||||||
// register <nick> <name>
|
|
||||||
// Register a new user.
|
|
||||||
//
|
|
||||||
// $ chat help post
|
|
||||||
// usage: chat [<flags>] post [<flags>] <channel> [<text>]
|
|
||||||
//
|
|
||||||
// Post a message to a channel.
|
|
||||||
//
|
|
||||||
// Flags:
|
|
||||||
// --image=IMAGE image to post
|
|
||||||
//
|
|
||||||
// Args:
|
|
||||||
// <channel> channel to post to
|
|
||||||
// [<text>] text to post
|
|
||||||
// $ chat post --image=~/Downloads/owls.jpg pics
|
|
||||||
//
|
|
||||||
// From code like this:
|
|
||||||
//
|
|
||||||
// package main
|
|
||||||
//
|
|
||||||
// import "gopkg.in/alecthomas/kingpin.v2"
|
|
||||||
//
|
|
||||||
// var (
|
|
||||||
// debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool()
|
|
||||||
// serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP()
|
|
||||||
//
|
|
||||||
// register = kingpin.Command("register", "Register a new user.")
|
|
||||||
// registerNick = register.Arg("nick", "nickname for user").Required().String()
|
|
||||||
// registerName = register.Arg("name", "name of user").Required().String()
|
|
||||||
//
|
|
||||||
// post = kingpin.Command("post", "Post a message to a channel.")
|
|
||||||
// postImage = post.Flag("image", "image to post").ExistingFile()
|
|
||||||
// postChannel = post.Arg("channel", "channel to post to").Required().String()
|
|
||||||
// postText = post.Arg("text", "text to post").String()
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func main() {
|
|
||||||
// switch kingpin.Parse() {
|
|
||||||
// // Register user
|
|
||||||
// case "register":
|
|
||||||
// println(*registerNick)
|
|
||||||
//
|
|
||||||
// // Post message
|
|
||||||
// case "post":
|
|
||||||
// if *postImage != nil {
|
|
||||||
// }
|
|
||||||
// if *postText != "" {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
package kingpin
|
|
45
vendor/gopkg.in/alecthomas/kingpin.v2/envar.go
generated
vendored
45
vendor/gopkg.in/alecthomas/kingpin.v2/envar.go
generated
vendored
|
@ -1,45 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
envVarValuesSeparator = "\r?\n"
|
|
||||||
envVarValuesTrimmer = regexp.MustCompile(envVarValuesSeparator + "$")
|
|
||||||
envVarValuesSplitter = regexp.MustCompile(envVarValuesSeparator)
|
|
||||||
)
|
|
||||||
|
|
||||||
type envarMixin struct {
|
|
||||||
envar string
|
|
||||||
noEnvar bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *envarMixin) HasEnvarValue() bool {
|
|
||||||
return e.GetEnvarValue() != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *envarMixin) GetEnvarValue() string {
|
|
||||||
if e.noEnvar || e.envar == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return os.Getenv(e.envar)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *envarMixin) GetSplitEnvarValue() []string {
|
|
||||||
values := make([]string, 0)
|
|
||||||
|
|
||||||
envarValue := e.GetEnvarValue()
|
|
||||||
if envarValue == "" {
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split by new line to extract multiple values, if any.
|
|
||||||
trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "")
|
|
||||||
for _, value := range envVarValuesSplitter.Split(trimmed, -1) {
|
|
||||||
values = append(values, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return values
|
|
||||||
}
|
|
308
vendor/gopkg.in/alecthomas/kingpin.v2/flags.go
generated
vendored
308
vendor/gopkg.in/alecthomas/kingpin.v2/flags.go
generated
vendored
|
@ -1,308 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type flagGroup struct {
|
|
||||||
short map[string]*FlagClause
|
|
||||||
long map[string]*FlagClause
|
|
||||||
flagOrder []*FlagClause
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFlagGroup() *flagGroup {
|
|
||||||
return &flagGroup{
|
|
||||||
short: map[string]*FlagClause{},
|
|
||||||
long: map[string]*FlagClause{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFlag gets a flag definition.
|
|
||||||
//
|
|
||||||
// This allows existing flags to be modified after definition but before parsing. Useful for
|
|
||||||
// modular applications.
|
|
||||||
func (f *flagGroup) GetFlag(name string) *FlagClause {
|
|
||||||
return f.long[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flag defines a new flag with the given long name and help.
|
|
||||||
func (f *flagGroup) Flag(name, help string) *FlagClause {
|
|
||||||
flag := newFlag(name, help)
|
|
||||||
f.long[name] = flag
|
|
||||||
f.flagOrder = append(f.flagOrder, flag)
|
|
||||||
return flag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *flagGroup) init(defaultEnvarPrefix string) error {
|
|
||||||
if err := f.checkDuplicates(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, flag := range f.long {
|
|
||||||
if defaultEnvarPrefix != "" && !flag.noEnvar && flag.envar == "" {
|
|
||||||
flag.envar = envarTransform(defaultEnvarPrefix + "_" + flag.name)
|
|
||||||
}
|
|
||||||
if err := flag.init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if flag.shorthand != 0 {
|
|
||||||
f.short[string(flag.shorthand)] = flag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *flagGroup) checkDuplicates() error {
|
|
||||||
seenShort := map[rune]bool{}
|
|
||||||
seenLong := map[string]bool{}
|
|
||||||
for _, flag := range f.flagOrder {
|
|
||||||
if flag.shorthand != 0 {
|
|
||||||
if _, ok := seenShort[flag.shorthand]; ok {
|
|
||||||
return fmt.Errorf("duplicate short flag -%c", flag.shorthand)
|
|
||||||
}
|
|
||||||
seenShort[flag.shorthand] = true
|
|
||||||
}
|
|
||||||
if _, ok := seenLong[flag.name]; ok {
|
|
||||||
return fmt.Errorf("duplicate long flag --%s", flag.name)
|
|
||||||
}
|
|
||||||
seenLong[flag.name] = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *flagGroup) parse(context *ParseContext) (*FlagClause, error) {
|
|
||||||
var token *Token
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
token = context.Peek()
|
|
||||||
switch token.Type {
|
|
||||||
case TokenEOL:
|
|
||||||
break loop
|
|
||||||
|
|
||||||
case TokenLong, TokenShort:
|
|
||||||
flagToken := token
|
|
||||||
defaultValue := ""
|
|
||||||
var flag *FlagClause
|
|
||||||
var ok bool
|
|
||||||
invert := false
|
|
||||||
|
|
||||||
name := token.Value
|
|
||||||
if token.Type == TokenLong {
|
|
||||||
flag, ok = f.long[name]
|
|
||||||
if !ok {
|
|
||||||
if strings.HasPrefix(name, "no-") {
|
|
||||||
name = name[3:]
|
|
||||||
invert = true
|
|
||||||
}
|
|
||||||
flag, ok = f.long[name]
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
flag, ok = f.short[name]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("unknown short flag '%s'", flagToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Next()
|
|
||||||
|
|
||||||
fb, ok := flag.value.(boolFlag)
|
|
||||||
if ok && fb.IsBoolFlag() {
|
|
||||||
if invert {
|
|
||||||
defaultValue = "false"
|
|
||||||
} else {
|
|
||||||
defaultValue = "true"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if invert {
|
|
||||||
context.Push(token)
|
|
||||||
return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
|
|
||||||
}
|
|
||||||
token = context.Peek()
|
|
||||||
if token.Type != TokenArg {
|
|
||||||
context.Push(token)
|
|
||||||
return nil, fmt.Errorf("expected argument for flag '%s'", flagToken)
|
|
||||||
}
|
|
||||||
context.Next()
|
|
||||||
defaultValue = token.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
context.matchedFlag(flag, defaultValue)
|
|
||||||
return flag, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlagClause is a fluid interface used to build flags.
|
|
||||||
type FlagClause struct {
|
|
||||||
parserMixin
|
|
||||||
actionMixin
|
|
||||||
completionsMixin
|
|
||||||
envarMixin
|
|
||||||
name string
|
|
||||||
shorthand rune
|
|
||||||
help string
|
|
||||||
defaultValues []string
|
|
||||||
placeholder string
|
|
||||||
hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFlag(name, help string) *FlagClause {
|
|
||||||
f := &FlagClause{
|
|
||||||
name: name,
|
|
||||||
help: help,
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagClause) setDefault() error {
|
|
||||||
if f.HasEnvarValue() {
|
|
||||||
if v, ok := f.value.(repeatableFlag); !ok || !v.IsCumulative() {
|
|
||||||
// Use the value as-is
|
|
||||||
return f.value.Set(f.GetEnvarValue())
|
|
||||||
} else {
|
|
||||||
for _, value := range f.GetSplitEnvarValue() {
|
|
||||||
if err := f.value.Set(value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(f.defaultValues) > 0 {
|
|
||||||
for _, defaultValue := range f.defaultValues {
|
|
||||||
if err := f.value.Set(defaultValue); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagClause) needsValue() bool {
|
|
||||||
haveDefault := len(f.defaultValues) > 0
|
|
||||||
return f.required && !(haveDefault || f.HasEnvarValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagClause) init() error {
|
|
||||||
if f.required && len(f.defaultValues) > 0 {
|
|
||||||
return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name)
|
|
||||||
}
|
|
||||||
if f.value == nil {
|
|
||||||
return fmt.Errorf("no type defined for --%s (eg. .String())", f.name)
|
|
||||||
}
|
|
||||||
if v, ok := f.value.(repeatableFlag); (!ok || !v.IsCumulative()) && len(f.defaultValues) > 1 {
|
|
||||||
return fmt.Errorf("invalid default for '--%s', expecting single value", f.name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatch to the given function after the flag is parsed and validated.
|
|
||||||
func (f *FlagClause) Action(action Action) *FlagClause {
|
|
||||||
f.addAction(action)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagClause) PreAction(action Action) *FlagClause {
|
|
||||||
f.addPreAction(action)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// HintAction registers a HintAction (function) for the flag to provide completions
|
|
||||||
func (a *FlagClause) HintAction(action HintAction) *FlagClause {
|
|
||||||
a.addHintAction(action)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// HintOptions registers any number of options for the flag to provide completions
|
|
||||||
func (a *FlagClause) HintOptions(options ...string) *FlagClause {
|
|
||||||
a.addHintAction(func() []string {
|
|
||||||
return options
|
|
||||||
})
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *FlagClause) EnumVar(target *string, options ...string) {
|
|
||||||
a.parserMixin.EnumVar(target, options...)
|
|
||||||
a.addHintActionBuiltin(func() []string {
|
|
||||||
return options
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *FlagClause) Enum(options ...string) (target *string) {
|
|
||||||
a.addHintActionBuiltin(func() []string {
|
|
||||||
return options
|
|
||||||
})
|
|
||||||
return a.parserMixin.Enum(options...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default values for this flag. They *must* be parseable by the value of the flag.
|
|
||||||
func (f *FlagClause) Default(values ...string) *FlagClause {
|
|
||||||
f.defaultValues = values
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEPRECATED: Use Envar(name) instead.
|
|
||||||
func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause {
|
|
||||||
return f.Envar(envar)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Envar overrides the default value(s) for a flag from an environment variable,
|
|
||||||
// if it is set. Several default values can be provided by using new lines to
|
|
||||||
// separate them.
|
|
||||||
func (f *FlagClause) Envar(name string) *FlagClause {
|
|
||||||
f.envar = name
|
|
||||||
f.noEnvar = false
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoEnvar forces environment variable defaults to be disabled for this flag.
|
|
||||||
// Most useful in conjunction with app.DefaultEnvars().
|
|
||||||
func (f *FlagClause) NoEnvar() *FlagClause {
|
|
||||||
f.envar = ""
|
|
||||||
f.noEnvar = true
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// PlaceHolder sets the place-holder string used for flag values in the help. The
|
|
||||||
// default behaviour is to use the value provided by Default() if provided,
|
|
||||||
// then fall back on the capitalized flag name.
|
|
||||||
func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause {
|
|
||||||
f.placeholder = placeholder
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hidden hides a flag from usage but still allows it to be used.
|
|
||||||
func (f *FlagClause) Hidden() *FlagClause {
|
|
||||||
f.hidden = true
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required makes the flag required. You can not provide a Default() value to a Required() flag.
|
|
||||||
func (f *FlagClause) Required() *FlagClause {
|
|
||||||
f.required = true
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short sets the short flag name.
|
|
||||||
func (f *FlagClause) Short(name rune) *FlagClause {
|
|
||||||
f.shorthand = name
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool makes this flag a boolean flag.
|
|
||||||
func (f *FlagClause) Bool() (target *bool) {
|
|
||||||
target = new(bool)
|
|
||||||
f.SetValue(newBoolValue(target))
|
|
||||||
return
|
|
||||||
}
|
|
94
vendor/gopkg.in/alecthomas/kingpin.v2/global.go
generated
vendored
94
vendor/gopkg.in/alecthomas/kingpin.v2/global.go
generated
vendored
|
@ -1,94 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// CommandLine is the default Kingpin parser.
|
|
||||||
CommandLine = New(filepath.Base(os.Args[0]), "")
|
|
||||||
// Global help flag. Exposed for user customisation.
|
|
||||||
HelpFlag = CommandLine.HelpFlag
|
|
||||||
// Top-level help command. Exposed for user customisation. May be nil.
|
|
||||||
HelpCommand = CommandLine.HelpCommand
|
|
||||||
// Global version flag. Exposed for user customisation. May be nil.
|
|
||||||
VersionFlag = CommandLine.VersionFlag
|
|
||||||
)
|
|
||||||
|
|
||||||
// Command adds a new command to the default parser.
|
|
||||||
func Command(name, help string) *CmdClause {
|
|
||||||
return CommandLine.Command(name, help)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flag adds a new flag to the default parser.
|
|
||||||
func Flag(name, help string) *FlagClause {
|
|
||||||
return CommandLine.Flag(name, help)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arg adds a new argument to the top-level of the default parser.
|
|
||||||
func Arg(name, help string) *ArgClause {
|
|
||||||
return CommandLine.Arg(name, help)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse and return the selected command. Will call the termination handler if
|
|
||||||
// an error is encountered.
|
|
||||||
func Parse() string {
|
|
||||||
selected := MustParse(CommandLine.Parse(os.Args[1:]))
|
|
||||||
if selected == "" && CommandLine.cmdGroup.have() {
|
|
||||||
Usage()
|
|
||||||
CommandLine.terminate(0)
|
|
||||||
}
|
|
||||||
return selected
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf prints an error message to stderr.
|
|
||||||
func Errorf(format string, args ...interface{}) {
|
|
||||||
CommandLine.Errorf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf prints an error message to stderr and exits.
|
|
||||||
func Fatalf(format string, args ...interface{}) {
|
|
||||||
CommandLine.Fatalf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalIfError prints an error and exits if err is not nil. The error is printed
|
|
||||||
// with the given prefix.
|
|
||||||
func FatalIfError(err error, format string, args ...interface{}) {
|
|
||||||
CommandLine.FatalIfError(err, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalUsage prints an error message followed by usage information, then
|
|
||||||
// exits with a non-zero status.
|
|
||||||
func FatalUsage(format string, args ...interface{}) {
|
|
||||||
CommandLine.FatalUsage(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalUsageContext writes a printf formatted error message to stderr, then
|
|
||||||
// usage information for the given ParseContext, before exiting.
|
|
||||||
func FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
|
|
||||||
CommandLine.FatalUsageContext(context, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage prints usage to stderr.
|
|
||||||
func Usage() {
|
|
||||||
CommandLine.Usage(os.Args[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global usage template to use (defaults to DefaultUsageTemplate).
|
|
||||||
func UsageTemplate(template string) *Application {
|
|
||||||
return CommandLine.UsageTemplate(template)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustParse can be used with app.Parse(args) to exit with an error if parsing fails.
|
|
||||||
func MustParse(command string, err error) string {
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("%s, try --help", err)
|
|
||||||
}
|
|
||||||
return command
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version adds a flag for displaying the application version number.
|
|
||||||
func Version(version string) *Application {
|
|
||||||
return CommandLine.Version(version)
|
|
||||||
}
|
|
9
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth.go
generated
vendored
9
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth.go
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
|
|
||||||
|
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
func guessWidth(w io.Writer) int {
|
|
||||||
return 80
|
|
||||||
}
|
|
38
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go
generated
vendored
38
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go
generated
vendored
|
@ -1,38 +0,0 @@
|
||||||
// +build !appengine,linux freebsd darwin dragonfly netbsd openbsd
|
|
||||||
|
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func guessWidth(w io.Writer) int {
|
|
||||||
// check if COLUMNS env is set to comply with
|
|
||||||
// http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html
|
|
||||||
colsStr := os.Getenv("COLUMNS")
|
|
||||||
if colsStr != "" {
|
|
||||||
if cols, err := strconv.Atoi(colsStr); err == nil {
|
|
||||||
return cols
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if t, ok := w.(*os.File); ok {
|
|
||||||
fd := t.Fd()
|
|
||||||
var dimensions [4]uint16
|
|
||||||
|
|
||||||
if _, _, err := syscall.Syscall6(
|
|
||||||
syscall.SYS_IOCTL,
|
|
||||||
uintptr(fd),
|
|
||||||
uintptr(syscall.TIOCGWINSZ),
|
|
||||||
uintptr(unsafe.Pointer(&dimensions)),
|
|
||||||
0, 0, 0,
|
|
||||||
); err == 0 {
|
|
||||||
return int(dimensions[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 80
|
|
||||||
}
|
|
227
vendor/gopkg.in/alecthomas/kingpin.v2/model.go
generated
vendored
227
vendor/gopkg.in/alecthomas/kingpin.v2/model.go
generated
vendored
|
@ -1,227 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Data model for Kingpin command-line structure.
|
|
||||||
|
|
||||||
type FlagGroupModel struct {
|
|
||||||
Flags []*FlagModel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagGroupModel) FlagSummary() string {
|
|
||||||
out := []string{}
|
|
||||||
count := 0
|
|
||||||
for _, flag := range f.Flags {
|
|
||||||
if flag.Name != "help" {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
if flag.Required {
|
|
||||||
if flag.IsBoolFlag() {
|
|
||||||
out = append(out, fmt.Sprintf("--[no-]%s", flag.Name))
|
|
||||||
} else {
|
|
||||||
out = append(out, fmt.Sprintf("--%s=%s", flag.Name, flag.FormatPlaceHolder()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if count != len(out) {
|
|
||||||
out = append(out, "[<flags>]")
|
|
||||||
}
|
|
||||||
return strings.Join(out, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
type FlagModel struct {
|
|
||||||
Name string
|
|
||||||
Help string
|
|
||||||
Short rune
|
|
||||||
Default []string
|
|
||||||
Envar string
|
|
||||||
PlaceHolder string
|
|
||||||
Required bool
|
|
||||||
Hidden bool
|
|
||||||
Value Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagModel) String() string {
|
|
||||||
return f.Value.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagModel) IsBoolFlag() bool {
|
|
||||||
if fl, ok := f.Value.(boolFlag); ok {
|
|
||||||
return fl.IsBoolFlag()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagModel) FormatPlaceHolder() string {
|
|
||||||
if f.PlaceHolder != "" {
|
|
||||||
return f.PlaceHolder
|
|
||||||
}
|
|
||||||
if len(f.Default) > 0 {
|
|
||||||
ellipsis := ""
|
|
||||||
if len(f.Default) > 1 {
|
|
||||||
ellipsis = "..."
|
|
||||||
}
|
|
||||||
if _, ok := f.Value.(*stringValue); ok {
|
|
||||||
return strconv.Quote(f.Default[0]) + ellipsis
|
|
||||||
}
|
|
||||||
return f.Default[0] + ellipsis
|
|
||||||
}
|
|
||||||
return strings.ToUpper(f.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArgGroupModel struct {
|
|
||||||
Args []*ArgModel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgGroupModel) ArgSummary() string {
|
|
||||||
depth := 0
|
|
||||||
out := []string{}
|
|
||||||
for _, arg := range a.Args {
|
|
||||||
h := "<" + arg.Name + ">"
|
|
||||||
if !arg.Required {
|
|
||||||
h = "[" + h
|
|
||||||
depth++
|
|
||||||
}
|
|
||||||
out = append(out, h)
|
|
||||||
}
|
|
||||||
out[len(out)-1] = out[len(out)-1] + strings.Repeat("]", depth)
|
|
||||||
return strings.Join(out, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArgModel struct {
|
|
||||||
Name string
|
|
||||||
Help string
|
|
||||||
Default []string
|
|
||||||
Envar string
|
|
||||||
Required bool
|
|
||||||
Value Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgModel) String() string {
|
|
||||||
return a.Value.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type CmdGroupModel struct {
|
|
||||||
Commands []*CmdModel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) {
|
|
||||||
for _, cmd := range c.Commands {
|
|
||||||
if len(cmd.Commands) == 0 {
|
|
||||||
out = append(out, cmd)
|
|
||||||
}
|
|
||||||
out = append(out, cmd.FlattenedCommands()...)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type CmdModel struct {
|
|
||||||
Name string
|
|
||||||
Aliases []string
|
|
||||||
Help string
|
|
||||||
FullCommand string
|
|
||||||
Depth int
|
|
||||||
Hidden bool
|
|
||||||
Default bool
|
|
||||||
*FlagGroupModel
|
|
||||||
*ArgGroupModel
|
|
||||||
*CmdGroupModel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdModel) String() string {
|
|
||||||
return c.FullCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApplicationModel struct {
|
|
||||||
Name string
|
|
||||||
Help string
|
|
||||||
Version string
|
|
||||||
Author string
|
|
||||||
*ArgGroupModel
|
|
||||||
*CmdGroupModel
|
|
||||||
*FlagGroupModel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) Model() *ApplicationModel {
|
|
||||||
return &ApplicationModel{
|
|
||||||
Name: a.Name,
|
|
||||||
Help: a.Help,
|
|
||||||
Version: a.version,
|
|
||||||
Author: a.author,
|
|
||||||
FlagGroupModel: a.flagGroup.Model(),
|
|
||||||
ArgGroupModel: a.argGroup.Model(),
|
|
||||||
CmdGroupModel: a.cmdGroup.Model(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *argGroup) Model() *ArgGroupModel {
|
|
||||||
m := &ArgGroupModel{}
|
|
||||||
for _, arg := range a.args {
|
|
||||||
m.Args = append(m.Args, arg.Model())
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ArgClause) Model() *ArgModel {
|
|
||||||
return &ArgModel{
|
|
||||||
Name: a.name,
|
|
||||||
Help: a.help,
|
|
||||||
Default: a.defaultValues,
|
|
||||||
Envar: a.envar,
|
|
||||||
Required: a.required,
|
|
||||||
Value: a.value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *flagGroup) Model() *FlagGroupModel {
|
|
||||||
m := &FlagGroupModel{}
|
|
||||||
for _, fl := range f.flagOrder {
|
|
||||||
m.Flags = append(m.Flags, fl.Model())
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FlagClause) Model() *FlagModel {
|
|
||||||
return &FlagModel{
|
|
||||||
Name: f.name,
|
|
||||||
Help: f.help,
|
|
||||||
Short: rune(f.shorthand),
|
|
||||||
Default: f.defaultValues,
|
|
||||||
Envar: f.envar,
|
|
||||||
PlaceHolder: f.placeholder,
|
|
||||||
Required: f.required,
|
|
||||||
Hidden: f.hidden,
|
|
||||||
Value: f.value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cmdGroup) Model() *CmdGroupModel {
|
|
||||||
m := &CmdGroupModel{}
|
|
||||||
for _, cm := range c.commandOrder {
|
|
||||||
m.Commands = append(m.Commands, cm.Model())
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CmdClause) Model() *CmdModel {
|
|
||||||
depth := 0
|
|
||||||
for i := c; i != nil; i = i.parent {
|
|
||||||
depth++
|
|
||||||
}
|
|
||||||
return &CmdModel{
|
|
||||||
Name: c.name,
|
|
||||||
Aliases: c.aliases,
|
|
||||||
Help: c.help,
|
|
||||||
Depth: depth,
|
|
||||||
Hidden: c.hidden,
|
|
||||||
Default: c.isDefault,
|
|
||||||
FullCommand: c.FullCommand(),
|
|
||||||
FlagGroupModel: c.flagGroup.Model(),
|
|
||||||
ArgGroupModel: c.argGroup.Model(),
|
|
||||||
CmdGroupModel: c.cmdGroup.Model(),
|
|
||||||
}
|
|
||||||
}
|
|
396
vendor/gopkg.in/alecthomas/kingpin.v2/parser.go
generated
vendored
396
vendor/gopkg.in/alecthomas/kingpin.v2/parser.go
generated
vendored
|
@ -1,396 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TokenType int
|
|
||||||
|
|
||||||
// Token types.
|
|
||||||
const (
|
|
||||||
TokenShort TokenType = iota
|
|
||||||
TokenLong
|
|
||||||
TokenArg
|
|
||||||
TokenError
|
|
||||||
TokenEOL
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t TokenType) String() string {
|
|
||||||
switch t {
|
|
||||||
case TokenShort:
|
|
||||||
return "short flag"
|
|
||||||
case TokenLong:
|
|
||||||
return "long flag"
|
|
||||||
case TokenArg:
|
|
||||||
return "argument"
|
|
||||||
case TokenError:
|
|
||||||
return "error"
|
|
||||||
case TokenEOL:
|
|
||||||
return "<EOL>"
|
|
||||||
}
|
|
||||||
return "?"
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
TokenEOLMarker = Token{-1, TokenEOL, ""}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Token struct {
|
|
||||||
Index int
|
|
||||||
Type TokenType
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) Equal(o *Token) bool {
|
|
||||||
return t.Index == o.Index
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) IsFlag() bool {
|
|
||||||
return t.Type == TokenShort || t.Type == TokenLong
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) IsEOF() bool {
|
|
||||||
return t.Type == TokenEOL
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) String() string {
|
|
||||||
switch t.Type {
|
|
||||||
case TokenShort:
|
|
||||||
return "-" + t.Value
|
|
||||||
case TokenLong:
|
|
||||||
return "--" + t.Value
|
|
||||||
case TokenArg:
|
|
||||||
return t.Value
|
|
||||||
case TokenError:
|
|
||||||
return "error: " + t.Value
|
|
||||||
case TokenEOL:
|
|
||||||
return "<EOL>"
|
|
||||||
default:
|
|
||||||
panic("unhandled type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A union of possible elements in a parse stack.
|
|
||||||
type ParseElement struct {
|
|
||||||
// Clause is either *CmdClause, *ArgClause or *FlagClause.
|
|
||||||
Clause interface{}
|
|
||||||
// Value is corresponding value for an ArgClause or FlagClause (if any).
|
|
||||||
Value *string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseContext holds the current context of the parser. When passed to
|
|
||||||
// Action() callbacks Elements will be fully populated with *FlagClause,
|
|
||||||
// *ArgClause and *CmdClause values and their corresponding arguments (if
|
|
||||||
// any).
|
|
||||||
type ParseContext struct {
|
|
||||||
SelectedCommand *CmdClause
|
|
||||||
ignoreDefault bool
|
|
||||||
argsOnly bool
|
|
||||||
peek []*Token
|
|
||||||
argi int // Index of current command-line arg we're processing.
|
|
||||||
args []string
|
|
||||||
rawArgs []string
|
|
||||||
flags *flagGroup
|
|
||||||
arguments *argGroup
|
|
||||||
argumenti int // Cursor into arguments
|
|
||||||
// Flags, arguments and commands encountered and collected during parse.
|
|
||||||
Elements []*ParseElement
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) nextArg() *ArgClause {
|
|
||||||
if p.argumenti >= len(p.arguments.args) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
arg := p.arguments.args[p.argumenti]
|
|
||||||
if !arg.consumesRemainder() {
|
|
||||||
p.argumenti++
|
|
||||||
}
|
|
||||||
return arg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) next() {
|
|
||||||
p.argi++
|
|
||||||
p.args = p.args[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasTrailingArgs returns true if there are unparsed command-line arguments.
|
|
||||||
// This can occur if the parser can not match remaining arguments.
|
|
||||||
func (p *ParseContext) HasTrailingArgs() bool {
|
|
||||||
return len(p.args) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func tokenize(args []string, ignoreDefault bool) *ParseContext {
|
|
||||||
return &ParseContext{
|
|
||||||
ignoreDefault: ignoreDefault,
|
|
||||||
args: args,
|
|
||||||
rawArgs: args,
|
|
||||||
flags: newFlagGroup(),
|
|
||||||
arguments: newArgGroup(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) mergeFlags(flags *flagGroup) {
|
|
||||||
for _, flag := range flags.flagOrder {
|
|
||||||
if flag.shorthand != 0 {
|
|
||||||
p.flags.short[string(flag.shorthand)] = flag
|
|
||||||
}
|
|
||||||
p.flags.long[flag.name] = flag
|
|
||||||
p.flags.flagOrder = append(p.flags.flagOrder, flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) mergeArgs(args *argGroup) {
|
|
||||||
for _, arg := range args.args {
|
|
||||||
p.arguments.args = append(p.arguments.args, arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) EOL() bool {
|
|
||||||
return p.Peek().Type == TokenEOL
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) Error() bool {
|
|
||||||
return p.Peek().Type == TokenError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next token in the parse context.
|
|
||||||
func (p *ParseContext) Next() *Token {
|
|
||||||
if len(p.peek) > 0 {
|
|
||||||
return p.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// End of tokens.
|
|
||||||
if len(p.args) == 0 {
|
|
||||||
return &Token{Index: p.argi, Type: TokenEOL}
|
|
||||||
}
|
|
||||||
|
|
||||||
arg := p.args[0]
|
|
||||||
p.next()
|
|
||||||
|
|
||||||
if p.argsOnly {
|
|
||||||
return &Token{p.argi, TokenArg, arg}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All remaining args are passed directly.
|
|
||||||
if arg == "--" {
|
|
||||||
p.argsOnly = true
|
|
||||||
return p.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(arg, "--") {
|
|
||||||
parts := strings.SplitN(arg[2:], "=", 2)
|
|
||||||
token := &Token{p.argi, TokenLong, parts[0]}
|
|
||||||
if len(parts) == 2 {
|
|
||||||
p.Push(&Token{p.argi, TokenArg, parts[1]})
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(arg, "-") {
|
|
||||||
if len(arg) == 1 {
|
|
||||||
return &Token{Index: p.argi, Type: TokenShort}
|
|
||||||
}
|
|
||||||
shortRune, size := utf8.DecodeRuneInString(arg[1:])
|
|
||||||
short := string(shortRune)
|
|
||||||
flag, ok := p.flags.short[short]
|
|
||||||
// Not a known short flag, we'll just return it anyway.
|
|
||||||
if !ok {
|
|
||||||
} else if fb, ok := flag.value.(boolFlag); ok && fb.IsBoolFlag() {
|
|
||||||
// Bool short flag.
|
|
||||||
} else {
|
|
||||||
// Short flag with combined argument: -fARG
|
|
||||||
token := &Token{p.argi, TokenShort, short}
|
|
||||||
if len(arg) > size+1 {
|
|
||||||
p.Push(&Token{p.argi, TokenArg, arg[size+1:]})
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(arg) > size+1 {
|
|
||||||
p.args = append([]string{"-" + arg[size+1:]}, p.args...)
|
|
||||||
}
|
|
||||||
return &Token{p.argi, TokenShort, short}
|
|
||||||
} else if strings.HasPrefix(arg, "@") {
|
|
||||||
expanded, err := ExpandArgsFromFile(arg[1:])
|
|
||||||
if err != nil {
|
|
||||||
return &Token{p.argi, TokenError, err.Error()}
|
|
||||||
}
|
|
||||||
if len(p.args) == 0 {
|
|
||||||
p.args = expanded
|
|
||||||
} else {
|
|
||||||
p.args = append(expanded, p.args...)
|
|
||||||
}
|
|
||||||
return p.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Token{p.argi, TokenArg, arg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) Peek() *Token {
|
|
||||||
if len(p.peek) == 0 {
|
|
||||||
return p.Push(p.Next())
|
|
||||||
}
|
|
||||||
return p.peek[len(p.peek)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) Push(token *Token) *Token {
|
|
||||||
p.peek = append(p.peek, token)
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) pop() *Token {
|
|
||||||
end := len(p.peek) - 1
|
|
||||||
token := p.peek[end]
|
|
||||||
p.peek = p.peek[0:end]
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) String() string {
|
|
||||||
return p.SelectedCommand.FullCommand()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) matchedFlag(flag *FlagClause, value string) {
|
|
||||||
p.Elements = append(p.Elements, &ParseElement{Clause: flag, Value: &value})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) matchedArg(arg *ArgClause, value string) {
|
|
||||||
p.Elements = append(p.Elements, &ParseElement{Clause: arg, Value: &value})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseContext) matchedCmd(cmd *CmdClause) {
|
|
||||||
p.Elements = append(p.Elements, &ParseElement{Clause: cmd})
|
|
||||||
p.mergeFlags(cmd.flagGroup)
|
|
||||||
p.mergeArgs(cmd.argGroup)
|
|
||||||
p.SelectedCommand = cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand arguments from a file. Lines starting with # will be treated as comments.
|
|
||||||
func ExpandArgsFromFile(filename string) (out []string, err error) {
|
|
||||||
if filename == "" {
|
|
||||||
return nil, fmt.Errorf("expected @ file to expand arguments from")
|
|
||||||
}
|
|
||||||
r, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open arguments file %q: %s", filename, err)
|
|
||||||
}
|
|
||||||
defer r.Close()
|
|
||||||
scanner := bufio.NewScanner(r)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if strings.HasPrefix(line, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out = append(out, line)
|
|
||||||
}
|
|
||||||
err = scanner.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read arguments from %q: %s", filename, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(context *ParseContext, app *Application) (err error) {
|
|
||||||
context.mergeFlags(app.flagGroup)
|
|
||||||
context.mergeArgs(app.argGroup)
|
|
||||||
|
|
||||||
cmds := app.cmdGroup
|
|
||||||
ignoreDefault := context.ignoreDefault
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for !context.EOL() && !context.Error() {
|
|
||||||
token := context.Peek()
|
|
||||||
|
|
||||||
switch token.Type {
|
|
||||||
case TokenLong, TokenShort:
|
|
||||||
if flag, err := context.flags.parse(context); err != nil {
|
|
||||||
if !ignoreDefault {
|
|
||||||
if cmd := cmds.defaultSubcommand(); cmd != nil {
|
|
||||||
cmd.completionAlts = cmds.cmdNames()
|
|
||||||
context.matchedCmd(cmd)
|
|
||||||
cmds = cmd.cmdGroup
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
} else if flag == HelpFlag {
|
|
||||||
ignoreDefault = true
|
|
||||||
}
|
|
||||||
|
|
||||||
case TokenArg:
|
|
||||||
if cmds.have() {
|
|
||||||
selectedDefault := false
|
|
||||||
cmd, ok := cmds.commands[token.String()]
|
|
||||||
if !ok {
|
|
||||||
if !ignoreDefault {
|
|
||||||
if cmd = cmds.defaultSubcommand(); cmd != nil {
|
|
||||||
cmd.completionAlts = cmds.cmdNames()
|
|
||||||
selectedDefault = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cmd == nil {
|
|
||||||
return fmt.Errorf("expected command but got %q", token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cmd == HelpCommand {
|
|
||||||
ignoreDefault = true
|
|
||||||
}
|
|
||||||
cmd.completionAlts = nil
|
|
||||||
context.matchedCmd(cmd)
|
|
||||||
cmds = cmd.cmdGroup
|
|
||||||
if !selectedDefault {
|
|
||||||
context.Next()
|
|
||||||
}
|
|
||||||
} else if context.arguments.have() {
|
|
||||||
if app.noInterspersed {
|
|
||||||
// no more flags
|
|
||||||
context.argsOnly = true
|
|
||||||
}
|
|
||||||
arg := context.nextArg()
|
|
||||||
if arg == nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
context.matchedArg(arg, token.String())
|
|
||||||
context.Next()
|
|
||||||
} else {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
|
|
||||||
case TokenEOL:
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to innermost default command.
|
|
||||||
for !ignoreDefault {
|
|
||||||
if cmd := cmds.defaultSubcommand(); cmd != nil {
|
|
||||||
cmd.completionAlts = cmds.cmdNames()
|
|
||||||
context.matchedCmd(cmd)
|
|
||||||
cmds = cmd.cmdGroup
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.Error() {
|
|
||||||
return fmt.Errorf("%s", context.Peek().Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !context.EOL() {
|
|
||||||
return fmt.Errorf("unexpected %s", context.Peek())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set defaults for all remaining args.
|
|
||||||
for arg := context.nextArg(); arg != nil && !arg.consumesRemainder(); arg = context.nextArg() {
|
|
||||||
for _, defaultValue := range arg.defaultValues {
|
|
||||||
if err := arg.value.Set(defaultValue); err != nil {
|
|
||||||
return fmt.Errorf("invalid default value '%s' for argument '%s'", defaultValue, arg.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
212
vendor/gopkg.in/alecthomas/kingpin.v2/parsers.go
generated
vendored
212
vendor/gopkg.in/alecthomas/kingpin.v2/parsers.go
generated
vendored
|
@ -1,212 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/alecthomas/units"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Settings interface {
|
|
||||||
SetValue(value Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
type parserMixin struct {
|
|
||||||
value Value
|
|
||||||
required bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) SetValue(value Value) {
|
|
||||||
p.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringMap provides key=value parsing into a map.
|
|
||||||
func (p *parserMixin) StringMap() (target *map[string]string) {
|
|
||||||
target = &(map[string]string{})
|
|
||||||
p.StringMapVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duration sets the parser to a time.Duration parser.
|
|
||||||
func (p *parserMixin) Duration() (target *time.Duration) {
|
|
||||||
target = new(time.Duration)
|
|
||||||
p.DurationVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes parses numeric byte units. eg. 1.5KB
|
|
||||||
func (p *parserMixin) Bytes() (target *units.Base2Bytes) {
|
|
||||||
target = new(units.Base2Bytes)
|
|
||||||
p.BytesVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP sets the parser to a net.IP parser.
|
|
||||||
func (p *parserMixin) IP() (target *net.IP) {
|
|
||||||
target = new(net.IP)
|
|
||||||
p.IPVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCP (host:port) address.
|
|
||||||
func (p *parserMixin) TCP() (target **net.TCPAddr) {
|
|
||||||
target = new(*net.TCPAddr)
|
|
||||||
p.TCPVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCPVar (host:port) address.
|
|
||||||
func (p *parserMixin) TCPVar(target **net.TCPAddr) {
|
|
||||||
p.SetValue(newTCPAddrValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingFile sets the parser to one that requires and returns an existing file.
|
|
||||||
func (p *parserMixin) ExistingFile() (target *string) {
|
|
||||||
target = new(string)
|
|
||||||
p.ExistingFileVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingDir sets the parser to one that requires and returns an existing directory.
|
|
||||||
func (p *parserMixin) ExistingDir() (target *string) {
|
|
||||||
target = new(string)
|
|
||||||
p.ExistingDirVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingFileOrDir sets the parser to one that requires and returns an existing file OR directory.
|
|
||||||
func (p *parserMixin) ExistingFileOrDir() (target *string) {
|
|
||||||
target = new(string)
|
|
||||||
p.ExistingFileOrDirVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// File returns an os.File against an existing file.
|
|
||||||
func (p *parserMixin) File() (target **os.File) {
|
|
||||||
target = new(*os.File)
|
|
||||||
p.FileVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// File attempts to open a File with os.OpenFile(flag, perm).
|
|
||||||
func (p *parserMixin) OpenFile(flag int, perm os.FileMode) (target **os.File) {
|
|
||||||
target = new(*os.File)
|
|
||||||
p.OpenFileVar(target, flag, perm)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// URL provides a valid, parsed url.URL.
|
|
||||||
func (p *parserMixin) URL() (target **url.URL) {
|
|
||||||
target = new(*url.URL)
|
|
||||||
p.URLVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringMap provides key=value parsing into a map.
|
|
||||||
func (p *parserMixin) StringMapVar(target *map[string]string) {
|
|
||||||
p.SetValue(newStringMapValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float sets the parser to a float64 parser.
|
|
||||||
func (p *parserMixin) Float() (target *float64) {
|
|
||||||
return p.Float64()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float sets the parser to a float64 parser.
|
|
||||||
func (p *parserMixin) FloatVar(target *float64) {
|
|
||||||
p.Float64Var(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duration sets the parser to a time.Duration parser.
|
|
||||||
func (p *parserMixin) DurationVar(target *time.Duration) {
|
|
||||||
p.SetValue(newDurationValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BytesVar parses numeric byte units. eg. 1.5KB
|
|
||||||
func (p *parserMixin) BytesVar(target *units.Base2Bytes) {
|
|
||||||
p.SetValue(newBytesValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP sets the parser to a net.IP parser.
|
|
||||||
func (p *parserMixin) IPVar(target *net.IP) {
|
|
||||||
p.SetValue(newIPValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingFile sets the parser to one that requires and returns an existing file.
|
|
||||||
func (p *parserMixin) ExistingFileVar(target *string) {
|
|
||||||
p.SetValue(newExistingFileValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingDir sets the parser to one that requires and returns an existing directory.
|
|
||||||
func (p *parserMixin) ExistingDirVar(target *string) {
|
|
||||||
p.SetValue(newExistingDirValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingDir sets the parser to one that requires and returns an existing directory.
|
|
||||||
func (p *parserMixin) ExistingFileOrDirVar(target *string) {
|
|
||||||
p.SetValue(newExistingFileOrDirValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileVar opens an existing file.
|
|
||||||
func (p *parserMixin) FileVar(target **os.File) {
|
|
||||||
p.SetValue(newFileValue(target, os.O_RDONLY, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenFileVar calls os.OpenFile(flag, perm)
|
|
||||||
func (p *parserMixin) OpenFileVar(target **os.File, flag int, perm os.FileMode) {
|
|
||||||
p.SetValue(newFileValue(target, flag, perm))
|
|
||||||
}
|
|
||||||
|
|
||||||
// URL provides a valid, parsed url.URL.
|
|
||||||
func (p *parserMixin) URLVar(target **url.URL) {
|
|
||||||
p.SetValue(newURLValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// URLList provides a parsed list of url.URL values.
|
|
||||||
func (p *parserMixin) URLList() (target *[]*url.URL) {
|
|
||||||
target = new([]*url.URL)
|
|
||||||
p.URLListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// URLListVar provides a parsed list of url.URL values.
|
|
||||||
func (p *parserMixin) URLListVar(target *[]*url.URL) {
|
|
||||||
p.SetValue(newURLListValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enum allows a value from a set of options.
|
|
||||||
func (p *parserMixin) Enum(options ...string) (target *string) {
|
|
||||||
target = new(string)
|
|
||||||
p.EnumVar(target, options...)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumVar allows a value from a set of options.
|
|
||||||
func (p *parserMixin) EnumVar(target *string, options ...string) {
|
|
||||||
p.SetValue(newEnumFlag(target, options...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums allows a set of values from a set of options.
|
|
||||||
func (p *parserMixin) Enums(options ...string) (target *[]string) {
|
|
||||||
target = new([]string)
|
|
||||||
p.EnumsVar(target, options...)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumVar allows a value from a set of options.
|
|
||||||
func (p *parserMixin) EnumsVar(target *[]string, options ...string) {
|
|
||||||
p.SetValue(newEnumsFlag(target, options...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Counter increments a number each time it is encountered.
|
|
||||||
func (p *parserMixin) Counter() (target *int) {
|
|
||||||
target = new(int)
|
|
||||||
p.CounterVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) CounterVar(target *int) {
|
|
||||||
p.SetValue(newCounterValue(target))
|
|
||||||
}
|
|
262
vendor/gopkg.in/alecthomas/kingpin.v2/templates.go
generated
vendored
262
vendor/gopkg.in/alecthomas/kingpin.v2/templates.go
generated
vendored
|
@ -1,262 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
// Default usage template.
|
|
||||||
var DefaultUsageTemplate = `{{define "FormatCommand"}}\
|
|
||||||
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
|
||||||
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatCommands"}}\
|
|
||||||
{{range .FlattenedCommands}}\
|
|
||||||
{{if not .Hidden}}\
|
|
||||||
{{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
|
|
||||||
{{.Help|Wrap 4}}
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatUsage"}}\
|
|
||||||
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
|
||||||
{{if .Help}}
|
|
||||||
{{.Help|Wrap 0}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{if .Context.SelectedCommand}}\
|
|
||||||
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
|
|
||||||
{{else}}\
|
|
||||||
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.Flags}}\
|
|
||||||
Flags:
|
|
||||||
{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.Args}}\
|
|
||||||
Args:
|
|
||||||
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.SelectedCommand}}\
|
|
||||||
{{if len .Context.SelectedCommand.Commands}}\
|
|
||||||
Subcommands:
|
|
||||||
{{template "FormatCommands" .Context.SelectedCommand}}
|
|
||||||
{{end}}\
|
|
||||||
{{else if .App.Commands}}\
|
|
||||||
Commands:
|
|
||||||
{{template "FormatCommands" .App}}
|
|
||||||
{{end}}\
|
|
||||||
`
|
|
||||||
|
|
||||||
// Usage template where command's optional flags are listed separately
|
|
||||||
var SeparateOptionalFlagsUsageTemplate = `{{define "FormatCommand"}}\
|
|
||||||
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
|
||||||
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatCommands"}}\
|
|
||||||
{{range .FlattenedCommands}}\
|
|
||||||
{{if not .Hidden}}\
|
|
||||||
{{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
|
|
||||||
{{.Help|Wrap 4}}
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatUsage"}}\
|
|
||||||
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
|
||||||
{{if .Help}}
|
|
||||||
{{.Help|Wrap 0}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.SelectedCommand}}\
|
|
||||||
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
|
|
||||||
{{else}}\
|
|
||||||
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{if .Context.Flags|RequiredFlags}}\
|
|
||||||
Required flags:
|
|
||||||
{{.Context.Flags|RequiredFlags|FlagsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.Flags|OptionalFlags}}\
|
|
||||||
Optional flags:
|
|
||||||
{{.Context.Flags|OptionalFlags|FlagsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.Args}}\
|
|
||||||
Args:
|
|
||||||
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.SelectedCommand}}\
|
|
||||||
Subcommands:
|
|
||||||
{{if .Context.SelectedCommand.Commands}}\
|
|
||||||
{{template "FormatCommands" .Context.SelectedCommand}}
|
|
||||||
{{end}}\
|
|
||||||
{{else if .App.Commands}}\
|
|
||||||
Commands:
|
|
||||||
{{template "FormatCommands" .App}}
|
|
||||||
{{end}}\
|
|
||||||
`
|
|
||||||
|
|
||||||
// Usage template with compactly formatted commands.
|
|
||||||
var CompactUsageTemplate = `{{define "FormatCommand"}}\
|
|
||||||
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
|
||||||
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatCommandList"}}\
|
|
||||||
{{range .}}\
|
|
||||||
{{if not .Hidden}}\
|
|
||||||
{{.Depth|Indent}}{{.Name}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
|
|
||||||
{{end}}\
|
|
||||||
{{template "FormatCommandList" .Commands}}\
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatUsage"}}\
|
|
||||||
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
|
||||||
{{if .Help}}
|
|
||||||
{{.Help|Wrap 0}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{if .Context.SelectedCommand}}\
|
|
||||||
usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
|
|
||||||
{{else}}\
|
|
||||||
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.Flags}}\
|
|
||||||
Flags:
|
|
||||||
{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.Args}}\
|
|
||||||
Args:
|
|
||||||
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.SelectedCommand}}\
|
|
||||||
{{if .Context.SelectedCommand.Commands}}\
|
|
||||||
Commands:
|
|
||||||
{{.Context.SelectedCommand}}
|
|
||||||
{{template "FormatCommandList" .Context.SelectedCommand.Commands}}
|
|
||||||
{{end}}\
|
|
||||||
{{else if .App.Commands}}\
|
|
||||||
Commands:
|
|
||||||
{{template "FormatCommandList" .App.Commands}}
|
|
||||||
{{end}}\
|
|
||||||
`
|
|
||||||
|
|
||||||
var ManPageTemplate = `{{define "FormatFlags"}}\
|
|
||||||
{{range .Flags}}\
|
|
||||||
{{if not .Hidden}}\
|
|
||||||
.TP
|
|
||||||
\fB{{if .Short}}-{{.Short|Char}}, {{end}}--{{.Name}}{{if not .IsBoolFlag}}={{.FormatPlaceHolder}}{{end}}\\fR
|
|
||||||
{{.Help}}
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatCommand"}}\
|
|
||||||
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
|
||||||
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}{{if .Default}}*{{end}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatCommands"}}\
|
|
||||||
{{range .FlattenedCommands}}\
|
|
||||||
{{if not .Hidden}}\
|
|
||||||
.SS
|
|
||||||
\fB{{.FullCommand}}{{template "FormatCommand" .}}\\fR
|
|
||||||
.PP
|
|
||||||
{{.Help}}
|
|
||||||
{{template "FormatFlags" .}}\
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatUsage"}}\
|
|
||||||
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}\\fR
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
.TH {{.App.Name}} 1 {{.App.Version}} "{{.App.Author}}"
|
|
||||||
.SH "NAME"
|
|
||||||
{{.App.Name}}
|
|
||||||
.SH "SYNOPSIS"
|
|
||||||
.TP
|
|
||||||
\fB{{.App.Name}}{{template "FormatUsage" .App}}
|
|
||||||
.SH "DESCRIPTION"
|
|
||||||
{{.App.Help}}
|
|
||||||
.SH "OPTIONS"
|
|
||||||
{{template "FormatFlags" .App}}\
|
|
||||||
{{if .App.Commands}}\
|
|
||||||
.SH "COMMANDS"
|
|
||||||
{{template "FormatCommands" .App}}\
|
|
||||||
{{end}}\
|
|
||||||
`
|
|
||||||
|
|
||||||
// Default usage template.
|
|
||||||
var LongHelpTemplate = `{{define "FormatCommand"}}\
|
|
||||||
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
|
|
||||||
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatCommands"}}\
|
|
||||||
{{range .FlattenedCommands}}\
|
|
||||||
{{if not .Hidden}}\
|
|
||||||
{{.FullCommand}}{{template "FormatCommand" .}}
|
|
||||||
{{.Help|Wrap 4}}
|
|
||||||
{{with .Flags|FlagsToTwoColumns}}{{FormatTwoColumnsWithIndent . 4 2}}{{end}}
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{define "FormatUsage"}}\
|
|
||||||
{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
|
|
||||||
{{if .Help}}
|
|
||||||
{{.Help|Wrap 0}}\
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
{{end}}\
|
|
||||||
|
|
||||||
usage: {{.App.Name}}{{template "FormatUsage" .App}}
|
|
||||||
{{if .Context.Flags}}\
|
|
||||||
Flags:
|
|
||||||
{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .Context.Args}}\
|
|
||||||
Args:
|
|
||||||
{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
|
|
||||||
{{end}}\
|
|
||||||
{{if .App.Commands}}\
|
|
||||||
Commands:
|
|
||||||
{{template "FormatCommands" .App}}
|
|
||||||
{{end}}\
|
|
||||||
`
|
|
||||||
|
|
||||||
var BashCompletionTemplate = `
|
|
||||||
_{{.App.Name}}_bash_autocomplete() {
|
|
||||||
local cur prev opts base
|
|
||||||
COMPREPLY=()
|
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} )
|
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}}
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
var ZshCompletionTemplate = `
|
|
||||||
#compdef {{.App.Name}}
|
|
||||||
autoload -U compinit && compinit
|
|
||||||
autoload -U bashcompinit && bashcompinit
|
|
||||||
|
|
||||||
_{{.App.Name}}_bash_autocomplete() {
|
|
||||||
local cur prev opts base
|
|
||||||
COMPREPLY=()
|
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} )
|
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}}
|
|
||||||
`
|
|
211
vendor/gopkg.in/alecthomas/kingpin.v2/usage.go
generated
vendored
211
vendor/gopkg.in/alecthomas/kingpin.v2/usage.go
generated
vendored
|
@ -1,211 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"go/doc"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alecthomas/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
preIndent = " "
|
|
||||||
)
|
|
||||||
|
|
||||||
func formatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) {
|
|
||||||
// Find size of first column.
|
|
||||||
s := 0
|
|
||||||
for _, row := range rows {
|
|
||||||
if c := len(row[0]); c > s && c < 30 {
|
|
||||||
s = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
indentStr := strings.Repeat(" ", indent)
|
|
||||||
offsetStr := strings.Repeat(" ", s+padding)
|
|
||||||
|
|
||||||
for _, row := range rows {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent)
|
|
||||||
lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
|
|
||||||
fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "")
|
|
||||||
if len(row[0]) >= 30 {
|
|
||||||
fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s\n", lines[0])
|
|
||||||
for _, line := range lines[1:] {
|
|
||||||
fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage writes application usage to w. It parses args to determine
|
|
||||||
// appropriate help context, such as which command to show help for.
|
|
||||||
func (a *Application) Usage(args []string) {
|
|
||||||
context, err := a.parseContext(true, args)
|
|
||||||
a.FatalIfError(err, "")
|
|
||||||
if err := a.UsageForContextWithTemplate(context, 2, a.usageTemplate); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatAppUsage(app *ApplicationModel) string {
|
|
||||||
s := []string{app.Name}
|
|
||||||
if len(app.Flags) > 0 {
|
|
||||||
s = append(s, app.FlagSummary())
|
|
||||||
}
|
|
||||||
if len(app.Args) > 0 {
|
|
||||||
s = append(s, app.ArgSummary())
|
|
||||||
}
|
|
||||||
return strings.Join(s, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string {
|
|
||||||
s := []string{app.Name, cmd.String()}
|
|
||||||
if len(app.Flags) > 0 {
|
|
||||||
s = append(s, app.FlagSummary())
|
|
||||||
}
|
|
||||||
if len(app.Args) > 0 {
|
|
||||||
s = append(s, app.ArgSummary())
|
|
||||||
}
|
|
||||||
return strings.Join(s, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatFlag(haveShort bool, flag *FlagModel) string {
|
|
||||||
flagString := ""
|
|
||||||
if flag.Short != 0 {
|
|
||||||
flagString += fmt.Sprintf("-%c, --%s", flag.Short, flag.Name)
|
|
||||||
} else {
|
|
||||||
if haveShort {
|
|
||||||
flagString += fmt.Sprintf(" --%s", flag.Name)
|
|
||||||
} else {
|
|
||||||
flagString += fmt.Sprintf("--%s", flag.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !flag.IsBoolFlag() {
|
|
||||||
flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
|
|
||||||
}
|
|
||||||
if v, ok := flag.Value.(repeatableFlag); ok && v.IsCumulative() {
|
|
||||||
flagString += " ..."
|
|
||||||
}
|
|
||||||
return flagString
|
|
||||||
}
|
|
||||||
|
|
||||||
type templateParseContext struct {
|
|
||||||
SelectedCommand *CmdModel
|
|
||||||
*FlagGroupModel
|
|
||||||
*ArgGroupModel
|
|
||||||
}
|
|
||||||
|
|
||||||
type templateContext struct {
|
|
||||||
App *ApplicationModel
|
|
||||||
Width int
|
|
||||||
Context *templateParseContext
|
|
||||||
}
|
|
||||||
|
|
||||||
// UsageForContext displays usage information from a ParseContext (obtained from
|
|
||||||
// Application.ParseContext() or Action(f) callbacks).
|
|
||||||
func (a *Application) UsageForContext(context *ParseContext) error {
|
|
||||||
return a.UsageForContextWithTemplate(context, 2, a.usageTemplate)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UsageForContextWithTemplate is the base usage function. You generally don't need to use this.
|
|
||||||
func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error {
|
|
||||||
width := guessWidth(a.usageWriter)
|
|
||||||
funcs := template.FuncMap{
|
|
||||||
"Indent": func(level int) string {
|
|
||||||
return strings.Repeat(" ", level*indent)
|
|
||||||
},
|
|
||||||
"Wrap": func(indent int, s string) string {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
indentText := strings.Repeat(" ", indent)
|
|
||||||
doc.ToText(buf, s, indentText, " "+indentText, width-indent)
|
|
||||||
return buf.String()
|
|
||||||
},
|
|
||||||
"FormatFlag": formatFlag,
|
|
||||||
"FlagsToTwoColumns": func(f []*FlagModel) [][2]string {
|
|
||||||
rows := [][2]string{}
|
|
||||||
haveShort := false
|
|
||||||
for _, flag := range f {
|
|
||||||
if flag.Short != 0 {
|
|
||||||
haveShort = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, flag := range f {
|
|
||||||
if !flag.Hidden {
|
|
||||||
rows = append(rows, [2]string{formatFlag(haveShort, flag), flag.Help})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rows
|
|
||||||
},
|
|
||||||
"RequiredFlags": func(f []*FlagModel) []*FlagModel {
|
|
||||||
requiredFlags := []*FlagModel{}
|
|
||||||
for _, flag := range f {
|
|
||||||
if flag.Required {
|
|
||||||
requiredFlags = append(requiredFlags, flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return requiredFlags
|
|
||||||
},
|
|
||||||
"OptionalFlags": func(f []*FlagModel) []*FlagModel {
|
|
||||||
optionalFlags := []*FlagModel{}
|
|
||||||
for _, flag := range f {
|
|
||||||
if !flag.Required {
|
|
||||||
optionalFlags = append(optionalFlags, flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return optionalFlags
|
|
||||||
},
|
|
||||||
"ArgsToTwoColumns": func(a []*ArgModel) [][2]string {
|
|
||||||
rows := [][2]string{}
|
|
||||||
for _, arg := range a {
|
|
||||||
s := "<" + arg.Name + ">"
|
|
||||||
if !arg.Required {
|
|
||||||
s = "[" + s + "]"
|
|
||||||
}
|
|
||||||
rows = append(rows, [2]string{s, arg.Help})
|
|
||||||
}
|
|
||||||
return rows
|
|
||||||
},
|
|
||||||
"FormatTwoColumns": func(rows [][2]string) string {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
formatTwoColumns(buf, indent, indent, width, rows)
|
|
||||||
return buf.String()
|
|
||||||
},
|
|
||||||
"FormatTwoColumnsWithIndent": func(rows [][2]string, indent, padding int) string {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
formatTwoColumns(buf, indent, padding, width, rows)
|
|
||||||
return buf.String()
|
|
||||||
},
|
|
||||||
"FormatAppUsage": formatAppUsage,
|
|
||||||
"FormatCommandUsage": formatCmdUsage,
|
|
||||||
"IsCumulative": func(value Value) bool {
|
|
||||||
r, ok := value.(remainderArg)
|
|
||||||
return ok && r.IsCumulative()
|
|
||||||
},
|
|
||||||
"Char": func(c rune) string {
|
|
||||||
return string(c)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
t, err := template.New("usage").Funcs(funcs).Parse(tmpl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var selectedCommand *CmdModel
|
|
||||||
if context.SelectedCommand != nil {
|
|
||||||
selectedCommand = context.SelectedCommand.Model()
|
|
||||||
}
|
|
||||||
ctx := templateContext{
|
|
||||||
App: a.Model(),
|
|
||||||
Width: width,
|
|
||||||
Context: &templateParseContext{
|
|
||||||
SelectedCommand: selectedCommand,
|
|
||||||
FlagGroupModel: context.flags.Model(),
|
|
||||||
ArgGroupModel: context.arguments.Model(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return t.Execute(a.usageWriter, ctx)
|
|
||||||
}
|
|
470
vendor/gopkg.in/alecthomas/kingpin.v2/values.go
generated
vendored
470
vendor/gopkg.in/alecthomas/kingpin.v2/values.go
generated
vendored
|
@ -1,470 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
//go:generate go run ./cmd/genvalues/main.go
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/alecthomas/units"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NOTE: Most of the base type values were lifted from:
|
|
||||||
// http://golang.org/src/pkg/flag/flag.go?s=20146:20222
|
|
||||||
|
|
||||||
// Value is the interface to the dynamic value stored in a flag.
|
|
||||||
// (The default value is represented as a string.)
|
|
||||||
//
|
|
||||||
// If a Value has an IsBoolFlag() bool method returning true, the command-line
|
|
||||||
// parser makes --name equivalent to -name=true rather than using the next
|
|
||||||
// command-line argument, and adds a --no-name counterpart for negating the
|
|
||||||
// flag.
|
|
||||||
type Value interface {
|
|
||||||
String() string
|
|
||||||
Set(string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter is an interface that allows the contents of a Value to be retrieved.
|
|
||||||
// It wraps the Value interface, rather than being part of it, because it
|
|
||||||
// appeared after Go 1 and its compatibility rules. All Value types provided
|
|
||||||
// by this package satisfy the Getter interface.
|
|
||||||
type Getter interface {
|
|
||||||
Value
|
|
||||||
Get() interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional interface to indicate boolean flags that don't accept a value, and
|
|
||||||
// implicitly have a --no-<x> negation counterpart.
|
|
||||||
type boolFlag interface {
|
|
||||||
Value
|
|
||||||
IsBoolFlag() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional interface for arguments that cumulatively consume all remaining
|
|
||||||
// input.
|
|
||||||
type remainderArg interface {
|
|
||||||
Value
|
|
||||||
IsCumulative() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional interface for flags that can be repeated.
|
|
||||||
type repeatableFlag interface {
|
|
||||||
Value
|
|
||||||
IsCumulative() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type accumulator struct {
|
|
||||||
element func(value interface{}) Value
|
|
||||||
typ reflect.Type
|
|
||||||
slice reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use reflection to accumulate values into a slice.
|
|
||||||
//
|
|
||||||
// target := []string{}
|
|
||||||
// newAccumulator(&target, func (value interface{}) Value {
|
|
||||||
// return newStringValue(value.(*string))
|
|
||||||
// })
|
|
||||||
func newAccumulator(slice interface{}, element func(value interface{}) Value) *accumulator {
|
|
||||||
typ := reflect.TypeOf(slice)
|
|
||||||
if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Slice {
|
|
||||||
panic("expected a pointer to a slice")
|
|
||||||
}
|
|
||||||
return &accumulator{
|
|
||||||
element: element,
|
|
||||||
typ: typ.Elem().Elem(),
|
|
||||||
slice: reflect.ValueOf(slice),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *accumulator) String() string {
|
|
||||||
out := []string{}
|
|
||||||
s := a.slice.Elem()
|
|
||||||
for i := 0; i < s.Len(); i++ {
|
|
||||||
out = append(out, a.element(s.Index(i).Addr().Interface()).String())
|
|
||||||
}
|
|
||||||
return strings.Join(out, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *accumulator) Set(value string) error {
|
|
||||||
e := reflect.New(a.typ)
|
|
||||||
if err := a.element(e.Interface()).Set(value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
slice := reflect.Append(a.slice.Elem(), e.Elem())
|
|
||||||
a.slice.Elem().Set(slice)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *accumulator) Get() interface{} {
|
|
||||||
return a.slice.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *accumulator) IsCumulative() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *boolValue) IsBoolFlag() bool { return true }
|
|
||||||
|
|
||||||
// -- time.Duration Value
|
|
||||||
type durationValue time.Duration
|
|
||||||
|
|
||||||
func newDurationValue(p *time.Duration) *durationValue {
|
|
||||||
return (*durationValue)(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *durationValue) Set(s string) error {
|
|
||||||
v, err := time.ParseDuration(s)
|
|
||||||
*d = durationValue(v)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *durationValue) Get() interface{} { return time.Duration(*d) }
|
|
||||||
|
|
||||||
func (d *durationValue) String() string { return (*time.Duration)(d).String() }
|
|
||||||
|
|
||||||
// -- map[string]string Value
|
|
||||||
type stringMapValue map[string]string
|
|
||||||
|
|
||||||
func newStringMapValue(p *map[string]string) *stringMapValue {
|
|
||||||
return (*stringMapValue)(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
var stringMapRegex = regexp.MustCompile("[:=]")
|
|
||||||
|
|
||||||
func (s *stringMapValue) Set(value string) error {
|
|
||||||
parts := stringMapRegex.Split(value, 2)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return fmt.Errorf("expected KEY=VALUE got '%s'", value)
|
|
||||||
}
|
|
||||||
(*s)[parts[0]] = parts[1]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stringMapValue) Get() interface{} {
|
|
||||||
return (map[string]string)(*s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stringMapValue) String() string {
|
|
||||||
return fmt.Sprintf("%s", map[string]string(*s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stringMapValue) IsCumulative() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- net.IP Value
|
|
||||||
type ipValue net.IP
|
|
||||||
|
|
||||||
func newIPValue(p *net.IP) *ipValue {
|
|
||||||
return (*ipValue)(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipValue) Set(value string) error {
|
|
||||||
if ip := net.ParseIP(value); ip == nil {
|
|
||||||
return fmt.Errorf("'%s' is not an IP address", value)
|
|
||||||
} else {
|
|
||||||
*i = *(*ipValue)(&ip)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipValue) Get() interface{} {
|
|
||||||
return (net.IP)(*i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *ipValue) String() string {
|
|
||||||
return (*net.IP)(i).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- *net.TCPAddr Value
|
|
||||||
type tcpAddrValue struct {
|
|
||||||
addr **net.TCPAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTCPAddrValue(p **net.TCPAddr) *tcpAddrValue {
|
|
||||||
return &tcpAddrValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *tcpAddrValue) Set(value string) error {
|
|
||||||
if addr, err := net.ResolveTCPAddr("tcp", value); err != nil {
|
|
||||||
return fmt.Errorf("'%s' is not a valid TCP address: %s", value, err)
|
|
||||||
} else {
|
|
||||||
*i.addr = addr
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcpAddrValue) Get() interface{} {
|
|
||||||
return (*net.TCPAddr)(*t.addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *tcpAddrValue) String() string {
|
|
||||||
return (*i.addr).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- existingFile Value
|
|
||||||
|
|
||||||
type fileStatValue struct {
|
|
||||||
path *string
|
|
||||||
predicate func(os.FileInfo) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFileStatValue(p *string, predicate func(os.FileInfo) error) *fileStatValue {
|
|
||||||
return &fileStatValue{
|
|
||||||
path: p,
|
|
||||||
predicate: predicate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *fileStatValue) Set(value string) error {
|
|
||||||
if s, err := os.Stat(value); os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("path '%s' does not exist", value)
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := e.predicate(s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*e.path = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fileStatValue) Get() interface{} {
|
|
||||||
return (string)(*f.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *fileStatValue) String() string {
|
|
||||||
return *e.path
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- os.File value
|
|
||||||
|
|
||||||
type fileValue struct {
|
|
||||||
f **os.File
|
|
||||||
flag int
|
|
||||||
perm os.FileMode
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFileValue(p **os.File, flag int, perm os.FileMode) *fileValue {
|
|
||||||
return &fileValue{p, flag, perm}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fileValue) Set(value string) error {
|
|
||||||
if fd, err := os.OpenFile(value, f.flag, f.perm); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
*f.f = fd
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fileValue) Get() interface{} {
|
|
||||||
return (*os.File)(*f.f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fileValue) String() string {
|
|
||||||
if *f.f == nil {
|
|
||||||
return "<nil>"
|
|
||||||
}
|
|
||||||
return (*f.f).Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- url.URL Value
|
|
||||||
type urlValue struct {
|
|
||||||
u **url.URL
|
|
||||||
}
|
|
||||||
|
|
||||||
func newURLValue(p **url.URL) *urlValue {
|
|
||||||
return &urlValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *urlValue) Set(value string) error {
|
|
||||||
if url, err := url.Parse(value); err != nil {
|
|
||||||
return fmt.Errorf("invalid URL: %s", err)
|
|
||||||
} else {
|
|
||||||
*u.u = url
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *urlValue) Get() interface{} {
|
|
||||||
return (*url.URL)(*u.u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *urlValue) String() string {
|
|
||||||
if *u.u == nil {
|
|
||||||
return "<nil>"
|
|
||||||
}
|
|
||||||
return (*u.u).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- []*url.URL Value
|
|
||||||
type urlListValue []*url.URL
|
|
||||||
|
|
||||||
func newURLListValue(p *[]*url.URL) *urlListValue {
|
|
||||||
return (*urlListValue)(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *urlListValue) Set(value string) error {
|
|
||||||
if url, err := url.Parse(value); err != nil {
|
|
||||||
return fmt.Errorf("invalid URL: %s", err)
|
|
||||||
} else {
|
|
||||||
*u = append(*u, url)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *urlListValue) Get() interface{} {
|
|
||||||
return ([]*url.URL)(*u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *urlListValue) String() string {
|
|
||||||
out := []string{}
|
|
||||||
for _, url := range *u {
|
|
||||||
out = append(out, url.String())
|
|
||||||
}
|
|
||||||
return strings.Join(out, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *urlListValue) IsCumulative() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// A flag whose value must be in a set of options.
|
|
||||||
type enumValue struct {
|
|
||||||
value *string
|
|
||||||
options []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEnumFlag(target *string, options ...string) *enumValue {
|
|
||||||
return &enumValue{
|
|
||||||
value: target,
|
|
||||||
options: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *enumValue) String() string {
|
|
||||||
return *a.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *enumValue) Set(value string) error {
|
|
||||||
for _, v := range a.options {
|
|
||||||
if v == value {
|
|
||||||
*a.value = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(a.options, ","), value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *enumValue) Get() interface{} {
|
|
||||||
return (string)(*e.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- []string Enum Value
|
|
||||||
type enumsValue struct {
|
|
||||||
value *[]string
|
|
||||||
options []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEnumsFlag(target *[]string, options ...string) *enumsValue {
|
|
||||||
return &enumsValue{
|
|
||||||
value: target,
|
|
||||||
options: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *enumsValue) Set(value string) error {
|
|
||||||
for _, v := range s.options {
|
|
||||||
if v == value {
|
|
||||||
*s.value = append(*s.value, value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(s.options, ","), value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *enumsValue) Get() interface{} {
|
|
||||||
return ([]string)(*e.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *enumsValue) String() string {
|
|
||||||
return strings.Join(*s.value, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *enumsValue) IsCumulative() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- units.Base2Bytes Value
|
|
||||||
type bytesValue units.Base2Bytes
|
|
||||||
|
|
||||||
func newBytesValue(p *units.Base2Bytes) *bytesValue {
|
|
||||||
return (*bytesValue)(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *bytesValue) Set(s string) error {
|
|
||||||
v, err := units.ParseBase2Bytes(s)
|
|
||||||
*d = bytesValue(v)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *bytesValue) Get() interface{} { return units.Base2Bytes(*d) }
|
|
||||||
|
|
||||||
func (d *bytesValue) String() string { return (*units.Base2Bytes)(d).String() }
|
|
||||||
|
|
||||||
func newExistingFileValue(target *string) *fileStatValue {
|
|
||||||
return newFileStatValue(target, func(s os.FileInfo) error {
|
|
||||||
if s.IsDir() {
|
|
||||||
return fmt.Errorf("'%s' is a directory", s.Name())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExistingDirValue(target *string) *fileStatValue {
|
|
||||||
return newFileStatValue(target, func(s os.FileInfo) error {
|
|
||||||
if !s.IsDir() {
|
|
||||||
return fmt.Errorf("'%s' is a file", s.Name())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExistingFileOrDirValue(target *string) *fileStatValue {
|
|
||||||
return newFileStatValue(target, func(s os.FileInfo) error { return nil })
|
|
||||||
}
|
|
||||||
|
|
||||||
type counterValue int
|
|
||||||
|
|
||||||
func newCounterValue(n *int) *counterValue {
|
|
||||||
return (*counterValue)(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *counterValue) Set(s string) error {
|
|
||||||
*c++
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *counterValue) Get() interface{} { return (int)(*c) }
|
|
||||||
func (c *counterValue) IsBoolFlag() bool { return true }
|
|
||||||
func (c *counterValue) String() string { return fmt.Sprintf("%d", *c) }
|
|
||||||
func (c *counterValue) IsCumulative() bool { return true }
|
|
||||||
|
|
||||||
func resolveHost(value string) (net.IP, error) {
|
|
||||||
if ip := net.ParseIP(value); ip != nil {
|
|
||||||
return ip, nil
|
|
||||||
} else {
|
|
||||||
if addr, err := net.ResolveIPAddr("ip", value); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
return addr.IP, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
821
vendor/gopkg.in/alecthomas/kingpin.v2/values_generated.go
generated
vendored
821
vendor/gopkg.in/alecthomas/kingpin.v2/values_generated.go
generated
vendored
|
@ -1,821 +0,0 @@
|
||||||
package kingpin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This file is autogenerated by "go generate .". Do not modify.
|
|
||||||
|
|
||||||
// -- bool Value
|
|
||||||
type boolValue struct{ v *bool }
|
|
||||||
|
|
||||||
func newBoolValue(p *bool) *boolValue {
|
|
||||||
return &boolValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *boolValue) Set(s string) error {
|
|
||||||
v, err := strconv.ParseBool(s)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (bool)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *boolValue) Get() interface{} { return (bool)(*f.v) }
|
|
||||||
|
|
||||||
func (f *boolValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Bool parses the next command-line value as bool.
|
|
||||||
func (p *parserMixin) Bool() (target *bool) {
|
|
||||||
target = new(bool)
|
|
||||||
p.BoolVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) BoolVar(target *bool) {
|
|
||||||
p.SetValue(newBoolValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolList accumulates bool values into a slice.
|
|
||||||
func (p *parserMixin) BoolList() (target *[]bool) {
|
|
||||||
target = new([]bool)
|
|
||||||
p.BoolListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) BoolListVar(target *[]bool) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newBoolValue(v.(*bool))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- string Value
|
|
||||||
type stringValue struct{ v *string }
|
|
||||||
|
|
||||||
func newStringValue(p *string) *stringValue {
|
|
||||||
return &stringValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *stringValue) Set(s string) error {
|
|
||||||
v, err := s, error(nil)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (string)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *stringValue) Get() interface{} { return (string)(*f.v) }
|
|
||||||
|
|
||||||
func (f *stringValue) String() string { return string(*f.v) }
|
|
||||||
|
|
||||||
// String parses the next command-line value as string.
|
|
||||||
func (p *parserMixin) String() (target *string) {
|
|
||||||
target = new(string)
|
|
||||||
p.StringVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) StringVar(target *string) {
|
|
||||||
p.SetValue(newStringValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings accumulates string values into a slice.
|
|
||||||
func (p *parserMixin) Strings() (target *[]string) {
|
|
||||||
target = new([]string)
|
|
||||||
p.StringsVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) StringsVar(target *[]string) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newStringValue(v.(*string))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- uint Value
|
|
||||||
type uintValue struct{ v *uint }
|
|
||||||
|
|
||||||
func newUintValue(p *uint) *uintValue {
|
|
||||||
return &uintValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uintValue) Set(s string) error {
|
|
||||||
v, err := strconv.ParseUint(s, 0, 64)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (uint)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uintValue) Get() interface{} { return (uint)(*f.v) }
|
|
||||||
|
|
||||||
func (f *uintValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Uint parses the next command-line value as uint.
|
|
||||||
func (p *parserMixin) Uint() (target *uint) {
|
|
||||||
target = new(uint)
|
|
||||||
p.UintVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) UintVar(target *uint) {
|
|
||||||
p.SetValue(newUintValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uints accumulates uint values into a slice.
|
|
||||||
func (p *parserMixin) Uints() (target *[]uint) {
|
|
||||||
target = new([]uint)
|
|
||||||
p.UintsVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) UintsVar(target *[]uint) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newUintValue(v.(*uint))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- uint8 Value
|
|
||||||
type uint8Value struct{ v *uint8 }
|
|
||||||
|
|
||||||
func newUint8Value(p *uint8) *uint8Value {
|
|
||||||
return &uint8Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint8Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseUint(s, 0, 8)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (uint8)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint8Value) Get() interface{} { return (uint8)(*f.v) }
|
|
||||||
|
|
||||||
func (f *uint8Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Uint8 parses the next command-line value as uint8.
|
|
||||||
func (p *parserMixin) Uint8() (target *uint8) {
|
|
||||||
target = new(uint8)
|
|
||||||
p.Uint8Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint8Var(target *uint8) {
|
|
||||||
p.SetValue(newUint8Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint8List accumulates uint8 values into a slice.
|
|
||||||
func (p *parserMixin) Uint8List() (target *[]uint8) {
|
|
||||||
target = new([]uint8)
|
|
||||||
p.Uint8ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint8ListVar(target *[]uint8) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newUint8Value(v.(*uint8))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- uint16 Value
|
|
||||||
type uint16Value struct{ v *uint16 }
|
|
||||||
|
|
||||||
func newUint16Value(p *uint16) *uint16Value {
|
|
||||||
return &uint16Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint16Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseUint(s, 0, 16)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (uint16)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint16Value) Get() interface{} { return (uint16)(*f.v) }
|
|
||||||
|
|
||||||
func (f *uint16Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Uint16 parses the next command-line value as uint16.
|
|
||||||
func (p *parserMixin) Uint16() (target *uint16) {
|
|
||||||
target = new(uint16)
|
|
||||||
p.Uint16Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint16Var(target *uint16) {
|
|
||||||
p.SetValue(newUint16Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint16List accumulates uint16 values into a slice.
|
|
||||||
func (p *parserMixin) Uint16List() (target *[]uint16) {
|
|
||||||
target = new([]uint16)
|
|
||||||
p.Uint16ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint16ListVar(target *[]uint16) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newUint16Value(v.(*uint16))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- uint32 Value
|
|
||||||
type uint32Value struct{ v *uint32 }
|
|
||||||
|
|
||||||
func newUint32Value(p *uint32) *uint32Value {
|
|
||||||
return &uint32Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint32Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseUint(s, 0, 32)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (uint32)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint32Value) Get() interface{} { return (uint32)(*f.v) }
|
|
||||||
|
|
||||||
func (f *uint32Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Uint32 parses the next command-line value as uint32.
|
|
||||||
func (p *parserMixin) Uint32() (target *uint32) {
|
|
||||||
target = new(uint32)
|
|
||||||
p.Uint32Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint32Var(target *uint32) {
|
|
||||||
p.SetValue(newUint32Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint32List accumulates uint32 values into a slice.
|
|
||||||
func (p *parserMixin) Uint32List() (target *[]uint32) {
|
|
||||||
target = new([]uint32)
|
|
||||||
p.Uint32ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint32ListVar(target *[]uint32) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newUint32Value(v.(*uint32))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- uint64 Value
|
|
||||||
type uint64Value struct{ v *uint64 }
|
|
||||||
|
|
||||||
func newUint64Value(p *uint64) *uint64Value {
|
|
||||||
return &uint64Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint64Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseUint(s, 0, 64)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (uint64)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *uint64Value) Get() interface{} { return (uint64)(*f.v) }
|
|
||||||
|
|
||||||
func (f *uint64Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Uint64 parses the next command-line value as uint64.
|
|
||||||
func (p *parserMixin) Uint64() (target *uint64) {
|
|
||||||
target = new(uint64)
|
|
||||||
p.Uint64Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint64Var(target *uint64) {
|
|
||||||
p.SetValue(newUint64Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64List accumulates uint64 values into a slice.
|
|
||||||
func (p *parserMixin) Uint64List() (target *[]uint64) {
|
|
||||||
target = new([]uint64)
|
|
||||||
p.Uint64ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Uint64ListVar(target *[]uint64) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newUint64Value(v.(*uint64))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- int Value
|
|
||||||
type intValue struct{ v *int }
|
|
||||||
|
|
||||||
func newIntValue(p *int) *intValue {
|
|
||||||
return &intValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *intValue) Set(s string) error {
|
|
||||||
v, err := strconv.ParseFloat(s, 64)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (int)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *intValue) Get() interface{} { return (int)(*f.v) }
|
|
||||||
|
|
||||||
func (f *intValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Int parses the next command-line value as int.
|
|
||||||
func (p *parserMixin) Int() (target *int) {
|
|
||||||
target = new(int)
|
|
||||||
p.IntVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) IntVar(target *int) {
|
|
||||||
p.SetValue(newIntValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ints accumulates int values into a slice.
|
|
||||||
func (p *parserMixin) Ints() (target *[]int) {
|
|
||||||
target = new([]int)
|
|
||||||
p.IntsVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) IntsVar(target *[]int) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newIntValue(v.(*int))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- int8 Value
|
|
||||||
type int8Value struct{ v *int8 }
|
|
||||||
|
|
||||||
func newInt8Value(p *int8) *int8Value {
|
|
||||||
return &int8Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int8Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseInt(s, 0, 8)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (int8)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int8Value) Get() interface{} { return (int8)(*f.v) }
|
|
||||||
|
|
||||||
func (f *int8Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Int8 parses the next command-line value as int8.
|
|
||||||
func (p *parserMixin) Int8() (target *int8) {
|
|
||||||
target = new(int8)
|
|
||||||
p.Int8Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int8Var(target *int8) {
|
|
||||||
p.SetValue(newInt8Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int8List accumulates int8 values into a slice.
|
|
||||||
func (p *parserMixin) Int8List() (target *[]int8) {
|
|
||||||
target = new([]int8)
|
|
||||||
p.Int8ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int8ListVar(target *[]int8) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newInt8Value(v.(*int8))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- int16 Value
|
|
||||||
type int16Value struct{ v *int16 }
|
|
||||||
|
|
||||||
func newInt16Value(p *int16) *int16Value {
|
|
||||||
return &int16Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int16Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseInt(s, 0, 16)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (int16)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int16Value) Get() interface{} { return (int16)(*f.v) }
|
|
||||||
|
|
||||||
func (f *int16Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Int16 parses the next command-line value as int16.
|
|
||||||
func (p *parserMixin) Int16() (target *int16) {
|
|
||||||
target = new(int16)
|
|
||||||
p.Int16Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int16Var(target *int16) {
|
|
||||||
p.SetValue(newInt16Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int16List accumulates int16 values into a slice.
|
|
||||||
func (p *parserMixin) Int16List() (target *[]int16) {
|
|
||||||
target = new([]int16)
|
|
||||||
p.Int16ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int16ListVar(target *[]int16) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newInt16Value(v.(*int16))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- int32 Value
|
|
||||||
type int32Value struct{ v *int32 }
|
|
||||||
|
|
||||||
func newInt32Value(p *int32) *int32Value {
|
|
||||||
return &int32Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int32Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseInt(s, 0, 32)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (int32)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int32Value) Get() interface{} { return (int32)(*f.v) }
|
|
||||||
|
|
||||||
func (f *int32Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Int32 parses the next command-line value as int32.
|
|
||||||
func (p *parserMixin) Int32() (target *int32) {
|
|
||||||
target = new(int32)
|
|
||||||
p.Int32Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int32Var(target *int32) {
|
|
||||||
p.SetValue(newInt32Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int32List accumulates int32 values into a slice.
|
|
||||||
func (p *parserMixin) Int32List() (target *[]int32) {
|
|
||||||
target = new([]int32)
|
|
||||||
p.Int32ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int32ListVar(target *[]int32) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newInt32Value(v.(*int32))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- int64 Value
|
|
||||||
type int64Value struct{ v *int64 }
|
|
||||||
|
|
||||||
func newInt64Value(p *int64) *int64Value {
|
|
||||||
return &int64Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int64Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseInt(s, 0, 64)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (int64)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *int64Value) Get() interface{} { return (int64)(*f.v) }
|
|
||||||
|
|
||||||
func (f *int64Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Int64 parses the next command-line value as int64.
|
|
||||||
func (p *parserMixin) Int64() (target *int64) {
|
|
||||||
target = new(int64)
|
|
||||||
p.Int64Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int64Var(target *int64) {
|
|
||||||
p.SetValue(newInt64Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64List accumulates int64 values into a slice.
|
|
||||||
func (p *parserMixin) Int64List() (target *[]int64) {
|
|
||||||
target = new([]int64)
|
|
||||||
p.Int64ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Int64ListVar(target *[]int64) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newInt64Value(v.(*int64))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- float64 Value
|
|
||||||
type float64Value struct{ v *float64 }
|
|
||||||
|
|
||||||
func newFloat64Value(p *float64) *float64Value {
|
|
||||||
return &float64Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *float64Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseFloat(s, 64)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (float64)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *float64Value) Get() interface{} { return (float64)(*f.v) }
|
|
||||||
|
|
||||||
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Float64 parses the next command-line value as float64.
|
|
||||||
func (p *parserMixin) Float64() (target *float64) {
|
|
||||||
target = new(float64)
|
|
||||||
p.Float64Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Float64Var(target *float64) {
|
|
||||||
p.SetValue(newFloat64Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64List accumulates float64 values into a slice.
|
|
||||||
func (p *parserMixin) Float64List() (target *[]float64) {
|
|
||||||
target = new([]float64)
|
|
||||||
p.Float64ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Float64ListVar(target *[]float64) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newFloat64Value(v.(*float64))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- float32 Value
|
|
||||||
type float32Value struct{ v *float32 }
|
|
||||||
|
|
||||||
func newFloat32Value(p *float32) *float32Value {
|
|
||||||
return &float32Value{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *float32Value) Set(s string) error {
|
|
||||||
v, err := strconv.ParseFloat(s, 32)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (float32)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *float32Value) Get() interface{} { return (float32)(*f.v) }
|
|
||||||
|
|
||||||
func (f *float32Value) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Float32 parses the next command-line value as float32.
|
|
||||||
func (p *parserMixin) Float32() (target *float32) {
|
|
||||||
target = new(float32)
|
|
||||||
p.Float32Var(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Float32Var(target *float32) {
|
|
||||||
p.SetValue(newFloat32Value(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float32List accumulates float32 values into a slice.
|
|
||||||
func (p *parserMixin) Float32List() (target *[]float32) {
|
|
||||||
target = new([]float32)
|
|
||||||
p.Float32ListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) Float32ListVar(target *[]float32) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newFloat32Value(v.(*float32))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DurationList accumulates time.Duration values into a slice.
|
|
||||||
func (p *parserMixin) DurationList() (target *[]time.Duration) {
|
|
||||||
target = new([]time.Duration)
|
|
||||||
p.DurationListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) DurationListVar(target *[]time.Duration) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newDurationValue(v.(*time.Duration))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPList accumulates net.IP values into a slice.
|
|
||||||
func (p *parserMixin) IPList() (target *[]net.IP) {
|
|
||||||
target = new([]net.IP)
|
|
||||||
p.IPListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) IPListVar(target *[]net.IP) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newIPValue(v.(*net.IP))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCPList accumulates *net.TCPAddr values into a slice.
|
|
||||||
func (p *parserMixin) TCPList() (target *[]*net.TCPAddr) {
|
|
||||||
target = new([]*net.TCPAddr)
|
|
||||||
p.TCPListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) TCPListVar(target *[]*net.TCPAddr) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newTCPAddrValue(v.(**net.TCPAddr))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingFiles accumulates string values into a slice.
|
|
||||||
func (p *parserMixin) ExistingFiles() (target *[]string) {
|
|
||||||
target = new([]string)
|
|
||||||
p.ExistingFilesVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) ExistingFilesVar(target *[]string) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newExistingFileValue(v.(*string))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingDirs accumulates string values into a slice.
|
|
||||||
func (p *parserMixin) ExistingDirs() (target *[]string) {
|
|
||||||
target = new([]string)
|
|
||||||
p.ExistingDirsVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) ExistingDirsVar(target *[]string) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newExistingDirValue(v.(*string))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistingFilesOrDirs accumulates string values into a slice.
|
|
||||||
func (p *parserMixin) ExistingFilesOrDirs() (target *[]string) {
|
|
||||||
target = new([]string)
|
|
||||||
p.ExistingFilesOrDirsVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) ExistingFilesOrDirsVar(target *[]string) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newExistingFileOrDirValue(v.(*string))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- *regexp.Regexp Value
|
|
||||||
type regexpValue struct{ v **regexp.Regexp }
|
|
||||||
|
|
||||||
func newRegexpValue(p **regexp.Regexp) *regexpValue {
|
|
||||||
return ®expValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *regexpValue) Set(s string) error {
|
|
||||||
v, err := regexp.Compile(s)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (*regexp.Regexp)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *regexpValue) Get() interface{} { return (*regexp.Regexp)(*f.v) }
|
|
||||||
|
|
||||||
func (f *regexpValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Regexp parses the next command-line value as *regexp.Regexp.
|
|
||||||
func (p *parserMixin) Regexp() (target **regexp.Regexp) {
|
|
||||||
target = new(*regexp.Regexp)
|
|
||||||
p.RegexpVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) RegexpVar(target **regexp.Regexp) {
|
|
||||||
p.SetValue(newRegexpValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegexpList accumulates *regexp.Regexp values into a slice.
|
|
||||||
func (p *parserMixin) RegexpList() (target *[]*regexp.Regexp) {
|
|
||||||
target = new([]*regexp.Regexp)
|
|
||||||
p.RegexpListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) RegexpListVar(target *[]*regexp.Regexp) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newRegexpValue(v.(**regexp.Regexp))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- net.IP Value
|
|
||||||
type resolvedIPValue struct{ v *net.IP }
|
|
||||||
|
|
||||||
func newResolvedIPValue(p *net.IP) *resolvedIPValue {
|
|
||||||
return &resolvedIPValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *resolvedIPValue) Set(s string) error {
|
|
||||||
v, err := resolveHost(s)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = (net.IP)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *resolvedIPValue) Get() interface{} { return (net.IP)(*f.v) }
|
|
||||||
|
|
||||||
func (f *resolvedIPValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Resolve a hostname or IP to an IP.
|
|
||||||
func (p *parserMixin) ResolvedIP() (target *net.IP) {
|
|
||||||
target = new(net.IP)
|
|
||||||
p.ResolvedIPVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) ResolvedIPVar(target *net.IP) {
|
|
||||||
p.SetValue(newResolvedIPValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolvedIPList accumulates net.IP values into a slice.
|
|
||||||
func (p *parserMixin) ResolvedIPList() (target *[]net.IP) {
|
|
||||||
target = new([]net.IP)
|
|
||||||
p.ResolvedIPListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) ResolvedIPListVar(target *[]net.IP) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newResolvedIPValue(v.(*net.IP))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- []byte Value
|
|
||||||
type hexBytesValue struct{ v *[]byte }
|
|
||||||
|
|
||||||
func newHexBytesValue(p *[]byte) *hexBytesValue {
|
|
||||||
return &hexBytesValue{p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *hexBytesValue) Set(s string) error {
|
|
||||||
v, err := hex.DecodeString(s)
|
|
||||||
if err == nil {
|
|
||||||
*f.v = ([]byte)(v)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *hexBytesValue) Get() interface{} { return ([]byte)(*f.v) }
|
|
||||||
|
|
||||||
func (f *hexBytesValue) String() string { return fmt.Sprintf("%v", *f.v) }
|
|
||||||
|
|
||||||
// Bytes as a hex string.
|
|
||||||
func (p *parserMixin) HexBytes() (target *[]byte) {
|
|
||||||
target = new([]byte)
|
|
||||||
p.HexBytesVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) HexBytesVar(target *[]byte) {
|
|
||||||
p.SetValue(newHexBytesValue(target))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HexBytesList accumulates []byte values into a slice.
|
|
||||||
func (p *parserMixin) HexBytesList() (target *[][]byte) {
|
|
||||||
target = new([][]byte)
|
|
||||||
p.HexBytesListVar(target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parserMixin) HexBytesListVar(target *[][]byte) {
|
|
||||||
p.SetValue(newAccumulator(target, func(v interface{}) Value {
|
|
||||||
return newHexBytesValue(v.(*[]byte))
|
|
||||||
}))
|
|
||||||
}
|
|
Loading…
Reference in a new issue