summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/magiconair
diff options
context:
space:
mode:
authorCorey Hulen <corey@hulen.com>2017-03-24 23:31:34 -0700
committerenahum <nahumhbl@gmail.com>2017-03-25 03:31:34 -0300
commit54d3d47daf9190275bbdaf8703b84969a4593451 (patch)
tree05899b296d0186c1a0da8a540bc486e34ad8eec9 /vendor/github.com/magiconair
parent7460302dec7796e01c98264e84bece8169cb6ed9 (diff)
downloadchat-54d3d47daf9190275bbdaf8703b84969a4593451.tar.gz
chat-54d3d47daf9190275bbdaf8703b84969a4593451.tar.bz2
chat-54d3d47daf9190275bbdaf8703b84969a4593451.zip
PLT-6076 Adding viper libs for config file changes (#5871)
* Adding viper libs for config file changes * Removing the old fsnotify lib * updating some missing libs
Diffstat (limited to 'vendor/github.com/magiconair')
-rw-r--r--vendor/github.com/magiconair/properties/.gitignore6
-rw-r--r--vendor/github.com/magiconair/properties/.travis.yml8
-rw-r--r--vendor/github.com/magiconair/properties/CHANGELOG.md96
-rw-r--r--vendor/github.com/magiconair/properties/LICENSE25
-rw-r--r--vendor/github.com/magiconair/properties/README.md100
-rw-r--r--vendor/github.com/magiconair/properties/assert/assert.go90
-rw-r--r--vendor/github.com/magiconair/properties/assert/assert_test.go55
-rw-r--r--vendor/github.com/magiconair/properties/benchmark_test.go24
-rw-r--r--vendor/github.com/magiconair/properties/decode.go289
-rw-r--r--vendor/github.com/magiconair/properties/decode_test.go299
-rw-r--r--vendor/github.com/magiconair/properties/doc.go156
-rw-r--r--vendor/github.com/magiconair/properties/example_test.go93
-rw-r--r--vendor/github.com/magiconair/properties/integrate.go34
-rw-r--r--vendor/github.com/magiconair/properties/integrate_test.go76
-rw-r--r--vendor/github.com/magiconair/properties/lex.go408
-rw-r--r--vendor/github.com/magiconair/properties/load.go241
-rw-r--r--vendor/github.com/magiconair/properties/load_test.go231
-rw-r--r--vendor/github.com/magiconair/properties/parser.go95
-rw-r--r--vendor/github.com/magiconair/properties/properties.go808
-rw-r--r--vendor/github.com/magiconair/properties/properties_test.go934
-rw-r--r--vendor/github.com/magiconair/properties/rangecheck.go31
21 files changed, 4099 insertions, 0 deletions
diff --git a/vendor/github.com/magiconair/properties/.gitignore b/vendor/github.com/magiconair/properties/.gitignore
new file mode 100644
index 000000000..e7081ff52
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/.gitignore
@@ -0,0 +1,6 @@
+*.sublime-project
+*.sublime-workspace
+*.un~
+*.swp
+.idea/
+*.iml
diff --git a/vendor/github.com/magiconair/properties/.travis.yml b/vendor/github.com/magiconair/properties/.travis.yml
new file mode 100644
index 000000000..60436b202
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/.travis.yml
@@ -0,0 +1,8 @@
+language: go
+go:
+ - 1.4.x
+ - 1.5.x
+ - 1.6.x
+ - 1.7.x
+ - 1.8.x
+ - tip
diff --git a/vendor/github.com/magiconair/properties/CHANGELOG.md b/vendor/github.com/magiconair/properties/CHANGELOG.md
new file mode 100644
index 000000000..1d86d0c18
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/CHANGELOG.md
@@ -0,0 +1,96 @@
+## Changelog
+
+### Unreleased
+
+ * [Issue #17](https://github.com/magiconair/properties/issues/17): Add [SetValue()](http://godoc.org/github.com/magiconair/properties#Properties.SetValue) method to set values generically
+ * [Issue #22](https://github.com/magiconair/properties/issues/22): Add [LoadMap()](http://godoc.org/github.com/magiconair/properties#LoadMap) function to load properties from a string map
+
+### [1.7.2](https://github.com/magiconair/properties/tags/v1.7.2) - 20 Mar 2017
+
+ * [Issue #15](https://github.com/magiconair/properties/issues/15): Drop gocheck dependency
+ * [PR #21](https://github.com/magiconair/properties/pull/21): Add [Map()](http://godoc.org/github.com/magiconair/properties#Properties.Map) and [FilterFunc()](http://godoc.org/github.com/magiconair/properties#Properties.FilterFunc)
+
+### [1.7.1](https://github.com/magiconair/properties/tags/v1.7.1) - 13 Jan 2017
+
+ * [PR #16](https://github.com/magiconair/properties/pull/16): Keep gofmt happy
+ * [PR #18](https://github.com/magiconair/properties/pull/18): Fix Delete() function
+
+### [1.7.0](https://github.com/magiconair/properties/tags/v1.7.0) - 20 Mar 2016
+
+ * [Issue #10](https://github.com/magiconair/properties/issues/10): Add [LoadURL,LoadURLs,MustLoadURL,MustLoadURLs](http://godoc.org/github.com/magiconair/properties#LoadURL) method to load properties from a URL.
+ * [Issue #11](https://github.com/magiconair/properties/issues/11): Add [LoadString,MustLoadString](http://godoc.org/github.com/magiconair/properties#LoadString) method to load properties from an UTF8 string.
+ * [PR #8](https://github.com/magiconair/properties/pull/8): Add [MustFlag](http://godoc.org/github.com/magiconair/properties#Properties.MustFlag) method to provide overrides via command line flags. (@pascaldekloe)
+
+### [1.6.0](https://github.com/magiconair/properties/tags/v1.6.0) - 11 Dec 2015
+
+ * Add [Decode](http://godoc.org/github.com/magiconair/properties#Properties.Decode) method to populate struct from properties via tags.
+
+### [1.5.6](https://github.com/magiconair/properties/tags/v1.5.6) - 18 Oct 2015
+
+ * Vendored in gopkg.in/check.v1
+
+### [1.5.5](https://github.com/magiconair/properties/tags/v1.5.5) - 31 Jul 2015
+
+ * [PR #6](https://github.com/magiconair/properties/pull/6): Add [Delete](http://godoc.org/github.com/magiconair/properties#Properties.Delete) method to remove keys including comments. (@gerbenjacobs)
+
+### [1.5.4](https://github.com/magiconair/properties/tags/v1.5.4) - 23 Jun 2015
+
+ * [Issue #5](https://github.com/magiconair/properties/issues/5): Allow disabling of property expansion [DisableExpansion](http://godoc.org/github.com/magiconair/properties#Properties.DisableExpansion). When property expansion is disabled Properties become a simple key/value store and don't check for circular references.
+
+### [1.5.3](https://github.com/magiconair/properties/tags/v1.5.3) - 02 Jun 2015
+
+ * [Issue #4](https://github.com/magiconair/properties/issues/4): Maintain key order in [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) and [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp)
+
+### [1.5.2](https://github.com/magiconair/properties/tags/v1.5.2) - 10 Apr 2015
+
+ * [Issue #3](https://github.com/magiconair/properties/issues/3): Don't print comments in [WriteComment()](http://godoc.org/github.com/magiconair/properties#Properties.WriteComment) if they are all empty
+ * Add clickable links to README
+
+### [1.5.1](https://github.com/magiconair/properties/tags/v1.5.1) - 08 Dec 2014
+
+ * Added [GetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.GetParsedDuration) and [MustGetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.MustGetParsedDuration) for values specified compatible with
+ [time.ParseDuration()](http://golang.org/pkg/time/#ParseDuration).
+
+### [1.5.0](https://github.com/magiconair/properties/tags/v1.5.0) - 18 Nov 2014
+
+ * Added support for single and multi-line comments (reading, writing and updating)
+ * The order of keys is now preserved
+ * Calling [Set()](http://godoc.org/github.com/magiconair/properties#Properties.Set) with an empty key now silently ignores the call and does not create a new entry
+ * Added a [MustSet()](http://godoc.org/github.com/magiconair/properties#Properties.MustSet) method
+ * Migrated test library from launchpad.net/gocheck to [gopkg.in/check.v1](http://gopkg.in/check.v1)
+
+### [1.4.2](https://github.com/magiconair/properties/tags/v1.4.2) - 15 Nov 2014
+
+ * [Issue #2](https://github.com/magiconair/properties/issues/2): Fixed goroutine leak in parser which created two lexers but cleaned up only one
+
+### [1.4.1](https://github.com/magiconair/properties/tags/v1.4.1) - 13 Nov 2014
+
+ * [Issue #1](https://github.com/magiconair/properties/issues/1): Fixed bug in Keys() method which returned an empty string
+
+### [1.4.0](https://github.com/magiconair/properties/tags/v1.4.0) - 23 Sep 2014
+
+ * Added [Keys()](http://godoc.org/github.com/magiconair/properties#Properties.Keys) to get the keys
+ * Added [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) and [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) to get a subset of the properties
+
+### [1.3.0](https://github.com/magiconair/properties/tags/v1.3.0) - 18 Mar 2014
+
+* Added support for time.Duration
+* Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tags/vior configurable (log.Fatal, panic) - custom)
+* Changed default of MustXXX() failure from panic to log.Fatal
+
+### [1.2.0](https://github.com/magiconair/properties/tags/v1.2.0) - 05 Mar 2014
+
+* Added MustGet... functions
+* Added support for int and uint with range checks on 32 bit platforms
+
+### [1.1.0](https://github.com/magiconair/properties/tags/v1.1.0) - 20 Jan 2014
+
+* Renamed from goproperties to properties
+* Added support for expansion of environment vars in
+ filenames and value expressions
+* Fixed bug where value expressions were not at the
+ start of the string
+
+### [1.0.0](https://github.com/magiconair/properties/tags/v1.0.0) - 7 Jan 2014
+
+* Initial release
diff --git a/vendor/github.com/magiconair/properties/LICENSE b/vendor/github.com/magiconair/properties/LICENSE
new file mode 100644
index 000000000..7eab43b6b
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/LICENSE
@@ -0,0 +1,25 @@
+goproperties - properties file decoder for Go
+
+Copyright (c) 2013-2014 - Frank Schroeder
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. 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.
+
+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.
diff --git a/vendor/github.com/magiconair/properties/README.md b/vendor/github.com/magiconair/properties/README.md
new file mode 100644
index 000000000..71b6a5359
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/README.md
@@ -0,0 +1,100 @@
+Overview [![Build Status](https://travis-ci.org/magiconair/properties.svg?branch=master)](https://travis-ci.org/magiconair/properties)
+========
+
+#### Current version: 1.7.2
+
+properties is a Go library for reading and writing properties files.
+
+It supports reading from multiple files or URLs and Spring style recursive
+property expansion of expressions like `${key}` to their corresponding value.
+Value expressions can refer to other keys like in `${key}` or to environment
+variables like in `${USER}`. Filenames can also contain environment variables
+like in `/home/${USER}/myapp.properties`.
+
+Properties can be decoded into structs, maps, arrays and values through
+struct tags.
+
+Comments and the order of keys are preserved. Comments can be modified
+and can be written to the output.
+
+The properties library supports both ISO-8859-1 and UTF-8 encoded data.
+
+Starting from version 1.3.0 the behavior of the MustXXX() functions is
+configurable by providing a custom `ErrorHandler` function. The default has
+changed from `panic` to `log.Fatal` but this is configurable and custom
+error handling functions can be provided. See the package documentation for
+details.
+
+Read the full documentation on [GoDoc](https://godoc.org/github.com/magiconair/properties) [![GoDoc](https://godoc.org/github.com/magiconair/properties?status.png)](https://godoc.org/github.com/magiconair/properties)
+
+Getting Started
+---------------
+
+```go
+import (
+ "flag"
+ "github.com/magiconair/properties"
+)
+
+func main() {
+ // init from a file
+ p := properties.MustLoadFile("${HOME}/config.properties", properties.UTF8)
+
+ // or multiple files
+ p = properties.MustLoadFiles([]string{
+ "${HOME}/config.properties",
+ "${HOME}/config-${USER}.properties",
+ }, properties.UTF8, true)
+
+ // or from a map
+ p = properties.LoadMap(map[string]string{"key": "value", "abc": "def"})
+
+ // or from a string
+ p = properties.MustLoadString("key=value\nabc=def")
+
+ // or from a URL
+ p = properties.MustLoadURL("http://host/path")
+
+ // or from multiple URLs
+ p = properties.MustLoadURL([]string{
+ "http://host/config",
+ "http://host/config-${USER}",
+ }, true)
+
+ // or from flags
+ p.MustFlag(flag.CommandLine)
+
+ // get values through getters
+ host := p.MustGetString("host")
+ port := p.GetInt("port", 8080)
+
+ // or through Decode
+ type Config struct {
+ Host string `properties:"host"`
+ Port int `properties:"port,default=9000"`
+ Accept []string `properties:"accept,default=image/png;image;gif"`
+ Timeout time.Duration `properties:"timeout,default=5s"`
+ }
+ var cfg Config
+ if err := p.Decode(&cfg); err != nil {
+ log.Fatal(err)
+ }
+}
+
+```
+
+Installation and Upgrade
+------------------------
+
+```
+$ go get -u github.com/magiconair/properties
+```
+
+License
+-------
+
+2 clause BSD license. See [LICENSE](https://github.com/magiconair/properties/blob/master/LICENSE) file for details.
+
+ToDo
+----
+* Dump contents with passwords and secrets obscured
diff --git a/vendor/github.com/magiconair/properties/assert/assert.go b/vendor/github.com/magiconair/properties/assert/assert.go
new file mode 100644
index 000000000..cb1097baa
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/assert/assert.go
@@ -0,0 +1,90 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package assert provides helper functions for testing.
+package assert
+
+import (
+ "fmt"
+ "path/filepath"
+ "reflect"
+ "regexp"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+// skip defines the default call depth
+const skip = 2
+
+// Equal asserts that got and want are equal as defined by
+// reflect.DeepEqual. The test fails with msg if they are not equal.
+func Equal(t *testing.T, got, want interface{}, msg ...string) {
+ if x := equal(2, got, want, msg...); x != "" {
+ fmt.Println(x)
+ t.Fail()
+ }
+}
+
+func equal(skip int, got, want interface{}, msg ...string) string {
+ if !reflect.DeepEqual(got, want) {
+ return fail(skip, "got %v want %v %s", got, want, strings.Join(msg, " "))
+ }
+ return ""
+}
+
+// Panic asserts that function fn() panics.
+// It assumes that recover() either returns a string or
+// an error and fails if the message does not match
+// the regular expression in 'matches'.
+func Panic(t *testing.T, fn func(), matches string) {
+ if x := doesPanic(2, fn, matches); x != "" {
+ fmt.Println(x)
+ t.Fail()
+ }
+}
+
+func doesPanic(skip int, fn func(), expr string) (err string) {
+ defer func() {
+ r := recover()
+ if r == nil {
+ err = fail(skip, "did not panic")
+ return
+ }
+ var v string
+ switch r.(type) {
+ case error:
+ v = r.(error).Error()
+ case string:
+ v = r.(string)
+ }
+ err = matches(skip, v, expr)
+ }()
+ fn()
+ return ""
+}
+
+// Matches asserts that a value matches a given regular expression.
+func Matches(t *testing.T, value, expr string) {
+ if x := matches(2, value, expr); x != "" {
+ fmt.Println(x)
+ t.Fail()
+ }
+}
+
+func matches(skip int, value, expr string) string {
+ ok, err := regexp.MatchString(expr, value)
+ if err != nil {
+ return fail(skip, "invalid pattern %q. %s", expr, err)
+ }
+ if !ok {
+ return fail(skip, "got %s which does not match %s", value, expr)
+ }
+ return ""
+}
+
+func fail(skip int, format string, args ...interface{}) string {
+ _, file, line, _ := runtime.Caller(skip)
+ return fmt.Sprintf("\t%s:%d: %s\n", filepath.Base(file), line, fmt.Sprintf(format, args...))
+}
diff --git a/vendor/github.com/magiconair/properties/assert/assert_test.go b/vendor/github.com/magiconair/properties/assert/assert_test.go
new file mode 100644
index 000000000..dcef73dfc
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/assert/assert_test.go
@@ -0,0 +1,55 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package assert
+
+import "testing"
+
+func TestEqualEquals(t *testing.T) {
+ if got, want := equal(2, "a", "a"), ""; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
+
+func TestEqualFails(t *testing.T) {
+ if got, want := equal(2, "a", "b"), "\tassert_test.go:16: got a want b \n"; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
+
+func TestPanicPanics(t *testing.T) {
+ if got, want := doesPanic(2, func() { panic("foo") }, ""), ""; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
+
+func TestPanicPanicsAndMatches(t *testing.T) {
+ if got, want := doesPanic(2, func() { panic("foo") }, "foo"), ""; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
+
+func TestPanicPanicsAndDoesNotMatch(t *testing.T) {
+ if got, want := doesPanic(2, func() { panic("foo") }, "bar"), "\tassert.go:62: got foo which does not match bar\n"; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
+
+func TestPanicPanicsAndDoesNotPanic(t *testing.T) {
+ if got, want := doesPanic(2, func() {}, "bar"), "\tassert.go:65: did not panic\n"; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
+
+func TestMatchesMatches(t *testing.T) {
+ if got, want := matches(2, "aaa", "a"), ""; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
+
+func TestMatchesDoesNotMatch(t *testing.T) {
+ if got, want := matches(2, "aaa", "b"), "\tassert_test.go:52: got aaa which does not match b\n"; got != want {
+ t.Fatalf("got %q want %q", got, want)
+ }
+}
diff --git a/vendor/github.com/magiconair/properties/benchmark_test.go b/vendor/github.com/magiconair/properties/benchmark_test.go
new file mode 100644
index 000000000..62c7cc571
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/benchmark_test.go
@@ -0,0 +1,24 @@
+// Copyright 2013-2014 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "fmt"
+ "testing"
+)
+
+// Benchmarks the decoder by creating a property file with 1000 key/value pairs.
+func BenchmarkLoad(b *testing.B) {
+ input := ""
+ for i := 0; i < 1000; i++ {
+ input += fmt.Sprintf("key%d=value%d\n", i, i)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if _, err := Load([]byte(input), ISO_8859_1); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/vendor/github.com/magiconair/properties/decode.go b/vendor/github.com/magiconair/properties/decode.go
new file mode 100644
index 000000000..0a961bb04
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/decode.go
@@ -0,0 +1,289 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Decode assigns property values to exported fields of a struct.
+//
+// Decode traverses v recursively and returns an error if a value cannot be
+// converted to the field type or a required value is missing for a field.
+//
+// The following type dependent decodings are used:
+//
+// String, boolean, numeric fields have the value of the property key assigned.
+// The property key name is the name of the field. A different key and a default
+// value can be set in the field's tag. Fields without default value are
+// required. If the value cannot be converted to the field type an error is
+// returned.
+//
+// time.Duration fields have the result of time.ParseDuration() assigned.
+//
+// time.Time fields have the vaule of time.Parse() assigned. The default layout
+// is time.RFC3339 but can be set in the field's tag.
+//
+// Arrays and slices of string, boolean, numeric, time.Duration and time.Time
+// fields have the value interpreted as a comma separated list of values. The
+// individual values are trimmed of whitespace and empty values are ignored. A
+// default value can be provided as a semicolon separated list in the field's
+// tag.
+//
+// Struct fields are decoded recursively using the field name plus "." as
+// prefix. The prefix (without dot) can be overridden in the field's tag.
+// Default values are not supported in the field's tag. Specify them on the
+// fields of the inner struct instead.
+//
+// Map fields must have a key of type string and are decoded recursively by
+// using the field's name plus ".' as prefix and the next element of the key
+// name as map key. The prefix (without dot) can be overridden in the field's
+// tag. Default values are not supported.
+//
+// Examples:
+//
+// // Field is ignored.
+// Field int `properties:"-"`
+//
+// // Field is assigned value of 'Field'.
+// Field int
+//
+// // Field is assigned value of 'myName'.
+// Field int `properties:"myName"`
+//
+// // Field is assigned value of key 'myName' and has a default
+// // value 15 if the key does not exist.
+// Field int `properties:"myName,default=15"`
+//
+// // Field is assigned value of key 'Field' and has a default
+// // value 15 if the key does not exist.
+// Field int `properties:",default=15"`
+//
+// // Field is assigned value of key 'date' and the date
+// // is in format 2006-01-02
+// Field time.Time `properties:"date,layout=2006-01-02"`
+//
+// // Field is assigned the non-empty and whitespace trimmed
+// // values of key 'Field' split by commas.
+// Field []string
+//
+// // Field is assigned the non-empty and whitespace trimmed
+// // values of key 'Field' split by commas and has a default
+// // value ["a", "b", "c"] if the key does not exist.
+// Field []string `properties:",default=a;b;c"`
+//
+// // Field is decoded recursively with "Field." as key prefix.
+// Field SomeStruct
+//
+// // Field is decoded recursively with "myName." as key prefix.
+// Field SomeStruct `properties:"myName"`
+//
+// // Field is decoded recursively with "Field." as key prefix
+// // and the next dotted element of the key as map key.
+// Field map[string]string
+//
+// // Field is decoded recursively with "myName." as key prefix
+// // and the next dotted element of the key as map key.
+// Field map[string]string `properties:"myName"`
+func (p *Properties) Decode(x interface{}) error {
+ t, v := reflect.TypeOf(x), reflect.ValueOf(x)
+ if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct {
+ return fmt.Errorf("not a pointer to struct: %s", t)
+ }
+ if err := dec(p, "", nil, nil, v); err != nil {
+ return err
+ }
+ return nil
+}
+
+func dec(p *Properties, key string, def *string, opts map[string]string, v reflect.Value) error {
+ t := v.Type()
+
+ // value returns the property value for key or the default if provided.
+ value := func() (string, error) {
+ if val, ok := p.Get(key); ok {
+ return val, nil
+ }
+ if def != nil {
+ return *def, nil
+ }
+ return "", fmt.Errorf("missing required key %s", key)
+ }
+
+ // conv converts a string to a value of the given type.
+ conv := func(s string, t reflect.Type) (val reflect.Value, err error) {
+ var v interface{}
+
+ switch {
+ case isDuration(t):
+ v, err = time.ParseDuration(s)
+
+ case isTime(t):
+ layout := opts["layout"]
+ if layout == "" {
+ layout = time.RFC3339
+ }
+ v, err = time.Parse(layout, s)
+
+ case isBool(t):
+ v, err = boolVal(s), nil
+
+ case isString(t):
+ v, err = s, nil
+
+ case isFloat(t):
+ v, err = strconv.ParseFloat(s, 64)
+
+ case isInt(t):
+ v, err = strconv.ParseInt(s, 10, 64)
+
+ case isUint(t):
+ v, err = strconv.ParseUint(s, 10, 64)
+
+ default:
+ return reflect.Zero(t), fmt.Errorf("unsupported type %s", t)
+ }
+ if err != nil {
+ return reflect.Zero(t), err
+ }
+ return reflect.ValueOf(v).Convert(t), nil
+ }
+
+ // keydef returns the property key and the default value based on the
+ // name of the struct field and the options in the tag.
+ keydef := func(f reflect.StructField) (string, *string, map[string]string) {
+ _key, _opts := parseTag(f.Tag.Get("properties"))
+
+ var _def *string
+ if d, ok := _opts["default"]; ok {
+ _def = &d
+ }
+ if _key != "" {
+ return _key, _def, _opts
+ }
+ return f.Name, _def, _opts
+ }
+
+ switch {
+ case isDuration(t) || isTime(t) || isBool(t) || isString(t) || isFloat(t) || isInt(t) || isUint(t):
+ s, err := value()
+ if err != nil {
+ return err
+ }
+ val, err := conv(s, t)
+ if err != nil {
+ return err
+ }
+ v.Set(val)
+
+ case isPtr(t):
+ return dec(p, key, def, opts, v.Elem())
+
+ case isStruct(t):
+ for i := 0; i < v.NumField(); i++ {
+ fv := v.Field(i)
+ fk, def, opts := keydef(t.Field(i))
+ if !fv.CanSet() {
+ return fmt.Errorf("cannot set %s", t.Field(i).Name)
+ }
+ if fk == "-" {
+ continue
+ }
+ if key != "" {
+ fk = key + "." + fk
+ }
+ if err := dec(p, fk, def, opts, fv); err != nil {
+ return err
+ }
+ }
+ return nil
+
+ case isArray(t):
+ val, err := value()
+ if err != nil {
+ return err
+ }
+ vals := split(val, ";")
+ a := reflect.MakeSlice(t, 0, len(vals))
+ for _, s := range vals {
+ val, err := conv(s, t.Elem())
+ if err != nil {
+ return err
+ }
+ a = reflect.Append(a, val)
+ }
+ v.Set(a)
+
+ case isMap(t):
+ valT := t.Elem()
+ m := reflect.MakeMap(t)
+ for postfix := range p.FilterStripPrefix(key + ".").m {
+ pp := strings.SplitN(postfix, ".", 2)
+ mk, mv := pp[0], reflect.New(valT)
+ if err := dec(p, key+"."+mk, nil, nil, mv); err != nil {
+ return err
+ }
+ m.SetMapIndex(reflect.ValueOf(mk), mv.Elem())
+ }
+ v.Set(m)
+
+ default:
+ return fmt.Errorf("unsupported type %s", t)
+ }
+ return nil
+}
+
+// split splits a string on sep, trims whitespace of elements
+// and omits empty elements
+func split(s string, sep string) []string {
+ var a []string
+ for _, v := range strings.Split(s, sep) {
+ if v = strings.TrimSpace(v); v != "" {
+ a = append(a, v)
+ }
+ }
+ return a
+}
+
+// parseTag parses a "key,k=v,k=v,..."
+func parseTag(tag string) (key string, opts map[string]string) {
+ opts = map[string]string{}
+ for i, s := range strings.Split(tag, ",") {
+ if i == 0 {
+ key = s
+ continue
+ }
+
+ pp := strings.SplitN(s, "=", 2)
+ if len(pp) == 1 {
+ opts[pp[0]] = ""
+ } else {
+ opts[pp[0]] = pp[1]
+ }
+ }
+ return key, opts
+}
+
+func isArray(t reflect.Type) bool { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice }
+func isBool(t reflect.Type) bool { return t.Kind() == reflect.Bool }
+func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
+func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map }
+func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr }
+func isString(t reflect.Type) bool { return t.Kind() == reflect.String }
+func isStruct(t reflect.Type) bool { return t.Kind() == reflect.Struct }
+func isTime(t reflect.Type) bool { return t == reflect.TypeOf(time.Time{}) }
+func isFloat(t reflect.Type) bool {
+ return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64
+}
+func isInt(t reflect.Type) bool {
+ return t.Kind() == reflect.Int || t.Kind() == reflect.Int8 || t.Kind() == reflect.Int16 || t.Kind() == reflect.Int32 || t.Kind() == reflect.Int64
+}
+func isUint(t reflect.Type) bool {
+ return t.Kind() == reflect.Uint || t.Kind() == reflect.Uint8 || t.Kind() == reflect.Uint16 || t.Kind() == reflect.Uint32 || t.Kind() == reflect.Uint64
+}
diff --git a/vendor/github.com/magiconair/properties/decode_test.go b/vendor/github.com/magiconair/properties/decode_test.go
new file mode 100644
index 000000000..c82931482
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/decode_test.go
@@ -0,0 +1,299 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestDecodeValues(t *testing.T) {
+ type S struct {
+ S string
+ BT bool
+ BF bool
+ I int
+ I8 int8
+ I16 int16
+ I32 int32
+ I64 int64
+ U uint
+ U8 uint8
+ U16 uint16
+ U32 uint32
+ U64 uint64
+ F32 float32
+ F64 float64
+ D time.Duration
+ TM time.Time
+ }
+ in := `
+ S=abc
+ BT=true
+ BF=false
+ I=-1
+ I8=-8
+ I16=-16
+ I32=-32
+ I64=-64
+ U=1
+ U8=8
+ U16=16
+ U32=32
+ U64=64
+ F32=3.2
+ F64=6.4
+ D=5s
+ TM=2015-01-02T12:34:56Z
+ `
+ out := &S{
+ S: "abc",
+ BT: true,
+ BF: false,
+ I: -1,
+ I8: -8,
+ I16: -16,
+ I32: -32,
+ I64: -64,
+ U: 1,
+ U8: 8,
+ U16: 16,
+ U32: 32,
+ U64: 64,
+ F32: 3.2,
+ F64: 6.4,
+ D: 5 * time.Second,
+ TM: tm(t, time.RFC3339, "2015-01-02T12:34:56Z"),
+ }
+ testDecode(t, in, &S{}, out)
+}
+
+func TestDecodeValueDefaults(t *testing.T) {
+ type S struct {
+ S string `properties:",default=abc"`
+ BT bool `properties:",default=true"`
+ BF bool `properties:",default=false"`
+ I int `properties:",default=-1"`
+ I8 int8 `properties:",default=-8"`
+ I16 int16 `properties:",default=-16"`
+ I32 int32 `properties:",default=-32"`
+ I64 int64 `properties:",default=-64"`
+ U uint `properties:",default=1"`
+ U8 uint8 `properties:",default=8"`
+ U16 uint16 `properties:",default=16"`
+ U32 uint32 `properties:",default=32"`
+ U64 uint64 `properties:",default=64"`
+ F32 float32 `properties:",default=3.2"`
+ F64 float64 `properties:",default=6.4"`
+ D time.Duration `properties:",default=5s"`
+ TM time.Time `properties:",default=2015-01-02T12:34:56Z"`
+ }
+ out := &S{
+ S: "abc",
+ BT: true,
+ BF: false,
+ I: -1,
+ I8: -8,
+ I16: -16,
+ I32: -32,
+ I64: -64,
+ U: 1,
+ U8: 8,
+ U16: 16,
+ U32: 32,
+ U64: 64,
+ F32: 3.2,
+ F64: 6.4,
+ D: 5 * time.Second,
+ TM: tm(t, time.RFC3339, "2015-01-02T12:34:56Z"),
+ }
+ testDecode(t, "", &S{}, out)
+}
+
+func TestDecodeArrays(t *testing.T) {
+ type S struct {
+ S []string
+ B []bool
+ I []int
+ I8 []int8
+ I16 []int16
+ I32 []int32
+ I64 []int64
+ U []uint
+ U8 []uint8
+ U16 []uint16
+ U32 []uint32
+ U64 []uint64
+ F32 []float32
+ F64 []float64
+ D []time.Duration
+ TM []time.Time
+ }
+ in := `
+ S=a;b
+ B=true;false
+ I=-1;-2
+ I8=-8;-9
+ I16=-16;-17
+ I32=-32;-33
+ I64=-64;-65
+ U=1;2
+ U8=8;9
+ U16=16;17
+ U32=32;33
+ U64=64;65
+ F32=3.2;3.3
+ F64=6.4;6.5
+ D=4s;5s
+ TM=2015-01-01T00:00:00Z;2016-01-01T00:00:00Z
+ `
+ out := &S{
+ S: []string{"a", "b"},
+ B: []bool{true, false},
+ I: []int{-1, -2},
+ I8: []int8{-8, -9},
+ I16: []int16{-16, -17},
+ I32: []int32{-32, -33},
+ I64: []int64{-64, -65},
+ U: []uint{1, 2},
+ U8: []uint8{8, 9},
+ U16: []uint16{16, 17},
+ U32: []uint32{32, 33},
+ U64: []uint64{64, 65},
+ F32: []float32{3.2, 3.3},
+ F64: []float64{6.4, 6.5},
+ D: []time.Duration{4 * time.Second, 5 * time.Second},
+ TM: []time.Time{tm(t, time.RFC3339, "2015-01-01T00:00:00Z"), tm(t, time.RFC3339, "2016-01-01T00:00:00Z")},
+ }
+ testDecode(t, in, &S{}, out)
+}
+
+func TestDecodeArrayDefaults(t *testing.T) {
+ type S struct {
+ S []string `properties:",default=a;b"`
+ B []bool `properties:",default=true;false"`
+ I []int `properties:",default=-1;-2"`
+ I8 []int8 `properties:",default=-8;-9"`
+ I16 []int16 `properties:",default=-16;-17"`
+ I32 []int32 `properties:",default=-32;-33"`
+ I64 []int64 `properties:",default=-64;-65"`
+ U []uint `properties:",default=1;2"`
+ U8 []uint8 `properties:",default=8;9"`
+ U16 []uint16 `properties:",default=16;17"`
+ U32 []uint32 `properties:",default=32;33"`
+ U64 []uint64 `properties:",default=64;65"`
+ F32 []float32 `properties:",default=3.2;3.3"`
+ F64 []float64 `properties:",default=6.4;6.5"`
+ D []time.Duration `properties:",default=4s;5s"`
+ TM []time.Time `properties:",default=2015-01-01T00:00:00Z;2016-01-01T00:00:00Z"`
+ }
+ out := &S{
+ S: []string{"a", "b"},
+ B: []bool{true, false},
+ I: []int{-1, -2},
+ I8: []int8{-8, -9},
+ I16: []int16{-16, -17},
+ I32: []int32{-32, -33},
+ I64: []int64{-64, -65},
+ U: []uint{1, 2},
+ U8: []uint8{8, 9},
+ U16: []uint16{16, 17},
+ U32: []uint32{32, 33},
+ U64: []uint64{64, 65},
+ F32: []float32{3.2, 3.3},
+ F64: []float64{6.4, 6.5},
+ D: []time.Duration{4 * time.Second, 5 * time.Second},
+ TM: []time.Time{tm(t, time.RFC3339, "2015-01-01T00:00:00Z"), tm(t, time.RFC3339, "2016-01-01T00:00:00Z")},
+ }
+ testDecode(t, "", &S{}, out)
+}
+
+func TestDecodeSkipUndef(t *testing.T) {
+ type S struct {
+ X string `properties:"-"`
+ Undef string `properties:",default=some value"`
+ }
+ in := `X=ignore`
+ out := &S{"", "some value"}
+ testDecode(t, in, &S{}, out)
+}
+
+func TestDecodeStruct(t *testing.T) {
+ type A struct {
+ S string
+ T string `properties:"t"`
+ U string `properties:"u,default=uuu"`
+ }
+ type S struct {
+ A A
+ B A `properties:"b"`
+ }
+ in := `
+ A.S=sss
+ A.t=ttt
+ b.S=SSS
+ b.t=TTT
+ `
+ out := &S{
+ A{S: "sss", T: "ttt", U: "uuu"},
+ A{S: "SSS", T: "TTT", U: "uuu"},
+ }
+ testDecode(t, in, &S{}, out)
+}
+
+func TestDecodeMap(t *testing.T) {
+ type S struct {
+ A string `properties:"a"`
+ }
+ type X struct {
+ A map[string]string
+ B map[string][]string
+ C map[string]map[string]string
+ D map[string]S
+ E map[string]int
+ F map[string]int `properties:"-"`
+ }
+ in := `
+ A.foo=bar
+ A.bar=bang
+ B.foo=a;b;c
+ B.bar=1;2;3
+ C.foo.one=1
+ C.foo.two=2
+ C.bar.three=3
+ C.bar.four=4
+ D.foo.a=bar
+ `
+ out := &X{
+ A: map[string]string{"foo": "bar", "bar": "bang"},
+ B: map[string][]string{"foo": []string{"a", "b", "c"}, "bar": []string{"1", "2", "3"}},
+ C: map[string]map[string]string{"foo": map[string]string{"one": "1", "two": "2"}, "bar": map[string]string{"three": "3", "four": "4"}},
+ D: map[string]S{"foo": S{"bar"}},
+ E: map[string]int{},
+ }
+ testDecode(t, in, &X{}, out)
+}
+
+func testDecode(t *testing.T, in string, v, out interface{}) {
+ p, err := parse(in)
+ if err != nil {
+ t.Fatalf("got %v want nil", err)
+ }
+ if err := p.Decode(v); err != nil {
+ t.Fatalf("got %v want nil", err)
+ }
+ if got, want := v, out; !reflect.DeepEqual(got, want) {
+ t.Fatalf("\ngot %+v\nwant %+v", got, want)
+ }
+}
+
+func tm(t *testing.T, layout, s string) time.Time {
+ tm, err := time.Parse(layout, s)
+ if err != nil {
+ t.Fatalf("got %v want nil", err)
+ }
+ return tm
+}
diff --git a/vendor/github.com/magiconair/properties/doc.go b/vendor/github.com/magiconair/properties/doc.go
new file mode 100644
index 000000000..36c836808
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/doc.go
@@ -0,0 +1,156 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package properties provides functions for reading and writing
+// ISO-8859-1 and UTF-8 encoded .properties files and has
+// support for recursive property expansion.
+//
+// Java properties files are ISO-8859-1 encoded and use Unicode
+// literals for characters outside the ISO character set. Unicode
+// literals can be used in UTF-8 encoded properties files but
+// aren't necessary.
+//
+// To load a single properties file use MustLoadFile():
+//
+// p := properties.MustLoadFile(filename, properties.UTF8)
+//
+// To load multiple properties files use MustLoadFiles()
+// which loads the files in the given order and merges the
+// result. Missing properties files can be ignored if the
+// 'ignoreMissing' flag is set to true.
+//
+// Filenames can contain environment variables which are expanded
+// before loading.
+//
+// f1 := "/etc/myapp/myapp.conf"
+// f2 := "/home/${USER}/myapp.conf"
+// p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true)
+//
+// All of the different key/value delimiters ' ', ':' and '=' are
+// supported as well as the comment characters '!' and '#' and
+// multi-line values.
+//
+// ! this is a comment
+// # and so is this
+//
+// # the following expressions are equal
+// key value
+// key=value
+// key:value
+// key = value
+// key : value
+// key = val\
+// ue
+//
+// Properties stores all comments preceding a key and provides
+// GetComments() and SetComments() methods to retrieve and
+// update them. The convenience functions GetComment() and
+// SetComment() allow access to the last comment. The
+// WriteComment() method writes properties files including
+// the comments and with the keys in the original order.
+// This can be used for sanitizing properties files.
+//
+// Property expansion is recursive and circular references
+// and malformed expressions are not allowed and cause an
+// error. Expansion of environment variables is supported.
+//
+// # standard property
+// key = value
+//
+// # property expansion: key2 = value
+// key2 = ${key}
+//
+// # recursive expansion: key3 = value
+// key3 = ${key2}
+//
+// # circular reference (error)
+// key = ${key}
+//
+// # malformed expression (error)
+// key = ${ke
+//
+// # refers to the users' home dir
+// home = ${HOME}
+//
+// # local key takes precendence over env var: u = foo
+// USER = foo
+// u = ${USER}
+//
+// The default property expansion format is ${key} but can be
+// changed by setting different pre- and postfix values on the
+// Properties object.
+//
+// p := properties.NewProperties()
+// p.Prefix = "#["
+// p.Postfix = "]#"
+//
+// Properties provides convenience functions for getting typed
+// values with default values if the key does not exist or the
+// type conversion failed.
+//
+// # Returns true if the value is either "1", "on", "yes" or "true"
+// # Returns false for every other value and the default value if
+// # the key does not exist.
+// v = p.GetBool("key", false)
+//
+// # Returns the value if the key exists and the format conversion
+// # was successful. Otherwise, the default value is returned.
+// v = p.GetInt64("key", 999)
+// v = p.GetUint64("key", 999)
+// v = p.GetFloat64("key", 123.0)
+// v = p.GetString("key", "def")
+// v = p.GetDuration("key", 999)
+//
+// As an alterantive properties may be applied with the standard
+// library's flag implementation at any time.
+//
+// # Standard configuration
+// v = flag.Int("key", 999, "help message")
+// flag.Parse()
+//
+// # Merge p into the flag set
+// p.MustFlag(flag.CommandLine)
+//
+// Properties provides several MustXXX() convenience functions
+// which will terminate the app if an error occurs. The behavior
+// of the failure is configurable and the default is to call
+// log.Fatal(err). To have the MustXXX() functions panic instead
+// of logging the error set a different ErrorHandler before
+// you use the Properties package.
+//
+// properties.ErrorHandler = properties.PanicHandler
+//
+// # Will panic instead of logging an error
+// p := properties.MustLoadFile("config.properties")
+//
+// You can also provide your own ErrorHandler function. The only requirement
+// is that the error handler function must exit after handling the error.
+//
+// properties.ErrorHandler = func(err error) {
+// fmt.Println(err)
+// os.Exit(1)
+// }
+//
+// # Will write to stdout and then exit
+// p := properties.MustLoadFile("config.properties")
+//
+// Properties can also be loaded into a struct via the `Decode`
+// method, e.g.
+//
+// type S struct {
+// A string `properties:"a,default=foo"`
+// D time.Duration `properties:"timeout,default=5s"`
+// E time.Time `properties:"expires,layout=2006-01-02,default=2015-01-01"`
+// }
+//
+// See `Decode()` method for the full documentation.
+//
+// The following documents provide a description of the properties
+// file format.
+//
+// http://en.wikipedia.org/wiki/.properties
+//
+// http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29
+//
+package properties
diff --git a/vendor/github.com/magiconair/properties/example_test.go b/vendor/github.com/magiconair/properties/example_test.go
new file mode 100644
index 000000000..6f21dfbd8
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/example_test.go
@@ -0,0 +1,93 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "fmt"
+ "log"
+)
+
+func ExampleLoad_iso88591() {
+ buf := []byte("key = ISO-8859-1 value with unicode literal \\u2318 and umlaut \xE4") // 0xE4 == ä
+ p, _ := Load(buf, ISO_8859_1)
+ v, ok := p.Get("key")
+ fmt.Println(ok)
+ fmt.Println(v)
+ // Output:
+ // true
+ // ISO-8859-1 value with unicode literal ⌘ and umlaut ä
+}
+
+func ExampleLoad_utf8() {
+ p, _ := Load([]byte("key = UTF-8 value with unicode character ⌘ and umlaut ä"), UTF8)
+ v, ok := p.Get("key")
+ fmt.Println(ok)
+ fmt.Println(v)
+ // Output:
+ // true
+ // UTF-8 value with unicode character ⌘ and umlaut ä
+}
+
+func ExampleProperties_GetBool() {
+ var input = `
+ key=1
+ key2=On
+ key3=YES
+ key4=true`
+ p, _ := Load([]byte(input), ISO_8859_1)
+ fmt.Println(p.GetBool("key", false))
+ fmt.Println(p.GetBool("key2", false))
+ fmt.Println(p.GetBool("key3", false))
+ fmt.Println(p.GetBool("key4", false))
+ fmt.Println(p.GetBool("keyX", false))
+ // Output:
+ // true
+ // true
+ // true
+ // true
+ // false
+}
+
+func ExampleProperties_GetString() {
+ p, _ := Load([]byte("key=value"), ISO_8859_1)
+ v := p.GetString("another key", "default value")
+ fmt.Println(v)
+ // Output:
+ // default value
+}
+
+func Example() {
+ // Decode some key/value pairs with expressions
+ p, err := Load([]byte("key=value\nkey2=${key}"), ISO_8859_1)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Get a valid key
+ if v, ok := p.Get("key"); ok {
+ fmt.Println(v)
+ }
+
+ // Get an invalid key
+ if _, ok := p.Get("does not exist"); !ok {
+ fmt.Println("invalid key")
+ }
+
+ // Get a key with a default value
+ v := p.GetString("does not exist", "some value")
+ fmt.Println(v)
+
+ // Dump the expanded key/value pairs of the Properties
+ fmt.Println("Expanded key/value pairs")
+ fmt.Println(p)
+
+ // Output:
+ // value
+ // invalid key
+ // some value
+ // Expanded key/value pairs
+ // key = value
+ // key2 = value
+}
diff --git a/vendor/github.com/magiconair/properties/integrate.go b/vendor/github.com/magiconair/properties/integrate.go
new file mode 100644
index 000000000..0d775e035
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/integrate.go
@@ -0,0 +1,34 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import "flag"
+
+// MustFlag sets flags that are skipped by dst.Parse when p contains
+// the respective key for flag.Flag.Name.
+//
+// It's use is recommended with command line arguments as in:
+// flag.Parse()
+// p.MustFlag(flag.CommandLine)
+func (p *Properties) MustFlag(dst *flag.FlagSet) {
+ m := make(map[string]*flag.Flag)
+ dst.VisitAll(func(f *flag.Flag) {
+ m[f.Name] = f
+ })
+ dst.Visit(func(f *flag.Flag) {
+ delete(m, f.Name) // overridden
+ })
+
+ for name, f := range m {
+ v, ok := p.Get(name)
+ if !ok {
+ continue
+ }
+
+ if err := f.Value.Set(v); err != nil {
+ ErrorHandler(err)
+ }
+ }
+}
diff --git a/vendor/github.com/magiconair/properties/integrate_test.go b/vendor/github.com/magiconair/properties/integrate_test.go
new file mode 100644
index 000000000..cbee181f6
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/integrate_test.go
@@ -0,0 +1,76 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "flag"
+ "fmt"
+ "testing"
+)
+
+// TestFlag verifies Properties.MustFlag without flag.FlagSet.Parse
+func TestFlag(t *testing.T) {
+ f := flag.NewFlagSet("src", flag.PanicOnError)
+ gotS := f.String("s", "?", "string flag")
+ gotI := f.Int("i", -1, "int flag")
+
+ p := NewProperties()
+ p.MustSet("s", "t")
+ p.MustSet("i", "9")
+ p.MustFlag(f)
+
+ if want := "t"; *gotS != want {
+ t.Errorf("Got string s=%q, want %q", *gotS, want)
+ }
+ if want := 9; *gotI != want {
+ t.Errorf("Got int i=%d, want %d", *gotI, want)
+ }
+}
+
+// TestFlagOverride verifies Properties.MustFlag with flag.FlagSet.Parse.
+func TestFlagOverride(t *testing.T) {
+ f := flag.NewFlagSet("src", flag.PanicOnError)
+ gotA := f.Int("a", 1, "remain default")
+ gotB := f.Int("b", 2, "customized")
+ gotC := f.Int("c", 3, "overridden")
+
+ if err := f.Parse([]string{"-c", "4"}); err != nil {
+ t.Fatal(err)
+ }
+
+ p := NewProperties()
+ p.MustSet("b", "5")
+ p.MustSet("c", "6")
+ p.MustFlag(f)
+
+ if want := 1; *gotA != want {
+ t.Errorf("Got remain default a=%d, want %d", *gotA, want)
+ }
+ if want := 5; *gotB != want {
+ t.Errorf("Got customized b=%d, want %d", *gotB, want)
+ }
+ if want := 4; *gotC != want {
+ t.Errorf("Got overriden c=%d, want %d", *gotC, want)
+ }
+}
+
+func ExampleProperties_MustFlag() {
+ x := flag.Int("x", 0, "demo customize")
+ y := flag.Int("y", 0, "demo override")
+
+ // Demo alternative for flag.Parse():
+ flag.CommandLine.Parse([]string{"-y", "10"})
+ fmt.Printf("flagged as x=%d, y=%d\n", *x, *y)
+
+ p := NewProperties()
+ p.MustSet("x", "7")
+ p.MustSet("y", "42") // note discard
+ p.MustFlag(flag.CommandLine)
+ fmt.Printf("configured to x=%d, y=%d\n", *x, *y)
+
+ // Output:
+ // flagged as x=0, y=10
+ // configured to x=7, y=10
+}
diff --git a/vendor/github.com/magiconair/properties/lex.go b/vendor/github.com/magiconair/properties/lex.go
new file mode 100644
index 000000000..a3cba0319
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/lex.go
@@ -0,0 +1,408 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// Parts of the lexer are from the template/text/parser package
+// For these parts the following applies:
+//
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file of the go 1.2
+// distribution.
+
+package properties
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+)
+
+// item represents a token or text string returned from the scanner.
+type item struct {
+ typ itemType // The type of this item.
+ pos int // The starting position, in bytes, of this item in the input string.
+ val string // The value of this item.
+}
+
+func (i item) String() string {
+ switch {
+ case i.typ == itemEOF:
+ return "EOF"
+ case i.typ == itemError:
+ return i.val
+ case len(i.val) > 10:
+ return fmt.Sprintf("%.10q...", i.val)
+ }
+ return fmt.Sprintf("%q", i.val)
+}
+
+// itemType identifies the type of lex items.
+type itemType int
+
+const (
+ itemError itemType = iota // error occurred; value is text of error
+ itemEOF
+ itemKey // a key
+ itemValue // a value
+ itemComment // a comment
+)
+
+// defines a constant for EOF
+const eof = -1
+
+// permitted whitespace characters space, FF and TAB
+const whitespace = " \f\t"
+
+// stateFn represents the state of the scanner as a function that returns the next state.
+type stateFn func(*lexer) stateFn
+
+// lexer holds the state of the scanner.
+type lexer struct {
+ input string // the string being scanned
+ state stateFn // the next lexing function to enter
+ pos int // current position in the input
+ start int // start position of this item
+ width int // width of last rune read from input
+ lastPos int // position of most recent item returned by nextItem
+ runes []rune // scanned runes for this item
+ items chan item // channel of scanned items
+}
+
+// next returns the next rune in the input.
+func (l *lexer) next() rune {
+ if l.pos >= len(l.input) {
+ l.width = 0
+ return eof
+ }
+ r, w := utf8.DecodeRuneInString(l.input[l.pos:])
+ l.width = w
+ l.pos += l.width
+ return r
+}
+
+// peek returns but does not consume the next rune in the input.
+func (l *lexer) peek() rune {
+ r := l.next()
+ l.backup()
+ return r
+}
+
+// backup steps back one rune. Can only be called once per call of next.
+func (l *lexer) backup() {
+ l.pos -= l.width
+}
+
+// emit passes an item back to the client.
+func (l *lexer) emit(t itemType) {
+ i := item{t, l.start, string(l.runes)}
+ l.items <- i
+ l.start = l.pos
+ l.runes = l.runes[:0]
+}
+
+// ignore skips over the pending input before this point.
+func (l *lexer) ignore() {
+ l.start = l.pos
+}
+
+// appends the rune to the current value
+func (l *lexer) appendRune(r rune) {
+ l.runes = append(l.runes, r)
+}
+
+// accept consumes the next rune if it's from the valid set.
+func (l *lexer) accept(valid string) bool {
+ if strings.ContainsRune(valid, l.next()) {
+ return true
+ }
+ l.backup()
+ return false
+}
+
+// acceptRun consumes a run of runes from the valid set.
+func (l *lexer) acceptRun(valid string) {
+ for strings.ContainsRune(valid, l.next()) {
+ }
+ l.backup()
+}
+
+// acceptRunUntil consumes a run of runes up to a terminator.
+func (l *lexer) acceptRunUntil(term rune) {
+ for term != l.next() {
+ }
+ l.backup()
+}
+
+// hasText returns true if the current parsed text is not empty.
+func (l *lexer) isNotEmpty() bool {
+ return l.pos > l.start
+}
+
+// lineNumber reports which line we're on, based on the position of
+// the previous item returned by nextItem. Doing it this way
+// means we don't have to worry about peek double counting.
+func (l *lexer) lineNumber() int {
+ return 1 + strings.Count(l.input[:l.lastPos], "\n")
+}
+
+// errorf returns an error token and terminates the scan by passing
+// back a nil pointer that will be the next state, terminating l.nextItem.
+func (l *lexer) errorf(format string, args ...interface{}) stateFn {
+ l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
+ return nil
+}
+
+// nextItem returns the next item from the input.
+func (l *lexer) nextItem() item {
+ i := <-l.items
+ l.lastPos = i.pos
+ return i
+}
+
+// lex creates a new scanner for the input string.
+func lex(input string) *lexer {
+ l := &lexer{
+ input: input,
+ items: make(chan item),
+ runes: make([]rune, 0, 32),
+ }
+ go l.run()
+ return l
+}
+
+// run runs the state machine for the lexer.
+func (l *lexer) run() {
+ for l.state = lexBeforeKey(l); l.state != nil; {
+ l.state = l.state(l)
+ }
+}
+
+// state functions
+
+// lexBeforeKey scans until a key begins.
+func lexBeforeKey(l *lexer) stateFn {
+ switch r := l.next(); {
+ case isEOF(r):
+ l.emit(itemEOF)
+ return nil
+
+ case isEOL(r):
+ l.ignore()
+ return lexBeforeKey
+
+ case isComment(r):
+ return lexComment
+
+ case isWhitespace(r):
+ l.acceptRun(whitespace)
+ l.ignore()
+ return lexKey
+
+ default:
+ l.backup()
+ return lexKey
+ }
+}
+
+// lexComment scans a comment line. The comment character has already been scanned.
+func lexComment(l *lexer) stateFn {
+ l.acceptRun(whitespace)
+ l.ignore()
+ for {
+ switch r := l.next(); {
+ case isEOF(r):
+ l.ignore()
+ l.emit(itemEOF)
+ return nil
+ case isEOL(r):
+ l.emit(itemComment)
+ return lexBeforeKey
+ default:
+ l.appendRune(r)
+ }
+ }
+}
+
+// lexKey scans the key up to a delimiter
+func lexKey(l *lexer) stateFn {
+ var r rune
+
+Loop:
+ for {
+ switch r = l.next(); {
+
+ case isEscape(r):
+ err := l.scanEscapeSequence()
+ if err != nil {
+ return l.errorf(err.Error())
+ }
+
+ case isEndOfKey(r):
+ l.backup()
+ break Loop
+
+ case isEOF(r):
+ break Loop
+
+ default:
+ l.appendRune(r)
+ }
+ }
+
+ if len(l.runes) > 0 {
+ l.emit(itemKey)
+ }
+
+ if isEOF(r) {
+ l.emit(itemEOF)
+ return nil
+ }
+
+ return lexBeforeValue
+}
+
+// lexBeforeValue scans the delimiter between key and value.
+// Leading and trailing whitespace is ignored.
+// We expect to be just after the key.
+func lexBeforeValue(l *lexer) stateFn {
+ l.acceptRun(whitespace)
+ l.accept(":=")
+ l.acceptRun(whitespace)
+ l.ignore()
+ return lexValue
+}
+
+// lexValue scans text until the end of the line. We expect to be just after the delimiter.
+func lexValue(l *lexer) stateFn {
+ for {
+ switch r := l.next(); {
+ case isEscape(r):
+ if isEOL(l.peek()) {
+ l.next()
+ l.acceptRun(whitespace)
+ } else {
+ err := l.scanEscapeSequence()
+ if err != nil {
+ return l.errorf(err.Error())
+ }
+ }
+
+ case isEOL(r):
+ l.emit(itemValue)
+ l.ignore()
+ return lexBeforeKey
+
+ case isEOF(r):
+ l.emit(itemValue)
+ l.emit(itemEOF)
+ return nil
+
+ default:
+ l.appendRune(r)
+ }
+ }
+}
+
+// scanEscapeSequence scans either one of the escaped characters
+// or a unicode literal. We expect to be after the escape character.
+func (l *lexer) scanEscapeSequence() error {
+ switch r := l.next(); {
+
+ case isEscapedCharacter(r):
+ l.appendRune(decodeEscapedCharacter(r))
+ return nil
+
+ case atUnicodeLiteral(r):
+ return l.scanUnicodeLiteral()
+
+ case isEOF(r):
+ return fmt.Errorf("premature EOF")
+
+ // silently drop the escape character and append the rune as is
+ default:
+ l.appendRune(r)
+ return nil
+ }
+}
+
+// scans a unicode literal in the form \uXXXX. We expect to be after the \u.
+func (l *lexer) scanUnicodeLiteral() error {
+ // scan the digits
+ d := make([]rune, 4)
+ for i := 0; i < 4; i++ {
+ d[i] = l.next()
+ if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {
+ return fmt.Errorf("invalid unicode literal")
+ }
+ }
+
+ // decode the digits into a rune
+ r, err := strconv.ParseInt(string(d), 16, 0)
+ if err != nil {
+ return err
+ }
+
+ l.appendRune(rune(r))
+ return nil
+}
+
+// decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character.
+func decodeEscapedCharacter(r rune) rune {
+ switch r {
+ case 'f':
+ return '\f'
+ case 'n':
+ return '\n'
+ case 'r':
+ return '\r'
+ case 't':
+ return '\t'
+ default:
+ return r
+ }
+}
+
+// atUnicodeLiteral reports whether we are at a unicode literal.
+// The escape character has already been consumed.
+func atUnicodeLiteral(r rune) bool {
+ return r == 'u'
+}
+
+// isComment reports whether we are at the start of a comment.
+func isComment(r rune) bool {
+ return r == '#' || r == '!'
+}
+
+// isEndOfKey reports whether the rune terminates the current key.
+func isEndOfKey(r rune) bool {
+ return strings.ContainsRune(" \f\t\r\n:=", r)
+}
+
+// isEOF reports whether we are at EOF.
+func isEOF(r rune) bool {
+ return r == eof
+}
+
+// isEOL reports whether we are at a new line character.
+func isEOL(r rune) bool {
+ return r == '\n' || r == '\r'
+}
+
+// isEscape reports whether the rune is the escape character which
+// prefixes unicode literals and other escaped characters.
+func isEscape(r rune) bool {
+ return r == '\\'
+}
+
+// isEscapedCharacter reports whether we are at one of the characters that need escaping.
+// The escape character has already been consumed.
+func isEscapedCharacter(r rune) bool {
+ return strings.ContainsRune(" :=fnrt", r)
+}
+
+// isWhitespace reports whether the rune is a whitespace character.
+func isWhitespace(r rune) bool {
+ return strings.ContainsRune(whitespace, r)
+}
diff --git a/vendor/github.com/magiconair/properties/load.go b/vendor/github.com/magiconair/properties/load.go
new file mode 100644
index 000000000..278cc2ea0
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/load.go
@@ -0,0 +1,241 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "strings"
+)
+
+// Encoding specifies encoding of the input data.
+type Encoding uint
+
+const (
+ // UTF8 interprets the input data as UTF-8.
+ UTF8 Encoding = 1 << iota
+
+ // ISO_8859_1 interprets the input data as ISO-8859-1.
+ ISO_8859_1
+)
+
+// Load reads a buffer into a Properties struct.
+func Load(buf []byte, enc Encoding) (*Properties, error) {
+ return loadBuf(buf, enc)
+}
+
+// LoadString reads an UTF8 string into a properties struct.
+func LoadString(s string) (*Properties, error) {
+ return loadBuf([]byte(s), UTF8)
+}
+
+// LoadMap creates a new Properties struct from a string map.
+func LoadMap(m map[string]string) *Properties {
+ p := NewProperties()
+ for k, v := range m {
+ p.Set(k, v)
+ }
+ return p
+}
+
+// LoadFile reads a file into a Properties struct.
+func LoadFile(filename string, enc Encoding) (*Properties, error) {
+ return loadAll([]string{filename}, enc, false)
+}
+
+// LoadFiles reads multiple files in the given order into
+// a Properties struct. If 'ignoreMissing' is true then
+// non-existent files will not be reported as error.
+func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
+ return loadAll(filenames, enc, ignoreMissing)
+}
+
+// LoadURL reads the content of the URL into a Properties struct.
+//
+// The encoding is determined via the Content-Type header which
+// should be set to 'text/plain'. If the 'charset' parameter is
+// missing, 'iso-8859-1' or 'latin1' the encoding is set to
+// ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the
+// encoding is set to UTF-8. A missing content type header is
+// interpreted as 'text/plain; charset=utf-8'.
+func LoadURL(url string) (*Properties, error) {
+ return loadAll([]string{url}, UTF8, false)
+}
+
+// LoadURLs reads the content of multiple URLs in the given order into a
+// Properties struct. If 'ignoreMissing' is true then a 404 status code will
+// not be reported as error. See LoadURL for the Content-Type header
+// and the encoding.
+func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
+ return loadAll(urls, UTF8, ignoreMissing)
+}
+
+// LoadAll reads the content of multiple URLs or files in the given order into a
+// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
+// not be reported as error. Encoding sets the encoding for files. For the URLs please see
+// LoadURL for the Content-Type header and the encoding.
+func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
+ return loadAll(names, enc, ignoreMissing)
+}
+
+// MustLoadString reads an UTF8 string into a Properties struct and
+// panics on error.
+func MustLoadString(s string) *Properties {
+ return must(LoadString(s))
+}
+
+// MustLoadFile reads a file into a Properties struct and
+// panics on error.
+func MustLoadFile(filename string, enc Encoding) *Properties {
+ return must(LoadFile(filename, enc))
+}
+
+// MustLoadFiles reads multiple files in the given order into
+// a Properties struct and panics on error. If 'ignoreMissing'
+// is true then non-existent files will not be reported as error.
+func MustLoadFiles(filenames []string, enc Encoding, ignoreMissing bool) *Properties {
+ return must(LoadFiles(filenames, enc, ignoreMissing))
+}
+
+// MustLoadURL reads the content of a URL into a Properties struct and
+// panics on error.
+func MustLoadURL(url string) *Properties {
+ return must(LoadURL(url))
+}
+
+// MustLoadURLs reads the content of multiple URLs in the given order into a
+// Properties struct and panics on error. If 'ignoreMissing' is true then a 404
+// status code will not be reported as error.
+func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
+ return must(LoadURLs(urls, ignoreMissing))
+}
+
+// MustLoadAll reads the content of multiple URLs or files in the given order into a
+// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
+// not be reported as error. Encoding sets the encoding for files. For the URLs please see
+// LoadURL for the Content-Type header and the encoding. It panics on error.
+func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties {
+ return must(LoadAll(names, enc, ignoreMissing))
+}
+
+func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
+ p, err := parse(convert(buf, enc))
+ if err != nil {
+ return nil, err
+ }
+ return p, p.check()
+}
+
+func loadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
+ result := NewProperties()
+ for _, name := range names {
+ n, err := expandName(name)
+ if err != nil {
+ return nil, err
+ }
+ var p *Properties
+ if strings.HasPrefix(n, "http://") || strings.HasPrefix(n, "https://") {
+ p, err = loadURL(n, ignoreMissing)
+ } else {
+ p, err = loadFile(n, enc, ignoreMissing)
+ }
+ if err != nil {
+ return nil, err
+ }
+ result.Merge(p)
+
+ }
+ return result, result.check()
+}
+
+func loadFile(filename string, enc Encoding, ignoreMissing bool) (*Properties, error) {
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ if ignoreMissing && os.IsNotExist(err) {
+ LogPrintf("properties: %s not found. skipping", filename)
+ return NewProperties(), nil
+ }
+ return nil, err
+ }
+ p, err := parse(convert(data, enc))
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+func loadURL(url string, ignoreMissing bool) (*Properties, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
+ }
+ if resp.StatusCode == 404 && ignoreMissing {
+ LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
+ return NewProperties(), nil
+ }
+ if resp.StatusCode != 200 {
+ return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
+ }
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
+ }
+ if err = resp.Body.Close(); err != nil {
+ return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
+ }
+
+ ct := resp.Header.Get("Content-Type")
+ var enc Encoding
+ switch strings.ToLower(ct) {
+ case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
+ enc = ISO_8859_1
+ case "", "text/plain; charset=utf-8":
+ enc = UTF8
+ default:
+ return nil, fmt.Errorf("properties: invalid content type %s", ct)
+ }
+
+ p, err := parse(convert(body, enc))
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+func must(p *Properties, err error) *Properties {
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return p
+}
+
+// expandName expands ${ENV_VAR} expressions in a name.
+// If the environment variable does not exist then it will be replaced
+// with an empty string. Malformed expressions like "${ENV_VAR" will
+// be reported as error.
+func expandName(name string) (string, error) {
+ return expand(name, make(map[string]bool), "${", "}", make(map[string]string))
+}
+
+// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
+// For ISO-8859-1 we can convert each byte straight into a rune since the
+// first 256 unicode code points cover ISO-8859-1.
+func convert(buf []byte, enc Encoding) string {
+ switch enc {
+ case UTF8:
+ return string(buf)
+ case ISO_8859_1:
+ runes := make([]rune, len(buf))
+ for i, b := range buf {
+ runes[i] = rune(b)
+ }
+ return string(runes)
+ default:
+ ErrorHandler(fmt.Errorf("unsupported encoding %v", enc))
+ }
+ panic("ErrorHandler should exit")
+}
diff --git a/vendor/github.com/magiconair/properties/load_test.go b/vendor/github.com/magiconair/properties/load_test.go
new file mode 100644
index 000000000..d8770c8a0
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/load_test.go
@@ -0,0 +1,231 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/magiconair/properties/assert"
+)
+
+func TestLoadFailsWithNotExistingFile(t *testing.T) {
+ _, err := LoadFile("doesnotexist.properties", ISO_8859_1)
+ assert.Equal(t, err != nil, true, "")
+ assert.Matches(t, err.Error(), "open.*no such file or directory")
+}
+
+func TestLoadFilesFailsOnNotExistingFile(t *testing.T) {
+ _, err := LoadFile("doesnotexist.properties", ISO_8859_1)
+ assert.Equal(t, err != nil, true, "")
+ assert.Matches(t, err.Error(), "open.*no such file or directory")
+}
+
+func TestLoadFilesDoesNotFailOnNotExistingFileAndIgnoreMissing(t *testing.T) {
+ p, err := LoadFiles([]string{"doesnotexist.properties"}, ISO_8859_1, true)
+ assert.Equal(t, err, nil)
+ assert.Equal(t, p.Len(), 0)
+}
+
+func TestLoadString(t *testing.T) {
+ x := "key=äüö"
+ p1 := MustLoadString(x)
+ p2 := must(Load([]byte(x), UTF8))
+ assert.Equal(t, p1, p2)
+}
+
+func TestLoadMap(t *testing.T) {
+ // LoadMap does not guarantee the same import order
+ // of keys every time since map access is randomized.
+ // Therefore, we need to compare the generated maps.
+ m := map[string]string{"key": "value", "abc": "def"}
+ assert.Equal(t, LoadMap(m).Map(), m)
+}
+
+func TestLoadFile(t *testing.T) {
+ tf := make(tempFiles, 0)
+ defer tf.removeAll()
+
+ filename := tf.makeFile("key=value")
+ p := MustLoadFile(filename, ISO_8859_1)
+
+ assert.Equal(t, p.Len(), 1)
+ assertKeyValues(t, "", p, "key", "value")
+}
+
+func TestLoadFiles(t *testing.T) {
+ tf := make(tempFiles, 0)
+ defer tf.removeAll()
+
+ filename := tf.makeFile("key=value")
+ filename2 := tf.makeFile("key2=value2")
+ p := MustLoadFiles([]string{filename, filename2}, ISO_8859_1, false)
+ assertKeyValues(t, "", p, "key", "value", "key2", "value2")
+}
+
+func TestLoadExpandedFile(t *testing.T) {
+ tf := make(tempFiles, 0)
+ defer tf.removeAll()
+
+ if err := os.Setenv("_VARX", "some-value"); err != nil {
+ t.Fatal(err)
+ }
+ filename := tf.makeFilePrefix(os.Getenv("_VARX"), "key=value")
+ filename = strings.Replace(filename, os.Getenv("_VARX"), "${_VARX}", -1)
+ p := MustLoadFile(filename, ISO_8859_1)
+ assertKeyValues(t, "", p, "key", "value")
+}
+
+func TestLoadFilesAndIgnoreMissing(t *testing.T) {
+ tf := make(tempFiles, 0)
+ defer tf.removeAll()
+
+ filename := tf.makeFile("key=value")
+ filename2 := tf.makeFile("key2=value2")
+ p := MustLoadFiles([]string{filename, filename + "foo", filename2, filename2 + "foo"}, ISO_8859_1, true)
+ assertKeyValues(t, "", p, "key", "value", "key2", "value2")
+}
+
+func TestLoadURL(t *testing.T) {
+ srv := testServer()
+ defer srv.Close()
+ p := MustLoadURL(srv.URL + "/a")
+ assertKeyValues(t, "", p, "key", "value")
+}
+
+func TestLoadURLs(t *testing.T) {
+ srv := testServer()
+ defer srv.Close()
+ p := MustLoadURLs([]string{srv.URL + "/a", srv.URL + "/b"}, false)
+ assertKeyValues(t, "", p, "key", "value", "key2", "value2")
+}
+
+func TestLoadURLsAndFailMissing(t *testing.T) {
+ srv := testServer()
+ defer srv.Close()
+ p, err := LoadURLs([]string{srv.URL + "/a", srv.URL + "/c"}, false)
+ assert.Equal(t, p, (*Properties)(nil))
+ assert.Matches(t, err.Error(), ".*returned 404.*")
+}
+
+func TestLoadURLsAndIgnoreMissing(t *testing.T) {
+ srv := testServer()
+ defer srv.Close()
+ p := MustLoadURLs([]string{srv.URL + "/a", srv.URL + "/b", srv.URL + "/c"}, true)
+ assertKeyValues(t, "", p, "key", "value", "key2", "value2")
+}
+
+func TestLoadURLEncoding(t *testing.T) {
+ srv := testServer()
+ defer srv.Close()
+
+ uris := []string{"/none", "/utf8", "/plain", "/latin1", "/iso88591"}
+ for i, uri := range uris {
+ p := MustLoadURL(srv.URL + uri)
+ assert.Equal(t, p.GetString("key", ""), "äöü", fmt.Sprintf("%d", i))
+ }
+}
+
+func TestLoadURLFailInvalidEncoding(t *testing.T) {
+ srv := testServer()
+ defer srv.Close()
+
+ p, err := LoadURL(srv.URL + "/json")
+ assert.Equal(t, p, (*Properties)(nil))
+ assert.Matches(t, err.Error(), ".*invalid content type.*")
+}
+
+func TestLoadAll(t *testing.T) {
+ tf := make(tempFiles, 0)
+ defer tf.removeAll()
+
+ filename := tf.makeFile("key=value")
+ filename2 := tf.makeFile("key2=value3")
+ filename3 := tf.makeFile("key=value4")
+ srv := testServer()
+ defer srv.Close()
+ p := MustLoadAll([]string{filename, filename2, srv.URL + "/a", srv.URL + "/b", filename3}, UTF8, false)
+ assertKeyValues(t, "", p, "key", "value4", "key2", "value2")
+}
+
+type tempFiles []string
+
+func (tf *tempFiles) removeAll() {
+ for _, path := range *tf {
+ err := os.Remove(path)
+ if err != nil {
+ fmt.Printf("os.Remove: %v", err)
+ }
+ }
+}
+
+func (tf *tempFiles) makeFile(data string) string {
+ return tf.makeFilePrefix("properties", data)
+}
+
+func (tf *tempFiles) makeFilePrefix(prefix, data string) string {
+ f, err := ioutil.TempFile("", prefix)
+ if err != nil {
+ panic("ioutil.TempFile: " + err.Error())
+ }
+
+ // remember the temp file so that we can remove it later
+ *tf = append(*tf, f.Name())
+
+ n, err := fmt.Fprint(f, data)
+ if err != nil {
+ panic("fmt.Fprintln: " + err.Error())
+ }
+ if n != len(data) {
+ panic(fmt.Sprintf("Data size mismatch. expected=%d wrote=%d\n", len(data), n))
+ }
+
+ err = f.Close()
+ if err != nil {
+ panic("f.Close: " + err.Error())
+ }
+
+ return f.Name()
+}
+
+func testServer() *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ send := func(data []byte, contentType string) {
+ w.Header().Set("Content-Type", contentType)
+ if _, err := w.Write(data); err != nil {
+ panic(err)
+ }
+ }
+
+ utf8 := []byte("key=äöü")
+ iso88591 := []byte{0x6b, 0x65, 0x79, 0x3d, 0xe4, 0xf6, 0xfc} // key=äöü
+
+ switch r.RequestURI {
+ case "/a":
+ send([]byte("key=value"), "")
+ case "/b":
+ send([]byte("key2=value2"), "")
+ case "/none":
+ send(utf8, "")
+ case "/utf8":
+ send(utf8, "text/plain; charset=utf-8")
+ case "/json":
+ send(utf8, "application/json; charset=utf-8")
+ case "/plain":
+ send(iso88591, "text/plain")
+ case "/latin1":
+ send(iso88591, "text/plain; charset=latin1")
+ case "/iso88591":
+ send(iso88591, "text/plain; charset=iso-8859-1")
+ default:
+ w.WriteHeader(404)
+ }
+ }))
+}
diff --git a/vendor/github.com/magiconair/properties/parser.go b/vendor/github.com/magiconair/properties/parser.go
new file mode 100644
index 000000000..90f555cb9
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/parser.go
@@ -0,0 +1,95 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "fmt"
+ "runtime"
+)
+
+type parser struct {
+ lex *lexer
+}
+
+func parse(input string) (properties *Properties, err error) {
+ p := &parser{lex: lex(input)}
+ defer p.recover(&err)
+
+ properties = NewProperties()
+ key := ""
+ comments := []string{}
+
+ for {
+ token := p.expectOneOf(itemComment, itemKey, itemEOF)
+ switch token.typ {
+ case itemEOF:
+ goto done
+ case itemComment:
+ comments = append(comments, token.val)
+ continue
+ case itemKey:
+ key = token.val
+ if _, ok := properties.m[key]; !ok {
+ properties.k = append(properties.k, key)
+ }
+ }
+
+ token = p.expectOneOf(itemValue, itemEOF)
+ if len(comments) > 0 {
+ properties.c[key] = comments
+ comments = []string{}
+ }
+ switch token.typ {
+ case itemEOF:
+ properties.m[key] = ""
+ goto done
+ case itemValue:
+ properties.m[key] = token.val
+ }
+ }
+
+done:
+ return properties, nil
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+ format = fmt.Sprintf("properties: Line %d: %s", p.lex.lineNumber(), format)
+ panic(fmt.Errorf(format, args...))
+}
+
+func (p *parser) expect(expected itemType) (token item) {
+ token = p.lex.nextItem()
+ if token.typ != expected {
+ p.unexpected(token)
+ }
+ return token
+}
+
+func (p *parser) expectOneOf(expected ...itemType) (token item) {
+ token = p.lex.nextItem()
+ for _, v := range expected {
+ if token.typ == v {
+ return token
+ }
+ }
+ p.unexpected(token)
+ panic("unexpected token")
+}
+
+func (p *parser) unexpected(token item) {
+ p.errorf(token.String())
+}
+
+// recover is the handler that turns panics into returns from the top level of Parse.
+func (p *parser) recover(errp *error) {
+ e := recover()
+ if e != nil {
+ if _, ok := e.(runtime.Error); ok {
+ panic(e)
+ }
+ *errp = e.(error)
+ }
+ return
+}
diff --git a/vendor/github.com/magiconair/properties/properties.go b/vendor/github.com/magiconair/properties/properties.go
new file mode 100644
index 000000000..4f3d5a458
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/properties.go
@@ -0,0 +1,808 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+// BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer.
+// BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used.
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+)
+
+// ErrorHandlerFunc defines the type of function which handles failures
+// of the MustXXX() functions. An error handler function must exit
+// the application after handling the error.
+type ErrorHandlerFunc func(error)
+
+// ErrorHandler is the function which handles failures of the MustXXX()
+// functions. The default is LogFatalHandler.
+var ErrorHandler ErrorHandlerFunc = LogFatalHandler
+
+// LogHandlerFunc defines the function prototype for logging errors.
+type LogHandlerFunc func(fmt string, args ...interface{})
+
+// LogPrintf defines a log handler which uses log.Printf.
+var LogPrintf LogHandlerFunc = log.Printf
+
+// LogFatalHandler handles the error by logging a fatal error and exiting.
+func LogFatalHandler(err error) {
+ log.Fatal(err)
+}
+
+// PanicHandler handles the error by panicking.
+func PanicHandler(err error) {
+ panic(err)
+}
+
+// -----------------------------------------------------------------------------
+
+// A Properties contains the key/value pairs from the properties input.
+// All values are stored in unexpanded form and are expanded at runtime
+type Properties struct {
+ // Pre-/Postfix for property expansion.
+ Prefix string
+ Postfix string
+
+ // DisableExpansion controls the expansion of properties on Get()
+ // and the check for circular references on Set(). When set to
+ // true Properties behaves like a simple key/value store and does
+ // not check for circular references on Get() or on Set().
+ DisableExpansion bool
+
+ // Stores the key/value pairs
+ m map[string]string
+
+ // Stores the comments per key.
+ c map[string][]string
+
+ // Stores the keys in order of appearance.
+ k []string
+}
+
+// NewProperties creates a new Properties struct with the default
+// configuration for "${key}" expressions.
+func NewProperties() *Properties {
+ return &Properties{
+ Prefix: "${",
+ Postfix: "}",
+ m: map[string]string{},
+ c: map[string][]string{},
+ k: []string{},
+ }
+}
+
+// Get returns the expanded value for the given key if exists.
+// Otherwise, ok is false.
+func (p *Properties) Get(key string) (value string, ok bool) {
+ v, ok := p.m[key]
+ if p.DisableExpansion {
+ return v, ok
+ }
+ if !ok {
+ return "", false
+ }
+
+ expanded, err := p.expand(v)
+
+ // we guarantee that the expanded value is free of
+ // circular references and malformed expressions
+ // so we panic if we still get an error here.
+ if err != nil {
+ ErrorHandler(fmt.Errorf("%s in %q", err, key+" = "+v))
+ }
+
+ return expanded, true
+}
+
+// MustGet returns the expanded value for the given key if exists.
+// Otherwise, it panics.
+func (p *Properties) MustGet(key string) string {
+ if v, ok := p.Get(key); ok {
+ return v
+ }
+ ErrorHandler(invalidKeyError(key))
+ panic("ErrorHandler should exit")
+}
+
+// ----------------------------------------------------------------------------
+
+// ClearComments removes the comments for all keys.
+func (p *Properties) ClearComments() {
+ p.c = map[string][]string{}
+}
+
+// ----------------------------------------------------------------------------
+
+// GetComment returns the last comment before the given key or an empty string.
+func (p *Properties) GetComment(key string) string {
+ comments, ok := p.c[key]
+ if !ok || len(comments) == 0 {
+ return ""
+ }
+ return comments[len(comments)-1]
+}
+
+// ----------------------------------------------------------------------------
+
+// GetComments returns all comments that appeared before the given key or nil.
+func (p *Properties) GetComments(key string) []string {
+ if comments, ok := p.c[key]; ok {
+ return comments
+ }
+ return nil
+}
+
+// ----------------------------------------------------------------------------
+
+// SetComment sets the comment for the key.
+func (p *Properties) SetComment(key, comment string) {
+ p.c[key] = []string{comment}
+}
+
+// ----------------------------------------------------------------------------
+
+// SetComments sets the comments for the key. If the comments are nil then
+// all comments for this key are deleted.
+func (p *Properties) SetComments(key string, comments []string) {
+ if comments == nil {
+ delete(p.c, key)
+ return
+ }
+ p.c[key] = comments
+}
+
+// ----------------------------------------------------------------------------
+
+// GetBool checks if the expanded value is one of '1', 'yes',
+// 'true' or 'on' if the key exists. The comparison is case-insensitive.
+// If the key does not exist the default value is returned.
+func (p *Properties) GetBool(key string, def bool) bool {
+ v, err := p.getBool(key)
+ if err != nil {
+ return def
+ }
+ return v
+}
+
+// MustGetBool checks if the expanded value is one of '1', 'yes',
+// 'true' or 'on' if the key exists. The comparison is case-insensitive.
+// If the key does not exist the function panics.
+func (p *Properties) MustGetBool(key string) bool {
+ v, err := p.getBool(key)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return v
+}
+
+func (p *Properties) getBool(key string) (value bool, err error) {
+ if v, ok := p.Get(key); ok {
+ return boolVal(v), nil
+ }
+ return false, invalidKeyError(key)
+}
+
+func boolVal(v string) bool {
+ v = strings.ToLower(v)
+ return v == "1" || v == "true" || v == "yes" || v == "on"
+}
+
+// ----------------------------------------------------------------------------
+
+// GetDuration parses the expanded value as an time.Duration (in ns) if the
+// key exists. If key does not exist or the value cannot be parsed the default
+// value is returned. In almost all cases you want to use GetParsedDuration().
+func (p *Properties) GetDuration(key string, def time.Duration) time.Duration {
+ v, err := p.getInt64(key)
+ if err != nil {
+ return def
+ }
+ return time.Duration(v)
+}
+
+// MustGetDuration parses the expanded value as an time.Duration (in ns) if
+// the key exists. If key does not exist or the value cannot be parsed the
+// function panics. In almost all cases you want to use MustGetParsedDuration().
+func (p *Properties) MustGetDuration(key string) time.Duration {
+ v, err := p.getInt64(key)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return time.Duration(v)
+}
+
+// ----------------------------------------------------------------------------
+
+// GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
+// If key does not exist or the value cannot be parsed the default
+// value is returned.
+func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration {
+ s, ok := p.Get(key)
+ if !ok {
+ return def
+ }
+ v, err := time.ParseDuration(s)
+ if err != nil {
+ return def
+ }
+ return v
+}
+
+// MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists.
+// If key does not exist or the value cannot be parsed the function panics.
+func (p *Properties) MustGetParsedDuration(key string) time.Duration {
+ s, ok := p.Get(key)
+ if !ok {
+ ErrorHandler(invalidKeyError(key))
+ }
+ v, err := time.ParseDuration(s)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return v
+}
+
+// ----------------------------------------------------------------------------
+
+// GetFloat64 parses the expanded value as a float64 if the key exists.
+// If key does not exist or the value cannot be parsed the default
+// value is returned.
+func (p *Properties) GetFloat64(key string, def float64) float64 {
+ v, err := p.getFloat64(key)
+ if err != nil {
+ return def
+ }
+ return v
+}
+
+// MustGetFloat64 parses the expanded value as a float64 if the key exists.
+// If key does not exist or the value cannot be parsed the function panics.
+func (p *Properties) MustGetFloat64(key string) float64 {
+ v, err := p.getFloat64(key)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return v
+}
+
+func (p *Properties) getFloat64(key string) (value float64, err error) {
+ if v, ok := p.Get(key); ok {
+ value, err = strconv.ParseFloat(v, 64)
+ if err != nil {
+ return 0, err
+ }
+ return value, nil
+ }
+ return 0, invalidKeyError(key)
+}
+
+// ----------------------------------------------------------------------------
+
+// GetInt parses the expanded value as an int if the key exists.
+// If key does not exist or the value cannot be parsed the default
+// value is returned. If the value does not fit into an int the
+// function panics with an out of range error.
+func (p *Properties) GetInt(key string, def int) int {
+ v, err := p.getInt64(key)
+ if err != nil {
+ return def
+ }
+ return intRangeCheck(key, v)
+}
+
+// MustGetInt parses the expanded value as an int if the key exists.
+// If key does not exist or the value cannot be parsed the function panics.
+// If the value does not fit into an int the function panics with
+// an out of range error.
+func (p *Properties) MustGetInt(key string) int {
+ v, err := p.getInt64(key)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return intRangeCheck(key, v)
+}
+
+// ----------------------------------------------------------------------------
+
+// GetInt64 parses the expanded value as an int64 if the key exists.
+// If key does not exist or the value cannot be parsed the default
+// value is returned.
+func (p *Properties) GetInt64(key string, def int64) int64 {
+ v, err := p.getInt64(key)
+ if err != nil {
+ return def
+ }
+ return v
+}
+
+// MustGetInt64 parses the expanded value as an int if the key exists.
+// If key does not exist or the value cannot be parsed the function panics.
+func (p *Properties) MustGetInt64(key string) int64 {
+ v, err := p.getInt64(key)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return v
+}
+
+func (p *Properties) getInt64(key string) (value int64, err error) {
+ if v, ok := p.Get(key); ok {
+ value, err = strconv.ParseInt(v, 10, 64)
+ if err != nil {
+ return 0, err
+ }
+ return value, nil
+ }
+ return 0, invalidKeyError(key)
+}
+
+// ----------------------------------------------------------------------------
+
+// GetUint parses the expanded value as an uint if the key exists.
+// If key does not exist or the value cannot be parsed the default
+// value is returned. If the value does not fit into an int the
+// function panics with an out of range error.
+func (p *Properties) GetUint(key string, def uint) uint {
+ v, err := p.getUint64(key)
+ if err != nil {
+ return def
+ }
+ return uintRangeCheck(key, v)
+}
+
+// MustGetUint parses the expanded value as an int if the key exists.
+// If key does not exist or the value cannot be parsed the function panics.
+// If the value does not fit into an int the function panics with
+// an out of range error.
+func (p *Properties) MustGetUint(key string) uint {
+ v, err := p.getUint64(key)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return uintRangeCheck(key, v)
+}
+
+// ----------------------------------------------------------------------------
+
+// GetUint64 parses the expanded value as an uint64 if the key exists.
+// If key does not exist or the value cannot be parsed the default
+// value is returned.
+func (p *Properties) GetUint64(key string, def uint64) uint64 {
+ v, err := p.getUint64(key)
+ if err != nil {
+ return def
+ }
+ return v
+}
+
+// MustGetUint64 parses the expanded value as an int if the key exists.
+// If key does not exist or the value cannot be parsed the function panics.
+func (p *Properties) MustGetUint64(key string) uint64 {
+ v, err := p.getUint64(key)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return v
+}
+
+func (p *Properties) getUint64(key string) (value uint64, err error) {
+ if v, ok := p.Get(key); ok {
+ value, err = strconv.ParseUint(v, 10, 64)
+ if err != nil {
+ return 0, err
+ }
+ return value, nil
+ }
+ return 0, invalidKeyError(key)
+}
+
+// ----------------------------------------------------------------------------
+
+// GetString returns the expanded value for the given key if exists or
+// the default value otherwise.
+func (p *Properties) GetString(key, def string) string {
+ if v, ok := p.Get(key); ok {
+ return v
+ }
+ return def
+}
+
+// MustGetString returns the expanded value for the given key if exists or
+// panics otherwise.
+func (p *Properties) MustGetString(key string) string {
+ if v, ok := p.Get(key); ok {
+ return v
+ }
+ ErrorHandler(invalidKeyError(key))
+ panic("ErrorHandler should exit")
+}
+
+// ----------------------------------------------------------------------------
+
+// Filter returns a new properties object which contains all properties
+// for which the key matches the pattern.
+func (p *Properties) Filter(pattern string) (*Properties, error) {
+ re, err := regexp.Compile(pattern)
+ if err != nil {
+ return nil, err
+ }
+
+ return p.FilterRegexp(re), nil
+}
+
+// FilterRegexp returns a new properties object which contains all properties
+// for which the key matches the regular expression.
+func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties {
+ pp := NewProperties()
+ for _, k := range p.k {
+ if re.MatchString(k) {
+ // TODO(fs): we are ignoring the error which flags a circular reference.
+ // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
+ pp.Set(k, p.m[k])
+ }
+ }
+ return pp
+}
+
+// FilterPrefix returns a new properties object with a subset of all keys
+// with the given prefix.
+func (p *Properties) FilterPrefix(prefix string) *Properties {
+ pp := NewProperties()
+ for _, k := range p.k {
+ if strings.HasPrefix(k, prefix) {
+ // TODO(fs): we are ignoring the error which flags a circular reference.
+ // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
+ pp.Set(k, p.m[k])
+ }
+ }
+ return pp
+}
+
+// FilterStripPrefix returns a new properties object with a subset of all keys
+// with the given prefix and the prefix removed from the keys.
+func (p *Properties) FilterStripPrefix(prefix string) *Properties {
+ pp := NewProperties()
+ n := len(prefix)
+ for _, k := range p.k {
+ if len(k) > len(prefix) && strings.HasPrefix(k, prefix) {
+ // TODO(fs): we are ignoring the error which flags a circular reference.
+ // TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference
+ // TODO(fs): this function should probably return an error but the signature is fixed
+ pp.Set(k[n:], p.m[k])
+ }
+ }
+ return pp
+}
+
+// Len returns the number of keys.
+func (p *Properties) Len() int {
+ return len(p.m)
+}
+
+// Keys returns all keys in the same order as in the input.
+func (p *Properties) Keys() []string {
+ keys := make([]string, len(p.k))
+ copy(keys, p.k)
+ return keys
+}
+
+// Set sets the property key to the corresponding value.
+// If a value for key existed before then ok is true and prev
+// contains the previous value. If the value contains a
+// circular reference or a malformed expression then
+// an error is returned.
+// An empty key is silently ignored.
+func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
+ if key == "" {
+ return "", false, nil
+ }
+
+ // if expansion is disabled we allow circular references
+ if p.DisableExpansion {
+ prev, ok = p.Get(key)
+ p.m[key] = value
+ return prev, ok, nil
+ }
+
+ // to check for a circular reference we temporarily need
+ // to set the new value. If there is an error then revert
+ // to the previous state. Only if all tests are successful
+ // then we add the key to the p.k list.
+ prev, ok = p.Get(key)
+ p.m[key] = value
+
+ // now check for a circular reference
+ _, err = p.expand(value)
+ if err != nil {
+
+ // revert to the previous state
+ if ok {
+ p.m[key] = prev
+ } else {
+ delete(p.m, key)
+ }
+
+ return "", false, err
+ }
+
+ if !ok {
+ p.k = append(p.k, key)
+ }
+
+ return prev, ok, nil
+}
+
+// SetValue sets property key to the default string value
+// as defined by fmt.Sprintf("%v").
+func (p *Properties) SetValue(key string, value interface{}) error {
+ _, _, err := p.Set(key, fmt.Sprintf("%v", value))
+ return err
+}
+
+// MustSet sets the property key to the corresponding value.
+// If a value for key existed before then ok is true and prev
+// contains the previous value. An empty key is silently ignored.
+func (p *Properties) MustSet(key, value string) (prev string, ok bool) {
+ prev, ok, err := p.Set(key, value)
+ if err != nil {
+ ErrorHandler(err)
+ }
+ return prev, ok
+}
+
+// String returns a string of all expanded 'key = value' pairs.
+func (p *Properties) String() string {
+ var s string
+ for _, key := range p.k {
+ value, _ := p.Get(key)
+ s = fmt.Sprintf("%s%s = %s\n", s, key, value)
+ }
+ return s
+}
+
+// Write writes all unexpanded 'key = value' pairs to the given writer.
+// Write returns the number of bytes written and any write error encountered.
+func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) {
+ return p.WriteComment(w, "", enc)
+}
+
+// WriteComment writes all unexpanced 'key = value' pairs to the given writer.
+// If prefix is not empty then comments are written with a blank line and the
+// given prefix. The prefix should be either "# " or "! " to be compatible with
+// the properties file format. Otherwise, the properties parser will not be
+// able to read the file back in. It returns the number of bytes written and
+// any write error encountered.
+func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) {
+ var x int
+
+ for _, key := range p.k {
+ value := p.m[key]
+
+ if prefix != "" {
+ if comments, ok := p.c[key]; ok {
+ // don't print comments if they are all empty
+ allEmpty := true
+ for _, c := range comments {
+ if c != "" {
+ allEmpty = false
+ break
+ }
+ }
+
+ if !allEmpty {
+ // add a blank line between entries but not at the top
+ if len(comments) > 0 && n > 0 {
+ x, err = fmt.Fprintln(w)
+ if err != nil {
+ return
+ }
+ n += x
+ }
+
+ for _, c := range comments {
+ x, err = fmt.Fprintf(w, "%s%s\n", prefix, encode(c, "", enc))
+ if err != nil {
+ return
+ }
+ n += x
+ }
+ }
+ }
+ }
+
+ x, err = fmt.Fprintf(w, "%s = %s\n", encode(key, " :", enc), encode(value, "", enc))
+ if err != nil {
+ return
+ }
+ n += x
+ }
+ return
+}
+
+// Map returns a copy of the properties as a map.
+func (p *Properties) Map() map[string]string {
+ m := make(map[string]string)
+ for k, v := range p.m {
+ m[k] = v
+ }
+ return m
+}
+
+// FilterFunc returns a copy of the properties which includes the values which passed all filters.
+func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties {
+ pp := NewProperties()
+outer:
+ for k, v := range p.m {
+ for _, f := range filters {
+ if !f(k, v) {
+ continue outer
+ }
+ pp.Set(k, v)
+ }
+ }
+ return pp
+}
+
+// ----------------------------------------------------------------------------
+
+// Delete removes the key and its comments.
+func (p *Properties) Delete(key string) {
+ delete(p.m, key)
+ delete(p.c, key)
+ newKeys := []string{}
+ for _, k := range p.k {
+ if k != key {
+ newKeys = append(newKeys, k)
+ }
+ }
+ p.k = newKeys
+}
+
+// Merge merges properties, comments and keys from other *Properties into p
+func (p *Properties) Merge(other *Properties) {
+ for k, v := range other.m {
+ p.m[k] = v
+ }
+ for k, v := range other.c {
+ p.c[k] = v
+ }
+
+outer:
+ for _, otherKey := range other.k {
+ for _, key := range p.k {
+ if otherKey == key {
+ continue outer
+ }
+ }
+ p.k = append(p.k, otherKey)
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+// check expands all values and returns an error if a circular reference or
+// a malformed expression was found.
+func (p *Properties) check() error {
+ for _, value := range p.m {
+ if _, err := p.expand(value); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (p *Properties) expand(input string) (string, error) {
+ // no pre/postfix -> nothing to expand
+ if p.Prefix == "" && p.Postfix == "" {
+ return input, nil
+ }
+
+ return expand(input, make(map[string]bool), p.Prefix, p.Postfix, p.m)
+}
+
+// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
+// The function keeps track of the keys that were already expanded and stops if it
+// detects a circular reference or a malformed expression of the form '(prefix)key'.
+func expand(s string, keys map[string]bool, prefix, postfix string, values map[string]string) (string, error) {
+ start := strings.Index(s, prefix)
+ if start == -1 {
+ return s, nil
+ }
+
+ keyStart := start + len(prefix)
+ keyLen := strings.Index(s[keyStart:], postfix)
+ if keyLen == -1 {
+ return "", fmt.Errorf("malformed expression")
+ }
+
+ end := keyStart + keyLen + len(postfix) - 1
+ key := s[keyStart : keyStart+keyLen]
+
+ // fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key)
+
+ if _, ok := keys[key]; ok {
+ return "", fmt.Errorf("circular reference")
+ }
+
+ val, ok := values[key]
+ if !ok {
+ val = os.Getenv(key)
+ }
+
+ // remember that we've seen the key
+ keys[key] = true
+
+ return expand(s[:start]+val+s[end+1:], keys, prefix, postfix, values)
+}
+
+// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
+func encode(s string, special string, enc Encoding) string {
+ switch enc {
+ case UTF8:
+ return encodeUtf8(s, special)
+ case ISO_8859_1:
+ return encodeIso(s, special)
+ default:
+ panic(fmt.Sprintf("unsupported encoding %v", enc))
+ }
+}
+
+func encodeUtf8(s string, special string) string {
+ v := ""
+ for pos := 0; pos < len(s); {
+ r, w := utf8.DecodeRuneInString(s[pos:])
+ pos += w
+ v += escape(r, special)
+ }
+ return v
+}
+
+func encodeIso(s string, special string) string {
+ var r rune
+ var w int
+ var v string
+ for pos := 0; pos < len(s); {
+ switch r, w = utf8.DecodeRuneInString(s[pos:]); {
+ case r < 1<<8: // single byte rune -> escape special chars only
+ v += escape(r, special)
+ case r < 1<<16: // two byte rune -> unicode literal
+ v += fmt.Sprintf("\\u%04x", r)
+ default: // more than two bytes per rune -> can't encode
+ v += "?"
+ }
+ pos += w
+ }
+ return v
+}
+
+func escape(r rune, special string) string {
+ switch r {
+ case '\f':
+ return "\\f"
+ case '\n':
+ return "\\n"
+ case '\r':
+ return "\\r"
+ case '\t':
+ return "\\t"
+ default:
+ if strings.ContainsRune(special, r) {
+ return "\\" + string(r)
+ }
+ return string(r)
+ }
+}
+
+func invalidKeyError(key string) error {
+ return fmt.Errorf("unknown property: %s", key)
+}
diff --git a/vendor/github.com/magiconair/properties/properties_test.go b/vendor/github.com/magiconair/properties/properties_test.go
new file mode 100644
index 000000000..0eac1f492
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/properties_test.go
@@ -0,0 +1,934 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "math"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/magiconair/properties/assert"
+)
+
+var verbose = flag.Bool("verbose", false, "Verbose output")
+
+func init() {
+ ErrorHandler = PanicHandler
+}
+
+// ----------------------------------------------------------------------------
+
+// define test cases in the form of
+// {"input", "key1", "value1", "key2", "value2", ...}
+var complexTests = [][]string{
+ // whitespace prefix
+ {" key=value", "key", "value"}, // SPACE prefix
+ {"\fkey=value", "key", "value"}, // FF prefix
+ {"\tkey=value", "key", "value"}, // TAB prefix
+ {" \f\tkey=value", "key", "value"}, // mix prefix
+
+ // multiple keys
+ {"key1=value1\nkey2=value2\n", "key1", "value1", "key2", "value2"},
+ {"key1=value1\rkey2=value2\r", "key1", "value1", "key2", "value2"},
+ {"key1=value1\r\nkey2=value2\r\n", "key1", "value1", "key2", "value2"},
+
+ // blank lines
+ {"\nkey=value\n", "key", "value"},
+ {"\rkey=value\r", "key", "value"},
+ {"\r\nkey=value\r\n", "key", "value"},
+
+ // escaped chars in key
+ {"k\\ ey = value", "k ey", "value"},
+ {"k\\:ey = value", "k:ey", "value"},
+ {"k\\=ey = value", "k=ey", "value"},
+ {"k\\fey = value", "k\fey", "value"},
+ {"k\\ney = value", "k\ney", "value"},
+ {"k\\rey = value", "k\rey", "value"},
+ {"k\\tey = value", "k\tey", "value"},
+
+ // escaped chars in value
+ {"key = v\\ alue", "key", "v alue"},
+ {"key = v\\:alue", "key", "v:alue"},
+ {"key = v\\=alue", "key", "v=alue"},
+ {"key = v\\falue", "key", "v\falue"},
+ {"key = v\\nalue", "key", "v\nalue"},
+ {"key = v\\ralue", "key", "v\ralue"},
+ {"key = v\\talue", "key", "v\talue"},
+
+ // silently dropped escape character
+ {"k\\zey = value", "kzey", "value"},
+ {"key = v\\zalue", "key", "vzalue"},
+
+ // unicode literals
+ {"key\\u2318 = value", "key⌘", "value"},
+ {"k\\u2318ey = value", "k⌘ey", "value"},
+ {"key = value\\u2318", "key", "value⌘"},
+ {"key = valu\\u2318e", "key", "valu⌘e"},
+
+ // multiline values
+ {"key = valueA,\\\n valueB", "key", "valueA,valueB"}, // SPACE indent
+ {"key = valueA,\\\n\f\f\fvalueB", "key", "valueA,valueB"}, // FF indent
+ {"key = valueA,\\\n\t\t\tvalueB", "key", "valueA,valueB"}, // TAB indent
+ {"key = valueA,\\\n \f\tvalueB", "key", "valueA,valueB"}, // mix indent
+
+ // comments
+ {"# this is a comment\n! and so is this\nkey1=value1\nkey#2=value#2\n\nkey!3=value!3\n# and another one\n! and the final one", "key1", "value1", "key#2", "value#2", "key!3", "value!3"},
+
+ // expansion tests
+ {"key=value\nkey2=${key}", "key", "value", "key2", "value"},
+ {"key=value\nkey2=aa${key}", "key", "value", "key2", "aavalue"},
+ {"key=value\nkey2=${key}bb", "key", "value", "key2", "valuebb"},
+ {"key=value\nkey2=aa${key}bb", "key", "value", "key2", "aavaluebb"},
+ {"key=value\nkey2=${key}\nkey3=${key2}", "key", "value", "key2", "value", "key3", "value"},
+ {"key=${USER}", "key", os.Getenv("USER")},
+ {"key=${USER}\nUSER=value", "key", "value", "USER", "value"},
+}
+
+// ----------------------------------------------------------------------------
+
+var commentTests = []struct {
+ input, key, value string
+ comments []string
+}{
+ {"key=value", "key", "value", nil},
+ {"#\nkey=value", "key", "value", []string{""}},
+ {"#comment\nkey=value", "key", "value", []string{"comment"}},
+ {"# comment\nkey=value", "key", "value", []string{"comment"}},
+ {"# comment\nkey=value", "key", "value", []string{"comment"}},
+ {"# comment\n\nkey=value", "key", "value", []string{"comment"}},
+ {"# comment1\n# comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+ {"# comment1\n\n# comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+ {"!comment\nkey=value", "key", "value", []string{"comment"}},
+ {"! comment\nkey=value", "key", "value", []string{"comment"}},
+ {"! comment\nkey=value", "key", "value", []string{"comment"}},
+ {"! comment\n\nkey=value", "key", "value", []string{"comment"}},
+ {"! comment1\n! comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+ {"! comment1\n\n! comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+}
+
+// ----------------------------------------------------------------------------
+
+var errorTests = []struct {
+ input, msg string
+}{
+ // unicode literals
+ {"key\\u1 = value", "invalid unicode literal"},
+ {"key\\u12 = value", "invalid unicode literal"},
+ {"key\\u123 = value", "invalid unicode literal"},
+ {"key\\u123g = value", "invalid unicode literal"},
+ {"key\\u123", "invalid unicode literal"},
+
+ // circular references
+ {"key=${key}", "circular reference"},
+ {"key1=${key2}\nkey2=${key1}", "circular reference"},
+
+ // malformed expressions
+ {"key=${ke", "malformed expression"},
+ {"key=valu${ke", "malformed expression"},
+}
+
+// ----------------------------------------------------------------------------
+
+var writeTests = []struct {
+ input, output, encoding string
+}{
+ // ISO-8859-1 tests
+ {"key = value", "key = value\n", "ISO-8859-1"},
+ {"key = value \\\n continued", "key = value continued\n", "ISO-8859-1"},
+ {"key⌘ = value", "key\\u2318 = value\n", "ISO-8859-1"},
+ {"ke\\ \\:y = value", "ke\\ \\:y = value\n", "ISO-8859-1"},
+
+ // UTF-8 tests
+ {"key = value", "key = value\n", "UTF-8"},
+ {"key = value \\\n continued", "key = value continued\n", "UTF-8"},
+ {"key⌘ = value⌘", "key⌘ = value⌘\n", "UTF-8"},
+ {"ke\\ \\:y = value", "ke\\ \\:y = value\n", "UTF-8"},
+}
+
+// ----------------------------------------------------------------------------
+
+var writeCommentTests = []struct {
+ input, output, encoding string
+}{
+ // ISO-8859-1 tests
+ {"key = value", "key = value\n", "ISO-8859-1"},
+ {"#\nkey = value", "key = value\n", "ISO-8859-1"},
+ {"#\n#\n#\nkey = value", "key = value\n", "ISO-8859-1"},
+ {"# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
+ {"\n# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
+ {"# comment\n\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
+ {"# comment1\n# comment2\nkey = value", "# comment1\n# comment2\nkey = value\n", "ISO-8859-1"},
+ {"#comment1\nkey1 = value1\n#comment2\nkey2 = value2", "# comment1\nkey1 = value1\n\n# comment2\nkey2 = value2\n", "ISO-8859-1"},
+
+ // UTF-8 tests
+ {"key = value", "key = value\n", "UTF-8"},
+ {"# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
+ {"\n# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
+ {"# comment⌘\n\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
+ {"# comment1⌘\n# comment2⌘\nkey = value⌘", "# comment1⌘\n# comment2⌘\nkey = value⌘\n", "UTF-8"},
+ {"#comment1⌘\nkey1 = value1⌘\n#comment2⌘\nkey2 = value2⌘", "# comment1⌘\nkey1 = value1⌘\n\n# comment2⌘\nkey2 = value2⌘\n", "UTF-8"},
+}
+
+// ----------------------------------------------------------------------------
+
+var boolTests = []struct {
+ input, key string
+ def, value bool
+}{
+ // valid values for TRUE
+ {"key = 1", "key", false, true},
+ {"key = on", "key", false, true},
+ {"key = On", "key", false, true},
+ {"key = ON", "key", false, true},
+ {"key = true", "key", false, true},
+ {"key = True", "key", false, true},
+ {"key = TRUE", "key", false, true},
+ {"key = yes", "key", false, true},
+ {"key = Yes", "key", false, true},
+ {"key = YES", "key", false, true},
+
+ // valid values for FALSE (all other)
+ {"key = 0", "key", true, false},
+ {"key = off", "key", true, false},
+ {"key = false", "key", true, false},
+ {"key = no", "key", true, false},
+
+ // non existent key
+ {"key = true", "key2", false, false},
+}
+
+// ----------------------------------------------------------------------------
+
+var durationTests = []struct {
+ input, key string
+ def, value time.Duration
+}{
+ // valid values
+ {"key = 1", "key", 999, 1},
+ {"key = 0", "key", 999, 0},
+ {"key = -1", "key", 999, -1},
+ {"key = 0123", "key", 999, 123},
+
+ // invalid values
+ {"key = 0xff", "key", 999, 999},
+ {"key = 1.0", "key", 999, 999},
+ {"key = a", "key", 999, 999},
+
+ // non existent key
+ {"key = 1", "key2", 999, 999},
+}
+
+// ----------------------------------------------------------------------------
+
+var parsedDurationTests = []struct {
+ input, key string
+ def, value time.Duration
+}{
+ // valid values
+ {"key = -1ns", "key", 999, -1 * time.Nanosecond},
+ {"key = 300ms", "key", 999, 300 * time.Millisecond},
+ {"key = 5s", "key", 999, 5 * time.Second},
+ {"key = 3h", "key", 999, 3 * time.Hour},
+ {"key = 2h45m", "key", 999, 2*time.Hour + 45*time.Minute},
+
+ // invalid values
+ {"key = 0xff", "key", 999, 999},
+ {"key = 1.0", "key", 999, 999},
+ {"key = a", "key", 999, 999},
+ {"key = 1", "key", 999, 999},
+ {"key = 0", "key", 999, 0},
+
+ // non existent key
+ {"key = 1", "key2", 999, 999},
+}
+
+// ----------------------------------------------------------------------------
+
+var floatTests = []struct {
+ input, key string
+ def, value float64
+}{
+ // valid values
+ {"key = 1.0", "key", 999, 1.0},
+ {"key = 0.0", "key", 999, 0.0},
+ {"key = -1.0", "key", 999, -1.0},
+ {"key = 1", "key", 999, 1},
+ {"key = 0", "key", 999, 0},
+ {"key = -1", "key", 999, -1},
+ {"key = 0123", "key", 999, 123},
+
+ // invalid values
+ {"key = 0xff", "key", 999, 999},
+ {"key = a", "key", 999, 999},
+
+ // non existent key
+ {"key = 1", "key2", 999, 999},
+}
+
+// ----------------------------------------------------------------------------
+
+var int64Tests = []struct {
+ input, key string
+ def, value int64
+}{
+ // valid values
+ {"key = 1", "key", 999, 1},
+ {"key = 0", "key", 999, 0},
+ {"key = -1", "key", 999, -1},
+ {"key = 0123", "key", 999, 123},
+
+ // invalid values
+ {"key = 0xff", "key", 999, 999},
+ {"key = 1.0", "key", 999, 999},
+ {"key = a", "key", 999, 999},
+
+ // non existent key
+ {"key = 1", "key2", 999, 999},
+}
+
+// ----------------------------------------------------------------------------
+
+var uint64Tests = []struct {
+ input, key string
+ def, value uint64
+}{
+ // valid values
+ {"key = 1", "key", 999, 1},
+ {"key = 0", "key", 999, 0},
+ {"key = 0123", "key", 999, 123},
+
+ // invalid values
+ {"key = -1", "key", 999, 999},
+ {"key = 0xff", "key", 999, 999},
+ {"key = 1.0", "key", 999, 999},
+ {"key = a", "key", 999, 999},
+
+ // non existent key
+ {"key = 1", "key2", 999, 999},
+}
+
+// ----------------------------------------------------------------------------
+
+var stringTests = []struct {
+ input, key string
+ def, value string
+}{
+ // valid values
+ {"key = abc", "key", "def", "abc"},
+
+ // non existent key
+ {"key = abc", "key2", "def", "def"},
+}
+
+// ----------------------------------------------------------------------------
+
+var keysTests = []struct {
+ input string
+ keys []string
+}{
+ {"", []string{}},
+ {"key = abc", []string{"key"}},
+ {"key = abc\nkey2=def", []string{"key", "key2"}},
+ {"key2 = abc\nkey=def", []string{"key2", "key"}},
+ {"key = abc\nkey=def", []string{"key"}},
+}
+
+// ----------------------------------------------------------------------------
+
+var filterTests = []struct {
+ input string
+ pattern string
+ keys []string
+ err string
+}{
+ {"", "", []string{}, ""},
+ {"", "abc", []string{}, ""},
+ {"key=value", "", []string{"key"}, ""},
+ {"key=value", "key=", []string{}, ""},
+ {"key=value\nfoo=bar", "", []string{"foo", "key"}, ""},
+ {"key=value\nfoo=bar", "f", []string{"foo"}, ""},
+ {"key=value\nfoo=bar", "fo", []string{"foo"}, ""},
+ {"key=value\nfoo=bar", "foo", []string{"foo"}, ""},
+ {"key=value\nfoo=bar", "fooo", []string{}, ""},
+ {"key=value\nkey2=value2\nfoo=bar", "ey", []string{"key", "key2"}, ""},
+ {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}, ""},
+ {"key=value\nkey2=value2\nfoo=bar", "^key", []string{"key", "key2"}, ""},
+ {"key=value\nkey2=value2\nfoo=bar", "^(key|foo)", []string{"foo", "key", "key2"}, ""},
+ {"key=value\nkey2=value2\nfoo=bar", "[ abc", nil, "error parsing regexp.*"},
+}
+
+// ----------------------------------------------------------------------------
+
+var filterPrefixTests = []struct {
+ input string
+ prefix string
+ keys []string
+}{
+ {"", "", []string{}},
+ {"", "abc", []string{}},
+ {"key=value", "", []string{"key"}},
+ {"key=value", "key=", []string{}},
+ {"key=value\nfoo=bar", "", []string{"foo", "key"}},
+ {"key=value\nfoo=bar", "f", []string{"foo"}},
+ {"key=value\nfoo=bar", "fo", []string{"foo"}},
+ {"key=value\nfoo=bar", "foo", []string{"foo"}},
+ {"key=value\nfoo=bar", "fooo", []string{}},
+ {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
+}
+
+// ----------------------------------------------------------------------------
+
+var filterStripPrefixTests = []struct {
+ input string
+ prefix string
+ keys []string
+}{
+ {"", "", []string{}},
+ {"", "abc", []string{}},
+ {"key=value", "", []string{"key"}},
+ {"key=value", "key=", []string{}},
+ {"key=value\nfoo=bar", "", []string{"foo", "key"}},
+ {"key=value\nfoo=bar", "f", []string{"foo"}},
+ {"key=value\nfoo=bar", "fo", []string{"foo"}},
+ {"key=value\nfoo=bar", "foo", []string{"foo"}},
+ {"key=value\nfoo=bar", "fooo", []string{}},
+ {"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
+}
+
+// ----------------------------------------------------------------------------
+
+var setTests = []struct {
+ input string
+ key, value string
+ prev string
+ ok bool
+ err string
+ keys []string
+}{
+ {"", "", "", "", false, "", []string{}},
+ {"", "key", "value", "", false, "", []string{"key"}},
+ {"key=value", "key2", "value2", "", false, "", []string{"key", "key2"}},
+ {"key=value", "abc", "value3", "", false, "", []string{"key", "abc"}},
+ {"key=value", "key", "value3", "value", true, "", []string{"key"}},
+}
+
+// ----------------------------------------------------------------------------
+
+// TestBasic tests basic single key/value combinations with all possible
+// whitespace, delimiter and newline permutations.
+func TestBasic(t *testing.T) {
+ testWhitespaceAndDelimiterCombinations(t, "key", "")
+ testWhitespaceAndDelimiterCombinations(t, "key", "value")
+ testWhitespaceAndDelimiterCombinations(t, "key", "value ")
+}
+
+func TestComplex(t *testing.T) {
+ for _, test := range complexTests {
+ testKeyValue(t, test[0], test[1:]...)
+ }
+}
+
+func TestErrors(t *testing.T) {
+ for _, test := range errorTests {
+ _, err := Load([]byte(test.input), ISO_8859_1)
+ assert.Equal(t, err != nil, true, "want error")
+ assert.Equal(t, strings.Contains(err.Error(), test.msg), true)
+ }
+}
+
+func TestDisableExpansion(t *testing.T) {
+ input := "key=value\nkey2=${key}"
+ p := mustParse(t, input)
+ p.DisableExpansion = true
+ assert.Equal(t, p.MustGet("key"), "value")
+ assert.Equal(t, p.MustGet("key2"), "${key}")
+
+ // with expansion disabled we can introduce circular references
+ p.MustSet("keyA", "${keyB}")
+ p.MustSet("keyB", "${keyA}")
+ assert.Equal(t, p.MustGet("keyA"), "${keyB}")
+ assert.Equal(t, p.MustGet("keyB"), "${keyA}")
+}
+
+func TestMustGet(t *testing.T) {
+ input := "key = value\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGet("key"), "value")
+ assert.Panic(t, func() { p.MustGet("invalid") }, "unknown property: invalid")
+}
+
+func TestGetBool(t *testing.T) {
+ for _, test := range boolTests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetBool(test.key, test.def), test.value)
+ }
+}
+
+func TestMustGetBool(t *testing.T) {
+ input := "key = true\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetBool("key"), true)
+ assert.Panic(t, func() { p.MustGetBool("invalid") }, "unknown property: invalid")
+}
+
+func TestGetDuration(t *testing.T) {
+ for _, test := range durationTests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetDuration(test.key, test.def), test.value)
+ }
+}
+
+func TestMustGetDuration(t *testing.T) {
+ input := "key = 123\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetDuration("key"), time.Duration(123))
+ assert.Panic(t, func() { p.MustGetDuration("key2") }, "strconv.ParseInt: parsing.*")
+ assert.Panic(t, func() { p.MustGetDuration("invalid") }, "unknown property: invalid")
+}
+
+func TestGetParsedDuration(t *testing.T) {
+ for _, test := range parsedDurationTests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetParsedDuration(test.key, test.def), test.value)
+ }
+}
+
+func TestMustGetParsedDuration(t *testing.T) {
+ input := "key = 123ms\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetParsedDuration("key"), 123*time.Millisecond)
+ assert.Panic(t, func() { p.MustGetParsedDuration("key2") }, "time: invalid duration ghi")
+ assert.Panic(t, func() { p.MustGetParsedDuration("invalid") }, "unknown property: invalid")
+}
+
+func TestGetFloat64(t *testing.T) {
+ for _, test := range floatTests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetFloat64(test.key, test.def), test.value)
+ }
+}
+
+func TestMustGetFloat64(t *testing.T) {
+ input := "key = 123\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetFloat64("key"), float64(123))
+ assert.Panic(t, func() { p.MustGetFloat64("key2") }, "strconv.ParseFloat: parsing.*")
+ assert.Panic(t, func() { p.MustGetFloat64("invalid") }, "unknown property: invalid")
+}
+
+func TestGetInt(t *testing.T) {
+ for _, test := range int64Tests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetInt(test.key, int(test.def)), int(test.value))
+ }
+}
+
+func TestMustGetInt(t *testing.T) {
+ input := "key = 123\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetInt("key"), int(123))
+ assert.Panic(t, func() { p.MustGetInt("key2") }, "strconv.ParseInt: parsing.*")
+ assert.Panic(t, func() { p.MustGetInt("invalid") }, "unknown property: invalid")
+}
+
+func TestGetInt64(t *testing.T) {
+ for _, test := range int64Tests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetInt64(test.key, test.def), test.value)
+ }
+}
+
+func TestMustGetInt64(t *testing.T) {
+ input := "key = 123\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetInt64("key"), int64(123))
+ assert.Panic(t, func() { p.MustGetInt64("key2") }, "strconv.ParseInt: parsing.*")
+ assert.Panic(t, func() { p.MustGetInt64("invalid") }, "unknown property: invalid")
+}
+
+func TestGetUint(t *testing.T) {
+ for _, test := range uint64Tests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetUint(test.key, uint(test.def)), uint(test.value))
+ }
+}
+
+func TestMustGetUint(t *testing.T) {
+ input := "key = 123\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetUint("key"), uint(123))
+ assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*")
+ assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid")
+}
+
+func TestGetUint64(t *testing.T) {
+ for _, test := range uint64Tests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetUint64(test.key, test.def), test.value)
+ }
+}
+
+func TestMustGetUint64(t *testing.T) {
+ input := "key = 123\nkey2 = ghi"
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetUint64("key"), uint64(123))
+ assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*")
+ assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid")
+}
+
+func TestGetString(t *testing.T) {
+ for _, test := range stringTests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), 1)
+ assert.Equal(t, p.GetString(test.key, test.def), test.value)
+ }
+}
+
+func TestMustGetString(t *testing.T) {
+ input := `key = value`
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetString("key"), "value")
+ assert.Panic(t, func() { p.MustGetString("invalid") }, "unknown property: invalid")
+}
+
+func TestComment(t *testing.T) {
+ for _, test := range commentTests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.MustGetString(test.key), test.value)
+ assert.Equal(t, p.GetComments(test.key), test.comments)
+ if test.comments != nil {
+ assert.Equal(t, p.GetComment(test.key), test.comments[len(test.comments)-1])
+ } else {
+ assert.Equal(t, p.GetComment(test.key), "")
+ }
+
+ // test setting comments
+ if len(test.comments) > 0 {
+ // set single comment
+ p.ClearComments()
+ assert.Equal(t, len(p.c), 0)
+ p.SetComment(test.key, test.comments[0])
+ assert.Equal(t, p.GetComment(test.key), test.comments[0])
+
+ // set multiple comments
+ p.ClearComments()
+ assert.Equal(t, len(p.c), 0)
+ p.SetComments(test.key, test.comments)
+ assert.Equal(t, p.GetComments(test.key), test.comments)
+
+ // clear comments for a key
+ p.SetComments(test.key, nil)
+ assert.Equal(t, p.GetComment(test.key), "")
+ assert.Equal(t, p.GetComments(test.key), ([]string)(nil))
+ }
+ }
+}
+
+func TestFilter(t *testing.T) {
+ for _, test := range filterTests {
+ p := mustParse(t, test.input)
+ pp, err := p.Filter(test.pattern)
+ if err != nil {
+ assert.Matches(t, err.Error(), test.err)
+ continue
+ }
+ assert.Equal(t, pp != nil, true, "want properties")
+ assert.Equal(t, pp.Len(), len(test.keys))
+ for _, key := range test.keys {
+ v1, ok1 := p.Get(key)
+ v2, ok2 := pp.Get(key)
+ assert.Equal(t, ok1, true)
+ assert.Equal(t, ok2, true)
+ assert.Equal(t, v1, v2)
+ }
+ }
+}
+
+func TestFilterPrefix(t *testing.T) {
+ for _, test := range filterPrefixTests {
+ p := mustParse(t, test.input)
+ pp := p.FilterPrefix(test.prefix)
+ assert.Equal(t, pp != nil, true, "want properties")
+ assert.Equal(t, pp.Len(), len(test.keys))
+ for _, key := range test.keys {
+ v1, ok1 := p.Get(key)
+ v2, ok2 := pp.Get(key)
+ assert.Equal(t, ok1, true)
+ assert.Equal(t, ok2, true)
+ assert.Equal(t, v1, v2)
+ }
+ }
+}
+
+func TestFilterStripPrefix(t *testing.T) {
+ for _, test := range filterStripPrefixTests {
+ p := mustParse(t, test.input)
+ pp := p.FilterPrefix(test.prefix)
+ assert.Equal(t, pp != nil, true, "want properties")
+ assert.Equal(t, pp.Len(), len(test.keys))
+ for _, key := range test.keys {
+ v1, ok1 := p.Get(key)
+ v2, ok2 := pp.Get(key)
+ assert.Equal(t, ok1, true)
+ assert.Equal(t, ok2, true)
+ assert.Equal(t, v1, v2)
+ }
+ }
+}
+
+func TestKeys(t *testing.T) {
+ for _, test := range keysTests {
+ p := mustParse(t, test.input)
+ assert.Equal(t, p.Len(), len(test.keys))
+ assert.Equal(t, len(p.Keys()), len(test.keys))
+ assert.Equal(t, p.Keys(), test.keys)
+ }
+}
+
+func TestSet(t *testing.T) {
+ for _, test := range setTests {
+ p := mustParse(t, test.input)
+ prev, ok, err := p.Set(test.key, test.value)
+ if test.err != "" {
+ assert.Matches(t, err.Error(), test.err)
+ continue
+ }
+
+ assert.Equal(t, err, nil)
+ assert.Equal(t, ok, test.ok)
+ if ok {
+ assert.Equal(t, prev, test.prev)
+ }
+ assert.Equal(t, p.Keys(), test.keys)
+ }
+}
+
+func TestSetValue(t *testing.T) {
+ tests := []interface{}{
+ true, false,
+ int8(123), int16(123), int32(123), int64(123), int(123),
+ uint8(123), uint16(123), uint32(123), uint64(123), uint(123),
+ float32(1.23), float64(1.23),
+ "abc",
+ }
+
+ for _, v := range tests {
+ p := NewProperties()
+ err := p.SetValue("x", v)
+ assert.Equal(t, err, nil)
+ assert.Equal(t, p.GetString("x", ""), fmt.Sprintf("%v", v))
+ }
+}
+
+func TestMustSet(t *testing.T) {
+ input := "key=${key}"
+ p := mustParse(t, input)
+ assert.Panic(t, func() { p.MustSet("key", "${key}") }, "circular reference .*")
+}
+
+func TestWrite(t *testing.T) {
+ for _, test := range writeTests {
+ p, err := parse(test.input)
+
+ buf := new(bytes.Buffer)
+ var n int
+ switch test.encoding {
+ case "UTF-8":
+ n, err = p.Write(buf, UTF8)
+ case "ISO-8859-1":
+ n, err = p.Write(buf, ISO_8859_1)
+ }
+ assert.Equal(t, err, nil)
+ s := string(buf.Bytes())
+ assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
+ assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
+ }
+}
+
+func TestWriteComment(t *testing.T) {
+ for _, test := range writeCommentTests {
+ p, err := parse(test.input)
+
+ buf := new(bytes.Buffer)
+ var n int
+ switch test.encoding {
+ case "UTF-8":
+ n, err = p.WriteComment(buf, "# ", UTF8)
+ case "ISO-8859-1":
+ n, err = p.WriteComment(buf, "# ", ISO_8859_1)
+ }
+ assert.Equal(t, err, nil)
+ s := string(buf.Bytes())
+ assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
+ assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
+ }
+}
+
+func TestCustomExpansionExpression(t *testing.T) {
+ testKeyValuePrePostfix(t, "*[", "]*", "key=value\nkey2=*[key]*", "key", "value", "key2", "value")
+}
+
+func TestPanicOn32BitIntOverflow(t *testing.T) {
+ is32Bit = true
+ var min, max int64 = math.MinInt32 - 1, math.MaxInt32 + 1
+ input := fmt.Sprintf("min=%d\nmax=%d", min, max)
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetInt64("min"), min)
+ assert.Equal(t, p.MustGetInt64("max"), max)
+ assert.Panic(t, func() { p.MustGetInt("min") }, ".* out of range")
+ assert.Panic(t, func() { p.MustGetInt("max") }, ".* out of range")
+}
+
+func TestPanicOn32BitUintOverflow(t *testing.T) {
+ is32Bit = true
+ var max uint64 = math.MaxUint32 + 1
+ input := fmt.Sprintf("max=%d", max)
+ p := mustParse(t, input)
+ assert.Equal(t, p.MustGetUint64("max"), max)
+ assert.Panic(t, func() { p.MustGetUint("max") }, ".* out of range")
+}
+
+func TestDeleteKey(t *testing.T) {
+ input := "#comments should also be gone\nkey=to-be-deleted\nsecond=key"
+ p := mustParse(t, input)
+ assert.Equal(t, len(p.m), 2)
+ assert.Equal(t, len(p.c), 1)
+ assert.Equal(t, len(p.k), 2)
+ p.Delete("key")
+ assert.Equal(t, len(p.m), 1)
+ assert.Equal(t, len(p.c), 0)
+ assert.Equal(t, len(p.k), 1)
+ assert.Equal(t, p.k[0], "second")
+ assert.Equal(t, p.m["second"], "key")
+}
+
+func TestDeleteUnknownKey(t *testing.T) {
+ input := "#comments should also be gone\nkey=to-be-deleted"
+ p := mustParse(t, input)
+ assert.Equal(t, len(p.m), 1)
+ assert.Equal(t, len(p.c), 1)
+ assert.Equal(t, len(p.k), 1)
+ p.Delete("wrong-key")
+ assert.Equal(t, len(p.m), 1)
+ assert.Equal(t, len(p.c), 1)
+ assert.Equal(t, len(p.k), 1)
+}
+
+func TestMerge(t *testing.T) {
+ input1 := "#comment\nkey=value\nkey2=value2"
+ input2 := "#another comment\nkey=another value\nkey3=value3"
+ p1 := mustParse(t, input1)
+ p2 := mustParse(t, input2)
+ p1.Merge(p2)
+ assert.Equal(t, len(p1.m), 3)
+ assert.Equal(t, len(p1.c), 1)
+ assert.Equal(t, len(p1.k), 3)
+ assert.Equal(t, p1.MustGet("key"), "another value")
+ assert.Equal(t, p1.GetComment("key"), "another comment")
+}
+
+func TestMap(t *testing.T) {
+ input := "key=value\nabc=def"
+ p := mustParse(t, input)
+ m := map[string]string{"key": "value", "abc": "def"}
+ assert.Equal(t, p.Map(), m)
+}
+
+func TestFilterFunc(t *testing.T) {
+ input := "key=value\nabc=def"
+ p := mustParse(t, input)
+ pp := p.FilterFunc(func(k, v string) bool {
+ return k != "abc"
+ })
+ m := map[string]string{"key": "value"}
+ assert.Equal(t, pp.Map(), m)
+}
+
+// ----------------------------------------------------------------------------
+
+// tests all combinations of delimiters, leading and/or trailing whitespace and newlines.
+func testWhitespaceAndDelimiterCombinations(t *testing.T, key, value string) {
+ whitespace := []string{"", " ", "\f", "\t"}
+ delimiters := []string{"", " ", "=", ":"}
+ newlines := []string{"", "\r", "\n", "\r\n"}
+ for _, dl := range delimiters {
+ for _, ws1 := range whitespace {
+ for _, ws2 := range whitespace {
+ for _, nl := range newlines {
+ // skip the one case where there is nothing between a key and a value
+ if ws1 == "" && dl == "" && ws2 == "" && value != "" {
+ continue
+ }
+
+ input := fmt.Sprintf("%s%s%s%s%s%s", key, ws1, dl, ws2, value, nl)
+ testKeyValue(t, input, key, value)
+ }
+ }
+ }
+ }
+}
+
+// tests whether key/value pairs exist for a given input.
+// keyvalues is expected to be an even number of strings of "key", "value", ...
+func testKeyValue(t *testing.T, input string, keyvalues ...string) {
+ testKeyValuePrePostfix(t, "${", "}", input, keyvalues...)
+}
+
+// tests whether key/value pairs exist for a given input.
+// keyvalues is expected to be an even number of strings of "key", "value", ...
+func testKeyValuePrePostfix(t *testing.T, prefix, postfix, input string, keyvalues ...string) {
+ p, err := Load([]byte(input), ISO_8859_1)
+ assert.Equal(t, err, nil)
+ p.Prefix = prefix
+ p.Postfix = postfix
+ assertKeyValues(t, input, p, keyvalues...)
+}
+
+// tests whether key/value pairs exist for a given input.
+// keyvalues is expected to be an even number of strings of "key", "value", ...
+func assertKeyValues(t *testing.T, input string, p *Properties, keyvalues ...string) {
+ assert.Equal(t, p != nil, true, "want properties")
+ assert.Equal(t, 2*p.Len(), len(keyvalues), "Odd number of key/value pairs.")
+
+ for i := 0; i < len(keyvalues); i += 2 {
+ key, value := keyvalues[i], keyvalues[i+1]
+ v, ok := p.Get(key)
+ if !ok {
+ t.Errorf("No key %q found (input=%q)", key, input)
+ }
+ if got, want := v, value; !reflect.DeepEqual(got, want) {
+ t.Errorf("Value %q does not match %q (input=%q)", v, value, input)
+ }
+ }
+}
+
+func mustParse(t *testing.T, s string) *Properties {
+ p, err := parse(s)
+ if err != nil {
+ t.Fatalf("parse failed with %s", err)
+ }
+ return p
+}
+
+// prints to stderr if the -verbose flag was given.
+func printf(format string, args ...interface{}) {
+ if *verbose {
+ fmt.Fprintf(os.Stderr, format, args...)
+ }
+}
diff --git a/vendor/github.com/magiconair/properties/rangecheck.go b/vendor/github.com/magiconair/properties/rangecheck.go
new file mode 100644
index 000000000..2e907d540
--- /dev/null
+++ b/vendor/github.com/magiconair/properties/rangecheck.go
@@ -0,0 +1,31 @@
+// Copyright 2017 Frank Schroeder. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package properties
+
+import (
+ "fmt"
+ "math"
+)
+
+// make this a var to overwrite it in a test
+var is32Bit = ^uint(0) == math.MaxUint32
+
+// intRangeCheck checks if the value fits into the int type and
+// panics if it does not.
+func intRangeCheck(key string, v int64) int {
+ if is32Bit && (v < math.MinInt32 || v > math.MaxInt32) {
+ panic(fmt.Sprintf("Value %d for key %s out of range", v, key))
+ }
+ return int(v)
+}
+
+// uintRangeCheck checks if the value fits into the uint type and
+// panics if it does not.
+func uintRangeCheck(key string, v uint64) uint {
+ if is32Bit && v > math.MaxUint32 {
+ panic(fmt.Sprintf("Value %d for key %s out of range", v, key))
+ }
+ return uint(v)
+}