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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
Træfik can be configured using a RESTful api.
|
||||
|
|
16
glide.lock
generated
16
glide.lock
generated
|
@ -1,5 +1,5 @@
|
|||
hash: 1cb8a7bc9dcca526370e84514c3f0c76b342e25d40ba584826d22f7e93be584c
|
||||
updated: 2017-08-01T19:11:22.221029923+02:00
|
||||
hash: b0b29ccc1d46dcfb5a689d225fdd35ea3ffebb9e149bf0081229033cb515ad45
|
||||
updated: 2017-08-10T19:56:04.346751095+02:00
|
||||
imports:
|
||||
- name: cloud.google.com/go
|
||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||
|
@ -8,6 +8,8 @@ imports:
|
|||
- internal
|
||||
- name: github.com/abbot/go-http-auth
|
||||
version: 0ddd408d5d60ea76e320503cc7dd091992dee608
|
||||
- name: github.com/aokoli/goutils
|
||||
version: 3391d3790d23d03408670993e957e8f408993c34
|
||||
- name: github.com/ArthurHlt/go-eureka-client
|
||||
version: 9d0a49cbd39aa3634ae1977e9f519a262b10adaf
|
||||
subpackages:
|
||||
|
@ -300,6 +302,10 @@ imports:
|
|||
version: 19f2c401e122352c047a84d6584dd51e2fb8fcc4
|
||||
subpackages:
|
||||
- coordinate
|
||||
- name: github.com/huandu/xstrings
|
||||
version: 3959339b333561bf62a38b424fd41517c2c90f40
|
||||
- name: github.com/imdario/mergo
|
||||
version: 3e95a51e0639b4cf372f2ccf74c86749d747fbdc
|
||||
- name: github.com/JamesClonk/vultr
|
||||
version: 0f156dd232bc4ebf8a32ba83fec57c0e4c9db69f
|
||||
subpackages:
|
||||
|
@ -320,6 +326,10 @@ imports:
|
|||
- buffer
|
||||
- jlexer
|
||||
- jwriter
|
||||
- name: github.com/Masterminds/semver
|
||||
version: 59c29afe1a994eacb71c833025ca7acf874bb1da
|
||||
- name: github.com/Masterminds/sprig
|
||||
version: 9526be0327b26ad31aa70296a7b10704883976d5
|
||||
- name: github.com/mattn/go-colorable
|
||||
version: 5411d3eea5978e6cdc258b30de592b60df6aba96
|
||||
repo: https://github.com/mattn/go-colorable
|
||||
|
@ -520,6 +530,8 @@ imports:
|
|||
- bcrypt
|
||||
- blowfish
|
||||
- ocsp
|
||||
- pbkdf2
|
||||
- scrypt
|
||||
- name: golang.org/x/net
|
||||
version: 242b6b35177ec3909636b6cf6a47e8c2c6324b5d
|
||||
subpackages:
|
||||
|
|
|
@ -198,6 +198,8 @@ import:
|
|||
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
|
||||
subpackages:
|
||||
- spew
|
||||
- package: github.com/Masterminds/sprig
|
||||
version: e039e20e500c2c025d9145be375e27cf42a94174
|
||||
testImport:
|
||||
- package: github.com/stvp/go-udp-testing
|
||||
- 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 {
|
||||
return label
|
||||
}
|
||||
return provider.Replace("/", "-", application.ID)
|
||||
return strings.Replace(application.ID, "/", "-", -1)
|
||||
}
|
||||
|
||||
func (p *Provider) getSubDomain(name string) string {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"unicode"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/containous/traefik/autogen"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
|
@ -59,14 +60,12 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
|
|||
err error
|
||||
)
|
||||
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 {
|
||||
defaultFuncMap[funcID] = funcElement
|
||||
}
|
||||
|
@ -102,15 +101,6 @@ func (p *BaseProvider) GetConfiguration(defaultTemplateFile string, funcMap temp
|
|||
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 {
|
||||
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) {
|
||||
templateFile, err := ioutil.TempFile("", "provider-configuration")
|
||||
if err != nil {
|
||||
|
@ -380,3 +349,51 @@ func TestDefaultFuncMap(t *testing.T) {
|
|||
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