Add the sprig functions in the template engine
This commit is contained in:
parent
ff11467022
commit
7ff6c32452
42 changed files with 5671 additions and 50 deletions
18
docs/toml.md
18
docs/toml.md
|
@ -613,6 +613,24 @@ If you want Træfik to watch file changes automatically, just add:
|
||||||
watch = true
|
watch = true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The configuration files can be also templates written using functions provided by [go template](https://golang.org/pkg/text/template/) as well as functions provided by the [sprig library](http://masterminds.github.io/sprig/), like this:
|
||||||
|
|
||||||
|
```tmpl
|
||||||
|
[backends]
|
||||||
|
[backends.backend1]
|
||||||
|
url = "http://firstserver"
|
||||||
|
[backends.backend2]
|
||||||
|
url = "http://secondserver"
|
||||||
|
|
||||||
|
{{$frontends := dict "frontend1" "backend1" "frontend2" "backend2"}}
|
||||||
|
[frontends]
|
||||||
|
{{range $frontend, $backend := $frontends}}
|
||||||
|
[frontends.{{$frontend}}]
|
||||||
|
backend = "{{$backend}}"
|
||||||
|
{{end}}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## API backend
|
## API backend
|
||||||
|
|
||||||
Træfik can be configured using a RESTful api.
|
Træfik can be configured using a RESTful api.
|
||||||
|
|
16
glide.lock
generated
16
glide.lock
generated
|
@ -1,5 +1,5 @@
|
||||||
hash: 1cb8a7bc9dcca526370e84514c3f0c76b342e25d40ba584826d22f7e93be584c
|
hash: b0b29ccc1d46dcfb5a689d225fdd35ea3ffebb9e149bf0081229033cb515ad45
|
||||||
updated: 2017-08-01T19:11:22.221029923+02:00
|
updated: 2017-08-10T19:56:04.346751095+02:00
|
||||||
imports:
|
imports:
|
||||||
- name: cloud.google.com/go
|
- name: cloud.google.com/go
|
||||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||||
|
@ -8,6 +8,8 @@ imports:
|
||||||
- internal
|
- internal
|
||||||
- name: github.com/abbot/go-http-auth
|
- name: github.com/abbot/go-http-auth
|
||||||
version: 0ddd408d5d60ea76e320503cc7dd091992dee608
|
version: 0ddd408d5d60ea76e320503cc7dd091992dee608
|
||||||
|
- name: github.com/aokoli/goutils
|
||||||
|
version: 3391d3790d23d03408670993e957e8f408993c34
|
||||||
- name: github.com/ArthurHlt/go-eureka-client
|
- name: github.com/ArthurHlt/go-eureka-client
|
||||||
version: 9d0a49cbd39aa3634ae1977e9f519a262b10adaf
|
version: 9d0a49cbd39aa3634ae1977e9f519a262b10adaf
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -300,6 +302,10 @@ imports:
|
||||||
version: 19f2c401e122352c047a84d6584dd51e2fb8fcc4
|
version: 19f2c401e122352c047a84d6584dd51e2fb8fcc4
|
||||||
subpackages:
|
subpackages:
|
||||||
- coordinate
|
- coordinate
|
||||||
|
- name: github.com/huandu/xstrings
|
||||||
|
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
||||||
|
- name: github.com/imdario/mergo
|
||||||
|
version: 3e95a51e0639b4cf372f2ccf74c86749d747fbdc
|
||||||
- name: github.com/JamesClonk/vultr
|
- name: github.com/JamesClonk/vultr
|
||||||
version: 0f156dd232bc4ebf8a32ba83fec57c0e4c9db69f
|
version: 0f156dd232bc4ebf8a32ba83fec57c0e4c9db69f
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -320,6 +326,10 @@ imports:
|
||||||
- buffer
|
- buffer
|
||||||
- jlexer
|
- jlexer
|
||||||
- jwriter
|
- jwriter
|
||||||
|
- name: github.com/Masterminds/semver
|
||||||
|
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
||||||
|
- name: github.com/Masterminds/sprig
|
||||||
|
version: 9526be0327b26ad31aa70296a7b10704883976d5
|
||||||
- name: github.com/mattn/go-colorable
|
- name: github.com/mattn/go-colorable
|
||||||
version: 5411d3eea5978e6cdc258b30de592b60df6aba96
|
version: 5411d3eea5978e6cdc258b30de592b60df6aba96
|
||||||
repo: https://github.com/mattn/go-colorable
|
repo: https://github.com/mattn/go-colorable
|
||||||
|
@ -520,6 +530,8 @@ imports:
|
||||||
- bcrypt
|
- bcrypt
|
||||||
- blowfish
|
- blowfish
|
||||||
- ocsp
|
- ocsp
|
||||||
|
- pbkdf2
|
||||||
|
- scrypt
|
||||||
- name: golang.org/x/net
|
- name: golang.org/x/net
|
||||||
version: 242b6b35177ec3909636b6cf6a47e8c2c6324b5d
|
version: 242b6b35177ec3909636b6cf6a47e8c2c6324b5d
|
||||||
subpackages:
|
subpackages:
|
||||||
|
|
|
@ -198,6 +198,8 @@ import:
|
||||||
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
|
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
|
||||||
subpackages:
|
subpackages:
|
||||||
- spew
|
- spew
|
||||||
|
- package: github.com/Masterminds/sprig
|
||||||
|
version: e039e20e500c2c025d9145be375e27cf42a94174
|
||||||
testImport:
|
testImport:
|
||||||
- package: github.com/stvp/go-udp-testing
|
- package: github.com/stvp/go-udp-testing
|
||||||
- package: github.com/docker/libcompose
|
- package: github.com/docker/libcompose
|
||||||
|
|
|
@ -351,7 +351,7 @@ func (p *Provider) getBackend(application marathon.Application) string {
|
||||||
if label, ok := p.getLabel(application, types.LabelBackend); ok {
|
if label, ok := p.getLabel(application, types.LabelBackend); ok {
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
return provider.Replace("/", "-", application.ID)
|
return strings.Replace(application.ID, "/", "-", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getSubDomain(name string) string {
|
func (p *Provider) getSubDomain(name string) string {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/Masterminds/sprig"
|
||||||
"github.com/containous/traefik/autogen"
|
"github.com/containous/traefik/autogen"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/safe"
|
"github.com/containous/traefik/safe"
|
||||||
|
@ -59,14 +60,12 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
configuration := new(types.Configuration)
|
configuration := new(types.Configuration)
|
||||||
var defaultFuncMap = template.FuncMap{
|
|
||||||
"replace": Replace,
|
|
||||||
"tolower": strings.ToLower,
|
|
||||||
"normalize": Normalize,
|
|
||||||
"split": split,
|
|
||||||
"contains": contains,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var defaultFuncMap = sprig.TxtFuncMap()
|
||||||
|
// tolower is deprecated in favor of sprig's lower function
|
||||||
|
defaultFuncMap["tolower"] = strings.ToLower
|
||||||
|
defaultFuncMap["normalize"] = Normalize
|
||||||
|
defaultFuncMap["split"] = split
|
||||||
for funcID, funcElement := range funcMap {
|
for funcID, funcElement := range funcMap {
|
||||||
defaultFuncMap[funcID] = funcElement
|
defaultFuncMap[funcID] = funcElement
|
||||||
}
|
}
|
||||||
|
@ -102,15 +101,6 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
|
||||||
return configuration, nil
|
return configuration, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace is an alias for strings.Replace
|
|
||||||
func Replace(s1 string, s2 string, s3 string) string {
|
|
||||||
return strings.Replace(s3, s1, s2, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(substr, s string) bool {
|
|
||||||
return strings.Contains(s, substr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func split(sep, s string) []string {
|
func split(sep, s string) []string {
|
||||||
return strings.Split(s, sep)
|
return strings.Split(s, sep)
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,37 +145,6 @@ func TestGetConfiguration(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReplace(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
str string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
str: "",
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
str: "foo",
|
|
||||||
expected: "bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
str: "foo foo",
|
|
||||||
expected: "bar bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
str: "somethingfoo",
|
|
||||||
expected: "somethingbar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
actual := Replace("foo", "bar", c.str)
|
|
||||||
if actual != c.expected {
|
|
||||||
t.Fatalf("expected %q, got %q, for %q", c.expected, actual, c.str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetConfigurationReturnsCorrectMaxConnConfiguration(t *testing.T) {
|
func TestGetConfigurationReturnsCorrectMaxConnConfiguration(t *testing.T) {
|
||||||
templateFile, err := ioutil.TempFile("", "provider-configuration")
|
templateFile, err := ioutil.TempFile("", "provider-configuration")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -380,3 +349,51 @@ func TestDefaultFuncMap(t *testing.T) {
|
||||||
t.Fatal("Frontend frontend-1 should exists, but it not")
|
t.Fatal("Frontend frontend-1 should exists, but it not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSprigFunctions(t *testing.T) {
|
||||||
|
templateFile, err := ioutil.TempFile("", "provider-configuration")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(templateFile.Name())
|
||||||
|
data := []byte(`
|
||||||
|
{{$backend_name := trimAll "-" uuidv4}}
|
||||||
|
[backends]
|
||||||
|
[backends.{{$backend_name}}]
|
||||||
|
[backends.{{$backend_name}}.circuitbreaker]
|
||||||
|
[backends.{{$backend_name}}.servers.server2]
|
||||||
|
url = "http://172.17.0.3:80"
|
||||||
|
weight = 1
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.{{normalize "frontend/1"}}]
|
||||||
|
backend = "{{$backend_name}}"
|
||||||
|
passHostHeader = true
|
||||||
|
[frontends.frontend-1.routes.test_2]
|
||||||
|
rule = "Path"
|
||||||
|
value = "/test"`)
|
||||||
|
err = ioutil.WriteFile(templateFile.Name(), data, 0700)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := &myProvider{
|
||||||
|
BaseProvider{
|
||||||
|
Filename: templateFile.Name(),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
configuration, err := provider.GetConfiguration(templateFile.Name(), nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Shouldn't have error out, got %v", err)
|
||||||
|
}
|
||||||
|
if configuration == nil {
|
||||||
|
t.Fatal("Configuration should not be nil, but was")
|
||||||
|
}
|
||||||
|
if len(configuration.Backends) != 1 {
|
||||||
|
t.Fatal("one backend should be defined, but it's not")
|
||||||
|
}
|
||||||
|
if _, ok := configuration.Frontends["frontend-1"]; !ok {
|
||||||
|
t.Fatal("Frontend frontend-1 should exists, but it not")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
20
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
Normal file
20
vendor/github.com/Masterminds/semver/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
The Masterminds
|
||||||
|
Copyright (C) 2014-2015, Matt Butcher and Matt Farina
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
Normal file
24
vendor/github.com/Masterminds/semver/collection.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package semver
|
||||||
|
|
||||||
|
// Collection is a collection of Version instances and implements the sort
|
||||||
|
// interface. See the sort package for more details.
|
||||||
|
// https://golang.org/pkg/sort/
|
||||||
|
type Collection []*Version
|
||||||
|
|
||||||
|
// Len returns the length of a collection. The number of Version instances
|
||||||
|
// on the slice.
|
||||||
|
func (c Collection) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is needed for the sort interface to compare two Version objects on the
|
||||||
|
// slice. If checks if one is less than the other.
|
||||||
|
func (c Collection) Less(i, j int) bool {
|
||||||
|
return c[i].LessThan(c[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is needed for the sort interface to replace the Version objects
|
||||||
|
// at two different positions in the slice.
|
||||||
|
func (c Collection) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
421
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
Normal file
421
vendor/github.com/Masterminds/semver/constraints.go
generated
vendored
Normal file
|
@ -0,0 +1,421 @@
|
||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constraints is one or more constraint that a semantic version can be
|
||||||
|
// checked against.
|
||||||
|
type Constraints struct {
|
||||||
|
constraints [][]*constraint
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConstraint returns a Constraints instance that a Version instance can
|
||||||
|
// be checked against. If there is a parse error it will be returned.
|
||||||
|
func NewConstraint(c string) (*Constraints, error) {
|
||||||
|
|
||||||
|
// Rewrite - ranges into a comparison operation.
|
||||||
|
c = rewriteRange(c)
|
||||||
|
|
||||||
|
ors := strings.Split(c, "||")
|
||||||
|
or := make([][]*constraint, len(ors))
|
||||||
|
for k, v := range ors {
|
||||||
|
cs := strings.Split(v, ",")
|
||||||
|
result := make([]*constraint, len(cs))
|
||||||
|
for i, s := range cs {
|
||||||
|
pc, err := parseConstraint(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result[i] = pc
|
||||||
|
}
|
||||||
|
or[k] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
o := &Constraints{constraints: or}
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tests if a version satisfies the constraints.
|
||||||
|
func (cs Constraints) Check(v *Version) bool {
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
if !c.check(v) {
|
||||||
|
joy = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks if a version satisfies a constraint. If not a slice of
|
||||||
|
// reasons for the failure are returned in addition to a bool.
|
||||||
|
func (cs Constraints) Validate(v *Version) (bool, []error) {
|
||||||
|
// loop over the ORs and check the inner ANDs
|
||||||
|
var e []error
|
||||||
|
for _, o := range cs.constraints {
|
||||||
|
joy := true
|
||||||
|
for _, c := range o {
|
||||||
|
if !c.check(v) {
|
||||||
|
em := fmt.Errorf(c.msg, v, c.orig)
|
||||||
|
e = append(e, em)
|
||||||
|
joy = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if joy {
|
||||||
|
return true, []error{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintOps map[string]cfunc
|
||||||
|
var constraintMsg map[string]string
|
||||||
|
var constraintRegex *regexp.Regexp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
constraintOps = map[string]cfunc{
|
||||||
|
"": constraintTildeOrEqual,
|
||||||
|
"=": constraintTildeOrEqual,
|
||||||
|
"!=": constraintNotEqual,
|
||||||
|
">": constraintGreaterThan,
|
||||||
|
"<": constraintLessThan,
|
||||||
|
">=": constraintGreaterThanEqual,
|
||||||
|
"=>": constraintGreaterThanEqual,
|
||||||
|
"<=": constraintLessThanEqual,
|
||||||
|
"=<": constraintLessThanEqual,
|
||||||
|
"~": constraintTilde,
|
||||||
|
"~>": constraintTilde,
|
||||||
|
"^": constraintCaret,
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintMsg = map[string]string{
|
||||||
|
"": "%s is not equal to %s",
|
||||||
|
"=": "%s is not equal to %s",
|
||||||
|
"!=": "%s is equal to %s",
|
||||||
|
">": "%s is less than or equal to %s",
|
||||||
|
"<": "%s is greater than or equal to %s",
|
||||||
|
">=": "%s is less than %s",
|
||||||
|
"=>": "%s is less than %s",
|
||||||
|
"<=": "%s is greater than %s",
|
||||||
|
"=<": "%s is greater than %s",
|
||||||
|
"~": "%s does not have same major and minor version as %s",
|
||||||
|
"~>": "%s does not have same major and minor version as %s",
|
||||||
|
"^": "%s does not have same major version as %s",
|
||||||
|
}
|
||||||
|
|
||||||
|
ops := make([]string, 0, len(constraintOps))
|
||||||
|
for k := range constraintOps {
|
||||||
|
ops = append(ops, regexp.QuoteMeta(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
constraintRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`^\s*(%s)\s*(%s)\s*$`,
|
||||||
|
strings.Join(ops, "|"),
|
||||||
|
cvRegex))
|
||||||
|
|
||||||
|
constraintRangeRegex = regexp.MustCompile(fmt.Sprintf(
|
||||||
|
`\s*(%s)\s+-\s+(%s)\s*`,
|
||||||
|
cvRegex, cvRegex))
|
||||||
|
}
|
||||||
|
|
||||||
|
// An individual constraint
|
||||||
|
type constraint struct {
|
||||||
|
// The callback function for the restraint. It performs the logic for
|
||||||
|
// the constraint.
|
||||||
|
function cfunc
|
||||||
|
|
||||||
|
msg string
|
||||||
|
|
||||||
|
// The version used in the constraint check. For example, if a constraint
|
||||||
|
// is '<= 2.0.0' the con a version instance representing 2.0.0.
|
||||||
|
con *Version
|
||||||
|
|
||||||
|
// The original parsed version (e.g., 4.x from != 4.x)
|
||||||
|
orig string
|
||||||
|
|
||||||
|
// When an x is used as part of the version (e.g., 1.x)
|
||||||
|
minorDirty bool
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a version meets the constraint
|
||||||
|
func (c *constraint) check(v *Version) bool {
|
||||||
|
return c.function(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cfunc func(v *Version, c *constraint) bool
|
||||||
|
|
||||||
|
func parseConstraint(c string) (*constraint, error) {
|
||||||
|
m := constraintRegex.FindStringSubmatch(c)
|
||||||
|
if m == nil {
|
||||||
|
return nil, fmt.Errorf("improper constraint: %s", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
ver := m[2]
|
||||||
|
orig := ver
|
||||||
|
minorDirty := false
|
||||||
|
dirty := false
|
||||||
|
if isX(m[3]) {
|
||||||
|
ver = "0.0.0"
|
||||||
|
dirty = true
|
||||||
|
} else if isX(strings.TrimPrefix(m[4], ".")) {
|
||||||
|
minorDirty = true
|
||||||
|
dirty = true
|
||||||
|
ver = fmt.Sprintf("%s.0.0%s", m[3], m[6])
|
||||||
|
} else if isX(strings.TrimPrefix(m[5], ".")) {
|
||||||
|
dirty = true
|
||||||
|
ver = fmt.Sprintf("%s%s.0%s", m[3], m[4], m[6])
|
||||||
|
}
|
||||||
|
|
||||||
|
con, err := NewVersion(ver)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
// The constraintRegex should catch any regex parsing errors. So,
|
||||||
|
// we should never get here.
|
||||||
|
return nil, errors.New("constraint Parser Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := &constraint{
|
||||||
|
function: constraintOps[m[1]],
|
||||||
|
msg: constraintMsg[m[1]],
|
||||||
|
con: con,
|
||||||
|
orig: orig,
|
||||||
|
minorDirty: minorDirty,
|
||||||
|
dirty: dirty,
|
||||||
|
}
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constraint functions
|
||||||
|
func constraintNotEqual(v *Version, c *constraint) bool {
|
||||||
|
if c.dirty {
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.con.Major() != v.Major() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.con.Minor() != v.Minor() && !c.minorDirty {
|
||||||
|
return true
|
||||||
|
} else if c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !v.Equal(c.con)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThan(v *Version, c *constraint) bool {
|
||||||
|
|
||||||
|
// An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease
|
||||||
|
// exists. This that case.
|
||||||
|
if !isNonZero(c.con) && isNonZero(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Compare(c.con) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThan(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
return v.Compare(c.con) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return false
|
||||||
|
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintGreaterThanEqual(v *Version, c *constraint) bool {
|
||||||
|
// An edge case the constraint is 0.0.0 and the version is 0.0.0-someprerelease
|
||||||
|
// exists. This that case.
|
||||||
|
if !isNonZero(c.con) && isNonZero(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Compare(c.con) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func constraintLessThanEqual(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.dirty {
|
||||||
|
return v.Compare(c.con) <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() > c.con.Major() {
|
||||||
|
return false
|
||||||
|
} else if v.Minor() > c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~*, ~>* --> >= 0.0.0 (any)
|
||||||
|
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
|
||||||
|
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
|
||||||
|
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
|
||||||
|
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
|
||||||
|
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
|
||||||
|
func constraintTilde(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~0.0.0 is a special case where all constraints are accepted. It's
|
||||||
|
// equivalent to >= 0.0.0.
|
||||||
|
if c.con.Major() == 0 && c.con.Minor() == 0 && c.con.Patch() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() != c.con.Major() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Minor() != c.con.Minor() && !c.minorDirty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
|
||||||
|
// it's a straight =
|
||||||
|
func constraintTildeOrEqual(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.dirty {
|
||||||
|
c.msg = constraintMsg["~"]
|
||||||
|
return constraintTilde(v, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Equal(c.con)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ^* --> (any)
|
||||||
|
// ^2, ^2.x, ^2.x.x --> >=2.0.0, <3.0.0
|
||||||
|
// ^2.0, ^2.0.x --> >=2.0.0, <3.0.0
|
||||||
|
// ^1.2, ^1.2.x --> >=1.2.0, <2.0.0
|
||||||
|
// ^1.2.3 --> >=1.2.3, <2.0.0
|
||||||
|
// ^1.2.0 --> >=1.2.0, <2.0.0
|
||||||
|
func constraintCaret(v *Version, c *constraint) bool {
|
||||||
|
// If there is a pre-release on the version but the constraint isn't looking
|
||||||
|
// for them assume that pre-releases are not compatible. See issue 21 for
|
||||||
|
// more details.
|
||||||
|
if v.Prerelease() != "" && c.con.Prerelease() == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.LessThan(c.con) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Major() != c.con.Major() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var constraintRangeRegex *regexp.Regexp
|
||||||
|
|
||||||
|
const cvRegex string = `v?([0-9|x|X|\*]+)(\.[0-9|x|X|\*]+)?(\.[0-9|x|X|\*]+)?` +
|
||||||
|
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||||
|
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||||
|
|
||||||
|
func isX(x string) bool {
|
||||||
|
switch x {
|
||||||
|
case "x", "*", "X":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteRange(i string) string {
|
||||||
|
m := constraintRangeRegex.FindAllStringSubmatch(i, -1)
|
||||||
|
if m == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
o := i
|
||||||
|
for _, v := range m {
|
||||||
|
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
|
||||||
|
o = strings.Replace(o, v[0], t, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect if a version is not zero (0.0.0)
|
||||||
|
func isNonZero(v *Version) bool {
|
||||||
|
if v.Major() != 0 || v.Minor() != 0 || v.Patch() != 0 || v.Prerelease() != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
Normal file
115
vendor/github.com/Masterminds/semver/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
Package semver provides the ability to work with Semantic Versions (http://semver.org) in Go.
|
||||||
|
|
||||||
|
Specifically it provides the ability to:
|
||||||
|
|
||||||
|
* Parse semantic versions
|
||||||
|
* Sort semantic versions
|
||||||
|
* Check if a semantic version fits within a set of constraints
|
||||||
|
* Optionally work with a `v` prefix
|
||||||
|
|
||||||
|
Parsing Semantic Versions
|
||||||
|
|
||||||
|
To parse a semantic version use the `NewVersion` function. For example,
|
||||||
|
|
||||||
|
v, err := semver.NewVersion("1.2.3-beta.1+build345")
|
||||||
|
|
||||||
|
If there is an error the version wasn't parseable. The version object has methods
|
||||||
|
to get the parts of the version, compare it to other versions, convert the
|
||||||
|
version back into a string, and get the original string. For more details
|
||||||
|
please see the documentation at https://godoc.org/github.com/Masterminds/semver.
|
||||||
|
|
||||||
|
Sorting Semantic Versions
|
||||||
|
|
||||||
|
A set of versions can be sorted using the `sort` package from the standard library.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
|
||||||
|
vs := make([]*semver.Version, len(raw))
|
||||||
|
for i, r := range raw {
|
||||||
|
v, err := semver.NewVersion(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing version: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(semver.Collection(vs))
|
||||||
|
|
||||||
|
Checking Version Constraints
|
||||||
|
|
||||||
|
Checking a version against version constraints is one of the most featureful
|
||||||
|
parts of the package.
|
||||||
|
|
||||||
|
c, err := semver.NewConstraint(">= 1.2.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle constraint not being parseable.
|
||||||
|
}
|
||||||
|
|
||||||
|
v, _ := semver.NewVersion("1.3")
|
||||||
|
if err != nil {
|
||||||
|
// Handle version not being parseable.
|
||||||
|
}
|
||||||
|
// Check if the version meets the constraints. The a variable will be true.
|
||||||
|
a := c.Check(v)
|
||||||
|
|
||||||
|
Basic Comparisons
|
||||||
|
|
||||||
|
There are two elements to the comparisons. First, a comparison string is a list
|
||||||
|
of comma separated and comparisons. These are then separated by || separated or
|
||||||
|
comparisons. For example, `">= 1.2, < 3.0.0 || >= 4.2.3"` is looking for a
|
||||||
|
comparison that's greater than or equal to 1.2 and less than 3.0.0 or is
|
||||||
|
greater than or equal to 4.2.3.
|
||||||
|
|
||||||
|
The basic comparisons are:
|
||||||
|
|
||||||
|
* `=`: equal (aliased to no operator)
|
||||||
|
* `!=`: not equal
|
||||||
|
* `>`: greater than
|
||||||
|
* `<`: less than
|
||||||
|
* `>=`: greater than or equal to
|
||||||
|
* `<=`: less than or equal to
|
||||||
|
|
||||||
|
Hyphen Range Comparisons
|
||||||
|
|
||||||
|
There are multiple methods to handle ranges and the first is hyphens ranges.
|
||||||
|
These look like:
|
||||||
|
|
||||||
|
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
|
||||||
|
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4, <= 4.5`
|
||||||
|
|
||||||
|
Wildcards In Comparisons
|
||||||
|
|
||||||
|
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
|
||||||
|
for all comparison operators. When used on the `=` operator it falls
|
||||||
|
back to the pack level comparison (see tilde below). For example,
|
||||||
|
|
||||||
|
* `1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `>= 1.2.x` is equivalent to `>= 1.2.0`
|
||||||
|
* `<= 2.x` is equivalent to `<= 3`
|
||||||
|
* `*` is equivalent to `>= 0.0.0`
|
||||||
|
|
||||||
|
Tilde Range Comparisons (Patch)
|
||||||
|
|
||||||
|
The tilde (`~`) comparison operator is for patch level ranges when a minor
|
||||||
|
version is specified and major level changes when the minor number is missing.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
* `~1.2.3` is equivalent to `>= 1.2.3, < 1.3.0`
|
||||||
|
* `~1` is equivalent to `>= 1, < 2`
|
||||||
|
* `~2.3` is equivalent to `>= 2.3, < 2.4`
|
||||||
|
* `~1.2.x` is equivalent to `>= 1.2.0, < 1.3.0`
|
||||||
|
* `~1.x` is equivalent to `>= 1, < 2`
|
||||||
|
|
||||||
|
Caret Range Comparisons (Major)
|
||||||
|
|
||||||
|
The caret (`^`) comparison operator is for major level changes. This is useful
|
||||||
|
when comparisons of API versions as a major change is API breaking. For example,
|
||||||
|
|
||||||
|
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
|
||||||
|
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
|
||||||
|
* `^2.3` is equivalent to `>= 2.3, < 3`
|
||||||
|
* `^2.x` is equivalent to `>= 2.0.0, < 3`
|
||||||
|
*/
|
||||||
|
package semver
|
375
vendor/github.com/Masterminds/semver/version.go
generated
vendored
Normal file
375
vendor/github.com/Masterminds/semver/version.go
generated
vendored
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
package semver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The compiled version of the regex created at init() is cached here so it
|
||||||
|
// only needs to be created once.
|
||||||
|
var versionRegex *regexp.Regexp
|
||||||
|
var validPrereleaseRegex *regexp.Regexp
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidSemVer is returned a version is found to be invalid when
|
||||||
|
// being parsed.
|
||||||
|
ErrInvalidSemVer = errors.New("Invalid Semantic Version")
|
||||||
|
|
||||||
|
// ErrInvalidMetadata is returned when the metadata is an invalid format
|
||||||
|
ErrInvalidMetadata = errors.New("Invalid Metadata string")
|
||||||
|
|
||||||
|
// ErrInvalidPrerelease is returned when the pre-release is an invalid format
|
||||||
|
ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
|
||||||
|
)
|
||||||
|
|
||||||
|
// SemVerRegex is the regular expression used to parse a semantic version.
|
||||||
|
const SemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
|
||||||
|
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
|
||||||
|
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
|
||||||
|
|
||||||
|
// ValidPrerelease is the regular expression which validates
|
||||||
|
// both prerelease and metadata values.
|
||||||
|
const ValidPrerelease string = `^([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*)`
|
||||||
|
|
||||||
|
// Version represents a single semantic version.
|
||||||
|
type Version struct {
|
||||||
|
major, minor, patch int64
|
||||||
|
pre string
|
||||||
|
metadata string
|
||||||
|
original string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
versionRegex = regexp.MustCompile("^" + SemVerRegex + "$")
|
||||||
|
validPrereleaseRegex = regexp.MustCompile(ValidPrerelease)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVersion parses a given version and returns an instance of Version or
|
||||||
|
// an error if unable to parse the version.
|
||||||
|
func NewVersion(v string) (*Version, error) {
|
||||||
|
m := versionRegex.FindStringSubmatch(v)
|
||||||
|
if m == nil {
|
||||||
|
return nil, ErrInvalidSemVer
|
||||||
|
}
|
||||||
|
|
||||||
|
sv := &Version{
|
||||||
|
metadata: m[8],
|
||||||
|
pre: m[5],
|
||||||
|
original: v,
|
||||||
|
}
|
||||||
|
|
||||||
|
var temp int64
|
||||||
|
temp, err := strconv.ParseInt(m[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.major = temp
|
||||||
|
|
||||||
|
if m[2] != "" {
|
||||||
|
temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.minor = temp
|
||||||
|
} else {
|
||||||
|
sv.minor = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if m[3] != "" {
|
||||||
|
temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing version segment: %s", err)
|
||||||
|
}
|
||||||
|
sv.patch = temp
|
||||||
|
} else {
|
||||||
|
sv.patch = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return sv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParse parses a given version and panics on error.
|
||||||
|
func MustParse(v string) *Version {
|
||||||
|
sv, err := NewVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts a Version object to a string.
|
||||||
|
// Note, if the original version contained a leading v this version will not.
|
||||||
|
// See the Original() method to retrieve the original value. Semantic Versions
|
||||||
|
// don't contain a leading v per the spec. Instead it's optional on
|
||||||
|
// impelementation.
|
||||||
|
func (v *Version) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, "%d.%d.%d", v.major, v.minor, v.patch)
|
||||||
|
if v.pre != "" {
|
||||||
|
fmt.Fprintf(&buf, "-%s", v.pre)
|
||||||
|
}
|
||||||
|
if v.metadata != "" {
|
||||||
|
fmt.Fprintf(&buf, "+%s", v.metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original returns the original value passed in to be parsed.
|
||||||
|
func (v *Version) Original() string {
|
||||||
|
return v.original
|
||||||
|
}
|
||||||
|
|
||||||
|
// Major returns the major version.
|
||||||
|
func (v *Version) Major() int64 {
|
||||||
|
return v.major
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minor returns the minor version.
|
||||||
|
func (v *Version) Minor() int64 {
|
||||||
|
return v.minor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch returns the patch version.
|
||||||
|
func (v *Version) Patch() int64 {
|
||||||
|
return v.patch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prerelease returns the pre-release version.
|
||||||
|
func (v *Version) Prerelease() string {
|
||||||
|
return v.pre
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata returns the metadata on the version.
|
||||||
|
func (v *Version) Metadata() string {
|
||||||
|
return v.metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// originalVPrefix returns the original 'v' prefix if any.
|
||||||
|
func (v *Version) originalVPrefix() string {
|
||||||
|
|
||||||
|
// Note, only lowercase v is supported as a prefix by the parser.
|
||||||
|
if v.original != "" && v.original[:1] == "v" {
|
||||||
|
return v.original[:1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncPatch produces the next patch version.
|
||||||
|
// If the current version does not have prerelease/metadata information,
|
||||||
|
// it unsets metadata and prerelease values, increments patch number.
|
||||||
|
// If the current version has any of prerelease or metadata information,
|
||||||
|
// it unsets both values and keeps curent patch value
|
||||||
|
func (v Version) IncPatch() Version {
|
||||||
|
vNext := v
|
||||||
|
// according to http://semver.org/#spec-item-9
|
||||||
|
// Pre-release versions have a lower precedence than the associated normal version.
|
||||||
|
// according to http://semver.org/#spec-item-10
|
||||||
|
// Build metadata SHOULD be ignored when determining version precedence.
|
||||||
|
if v.pre != "" {
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
} else {
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = v.patch + 1
|
||||||
|
}
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncMinor produces the next minor version.
|
||||||
|
// Sets patch to 0.
|
||||||
|
// Increments minor number.
|
||||||
|
// Unsets metadata.
|
||||||
|
// Unsets prerelease status.
|
||||||
|
func (v Version) IncMinor() Version {
|
||||||
|
vNext := v
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = 0
|
||||||
|
vNext.minor = v.minor + 1
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncMajor produces the next major version.
|
||||||
|
// Sets patch to 0.
|
||||||
|
// Sets minor to 0.
|
||||||
|
// Increments major number.
|
||||||
|
// Unsets metadata.
|
||||||
|
// Unsets prerelease status.
|
||||||
|
func (v Version) IncMajor() Version {
|
||||||
|
vNext := v
|
||||||
|
vNext.metadata = ""
|
||||||
|
vNext.pre = ""
|
||||||
|
vNext.patch = 0
|
||||||
|
vNext.minor = 0
|
||||||
|
vNext.major = v.major + 1
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrerelease defines the prerelease value.
|
||||||
|
// Value must not include the required 'hypen' prefix.
|
||||||
|
func (v Version) SetPrerelease(prerelease string) (Version, error) {
|
||||||
|
vNext := v
|
||||||
|
if len(prerelease) > 0 && !validPrereleaseRegex.MatchString(prerelease) {
|
||||||
|
return vNext, ErrInvalidPrerelease
|
||||||
|
}
|
||||||
|
vNext.pre = prerelease
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMetadata defines metadata value.
|
||||||
|
// Value must not include the required 'plus' prefix.
|
||||||
|
func (v Version) SetMetadata(metadata string) (Version, error) {
|
||||||
|
vNext := v
|
||||||
|
if len(metadata) > 0 && !validPrereleaseRegex.MatchString(metadata) {
|
||||||
|
return vNext, ErrInvalidMetadata
|
||||||
|
}
|
||||||
|
vNext.metadata = metadata
|
||||||
|
vNext.original = v.originalVPrefix() + "" + vNext.String()
|
||||||
|
return vNext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan tests if one version is less than another one.
|
||||||
|
func (v *Version) LessThan(o *Version) bool {
|
||||||
|
return v.Compare(o) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThan tests if one version is greater than another one.
|
||||||
|
func (v *Version) GreaterThan(o *Version) bool {
|
||||||
|
return v.Compare(o) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal tests if two versions are equal to each other.
|
||||||
|
// Note, versions can be equal with different metadata since metadata
|
||||||
|
// is not considered part of the comparable version.
|
||||||
|
func (v *Version) Equal(o *Version) bool {
|
||||||
|
return v.Compare(o) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare compares this version to another one. It returns -1, 0, or 1 if
|
||||||
|
// the version smaller, equal, or larger than the other version.
|
||||||
|
//
|
||||||
|
// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is
|
||||||
|
// lower than the version without a prerelease.
|
||||||
|
func (v *Version) Compare(o *Version) int {
|
||||||
|
// Compare the major, minor, and patch version for differences. If a
|
||||||
|
// difference is found return the comparison.
|
||||||
|
if d := compareSegment(v.Major(), o.Major()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
if d := compareSegment(v.Minor(), o.Minor()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
if d := compareSegment(v.Patch(), o.Patch()); d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point the major, minor, and patch versions are the same.
|
||||||
|
ps := v.pre
|
||||||
|
po := o.Prerelease()
|
||||||
|
|
||||||
|
if ps == "" && po == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if ps == "" {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if po == "" {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return comparePrerelease(ps, po)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareSegment(v, o int64) int {
|
||||||
|
if v < o {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if v > o {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePrerelease(v, o string) int {
|
||||||
|
|
||||||
|
// split the prelease versions by their part. The separator, per the spec,
|
||||||
|
// is a .
|
||||||
|
sparts := strings.Split(v, ".")
|
||||||
|
oparts := strings.Split(o, ".")
|
||||||
|
|
||||||
|
// Find the longer length of the parts to know how many loop iterations to
|
||||||
|
// go through.
|
||||||
|
slen := len(sparts)
|
||||||
|
olen := len(oparts)
|
||||||
|
|
||||||
|
l := slen
|
||||||
|
if olen > slen {
|
||||||
|
l = olen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over each part of the prereleases to compare the differences.
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
// Since the lentgh of the parts can be different we need to create
|
||||||
|
// a placeholder. This is to avoid out of bounds issues.
|
||||||
|
stemp := ""
|
||||||
|
if i < slen {
|
||||||
|
stemp = sparts[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
otemp := ""
|
||||||
|
if i < olen {
|
||||||
|
otemp = oparts[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
d := comparePrePart(stemp, otemp)
|
||||||
|
if d != 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaching here means two versions are of equal value but have different
|
||||||
|
// metadata (the part following a +). They are not identical in string form
|
||||||
|
// but the version comparison finds them to be equal.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePrePart(s, o string) int {
|
||||||
|
// Fastpath if they are equal
|
||||||
|
if s == o {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// When s or o are empty we can use the other in an attempt to determine
|
||||||
|
// the response.
|
||||||
|
if o == "" {
|
||||||
|
_, n := strconv.ParseInt(s, 10, 64)
|
||||||
|
if n != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
_, n := strconv.ParseInt(o, 10, 64)
|
||||||
|
if n != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if s > o {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
20
vendor/github.com/Masterminds/sprig/LICENSE.txt
generated
vendored
Normal file
20
vendor/github.com/Masterminds/sprig/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Sprig
|
||||||
|
Copyright (C) 2013 Masterminds
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
148
vendor/github.com/Masterminds/sprig/crypto.go
generated
vendored
Normal file
148
vendor/github.com/Masterminds/sprig/crypto.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/dsa"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"golang.org/x/crypto/scrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sha256sum(input string) string {
|
||||||
|
hash := sha256.Sum256([]byte(input))
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// uuidv4 provides a safe and secure UUID v4 implementation
|
||||||
|
func uuidv4() string {
|
||||||
|
return fmt.Sprintf("%s", uuid.NewV4())
|
||||||
|
}
|
||||||
|
|
||||||
|
var master_password_seed = "com.lyndir.masterpassword"
|
||||||
|
|
||||||
|
var password_type_templates = map[string][][]byte{
|
||||||
|
"maximum": {[]byte("anoxxxxxxxxxxxxxxxxx"), []byte("axxxxxxxxxxxxxxxxxno")},
|
||||||
|
"long": {[]byte("CvcvnoCvcvCvcv"), []byte("CvcvCvcvnoCvcv"), []byte("CvcvCvcvCvcvno"), []byte("CvccnoCvcvCvcv"), []byte("CvccCvcvnoCvcv"),
|
||||||
|
[]byte("CvccCvcvCvcvno"), []byte("CvcvnoCvccCvcv"), []byte("CvcvCvccnoCvcv"), []byte("CvcvCvccCvcvno"), []byte("CvcvnoCvcvCvcc"),
|
||||||
|
[]byte("CvcvCvcvnoCvcc"), []byte("CvcvCvcvCvccno"), []byte("CvccnoCvccCvcv"), []byte("CvccCvccnoCvcv"), []byte("CvccCvccCvcvno"),
|
||||||
|
[]byte("CvcvnoCvccCvcc"), []byte("CvcvCvccnoCvcc"), []byte("CvcvCvccCvccno"), []byte("CvccnoCvcvCvcc"), []byte("CvccCvcvnoCvcc"),
|
||||||
|
[]byte("CvccCvcvCvccno")},
|
||||||
|
"medium": {[]byte("CvcnoCvc"), []byte("CvcCvcno")},
|
||||||
|
"short": {[]byte("Cvcn")},
|
||||||
|
"basic": {[]byte("aaanaaan"), []byte("aannaaan"), []byte("aaannaaa")},
|
||||||
|
"pin": {[]byte("nnnn")},
|
||||||
|
}
|
||||||
|
|
||||||
|
var template_characters = map[byte]string{
|
||||||
|
'V': "AEIOU",
|
||||||
|
'C': "BCDFGHJKLMNPQRSTVWXYZ",
|
||||||
|
'v': "aeiou",
|
||||||
|
'c': "bcdfghjklmnpqrstvwxyz",
|
||||||
|
'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
|
||||||
|
'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
|
||||||
|
'n': "0123456789",
|
||||||
|
'o': "@&%?,=[]_:-+*$#!'^~;()/.",
|
||||||
|
'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
|
||||||
|
}
|
||||||
|
|
||||||
|
func derivePassword(counter uint32, password_type, password, user, site string) string {
|
||||||
|
var templates = password_type_templates[password_type]
|
||||||
|
if templates == nil {
|
||||||
|
return fmt.Sprintf("cannot find password template %s", password_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
buffer.WriteString(master_password_seed)
|
||||||
|
binary.Write(&buffer, binary.BigEndian, uint32(len(user)))
|
||||||
|
buffer.WriteString(user)
|
||||||
|
|
||||||
|
salt := buffer.Bytes()
|
||||||
|
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 2, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("failed to derive password: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.Truncate(len(master_password_seed))
|
||||||
|
binary.Write(&buffer, binary.BigEndian, uint32(len(site)))
|
||||||
|
buffer.WriteString(site)
|
||||||
|
binary.Write(&buffer, binary.BigEndian, counter)
|
||||||
|
|
||||||
|
var hmacv = hmac.New(sha256.New, key)
|
||||||
|
hmacv.Write(buffer.Bytes())
|
||||||
|
var seed = hmacv.Sum(nil)
|
||||||
|
var temp = templates[int(seed[0])%len(templates)]
|
||||||
|
|
||||||
|
buffer.Truncate(0)
|
||||||
|
for i, element := range temp {
|
||||||
|
pass_chars := template_characters[element]
|
||||||
|
pass_char := pass_chars[int(seed[i+1])%len(pass_chars)]
|
||||||
|
buffer.WriteByte(pass_char)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePrivateKey(typ string) string {
|
||||||
|
var priv interface{}
|
||||||
|
var err error
|
||||||
|
switch typ {
|
||||||
|
case "", "rsa":
|
||||||
|
// good enough for government work
|
||||||
|
priv, err = rsa.GenerateKey(rand.Reader, 4096)
|
||||||
|
case "dsa":
|
||||||
|
key := new(dsa.PrivateKey)
|
||||||
|
// again, good enough for government work
|
||||||
|
if err = dsa.GenerateParameters(&key.Parameters, rand.Reader, dsa.L2048N256); err != nil {
|
||||||
|
return fmt.Sprintf("failed to generate dsa params: %s", err)
|
||||||
|
}
|
||||||
|
err = dsa.GenerateKey(key, rand.Reader)
|
||||||
|
priv = key
|
||||||
|
case "ecdsa":
|
||||||
|
// again, good enough for government work
|
||||||
|
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
default:
|
||||||
|
return "Unknown type " + typ
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("failed to generate private key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pem.EncodeToMemory(pemBlockForKey(priv)))
|
||||||
|
}
|
||||||
|
|
||||||
|
type DSAKeyFormat struct {
|
||||||
|
Version int
|
||||||
|
P, Q, G, Y, X *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||||
|
switch k := priv.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||||
|
case *dsa.PrivateKey:
|
||||||
|
val := DSAKeyFormat{
|
||||||
|
P: k.P, Q: k.Q, G: k.G,
|
||||||
|
Y: k.Y, X: k.X,
|
||||||
|
}
|
||||||
|
bytes, _ := asn1.Marshal(val)
|
||||||
|
return &pem.Block{Type: "DSA PRIVATE KEY", Bytes: bytes}
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
b, _ := x509.MarshalECPrivateKey(k)
|
||||||
|
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
53
vendor/github.com/Masterminds/sprig/date.go
generated
vendored
Normal file
53
vendor/github.com/Masterminds/sprig/date.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Given a format and a date, format the date string.
|
||||||
|
//
|
||||||
|
// Date can be a `time.Time` or an `int, int32, int64`.
|
||||||
|
// In the later case, it is treated as seconds since UNIX
|
||||||
|
// epoch.
|
||||||
|
func date(fmt string, date interface{}) string {
|
||||||
|
return dateInZone(fmt, date, "Local")
|
||||||
|
}
|
||||||
|
|
||||||
|
func htmlDate(date interface{}) string {
|
||||||
|
return dateInZone("2006-01-02", date, "Local")
|
||||||
|
}
|
||||||
|
|
||||||
|
func htmlDateInZone(date interface{}, zone string) string {
|
||||||
|
return dateInZone("2006-01-02", date, zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateInZone(fmt string, date interface{}, zone string) string {
|
||||||
|
var t time.Time
|
||||||
|
switch date := date.(type) {
|
||||||
|
default:
|
||||||
|
t = time.Now()
|
||||||
|
case time.Time:
|
||||||
|
t = date
|
||||||
|
case int64:
|
||||||
|
t = time.Unix(date, 0)
|
||||||
|
case int:
|
||||||
|
t = time.Unix(int64(date), 0)
|
||||||
|
case int32:
|
||||||
|
t = time.Unix(int64(date), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
loc, err := time.LoadLocation(zone)
|
||||||
|
if err != nil {
|
||||||
|
loc, _ = time.LoadLocation("UTC")
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.In(loc).Format(fmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dateModify(fmt string, date time.Time) time.Time {
|
||||||
|
d, err := time.ParseDuration(fmt)
|
||||||
|
if err != nil {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
return date.Add(d)
|
||||||
|
}
|
75
vendor/github.com/Masterminds/sprig/defaults.go
generated
vendored
Normal file
75
vendor/github.com/Masterminds/sprig/defaults.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dfault checks whether `given` is set, and returns default if not set.
|
||||||
|
//
|
||||||
|
// This returns `d` if `given` appears not to be set, and `given` otherwise.
|
||||||
|
//
|
||||||
|
// For numeric types 0 is unset.
|
||||||
|
// For strings, maps, arrays, and slices, len() = 0 is considered unset.
|
||||||
|
// For bool, false is unset.
|
||||||
|
// Structs are never considered unset.
|
||||||
|
//
|
||||||
|
// For everything else, including pointers, a nil value is unset.
|
||||||
|
func dfault(d interface{}, given ...interface{}) interface{} {
|
||||||
|
|
||||||
|
if empty(given) || empty(given[0]) {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return given[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty returns true if the given value has the zero value for its type.
|
||||||
|
func empty(given interface{}) bool {
|
||||||
|
g := reflect.ValueOf(given)
|
||||||
|
if !g.IsValid() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basically adapted from text/template.isTrue
|
||||||
|
switch g.Kind() {
|
||||||
|
default:
|
||||||
|
return g.IsNil()
|
||||||
|
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
||||||
|
return g.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return g.Bool() == false
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
return g.Complex() == 0
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return g.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return g.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return g.Float() == 0
|
||||||
|
case reflect.Struct:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// coalesce returns the first non-empty value.
|
||||||
|
func coalesce(v ...interface{}) interface{} {
|
||||||
|
for _, val := range v {
|
||||||
|
if !empty(val) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toJson encodes an item into a JSON string
|
||||||
|
func toJson(v interface{}) string {
|
||||||
|
output, _ := json.Marshal(v)
|
||||||
|
return string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toPrettyJson encodes an item into a pretty (indented) JSON string
|
||||||
|
func toPrettyJson(v interface{}) string {
|
||||||
|
output, _ := json.MarshalIndent(v, "", " ")
|
||||||
|
return string(output)
|
||||||
|
}
|
84
vendor/github.com/Masterminds/sprig/dict.go
generated
vendored
Normal file
84
vendor/github.com/Masterminds/sprig/dict.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import "github.com/imdario/mergo"
|
||||||
|
|
||||||
|
func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} {
|
||||||
|
d[key] = value
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func unset(d map[string]interface{}, key string) map[string]interface{} {
|
||||||
|
delete(d, key)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasKey(d map[string]interface{}, key string) bool {
|
||||||
|
_, ok := d[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func pluck(key string, d ...map[string]interface{}) []interface{} {
|
||||||
|
res := []interface{}{}
|
||||||
|
for _, dict := range d {
|
||||||
|
if val, ok := dict[key]; ok {
|
||||||
|
res = append(res, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func keys(dict map[string]interface{}) []string {
|
||||||
|
k := []string{}
|
||||||
|
for key := range dict {
|
||||||
|
k = append(k, key)
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func pick(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||||
|
res := map[string]interface{}{}
|
||||||
|
for _, k := range keys {
|
||||||
|
if v, ok := dict[k]; ok {
|
||||||
|
res[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func omit(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||||
|
res := map[string]interface{}{}
|
||||||
|
|
||||||
|
omit := make(map[string]bool, len(keys))
|
||||||
|
for _, k := range keys {
|
||||||
|
omit[k] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range dict {
|
||||||
|
if _, ok := omit[k]; !ok {
|
||||||
|
res[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func dict(v ...interface{}) map[string]interface{} {
|
||||||
|
dict := map[string]interface{}{}
|
||||||
|
lenv := len(v)
|
||||||
|
for i := 0; i < lenv; i += 2 {
|
||||||
|
key := strval(v[i])
|
||||||
|
if i+1 >= lenv {
|
||||||
|
dict[key] = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dict[key] = v[i+1]
|
||||||
|
}
|
||||||
|
return dict
|
||||||
|
}
|
||||||
|
|
||||||
|
func merge(dst map[string]interface{}, src map[string]interface{}) interface{} {
|
||||||
|
if err := mergo.Merge(&dst, src); err != nil {
|
||||||
|
// Swallow errors inside of a template.
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
225
vendor/github.com/Masterminds/sprig/doc.go
generated
vendored
Normal file
225
vendor/github.com/Masterminds/sprig/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
Sprig: Template functions for Go.
|
||||||
|
|
||||||
|
This package contains a number of utility functions for working with data
|
||||||
|
inside of Go `html/template` and `text/template` files.
|
||||||
|
|
||||||
|
To add these functions, use the `template.Funcs()` method:
|
||||||
|
|
||||||
|
t := templates.New("foo").Funcs(sprig.FuncMap())
|
||||||
|
|
||||||
|
Note that you should add the function map before you parse any template files.
|
||||||
|
|
||||||
|
In several cases, Sprig reverses the order of arguments from the way they
|
||||||
|
appear in the standard library. This is to make it easier to pipe
|
||||||
|
arguments into functions.
|
||||||
|
|
||||||
|
Date Functions
|
||||||
|
|
||||||
|
- date FORMAT TIME: Format a date, where a date is an integer type or a time.Time type, and
|
||||||
|
format is a time.Format formatting string.
|
||||||
|
- dateModify: Given a date, modify it with a duration: `date_modify "-1.5h" now`. If the duration doesn't
|
||||||
|
parse, it returns the time unaltered. See `time.ParseDuration` for info on duration strings.
|
||||||
|
- now: Current time.Time, for feeding into date-related functions.
|
||||||
|
- htmlDate TIME: Format a date for use in the value field of an HTML "date" form element.
|
||||||
|
- dateInZone FORMAT TIME TZ: Like date, but takes three arguments: format, timestamp,
|
||||||
|
timezone.
|
||||||
|
- htmlDateInZone TIME TZ: Like htmlDate, but takes two arguments: timestamp,
|
||||||
|
timezone.
|
||||||
|
|
||||||
|
String Functions
|
||||||
|
|
||||||
|
- abbrev: Truncate a string with ellipses. `abbrev 5 "hello world"` yields "he..."
|
||||||
|
- abbrevboth: Abbreviate from both sides, yielding "...lo wo..."
|
||||||
|
- trunc: Truncate a string (no suffix). `trunc 5 "Hello World"` yields "hello".
|
||||||
|
- trim: strings.TrimSpace
|
||||||
|
- trimAll: strings.Trim, but with the argument order reversed `trimAll "$" "$5.00"` or `"$5.00 | trimAll "$"`
|
||||||
|
- trimSuffix: strings.TrimSuffix, but with the argument order reversed: `trimSuffix "-" "ends-with-"`
|
||||||
|
- trimPrefix: strings.TrimPrefix, but with the argument order reversed `trimPrefix "$" "$5"`
|
||||||
|
- upper: strings.ToUpper
|
||||||
|
- lower: strings.ToLower
|
||||||
|
- nospace: Remove all space characters from a string. `nospace "h e l l o"` becomes "hello"
|
||||||
|
- title: strings.Title
|
||||||
|
- untitle: Remove title casing
|
||||||
|
- repeat: strings.Repeat, but with the arguments switched: `repeat count str`. (This simplifies common pipelines)
|
||||||
|
- substr: Given string, start, and length, return a substr.
|
||||||
|
- initials: Given a multi-word string, return the initials. `initials "Matt Butcher"` returns "MB"
|
||||||
|
- randAlphaNum: Given a length, generate a random alphanumeric sequence
|
||||||
|
- randAlpha: Given a length, generate an alphabetic string
|
||||||
|
- randAscii: Given a length, generate a random ASCII string (symbols included)
|
||||||
|
- randNumeric: Given a length, generate a string of digits.
|
||||||
|
- wrap: Force a line wrap at the given width. `wrap 80 "imagine a longer string"`
|
||||||
|
- wrapWith: Wrap a line at the given length, but using 'sep' instead of a newline. `wrapWith 50, "<br>", $html`
|
||||||
|
- contains: strings.Contains, but with the arguments switched: `contains substr str`. (This simplifies common pipelines)
|
||||||
|
- hasPrefix: strings.hasPrefix, but with the arguments switched
|
||||||
|
- hasSuffix: strings.hasSuffix, but with the arguments switched
|
||||||
|
- quote: Wrap string(s) in double quotation marks, escape the contents by adding '\' before '"'.
|
||||||
|
- squote: Wrap string(s) in double quotation marks, does not escape content.
|
||||||
|
- cat: Concatenate strings, separating them by spaces. `cat $a $b $c`.
|
||||||
|
- indent: Indent a string using space characters. `indent 4 "foo\nbar"` produces " foo\n bar"
|
||||||
|
- replace: Replace an old with a new in a string: `$name | replace " " "-"`
|
||||||
|
- plural: Choose singular or plural based on length: `len $fish | plural "one anchovy" "many anchovies"`
|
||||||
|
- sha256sum: Generate a hex encoded sha256 hash of the input
|
||||||
|
- toString: Convert something to a string
|
||||||
|
|
||||||
|
String Slice Functions:
|
||||||
|
|
||||||
|
- join: strings.Join, but as `join SEP SLICE`
|
||||||
|
- split: strings.Split, but as `split SEP STRING`. The results are returned
|
||||||
|
as a map with the indexes set to _N, where N is an integer starting from 0.
|
||||||
|
Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`)
|
||||||
|
- splitList: strings.Split, but as `split SEP STRING`. The results are returned
|
||||||
|
as an array.
|
||||||
|
- toStrings: convert a list to a list of strings. 'list 1 2 3 | toStrings' produces '["1" "2" "3"]'
|
||||||
|
- sortAlpha: sort a list lexicographically.
|
||||||
|
|
||||||
|
Integer Slice Functions:
|
||||||
|
|
||||||
|
- until: Given an integer, returns a slice of counting integers from 0 to one
|
||||||
|
less than the given integer: `range $i, $e := until 5`
|
||||||
|
- untilStep: Given start, stop, and step, return an integer slice starting at
|
||||||
|
'start', stopping at `stop`, and incrementing by 'step. This is the same
|
||||||
|
as Python's long-form of 'range'.
|
||||||
|
|
||||||
|
Conversions:
|
||||||
|
|
||||||
|
- atoi: Convert a string to an integer. 0 if the integer could not be parsed.
|
||||||
|
- in64: Convert a string or another numeric type to an int64.
|
||||||
|
- int: Convert a string or another numeric type to an int.
|
||||||
|
- float64: Convert a string or another numeric type to a float64.
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
|
||||||
|
- default: Give a default value. Used like this: trim " "| default "empty".
|
||||||
|
Since trim produces an empty string, the default value is returned. For
|
||||||
|
things with a length (strings, slices, maps), len(0) will trigger the default.
|
||||||
|
For numbers, the value 0 will trigger the default. For booleans, false will
|
||||||
|
trigger the default. For structs, the default is never returned (there is
|
||||||
|
no clear empty condition). For everything else, nil value triggers a default.
|
||||||
|
- empty: Return true if the given value is the zero value for its type.
|
||||||
|
Caveats: structs are always non-empty. This should match the behavior of
|
||||||
|
{{if pipeline}}, but can be used inside of a pipeline.
|
||||||
|
- coalesce: Given a list of items, return the first non-empty one.
|
||||||
|
This follows the same rules as 'empty'. '{{ coalesce .someVal 0 "hello" }}`
|
||||||
|
will return `.someVal` if set, or else return "hello". The 0 is skipped
|
||||||
|
because it is an empty value.
|
||||||
|
- compact: Return a copy of a list with all of the empty values removed.
|
||||||
|
'list 0 1 2 "" | compact' will return '[1 2]'
|
||||||
|
|
||||||
|
OS:
|
||||||
|
- env: Resolve an environment variable
|
||||||
|
- expandenv: Expand a string through the environment
|
||||||
|
|
||||||
|
File Paths:
|
||||||
|
- base: Return the last element of a path. https://golang.org/pkg/path#Base
|
||||||
|
- dir: Remove the last element of a path. https://golang.org/pkg/path#Dir
|
||||||
|
- clean: Clean a path to the shortest equivalent name. (e.g. remove "foo/.."
|
||||||
|
from "foo/../bar.html") https://golang.org/pkg/path#Clean
|
||||||
|
- ext: https://golang.org/pkg/path#Ext
|
||||||
|
- isAbs: https://golang.org/pkg/path#IsAbs
|
||||||
|
|
||||||
|
Encoding:
|
||||||
|
- b64enc: Base 64 encode a string.
|
||||||
|
- b64dec: Base 64 decode a string.
|
||||||
|
|
||||||
|
Reflection:
|
||||||
|
|
||||||
|
- typeOf: Takes an interface and returns a string representation of the type.
|
||||||
|
For pointers, this will return a type prefixed with an asterisk(`*`). So
|
||||||
|
a pointer to type `Foo` will be `*Foo`.
|
||||||
|
- typeIs: Compares an interface with a string name, and returns true if they match.
|
||||||
|
Note that a pointer will not match a reference. For example `*Foo` will not
|
||||||
|
match `Foo`.
|
||||||
|
- typeIsLike: Compares an interface with a string name and returns true if
|
||||||
|
the interface is that `name` or that `*name`. In other words, if the given
|
||||||
|
value matches the given type or is a pointer to the given type, this returns
|
||||||
|
true.
|
||||||
|
- kindOf: Takes an interface and returns a string representation of its kind.
|
||||||
|
- kindIs: Returns true if the given string matches the kind of the given interface.
|
||||||
|
|
||||||
|
Note: None of these can test whether or not something implements a given
|
||||||
|
interface, since doing so would require compiling the interface in ahead of
|
||||||
|
time.
|
||||||
|
|
||||||
|
Data Structures:
|
||||||
|
|
||||||
|
- tuple: Takes an arbitrary list of items and returns a slice of items. Its
|
||||||
|
tuple-ish properties are mainly gained through the template idiom, and not
|
||||||
|
through an API provided here. WARNING: The implementation of tuple will
|
||||||
|
change in the future.
|
||||||
|
- list: An arbitrary ordered list of items. (This is prefered over tuple.)
|
||||||
|
- dict: Takes a list of name/values and returns a map[string]interface{}.
|
||||||
|
The first parameter is converted to a string and stored as a key, the
|
||||||
|
second parameter is treated as the value. And so on, with odds as keys and
|
||||||
|
evens as values. If the function call ends with an odd, the last key will
|
||||||
|
be assigned the empty string. Non-string keys are converted to strings as
|
||||||
|
follows: []byte are converted, fmt.Stringers will have String() called.
|
||||||
|
errors will have Error() called. All others will be passed through
|
||||||
|
fmt.Sprtinf("%v").
|
||||||
|
|
||||||
|
Lists Functions:
|
||||||
|
|
||||||
|
These are used to manipulate lists: '{{ list 1 2 3 | reverse | first }}'
|
||||||
|
|
||||||
|
- first: Get the first item in a 'list'. 'list 1 2 3 | first' prints '1'
|
||||||
|
- last: Get the last item in a 'list': 'list 1 2 3 | last ' prints '3'
|
||||||
|
- rest: Get all but the first item in a list: 'list 1 2 3 | rest' returns '[2 3]'
|
||||||
|
- initial: Get all but the last item in a list: 'list 1 2 3 | initial' returns '[1 2]'
|
||||||
|
- append: Add an item to the end of a list: 'append $list 4' adds '4' to the end of '$list'
|
||||||
|
- prepend: Add an item to the beginning of a list: 'prepend $list 4' puts 4 at the beginning of the list.
|
||||||
|
- reverse: Reverse the items in a list.
|
||||||
|
- uniq: Remove duplicates from a list.
|
||||||
|
- without: Return a list with the given values removed: 'without (list 1 2 3) 1' would return '[2 3]'
|
||||||
|
- has: Return 'true' if the item is found in the list: 'has "foo" $list' will return 'true' if the list contains "foo"
|
||||||
|
|
||||||
|
Dict Functions:
|
||||||
|
|
||||||
|
These are used to manipulate dicts.
|
||||||
|
|
||||||
|
- set: Takes a dict, a key, and a value, and sets that key/value pair in
|
||||||
|
the dict. `set $dict $key $value`. For convenience, it returns the dict,
|
||||||
|
even though the dict was modified in place.
|
||||||
|
- unset: Takes a dict and a key, and deletes that key/value pair from the
|
||||||
|
dict. `unset $dict $key`. This returns the dict for convenience.
|
||||||
|
- hasKey: Takes a dict and a key, and returns boolean true if the key is in
|
||||||
|
the dict.
|
||||||
|
- pluck: Given a key and one or more maps, get all of the values for that key.
|
||||||
|
- keys: Get an array of all of the keys in a dict.
|
||||||
|
- pick: Select just the given keys out of the dict, and return a new dict.
|
||||||
|
- omit: Return a dict without the given keys.
|
||||||
|
|
||||||
|
Math Functions:
|
||||||
|
|
||||||
|
Integer functions will convert integers of any width to `int64`. If a
|
||||||
|
string is passed in, functions will attempt to convert with
|
||||||
|
`strconv.ParseInt(s, 1064)`. If this fails, the value will be treated as 0.
|
||||||
|
|
||||||
|
- add1: Increment an integer by 1
|
||||||
|
- add: Sum an arbitrary number of integers
|
||||||
|
- sub: Subtract the second integer from the first
|
||||||
|
- div: Divide the first integer by the second
|
||||||
|
- mod: Module of first integer divided by second
|
||||||
|
- mul: Multiply integers
|
||||||
|
- max: Return the biggest of a series of one or more integers
|
||||||
|
- min: Return the smallest of a series of one or more integers
|
||||||
|
- biggest: DEPRECATED. Return the biggest of a series of one or more integers
|
||||||
|
|
||||||
|
Crypto Functions:
|
||||||
|
|
||||||
|
- genPrivateKey: Generate a private key for the given cryptosystem. If no
|
||||||
|
argument is supplied, by default it will generate a private key using
|
||||||
|
the RSA algorithm. Accepted values are `rsa`, `dsa`, and `ecdsa`.
|
||||||
|
- derivePassword: Derive a password from the given parameters according to the ["Master Password" algorithm](http://masterpasswordapp.com/algorithm.html)
|
||||||
|
Given parameters (in order) are:
|
||||||
|
`counter` (starting with 1), `password_type` (maximum, long, medium, short, basic, or pin), `password`,
|
||||||
|
`user`, and `site`
|
||||||
|
|
||||||
|
SemVer Functions:
|
||||||
|
|
||||||
|
These functions provide version parsing and comparisons for SemVer 2 version
|
||||||
|
strings.
|
||||||
|
|
||||||
|
- semver: Parse a semantic version and return a Version object.
|
||||||
|
- semverCompare: Compare a SemVer range to a particular version.
|
||||||
|
*/
|
||||||
|
package sprig
|
261
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
Normal file
261
vendor/github.com/Masterminds/sprig/functions.go
generated
vendored
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
ttemplate "text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
util "github.com/aokoli/goutils"
|
||||||
|
"github.com/huandu/xstrings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Produce the function map.
|
||||||
|
//
|
||||||
|
// Use this to pass the functions into the template engine:
|
||||||
|
//
|
||||||
|
// tpl := template.New("foo").Funcs(sprig.FuncMap()))
|
||||||
|
//
|
||||||
|
func FuncMap() template.FuncMap {
|
||||||
|
return HtmlFuncMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HermeticTextFuncMap returns a 'text/template'.FuncMap with only repeatable functions.
|
||||||
|
func HermeticTxtFuncMap() ttemplate.FuncMap {
|
||||||
|
r := TxtFuncMap()
|
||||||
|
for _, name := range nonhermeticFunctions {
|
||||||
|
delete(r, name)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions.
|
||||||
|
func HermeticHtmlFuncMap() template.FuncMap {
|
||||||
|
r := HtmlFuncMap()
|
||||||
|
for _, name := range nonhermeticFunctions {
|
||||||
|
delete(r, name)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextFuncMap returns a 'text/template'.FuncMap
|
||||||
|
func TxtFuncMap() ttemplate.FuncMap {
|
||||||
|
return ttemplate.FuncMap(GenericFuncMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// HtmlFuncMap returns an 'html/template'.Funcmap
|
||||||
|
func HtmlFuncMap() template.FuncMap {
|
||||||
|
return template.FuncMap(GenericFuncMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
|
||||||
|
func GenericFuncMap() map[string]interface{} {
|
||||||
|
gfm := make(map[string]interface{}, len(genericMap))
|
||||||
|
for k, v := range genericMap {
|
||||||
|
gfm[k] = v
|
||||||
|
}
|
||||||
|
return gfm
|
||||||
|
}
|
||||||
|
|
||||||
|
// These functions are not guaranteed to evaluate to the same result for given input, because they
|
||||||
|
// refer to the environemnt or global state.
|
||||||
|
var nonhermeticFunctions = []string{
|
||||||
|
// Date functions
|
||||||
|
"date",
|
||||||
|
"date_in_zone",
|
||||||
|
"date_modify",
|
||||||
|
"now",
|
||||||
|
"htmlDate",
|
||||||
|
"htmlDateInZone",
|
||||||
|
"dateInZone",
|
||||||
|
"dateModify",
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
"randAlphaNum",
|
||||||
|
"randAlpha",
|
||||||
|
"randAscii",
|
||||||
|
"randNumeric",
|
||||||
|
"uuidv4",
|
||||||
|
|
||||||
|
// OS
|
||||||
|
"env",
|
||||||
|
"expandenv",
|
||||||
|
}
|
||||||
|
|
||||||
|
var genericMap = map[string]interface{}{
|
||||||
|
"hello": func() string { return "Hello!" },
|
||||||
|
|
||||||
|
// Date functions
|
||||||
|
"date": date,
|
||||||
|
"date_in_zone": dateInZone,
|
||||||
|
"date_modify": dateModify,
|
||||||
|
"now": func() time.Time { return time.Now() },
|
||||||
|
"htmlDate": htmlDate,
|
||||||
|
"htmlDateInZone": htmlDateInZone,
|
||||||
|
"dateInZone": dateInZone,
|
||||||
|
"dateModify": dateModify,
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
"abbrev": abbrev,
|
||||||
|
"abbrevboth": abbrevboth,
|
||||||
|
"trunc": trunc,
|
||||||
|
"trim": strings.TrimSpace,
|
||||||
|
"upper": strings.ToUpper,
|
||||||
|
"lower": strings.ToLower,
|
||||||
|
"title": strings.Title,
|
||||||
|
"untitle": untitle,
|
||||||
|
"substr": substring,
|
||||||
|
// Switch order so that "foo" | repeat 5
|
||||||
|
"repeat": func(count int, str string) string { return strings.Repeat(str, count) },
|
||||||
|
// Deprecated: Use trimAll.
|
||||||
|
"trimall": func(a, b string) string { return strings.Trim(b, a) },
|
||||||
|
// Switch order so that "$foo" | trimall "$"
|
||||||
|
"trimAll": func(a, b string) string { return strings.Trim(b, a) },
|
||||||
|
"trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
|
||||||
|
"trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },
|
||||||
|
"nospace": util.DeleteWhiteSpace,
|
||||||
|
"initials": initials,
|
||||||
|
"randAlphaNum": randAlphaNumeric,
|
||||||
|
"randAlpha": randAlpha,
|
||||||
|
"randAscii": randAscii,
|
||||||
|
"randNumeric": randNumeric,
|
||||||
|
"swapcase": util.SwapCase,
|
||||||
|
"shuffle": xstrings.Shuffle,
|
||||||
|
"snakecase": xstrings.ToSnakeCase,
|
||||||
|
"camelcase": xstrings.ToCamelCase,
|
||||||
|
"wrap": func(l int, s string) string { return util.Wrap(s, l) },
|
||||||
|
"wrapWith": func(l int, sep, str string) string { return util.WrapCustom(str, l, sep, true) },
|
||||||
|
// Switch order so that "foobar" | contains "foo"
|
||||||
|
"contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
|
||||||
|
"hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
|
||||||
|
"hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
|
||||||
|
"quote": quote,
|
||||||
|
"squote": squote,
|
||||||
|
"cat": cat,
|
||||||
|
"indent": indent,
|
||||||
|
"replace": replace,
|
||||||
|
"plural": plural,
|
||||||
|
"sha256sum": sha256sum,
|
||||||
|
"toString": strval,
|
||||||
|
|
||||||
|
// Wrap Atoi to stop errors.
|
||||||
|
"atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
|
||||||
|
"int64": toInt64,
|
||||||
|
"int": toInt,
|
||||||
|
"float64": toFloat64,
|
||||||
|
|
||||||
|
//"gt": func(a, b int) bool {return a > b},
|
||||||
|
//"gte": func(a, b int) bool {return a >= b},
|
||||||
|
//"lt": func(a, b int) bool {return a < b},
|
||||||
|
//"lte": func(a, b int) bool {return a <= b},
|
||||||
|
|
||||||
|
// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
|
||||||
|
"split": split,
|
||||||
|
"splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
|
||||||
|
"toStrings": strslice,
|
||||||
|
|
||||||
|
"until": until,
|
||||||
|
"untilStep": untilStep,
|
||||||
|
|
||||||
|
// VERY basic arithmetic.
|
||||||
|
"add1": func(i interface{}) int64 { return toInt64(i) + 1 },
|
||||||
|
"add": func(i ...interface{}) int64 {
|
||||||
|
var a int64 = 0
|
||||||
|
for _, b := range i {
|
||||||
|
a += toInt64(b)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
"sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
|
||||||
|
"div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
|
||||||
|
"mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
|
||||||
|
"mul": func(a interface{}, v ...interface{}) int64 {
|
||||||
|
val := toInt64(a)
|
||||||
|
for _, b := range v {
|
||||||
|
val = val * toInt64(b)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
},
|
||||||
|
"biggest": max,
|
||||||
|
"max": max,
|
||||||
|
"min": min,
|
||||||
|
|
||||||
|
// string slices. Note that we reverse the order b/c that's better
|
||||||
|
// for template processing.
|
||||||
|
"join": join,
|
||||||
|
"sortAlpha": sortAlpha,
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
"default": dfault,
|
||||||
|
"empty": empty,
|
||||||
|
"coalesce": coalesce,
|
||||||
|
"compact": compact,
|
||||||
|
"toJson": toJson,
|
||||||
|
"toPrettyJson": toPrettyJson,
|
||||||
|
|
||||||
|
// Reflection
|
||||||
|
"typeOf": typeOf,
|
||||||
|
"typeIs": typeIs,
|
||||||
|
"typeIsLike": typeIsLike,
|
||||||
|
"kindOf": kindOf,
|
||||||
|
"kindIs": kindIs,
|
||||||
|
|
||||||
|
// OS:
|
||||||
|
"env": func(s string) string { return os.Getenv(s) },
|
||||||
|
"expandenv": func(s string) string { return os.ExpandEnv(s) },
|
||||||
|
|
||||||
|
// File Paths:
|
||||||
|
"base": path.Base,
|
||||||
|
"dir": path.Dir,
|
||||||
|
"clean": path.Clean,
|
||||||
|
"ext": path.Ext,
|
||||||
|
"isAbs": path.IsAbs,
|
||||||
|
|
||||||
|
// Encoding:
|
||||||
|
"b64enc": base64encode,
|
||||||
|
"b64dec": base64decode,
|
||||||
|
"b32enc": base32encode,
|
||||||
|
"b32dec": base32decode,
|
||||||
|
|
||||||
|
// Data Structures:
|
||||||
|
"tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
|
||||||
|
"list": list,
|
||||||
|
"dict": dict,
|
||||||
|
"set": set,
|
||||||
|
"unset": unset,
|
||||||
|
"hasKey": hasKey,
|
||||||
|
"pluck": pluck,
|
||||||
|
"keys": keys,
|
||||||
|
"pick": pick,
|
||||||
|
"omit": omit,
|
||||||
|
"merge": merge,
|
||||||
|
|
||||||
|
"append": push, "push": push,
|
||||||
|
"prepend": prepend,
|
||||||
|
"first": first,
|
||||||
|
"rest": rest,
|
||||||
|
"last": last,
|
||||||
|
"initial": initial,
|
||||||
|
"reverse": reverse,
|
||||||
|
"uniq": uniq,
|
||||||
|
"without": without,
|
||||||
|
"has": func(needle interface{}, haystack []interface{}) bool { return inList(haystack, needle) },
|
||||||
|
|
||||||
|
// Crypto:
|
||||||
|
"genPrivateKey": generatePrivateKey,
|
||||||
|
"derivePassword": derivePassword,
|
||||||
|
|
||||||
|
// UUIDs:
|
||||||
|
"uuidv4": uuidv4,
|
||||||
|
|
||||||
|
// SemVer:
|
||||||
|
"semver": semver,
|
||||||
|
"semverCompare": semverCompare,
|
||||||
|
|
||||||
|
// Flow Control:
|
||||||
|
"fail": func(msg string) (string, error) { return "", errors.New(msg) },
|
||||||
|
}
|
109
vendor/github.com/Masterminds/sprig/list.go
generated
vendored
Normal file
109
vendor/github.com/Masterminds/sprig/list.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func list(v ...interface{}) []interface{} {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func push(list []interface{}, v interface{}) []interface{} {
|
||||||
|
return append(list, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepend(list []interface{}, v interface{}) []interface{} {
|
||||||
|
return append([]interface{}{v}, list...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func last(list []interface{}) interface{} {
|
||||||
|
l := len(list)
|
||||||
|
if l == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return list[l-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func first(list []interface{}) interface{} {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return list[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func rest(list []interface{}) []interface{} {
|
||||||
|
if len(list) == 0 {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
return list[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func initial(list []interface{}) []interface{} {
|
||||||
|
l := len(list)
|
||||||
|
if l == 0 {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
return list[:l-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortAlpha(list interface{}) []string {
|
||||||
|
k := reflect.Indirect(reflect.ValueOf(list)).Kind()
|
||||||
|
switch k {
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
a := strslice(list)
|
||||||
|
s := sort.StringSlice(a)
|
||||||
|
s.Sort()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return []string{strval(list)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverse(v []interface{}) []interface{} {
|
||||||
|
// We do not sort in place because the incomming array should not be altered.
|
||||||
|
l := len(v)
|
||||||
|
c := make([]interface{}, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
c[l-i-1] = v[i]
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func compact(list []interface{}) []interface{} {
|
||||||
|
res := []interface{}{}
|
||||||
|
for _, item := range list {
|
||||||
|
if !empty(item) {
|
||||||
|
res = append(res, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniq(list []interface{}) []interface{} {
|
||||||
|
dest := []interface{}{}
|
||||||
|
for _, item := range list {
|
||||||
|
if !inList(dest, item) {
|
||||||
|
dest = append(dest, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
func inList(haystack []interface{}, needle interface{}) bool {
|
||||||
|
for _, h := range haystack {
|
||||||
|
if reflect.DeepEqual(needle, h) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func without(list []interface{}, omit ...interface{}) []interface{} {
|
||||||
|
res := []interface{}{}
|
||||||
|
for _, i := range list {
|
||||||
|
if !inList(omit, i) {
|
||||||
|
res = append(res, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
129
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
Normal file
129
vendor/github.com/Masterminds/sprig/numeric.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// toFloat64 converts 64-bit floats
|
||||||
|
func toFloat64(v interface{}) float64 {
|
||||||
|
if str, ok := v.(string); ok {
|
||||||
|
iv, err := strconv.ParseFloat(str, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.Indirect(reflect.ValueOf(v))
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return float64(val.Int())
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||||
|
return float64(val.Uint())
|
||||||
|
case reflect.Uint, reflect.Uint64:
|
||||||
|
return float64(val.Uint())
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return val.Float()
|
||||||
|
case reflect.Bool:
|
||||||
|
if val.Bool() == true {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toInt(v interface{}) int {
|
||||||
|
//It's not optimal. Bud I don't want duplicate toInt64 code.
|
||||||
|
return int(toInt64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// toInt64 converts integer types to 64-bit integers
|
||||||
|
func toInt64(v interface{}) int64 {
|
||||||
|
if str, ok := v.(string); ok {
|
||||||
|
iv, err := strconv.ParseInt(str, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return iv
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.Indirect(reflect.ValueOf(v))
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||||
|
return val.Int()
|
||||||
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||||
|
return int64(val.Uint())
|
||||||
|
case reflect.Uint, reflect.Uint64:
|
||||||
|
tv := val.Uint()
|
||||||
|
if tv <= math.MaxInt64 {
|
||||||
|
return int64(tv)
|
||||||
|
}
|
||||||
|
// TODO: What is the sensible thing to do here?
|
||||||
|
return math.MaxInt64
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return int64(val.Float())
|
||||||
|
case reflect.Bool:
|
||||||
|
if val.Bool() == true {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a interface{}, i ...interface{}) int64 {
|
||||||
|
aa := toInt64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toInt64(b)
|
||||||
|
if bb > aa {
|
||||||
|
aa = bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a interface{}, i ...interface{}) int64 {
|
||||||
|
aa := toInt64(a)
|
||||||
|
for _, b := range i {
|
||||||
|
bb := toInt64(b)
|
||||||
|
if bb < aa {
|
||||||
|
aa = bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aa
|
||||||
|
}
|
||||||
|
|
||||||
|
func until(count int) []int {
|
||||||
|
step := 1
|
||||||
|
if count < 0 {
|
||||||
|
step = -1
|
||||||
|
}
|
||||||
|
return untilStep(0, count, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
func untilStep(start, stop, step int) []int {
|
||||||
|
v := []int{}
|
||||||
|
|
||||||
|
if stop < start {
|
||||||
|
if step >= 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
for i := start; i > stop; i += step {
|
||||||
|
v = append(v, i)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
if step <= 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
for i := start; i < stop; i += step {
|
||||||
|
v = append(v, i)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
28
vendor/github.com/Masterminds/sprig/reflect.go
generated
vendored
Normal file
28
vendor/github.com/Masterminds/sprig/reflect.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// typeIs returns true if the src is the type named in target.
|
||||||
|
func typeIs(target string, src interface{}) bool {
|
||||||
|
return target == typeOf(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeIsLike(target string, src interface{}) bool {
|
||||||
|
t := typeOf(src)
|
||||||
|
return target == t || "*"+target == t
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeOf(src interface{}) string {
|
||||||
|
return fmt.Sprintf("%T", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindIs(target string, src interface{}) bool {
|
||||||
|
return target == kindOf(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindOf(src interface{}) string {
|
||||||
|
return reflect.ValueOf(src).Kind().String()
|
||||||
|
}
|
23
vendor/github.com/Masterminds/sprig/semver.go
generated
vendored
Normal file
23
vendor/github.com/Masterminds/sprig/semver.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
sv2 "github.com/Masterminds/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func semverCompare(constraint, version string) (bool, error) {
|
||||||
|
c, err := sv2.NewConstraint(constraint)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := sv2.NewVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Check(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func semver(version string) (*sv2.Version, error) {
|
||||||
|
return sv2.NewVersion(version)
|
||||||
|
}
|
197
vendor/github.com/Masterminds/sprig/strings.go
generated
vendored
Normal file
197
vendor/github.com/Masterminds/sprig/strings.go
generated
vendored
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
package sprig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
util "github.com/aokoli/goutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func base64encode(v string) string {
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func base64decode(v string) string {
|
||||||
|
data, err := base64.StdEncoding.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func base32encode(v string) string {
|
||||||
|
return base32.StdEncoding.EncodeToString([]byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func base32decode(v string) string {
|
||||||
|
data, err := base32.StdEncoding.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func abbrev(width int, s string) string {
|
||||||
|
if width < 4 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r, _ := util.Abbreviate(s, width)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func abbrevboth(left, right int, s string) string {
|
||||||
|
if right < 4 || left > 0 && right < 7 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r, _ := util.AbbreviateFull(s, left, right)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
func initials(s string) string {
|
||||||
|
// Wrap this just to eliminate the var args, which templates don't do well.
|
||||||
|
return util.Initials(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randAlphaNumeric(count int) string {
|
||||||
|
// It is not possible, it appears, to actually generate an error here.
|
||||||
|
r, _ := util.RandomAlphaNumeric(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func randAlpha(count int) string {
|
||||||
|
r, _ := util.RandomAlphabetic(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func randAscii(count int) string {
|
||||||
|
r, _ := util.RandomAscii(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func randNumeric(count int) string {
|
||||||
|
r, _ := util.RandomNumeric(count)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func untitle(str string) string {
|
||||||
|
return util.Uncapitalize(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func quote(str ...interface{}) string {
|
||||||
|
out := make([]string, len(str))
|
||||||
|
for i, s := range str {
|
||||||
|
out[i] = fmt.Sprintf("%q", strval(s))
|
||||||
|
}
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func squote(str ...interface{}) string {
|
||||||
|
out := make([]string, len(str))
|
||||||
|
for i, s := range str {
|
||||||
|
out[i] = fmt.Sprintf("'%v'", s)
|
||||||
|
}
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func cat(v ...interface{}) string {
|
||||||
|
r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
|
||||||
|
return fmt.Sprintf(r, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func indent(spaces int, v string) string {
|
||||||
|
pad := strings.Repeat(" ", spaces)
|
||||||
|
return pad + strings.Replace(v, "\n", "\n"+pad, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func replace(old, new, src string) string {
|
||||||
|
return strings.Replace(src, old, new, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func plural(one, many string, count int) string {
|
||||||
|
if count == 1 {
|
||||||
|
return one
|
||||||
|
}
|
||||||
|
return many
|
||||||
|
}
|
||||||
|
|
||||||
|
func strslice(v interface{}) []string {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case []string:
|
||||||
|
return v
|
||||||
|
case []interface{}:
|
||||||
|
l := len(v)
|
||||||
|
b := make([]string, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
b[i] = strval(v[i])
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
default:
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
l := val.Len()
|
||||||
|
b := make([]string, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
b[i] = strval(val.Index(i).Interface())
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
default:
|
||||||
|
return []string{strval(v)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func strval(v interface{}) string {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case []byte:
|
||||||
|
return string(v)
|
||||||
|
case error:
|
||||||
|
return v.Error()
|
||||||
|
case fmt.Stringer:
|
||||||
|
return v.String()
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func trunc(c int, s string) string {
|
||||||
|
if len(s) <= c {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s[0:c]
|
||||||
|
}
|
||||||
|
|
||||||
|
func join(sep string, v interface{}) string {
|
||||||
|
return strings.Join(strslice(v), sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func split(sep, orig string) map[string]string {
|
||||||
|
parts := strings.Split(orig, sep)
|
||||||
|
res := make(map[string]string, len(parts))
|
||||||
|
for i, v := range parts {
|
||||||
|
res["_"+strconv.Itoa(i)] = v
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// substring creates a substring of the given string.
|
||||||
|
//
|
||||||
|
// If start is < 0, this calls string[:length].
|
||||||
|
//
|
||||||
|
// If start is >= 0 and length < 0, this calls string[start:]
|
||||||
|
//
|
||||||
|
// Otherwise, this calls string[start, length].
|
||||||
|
func substring(start, length int, s string) string {
|
||||||
|
if start < 0 {
|
||||||
|
return s[:length]
|
||||||
|
}
|
||||||
|
if length < 0 {
|
||||||
|
return s[start:]
|
||||||
|
}
|
||||||
|
return s[start:length]
|
||||||
|
}
|
202
vendor/github.com/aokoli/goutils/LICENSE.txt
generated
vendored
Normal file
202
vendor/github.com/aokoli/goutils/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
268
vendor/github.com/aokoli/goutils/randomstringutils.go
generated
vendored
Normal file
268
vendor/github.com/aokoli/goutils/randomstringutils.go
generated
vendored
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 Alexander Okoli
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package goutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RANDOM provides the time-based seed used to generate random numbers
|
||||||
|
var RANDOM = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
/*
|
||||||
|
RandomNonAlphaNumeric creates a random string whose length is the number of characters specified.
|
||||||
|
Characters will be chosen from the set of all characters (ASCII/Unicode values between 0 to 2,147,483,647 (math.MaxInt32)).
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
count - the length of random string to create
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||||
|
*/
|
||||||
|
func RandomNonAlphaNumeric(count int) (string, error) {
|
||||||
|
return RandomAlphaNumericCustom(count, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RandomAscii creates a random string whose length is the number of characters specified.
|
||||||
|
Characters will be chosen from the set of characters whose ASCII value is between 32 and 126 (inclusive).
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
count - the length of random string to create
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||||
|
*/
|
||||||
|
func RandomAscii(count int) (string, error) {
|
||||||
|
return Random(count, 32, 127, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RandomNumeric creates a random string whose length is the number of characters specified.
|
||||||
|
Characters will be chosen from the set of numeric characters.
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
count - the length of random string to create
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||||
|
*/
|
||||||
|
func RandomNumeric(count int) (string, error) {
|
||||||
|
return Random(count, 0, 0, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RandomAlphabetic creates a random string whose length is the number of characters specified.
|
||||||
|
Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
count - the length of random string to create
|
||||||
|
letters - if true, generated string may include alphabetic characters
|
||||||
|
numbers - if true, generated string may include numeric characters
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||||
|
*/
|
||||||
|
func RandomAlphabetic(count int) (string, error) {
|
||||||
|
return Random(count, 0, 0, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RandomAlphaNumeric creates a random string whose length is the number of characters specified.
|
||||||
|
Characters will be chosen from the set of alpha-numeric characters.
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
count - the length of random string to create
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||||
|
*/
|
||||||
|
func RandomAlphaNumeric(count int) (string, error) {
|
||||||
|
RandomString, err := Random(count, 0, 0, true, true)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error: %s", err)
|
||||||
|
}
|
||||||
|
match, err := regexp.MatchString("([0-9]+)", RandomString)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !match {
|
||||||
|
//Get the position between 0 and the length of the string-1 to insert a random number
|
||||||
|
position := rand.Intn(count)
|
||||||
|
//Insert a random number between [0-9] in the position
|
||||||
|
RandomString = RandomString[:position] + string('0'+rand.Intn(10)) + RandomString[position+1:]
|
||||||
|
return RandomString, err
|
||||||
|
}
|
||||||
|
return RandomString, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RandomAlphaNumericCustom creates a random string whose length is the number of characters specified.
|
||||||
|
Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
count - the length of random string to create
|
||||||
|
letters - if true, generated string may include alphabetic characters
|
||||||
|
numbers - if true, generated string may include numeric characters
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||||
|
*/
|
||||||
|
func RandomAlphaNumericCustom(count int, letters bool, numbers bool) (string, error) {
|
||||||
|
return Random(count, 0, 0, letters, numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Random creates a random string based on a variety of options, using default source of randomness.
|
||||||
|
This method has exactly the same semantics as RandomSeed(int, int, int, bool, bool, []char, *rand.Rand), but
|
||||||
|
instead of using an externally supplied source of randomness, it uses the internal *rand.Rand instance.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
count - the length of random string to create
|
||||||
|
start - the position in set of chars (ASCII/Unicode int) to start at
|
||||||
|
end - the position in set of chars (ASCII/Unicode int) to end before
|
||||||
|
letters - if true, generated string may include alphabetic characters
|
||||||
|
numbers - if true, generated string may include numeric characters
|
||||||
|
chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from an invalid parameter within underlying function, RandomSeed(...)
|
||||||
|
*/
|
||||||
|
func Random(count int, start int, end int, letters bool, numbers bool, chars ...rune) (string, error) {
|
||||||
|
return RandomSeed(count, start, end, letters, numbers, chars, RANDOM)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RandomSeed creates a random string based on a variety of options, using supplied source of randomness.
|
||||||
|
If the parameters start and end are both 0, start and end are set to ' ' and 'z', the ASCII printable characters, will be used,
|
||||||
|
unless letters and numbers are both false, in which case, start and end are set to 0 and math.MaxInt32, respectively.
|
||||||
|
If chars is not nil, characters stored in chars that are between start and end are chosen.
|
||||||
|
This method accepts a user-supplied *rand.Rand instance to use as a source of randomness. By seeding a single *rand.Rand instance
|
||||||
|
with a fixed seed and using it for each call, the same random sequence of strings can be generated repeatedly and predictably.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
count - the length of random string to create
|
||||||
|
start - the position in set of chars (ASCII/Unicode decimals) to start at
|
||||||
|
end - the position in set of chars (ASCII/Unicode decimals) to end before
|
||||||
|
letters - if true, generated string may include alphabetic characters
|
||||||
|
numbers - if true, generated string may include numeric characters
|
||||||
|
chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars.
|
||||||
|
random - a source of randomness.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - the random string
|
||||||
|
error - an error stemming from invalid parameters: if count < 0; or the provided chars array is empty; or end <= start; or end > len(chars)
|
||||||
|
*/
|
||||||
|
func RandomSeed(count int, start int, end int, letters bool, numbers bool, chars []rune, random *rand.Rand) (string, error) {
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return "", nil
|
||||||
|
} else if count < 0 {
|
||||||
|
err := fmt.Errorf("randomstringutils illegal argument: Requested random string length %v is less than 0.", count) // equiv to err := errors.New("...")
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if chars != nil && len(chars) == 0 {
|
||||||
|
err := fmt.Errorf("randomstringutils illegal argument: The chars array must not be empty")
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if start == 0 && end == 0 {
|
||||||
|
if chars != nil {
|
||||||
|
end = len(chars)
|
||||||
|
} else {
|
||||||
|
if !letters && !numbers {
|
||||||
|
end = math.MaxInt32
|
||||||
|
} else {
|
||||||
|
end = 'z' + 1
|
||||||
|
start = ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if end <= start {
|
||||||
|
err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) must be greater than start (%v)", end, start)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if chars != nil && end > len(chars) {
|
||||||
|
err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) cannot be greater than len(chars) (%v)", end, len(chars))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := make([]rune, count)
|
||||||
|
gap := end - start
|
||||||
|
|
||||||
|
// high-surrogates range, (\uD800-\uDBFF) = 55296 - 56319
|
||||||
|
// low-surrogates range, (\uDC00-\uDFFF) = 56320 - 57343
|
||||||
|
|
||||||
|
for count != 0 {
|
||||||
|
count--
|
||||||
|
var ch rune
|
||||||
|
if chars == nil {
|
||||||
|
ch = rune(random.Intn(gap) + start)
|
||||||
|
} else {
|
||||||
|
ch = chars[random.Intn(gap)+start]
|
||||||
|
}
|
||||||
|
|
||||||
|
if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {
|
||||||
|
if ch >= 56320 && ch <= 57343 { // low surrogate range
|
||||||
|
if count == 0 {
|
||||||
|
count++
|
||||||
|
} else {
|
||||||
|
// Insert low surrogate
|
||||||
|
buffer[count] = ch
|
||||||
|
count--
|
||||||
|
// Insert high surrogate
|
||||||
|
buffer[count] = rune(55296 + random.Intn(128))
|
||||||
|
}
|
||||||
|
} else if ch >= 55296 && ch <= 56191 { // High surrogates range (Partial)
|
||||||
|
if count == 0 {
|
||||||
|
count++
|
||||||
|
} else {
|
||||||
|
// Insert low surrogate
|
||||||
|
buffer[count] = rune(56320 + random.Intn(128))
|
||||||
|
count--
|
||||||
|
// Insert high surrogate
|
||||||
|
buffer[count] = ch
|
||||||
|
}
|
||||||
|
} else if ch >= 56192 && ch <= 56319 {
|
||||||
|
// private high surrogate, skip it
|
||||||
|
count++
|
||||||
|
} else {
|
||||||
|
// not one of the surrogates*
|
||||||
|
buffer[count] = ch
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(buffer), nil
|
||||||
|
}
|
224
vendor/github.com/aokoli/goutils/stringutils.go
generated
vendored
Normal file
224
vendor/github.com/aokoli/goutils/stringutils.go
generated
vendored
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 Alexander Okoli
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package goutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Typically returned by functions where a searched item cannot be found
|
||||||
|
const INDEX_NOT_FOUND = -1
|
||||||
|
|
||||||
|
/*
|
||||||
|
Abbreviate abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "Now is the time for..."
|
||||||
|
|
||||||
|
Specifically, the algorithm is as follows:
|
||||||
|
|
||||||
|
- If str is less than maxWidth characters long, return it.
|
||||||
|
- Else abbreviate it to (str[0:maxWidth - 3] + "...").
|
||||||
|
- If maxWidth is less than 4, return an illegal argument error.
|
||||||
|
- In no case will it return a string of length greater than maxWidth.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to check
|
||||||
|
maxWidth - maximum length of result string, must be at least 4
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - abbreviated string
|
||||||
|
error - if the width is too small
|
||||||
|
*/
|
||||||
|
func Abbreviate(str string, maxWidth int) (string, error) {
|
||||||
|
return AbbreviateFull(str, 0, maxWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AbbreviateFull abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "...is the time for..."
|
||||||
|
This function works like Abbreviate(string, int), but allows you to specify a "left edge" offset. Note that this left edge is not
|
||||||
|
necessarily going to be the leftmost character in the result, or the first character following the ellipses, but it will appear
|
||||||
|
somewhere in the result.
|
||||||
|
In no case will it return a string of length greater than maxWidth.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to check
|
||||||
|
offset - left edge of source string
|
||||||
|
maxWidth - maximum length of result string, must be at least 4
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string - abbreviated string
|
||||||
|
error - if the width is too small
|
||||||
|
*/
|
||||||
|
func AbbreviateFull(str string, offset int, maxWidth int) (string, error) {
|
||||||
|
if str == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if maxWidth < 4 {
|
||||||
|
err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width is 4")
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(str) <= maxWidth {
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
if offset > len(str) {
|
||||||
|
offset = len(str)
|
||||||
|
}
|
||||||
|
if len(str)-offset < (maxWidth - 3) { // 15 - 5 < 10 - 3 = 10 < 7
|
||||||
|
offset = len(str) - (maxWidth - 3)
|
||||||
|
}
|
||||||
|
abrevMarker := "..."
|
||||||
|
if offset <= 4 {
|
||||||
|
return str[0:maxWidth-3] + abrevMarker, nil // str.substring(0, maxWidth - 3) + abrevMarker;
|
||||||
|
}
|
||||||
|
if maxWidth < 7 {
|
||||||
|
err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width with offset is 7")
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if (offset + maxWidth - 3) < len(str) { // 5 + (10-3) < 15 = 12 < 15
|
||||||
|
abrevStr, _ := Abbreviate(str[offset:len(str)], (maxWidth - 3))
|
||||||
|
return abrevMarker + abrevStr, nil // abrevMarker + abbreviate(str.substring(offset), maxWidth - 3);
|
||||||
|
}
|
||||||
|
return abrevMarker + str[(len(str)-(maxWidth-3)):len(str)], nil // abrevMarker + str.substring(str.length() - (maxWidth - 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DeleteWhiteSpace deletes all whitespaces from a string as defined by unicode.IsSpace(rune).
|
||||||
|
It returns the string without whitespaces.
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
str - the string to delete whitespace from, may be nil
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
the string without whitespaces
|
||||||
|
*/
|
||||||
|
func DeleteWhiteSpace(str string) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
sz := len(str)
|
||||||
|
var chs bytes.Buffer
|
||||||
|
count := 0
|
||||||
|
for i := 0; i < sz; i++ {
|
||||||
|
ch := rune(str[i])
|
||||||
|
if !unicode.IsSpace(ch) {
|
||||||
|
chs.WriteRune(ch)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count == sz {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return chs.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IndexOfDifference compares two strings, and returns the index at which the strings begin to differ.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str1 - the first string
|
||||||
|
str2 - the second string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
the index where str1 and str2 begin to differ; -1 if they are equal
|
||||||
|
*/
|
||||||
|
func IndexOfDifference(str1 string, str2 string) int {
|
||||||
|
if str1 == str2 {
|
||||||
|
return INDEX_NOT_FOUND
|
||||||
|
}
|
||||||
|
if IsEmpty(str1) || IsEmpty(str2) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
for i = 0; i < len(str1) && i < len(str2); i++ {
|
||||||
|
if rune(str1[i]) != rune(str2[i]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < len(str2) || i < len(str1) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return INDEX_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IsBlank checks if a string is whitespace or empty (""). Observe the following behavior:
|
||||||
|
|
||||||
|
goutils.IsBlank("") = true
|
||||||
|
goutils.IsBlank(" ") = true
|
||||||
|
goutils.IsBlank("bob") = false
|
||||||
|
goutils.IsBlank(" bob ") = false
|
||||||
|
|
||||||
|
Parameter:
|
||||||
|
str - the string to check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
true - if the string is whitespace or empty ("")
|
||||||
|
*/
|
||||||
|
func IsBlank(str string) bool {
|
||||||
|
strLen := len(str)
|
||||||
|
if str == "" || strLen == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for i := 0; i < strLen; i++ {
|
||||||
|
if unicode.IsSpace(rune(str[i])) == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IndexOf returns the index of the first instance of sub in str, with the search beginning from the
|
||||||
|
index start point specified. -1 is returned if sub is not present in str.
|
||||||
|
|
||||||
|
An empty string ("") will return -1 (INDEX_NOT_FOUND). A negative start position is treated as zero.
|
||||||
|
A start position greater than the string length returns -1.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to check
|
||||||
|
sub - the substring to find
|
||||||
|
start - the start position; negative treated as zero
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
the first index where the sub string was found (always >= start)
|
||||||
|
*/
|
||||||
|
func IndexOf(str string, sub string, start int) int {
|
||||||
|
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) < start {
|
||||||
|
return INDEX_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsEmpty(str) || IsEmpty(sub) {
|
||||||
|
return INDEX_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
partialIndex := strings.Index(str[start:len(str)], sub)
|
||||||
|
if partialIndex == -1 {
|
||||||
|
return INDEX_NOT_FOUND
|
||||||
|
}
|
||||||
|
return partialIndex + start
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks if a string is empty (""). Returns true if empty, and false otherwise.
|
||||||
|
func IsEmpty(str string) bool {
|
||||||
|
return len(str) == 0
|
||||||
|
}
|
356
vendor/github.com/aokoli/goutils/wordutils.go
generated
vendored
Normal file
356
vendor/github.com/aokoli/goutils/wordutils.go
generated
vendored
Normal file
|
@ -0,0 +1,356 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 Alexander Okoli
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package goutils provides utility functions to manipulate strings in various ways.
|
||||||
|
The code snippets below show examples of how to use goutils. Some functions return
|
||||||
|
errors while others do not, so usage would vary as a result.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aokoli/goutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// EXAMPLE 1: A goutils function which returns no errors
|
||||||
|
fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// EXAMPLE 2: A goutils function which returns an error
|
||||||
|
rand1, err1 := goutils.Random (-1, 0, 0, true, true)
|
||||||
|
|
||||||
|
if err1 != nil {
|
||||||
|
fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...)
|
||||||
|
} else {
|
||||||
|
fmt.Println(rand1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package goutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VERSION indicates the current version of goutils
|
||||||
|
const VERSION = "1.0.0"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wrap wraps a single line of text, identifying words by ' '.
|
||||||
|
New lines will be separated by '\n'. Very long words, such as URLs will not be wrapped.
|
||||||
|
Leading spaces on a new line are stripped. Trailing spaces are not stripped.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to be word wrapped
|
||||||
|
wrapLength - the column (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
a line with newlines inserted
|
||||||
|
*/
|
||||||
|
func Wrap(str string, wrapLength int) string {
|
||||||
|
return WrapCustom(str, wrapLength, "", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
WrapCustom wraps a single line of text, identifying words by ' '.
|
||||||
|
Leading spaces on a new line are stripped. Trailing spaces are not stripped.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to be word wrapped
|
||||||
|
wrapLength - the column number (a column can fit only one character) to wrap the words at, less than 1 is treated as 1
|
||||||
|
newLineStr - the string to insert for a new line, "" uses '\n'
|
||||||
|
wrapLongWords - true if long words (such as URLs) should be wrapped
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
a line with newlines inserted
|
||||||
|
*/
|
||||||
|
func WrapCustom(str string, wrapLength int, newLineStr string, wrapLongWords bool) string {
|
||||||
|
|
||||||
|
if str == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if newLineStr == "" {
|
||||||
|
newLineStr = "\n" // TODO Assumes "\n" is seperator. Explore SystemUtils.LINE_SEPARATOR from Apache Commons
|
||||||
|
}
|
||||||
|
if wrapLength < 1 {
|
||||||
|
wrapLength = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
inputLineLength := len(str)
|
||||||
|
offset := 0
|
||||||
|
|
||||||
|
var wrappedLine bytes.Buffer
|
||||||
|
|
||||||
|
for inputLineLength-offset > wrapLength {
|
||||||
|
|
||||||
|
if rune(str[offset]) == ' ' {
|
||||||
|
offset++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
end := wrapLength + offset + 1
|
||||||
|
spaceToWrapAt := strings.LastIndex(str[offset:end], " ") + offset
|
||||||
|
|
||||||
|
if spaceToWrapAt >= offset {
|
||||||
|
// normal word (not longer than wrapLength)
|
||||||
|
wrappedLine.WriteString(str[offset:spaceToWrapAt])
|
||||||
|
wrappedLine.WriteString(newLineStr)
|
||||||
|
offset = spaceToWrapAt + 1
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// long word or URL
|
||||||
|
if wrapLongWords {
|
||||||
|
end := wrapLength + offset
|
||||||
|
// long words are wrapped one line at a time
|
||||||
|
wrappedLine.WriteString(str[offset:end])
|
||||||
|
wrappedLine.WriteString(newLineStr)
|
||||||
|
offset += wrapLength
|
||||||
|
} else {
|
||||||
|
// long words aren't wrapped, just extended beyond limit
|
||||||
|
end := wrapLength + offset
|
||||||
|
spaceToWrapAt = strings.IndexRune(str[end:len(str)], ' ') + end
|
||||||
|
if spaceToWrapAt >= 0 {
|
||||||
|
wrappedLine.WriteString(str[offset:spaceToWrapAt])
|
||||||
|
wrappedLine.WriteString(newLineStr)
|
||||||
|
offset = spaceToWrapAt + 1
|
||||||
|
} else {
|
||||||
|
wrappedLine.WriteString(str[offset:len(str)])
|
||||||
|
offset = inputLineLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedLine.WriteString(str[offset:len(str)])
|
||||||
|
|
||||||
|
return wrappedLine.String()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Capitalize capitalizes all the delimiter separated words in a string. Only the first letter of each word is changed.
|
||||||
|
To convert the rest of each word to lowercase at the same time, use CapitalizeFully(str string, delimiters ...rune).
|
||||||
|
The delimiters represent a set of characters understood to separate words. The first string character
|
||||||
|
and the first non-delimiter character after a delimiter will be capitalized. A "" input string returns "".
|
||||||
|
Capitalization uses the Unicode title case, normally equivalent to upper case.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to capitalize
|
||||||
|
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
capitalized string
|
||||||
|
*/
|
||||||
|
func Capitalize(str string, delimiters ...rune) string {
|
||||||
|
|
||||||
|
var delimLen int
|
||||||
|
|
||||||
|
if delimiters == nil {
|
||||||
|
delimLen = -1
|
||||||
|
} else {
|
||||||
|
delimLen = len(delimiters)
|
||||||
|
}
|
||||||
|
|
||||||
|
if str == "" || delimLen == 0 {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := []rune(str)
|
||||||
|
capitalizeNext := true
|
||||||
|
for i := 0; i < len(buffer); i++ {
|
||||||
|
ch := buffer[i]
|
||||||
|
if isDelimiter(ch, delimiters...) {
|
||||||
|
capitalizeNext = true
|
||||||
|
} else if capitalizeNext {
|
||||||
|
buffer[i] = unicode.ToTitle(ch)
|
||||||
|
capitalizeNext = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(buffer)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
CapitalizeFully converts all the delimiter separated words in a string into capitalized words, that is each word is made up of a
|
||||||
|
titlecase character and then a series of lowercase characters. The delimiters represent a set of characters understood
|
||||||
|
to separate words. The first string character and the first non-delimiter character after a delimiter will be capitalized.
|
||||||
|
Capitalization uses the Unicode title case, normally equivalent to upper case.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to capitalize fully
|
||||||
|
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
capitalized string
|
||||||
|
*/
|
||||||
|
func CapitalizeFully(str string, delimiters ...rune) string {
|
||||||
|
|
||||||
|
var delimLen int
|
||||||
|
|
||||||
|
if delimiters == nil {
|
||||||
|
delimLen = -1
|
||||||
|
} else {
|
||||||
|
delimLen = len(delimiters)
|
||||||
|
}
|
||||||
|
|
||||||
|
if str == "" || delimLen == 0 {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
str = strings.ToLower(str)
|
||||||
|
return Capitalize(str, delimiters...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Uncapitalize uncapitalizes all the whitespace separated words in a string. Only the first letter of each word is changed.
|
||||||
|
The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter
|
||||||
|
character after a delimiter will be uncapitalized. Whitespace is defined by unicode.IsSpace(char).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to uncapitalize fully
|
||||||
|
delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
uncapitalized string
|
||||||
|
*/
|
||||||
|
func Uncapitalize(str string, delimiters ...rune) string {
|
||||||
|
|
||||||
|
var delimLen int
|
||||||
|
|
||||||
|
if delimiters == nil {
|
||||||
|
delimLen = -1
|
||||||
|
} else {
|
||||||
|
delimLen = len(delimiters)
|
||||||
|
}
|
||||||
|
|
||||||
|
if str == "" || delimLen == 0 {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := []rune(str)
|
||||||
|
uncapitalizeNext := true // TODO Always makes capitalize/un apply to first char.
|
||||||
|
for i := 0; i < len(buffer); i++ {
|
||||||
|
ch := buffer[i]
|
||||||
|
if isDelimiter(ch, delimiters...) {
|
||||||
|
uncapitalizeNext = true
|
||||||
|
} else if uncapitalizeNext {
|
||||||
|
buffer[i] = unicode.ToLower(ch)
|
||||||
|
uncapitalizeNext = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SwapCase swaps the case of a string using a word based algorithm.
|
||||||
|
|
||||||
|
Conversion algorithm:
|
||||||
|
|
||||||
|
Upper case character converts to Lower case
|
||||||
|
Title case character converts to Lower case
|
||||||
|
Lower case character after Whitespace or at start converts to Title case
|
||||||
|
Other Lower case character converts to Upper case
|
||||||
|
Whitespace is defined by unicode.IsSpace(char).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to swap case
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
the changed string
|
||||||
|
*/
|
||||||
|
func SwapCase(str string) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
buffer := []rune(str)
|
||||||
|
|
||||||
|
whitespace := true
|
||||||
|
|
||||||
|
for i := 0; i < len(buffer); i++ {
|
||||||
|
ch := buffer[i]
|
||||||
|
if unicode.IsUpper(ch) {
|
||||||
|
buffer[i] = unicode.ToLower(ch)
|
||||||
|
whitespace = false
|
||||||
|
} else if unicode.IsTitle(ch) {
|
||||||
|
buffer[i] = unicode.ToLower(ch)
|
||||||
|
whitespace = false
|
||||||
|
} else if unicode.IsLower(ch) {
|
||||||
|
if whitespace {
|
||||||
|
buffer[i] = unicode.ToTitle(ch)
|
||||||
|
whitespace = false
|
||||||
|
} else {
|
||||||
|
buffer[i] = unicode.ToUpper(ch)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
whitespace = unicode.IsSpace(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initials extracts the initial letters from each word in the string. The first letter of the string and all first
|
||||||
|
letters after the defined delimiters are returned as a new string. Their case is not changed. If the delimiters
|
||||||
|
parameter is excluded, then Whitespace is used. Whitespace is defined by unicode.IsSpacea(char). An empty delimiter array returns an empty string.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
str - the string to get initials from
|
||||||
|
delimiters - set of characters to determine words, exclusion of this parameter means whitespace would be delimeter
|
||||||
|
Returns:
|
||||||
|
string of initial letters
|
||||||
|
*/
|
||||||
|
func Initials(str string, delimiters ...rune) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
if delimiters != nil && len(delimiters) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
strLen := len(str)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
lastWasGap := true
|
||||||
|
for i := 0; i < strLen; i++ {
|
||||||
|
ch := rune(str[i])
|
||||||
|
|
||||||
|
if isDelimiter(ch, delimiters...) {
|
||||||
|
lastWasGap = true
|
||||||
|
} else if lastWasGap {
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
lastWasGap = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// private function (lower case func name)
|
||||||
|
func isDelimiter(ch rune, delimiters ...rune) bool {
|
||||||
|
if delimiters == nil {
|
||||||
|
return unicode.IsSpace(ch)
|
||||||
|
}
|
||||||
|
for _, delimiter := range delimiters {
|
||||||
|
if ch == delimiter {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
22
vendor/github.com/huandu/xstrings/LICENSE
generated
vendored
Normal file
22
vendor/github.com/huandu/xstrings/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Huan Du
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
25
vendor/github.com/huandu/xstrings/common.go
generated
vendored
Normal file
25
vendor/github.com/huandu/xstrings/common.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2015 Huan Du. All rights reserved.
|
||||||
|
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xstrings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const _BUFFER_INIT_GROW_SIZE_MAX = 2048
|
||||||
|
|
||||||
|
// Lazy initialize a buffer.
|
||||||
|
func allocBuffer(orig, cur string) *bytes.Buffer {
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
maxSize := len(orig) * 4
|
||||||
|
|
||||||
|
// Avoid to reserve too much memory at once.
|
||||||
|
if maxSize > _BUFFER_INIT_GROW_SIZE_MAX {
|
||||||
|
maxSize = _BUFFER_INIT_GROW_SIZE_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Grow(maxSize)
|
||||||
|
output.WriteString(orig[:len(orig)-len(cur)])
|
||||||
|
return output
|
||||||
|
}
|
357
vendor/github.com/huandu/xstrings/convert.go
generated
vendored
Normal file
357
vendor/github.com/huandu/xstrings/convert.go
generated
vendored
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
// Copyright 2015 Huan Du. All rights reserved.
|
||||||
|
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xstrings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/rand"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToCamelCase can convert all lower case characters behind underscores
|
||||||
|
// to upper case character.
|
||||||
|
// Underscore character will be removed in result except following cases.
|
||||||
|
// * More than 1 underscore.
|
||||||
|
// "a__b" => "A_B"
|
||||||
|
// * At the beginning of string.
|
||||||
|
// "_a" => "_A"
|
||||||
|
// * At the end of string.
|
||||||
|
// "ab_" => "Ab_"
|
||||||
|
func ToCamelCase(str string) string {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
var r0, r1 rune
|
||||||
|
var size int
|
||||||
|
|
||||||
|
// leading '_' will appear in output.
|
||||||
|
for len(str) > 0 {
|
||||||
|
r0, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:]
|
||||||
|
|
||||||
|
if r0 != '_' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(r0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(unicode.ToUpper(r0))
|
||||||
|
r0, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:]
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r1 = r0
|
||||||
|
r0, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:]
|
||||||
|
|
||||||
|
if r1 == '_' && r0 != '_' {
|
||||||
|
r0 = unicode.ToUpper(r0)
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(r1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(r0)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSnakeCase can convert all upper case characters in a string to
|
||||||
|
// underscore format.
|
||||||
|
//
|
||||||
|
// Some samples.
|
||||||
|
// "FirstName" => "first_name"
|
||||||
|
// "HTTPServer" => "http_server"
|
||||||
|
// "NoHTTPS" => "no_https"
|
||||||
|
// "GO_PATH" => "go_path"
|
||||||
|
// "GO PATH" => "go_path" // space is converted to underscore.
|
||||||
|
// "GO-PATH" => "go_path" // hyphen is converted to underscore.
|
||||||
|
func ToSnakeCase(str string) string {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
var prev, r0, r1 rune
|
||||||
|
var size int
|
||||||
|
|
||||||
|
r0 = '_'
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
prev = r0
|
||||||
|
r0, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:]
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case r0 == utf8.RuneError:
|
||||||
|
buf.WriteByte(byte(str[0]))
|
||||||
|
|
||||||
|
case unicode.IsUpper(r0):
|
||||||
|
if prev != '_' {
|
||||||
|
buf.WriteRune('_')
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(unicode.ToLower(r0))
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
r0, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:]
|
||||||
|
|
||||||
|
if !unicode.IsUpper(r0) {
|
||||||
|
buf.WriteRune(r0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// find next non-upper-case character and insert `_` properly.
|
||||||
|
// it's designed to convert `HTTPServer` to `http_server`.
|
||||||
|
// if there are more than 2 adjacent upper case characters in a word,
|
||||||
|
// treat them as an abbreviation plus a normal word.
|
||||||
|
for len(str) > 0 {
|
||||||
|
r1 = r0
|
||||||
|
r0, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:]
|
||||||
|
|
||||||
|
if r0 == utf8.RuneError {
|
||||||
|
buf.WriteRune(unicode.ToLower(r1))
|
||||||
|
buf.WriteByte(byte(str[0]))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !unicode.IsUpper(r0) {
|
||||||
|
if r0 == '_' || r0 == ' ' || r0 == '-' {
|
||||||
|
r0 = '_'
|
||||||
|
|
||||||
|
buf.WriteRune(unicode.ToLower(r1))
|
||||||
|
} else {
|
||||||
|
buf.WriteRune('_')
|
||||||
|
buf.WriteRune(unicode.ToLower(r1))
|
||||||
|
buf.WriteRune(r0)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(unicode.ToLower(r1))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) == 0 || r0 == '_' {
|
||||||
|
buf.WriteRune(unicode.ToLower(r0))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if r0 == ' ' || r0 == '-' {
|
||||||
|
r0 = '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(r0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapCase will swap characters case from upper to lower or lower to upper.
|
||||||
|
func SwapCase(str string) string {
|
||||||
|
var r rune
|
||||||
|
var size int
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case unicode.IsUpper(r):
|
||||||
|
buf.WriteRune(unicode.ToLower(r))
|
||||||
|
|
||||||
|
case unicode.IsLower(r):
|
||||||
|
buf.WriteRune(unicode.ToUpper(r))
|
||||||
|
|
||||||
|
default:
|
||||||
|
buf.WriteRune(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstRuneToUpper converts first rune to upper case if necessary.
|
||||||
|
func FirstRuneToUpper(str string) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
r, size := utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
if !unicode.IsLower(r) {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
buf.WriteRune(unicode.ToUpper(r))
|
||||||
|
buf.WriteString(str[size:])
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstRuneToLower converts first rune to lower case if necessary.
|
||||||
|
func FirstRuneToLower(str string) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
r, size := utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
if !unicode.IsUpper(r) {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
buf.WriteRune(unicode.ToLower(r))
|
||||||
|
buf.WriteString(str[size:])
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle randomizes runes in a string and returns the result.
|
||||||
|
// It uses default random source in `math/rand`.
|
||||||
|
func Shuffle(str string) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
runes := []rune(str)
|
||||||
|
index := 0
|
||||||
|
|
||||||
|
for i := len(runes) - 1; i > 0; i-- {
|
||||||
|
index = rand.Intn(i + 1)
|
||||||
|
|
||||||
|
if i != index {
|
||||||
|
runes[i], runes[index] = runes[index], runes[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(runes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShuffleSource randomizes runes in a string with given random source.
|
||||||
|
func ShuffleSource(str string, src rand.Source) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
runes := []rune(str)
|
||||||
|
index := 0
|
||||||
|
r := rand.New(src)
|
||||||
|
|
||||||
|
for i := len(runes) - 1; i > 0; i-- {
|
||||||
|
index = r.Intn(i + 1)
|
||||||
|
|
||||||
|
if i != index {
|
||||||
|
runes[i], runes[index] = runes[index], runes[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(runes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Successor returns the successor to string.
|
||||||
|
//
|
||||||
|
// If there is one alphanumeric rune is found in string, increase the rune by 1.
|
||||||
|
// If increment generates a "carry", the rune to the left of it is incremented.
|
||||||
|
// This process repeats until there is no carry, adding an additional rune if necessary.
|
||||||
|
//
|
||||||
|
// If there is no alphanumeric rune, the rightmost rune will be increased by 1
|
||||||
|
// regardless whether the result is a valid rune or not.
|
||||||
|
//
|
||||||
|
// Only following characters are alphanumeric.
|
||||||
|
// * a - z
|
||||||
|
// * A - Z
|
||||||
|
// * 0 - 9
|
||||||
|
//
|
||||||
|
// Samples (borrowed from ruby's String#succ document):
|
||||||
|
// "abcd" => "abce"
|
||||||
|
// "THX1138" => "THX1139"
|
||||||
|
// "<<koala>>" => "<<koalb>>"
|
||||||
|
// "1999zzz" => "2000aaa"
|
||||||
|
// "ZZZ9999" => "AAAA0000"
|
||||||
|
// "***" => "**+"
|
||||||
|
func Successor(str string) string {
|
||||||
|
if str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
var r rune
|
||||||
|
var i int
|
||||||
|
carry := ' '
|
||||||
|
runes := []rune(str)
|
||||||
|
l := len(runes)
|
||||||
|
lastAlphanumeric := l
|
||||||
|
|
||||||
|
for i = l - 1; i >= 0; i-- {
|
||||||
|
r = runes[i]
|
||||||
|
|
||||||
|
if ('a' <= r && r <= 'y') ||
|
||||||
|
('A' <= r && r <= 'Y') ||
|
||||||
|
('0' <= r && r <= '8') {
|
||||||
|
runes[i]++
|
||||||
|
carry = ' '
|
||||||
|
lastAlphanumeric = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r {
|
||||||
|
case 'z':
|
||||||
|
runes[i] = 'a'
|
||||||
|
carry = 'a'
|
||||||
|
lastAlphanumeric = i
|
||||||
|
|
||||||
|
case 'Z':
|
||||||
|
runes[i] = 'A'
|
||||||
|
carry = 'A'
|
||||||
|
lastAlphanumeric = i
|
||||||
|
|
||||||
|
case '9':
|
||||||
|
runes[i] = '0'
|
||||||
|
carry = '0'
|
||||||
|
lastAlphanumeric = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needs to add one character for carry.
|
||||||
|
if i < 0 && carry != ' ' {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
buf.Grow(l + 4) // Reserve enough space for write.
|
||||||
|
|
||||||
|
if lastAlphanumeric != 0 {
|
||||||
|
buf.WriteString(str[:lastAlphanumeric])
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteRune(carry)
|
||||||
|
|
||||||
|
for _, r = range runes[lastAlphanumeric:] {
|
||||||
|
buf.WriteRune(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// No alphanumeric character. Simply increase last rune's value.
|
||||||
|
if lastAlphanumeric == l {
|
||||||
|
runes[l-1]++
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(runes)
|
||||||
|
}
|
120
vendor/github.com/huandu/xstrings/count.go
generated
vendored
Normal file
120
vendor/github.com/huandu/xstrings/count.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// Copyright 2015 Huan Du. All rights reserved.
|
||||||
|
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xstrings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get str's utf8 rune length.
|
||||||
|
func Len(str string) int {
|
||||||
|
return utf8.RuneCountInString(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count number of words in a string.
|
||||||
|
//
|
||||||
|
// Word is defined as a locale dependent string containing alphabetic characters,
|
||||||
|
// which may also contain but not start with `'` and `-` characters.
|
||||||
|
func WordCount(str string) int {
|
||||||
|
var r rune
|
||||||
|
var size, n int
|
||||||
|
|
||||||
|
inWord := false
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case isAlphabet(r):
|
||||||
|
if !inWord {
|
||||||
|
inWord = true
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
case inWord && (r == '\'' || r == '-'):
|
||||||
|
// Still in word.
|
||||||
|
|
||||||
|
default:
|
||||||
|
inWord = false
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
const minCJKCharacter = '\u3400'
|
||||||
|
|
||||||
|
// Checks r is a letter but not CJK character.
|
||||||
|
func isAlphabet(r rune) bool {
|
||||||
|
if !unicode.IsLetter(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// Quick check for non-CJK character.
|
||||||
|
case r < minCJKCharacter:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Common CJK characters.
|
||||||
|
case r >= '\u4E00' && r <= '\u9FCC':
|
||||||
|
return false
|
||||||
|
|
||||||
|
// Rare CJK characters.
|
||||||
|
case r >= '\u3400' && r <= '\u4D85':
|
||||||
|
return false
|
||||||
|
|
||||||
|
// Rare and historic CJK characters.
|
||||||
|
case r >= '\U00020000' && r <= '\U0002B81D':
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width returns string width in monotype font.
|
||||||
|
// Multi-byte characters are usually twice the width of single byte characters.
|
||||||
|
//
|
||||||
|
// Algorithm comes from `mb_strwidth` in PHP.
|
||||||
|
// http://php.net/manual/en/function.mb-strwidth.php
|
||||||
|
func Width(str string) int {
|
||||||
|
var r rune
|
||||||
|
var size, n int
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
n += RuneWidth(r)
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuneWidth returns character width in monotype font.
|
||||||
|
// Multi-byte characters are usually twice the width of single byte characters.
|
||||||
|
//
|
||||||
|
// Algorithm comes from `mb_strwidth` in PHP.
|
||||||
|
// http://php.net/manual/en/function.mb-strwidth.php
|
||||||
|
func RuneWidth(r rune) int {
|
||||||
|
switch {
|
||||||
|
case r == utf8.RuneError || r < '\x20':
|
||||||
|
return 0
|
||||||
|
|
||||||
|
case '\x20' <= r && r < '\u2000':
|
||||||
|
return 1
|
||||||
|
|
||||||
|
case '\u2000' <= r && r < '\uFF61':
|
||||||
|
return 2
|
||||||
|
|
||||||
|
case '\uFF61' <= r && r < '\uFFA0':
|
||||||
|
return 1
|
||||||
|
|
||||||
|
case '\uFFA0' <= r:
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
8
vendor/github.com/huandu/xstrings/doc.go
generated
vendored
Normal file
8
vendor/github.com/huandu/xstrings/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright 2015 Huan Du. All rights reserved.
|
||||||
|
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package `xstrings` is to provide string algorithms which are useful but not included in `strings` package.
|
||||||
|
// See project home page for details. https://github.com/huandu/xstrings
|
||||||
|
//
|
||||||
|
// Package `xstrings` assumes all strings are encoded in utf8.
|
||||||
|
package xstrings
|
170
vendor/github.com/huandu/xstrings/format.go
generated
vendored
Normal file
170
vendor/github.com/huandu/xstrings/format.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright 2015 Huan Du. All rights reserved.
|
||||||
|
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xstrings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExpandTabs can expand tabs ('\t') rune in str to one or more spaces dpending on
|
||||||
|
// current column and tabSize.
|
||||||
|
// The column number is reset to zero after each newline ('\n') occurring in the str.
|
||||||
|
//
|
||||||
|
// ExpandTabs uses RuneWidth to decide rune's width.
|
||||||
|
// For example, CJK characters will be treated as two characters.
|
||||||
|
//
|
||||||
|
// If tabSize <= 0, ExpandTabs panics with error.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// ExpandTabs("a\tbc\tdef\tghij\tk", 4) => "a bc def ghij k"
|
||||||
|
// ExpandTabs("abcdefg\thij\nk\tl", 4) => "abcdefg hij\nk l"
|
||||||
|
// ExpandTabs("z中\t文\tw", 4) => "z中 文 w"
|
||||||
|
func ExpandTabs(str string, tabSize int) string {
|
||||||
|
if tabSize <= 0 {
|
||||||
|
panic("tab size must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r rune
|
||||||
|
var i, size, column, expand int
|
||||||
|
var output *bytes.Buffer
|
||||||
|
|
||||||
|
orig := str
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
if r == '\t' {
|
||||||
|
expand = tabSize - column%tabSize
|
||||||
|
|
||||||
|
if output == nil {
|
||||||
|
output = allocBuffer(orig, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 0; i < expand; i++ {
|
||||||
|
output.WriteByte(byte(' '))
|
||||||
|
}
|
||||||
|
|
||||||
|
column += expand
|
||||||
|
} else {
|
||||||
|
if r == '\n' {
|
||||||
|
column = 0
|
||||||
|
} else {
|
||||||
|
column += RuneWidth(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != nil {
|
||||||
|
output.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if output == nil {
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftJustify returns a string with pad string at right side if str's rune length is smaller than length.
|
||||||
|
// If str's rune length is larger than length, str itself will be returned.
|
||||||
|
//
|
||||||
|
// If pad is an empty string, str will be returned.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// LeftJustify("hello", 4, " ") => "hello"
|
||||||
|
// LeftJustify("hello", 10, " ") => "hello "
|
||||||
|
// LeftJustify("hello", 10, "123") => "hello12312"
|
||||||
|
func LeftJustify(str string, length int, pad string) string {
|
||||||
|
l := Len(str)
|
||||||
|
|
||||||
|
if l >= length || pad == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
remains := length - l
|
||||||
|
padLen := Len(pad)
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
output.Grow(len(str) + (remains/padLen+1)*len(pad))
|
||||||
|
output.WriteString(str)
|
||||||
|
writePadString(output, pad, padLen, remains)
|
||||||
|
return output.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightJustify returns a string with pad string at left side if str's rune length is smaller than length.
|
||||||
|
// If str's rune length is larger than length, str itself will be returned.
|
||||||
|
//
|
||||||
|
// If pad is an empty string, str will be returned.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// RightJustify("hello", 4, " ") => "hello"
|
||||||
|
// RightJustify("hello", 10, " ") => " hello"
|
||||||
|
// RightJustify("hello", 10, "123") => "12312hello"
|
||||||
|
func RightJustify(str string, length int, pad string) string {
|
||||||
|
l := Len(str)
|
||||||
|
|
||||||
|
if l >= length || pad == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
remains := length - l
|
||||||
|
padLen := Len(pad)
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
output.Grow(len(str) + (remains/padLen+1)*len(pad))
|
||||||
|
writePadString(output, pad, padLen, remains)
|
||||||
|
output.WriteString(str)
|
||||||
|
return output.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center returns a string with pad string at both side if str's rune length is smaller than length.
|
||||||
|
// If str's rune length is larger than length, str itself will be returned.
|
||||||
|
//
|
||||||
|
// If pad is an empty string, str will be returned.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// Center("hello", 4, " ") => "hello"
|
||||||
|
// Center("hello", 10, " ") => " hello "
|
||||||
|
// Center("hello", 10, "123") => "12hello123"
|
||||||
|
func Center(str string, length int, pad string) string {
|
||||||
|
l := Len(str)
|
||||||
|
|
||||||
|
if l >= length || pad == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
remains := length - l
|
||||||
|
padLen := Len(pad)
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
output.Grow(len(str) + (remains/padLen+1)*len(pad))
|
||||||
|
writePadString(output, pad, padLen, remains/2)
|
||||||
|
output.WriteString(str)
|
||||||
|
writePadString(output, pad, padLen, (remains+1)/2)
|
||||||
|
return output.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePadString(output *bytes.Buffer, pad string, padLen, remains int) {
|
||||||
|
var r rune
|
||||||
|
var size int
|
||||||
|
|
||||||
|
repeats := remains / padLen
|
||||||
|
|
||||||
|
for i := 0; i < repeats; i++ {
|
||||||
|
output.WriteString(pad)
|
||||||
|
}
|
||||||
|
|
||||||
|
remains = remains % padLen
|
||||||
|
|
||||||
|
if remains != 0 {
|
||||||
|
for i := 0; i < remains; i++ {
|
||||||
|
r, size = utf8.DecodeRuneInString(pad)
|
||||||
|
output.WriteRune(r)
|
||||||
|
pad = pad[size:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
217
vendor/github.com/huandu/xstrings/manipulate.go
generated
vendored
Normal file
217
vendor/github.com/huandu/xstrings/manipulate.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
// Copyright 2015 Huan Du. All rights reserved.
|
||||||
|
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xstrings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reverse a utf8 encoded string.
|
||||||
|
func Reverse(str string) string {
|
||||||
|
var size int
|
||||||
|
|
||||||
|
tail := len(str)
|
||||||
|
buf := make([]byte, tail)
|
||||||
|
s := buf
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
_, size = utf8.DecodeRuneInString(str)
|
||||||
|
tail -= size
|
||||||
|
s = append(s[:tail], []byte(str[:size])...)
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice a string by rune.
|
||||||
|
//
|
||||||
|
// Start must satisfy 0 <= start <= rune length.
|
||||||
|
//
|
||||||
|
// End can be positive, zero or negative.
|
||||||
|
// If end >= 0, start and end must satisfy start <= end <= rune length.
|
||||||
|
// If end < 0, it means slice to the end of string.
|
||||||
|
//
|
||||||
|
// Otherwise, Slice will panic as out of range.
|
||||||
|
func Slice(str string, start, end int) string {
|
||||||
|
var size, startPos, endPos int
|
||||||
|
|
||||||
|
origin := str
|
||||||
|
|
||||||
|
if start < 0 || end > len(str) || (end >= 0 && start > end) {
|
||||||
|
panic("out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
if end >= 0 {
|
||||||
|
end -= start
|
||||||
|
}
|
||||||
|
|
||||||
|
for start > 0 && len(str) > 0 {
|
||||||
|
_, size = utf8.DecodeRuneInString(str)
|
||||||
|
start--
|
||||||
|
startPos += size
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if end < 0 {
|
||||||
|
return origin[startPos:]
|
||||||
|
}
|
||||||
|
|
||||||
|
endPos = startPos
|
||||||
|
|
||||||
|
for end > 0 && len(str) > 0 {
|
||||||
|
_, size = utf8.DecodeRuneInString(str)
|
||||||
|
end--
|
||||||
|
endPos += size
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str) == 0 && (start > 0 || end > 0) {
|
||||||
|
panic("out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
return origin[startPos:endPos]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partition splits a string by sep into three parts.
|
||||||
|
// The return value is a slice of strings with head, match and tail.
|
||||||
|
//
|
||||||
|
// If str contains sep, for example "hello" and "l", Partition returns
|
||||||
|
// "he", "l", "lo"
|
||||||
|
//
|
||||||
|
// If str doesn't contain sep, for example "hello" and "x", Partition returns
|
||||||
|
// "hello", "", ""
|
||||||
|
func Partition(str, sep string) (head, match, tail string) {
|
||||||
|
index := strings.Index(str, sep)
|
||||||
|
|
||||||
|
if index == -1 {
|
||||||
|
head = str
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
head = str[:index]
|
||||||
|
match = str[index : index+len(sep)]
|
||||||
|
tail = str[index+len(sep):]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastPartition splits a string by last instance of sep into three parts.
|
||||||
|
// The return value is a slice of strings with head, match and tail.
|
||||||
|
//
|
||||||
|
// If str contains sep, for example "hello" and "l", LastPartition returns
|
||||||
|
// "hel", "l", "o"
|
||||||
|
//
|
||||||
|
// If str doesn't contain sep, for example "hello" and "x", LastPartition returns
|
||||||
|
// "", "", "hello"
|
||||||
|
func LastPartition(str, sep string) (head, match, tail string) {
|
||||||
|
index := strings.LastIndex(str, sep)
|
||||||
|
|
||||||
|
if index == -1 {
|
||||||
|
tail = str
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
head = str[:index]
|
||||||
|
match = str[index : index+len(sep)]
|
||||||
|
tail = str[index+len(sep):]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert src into dst at given rune index.
|
||||||
|
// Index is counted by runes instead of bytes.
|
||||||
|
//
|
||||||
|
// If index is out of range of dst, panic with out of range.
|
||||||
|
func Insert(dst, src string, index int) string {
|
||||||
|
return Slice(dst, 0, index) + src + Slice(dst, index, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrubs invalid utf8 bytes with repl string.
|
||||||
|
// Adjacent invalid bytes are replaced only once.
|
||||||
|
func Scrub(str, repl string) string {
|
||||||
|
var buf *bytes.Buffer
|
||||||
|
var r rune
|
||||||
|
var size, pos int
|
||||||
|
var hasError bool
|
||||||
|
|
||||||
|
origin := str
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
if r == utf8.RuneError {
|
||||||
|
if !hasError {
|
||||||
|
if buf == nil {
|
||||||
|
buf = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(origin[:pos])
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
} else if hasError {
|
||||||
|
hasError = false
|
||||||
|
buf.WriteString(repl)
|
||||||
|
|
||||||
|
origin = origin[pos:]
|
||||||
|
pos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += size
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf != nil {
|
||||||
|
buf.WriteString(origin)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// No invalid byte.
|
||||||
|
return origin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splits a string into words. Returns a slice of words.
|
||||||
|
// If there is no word in a string, return nil.
|
||||||
|
//
|
||||||
|
// Word is defined as a locale dependent string containing alphabetic characters,
|
||||||
|
// which may also contain but not start with `'` and `-` characters.
|
||||||
|
func WordSplit(str string) []string {
|
||||||
|
var word string
|
||||||
|
var words []string
|
||||||
|
var r rune
|
||||||
|
var size, pos int
|
||||||
|
|
||||||
|
inWord := false
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case isAlphabet(r):
|
||||||
|
if !inWord {
|
||||||
|
inWord = true
|
||||||
|
word = str
|
||||||
|
pos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case inWord && (r == '\'' || r == '-'):
|
||||||
|
// Still in word.
|
||||||
|
|
||||||
|
default:
|
||||||
|
if inWord {
|
||||||
|
inWord = false
|
||||||
|
words = append(words, word[:pos])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += size
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if inWord {
|
||||||
|
words = append(words, word[:pos])
|
||||||
|
}
|
||||||
|
|
||||||
|
return words
|
||||||
|
}
|
545
vendor/github.com/huandu/xstrings/translate.go
generated
vendored
Normal file
545
vendor/github.com/huandu/xstrings/translate.go
generated
vendored
Normal file
|
@ -0,0 +1,545 @@
|
||||||
|
// Copyright 2015 Huan Du. All rights reserved.
|
||||||
|
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package xstrings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type runeRangeMap struct {
|
||||||
|
FromLo rune // Lower bound of range map.
|
||||||
|
FromHi rune // An inclusive higher bound of range map.
|
||||||
|
ToLo rune
|
||||||
|
ToHi rune
|
||||||
|
}
|
||||||
|
|
||||||
|
type runeDict struct {
|
||||||
|
Dict [unicode.MaxASCII + 1]rune
|
||||||
|
}
|
||||||
|
|
||||||
|
type runeMap map[rune]rune
|
||||||
|
|
||||||
|
// Translator can translate string with pre-compiled from and to patterns.
|
||||||
|
// If a from/to pattern pair needs to be used more than once, it's recommended
|
||||||
|
// to create a Translator and reuse it.
|
||||||
|
type Translator struct {
|
||||||
|
quickDict *runeDict // A quick dictionary to look up rune by index. Only availabe for latin runes.
|
||||||
|
runeMap runeMap // Rune map for translation.
|
||||||
|
ranges []*runeRangeMap // Ranges of runes.
|
||||||
|
mappedRune rune // If mappedRune >= 0, all matched runes are translated to the mappedRune.
|
||||||
|
reverted bool // If to pattern is empty, all matched characters will be deleted.
|
||||||
|
hasPattern bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTranslator creates new Translator through a from/to pattern pair.
|
||||||
|
func NewTranslator(from, to string) *Translator {
|
||||||
|
tr := &Translator{}
|
||||||
|
|
||||||
|
if from == "" {
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
|
||||||
|
reverted := from[0] == '^'
|
||||||
|
deletion := len(to) == 0
|
||||||
|
|
||||||
|
if reverted {
|
||||||
|
from = from[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
var fromStart, fromEnd, fromRangeStep rune
|
||||||
|
var toStart, toEnd, toRangeStep rune
|
||||||
|
var fromRangeSize, toRangeSize rune
|
||||||
|
var singleRunes []rune
|
||||||
|
|
||||||
|
// Update the to rune range.
|
||||||
|
updateRange := func() {
|
||||||
|
// No more rune to read in the to rune pattern.
|
||||||
|
if toEnd == utf8.RuneError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if toRangeStep == 0 {
|
||||||
|
to, toStart, toEnd, toRangeStep = nextRuneRange(to, toEnd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current range is not empty. Consume 1 rune from start.
|
||||||
|
if toStart != toEnd {
|
||||||
|
toStart += toRangeStep
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// No more rune. Repeat the last rune.
|
||||||
|
if to == "" {
|
||||||
|
toEnd = utf8.RuneError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both start and end are used. Read two more runes from the to pattern.
|
||||||
|
to, toStart, toEnd, toRangeStep = nextRuneRange(to, utf8.RuneError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if deletion {
|
||||||
|
toStart = utf8.RuneError
|
||||||
|
toEnd = utf8.RuneError
|
||||||
|
} else {
|
||||||
|
// If from pattern is reverted, only the last rune in the to pattern will be used.
|
||||||
|
if reverted {
|
||||||
|
var size int
|
||||||
|
|
||||||
|
for len(to) > 0 {
|
||||||
|
toStart, size = utf8.DecodeRuneInString(to)
|
||||||
|
to = to[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
toEnd = utf8.RuneError
|
||||||
|
} else {
|
||||||
|
to, toStart, toEnd, toRangeStep = nextRuneRange(to, utf8.RuneError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fromEnd = utf8.RuneError
|
||||||
|
|
||||||
|
for len(from) > 0 {
|
||||||
|
from, fromStart, fromEnd, fromRangeStep = nextRuneRange(from, fromEnd)
|
||||||
|
|
||||||
|
// fromStart is a single character. Just map it with a rune in the to pattern.
|
||||||
|
if fromRangeStep == 0 {
|
||||||
|
singleRunes = tr.addRune(fromStart, toStart, singleRunes)
|
||||||
|
updateRange()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for toEnd != utf8.RuneError && fromStart != fromEnd {
|
||||||
|
// If mapped rune is a single character instead of a range, simply shift first
|
||||||
|
// rune in the range.
|
||||||
|
if toRangeStep == 0 {
|
||||||
|
singleRunes = tr.addRune(fromStart, toStart, singleRunes)
|
||||||
|
updateRange()
|
||||||
|
fromStart += fromRangeStep
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fromRangeSize = (fromEnd - fromStart) * fromRangeStep
|
||||||
|
toRangeSize = (toEnd - toStart) * toRangeStep
|
||||||
|
|
||||||
|
// Not enough runes in the to pattern. Need to read more.
|
||||||
|
if fromRangeSize > toRangeSize {
|
||||||
|
fromStart, toStart = tr.addRuneRange(fromStart, fromStart+toRangeSize*fromRangeStep, toStart, toEnd, singleRunes)
|
||||||
|
fromStart += fromRangeStep
|
||||||
|
updateRange()
|
||||||
|
|
||||||
|
// Edge case: If fromRangeSize == toRangeSize + 1, the last fromStart value needs be considered
|
||||||
|
// as a single rune.
|
||||||
|
if fromStart == fromEnd {
|
||||||
|
singleRunes = tr.addRune(fromStart, toStart, singleRunes)
|
||||||
|
updateRange()
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fromStart, toStart = tr.addRuneRange(fromStart, fromEnd, toStart, toStart+fromRangeSize*toRangeStep, singleRunes)
|
||||||
|
updateRange()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if fromStart == fromEnd {
|
||||||
|
fromEnd = utf8.RuneError
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fromStart, toStart = tr.addRuneRange(fromStart, fromEnd, toStart, toStart, singleRunes)
|
||||||
|
fromEnd = utf8.RuneError
|
||||||
|
}
|
||||||
|
|
||||||
|
if fromEnd != utf8.RuneError {
|
||||||
|
singleRunes = tr.addRune(fromEnd, toStart, singleRunes)
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.reverted = reverted
|
||||||
|
tr.mappedRune = -1
|
||||||
|
tr.hasPattern = true
|
||||||
|
|
||||||
|
// Translate RuneError only if in deletion or reverted mode.
|
||||||
|
if deletion || reverted {
|
||||||
|
tr.mappedRune = toStart
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *Translator) addRune(from, to rune, singleRunes []rune) []rune {
|
||||||
|
if from <= unicode.MaxASCII {
|
||||||
|
if tr.quickDict == nil {
|
||||||
|
tr.quickDict = &runeDict{}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.quickDict.Dict[from] = to
|
||||||
|
} else {
|
||||||
|
if tr.runeMap == nil {
|
||||||
|
tr.runeMap = make(runeMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.runeMap[from] = to
|
||||||
|
}
|
||||||
|
|
||||||
|
singleRunes = append(singleRunes, from)
|
||||||
|
return singleRunes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *Translator) addRuneRange(fromLo, fromHi, toLo, toHi rune, singleRunes []rune) (rune, rune) {
|
||||||
|
var r rune
|
||||||
|
var rrm *runeRangeMap
|
||||||
|
|
||||||
|
if fromLo < fromHi {
|
||||||
|
rrm = &runeRangeMap{
|
||||||
|
FromLo: fromLo,
|
||||||
|
FromHi: fromHi,
|
||||||
|
ToLo: toLo,
|
||||||
|
ToHi: toHi,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rrm = &runeRangeMap{
|
||||||
|
FromLo: fromHi,
|
||||||
|
FromHi: fromLo,
|
||||||
|
ToLo: toHi,
|
||||||
|
ToHi: toLo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is any single rune conflicts with this rune range, clear single rune record.
|
||||||
|
for _, r = range singleRunes {
|
||||||
|
if rrm.FromLo <= r && r <= rrm.FromHi {
|
||||||
|
if r <= unicode.MaxASCII {
|
||||||
|
tr.quickDict.Dict[r] = 0
|
||||||
|
} else {
|
||||||
|
delete(tr.runeMap, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.ranges = append(tr.ranges, rrm)
|
||||||
|
return fromHi, toHi
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextRuneRange(str string, last rune) (remaining string, start, end rune, rangeStep rune) {
|
||||||
|
var r rune
|
||||||
|
var size int
|
||||||
|
|
||||||
|
remaining = str
|
||||||
|
escaping := false
|
||||||
|
isRange := false
|
||||||
|
|
||||||
|
for len(remaining) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(remaining)
|
||||||
|
remaining = remaining[size:]
|
||||||
|
|
||||||
|
// Parse special characters.
|
||||||
|
if !escaping {
|
||||||
|
if r == '\\' {
|
||||||
|
escaping = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == '-' {
|
||||||
|
// Ignore slash at beginning of string.
|
||||||
|
if last == utf8.RuneError {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
start = last
|
||||||
|
isRange = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
escaping = false
|
||||||
|
|
||||||
|
if last != utf8.RuneError {
|
||||||
|
// This is a range which start and end are the same.
|
||||||
|
// Considier it as a normal character.
|
||||||
|
if isRange && last == r {
|
||||||
|
isRange = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
start = last
|
||||||
|
end = r
|
||||||
|
|
||||||
|
if isRange {
|
||||||
|
if start < end {
|
||||||
|
rangeStep = 1
|
||||||
|
} else {
|
||||||
|
rangeStep = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
last = r
|
||||||
|
}
|
||||||
|
|
||||||
|
start = last
|
||||||
|
end = utf8.RuneError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate str with a from/to pattern pair.
|
||||||
|
//
|
||||||
|
// See comment in Translate function for usage and samples.
|
||||||
|
func (tr *Translator) Translate(str string) string {
|
||||||
|
if !tr.hasPattern || str == "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
var r rune
|
||||||
|
var size int
|
||||||
|
var needTr bool
|
||||||
|
|
||||||
|
orig := str
|
||||||
|
|
||||||
|
var output *bytes.Buffer
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
r, needTr = tr.TranslateRune(r)
|
||||||
|
|
||||||
|
if needTr && output == nil {
|
||||||
|
output = allocBuffer(orig, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r != utf8.RuneError && output != nil {
|
||||||
|
output.WriteRune(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// No character is translated.
|
||||||
|
if output == nil {
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranslateRune return translated rune and true if r matches the from pattern.
|
||||||
|
// If r doesn't match the pattern, original r is returned and translated is false.
|
||||||
|
func (tr *Translator) TranslateRune(r rune) (result rune, translated bool) {
|
||||||
|
switch {
|
||||||
|
case tr.quickDict != nil:
|
||||||
|
if r <= unicode.MaxASCII {
|
||||||
|
result = tr.quickDict.Dict[r]
|
||||||
|
|
||||||
|
if result != 0 {
|
||||||
|
translated = true
|
||||||
|
|
||||||
|
if tr.mappedRune >= 0 {
|
||||||
|
result = tr.mappedRune
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case tr.runeMap != nil:
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if result, ok = tr.runeMap[r]; ok {
|
||||||
|
translated = true
|
||||||
|
|
||||||
|
if tr.mappedRune >= 0 {
|
||||||
|
result = tr.mappedRune
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
default:
|
||||||
|
var rrm *runeRangeMap
|
||||||
|
ranges := tr.ranges
|
||||||
|
|
||||||
|
for i := len(ranges) - 1; i >= 0; i-- {
|
||||||
|
rrm = ranges[i]
|
||||||
|
|
||||||
|
if rrm.FromLo <= r && r <= rrm.FromHi {
|
||||||
|
translated = true
|
||||||
|
|
||||||
|
if tr.mappedRune >= 0 {
|
||||||
|
result = tr.mappedRune
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if rrm.ToLo < rrm.ToHi {
|
||||||
|
result = rrm.ToLo + r - rrm.FromLo
|
||||||
|
} else if rrm.ToLo > rrm.ToHi {
|
||||||
|
// ToHi can be smaller than ToLo if range is from higher to lower.
|
||||||
|
result = rrm.ToLo - r + rrm.FromLo
|
||||||
|
} else {
|
||||||
|
result = rrm.ToLo
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.reverted {
|
||||||
|
if !translated {
|
||||||
|
result = tr.mappedRune
|
||||||
|
}
|
||||||
|
|
||||||
|
translated = !translated
|
||||||
|
}
|
||||||
|
|
||||||
|
if !translated {
|
||||||
|
result = r
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPattern returns true if Translator has one pattern at least.
|
||||||
|
func (tr *Translator) HasPattern() bool {
|
||||||
|
return tr.hasPattern
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate str with the characters defined in from replaced by characters defined in to.
|
||||||
|
//
|
||||||
|
// From and to are patterns representing a set of characters. Pattern is defined as following.
|
||||||
|
//
|
||||||
|
// * Special characters
|
||||||
|
// * '-' means a range of runes, e.g.
|
||||||
|
// * "a-z" means all characters from 'a' to 'z' inclusive;
|
||||||
|
// * "z-a" means all characters from 'z' to 'a' inclusive.
|
||||||
|
// * '^' as first character means a set of all runes excepted listed, e.g.
|
||||||
|
// * "^a-z" means all characters except 'a' to 'z' inclusive.
|
||||||
|
// * '\' escapes special characters.
|
||||||
|
// * Normal character represents itself, e.g. "abc" is a set including 'a', 'b' and 'c'.
|
||||||
|
//
|
||||||
|
// Translate will try to find a 1:1 mapping from from to to.
|
||||||
|
// If to is smaller than from, last rune in to will be used to map "out of range" characters in from.
|
||||||
|
//
|
||||||
|
// Note that '^' only works in the from pattern. It will be considered as a normal character in the to pattern.
|
||||||
|
//
|
||||||
|
// If the to pattern is an empty string, Translate works exactly the same as Delete.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// Translate("hello", "aeiou", "12345") => "h2ll4"
|
||||||
|
// Translate("hello", "a-z", "A-Z") => "HELLO"
|
||||||
|
// Translate("hello", "z-a", "a-z") => "svool"
|
||||||
|
// Translate("hello", "aeiou", "*") => "h*ll*"
|
||||||
|
// Translate("hello", "^l", "*") => "**ll*"
|
||||||
|
// Translate("hello ^ world", `\^lo`, "*") => "he*** * w*r*d"
|
||||||
|
func Translate(str, from, to string) string {
|
||||||
|
tr := NewTranslator(from, to)
|
||||||
|
return tr.Translate(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete runes in str matching the pattern.
|
||||||
|
// Pattern is defined in Translate function.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// Delete("hello", "aeiou") => "hll"
|
||||||
|
// Delete("hello", "a-k") => "llo"
|
||||||
|
// Delete("hello", "^a-k") => "he"
|
||||||
|
func Delete(str, pattern string) string {
|
||||||
|
tr := NewTranslator(pattern, "")
|
||||||
|
return tr.Translate(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count how many runes in str match the pattern.
|
||||||
|
// Pattern is defined in Translate function.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// Count("hello", "aeiou") => 3
|
||||||
|
// Count("hello", "a-k") => 3
|
||||||
|
// Count("hello", "^a-k") => 2
|
||||||
|
func Count(str, pattern string) int {
|
||||||
|
if pattern == "" || str == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var r rune
|
||||||
|
var size int
|
||||||
|
var matched bool
|
||||||
|
|
||||||
|
tr := NewTranslator(pattern, "")
|
||||||
|
cnt := 0
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
str = str[size:]
|
||||||
|
|
||||||
|
if _, matched = tr.TranslateRune(r); matched {
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Squeeze deletes adjacent repeated runes in str.
|
||||||
|
// If pattern is not empty, only runes matching the pattern will be squeezed.
|
||||||
|
//
|
||||||
|
// Samples:
|
||||||
|
// Squeeze("hello", "") => "helo"
|
||||||
|
// Squeeze("hello", "m-z") => "hello"
|
||||||
|
func Squeeze(str, pattern string) string {
|
||||||
|
var last, r rune
|
||||||
|
var size int
|
||||||
|
var skipSqueeze, matched bool
|
||||||
|
var tr *Translator
|
||||||
|
var output *bytes.Buffer
|
||||||
|
|
||||||
|
orig := str
|
||||||
|
last = -1
|
||||||
|
|
||||||
|
if len(pattern) > 0 {
|
||||||
|
tr = NewTranslator(pattern, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(str) > 0 {
|
||||||
|
r, size = utf8.DecodeRuneInString(str)
|
||||||
|
|
||||||
|
// Need to squeeze the str.
|
||||||
|
if last == r && !skipSqueeze {
|
||||||
|
if tr != nil {
|
||||||
|
if _, matched = tr.TranslateRune(r); !matched {
|
||||||
|
skipSqueeze = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if output == nil {
|
||||||
|
output = allocBuffer(orig, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
if skipSqueeze {
|
||||||
|
output.WriteRune(r)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if output != nil {
|
||||||
|
output.WriteRune(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
last = r
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if output == nil {
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.String()
|
||||||
|
}
|
28
vendor/github.com/imdario/mergo/LICENSE
generated
vendored
Normal file
28
vendor/github.com/imdario/mergo/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
Copyright (c) 2013 Dario Castañé. All rights reserved.
|
||||||
|
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.
|
44
vendor/github.com/imdario/mergo/doc.go
generated
vendored
Normal file
44
vendor/github.com/imdario/mergo/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2013 Dario Castañé. All rights reserved.
|
||||||
|
// Copyright 2009 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 mergo merges same-type structs and maps by setting default values in zero-value fields.
|
||||||
|
|
||||||
|
Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
|
||||||
|
|
||||||
|
Usage
|
||||||
|
|
||||||
|
From my own work-in-progress project:
|
||||||
|
|
||||||
|
type networkConfig struct {
|
||||||
|
Protocol string
|
||||||
|
Address string
|
||||||
|
ServerType string `json: "server_type"`
|
||||||
|
Port uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type FssnConfig struct {
|
||||||
|
Network networkConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var fssnDefault = FssnConfig {
|
||||||
|
networkConfig {
|
||||||
|
"tcp",
|
||||||
|
"127.0.0.1",
|
||||||
|
"http",
|
||||||
|
31560,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inside a function [...]
|
||||||
|
|
||||||
|
if err := mergo.Merge(&config, fssnDefault); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// More code [...]
|
||||||
|
|
||||||
|
*/
|
||||||
|
package mergo
|
156
vendor/github.com/imdario/mergo/map.go
generated
vendored
Normal file
156
vendor/github.com/imdario/mergo/map.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
// Copyright 2014 Dario Castañé. All rights reserved.
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// Based on src/pkg/reflect/deepequal.go from official
|
||||||
|
// golang's stdlib.
|
||||||
|
|
||||||
|
package mergo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
func changeInitialCase(s string, mapper func(rune) rune) string {
|
||||||
|
if s == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r, n := utf8.DecodeRuneInString(s)
|
||||||
|
return string(mapper(r)) + s[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func isExported(field reflect.StructField) bool {
|
||||||
|
r, _ := utf8.DecodeRuneInString(field.Name)
|
||||||
|
return r >= 'A' && r <= 'Z'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverses recursively both values, assigning src's fields values to dst.
|
||||||
|
// The map argument tracks comparisons that have already been seen, which allows
|
||||||
|
// short circuiting on recursive types.
|
||||||
|
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
|
||||||
|
if dst.CanAddr() {
|
||||||
|
addr := dst.UnsafeAddr()
|
||||||
|
h := 17 * addr
|
||||||
|
seen := visited[h]
|
||||||
|
typ := dst.Type()
|
||||||
|
for p := seen; p != nil; p = p.next {
|
||||||
|
if p.ptr == addr && p.typ == typ {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remember, remember...
|
||||||
|
visited[h] = &visit{addr, typ, seen}
|
||||||
|
}
|
||||||
|
zeroValue := reflect.Value{}
|
||||||
|
switch dst.Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
dstMap := dst.Interface().(map[string]interface{})
|
||||||
|
for i, n := 0, src.NumField(); i < n; i++ {
|
||||||
|
srcType := src.Type()
|
||||||
|
field := srcType.Field(i)
|
||||||
|
if !isExported(field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldName := field.Name
|
||||||
|
fieldName = changeInitialCase(fieldName, unicode.ToLower)
|
||||||
|
if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) {
|
||||||
|
dstMap[fieldName] = src.Field(i).Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
srcMap := src.Interface().(map[string]interface{})
|
||||||
|
for key := range srcMap {
|
||||||
|
srcValue := srcMap[key]
|
||||||
|
fieldName := changeInitialCase(key, unicode.ToUpper)
|
||||||
|
dstElement := dst.FieldByName(fieldName)
|
||||||
|
if dstElement == zeroValue {
|
||||||
|
// We discard it because the field doesn't exist.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
srcElement := reflect.ValueOf(srcValue)
|
||||||
|
dstKind := dstElement.Kind()
|
||||||
|
srcKind := srcElement.Kind()
|
||||||
|
if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
|
||||||
|
srcElement = srcElement.Elem()
|
||||||
|
srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
|
||||||
|
} else if dstKind == reflect.Ptr {
|
||||||
|
// Can this work? I guess it can't.
|
||||||
|
if srcKind != reflect.Ptr && srcElement.CanAddr() {
|
||||||
|
srcPtr := srcElement.Addr()
|
||||||
|
srcElement = reflect.ValueOf(srcPtr)
|
||||||
|
srcKind = reflect.Ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !srcElement.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if srcKind == dstKind {
|
||||||
|
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if srcKind == reflect.Map {
|
||||||
|
if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map sets fields' values in dst from src.
|
||||||
|
// src can be a map with string keys or a struct. dst must be the opposite:
|
||||||
|
// if src is a map, dst must be a valid pointer to struct. If src is a struct,
|
||||||
|
// dst must be map[string]interface{}.
|
||||||
|
// It won't merge unexported (private) fields and will do recursively
|
||||||
|
// any exported field.
|
||||||
|
// If dst is a map, keys will be src fields' names in lower camel case.
|
||||||
|
// Missing key in src that doesn't match a field in dst will be skipped. This
|
||||||
|
// doesn't apply if dst is a map.
|
||||||
|
// This is separated method from Merge because it is cleaner and it keeps sane
|
||||||
|
// semantics: merging equal types, mapping different (restricted) types.
|
||||||
|
func Map(dst, src interface{}) error {
|
||||||
|
return _map(dst, src, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by
|
||||||
|
// non-empty src attribute values.
|
||||||
|
func MapWithOverwrite(dst, src interface{}) error {
|
||||||
|
return _map(dst, src, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _map(dst, src interface{}, overwrite bool) error {
|
||||||
|
var (
|
||||||
|
vDst, vSrc reflect.Value
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// To be friction-less, we redirect equal-type arguments
|
||||||
|
// to deepMerge. Only because arguments can be anything.
|
||||||
|
if vSrc.Kind() == vDst.Kind() {
|
||||||
|
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
|
||||||
|
}
|
||||||
|
switch vSrc.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
if vDst.Kind() != reflect.Map {
|
||||||
|
return ErrExpectedMapAsDestination
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if vDst.Kind() != reflect.Struct {
|
||||||
|
return ErrExpectedStructAsDestination
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ErrNotSupported
|
||||||
|
}
|
||||||
|
return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
|
||||||
|
}
|
123
vendor/github.com/imdario/mergo/merge.go
generated
vendored
Normal file
123
vendor/github.com/imdario/mergo/merge.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright 2013 Dario Castañé. All rights reserved.
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// Based on src/pkg/reflect/deepequal.go from official
|
||||||
|
// golang's stdlib.
|
||||||
|
|
||||||
|
package mergo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Traverses recursively both values, assigning src's fields values to dst.
|
||||||
|
// The map argument tracks comparisons that have already been seen, which allows
|
||||||
|
// short circuiting on recursive types.
|
||||||
|
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
|
||||||
|
if !src.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dst.CanAddr() {
|
||||||
|
addr := dst.UnsafeAddr()
|
||||||
|
h := 17 * addr
|
||||||
|
seen := visited[h]
|
||||||
|
typ := dst.Type()
|
||||||
|
for p := seen; p != nil; p = p.next {
|
||||||
|
if p.ptr == addr && p.typ == typ {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remember, remember...
|
||||||
|
visited[h] = &visit{addr, typ, seen}
|
||||||
|
}
|
||||||
|
switch dst.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
||||||
|
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
for _, key := range src.MapKeys() {
|
||||||
|
srcElement := src.MapIndex(key)
|
||||||
|
if !srcElement.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dstElement := dst.MapIndex(key)
|
||||||
|
switch srcElement.Kind() {
|
||||||
|
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
|
||||||
|
if srcElement.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
if !srcElement.CanInterface() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch reflect.TypeOf(srcElement.Interface()).Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
fallthrough
|
||||||
|
case reflect.Ptr:
|
||||||
|
fallthrough
|
||||||
|
case reflect.Map:
|
||||||
|
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
|
||||||
|
if dst.IsNil() {
|
||||||
|
dst.Set(reflect.MakeMap(dst.Type()))
|
||||||
|
}
|
||||||
|
dst.SetMapIndex(key, srcElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Ptr:
|
||||||
|
fallthrough
|
||||||
|
case reflect.Interface:
|
||||||
|
if src.IsNil() {
|
||||||
|
break
|
||||||
|
} else if dst.IsNil() {
|
||||||
|
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
||||||
|
dst.Set(src)
|
||||||
|
}
|
||||||
|
} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
|
||||||
|
dst.Set(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge will fill any empty for value type attributes on the dst struct using corresponding
|
||||||
|
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
|
||||||
|
// and dst must be a pointer to struct.
|
||||||
|
// It won't merge unexported (private) fields and will do recursively any exported field.
|
||||||
|
func Merge(dst, src interface{}) error {
|
||||||
|
return merge(dst, src, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
|
||||||
|
// non-empty src attribute values.
|
||||||
|
func MergeWithOverwrite(dst, src interface{}) error {
|
||||||
|
return merge(dst, src, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func merge(dst, src interface{}, overwrite bool) error {
|
||||||
|
var (
|
||||||
|
vDst, vSrc reflect.Value
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vDst.Type() != vSrc.Type() {
|
||||||
|
return ErrDifferentArgumentsTypes
|
||||||
|
}
|
||||||
|
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
|
||||||
|
}
|
90
vendor/github.com/imdario/mergo/mergo.go
generated
vendored
Normal file
90
vendor/github.com/imdario/mergo/mergo.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2013 Dario Castañé. All rights reserved.
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// Based on src/pkg/reflect/deepequal.go from official
|
||||||
|
// golang's stdlib.
|
||||||
|
|
||||||
|
package mergo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Errors reported by Mergo when it finds invalid arguments.
|
||||||
|
var (
|
||||||
|
ErrNilArguments = errors.New("src and dst must not be nil")
|
||||||
|
ErrDifferentArgumentsTypes = errors.New("src and dst must be of same type")
|
||||||
|
ErrNotSupported = errors.New("only structs and maps are supported")
|
||||||
|
ErrExpectedMapAsDestination = errors.New("dst was expected to be a map")
|
||||||
|
ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct")
|
||||||
|
)
|
||||||
|
|
||||||
|
// During deepMerge, must keep track of checks that are
|
||||||
|
// in progress. The comparison algorithm assumes that all
|
||||||
|
// checks in progress are true when it reencounters them.
|
||||||
|
// Visited are stored in a map indexed by 17 * a1 + a2;
|
||||||
|
type visit struct {
|
||||||
|
ptr uintptr
|
||||||
|
typ reflect.Type
|
||||||
|
next *visit
|
||||||
|
}
|
||||||
|
|
||||||
|
// From src/pkg/encoding/json.
|
||||||
|
func isEmptyValue(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Interface, reflect.Ptr:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) {
|
||||||
|
if dst == nil || src == nil {
|
||||||
|
err = ErrNilArguments
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vDst = reflect.ValueOf(dst).Elem()
|
||||||
|
if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map {
|
||||||
|
err = ErrNotSupported
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vSrc = reflect.ValueOf(src)
|
||||||
|
// We check if vSrc is a pointer to dereference it.
|
||||||
|
if vSrc.Kind() == reflect.Ptr {
|
||||||
|
vSrc = vSrc.Elem()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverses recursively both values, assigning src's fields values to dst.
|
||||||
|
// The map argument tracks comparisons that have already been seen, which allows
|
||||||
|
// short circuiting on recursive types.
|
||||||
|
func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) {
|
||||||
|
if dst.CanAddr() {
|
||||||
|
addr := dst.UnsafeAddr()
|
||||||
|
h := 17 * addr
|
||||||
|
seen := visited[h]
|
||||||
|
typ := dst.Type()
|
||||||
|
for p := seen; p != nil; p = p.next {
|
||||||
|
if p.ptr == addr && p.typ == typ {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remember, remember...
|
||||||
|
visited[h] = &visit{addr, typ, seen}
|
||||||
|
}
|
||||||
|
return // TODO refactor
|
||||||
|
}
|
77
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go
generated
vendored
Normal file
77
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2012 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 pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
|
||||||
|
2898 / PKCS #5 v2.0.
|
||||||
|
|
||||||
|
A key derivation function is useful when encrypting data based on a password
|
||||||
|
or any other not-fully-random data. It uses a pseudorandom function to derive
|
||||||
|
a secure encryption key based on the password.
|
||||||
|
|
||||||
|
While v2.0 of the standard defines only one pseudorandom function to use,
|
||||||
|
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
|
||||||
|
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
|
||||||
|
choose, you can pass the `New` functions from the different SHA packages to
|
||||||
|
pbkdf2.Key.
|
||||||
|
*/
|
||||||
|
package pbkdf2 // import "golang.org/x/crypto/pbkdf2"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key derives a key from the password, salt and iteration count, returning a
|
||||||
|
// []byte of length keylen that can be used as cryptographic key. The key is
|
||||||
|
// derived based on the method described as PBKDF2 with the HMAC variant using
|
||||||
|
// the supplied hash function.
|
||||||
|
//
|
||||||
|
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
|
||||||
|
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
|
||||||
|
// doing:
|
||||||
|
//
|
||||||
|
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
|
||||||
|
//
|
||||||
|
// Remember to get a good random salt. At least 8 bytes is recommended by the
|
||||||
|
// RFC.
|
||||||
|
//
|
||||||
|
// Using a higher iteration count will increase the cost of an exhaustive
|
||||||
|
// search but will also make derivation proportionally slower.
|
||||||
|
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||||
|
prf := hmac.New(h, password)
|
||||||
|
hashLen := prf.Size()
|
||||||
|
numBlocks := (keyLen + hashLen - 1) / hashLen
|
||||||
|
|
||||||
|
var buf [4]byte
|
||||||
|
dk := make([]byte, 0, numBlocks*hashLen)
|
||||||
|
U := make([]byte, hashLen)
|
||||||
|
for block := 1; block <= numBlocks; block++ {
|
||||||
|
// N.B.: || means concatenation, ^ means XOR
|
||||||
|
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
|
||||||
|
// U_1 = PRF(password, salt || uint(i))
|
||||||
|
prf.Reset()
|
||||||
|
prf.Write(salt)
|
||||||
|
buf[0] = byte(block >> 24)
|
||||||
|
buf[1] = byte(block >> 16)
|
||||||
|
buf[2] = byte(block >> 8)
|
||||||
|
buf[3] = byte(block)
|
||||||
|
prf.Write(buf[:4])
|
||||||
|
dk = prf.Sum(dk)
|
||||||
|
T := dk[len(dk)-hashLen:]
|
||||||
|
copy(U, T)
|
||||||
|
|
||||||
|
// U_n = PRF(password, U_(n-1))
|
||||||
|
for n := 2; n <= iter; n++ {
|
||||||
|
prf.Reset()
|
||||||
|
prf.Write(U)
|
||||||
|
U = U[:0]
|
||||||
|
U = prf.Sum(U)
|
||||||
|
for x := range U {
|
||||||
|
T[x] ^= U[x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dk[:keyLen]
|
||||||
|
}
|
243
vendor/golang.org/x/crypto/scrypt/scrypt.go
generated
vendored
Normal file
243
vendor/golang.org/x/crypto/scrypt/scrypt.go
generated
vendored
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
// Copyright 2012 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 scrypt implements the scrypt key derivation function as defined in
|
||||||
|
// Colin Percival's paper "Stronger Key Derivation via Sequential Memory-Hard
|
||||||
|
// Functions" (http://www.tarsnap.com/scrypt/scrypt.pdf).
|
||||||
|
package scrypt // import "golang.org/x/crypto/scrypt"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxInt = int(^uint(0) >> 1)
|
||||||
|
|
||||||
|
// blockCopy copies n numbers from src into dst.
|
||||||
|
func blockCopy(dst, src []uint32, n int) {
|
||||||
|
copy(dst, src[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockXOR XORs numbers from dst with n numbers from src.
|
||||||
|
func blockXOR(dst, src []uint32, n int) {
|
||||||
|
for i, v := range src[:n] {
|
||||||
|
dst[i] ^= v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in,
|
||||||
|
// and puts the result into both both tmp and out.
|
||||||
|
func salsaXOR(tmp *[16]uint32, in, out []uint32) {
|
||||||
|
w0 := tmp[0] ^ in[0]
|
||||||
|
w1 := tmp[1] ^ in[1]
|
||||||
|
w2 := tmp[2] ^ in[2]
|
||||||
|
w3 := tmp[3] ^ in[3]
|
||||||
|
w4 := tmp[4] ^ in[4]
|
||||||
|
w5 := tmp[5] ^ in[5]
|
||||||
|
w6 := tmp[6] ^ in[6]
|
||||||
|
w7 := tmp[7] ^ in[7]
|
||||||
|
w8 := tmp[8] ^ in[8]
|
||||||
|
w9 := tmp[9] ^ in[9]
|
||||||
|
w10 := tmp[10] ^ in[10]
|
||||||
|
w11 := tmp[11] ^ in[11]
|
||||||
|
w12 := tmp[12] ^ in[12]
|
||||||
|
w13 := tmp[13] ^ in[13]
|
||||||
|
w14 := tmp[14] ^ in[14]
|
||||||
|
w15 := tmp[15] ^ in[15]
|
||||||
|
|
||||||
|
x0, x1, x2, x3, x4, x5, x6, x7, x8 := w0, w1, w2, w3, w4, w5, w6, w7, w8
|
||||||
|
x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15
|
||||||
|
|
||||||
|
for i := 0; i < 8; i += 2 {
|
||||||
|
u := x0 + x12
|
||||||
|
x4 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x4 + x0
|
||||||
|
x8 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x8 + x4
|
||||||
|
x12 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x12 + x8
|
||||||
|
x0 ^= u<<18 | u>>(32-18)
|
||||||
|
|
||||||
|
u = x5 + x1
|
||||||
|
x9 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x9 + x5
|
||||||
|
x13 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x13 + x9
|
||||||
|
x1 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x1 + x13
|
||||||
|
x5 ^= u<<18 | u>>(32-18)
|
||||||
|
|
||||||
|
u = x10 + x6
|
||||||
|
x14 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x14 + x10
|
||||||
|
x2 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x2 + x14
|
||||||
|
x6 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x6 + x2
|
||||||
|
x10 ^= u<<18 | u>>(32-18)
|
||||||
|
|
||||||
|
u = x15 + x11
|
||||||
|
x3 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x3 + x15
|
||||||
|
x7 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x7 + x3
|
||||||
|
x11 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x11 + x7
|
||||||
|
x15 ^= u<<18 | u>>(32-18)
|
||||||
|
|
||||||
|
u = x0 + x3
|
||||||
|
x1 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x1 + x0
|
||||||
|
x2 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x2 + x1
|
||||||
|
x3 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x3 + x2
|
||||||
|
x0 ^= u<<18 | u>>(32-18)
|
||||||
|
|
||||||
|
u = x5 + x4
|
||||||
|
x6 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x6 + x5
|
||||||
|
x7 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x7 + x6
|
||||||
|
x4 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x4 + x7
|
||||||
|
x5 ^= u<<18 | u>>(32-18)
|
||||||
|
|
||||||
|
u = x10 + x9
|
||||||
|
x11 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x11 + x10
|
||||||
|
x8 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x8 + x11
|
||||||
|
x9 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x9 + x8
|
||||||
|
x10 ^= u<<18 | u>>(32-18)
|
||||||
|
|
||||||
|
u = x15 + x14
|
||||||
|
x12 ^= u<<7 | u>>(32-7)
|
||||||
|
u = x12 + x15
|
||||||
|
x13 ^= u<<9 | u>>(32-9)
|
||||||
|
u = x13 + x12
|
||||||
|
x14 ^= u<<13 | u>>(32-13)
|
||||||
|
u = x14 + x13
|
||||||
|
x15 ^= u<<18 | u>>(32-18)
|
||||||
|
}
|
||||||
|
x0 += w0
|
||||||
|
x1 += w1
|
||||||
|
x2 += w2
|
||||||
|
x3 += w3
|
||||||
|
x4 += w4
|
||||||
|
x5 += w5
|
||||||
|
x6 += w6
|
||||||
|
x7 += w7
|
||||||
|
x8 += w8
|
||||||
|
x9 += w9
|
||||||
|
x10 += w10
|
||||||
|
x11 += w11
|
||||||
|
x12 += w12
|
||||||
|
x13 += w13
|
||||||
|
x14 += w14
|
||||||
|
x15 += w15
|
||||||
|
|
||||||
|
out[0], tmp[0] = x0, x0
|
||||||
|
out[1], tmp[1] = x1, x1
|
||||||
|
out[2], tmp[2] = x2, x2
|
||||||
|
out[3], tmp[3] = x3, x3
|
||||||
|
out[4], tmp[4] = x4, x4
|
||||||
|
out[5], tmp[5] = x5, x5
|
||||||
|
out[6], tmp[6] = x6, x6
|
||||||
|
out[7], tmp[7] = x7, x7
|
||||||
|
out[8], tmp[8] = x8, x8
|
||||||
|
out[9], tmp[9] = x9, x9
|
||||||
|
out[10], tmp[10] = x10, x10
|
||||||
|
out[11], tmp[11] = x11, x11
|
||||||
|
out[12], tmp[12] = x12, x12
|
||||||
|
out[13], tmp[13] = x13, x13
|
||||||
|
out[14], tmp[14] = x14, x14
|
||||||
|
out[15], tmp[15] = x15, x15
|
||||||
|
}
|
||||||
|
|
||||||
|
func blockMix(tmp *[16]uint32, in, out []uint32, r int) {
|
||||||
|
blockCopy(tmp[:], in[(2*r-1)*16:], 16)
|
||||||
|
for i := 0; i < 2*r; i += 2 {
|
||||||
|
salsaXOR(tmp, in[i*16:], out[i*8:])
|
||||||
|
salsaXOR(tmp, in[i*16+16:], out[i*8+r*16:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func integer(b []uint32, r int) uint64 {
|
||||||
|
j := (2*r - 1) * 16
|
||||||
|
return uint64(b[j]) | uint64(b[j+1])<<32
|
||||||
|
}
|
||||||
|
|
||||||
|
func smix(b []byte, r, N int, v, xy []uint32) {
|
||||||
|
var tmp [16]uint32
|
||||||
|
x := xy
|
||||||
|
y := xy[32*r:]
|
||||||
|
|
||||||
|
j := 0
|
||||||
|
for i := 0; i < 32*r; i++ {
|
||||||
|
x[i] = uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24
|
||||||
|
j += 4
|
||||||
|
}
|
||||||
|
for i := 0; i < N; i += 2 {
|
||||||
|
blockCopy(v[i*(32*r):], x, 32*r)
|
||||||
|
blockMix(&tmp, x, y, r)
|
||||||
|
|
||||||
|
blockCopy(v[(i+1)*(32*r):], y, 32*r)
|
||||||
|
blockMix(&tmp, y, x, r)
|
||||||
|
}
|
||||||
|
for i := 0; i < N; i += 2 {
|
||||||
|
j := int(integer(x, r) & uint64(N-1))
|
||||||
|
blockXOR(x, v[j*(32*r):], 32*r)
|
||||||
|
blockMix(&tmp, x, y, r)
|
||||||
|
|
||||||
|
j = int(integer(y, r) & uint64(N-1))
|
||||||
|
blockXOR(y, v[j*(32*r):], 32*r)
|
||||||
|
blockMix(&tmp, y, x, r)
|
||||||
|
}
|
||||||
|
j = 0
|
||||||
|
for _, v := range x[:32*r] {
|
||||||
|
b[j+0] = byte(v >> 0)
|
||||||
|
b[j+1] = byte(v >> 8)
|
||||||
|
b[j+2] = byte(v >> 16)
|
||||||
|
b[j+3] = byte(v >> 24)
|
||||||
|
j += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key derives a key from the password, salt, and cost parameters, returning
|
||||||
|
// a byte slice of length keyLen that can be used as cryptographic key.
|
||||||
|
//
|
||||||
|
// N is a CPU/memory cost parameter, which must be a power of two greater than 1.
|
||||||
|
// r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the
|
||||||
|
// limits, the function returns a nil byte slice and an error.
|
||||||
|
//
|
||||||
|
// For example, you can get a derived key for e.g. AES-256 (which needs a
|
||||||
|
// 32-byte key) by doing:
|
||||||
|
//
|
||||||
|
// dk := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32)
|
||||||
|
//
|
||||||
|
// The recommended parameters for interactive logins as of 2009 are N=16384,
|
||||||
|
// r=8, p=1. They should be increased as memory latency and CPU parallelism
|
||||||
|
// increases. Remember to get a good random salt.
|
||||||
|
func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) {
|
||||||
|
if N <= 1 || N&(N-1) != 0 {
|
||||||
|
return nil, errors.New("scrypt: N must be > 1 and a power of 2")
|
||||||
|
}
|
||||||
|
if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r {
|
||||||
|
return nil, errors.New("scrypt: parameters are too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
xy := make([]uint32, 64*r)
|
||||||
|
v := make([]uint32, 32*N*r)
|
||||||
|
b := pbkdf2.Key(password, salt, 1, p*128*r, sha256.New)
|
||||||
|
|
||||||
|
for i := 0; i < p; i++ {
|
||||||
|
smix(b[i*128*r:], r, N, v, xy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pbkdf2.Key(password, b, 1, keyLen, sha256.New), nil
|
||||||
|
}
|
Loading…
Reference in a new issue