diff --git a/CHANGELOG.md b/CHANGELOG.md index af8c1533b..5e67aff3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All changes to `doctl` will be documented in this file. +## [1.9.0] - 2018-08-27 + +- #343 Add support for Spaces CDN. - @sunny-b + ## [1.8.3] - 2018-06-13 - #326 Fix required arguments (#325). - @adamwg diff --git a/Dockerfile b/Dockerfile index fd7a1af11..68f14ad96 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.5 -ENV DOCTL_VERSION=1.8.2 +ENV DOCTL_VERSION=1.9.0 RUN apk add --no-cache curl diff --git a/Gopkg.lock b/Gopkg.lock index 32c30c692..5ea7432d7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,392 +2,248 @@ [[projects]] - digest = "1:701d970fe50403e0263334affc919c38c2464e58851ec24689373d2dbcd95962" + name = "github.com/BurntSushi/toml" + packages = ["."] + revision = "5c4df71dfe9ac89ef6287afc05e4c1b16ae65a1e" + +[[projects]] name = "github.com/blang/semver" packages = ["."] - pruneopts = "" revision = "aea32c919a18e5ef4537bbd283ff29594b1b0165" version = "v3.1.0" [[projects]] - digest = "1:76532b02a60a39e610f214830dc8f507d0fe7fc5f456761e302e24aae938c6b0" name = "github.com/cpuguy83/go-md2man" packages = ["md2man"] - pruneopts = "" revision = "2724a9c9051aa62e9cca11304e7dd518e9e41599" version = "v1.0.5" [[projects]] - digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" name = "github.com/davecgh/go-spew" packages = ["spew"] - pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] - digest = "1:b31080395daa46b9381bb4385236c4654960ad8f4e78fdd4c981f45d04365466" + digest = "1:8a964cb8355fb61ebe48ae8b543e93e4d687d6f4ad45a10e197fde8676b462a9" name = "github.com/digitalocean/godo" packages = [ ".", "util", ] pruneopts = "" - revision = "04a8dba8b56cf79f4676408f0b3df18fc7414489" - version = "v1.4.0" + revision = "ddc940d8ef8b736b5cdfc47f24cdf95b9a4b73f6" + version = "v1.4.1" + [[projects]] - digest = "1:7dcfb2f910ad092e2845d312663dfe72075a172f76daff16e6e2c9722a73dc19" name = "github.com/dustin/go-humanize" packages = ["."] - pruneopts = "" revision = "8929fe90cee4b2cb9deb468b51fb34eba64d1bf0" [[projects]] - digest = "1:85d69fddb9c2f4ac09c7fe44c324d1d81eb58397356eccd5bd239b3d03a7d7fd" name = "github.com/fatih/color" packages = ["."] - pruneopts = "" revision = "7a5857db0b2752a436d8461d88c42dea0ee191c0" [[projects]] - digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" - name = "github.com/fsnotify/fsnotify" - packages = ["."] - pruneopts = "" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - digest = "1:3294cee8d1604914e1d6137a963c540832991e18983716b30ab5be274477df9c" name = "github.com/gobwas/glob" - packages = [ - ".", - "match", - ] - pruneopts = "" + packages = [".","match"] revision = "3222d377756fc0940b549f57a038bc9ba2716628" [[projects]] - digest = "1:d4a8ea264203f9781a860fc407f7accf33ab844455004faacd93ec4e0890e9f0" name = "github.com/golang/protobuf" packages = ["proto"] - pruneopts = "" revision = "0dfe8f37844c14cb32c7247925270e0f7ba90973" [[projects]] - digest = "1:274ad8931e8b328ba8d0eb379b34ebbe146106410d676cfa2df9f00cfa877f90" name = "github.com/google/go-querystring" packages = ["query"] - pruneopts = "" revision = "2a60fc2ba6c19de80291203597d752e9ba58e4c0" [[projects]] - digest = "1:cf1e90d3ecd851452ed90248bcf087a7c32ab94a409aaa12a96d131a480a2bbe" name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/printer", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "" + packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"] revision = "1c284ec98f4b398443cbabb0d9197f7f4cc0077c" [[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] - pruneopts = "" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] branch = "master" - digest = "1:abde445510ed7b23086372c0bfdb607091e8118ca94ee853153a2d7709d65087" name = "github.com/kr/fs" packages = ["."] - pruneopts = "" revision = "2788f0dbd16903de03cb8186e5c7d97b69ad387b" [[projects]] - digest = "1:b071dc6f0504d75a07a68288dbfd19226682b0a516383895fb0b481b92365ddf" + name = "github.com/kr/pretty" + packages = ["."] + revision = "e6ac2fc51e89a3249e82157fa0bb7a18ef9dd5bb" + +[[projects]] + name = "github.com/kr/text" + packages = ["."] + revision = "bb797dc4fb8320488f47bf11de07a733d7233e1f" + +[[projects]] name = "github.com/magiconair/properties" packages = ["."] - pruneopts = "" revision = "c81f9d71af8f8cba1466501d30326b99a4e56c19" [[projects]] - digest = "1:bf77eee6b1baa8f8e00c0c14793b4fe457f78889120e51a3fa11ea7666a3a51d" name = "github.com/mattn/go-colorable" packages = ["."] - pruneopts = "" revision = "9fdad7c47650b7d2e1da50644c1f4ba7f172f252" [[projects]] - digest = "1:cce836b4630bd4491cbfb4e26a563cc084af1739ef42333344f4f66b8919f05d" name = "github.com/mattn/go-isatty" packages = ["."] - pruneopts = "" revision = "56b76bdf51f7708750eac80fa38b952bb9f32639" [[projects]] branch = "master" - digest = "1:a54a4dd40a42286768c4dd1603cb0b0fc3f41016903168b5a49d9c26d1961bd6" name = "github.com/mitchellh/ioprogress" packages = ["."] - pruneopts = "" revision = "8163955264568045f462ae7e2d6d07b2001fc997" [[projects]] - digest = "1:e6bde7d0156586ff397479872c62f34489f89531d3a91251a259e12f3b0f5da9" name = "github.com/mitchellh/mapstructure" packages = ["."] - pruneopts = "" revision = "d2dd0262208475919e1a362f675cfc0e7c10e905" [[projects]] - digest = "1:58d86a942cb7aab7a1618db879f0af80798d31b9dd68ce8c282b5311dfdd2ec7" name = "github.com/natefinch/pie" packages = ["."] - pruneopts = "" revision = "6059396cf8c679e5b6a7c82378e4623ca62f91ff" [[projects]] - digest = "1:ae786f2519744f1f7912c25f9c0ba78b742cd5616d66776549fbaa9d1edfd42d" name = "github.com/pborman/uuid" packages = ["."] - pruneopts = "" revision = "c55201b036063326c5b1b89ccfe45a184973d073" [[projects]] - digest = "1:894aef961c056b6d85d12bac890bf60c44e99b46292888bfa66caf529f804457" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - -[[projects]] - digest = "1:be1e8fbc448b6e1962ec5f7ac6b68efef7c4f02d8beb42d5b7a1f153a696f773" name = "github.com/pkg/sftp" packages = ["."] - pruneopts = "" revision = "e84cc8c755ca39b7b64f510fe1fffc1b51f210a5" [[projects]] - digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] - pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] - digest = "1:4903de66fb775c55048754c94e582d06834098b633eddfa6d436ca3432f44b04" name = "github.com/russross/blackfriday" packages = ["."] - pruneopts = "" revision = "006144af03eeeff1037240a71865a9fd61f1c25f" [[projects]] branch = "master" - digest = "1:d0148ccd69edc991a8e0e472ae9720f5b46209e12a00c0ba11b4cbcc4fc2c72b" name = "github.com/shiena/ansicolor" packages = ["."] - pruneopts = "" revision = "a422bbe96644373c5753384a59d678f7d261ff10" [[projects]] - digest = "1:0bd3d57832f5fa1abc023b3f4d52fa7b06d79a82d116c54055d2c5d0be61d8f7" name = "github.com/shurcooL/sanitized_anchor_name" packages = ["."] - pruneopts = "" revision = "10ef21a441db47d8b13ebcc5fd2310f636973c77" [[projects]] - digest = "1:81eac6465397feab14c585c626e45477d1805e9337c7a8b1161de09ea85a97a1" name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - "sftp", - ] - pruneopts = "" + packages = [".","mem","sftp"] revision = "28789ef4b78c82371d454bb13c56ef641bf50c6e" [[projects]] - digest = "1:d0b38ba6da419a6d4380700218eeec8623841d44a856bb57369c172fbf692ab4" name = "github.com/spf13/cast" packages = ["."] - pruneopts = "" - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" + revision = "ee7b3e0353166ab1f3a605294ac8cd2b77953778" [[projects]] - digest = "1:cf0f3c1f96972ccecdf56e960400fb3b6f36d9827564f1d3ce2de21c28287379" name = "github.com/spf13/cobra" - packages = [ - ".", - "doc", - ] - pruneopts = "" + packages = [".","doc"] revision = "65a708cee0a4424f4e353d031ce440643e312f92" [[projects]] - digest = "1:a98bfccb2ddb6cea7ffcc43d9513cba2f4ff0c2cbddea4d2e8e5b0a8d90e95fc" name = "github.com/spf13/jwalterweatherman" packages = ["."] - pruneopts = "" revision = "d00654080cddbd2b082acaa74007cb94a2b40866" [[projects]] - digest = "1:630af6f62aa7d4ac3e8847cd6d067d7b4a4d25ecdd3e8cc7c037aea5a34b2cc5" name = "github.com/spf13/pflag" packages = ["."] - pruneopts = "" revision = "7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7" [[projects]] - digest = "1:a3018c5a423d6cfacf5c6e736ad07637494728fb446390e3f34cacae9e80a529" name = "github.com/spf13/viper" packages = ["."] - pruneopts = "" - revision = "907c19d40d9a6c9bb55f040ff4ae45271a4754b9" - version = "v1.1.0" + revision = "c975dc1b4eacf4ec7fdbf0873638de5d090ba323" [[projects]] branch = "master" - digest = "1:ed7ac53c7d59041f27964d3f04e021b45ecb5f23c842c84d778a7f1fb67e2ce9" name = "github.com/stretchr/objx" packages = ["."] - pruneopts = "" revision = "1a9d0bb9f541897e62256577b352fdbc1fb4fd94" [[projects]] - digest = "1:fde9f539530362ced5f05bd8477eb731a88f3b82301aa9bb1becc51e3ffd781f" name = "github.com/stretchr/testify" - packages = [ - "assert", - "mock", - ] - pruneopts = "" + packages = ["assert","mock"] revision = "4d4bfba8f1d1027c4fdbe371823030df51419987" [[projects]] branch = "master" - digest = "1:81f435c83e3523a7ee3f277769727f73ca66218ca8188d96a0935a4841b47a76" name = "github.com/tent/http-link-go" packages = ["."] - pruneopts = "" revision = "ac974c61c2f990f4115b119354b5e0b47550e888" [[projects]] - digest = "1:d98b9646fd6635739cdd0555186de09dd895c2d1b899bd77d570237a44301117" name = "golang.org/x/crypto" - packages = [ - "curve25519", - "ssh", - "ssh/agent", - "ssh/terminal", - ] - pruneopts = "" + packages = ["curve25519","ssh","ssh/agent","ssh/terminal"] revision = "1f22c0103821b9390939b6776727195525381532" [[projects]] - digest = "1:19b921602e989ecd0e369be20ea994e7ce9d9e68cb855fac05cb0cbe214427dd" name = "golang.org/x/net" packages = ["context"] - pruneopts = "" revision = "cbbbe2bc0f2efdd2afb318d93f1eadb19350e4a3" [[projects]] - digest = "1:69330e4325d170693145e1af736ba131539be5f03242988470ae6e6d5b3c4c23" name = "golang.org/x/oauth2" - packages = [ - ".", - "internal", - ] - pruneopts = "" + packages = [".","internal"] revision = "8a57ed94ffd43444c0879fe75701732a38afc985" [[projects]] branch = "master" - digest = "1:5cfdfd9ad3bb16158ab73df545388f3d5af149972a9a537e06c26b9d32006ba4" name = "golang.org/x/sys" packages = ["unix"] - pruneopts = "" revision = "ebfc5b4631820b793c9010c87fd8fef0f39eb082" [[projects]] - digest = "1:60519fd9d4875ab3b3df9d241c0ec2cb1ab5613f2ef367b6732d40ccdbcfd54c" name = "golang.org/x/text" - packages = [ - "internal/gen", - "internal/triegen", - "internal/ucd", - "transform", - "unicode/cldr", - "unicode/norm", - ] - pruneopts = "" + packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm"] revision = "07b9a78963006a15c538ec5175243979025fa7a8" [[projects]] - digest = "1:c14f4bbb8c6df35f3a1d895d4d850ef26dfc1a579df8a3c8bf805e87d48dd8a5" name = "google.golang.org/appengine" - packages = [ - "internal", - "internal/base", - "internal/datastore", - "internal/log", - "internal/remote_api", - "internal/urlfetch", - "urlfetch", - ] - pruneopts = "" + packages = ["internal","internal/base","internal/datastore","internal/log","internal/remote_api","internal/urlfetch","urlfetch"] revision = "6a436539be38c296a8075a871cc536686b458371" +[[projects]] + name = "gopkg.in/fsnotify.v1" + packages = ["."] + revision = "8611c35ab31c1c28aa903d33cf8b6e44a399b09e" + version = "v1.2.9" + [[projects]] branch = "v2" - digest = "1:ec77d5a2dc88f47a8d981cbbdc41a0b40cad284b8bdedcdba953c3603684c466" name = "gopkg.in/yaml.v2" packages = ["."] - pruneopts = "" revision = "f7716cbe52baa25d2e9b0d0da546fcf909fc16b4" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - input-imports = [ - "github.com/blang/semver", - "github.com/digitalocean/godo", - "github.com/digitalocean/godo/util", - "github.com/dustin/go-humanize", - "github.com/fatih/color", - "github.com/gobwas/glob", - "github.com/mitchellh/ioprogress", - "github.com/natefinch/pie", - "github.com/pborman/uuid", - "github.com/shiena/ansicolor", - "github.com/spf13/afero", - "github.com/spf13/cobra", - "github.com/spf13/cobra/doc", - "github.com/spf13/jwalterweatherman", - "github.com/spf13/viper", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/mock", - "golang.org/x/crypto/ssh", - "golang.org/x/crypto/ssh/agent", - "golang.org/x/crypto/ssh/terminal", - "golang.org/x/oauth2", - "gopkg.in/yaml.v2", - ] + inputs-digest = "1787f89ee12ce8855e7e91b55611236bd0524c3c95254e94fe19de0b6e56da98" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 37fcbc81f..f469bf2dd 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -5,7 +5,7 @@ [[constraint]] name = "github.com/digitalocean/godo" - version = "1.4.0" + version = "1.4.1" [[constraint]] name = "github.com/dustin/go-humanize" diff --git a/README.md b/README.md index 009e79b76..26b6f38e9 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ You can use [Snap](https://snapcraft.io/) on [Snap-supported](https://snapcraft. ``` sudo snap install doctl ``` - #### Arch Linux + #### Arch Linux Arch users not using snaps can install from the [AUR](https://aur.archlinux.org/packages/doctl-bin/). Support for Windows package managers is on the way. @@ -58,25 +58,25 @@ For example, with `wget`: ``` cd ~ -wget https://github.com/digitalocean/doctl/releases/download/v1.8.3/doctl-1.8.3-linux-amd64.tar.gz +wget https://github.com/digitalocean/doctl/releases/download/v1.9.0/doctl-1.9.0-linux-amd64.tar.gz ``` Or with `curl`: ``` cd ~ -curl -OL https://github.com/digitalocean/doctl/releases/download/v1.8.3/doctl-1.8.3-linux-amd64.tar.gz +curl -OL https://github.com/digitalocean/doctl/releases/download/v1.9.0/doctl-1.9.0-linux-amd64.tar.gz ``` Extract the binary. On GNU/Linux or OS X systems, you can use `tar`. ``` -tar xf ~/doctl-1.8.3-linux-amd64.tar.gz +tar xf ~/doctl-1.9.0-linux-amd64.tar.gz ``` Or download and extract with this oneliner: ``` -curl -sL https://github.com/digitalocean/doctl/releases/download/v1.8.3/doctl-1.8.3-linux-amd64.tar.gz | tar -xzv +curl -sL https://github.com/digitalocean/doctl/releases/download/v1.9.0/doctl-1.9.0-linux-amd64.tar.gz | tar -xzv ``` On Windows systems, you should be able to double-click the zip archive to extract the `doctl` executable. @@ -245,6 +245,6 @@ doctl compute ssh @ * [How To Use Doctl, the Official DigitalOcean Command-Line Client](https://www.digitalocean.com/community/tutorials/how-to-use-doctl-the-official-digitalocean-command-line-client) * [How To Work with DigitalOcean Load Balancers Using Doctl](https://www.digitalocean.com/community/tutorials/how-to-work-with-digitalocean-load-balancers-using-doctl) -* [How To Secure Web Server Infrastructure With DigitalOcean Cloud Firewalls Using Doctl](https://www.digitalocean.com/community/tutorials/how-to-secure-web-server-infrastructure-with-digitalocean-cloud-firewalls-using-doctl) -* [How To Work with DigitalOcean Block Storage Using Doctl](https://www.digitalocean.com/community/tutorials/how-to-work-with-digitalocean-block-storage-using-doctl) +* [How To Secure Web Server Infrastructure With DigitalOcean Cloud Firewalls Using Doctl](https://www.digitalocean.com/community/tutorials/how-to-secure-web-server-infrastructure-with-digitalocean-cloud-firewalls-using-doctl) +* [How To Work with DigitalOcean Block Storage Using Doctl](https://www.digitalocean.com/community/tutorials/how-to-work-with-digitalocean-block-storage-using-doctl) * [doctl Releases](https://github.com/digitalocean/doctl/releases) diff --git a/args.go b/args.go index ca27f0f96..8dbb0acc6 100644 --- a/args.go +++ b/args.go @@ -149,6 +149,11 @@ const ( // ArgVolumeList is the IDs of many volumes. ArgVolumeList = "volumes" + // ArgCDNTTL is a cdn ttl argument + ArgCDNTTL = "ttl" + // ArgCDNFiles is a cdn files argument + ArgCDNFiles = "files" + // ArgCertificateName is a name of the certificate. ArgCertificateName = "name" // ArgCertificateDNSNames is a list of DNS names. diff --git a/commands/cdns.go b/commands/cdns.go new file mode 100644 index 000000000..2ddcf7af8 --- /dev/null +++ b/commands/cdns.go @@ -0,0 +1,178 @@ +/* +Copyright 2018 The Doctl Authors All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package commands + +import ( + "errors" + "fmt" + + "github.com/digitalocean/doctl" + "github.com/digitalocean/doctl/do" + "github.com/digitalocean/godo" + "github.com/spf13/cobra" +) + +// CDN creates the CDN command +func CDN() *Command { + cmd := &Command{ + Command: &cobra.Command{ + Use: "cdn", + Short: "cdn commands", + Long: "cdn is used to access cdn commands", + }, + } + + CmdBuilder(cmd, RunCDNList, "list", "list cdn", Writer, + aliasOpt("ls"), displayerType(&cdn{})) + + cmdCDNCreate := CmdBuilder(cmd, RunCDNCreate, "create ", "create a cdn", Writer, + aliasOpt("c"), displayerType(&cdn{})) + AddIntFlag(cmdCDNCreate, doctl.ArgCDNTTL, "", 3600, "CDN ttl") + + cmdRunCDNDelete := CmdBuilder(cmd, RunCDNDelete, "delete ", "delete a cdn", Writer, + aliasOpt("rm")) + AddBoolFlag(cmdRunCDNDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Force cdn delete") + + CmdBuilder(cmd, RunCDNGet, "get ", "get a cdn", Writer, aliasOpt("g"), + displayerType(&cdn{})) + + cmdCDNUpdate := CmdBuilder(cmd, RunCDNUpdateTTL, "update ", "update a cdn", Writer, + aliasOpt("u"), displayerType(&cdn{})) + AddIntFlag(cmdCDNUpdate, doctl.ArgCDNTTL, "", 3600, "cdn ttl") + + cmdCDNFlushCache := CmdBuilder(cmd, RunCDNFlushCache, "flush ", "flush cdn cache", Writer, + aliasOpt("fc")) + AddStringSliceFlag(cmdCDNFlushCache, doctl.ArgCDNFiles, "", []string{"*"}, "cdn files") + + return cmd +} + +// RunCDNList returns a list of CDNs. +func RunCDNList(c *CmdConfig) error { + cdns, err := c.CDNs().List() + if err != nil { + return err + } + + return c.Display(&cdn{cdns: cdns}) +} + +// RunCDNGet returns an individual CDN. +func RunCDNGet(c *CmdConfig) error { + if len(c.Args) == 0 { + return doctl.NewMissingArgsErr(c.NS) + } + + id := c.Args[0] + item, err := c.CDNs().Get(id) + if err != nil { + return err + } + + return c.Display(&cdn{cdns: []do.CDN{*item}}) +} + +// RunCDNCreate creates a cdn. +func RunCDNCreate(c *CmdConfig) error { + if len(c.Args) == 0 { + return doctl.NewMissingArgsErr(c.NS) + } + + origin := c.Args[0] + + ttl, err := c.Doit.GetInt(c.NS, doctl.ArgCDNTTL) + if err != nil { + return err + } + if ttl <= 0 { + return errors.New("ttl cannot be zero or negative") + } + + createCDN := &godo.CDNCreateRequest{ + Origin: origin, + TTL: uint32(ttl), + } + + item, err := c.CDNs().Create(createCDN) + if err != nil { + return err + } + + return c.Display(&cdn{cdns: []do.CDN{*item}}) +} + +// RunCDNUpdateTTL updates an individual cdn ttl +func RunCDNUpdateTTL(c *CmdConfig) error { + if len(c.Args) == 0 { + return doctl.NewMissingArgsErr(c.NS) + } + + id := c.Args[0] + + cs := c.CDNs() + + ttl, err := c.Doit.GetInt(c.NS, doctl.ArgCDNTTL) + if err != nil { + return err + } + if ttl <= 0 { + return errors.New("ttl cannot be zero or negative") + } + + updateCDN := &godo.CDNUpdateRequest{TTL: uint32(ttl)} + + item, err := cs.UpdateTTL(id, updateCDN) + if err != nil { + return err + } + + return c.Display(&cdn{cdns: []do.CDN{*item}}) +} + +// RunCDNDelete deletes a cdn. +func RunCDNDelete(c *CmdConfig) error { + if len(c.Args) == 0 { + return doctl.NewMissingArgsErr(c.NS) + } + + force, err := c.Doit.GetBool(c.NS, doctl.ArgForce) + if err != nil { + return err + } + + if force || AskForConfirm("delete cdn") == nil { + id := c.Args[0] + return c.CDNs().Delete(id) + } + + return fmt.Errorf("operation aborted") +} + +// RunCDNFlushCache flushes the cache of an individual cdn +func RunCDNFlushCache(c *CmdConfig) error { + if len(c.Args) == 0 { + return doctl.NewMissingArgsErr(c.NS) + } + + id := c.Args[0] + + files, err := c.Doit.GetStringSlice(c.NS, doctl.ArgCDNFiles) + if err != nil { + return err + } + + flushCDN := &godo.CDNFlushCacheRequest{Files: files} + + return c.CDNs().FlushCache(id, flushCDN) +} diff --git a/commands/cdns_test.go b/commands/cdns_test.go new file mode 100644 index 000000000..207f6b12b --- /dev/null +++ b/commands/cdns_test.go @@ -0,0 +1,184 @@ +/* +Copyright 2018 The Doctl Authors All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package commands + +import ( + "testing" + "time" + + "github.com/digitalocean/doctl" + "github.com/digitalocean/doctl/do" + "github.com/digitalocean/godo" + "github.com/pborman/uuid" + "github.com/stretchr/testify/assert" +) + +var ( + cdnID = uuid.New() + cdnOrigin = "my-spaces.nyc3.digitaloceanspaces.com" + + testCDN = do.CDN{ + CDN: &godo.CDN{ + ID: cdnID, + Origin: cdnOrigin, + Endpoint: "my-spaces.nyc3.cdn.digitaloceanspaces.com", + TTL: 3600, + CreatedAt: time.Now(), + }, + } + + updatedCDN = do.CDN{ + CDN: &godo.CDN{ + ID: cdnID, + Origin: cdnOrigin, + Endpoint: "my-spaces.nyc3.cdn.digitaloceanspaces.com", + TTL: 60, + CreatedAt: time.Now(), + }, + } + + testCDNList = []do.CDN{ + testCDN, + } +) + +func TestCDNCommand(t *testing.T) { + cmd := CDN() + assert.NotNil(t, cmd) + assertCommandNames(t, cmd, "create", "delete", "get", "list", "update", "flush") +} + +func TestCDNsGet(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + tm.cdns.On("Get", cdnID).Return(&testCDN, nil) + + config.Args = append(config.Args, cdnID) + + err := RunCDNGet(config) + assert.NoError(t, err) + }) +} + +func TestCDNsGet_RequiredArguments(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + err := RunCDNGet(config) + assert.Error(t, err) + }) +} + +func TestCDNsList(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + tm.cdns.On("List").Return(testCDNList, nil) + + err := RunCDNList(config) + assert.NoError(t, err) + }) +} + +func TestCDNsCreate(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + cdncr := &godo.CDNCreateRequest{ + Origin: cdnOrigin, + TTL: 3600, + } + tm.cdns.On("Create", cdncr).Return(&testCDN, nil) + + config.Args = append(config.Args, cdnOrigin) + config.Doit.Set(config.NS, doctl.ArgCDNTTL, 3600) + + err := RunCDNCreate(config) + assert.NoError(t, err) + }) +} + +func TestCDNsCreate_RequiredArguments(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + err := RunCDNCreate(config) + assert.Error(t, err) + }) +} + +func TestCDNsCreate_ZeroFail(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + config.Args = append(config.Args, cdnOrigin) + config.Doit.Set(config.NS, doctl.ArgCDNTTL, 0) + + err := RunCDNCreate(config) + assert.Error(t, err) + }) +} + +func TestCDNsUpdate(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + cdnur := &godo.CDNUpdateRequest{ + TTL: 60, + } + tm.cdns.On("UpdateTTL", cdnID, cdnur).Return(&updatedCDN, nil) + + config.Args = append(config.Args, cdnID) + config.Doit.Set(config.NS, doctl.ArgCDNTTL, 60) + + err := RunCDNUpdateTTL(config) + assert.NoError(t, err) + }) +} + +func TestCDNsUpdate_ZeroFail(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + config.Args = append(config.Args, cdnID) + config.Doit.Set(config.NS, doctl.ArgCDNTTL, 0) + + err := RunCDNUpdateTTL(config) + assert.Error(t, err) + }) +} + +func TestCDNsDelete(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + tm.cdns.On("Delete", cdnID).Return(nil) + + config.Args = append(config.Args, cdnID) + config.Doit.Set(config.NS, doctl.ArgForce, true) + + err := RunCDNDelete(config) + assert.NoError(t, err) + }) +} + +func TestCDNsDelete_RequiredArguments(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + err := RunCDNDelete(config) + assert.Error(t, err) + }) +} + +func TestCDNsFlushCache(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + flushReq := &godo.CDNFlushCacheRequest{Files: []string{"*"}} + tm.cdns.On("FlushCache", cdnID, flushReq).Return(nil) + + config.Args = append(config.Args, cdnID) + config.Doit.Set(config.NS, doctl.ArgCDNFiles, []string{"*"}) + + err := RunCDNFlushCache(config) + assert.NoError(t, err) + }) +} + +func TestCDNsFlushCache_RequiredArguments(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + err := RunCDNFlushCache(config) + assert.Error(t, err) + }) +} diff --git a/commands/commands_test.go b/commands/commands_test.go index 74a7382cf..394e58d4d 100644 --- a/commands/commands_test.go +++ b/commands/commands_test.go @@ -163,6 +163,7 @@ type tcMocks struct { certificates domocks.CertificatesService loadBalancers domocks.LoadBalancersService firewalls domocks.FirewallsService + cdns domocks.CDNsService } func withTestClient(t *testing.T, tFn testFn) { @@ -188,7 +189,7 @@ func withTestClient(t *testing.T, tFn testFn) { return viper.GetString(doctl.ArgAccessToken) }, - setContextAccessToken: func(token string) { }, + setContextAccessToken: func(token string) {}, Keys: func() do.KeysService { return &tm.keys }, Sizes: func() do.SizesService { return &tm.sizes }, @@ -209,6 +210,7 @@ func withTestClient(t *testing.T, tFn testFn) { Certificates: func() do.CertificatesService { return &tm.certificates }, LoadBalancers: func() do.LoadBalancersService { return &tm.loadBalancers }, Firewalls: func() do.FirewallsService { return &tm.firewalls }, + CDNs: func() do.CDNsService { return &tm.cdns }, } tFn(config, tm) @@ -232,6 +234,7 @@ func withTestClient(t *testing.T, tFn testFn) { assert.True(t, tm.snapshots.AssertExpectations(t)) assert.True(t, tm.loadBalancers.AssertExpectations(t)) assert.True(t, tm.firewalls.AssertExpectations(t)) + assert.True(t, tm.cdns.AssertExpectations(t)) } type TestConfig struct { diff --git a/commands/doit.go b/commands/doit.go index e71bfa323..1201ca115 100644 --- a/commands/doit.go +++ b/commands/doit.go @@ -186,6 +186,7 @@ func computeCmd() *Command { } cmd.AddCommand(Actions()) + cmd.AddCommand(CDN()) cmd.AddCommand(Certificate()) cmd.AddCommand(DropletAction()) cmd.AddCommand(Droplet()) @@ -312,7 +313,7 @@ type CmdConfig struct { Out io.Writer Args []string - initServices func(*CmdConfig) error + initServices func(*CmdConfig) error getContextAccessToken func() string setContextAccessToken func(string) @@ -336,6 +337,7 @@ type CmdConfig struct { Snapshots func() do.SnapshotsService Certificates func() do.CertificatesService Firewalls func() do.FirewallsService + CDNs func() do.CDNsService } // NewCmdConfig creates an instance of a CmdConfig. @@ -373,6 +375,7 @@ func NewCmdConfig(ns string, dc doctl.Config, out io.Writer, args []string, init c.Certificates = func() do.CertificatesService { return do.NewCertificatesService(godoClient) } c.LoadBalancers = func() do.LoadBalancersService { return do.NewLoadBalancersService(godoClient) } c.Firewalls = func() do.FirewallsService { return do.NewFirewallsService(godoClient) } + c.CDNs = func() do.CDNsService { return do.NewCDNsService(godoClient) } return nil }, @@ -394,24 +397,24 @@ func NewCmdConfig(ns string, dc doctl.Config, out io.Writer, args []string, init } return token - }, + }, - setContextAccessToken: func(token string) { - context := Context - if context == "" { - context = viper.GetString("context") - } + setContextAccessToken: func(token string) { + context := Context + if context == "" { + context = viper.GetString("context") + } - switch context { - case "default": - viper.Set(doctl.ArgAccessToken, token) - default: - contexts := viper.GetStringMapString("auth-contexts") - contexts[context] = token + switch context { + case "default": + viper.Set(doctl.ArgAccessToken, token) + default: + contexts := viper.GetStringMapString("auth-contexts") + contexts[context] = token - viper.Set("auth-contexts", contexts) - } - }, + viper.Set("auth-contexts", contexts) + } + }, } if initGodo { diff --git a/commands/output.go b/commands/output.go index d884e4426..7ff87d254 100644 --- a/commands/output.go +++ b/commands/output.go @@ -579,6 +579,52 @@ func (t *tag) KV() []map[string]interface{} { return out } +//CDN Output + +type cdn struct { + cdns []do.CDN +} + +var _ Displayable = &cdn{} + +func (c *cdn) JSON(out io.Writer) error { + return writeJSON(c.cdns, out) +} + +func (c *cdn) Cols() []string { + return []string{ + "ID", "Origin", "Endpoint", "TTL", "CreatedAt", + } +} + +func (c *cdn) ColMap() map[string]string { + return map[string]string{ + "ID": "ID", + "Origin": "Origin", + "Endpoint": "Endpoint", + "TTL": "TTL", + "CreatedAt": "CreatedAt", + } +} + +func (c *cdn) KV() []map[string]interface{} { + out := []map[string]interface{}{} + + for _, cdn := range c.cdns { + m := map[string]interface{}{ + "ID": cdn.ID, + "Origin": cdn.Origin, + "Endpoint": cdn.Endpoint, + "TTL": cdn.TTL, + "CreatedAt": cdn.CreatedAt, + } + + out = append(out, m) + } + + return out +} + type volume struct { volumes []do.Volume } @@ -824,7 +870,6 @@ func (f *firewall) Cols() []string { } } -//"created_at":"2017-05-08T18:56:10Z","droplet_ids":[46298047],"disa func (f *firewall) ColMap() map[string]string { return map[string]string{ "ID": "ID", diff --git a/doit.go b/doit.go index 974b27f43..5b3ae6931 100644 --- a/doit.go +++ b/doit.go @@ -47,8 +47,8 @@ var ( // DoitVersion is doit's version. DoitVersion = Version{ Major: 1, - Minor: 8, - Patch: 3, + Minor: 9, + Patch: 0, Label: "dev", } @@ -269,7 +269,7 @@ func (c *LiveConfig) GetInt(ns, key string) (int, error) { nskey := fmt.Sprintf("%s.%s", ns, key) if _, ok := viper.AllSettings()[fmt.Sprintf("%s.required", nskey)]; ok { - if viper.GetInt(nskey) < 0 { + if viper.GetInt(nskey) == 0 { return 0, NewMissingArgsErr(nskey) } } @@ -286,7 +286,7 @@ func (c *LiveConfig) GetStringSlice(ns, key string) ([]string, error) { nskey := fmt.Sprintf("%s.%s", ns, key) if _, ok := viper.AllSettings()[fmt.Sprintf("%s.required", nskey)]; ok { - if viper.GetStringSlice(nskey) == nil { + if emptyStringSlice(viper.GetStringSlice(nskey)) { return nil, NewMissingArgsErr(nskey) } } @@ -309,3 +309,8 @@ func (c *LiveConfig) GetStringSlice(ns, key string) ([]string, error) { return out, nil } + +// This is needed because an empty StringSlice flag returns `["[]"]` +func emptyStringSlice(s []string) bool { + return len(s) == 1 && s[0] == "[]" +} diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fa5aff09c..851ea0627 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: doctl -version: "1.8.3" +version: "1.9.0" summary: A command line tool for DigitalOcean services description: doctl is a command line tool for DigitalOcean servics using the API. confinement: strict diff --git a/vendor/github.com/BurntSushi/toml/.gitignore b/vendor/github.com/BurntSushi/toml/.gitignore new file mode 100644 index 000000000..0cd380037 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/.gitignore @@ -0,0 +1,5 @@ +TAGS +tags +.*.swp +tomlcheck/tomlcheck +toml.test diff --git a/vendor/github.com/BurntSushi/toml/.travis.yml b/vendor/github.com/BurntSushi/toml/.travis.yml new file mode 100644 index 000000000..43caf6d02 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: + - 1.1 + - 1.2 + - tip +install: + - go install ./... + - go get github.com/BurntSushi/toml-test +script: + - export PATH="$PATH:$HOME/gopath/bin" + - make test + diff --git a/vendor/github.com/BurntSushi/toml/COMPATIBLE b/vendor/github.com/BurntSushi/toml/COMPATIBLE new file mode 100644 index 000000000..21e0938ca --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/COMPATIBLE @@ -0,0 +1,3 @@ +Compatible with TOML version +[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING new file mode 100644 index 000000000..5a8e33254 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/BurntSushi/toml/Makefile b/vendor/github.com/BurntSushi/toml/Makefile new file mode 100644 index 000000000..3600848d3 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/Makefile @@ -0,0 +1,19 @@ +install: + go install ./... + +test: install + go test -v + toml-test toml-test-decoder + toml-test -encoder toml-test-encoder + +fmt: + gofmt -w *.go */*.go + colcheck *.go */*.go + +tags: + find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS + +push: + git push origin master + git push github master + diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md new file mode 100644 index 000000000..5a5df6370 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/README.md @@ -0,0 +1,220 @@ +## TOML parser and encoder for Go with reflection + +TOML stands for Tom's Obvious, Minimal Language. This Go package provides a +reflection interface similar to Go's standard library `json` and `xml` +packages. This package also supports the `encoding.TextUnmarshaler` and +`encoding.TextMarshaler` interfaces so that you can define custom data +representations. (There is an example of this below.) + +Spec: https://github.com/mojombo/toml + +Compatible with TOML version +[v0.2.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.2.0.md) + +Documentation: http://godoc.org/github.com/BurntSushi/toml + +Installation: + +```bash +go get github.com/BurntSushi/toml +``` + +Try the toml validator: + +```bash +go get github.com/BurntSushi/toml/cmd/tomlv +tomlv some-toml-file.toml +``` + +[![Build status](https://api.travis-ci.org/BurntSushi/toml.png)](https://travis-ci.org/BurntSushi/toml) + + +### Testing + +This package passes all tests in +[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder +and the encoder. + +### Examples + +This package works similarly to how the Go standard library handles `XML` +and `JSON`. Namely, data is loaded into Go values via reflection. + +For the simplest example, consider some TOML file as just a list of keys +and values: + +```toml +Age = 25 +Cats = [ "Cauchy", "Plato" ] +Pi = 3.14 +Perfection = [ 6, 28, 496, 8128 ] +DOB = 1987-07-05T05:45:00Z +``` + +Which could be defined in Go as: + +```go +type Config struct { + Age int + Cats []string + Pi float64 + Perfection []int + DOB time.Time // requires `import time` +} +``` + +And then decoded with: + +```go +var conf Config +if _, err := toml.Decode(tomlData, &conf); err != nil { + // handle error +} +``` + +You can also use struct tags if your struct field name doesn't map to a TOML +key value directly: + +```toml +some_key_NAME = "wat" +``` + +```go +type TOML struct { + ObscureKey string `toml:"some_key_NAME"` +} +``` + +### Using the `encoding.TextUnmarshaler` interface + +Here's an example that automatically parses duration strings into +`time.Duration` values: + +```toml +[[song]] +name = "Thunder Road" +duration = "4m49s" + +[[song]] +name = "Stairway to Heaven" +duration = "8m03s" +``` + +Which can be decoded with: + +```go +type song struct { + Name string + Duration duration +} +type songs struct { + Song []song +} +var favorites songs +if _, err := toml.Decode(blob, &favorites); err != nil { + log.Fatal(err) +} + +for _, s := range favorites.Song { + fmt.Printf("%s (%s)\n", s.Name, s.Duration) +} +``` + +And you'll also need a `duration` type that satisfies the +`encoding.TextUnmarshaler` interface: + +```go +type duration struct { + time.Duration +} + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} +``` + +### More complex usage + +Here's an example of how to load the example from the official spec page: + +```toml +# This is a TOML document. Boom. + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +organization = "GitHub" +bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." +dob = 1979-05-27T07:32:00Z # First class dates? Why not? + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + + # You can indent as you please. Tabs or spaces. TOML don't care. + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] +``` + +And the corresponding Go types are: + +```go +type tomlConfig struct { + Title string + Owner ownerInfo + DB database `toml:"database"` + Servers map[string]server + Clients clients +} + +type ownerInfo struct { + Name string + Org string `toml:"organization"` + Bio string + DOB time.Time +} + +type database struct { + Server string + Ports []int + ConnMax int `toml:"connection_max"` + Enabled bool +} + +type server struct { + IP string + DC string +} + +type clients struct { + Data [][]interface{} + Hosts []string +} +``` + +Note that a case insensitive match will be tried if an exact match can't be +found. + +A working example of the above can be found in `_examples/example.{go,toml}`. + diff --git a/vendor/github.com/BurntSushi/toml/_examples/example.go b/vendor/github.com/BurntSushi/toml/_examples/example.go new file mode 100644 index 000000000..79f31f275 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/_examples/example.go @@ -0,0 +1,61 @@ +package main + +import ( + "fmt" + "time" + + "github.com/BurntSushi/toml" +) + +type tomlConfig struct { + Title string + Owner ownerInfo + DB database `toml:"database"` + Servers map[string]server + Clients clients +} + +type ownerInfo struct { + Name string + Org string `toml:"organization"` + Bio string + DOB time.Time +} + +type database struct { + Server string + Ports []int + ConnMax int `toml:"connection_max"` + Enabled bool +} + +type server struct { + IP string + DC string +} + +type clients struct { + Data [][]interface{} + Hosts []string +} + +func main() { + var config tomlConfig + if _, err := toml.DecodeFile("example.toml", &config); err != nil { + fmt.Println(err) + return + } + + fmt.Printf("Title: %s\n", config.Title) + fmt.Printf("Owner: %s (%s, %s), Born: %s\n", + config.Owner.Name, config.Owner.Org, config.Owner.Bio, + config.Owner.DOB) + fmt.Printf("Database: %s %v (Max conn. %d), Enabled? %v\n", + config.DB.Server, config.DB.Ports, config.DB.ConnMax, + config.DB.Enabled) + for serverName, server := range config.Servers { + fmt.Printf("Server: %s (%s, %s)\n", serverName, server.IP, server.DC) + } + fmt.Printf("Client data: %v\n", config.Clients.Data) + fmt.Printf("Client hosts: %v\n", config.Clients.Hosts) +} diff --git a/vendor/github.com/pelletier/go-toml/example.toml b/vendor/github.com/BurntSushi/toml/_examples/example.toml similarity index 89% rename from vendor/github.com/pelletier/go-toml/example.toml rename to vendor/github.com/BurntSushi/toml/_examples/example.toml index 3d902f282..32c7a4faa 100644 --- a/vendor/github.com/pelletier/go-toml/example.toml +++ b/vendor/github.com/BurntSushi/toml/_examples/example.toml @@ -27,3 +27,9 @@ enabled = true [clients] data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] diff --git a/vendor/github.com/BurntSushi/toml/_examples/hard.toml b/vendor/github.com/BurntSushi/toml/_examples/hard.toml new file mode 100644 index 000000000..26145d2b4 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/_examples/hard.toml @@ -0,0 +1,22 @@ +# Test file for TOML +# Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate +# This part you'll really hate + +[the] +test_string = "You'll hate me after this - #" # " Annoying, isn't it? + + [the.hard] + test_array = [ "] ", " # "] # ] There you go, parse this! + test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ] + # You didn't think it'd as easy as chucking out the last #, did you? + another_test_string = " Same thing, but with a string #" + harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too" + # Things will get harder + + [the.hard.bit#] + what? = "You don't think some user won't do that?" + multi_line_array = [ + "]", + # ] Oh yes I did + ] + diff --git a/vendor/github.com/BurntSushi/toml/_examples/implicit.toml b/vendor/github.com/BurntSushi/toml/_examples/implicit.toml new file mode 100644 index 000000000..1dea5ceb4 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/_examples/implicit.toml @@ -0,0 +1,4 @@ +# [x] you +# [x.y] don't +# [x.y.z] need these +[x.y.z.w] # for this to work diff --git a/vendor/github.com/BurntSushi/toml/_examples/invalid-apples.toml b/vendor/github.com/BurntSushi/toml/_examples/invalid-apples.toml new file mode 100644 index 000000000..74e9e337e --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/_examples/invalid-apples.toml @@ -0,0 +1,6 @@ +# DO NOT WANT +[fruit] +type = "apple" + +[fruit.type] +apple = "yes" diff --git a/vendor/github.com/pelletier/go-toml/example-crlf.toml b/vendor/github.com/BurntSushi/toml/_examples/invalid.toml similarity index 71% rename from vendor/github.com/pelletier/go-toml/example-crlf.toml rename to vendor/github.com/BurntSushi/toml/_examples/invalid.toml index 12950a163..beb1dba54 100644 --- a/vendor/github.com/pelletier/go-toml/example-crlf.toml +++ b/vendor/github.com/BurntSushi/toml/_examples/invalid.toml @@ -1,29 +1,35 @@ -# This is a TOML document. Boom. - -title = "TOML Example" - -[owner] -name = "Tom Preston-Werner" -organization = "GitHub" -bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." -dob = 1979-05-27T07:32:00Z # First class dates? Why not? - -[database] -server = "192.168.1.1" -ports = [ 8001, 8001, 8002 ] -connection_max = 5000 -enabled = true - -[servers] - - # You can indent as you please. Tabs or spaces. TOML don't care. - [servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc10" - -[clients] -data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it +# This is an INVALID TOML document. Boom. +# Can you spot the error without help? + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +organization = "GitHub" +bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." +dob = 1979-05-27T7:32:00Z # First class dates? Why not? + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + # You can indent as you please. Tabs or spaces. TOML don't care. + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] diff --git a/vendor/github.com/BurntSushi/toml/_examples/readme1.toml b/vendor/github.com/BurntSushi/toml/_examples/readme1.toml new file mode 100644 index 000000000..3e1261d4c --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/_examples/readme1.toml @@ -0,0 +1,5 @@ +Age = 25 +Cats = [ "Cauchy", "Plato" ] +Pi = 3.14 +Perfection = [ 6, 28, 496, 8128 ] +DOB = 1987-07-05T05:45:00Z diff --git a/vendor/github.com/BurntSushi/toml/_examples/readme2.toml b/vendor/github.com/BurntSushi/toml/_examples/readme2.toml new file mode 100644 index 000000000..b51cd9340 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/_examples/readme2.toml @@ -0,0 +1 @@ +some_key_NAME = "wat" diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING new file mode 100644 index 000000000..5a8e33254 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md new file mode 100644 index 000000000..24421eb70 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md @@ -0,0 +1,14 @@ +# Implements the TOML test suite interface + +This is an implementation of the interface expected by +[toml-test](https://github.com/BurntSushi/toml-test) for my +[toml parser written in Go](https://github.com/BurntSushi/toml). +In particular, it maps TOML data on `stdin` to a JSON format on `stdout`. + + +Compatible with TOML version +[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + +Compatible with `toml-test` version +[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0) + diff --git a/vendor/github.com/pelletier/go-toml/cmd/test_program.go b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go similarity index 60% rename from vendor/github.com/pelletier/go-toml/cmd/test_program.go rename to vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go index 73077f614..14e755700 100644 --- a/vendor/github.com/pelletier/go-toml/cmd/test_program.go +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go @@ -1,36 +1,47 @@ +// Command toml-test-decoder satisfies the toml-test interface for testing +// TOML decoders. Namely, it accepts TOML on stdin and outputs JSON on stdout. package main import ( "encoding/json" + "flag" "fmt" - "io/ioutil" "log" "os" + "path" "time" - "github.com/pelletier/go-toml" + "github.com/BurntSushi/toml" ) +func init() { + log.SetFlags(0) + + flag.Usage = usage + flag.Parse() +} + +func usage() { + log.Printf("Usage: %s < toml-file\n", path.Base(os.Args[0])) + flag.PrintDefaults() + + os.Exit(1) +} + func main() { - bytes, err := ioutil.ReadAll(os.Stdin) - if err != nil { - log.Fatalf("Error during TOML read: %s", err) - os.Exit(2) - } - tree, err := toml.Load(string(bytes)) - if err != nil { - log.Fatalf("Error during TOML load: %s", err) - os.Exit(1) + if flag.NArg() != 0 { + flag.Usage() } - typedTree := translate(*tree) + var tmp interface{} + if _, err := toml.DecodeReader(os.Stdin, &tmp); err != nil { + log.Fatalf("Error decoding TOML: %s", err) + } - if err := json.NewEncoder(os.Stdout).Encode(typedTree); err != nil { + typedTmp := translate(tmp) + if err := json.NewEncoder(os.Stdout).Encode(typedTmp); err != nil { log.Fatalf("Error encoding JSON: %s", err) - os.Exit(3) } - - os.Exit(0) } func translate(tomlData interface{}) interface{} { @@ -41,21 +52,6 @@ func translate(tomlData interface{}) interface{} { typed[k] = translate(v) } return typed - case *toml.Tree: - return translate(*orig) - case toml.Tree: - keys := orig.Keys() - typed := make(map[string]interface{}, len(keys)) - for _, k := range keys { - typed[k] = translate(orig.GetPath([]string{k})) - } - return typed - case []*toml.Tree: - typed := make([]map[string]interface{}, len(orig)) - for i, v := range orig { - typed[i] = translate(v).(map[string]interface{}) - } - return typed case []map[string]interface{}: typed := make([]map[string]interface{}, len(orig)) for i, v := range orig { @@ -67,6 +63,9 @@ func translate(tomlData interface{}) interface{} { for i, v := range orig { typed[i] = translate(v) } + + // We don't really need to tag arrays, but let's be future proof. + // (If TOML ever supports tuples, we'll need this.) return tag("array", typed) case time.Time: return tag("datetime", orig.Format("2006-01-02T15:04:05Z")) diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING new file mode 100644 index 000000000..5a8e33254 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md new file mode 100644 index 000000000..45a603f29 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md @@ -0,0 +1,14 @@ +# Implements the TOML test suite interface for TOML encoders + +This is an implementation of the interface expected by +[toml-test](https://github.com/BurntSushi/toml-test) for the +[TOML encoder](https://github.com/BurntSushi/toml). +In particular, it maps JSON data on `stdin` to a TOML format on `stdout`. + + +Compatible with TOML version +[v0.2.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.2.0.md) + +Compatible with `toml-test` version +[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0) + diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go new file mode 100644 index 000000000..092cc6844 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go @@ -0,0 +1,131 @@ +// Command toml-test-encoder satisfies the toml-test interface for testing +// TOML encoders. Namely, it accepts JSON on stdin and outputs TOML on stdout. +package main + +import ( + "encoding/json" + "flag" + "log" + "os" + "path" + "strconv" + "time" + + "github.com/BurntSushi/toml" +) + +func init() { + log.SetFlags(0) + + flag.Usage = usage + flag.Parse() +} + +func usage() { + log.Printf("Usage: %s < json-file\n", path.Base(os.Args[0])) + flag.PrintDefaults() + + os.Exit(1) +} + +func main() { + if flag.NArg() != 0 { + flag.Usage() + } + + var tmp interface{} + if err := json.NewDecoder(os.Stdin).Decode(&tmp); err != nil { + log.Fatalf("Error decoding JSON: %s", err) + } + + tomlData := translate(tmp) + if err := toml.NewEncoder(os.Stdout).Encode(tomlData); err != nil { + log.Fatalf("Error encoding TOML: %s", err) + } +} + +func translate(typedJson interface{}) interface{} { + switch v := typedJson.(type) { + case map[string]interface{}: + if len(v) == 2 && in("type", v) && in("value", v) { + return untag(v) + } + m := make(map[string]interface{}, len(v)) + for k, v2 := range v { + m[k] = translate(v2) + } + return m + case []interface{}: + tabArray := make([]map[string]interface{}, len(v)) + for i := range v { + if m, ok := translate(v[i]).(map[string]interface{}); ok { + tabArray[i] = m + } else { + log.Fatalf("JSON arrays may only contain objects. This " + + "corresponds to only tables being allowed in " + + "TOML table arrays.") + } + } + return tabArray + } + log.Fatalf("Unrecognized JSON format '%T'.", typedJson) + panic("unreachable") +} + +func untag(typed map[string]interface{}) interface{} { + t := typed["type"].(string) + v := typed["value"] + switch t { + case "string": + return v.(string) + case "integer": + v := v.(string) + n, err := strconv.Atoi(v) + if err != nil { + log.Fatalf("Could not parse '%s' as integer: %s", v, err) + } + return n + case "float": + v := v.(string) + f, err := strconv.ParseFloat(v, 64) + if err != nil { + log.Fatalf("Could not parse '%s' as float64: %s", v, err) + } + return f + case "datetime": + v := v.(string) + t, err := time.Parse("2006-01-02T15:04:05Z", v) + if err != nil { + log.Fatalf("Could not parse '%s' as a datetime: %s", v, err) + } + return t + case "bool": + v := v.(string) + switch v { + case "true": + return true + case "false": + return false + } + log.Fatalf("Could not parse '%s' as a boolean.", v) + case "array": + v := v.([]interface{}) + array := make([]interface{}, len(v)) + for i := range v { + if m, ok := v[i].(map[string]interface{}); ok { + array[i] = untag(m) + } else { + log.Fatalf("Arrays may only contain other arrays or "+ + "primitive values, but found a '%T'.", m) + } + } + return array + } + log.Fatalf("Unrecognized tag type '%s'.", t) + panic("unreachable") +} + +func in(key string, m map[string]interface{}) bool { + _, ok := m[key] + return ok +} diff --git a/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING new file mode 100644 index 000000000..5a8e33254 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/vendor/github.com/BurntSushi/toml/cmd/tomlv/README.md b/vendor/github.com/BurntSushi/toml/cmd/tomlv/README.md new file mode 100644 index 000000000..5df0dc32b --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/tomlv/README.md @@ -0,0 +1,22 @@ +# TOML Validator + +If Go is installed, it's simple to try it out: + +```bash +go get github.com/BurntSushi/toml/cmd/tomlv +tomlv some-toml-file.toml +``` + +You can see the types of every key in a TOML file with: + +```bash +tomlv -types some-toml-file.toml +``` + +At the moment, only one error message is reported at a time. Error messages +include line numbers. No output means that the files given are valid TOML, or +there is a bug in `tomlv`. + +Compatible with TOML version +[v0.1.0](https://github.com/mojombo/toml/blob/master/versions/toml-v0.1.0.md) + diff --git a/vendor/github.com/BurntSushi/toml/cmd/tomlv/main.go b/vendor/github.com/BurntSushi/toml/cmd/tomlv/main.go new file mode 100644 index 000000000..c7d689a7e --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/tomlv/main.go @@ -0,0 +1,61 @@ +// Command tomlv validates TOML documents and prints each key's type. +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path" + "strings" + "text/tabwriter" + + "github.com/BurntSushi/toml" +) + +var ( + flagTypes = false +) + +func init() { + log.SetFlags(0) + + flag.BoolVar(&flagTypes, "types", flagTypes, + "When set, the types of every defined key will be shown.") + + flag.Usage = usage + flag.Parse() +} + +func usage() { + log.Printf("Usage: %s toml-file [ toml-file ... ]\n", + path.Base(os.Args[0])) + flag.PrintDefaults() + + os.Exit(1) +} + +func main() { + if flag.NArg() < 1 { + flag.Usage() + } + for _, f := range flag.Args() { + var tmp interface{} + md, err := toml.DecodeFile(f, &tmp) + if err != nil { + log.Fatalf("Error in '%s': %s", f, err) + } + if flagTypes { + printTypes(md) + } + } +} + +func printTypes(md toml.MetaData) { + tabw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + for _, key := range md.Keys() { + fmt.Fprintf(tabw, "%s%s\t%s\n", + strings.Repeat(" ", len(key)-1), key, md.Type(key...)) + } + tabw.Flush() +} diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go new file mode 100644 index 000000000..6c7d398b8 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -0,0 +1,492 @@ +package toml + +import ( + "fmt" + "io" + "io/ioutil" + "math" + "reflect" + "strings" + "time" +) + +var e = fmt.Errorf + +// Unmarshaler is the interface implemented by objects that can unmarshal a +// TOML description of themselves. +type Unmarshaler interface { + UnmarshalTOML(interface{}) error +} + +// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. +func Unmarshal(p []byte, v interface{}) error { + _, err := Decode(string(p), v) + return err +} + +// Primitive is a TOML value that hasn't been decoded into a Go value. +// When using the various `Decode*` functions, the type `Primitive` may +// be given to any value, and its decoding will be delayed. +// +// A `Primitive` value can be decoded using the `PrimitiveDecode` function. +// +// The underlying representation of a `Primitive` value is subject to change. +// Do not rely on it. +// +// N.B. Primitive values are still parsed, so using them will only avoid +// the overhead of reflection. They can be useful when you don't know the +// exact type of TOML data until run time. +type Primitive struct { + undecoded interface{} + context Key +} + +// DEPRECATED! +// +// Use MetaData.PrimitiveDecode instead. +func PrimitiveDecode(primValue Primitive, v interface{}) error { + md := MetaData{decoded: make(map[string]bool)} + return md.unify(primValue.undecoded, rvalue(v)) +} + +// PrimitiveDecode is just like the other `Decode*` functions, except it +// decodes a TOML value that has already been parsed. Valid primitive values +// can *only* be obtained from values filled by the decoder functions, +// including this method. (i.e., `v` may contain more `Primitive` +// values.) +// +// Meta data for primitive values is included in the meta data returned by +// the `Decode*` functions with one exception: keys returned by the Undecoded +// method will only reflect keys that were decoded. Namely, any keys hidden +// behind a Primitive will be considered undecoded. Executing this method will +// update the undecoded keys in the meta data. (See the example.) +func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { + md.context = primValue.context + defer func() { md.context = nil }() + return md.unify(primValue.undecoded, rvalue(v)) +} + +// Decode will decode the contents of `data` in TOML format into a pointer +// `v`. +// +// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be +// used interchangeably.) +// +// TOML arrays of tables correspond to either a slice of structs or a slice +// of maps. +// +// TOML datetimes correspond to Go `time.Time` values. +// +// All other TOML types (float, string, int, bool and array) correspond +// to the obvious Go types. +// +// An exception to the above rules is if a type implements the +// encoding.TextUnmarshaler interface. In this case, any primitive TOML value +// (floats, strings, integers, booleans and datetimes) will be converted to +// a byte string and given to the value's UnmarshalText method. See the +// Unmarshaler example for a demonstration with time duration strings. +// +// Key mapping +// +// TOML keys can map to either keys in a Go map or field names in a Go +// struct. The special `toml` struct tag may be used to map TOML keys to +// struct fields that don't match the key name exactly. (See the example.) +// A case insensitive match to struct names will be tried if an exact match +// can't be found. +// +// The mapping between TOML values and Go values is loose. That is, there +// may exist TOML values that cannot be placed into your representation, and +// there may be parts of your representation that do not correspond to +// TOML values. This loose mapping can be made stricter by using the IsDefined +// and/or Undecoded methods on the MetaData returned. +// +// This decoder will not handle cyclic types. If a cyclic type is passed, +// `Decode` will not terminate. +func Decode(data string, v interface{}) (MetaData, error) { + p, err := parse(data) + if err != nil { + return MetaData{}, err + } + md := MetaData{ + p.mapping, p.types, p.ordered, + make(map[string]bool, len(p.ordered)), nil, + } + return md, md.unify(p.mapping, rvalue(v)) +} + +// DecodeFile is just like Decode, except it will automatically read the +// contents of the file at `fpath` and decode it for you. +func DecodeFile(fpath string, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadFile(fpath) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// DecodeReader is just like Decode, except it will consume all bytes +// from the reader and decode it for you. +func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadAll(r) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// unify performs a sort of type unification based on the structure of `rv`, +// which is the client representation. +// +// Any type mismatch produces an error. Finding a type that we don't know +// how to handle produces an unsupported type error. +func (md *MetaData) unify(data interface{}, rv reflect.Value) error { + + // Special case. Look for a `Primitive` value. + if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { + // Save the undecoded data and the key context into the primitive + // value. + context := make(Key, len(md.context)) + copy(context, md.context) + rv.Set(reflect.ValueOf(Primitive{ + undecoded: data, + context: context, + })) + return nil + } + + // Special case. Unmarshaler Interface support. + if rv.CanAddr() { + if v, ok := rv.Addr().Interface().(Unmarshaler); ok { + return v.UnmarshalTOML(data) + } + } + + // Special case. Handle time.Time values specifically. + // TODO: Remove this code when we decide to drop support for Go 1.1. + // This isn't necessary in Go 1.2 because time.Time satisfies the encoding + // interfaces. + if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) { + return md.unifyDatetime(data, rv) + } + + // Special case. Look for a value satisfying the TextUnmarshaler interface. + if v, ok := rv.Interface().(TextUnmarshaler); ok { + return md.unifyText(data, v) + } + // BUG(burntsushi) + // The behavior here is incorrect whenever a Go type satisfies the + // encoding.TextUnmarshaler interface but also corresponds to a TOML + // hash or array. In particular, the unmarshaler should only be applied + // to primitive TOML values. But at this point, it will be applied to + // all kinds of values and produce an incorrect error whenever those values + // are hashes or arrays (including arrays of tables). + + k := rv.Kind() + + // laziness + if k >= reflect.Int && k <= reflect.Uint64 { + return md.unifyInt(data, rv) + } + switch k { + case reflect.Ptr: + elem := reflect.New(rv.Type().Elem()) + err := md.unify(data, reflect.Indirect(elem)) + if err != nil { + return err + } + rv.Set(elem) + return nil + case reflect.Struct: + return md.unifyStruct(data, rv) + case reflect.Map: + return md.unifyMap(data, rv) + case reflect.Array: + return md.unifyArray(data, rv) + case reflect.Slice: + return md.unifySlice(data, rv) + case reflect.String: + return md.unifyString(data, rv) + case reflect.Bool: + return md.unifyBool(data, rv) + case reflect.Interface: + // we only support empty interfaces. + if rv.NumMethod() > 0 { + return e("Unsupported type '%s'.", rv.Kind()) + } + return md.unifyAnything(data, rv) + case reflect.Float32: + fallthrough + case reflect.Float64: + return md.unifyFloat64(data, rv) + } + return e("Unsupported type '%s'.", rv.Kind()) +} + +func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + return mismatch(rv, "map", mapping) + } + + for key, datum := range tmap { + var f *field + fields := cachedTypeFields(rv.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break + } + if f == nil && strings.EqualFold(ff.name, key) { + f = ff + } + } + if f != nil { + subv := rv + for _, i := range f.index { + subv = indirect(subv.Field(i)) + } + if isUnifiable(subv) { + md.decoded[md.context.add(key).String()] = true + md.context = append(md.context, key) + if err := md.unify(datum, subv); err != nil { + return e("Type mismatch for '%s.%s': %s", + rv.Type().String(), f.name, err) + } + md.context = md.context[0 : len(md.context)-1] + } else if f.name != "" { + // Bad user! No soup for you! + return e("Field '%s.%s' is unexported, and therefore cannot "+ + "be loaded with reflection.", rv.Type().String(), f.name) + } + } + } + return nil +} + +func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + return badtype("map", mapping) + } + if rv.IsNil() { + rv.Set(reflect.MakeMap(rv.Type())) + } + for k, v := range tmap { + md.decoded[md.context.add(k).String()] = true + md.context = append(md.context, k) + + rvkey := indirect(reflect.New(rv.Type().Key())) + rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) + if err := md.unify(v, rvval); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + + rvkey.SetString(k) + rv.SetMapIndex(rvkey, rvval) + } + return nil +} + +func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + return badtype("slice", data) + } + sliceLen := datav.Len() + if sliceLen != rv.Len() { + return e("expected array length %d; got TOML array of length %d", + rv.Len(), sliceLen) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + return badtype("slice", data) + } + sliceLen := datav.Len() + if rv.IsNil() { + rv.Set(reflect.MakeSlice(rv.Type(), sliceLen, sliceLen)) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { + sliceLen := data.Len() + for i := 0; i < sliceLen; i++ { + v := data.Index(i).Interface() + sliceval := indirect(rv.Index(i)) + if err := md.unify(v, sliceval); err != nil { + return err + } + } + return nil +} + +func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { + if _, ok := data.(time.Time); ok { + rv.Set(reflect.ValueOf(data)) + return nil + } + return badtype("time.Time", data) +} + +func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { + if s, ok := data.(string); ok { + rv.SetString(s) + return nil + } + return badtype("string", data) +} + +func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { + if num, ok := data.(float64); ok { + switch rv.Kind() { + case reflect.Float32: + fallthrough + case reflect.Float64: + rv.SetFloat(num) + default: + panic("bug") + } + return nil + } + return badtype("float", data) +} + +func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { + if num, ok := data.(int64); ok { + if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { + switch rv.Kind() { + case reflect.Int, reflect.Int64: + // No bounds checking necessary. + case reflect.Int8: + if num < math.MinInt8 || num > math.MaxInt8 { + return e("Value '%d' is out of range for int8.", num) + } + case reflect.Int16: + if num < math.MinInt16 || num > math.MaxInt16 { + return e("Value '%d' is out of range for int16.", num) + } + case reflect.Int32: + if num < math.MinInt32 || num > math.MaxInt32 { + return e("Value '%d' is out of range for int32.", num) + } + } + rv.SetInt(num) + } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { + unum := uint64(num) + switch rv.Kind() { + case reflect.Uint, reflect.Uint64: + // No bounds checking necessary. + case reflect.Uint8: + if num < 0 || unum > math.MaxUint8 { + return e("Value '%d' is out of range for uint8.", num) + } + case reflect.Uint16: + if num < 0 || unum > math.MaxUint16 { + return e("Value '%d' is out of range for uint16.", num) + } + case reflect.Uint32: + if num < 0 || unum > math.MaxUint32 { + return e("Value '%d' is out of range for uint32.", num) + } + } + rv.SetUint(unum) + } else { + panic("unreachable") + } + return nil + } + return badtype("integer", data) +} + +func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { + if b, ok := data.(bool); ok { + rv.SetBool(b) + return nil + } + return badtype("boolean", data) +} + +func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { + rv.Set(reflect.ValueOf(data)) + return nil +} + +func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error { + var s string + switch sdata := data.(type) { + case TextMarshaler: + text, err := sdata.MarshalText() + if err != nil { + return err + } + s = string(text) + case fmt.Stringer: + s = sdata.String() + case string: + s = sdata + case bool: + s = fmt.Sprintf("%v", sdata) + case int64: + s = fmt.Sprintf("%d", sdata) + case float64: + s = fmt.Sprintf("%f", sdata) + default: + return badtype("primitive (string-like)", data) + } + if err := v.UnmarshalText([]byte(s)); err != nil { + return err + } + return nil +} + +// rvalue returns a reflect.Value of `v`. All pointers are resolved. +func rvalue(v interface{}) reflect.Value { + return indirect(reflect.ValueOf(v)) +} + +// indirect returns the value pointed to by a pointer. +// Pointers are followed until the value is not a pointer. +// New values are allocated for each nil pointer. +// +// An exception to this rule is if the value satisfies an interface of +// interest to us (like encoding.TextUnmarshaler). +func indirect(v reflect.Value) reflect.Value { + if v.Kind() != reflect.Ptr { + if v.CanAddr() { + pv := v.Addr() + if _, ok := pv.Interface().(TextUnmarshaler); ok { + return pv + } + } + return v + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + return indirect(reflect.Indirect(v)) +} + +func isUnifiable(rv reflect.Value) bool { + if rv.CanSet() { + return true + } + if _, ok := rv.Interface().(TextUnmarshaler); ok { + return true + } + return false +} + +func badtype(expected string, data interface{}) error { + return e("Expected %s but found '%T'.", expected, data) +} + +func mismatch(user reflect.Value, expected string, data interface{}) error { + return e("Type mismatch for %s. Expected %s but found '%T'.", + user.Type().String(), expected, data) +} diff --git a/vendor/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/BurntSushi/toml/decode_meta.go new file mode 100644 index 000000000..ef6f545fa --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode_meta.go @@ -0,0 +1,122 @@ +package toml + +import "strings" + +// MetaData allows access to meta information about TOML data that may not +// be inferrable via reflection. In particular, whether a key has been defined +// and the TOML type of a key. +type MetaData struct { + mapping map[string]interface{} + types map[string]tomlType + keys []Key + decoded map[string]bool + context Key // Used only during decoding. +} + +// IsDefined returns true if the key given exists in the TOML data. The key +// should be specified hierarchially. e.g., +// +// // access the TOML key 'a.b.c' +// IsDefined("a", "b", "c") +// +// IsDefined will return false if an empty key given. Keys are case sensitive. +func (md *MetaData) IsDefined(key ...string) bool { + if len(key) == 0 { + return false + } + + var hash map[string]interface{} + var ok bool + var hashOrVal interface{} = md.mapping + for _, k := range key { + if hash, ok = hashOrVal.(map[string]interface{}); !ok { + return false + } + if hashOrVal, ok = hash[k]; !ok { + return false + } + } + return true +} + +// Type returns a string representation of the type of the key specified. +// +// Type will return the empty string if given an empty key or a key that +// does not exist. Keys are case sensitive. +func (md *MetaData) Type(key ...string) string { + fullkey := strings.Join(key, ".") + if typ, ok := md.types[fullkey]; ok { + return typ.typeString() + } + return "" +} + +// Key is the type of any TOML key, including key groups. Use (MetaData).Keys +// to get values of this type. +type Key []string + +func (k Key) String() string { + return strings.Join(k, ".") +} + +func (k Key) maybeQuotedAll() string { + var ss []string + for i := range k { + ss = append(ss, k.maybeQuoted(i)) + } + return strings.Join(ss, ".") +} + +func (k Key) maybeQuoted(i int) string { + quote := false + for _, c := range k[i] { + if !isBareKeyChar(c) { + quote = true + break + } + } + if quote { + return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" + } else { + return k[i] + } +} + +func (k Key) add(piece string) Key { + newKey := make(Key, len(k)+1) + copy(newKey, k) + newKey[len(k)] = piece + return newKey +} + +// Keys returns a slice of every key in the TOML data, including key groups. +// Each key is itself a slice, where the first element is the top of the +// hierarchy and the last is the most specific. +// +// The list will have the same order as the keys appeared in the TOML data. +// +// All keys returned are non-empty. +func (md *MetaData) Keys() []Key { + return md.keys +} + +// Undecoded returns all keys that have not been decoded in the order in which +// they appear in the original TOML document. +// +// This includes keys that haven't been decoded because of a Primitive value. +// Once the Primitive value is decoded, the keys will be considered decoded. +// +// Also note that decoding into an empty interface will result in no decoding, +// and so no keys will be considered decoded. +// +// In this sense, the Undecoded keys correspond to keys in the TOML document +// that do not have a concrete type in your representation. +func (md *MetaData) Undecoded() []Key { + undecoded := make([]Key, 0, len(md.keys)) + for _, key := range md.keys { + if !md.decoded[key.String()] { + undecoded = append(undecoded, key) + } + } + return undecoded +} diff --git a/vendor/github.com/BurntSushi/toml/decode_test.go b/vendor/github.com/BurntSushi/toml/decode_test.go new file mode 100644 index 000000000..3805931f2 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode_test.go @@ -0,0 +1,950 @@ +package toml + +import ( + "fmt" + "log" + "reflect" + "testing" + "time" +) + +func init() { + log.SetFlags(0) +} + +func TestDecodeSimple(t *testing.T) { + var testSimple = ` +age = 250 +andrew = "gallant" +kait = "brady" +now = 1987-07-05T05:45:00Z +yesOrNo = true +pi = 3.14 +colors = [ + ["red", "green", "blue"], + ["cyan", "magenta", "yellow", "black"], +] + +[My.Cats] +plato = "cat 1" +cauchy = "cat 2" +` + + type cats struct { + Plato string + Cauchy string + } + type simple struct { + Age int + Colors [][]string + Pi float64 + YesOrNo bool + Now time.Time + Andrew string + Kait string + My map[string]cats + } + + var val simple + _, err := Decode(testSimple, &val) + if err != nil { + t.Fatal(err) + } + + now, err := time.Parse("2006-01-02T15:04:05", "1987-07-05T05:45:00") + if err != nil { + panic(err) + } + var answer = simple{ + Age: 250, + Andrew: "gallant", + Kait: "brady", + Now: now, + YesOrNo: true, + Pi: 3.14, + Colors: [][]string{ + {"red", "green", "blue"}, + {"cyan", "magenta", "yellow", "black"}, + }, + My: map[string]cats{ + "Cats": cats{Plato: "cat 1", Cauchy: "cat 2"}, + }, + } + if !reflect.DeepEqual(val, answer) { + t.Fatalf("Expected\n-----\n%#v\n-----\nbut got\n-----\n%#v\n", + answer, val) + } +} + +func TestDecodeEmbedded(t *testing.T) { + type Dog struct{ Name string } + type Age int + + tests := map[string]struct { + input string + decodeInto interface{} + wantDecoded interface{} + }{ + "embedded struct": { + input: `Name = "milton"`, + decodeInto: &struct{ Dog }{}, + wantDecoded: &struct{ Dog }{Dog{"milton"}}, + }, + "embedded non-nil pointer to struct": { + input: `Name = "milton"`, + decodeInto: &struct{ *Dog }{}, + wantDecoded: &struct{ *Dog }{&Dog{"milton"}}, + }, + "embedded nil pointer to struct": { + input: ``, + decodeInto: &struct{ *Dog }{}, + wantDecoded: &struct{ *Dog }{nil}, + }, + "embedded int": { + input: `Age = -5`, + decodeInto: &struct{ Age }{}, + wantDecoded: &struct{ Age }{-5}, + }, + } + + for label, test := range tests { + _, err := Decode(test.input, test.decodeInto) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(test.wantDecoded, test.decodeInto) { + t.Errorf("%s: want decoded == %+v, got %+v", + label, test.wantDecoded, test.decodeInto) + } + } +} + +func TestTableArrays(t *testing.T) { + var tomlTableArrays = ` +[[albums]] +name = "Born to Run" + + [[albums.songs]] + name = "Jungleland" + + [[albums.songs]] + name = "Meeting Across the River" + +[[albums]] +name = "Born in the USA" + + [[albums.songs]] + name = "Glory Days" + + [[albums.songs]] + name = "Dancing in the Dark" +` + + type Song struct { + Name string + } + + type Album struct { + Name string + Songs []Song + } + + type Music struct { + Albums []Album + } + + expected := Music{[]Album{ + {"Born to Run", []Song{{"Jungleland"}, {"Meeting Across the River"}}}, + {"Born in the USA", []Song{{"Glory Days"}, {"Dancing in the Dark"}}}, + }} + var got Music + if _, err := Decode(tomlTableArrays, &got); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(expected, got) { + t.Fatalf("\n%#v\n!=\n%#v\n", expected, got) + } +} + +// Case insensitive matching tests. +// A bit more comprehensive than needed given the current implementation, +// but implementations change. +// Probably still missing demonstrations of some ugly corner cases regarding +// case insensitive matching and multiple fields. +func TestCase(t *testing.T) { + var caseToml = ` +tOpString = "string" +tOpInt = 1 +tOpFloat = 1.1 +tOpBool = true +tOpdate = 2006-01-02T15:04:05Z +tOparray = [ "array" ] +Match = "i should be in Match only" +MatcH = "i should be in MatcH only" +once = "just once" +[nEst.eD] +nEstedString = "another string" +` + + type InsensitiveEd struct { + NestedString string + } + + type InsensitiveNest struct { + Ed InsensitiveEd + } + + type Insensitive struct { + TopString string + TopInt int + TopFloat float64 + TopBool bool + TopDate time.Time + TopArray []string + Match string + MatcH string + Once string + OncE string + Nest InsensitiveNest + } + + tme, err := time.Parse(time.RFC3339, time.RFC3339[:len(time.RFC3339)-5]) + if err != nil { + panic(err) + } + expected := Insensitive{ + TopString: "string", + TopInt: 1, + TopFloat: 1.1, + TopBool: true, + TopDate: tme, + TopArray: []string{"array"}, + MatcH: "i should be in MatcH only", + Match: "i should be in Match only", + Once: "just once", + OncE: "", + Nest: InsensitiveNest{ + Ed: InsensitiveEd{NestedString: "another string"}, + }, + } + var got Insensitive + if _, err := Decode(caseToml, &got); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(expected, got) { + t.Fatalf("\n%#v\n!=\n%#v\n", expected, got) + } +} + +func TestPointers(t *testing.T) { + type Object struct { + Type string + Description string + } + + type Dict struct { + NamedObject map[string]*Object + BaseObject *Object + Strptr *string + Strptrs []*string + } + s1, s2, s3 := "blah", "abc", "def" + expected := &Dict{ + Strptr: &s1, + Strptrs: []*string{&s2, &s3}, + NamedObject: map[string]*Object{ + "foo": {"FOO", "fooooo!!!"}, + "bar": {"BAR", "ba-ba-ba-ba-barrrr!!!"}, + }, + BaseObject: &Object{"BASE", "da base"}, + } + + ex1 := ` +Strptr = "blah" +Strptrs = ["abc", "def"] + +[NamedObject.foo] +Type = "FOO" +Description = "fooooo!!!" + +[NamedObject.bar] +Type = "BAR" +Description = "ba-ba-ba-ba-barrrr!!!" + +[BaseObject] +Type = "BASE" +Description = "da base" +` + dict := new(Dict) + _, err := Decode(ex1, dict) + if err != nil { + t.Errorf("Decode error: %v", err) + } + if !reflect.DeepEqual(expected, dict) { + t.Fatalf("\n%#v\n!=\n%#v\n", expected, dict) + } +} + +type sphere struct { + Center [3]float64 + Radius float64 +} + +func TestDecodeSimpleArray(t *testing.T) { + var s1 sphere + if _, err := Decode(`center = [0.0, 1.5, 0.0]`, &s1); err != nil { + t.Fatal(err) + } +} + +func TestDecodeArrayWrongSize(t *testing.T) { + var s1 sphere + if _, err := Decode(`center = [0.1, 2.3]`, &s1); err == nil { + t.Fatal("Expected array type mismatch error") + } +} + +func TestDecodeLargeIntoSmallInt(t *testing.T) { + type table struct { + Value int8 + } + var tab table + if _, err := Decode(`value = 500`, &tab); err == nil { + t.Fatal("Expected integer out-of-bounds error.") + } +} + +func TestDecodeSizedInts(t *testing.T) { + type table struct { + U8 uint8 + U16 uint16 + U32 uint32 + U64 uint64 + U uint + I8 int8 + I16 int16 + I32 int32 + I64 int64 + I int + } + answer := table{1, 1, 1, 1, 1, -1, -1, -1, -1, -1} + toml := ` + u8 = 1 + u16 = 1 + u32 = 1 + u64 = 1 + u = 1 + i8 = -1 + i16 = -1 + i32 = -1 + i64 = -1 + i = -1 + ` + var tab table + if _, err := Decode(toml, &tab); err != nil { + t.Fatal(err.Error()) + } + if answer != tab { + t.Fatalf("Expected %#v but got %#v", answer, tab) + } +} + +func TestUnmarshaler(t *testing.T) { + + var tomlBlob = ` +[dishes.hamboogie] +name = "Hamboogie with fries" +price = 10.99 + +[[dishes.hamboogie.ingredients]] +name = "Bread Bun" + +[[dishes.hamboogie.ingredients]] +name = "Lettuce" + +[[dishes.hamboogie.ingredients]] +name = "Real Beef Patty" + +[[dishes.hamboogie.ingredients]] +name = "Tomato" + +[dishes.eggsalad] +name = "Egg Salad with rice" +price = 3.99 + +[[dishes.eggsalad.ingredients]] +name = "Egg" + +[[dishes.eggsalad.ingredients]] +name = "Mayo" + +[[dishes.eggsalad.ingredients]] +name = "Rice" +` + m := &menu{} + if _, err := Decode(tomlBlob, m); err != nil { + log.Fatal(err) + } + + if len(m.Dishes) != 2 { + t.Log("two dishes should be loaded with UnmarshalTOML()") + t.Errorf("expected %d but got %d", 2, len(m.Dishes)) + } + + eggSalad := m.Dishes["eggsalad"] + if _, ok := interface{}(eggSalad).(dish); !ok { + t.Errorf("expected a dish") + } + + if eggSalad.Name != "Egg Salad with rice" { + t.Errorf("expected the dish to be named 'Egg Salad with rice'") + } + + if len(eggSalad.Ingredients) != 3 { + t.Log("dish should be loaded with UnmarshalTOML()") + t.Errorf("expected %d but got %d", 3, len(eggSalad.Ingredients)) + } + + found := false + for _, i := range eggSalad.Ingredients { + if i.Name == "Rice" { + found = true + break + } + } + if !found { + t.Error("Rice was not loaded in UnmarshalTOML()") + } + + // test on a value - must be passed as * + o := menu{} + if _, err := Decode(tomlBlob, &o); err != nil { + log.Fatal(err) + } + +} + +type menu struct { + Dishes map[string]dish +} + +func (m *menu) UnmarshalTOML(p interface{}) error { + m.Dishes = make(map[string]dish) + data, _ := p.(map[string]interface{}) + dishes := data["dishes"].(map[string]interface{}) + for n, v := range dishes { + if d, ok := v.(map[string]interface{}); ok { + nd := dish{} + nd.UnmarshalTOML(d) + m.Dishes[n] = nd + } else { + return fmt.Errorf("not a dish") + } + } + return nil +} + +type dish struct { + Name string + Price float32 + Ingredients []ingredient +} + +func (d *dish) UnmarshalTOML(p interface{}) error { + data, _ := p.(map[string]interface{}) + d.Name, _ = data["name"].(string) + d.Price, _ = data["price"].(float32) + ingredients, _ := data["ingredients"].([]map[string]interface{}) + for _, e := range ingredients { + n, _ := interface{}(e).(map[string]interface{}) + name, _ := n["name"].(string) + i := ingredient{name} + d.Ingredients = append(d.Ingredients, i) + } + return nil +} + +type ingredient struct { + Name string +} + +func ExampleMetaData_PrimitiveDecode() { + var md MetaData + var err error + + var tomlBlob = ` +ranking = ["Springsteen", "J Geils"] + +[bands.Springsteen] +started = 1973 +albums = ["Greetings", "WIESS", "Born to Run", "Darkness"] + +[bands."J Geils"] +started = 1970 +albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"] +` + + type band struct { + Started int + Albums []string + } + type classics struct { + Ranking []string + Bands map[string]Primitive + } + + // Do the initial decode. Reflection is delayed on Primitive values. + var music classics + if md, err = Decode(tomlBlob, &music); err != nil { + log.Fatal(err) + } + + // MetaData still includes information on Primitive values. + fmt.Printf("Is `bands.Springsteen` defined? %v\n", + md.IsDefined("bands", "Springsteen")) + + // Decode primitive data into Go values. + for _, artist := range music.Ranking { + // A band is a primitive value, so we need to decode it to get a + // real `band` value. + primValue := music.Bands[artist] + + var aBand band + if err = md.PrimitiveDecode(primValue, &aBand); err != nil { + log.Fatal(err) + } + fmt.Printf("%s started in %d.\n", artist, aBand.Started) + } + // Check to see if there were any fields left undecoded. + // Note that this won't be empty before decoding the Primitive value! + fmt.Printf("Undecoded: %q\n", md.Undecoded()) + + // Output: + // Is `bands.Springsteen` defined? true + // Springsteen started in 1973. + // J Geils started in 1970. + // Undecoded: [] +} + +func ExampleDecode() { + var tomlBlob = ` +# Some comments. +[alpha] +ip = "10.0.0.1" + + [alpha.config] + Ports = [ 8001, 8002 ] + Location = "Toronto" + Created = 1987-07-05T05:45:00Z + +[beta] +ip = "10.0.0.2" + + [beta.config] + Ports = [ 9001, 9002 ] + Location = "New Jersey" + Created = 1887-01-05T05:55:00Z +` + + type serverConfig struct { + Ports []int + Location string + Created time.Time + } + + type server struct { + IP string `toml:"ip"` + Config serverConfig `toml:"config"` + } + + type servers map[string]server + + var config servers + if _, err := Decode(tomlBlob, &config); err != nil { + log.Fatal(err) + } + + for _, name := range []string{"alpha", "beta"} { + s := config[name] + fmt.Printf("Server: %s (ip: %s) in %s created on %s\n", + name, s.IP, s.Config.Location, + s.Config.Created.Format("2006-01-02")) + fmt.Printf("Ports: %v\n", s.Config.Ports) + } + + // Output: + // Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05 + // Ports: [8001 8002] + // Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05 + // Ports: [9001 9002] +} + +type duration struct { + time.Duration +} + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} + +// Example Unmarshaler shows how to decode TOML strings into your own +// custom data type. +func Example_unmarshaler() { + blob := ` +[[song]] +name = "Thunder Road" +duration = "4m49s" + +[[song]] +name = "Stairway to Heaven" +duration = "8m03s" +` + type song struct { + Name string + Duration duration + } + type songs struct { + Song []song + } + var favorites songs + if _, err := Decode(blob, &favorites); err != nil { + log.Fatal(err) + } + + // Code to implement the TextUnmarshaler interface for `duration`: + // + // type duration struct { + // time.Duration + // } + // + // func (d *duration) UnmarshalText(text []byte) error { + // var err error + // d.Duration, err = time.ParseDuration(string(text)) + // return err + // } + + for _, s := range favorites.Song { + fmt.Printf("%s (%s)\n", s.Name, s.Duration) + } + // Output: + // Thunder Road (4m49s) + // Stairway to Heaven (8m3s) +} + +// Example StrictDecoding shows how to detect whether there are keys in the +// TOML document that weren't decoded into the value given. This is useful +// for returning an error to the user if they've included extraneous fields +// in their configuration. +func Example_strictDecoding() { + var blob = ` +key1 = "value1" +key2 = "value2" +key3 = "value3" +` + type config struct { + Key1 string + Key3 string + } + + var conf config + md, err := Decode(blob, &conf) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Undecoded keys: %q\n", md.Undecoded()) + // Output: + // Undecoded keys: ["key2"] +} + +// Example UnmarshalTOML shows how to implement a struct type that knows how to +// unmarshal itself. The struct must take full responsibility for mapping the +// values passed into the struct. The method may be used with interfaces in a +// struct in cases where the actual type is not known until the data is +// examined. +func Example_unmarshalTOML() { + + var blob = ` +[[parts]] +type = "valve" +id = "valve-1" +size = 1.2 +rating = 4 + +[[parts]] +type = "valve" +id = "valve-2" +size = 2.1 +rating = 5 + +[[parts]] +type = "pipe" +id = "pipe-1" +length = 2.1 +diameter = 12 + +[[parts]] +type = "cable" +id = "cable-1" +length = 12 +rating = 3.1 +` + o := &order{} + err := Unmarshal([]byte(blob), o) + if err != nil { + log.Fatal(err) + } + + fmt.Println(len(o.parts)) + + for _, part := range o.parts { + fmt.Println(part.Name()) + } + + // Code to implement UmarshalJSON. + + // type order struct { + // // NOTE `order.parts` is a private slice of type `part` which is an + // // interface and may only be loaded from toml using the + // // UnmarshalTOML() method of the Umarshaler interface. + // parts parts + // } + + // func (o *order) UnmarshalTOML(data interface{}) error { + + // // NOTE the example below contains detailed type casting to show how + // // the 'data' is retrieved. In operational use, a type cast wrapper + // // may be prefered e.g. + // // + // // func AsMap(v interface{}) (map[string]interface{}, error) { + // // return v.(map[string]interface{}) + // // } + // // + // // resulting in: + // // d, _ := AsMap(data) + // // + + // d, _ := data.(map[string]interface{}) + // parts, _ := d["parts"].([]map[string]interface{}) + + // for _, p := range parts { + + // typ, _ := p["type"].(string) + // id, _ := p["id"].(string) + + // // detect the type of part and handle each case + // switch p["type"] { + // case "valve": + + // size := float32(p["size"].(float64)) + // rating := int(p["rating"].(int64)) + + // valve := &valve{ + // Type: typ, + // ID: id, + // Size: size, + // Rating: rating, + // } + + // o.parts = append(o.parts, valve) + + // case "pipe": + + // length := float32(p["length"].(float64)) + // diameter := int(p["diameter"].(int64)) + + // pipe := &pipe{ + // Type: typ, + // ID: id, + // Length: length, + // Diameter: diameter, + // } + + // o.parts = append(o.parts, pipe) + + // case "cable": + + // length := int(p["length"].(int64)) + // rating := float32(p["rating"].(float64)) + + // cable := &cable{ + // Type: typ, + // ID: id, + // Length: length, + // Rating: rating, + // } + + // o.parts = append(o.parts, cable) + + // } + // } + + // return nil + // } + + // type parts []part + + // type part interface { + // Name() string + // } + + // type valve struct { + // Type string + // ID string + // Size float32 + // Rating int + // } + + // func (v *valve) Name() string { + // return fmt.Sprintf("VALVE: %s", v.ID) + // } + + // type pipe struct { + // Type string + // ID string + // Length float32 + // Diameter int + // } + + // func (p *pipe) Name() string { + // return fmt.Sprintf("PIPE: %s", p.ID) + // } + + // type cable struct { + // Type string + // ID string + // Length int + // Rating float32 + // } + + // func (c *cable) Name() string { + // return fmt.Sprintf("CABLE: %s", c.ID) + // } + + // Output: + // 4 + // VALVE: valve-1 + // VALVE: valve-2 + // PIPE: pipe-1 + // CABLE: cable-1 + +} + +type order struct { + // NOTE `order.parts` is a private slice of type `part` which is an + // interface and may only be loaded from toml using the UnmarshalTOML() + // method of the Umarshaler interface. + parts parts +} + +func (o *order) UnmarshalTOML(data interface{}) error { + + // NOTE the example below contains detailed type casting to show how + // the 'data' is retrieved. In operational use, a type cast wrapper + // may be prefered e.g. + // + // func AsMap(v interface{}) (map[string]interface{}, error) { + // return v.(map[string]interface{}) + // } + // + // resulting in: + // d, _ := AsMap(data) + // + + d, _ := data.(map[string]interface{}) + parts, _ := d["parts"].([]map[string]interface{}) + + for _, p := range parts { + + typ, _ := p["type"].(string) + id, _ := p["id"].(string) + + // detect the type of part and handle each case + switch p["type"] { + case "valve": + + size := float32(p["size"].(float64)) + rating := int(p["rating"].(int64)) + + valve := &valve{ + Type: typ, + ID: id, + Size: size, + Rating: rating, + } + + o.parts = append(o.parts, valve) + + case "pipe": + + length := float32(p["length"].(float64)) + diameter := int(p["diameter"].(int64)) + + pipe := &pipe{ + Type: typ, + ID: id, + Length: length, + Diameter: diameter, + } + + o.parts = append(o.parts, pipe) + + case "cable": + + length := int(p["length"].(int64)) + rating := float32(p["rating"].(float64)) + + cable := &cable{ + Type: typ, + ID: id, + Length: length, + Rating: rating, + } + + o.parts = append(o.parts, cable) + + } + } + + return nil +} + +type parts []part + +type part interface { + Name() string +} + +type valve struct { + Type string + ID string + Size float32 + Rating int +} + +func (v *valve) Name() string { + return fmt.Sprintf("VALVE: %s", v.ID) +} + +type pipe struct { + Type string + ID string + Length float32 + Diameter int +} + +func (p *pipe) Name() string { + return fmt.Sprintf("PIPE: %s", p.ID) +} + +type cable struct { + Type string + ID string + Length int + Rating float32 +} + +func (c *cable) Name() string { + return fmt.Sprintf("CABLE: %s", c.ID) +} diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go new file mode 100644 index 000000000..fe2680004 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/doc.go @@ -0,0 +1,27 @@ +/* +Package toml provides facilities for decoding and encoding TOML configuration +files via reflection. There is also support for delaying decoding with +the Primitive type, and querying the set of keys in a TOML document with the +MetaData type. + +The specification implemented: https://github.com/mojombo/toml + +The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify +whether a file is a valid TOML document. It can also be used to print the +type of each key in a TOML document. + +Testing + +There are two important types of tests used for this package. The first is +contained inside '*_test.go' files and uses the standard Go unit testing +framework. These tests are primarily devoted to holistically testing the +decoder and encoder. + +The second type of testing is used to verify the implementation's adherence +to the TOML specification. These tests have been factored into their own +project: https://github.com/BurntSushi/toml-test + +The reason the tests are in a separate project is so that they can be used by +any implementation of TOML. Namely, it is language agnostic. +*/ +package toml diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go new file mode 100644 index 000000000..c7e227c7f --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -0,0 +1,551 @@ +package toml + +import ( + "bufio" + "errors" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + "time" +) + +type tomlEncodeError struct{ error } + +var ( + errArrayMixedElementTypes = errors.New( + "can't encode array with mixed element types") + errArrayNilElement = errors.New( + "can't encode array with nil element") + errNonString = errors.New( + "can't encode a map with non-string key type") + errAnonNonStruct = errors.New( + "can't encode an anonymous field that is not a struct") + errArrayNoTable = errors.New( + "TOML array element can't contain a table") + errNoKey = errors.New( + "top-level values must be a Go map or struct") + errAnything = errors.New("") // used in testing +) + +var quotedReplacer = strings.NewReplacer( + "\t", "\\t", + "\n", "\\n", + "\r", "\\r", + "\"", "\\\"", + "\\", "\\\\", +) + +// Encoder controls the encoding of Go values to a TOML document to some +// io.Writer. +// +// The indentation level can be controlled with the Indent field. +type Encoder struct { + // A single indentation level. By default it is two spaces. + Indent string + + // hasWritten is whether we have written any output to w yet. + hasWritten bool + w *bufio.Writer +} + +// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer +// given. By default, a single indentation level is 2 spaces. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: bufio.NewWriter(w), + Indent: " ", + } +} + +// Encode writes a TOML representation of the Go value to the underlying +// io.Writer. If the value given cannot be encoded to a valid TOML document, +// then an error is returned. +// +// The mapping between Go values and TOML values should be precisely the same +// as for the Decode* functions. Similarly, the TextMarshaler interface is +// supported by encoding the resulting bytes as strings. (If you want to write +// arbitrary binary data then you will need to use something like base64 since +// TOML does not have any binary types.) +// +// When encoding TOML hashes (i.e., Go maps or structs), keys without any +// sub-hashes are encoded first. +// +// If a Go map is encoded, then its keys are sorted alphabetically for +// deterministic output. More control over this behavior may be provided if +// there is demand for it. +// +// Encoding Go values without a corresponding TOML representation---like map +// types with non-string keys---will cause an error to be returned. Similarly +// for mixed arrays/slices, arrays/slices with nil elements, embedded +// non-struct types and nested slices containing maps or structs. +// (e.g., [][]map[string]string is not allowed but []map[string]string is OK +// and so is []map[string][]string.) +func (enc *Encoder) Encode(v interface{}) error { + rv := eindirect(reflect.ValueOf(v)) + if err := enc.safeEncode(Key([]string{}), rv); err != nil { + return err + } + return enc.w.Flush() +} + +func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { + defer func() { + if r := recover(); r != nil { + if terr, ok := r.(tomlEncodeError); ok { + err = terr.error + return + } + panic(r) + } + }() + enc.encode(key, rv) + return nil +} + +func (enc *Encoder) encode(key Key, rv reflect.Value) { + // Special case. Time needs to be in ISO8601 format. + // Special case. If we can marshal the type to text, then we used that. + // Basically, this prevents the encoder for handling these types as + // generic structs (or whatever the underlying type of a TextMarshaler is). + switch rv.Interface().(type) { + case time.Time, TextMarshaler: + enc.keyEqElement(key, rv) + return + } + + k := rv.Kind() + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: + enc.keyEqElement(key, rv) + case reflect.Array, reflect.Slice: + if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { + enc.eArrayOfTables(key, rv) + } else { + enc.keyEqElement(key, rv) + } + case reflect.Interface: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Map: + if rv.IsNil() { + return + } + enc.eTable(key, rv) + case reflect.Ptr: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Struct: + enc.eTable(key, rv) + default: + panic(e("Unsupported type for key '%s': %s", key, k)) + } +} + +// eElement encodes any value that can be an array element (primitives and +// arrays). +func (enc *Encoder) eElement(rv reflect.Value) { + switch v := rv.Interface().(type) { + case time.Time: + // Special case time.Time as a primitive. Has to come before + // TextMarshaler below because time.Time implements + // encoding.TextMarshaler, but we need to always use UTC. + enc.wf(v.In(time.FixedZone("UTC", 0)).Format("2006-01-02T15:04:05Z")) + return + case TextMarshaler: + // Special case. Use text marshaler if it's available for this value. + if s, err := v.MarshalText(); err != nil { + encPanic(err) + } else { + enc.writeQuoted(string(s)) + } + return + } + switch rv.Kind() { + case reflect.Bool: + enc.wf(strconv.FormatBool(rv.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + enc.wf(strconv.FormatInt(rv.Int(), 10)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64: + enc.wf(strconv.FormatUint(rv.Uint(), 10)) + case reflect.Float32: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32))) + case reflect.Float64: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64))) + case reflect.Array, reflect.Slice: + enc.eArrayOrSliceElement(rv) + case reflect.Interface: + enc.eElement(rv.Elem()) + case reflect.String: + enc.writeQuoted(rv.String()) + default: + panic(e("Unexpected primitive type: %s", rv.Kind())) + } +} + +// By the TOML spec, all floats must have a decimal with at least one +// number on either side. +func floatAddDecimal(fstr string) string { + if !strings.Contains(fstr, ".") { + return fstr + ".0" + } + return fstr +} + +func (enc *Encoder) writeQuoted(s string) { + enc.wf("\"%s\"", quotedReplacer.Replace(s)) +} + +func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { + length := rv.Len() + enc.wf("[") + for i := 0; i < length; i++ { + elem := rv.Index(i) + enc.eElement(elem) + if i != length-1 { + enc.wf(", ") + } + } + enc.wf("]") +} + +func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + for i := 0; i < rv.Len(); i++ { + trv := rv.Index(i) + if isNil(trv) { + continue + } + panicIfInvalidKey(key) + enc.newline() + enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + enc.eMapOrStruct(key, trv) + } +} + +func (enc *Encoder) eTable(key Key, rv reflect.Value) { + panicIfInvalidKey(key) + if len(key) == 1 { + // Output an extra new line between top-level tables. + // (The newline isn't written if nothing else has been written though.) + enc.newline() + } + if len(key) > 0 { + enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + } + enc.eMapOrStruct(key, rv) +} + +func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) { + switch rv := eindirect(rv); rv.Kind() { + case reflect.Map: + enc.eMap(key, rv) + case reflect.Struct: + enc.eStruct(key, rv) + default: + panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) + } +} + +func (enc *Encoder) eMap(key Key, rv reflect.Value) { + rt := rv.Type() + if rt.Key().Kind() != reflect.String { + encPanic(errNonString) + } + + // Sort keys so that we have deterministic output. And write keys directly + // underneath this key first, before writing sub-structs or sub-maps. + var mapKeysDirect, mapKeysSub []string + for _, mapKey := range rv.MapKeys() { + k := mapKey.String() + if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { + mapKeysSub = append(mapKeysSub, k) + } else { + mapKeysDirect = append(mapKeysDirect, k) + } + } + + var writeMapKeys = func(mapKeys []string) { + sort.Strings(mapKeys) + for _, mapKey := range mapKeys { + mrv := rv.MapIndex(reflect.ValueOf(mapKey)) + if isNil(mrv) { + // Don't write anything for nil fields. + continue + } + enc.encode(key.add(mapKey), mrv) + } + } + writeMapKeys(mapKeysDirect) + writeMapKeys(mapKeysSub) +} + +func (enc *Encoder) eStruct(key Key, rv reflect.Value) { + // Write keys for fields directly under this key first, because if we write + // a field that creates a new table, then all keys under it will be in that + // table (not the one we're writing here). + rt := rv.Type() + var fieldsDirect, fieldsSub [][]int + var addFields func(rt reflect.Type, rv reflect.Value, start []int) + addFields = func(rt reflect.Type, rv reflect.Value, start []int) { + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + // skip unexporded fields + if f.PkgPath != "" { + continue + } + frv := rv.Field(i) + if f.Anonymous { + frv := eindirect(frv) + t := frv.Type() + if t.Kind() != reflect.Struct { + encPanic(errAnonNonStruct) + } + addFields(t, frv, f.Index) + } else if typeIsHash(tomlTypeOfGo(frv)) { + fieldsSub = append(fieldsSub, append(start, f.Index...)) + } else { + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + } + } + } + addFields(rt, rv, nil) + + var writeFields = func(fields [][]int) { + for _, fieldIndex := range fields { + sft := rt.FieldByIndex(fieldIndex) + sf := rv.FieldByIndex(fieldIndex) + if isNil(sf) { + // Don't write anything for nil fields. + continue + } + + keyName := sft.Tag.Get("toml") + if keyName == "-" { + continue + } + if keyName == "" { + keyName = sft.Name + } + + keyName, opts := getOptions(keyName) + if _, ok := opts["omitempty"]; ok && isEmpty(sf) { + continue + } else if _, ok := opts["omitzero"]; ok && isZero(sf) { + continue + } + + enc.encode(key.add(keyName), sf) + } + } + writeFields(fieldsDirect) + writeFields(fieldsSub) +} + +// tomlTypeName returns the TOML type name of the Go value's type. It is +// used to determine whether the types of array elements are mixed (which is +// forbidden). If the Go value is nil, then it is illegal for it to be an array +// element, and valueIsNil is returned as true. + +// Returns the TOML type of a Go value. The type may be `nil`, which means +// no concrete TOML type could be found. +func tomlTypeOfGo(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() { + return nil + } + switch rv.Kind() { + case reflect.Bool: + return tomlBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + return tomlInteger + case reflect.Float32, reflect.Float64: + return tomlFloat + case reflect.Array, reflect.Slice: + if typeEqual(tomlHash, tomlArrayType(rv)) { + return tomlArrayHash + } else { + return tomlArray + } + case reflect.Ptr, reflect.Interface: + return tomlTypeOfGo(rv.Elem()) + case reflect.String: + return tomlString + case reflect.Map: + return tomlHash + case reflect.Struct: + switch rv.Interface().(type) { + case time.Time: + return tomlDatetime + case TextMarshaler: + return tomlString + default: + return tomlHash + } + default: + panic("unexpected reflect.Kind: " + rv.Kind().String()) + } +} + +// tomlArrayType returns the element type of a TOML array. The type returned +// may be nil if it cannot be determined (e.g., a nil slice or a zero length +// slize). This function may also panic if it finds a type that cannot be +// expressed in TOML (such as nil elements, heterogeneous arrays or directly +// nested arrays of tables). +func tomlArrayType(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { + return nil + } + firstType := tomlTypeOfGo(rv.Index(0)) + if firstType == nil { + encPanic(errArrayNilElement) + } + + rvlen := rv.Len() + for i := 1; i < rvlen; i++ { + elem := rv.Index(i) + switch elemType := tomlTypeOfGo(elem); { + case elemType == nil: + encPanic(errArrayNilElement) + case !typeEqual(firstType, elemType): + encPanic(errArrayMixedElementTypes) + } + } + // If we have a nested array, then we must make sure that the nested + // array contains ONLY primitives. + // This checks arbitrarily nested arrays. + if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) { + nest := tomlArrayType(eindirect(rv.Index(0))) + if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) { + encPanic(errArrayNoTable) + } + } + return firstType +} + +func getOptions(keyName string) (string, map[string]struct{}) { + opts := make(map[string]struct{}) + ss := strings.Split(keyName, ",") + name := ss[0] + if len(ss) > 1 { + for _, opt := range ss { + opts[opt] = struct{}{} + } + } + + return name, opts +} + +func isZero(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if rv.Int() == 0 { + return true + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if rv.Uint() == 0 { + return true + } + case reflect.Float32, reflect.Float64: + if rv.Float() == 0.0 { + return true + } + } + + return false +} + +func isEmpty(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.String: + if len(strings.TrimSpace(rv.String())) == 0 { + return true + } + case reflect.Array, reflect.Slice, reflect.Map: + if rv.Len() == 0 { + return true + } + } + + return false +} + +func (enc *Encoder) newline() { + if enc.hasWritten { + enc.wf("\n") + } +} + +func (enc *Encoder) keyEqElement(key Key, val reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + panicIfInvalidKey(key) + enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) + enc.eElement(val) + enc.newline() +} + +func (enc *Encoder) wf(format string, v ...interface{}) { + if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { + encPanic(err) + } + enc.hasWritten = true +} + +func (enc *Encoder) indentStr(key Key) string { + return strings.Repeat(enc.Indent, len(key)-1) +} + +func encPanic(err error) { + panic(tomlEncodeError{err}) +} + +func eindirect(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + return eindirect(v.Elem()) + default: + return v + } +} + +func isNil(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return rv.IsNil() + default: + return false + } +} + +func panicIfInvalidKey(key Key) { + for _, k := range key { + if len(k) == 0 { + encPanic(e("Key '%s' is not a valid table name. Key names "+ + "cannot be empty.", key.maybeQuotedAll())) + } + } +} + +func isValidKeyName(s string) bool { + return len(s) != 0 +} diff --git a/vendor/github.com/BurntSushi/toml/encode_test.go b/vendor/github.com/BurntSushi/toml/encode_test.go new file mode 100644 index 000000000..445ca8e21 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encode_test.go @@ -0,0 +1,542 @@ +package toml + +import ( + "bytes" + "fmt" + "log" + "net" + "testing" + "time" +) + +func TestEncodeRoundTrip(t *testing.T) { + type Config struct { + Age int + Cats []string + Pi float64 + Perfection []int + DOB time.Time + Ipaddress net.IP + } + + var inputs = Config{ + 13, + []string{"one", "two", "three"}, + 3.145, + []int{11, 2, 3, 4}, + time.Now(), + net.ParseIP("192.168.59.254"), + } + + var firstBuffer bytes.Buffer + e := NewEncoder(&firstBuffer) + err := e.Encode(inputs) + if err != nil { + t.Fatal(err) + } + var outputs Config + if _, err := Decode(firstBuffer.String(), &outputs); err != nil { + log.Printf("Could not decode:\n-----\n%s\n-----\n", + firstBuffer.String()) + t.Fatal(err) + } + + // could test each value individually, but I'm lazy + var secondBuffer bytes.Buffer + e2 := NewEncoder(&secondBuffer) + err = e2.Encode(outputs) + if err != nil { + t.Fatal(err) + } + if firstBuffer.String() != secondBuffer.String() { + t.Error( + firstBuffer.String(), + "\n\n is not identical to\n\n", + secondBuffer.String()) + } +} + +// XXX(burntsushi) +// I think these tests probably should be removed. They are good, but they +// ought to be obsolete by toml-test. +func TestEncode(t *testing.T) { + type Embedded struct { + Int int `toml:"_int"` + } + type NonStruct int + + date := time.Date(2014, 5, 11, 20, 30, 40, 0, time.FixedZone("IST", 3600)) + dateStr := "2014-05-11T19:30:40Z" + + tests := map[string]struct { + input interface{} + wantOutput string + wantError error + }{ + "bool field": { + input: struct { + BoolTrue bool + BoolFalse bool + }{true, false}, + wantOutput: "BoolTrue = true\nBoolFalse = false\n", + }, + "int fields": { + input: struct { + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + }{1, 2, 3, 4, 5}, + wantOutput: "Int = 1\nInt8 = 2\nInt16 = 3\nInt32 = 4\nInt64 = 5\n", + }, + "uint fields": { + input: struct { + Uint uint + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + }{1, 2, 3, 4, 5}, + wantOutput: "Uint = 1\nUint8 = 2\nUint16 = 3\nUint32 = 4" + + "\nUint64 = 5\n", + }, + "float fields": { + input: struct { + Float32 float32 + Float64 float64 + }{1.5, 2.5}, + wantOutput: "Float32 = 1.5\nFloat64 = 2.5\n", + }, + "string field": { + input: struct{ String string }{"foo"}, + wantOutput: "String = \"foo\"\n", + }, + "string field and unexported field": { + input: struct { + String string + unexported int + }{"foo", 0}, + wantOutput: "String = \"foo\"\n", + }, + "datetime field in UTC": { + input: struct{ Date time.Time }{date}, + wantOutput: fmt.Sprintf("Date = %s\n", dateStr), + }, + "datetime field as primitive": { + // Using a map here to fail if isStructOrMap() returns true for + // time.Time. + input: map[string]interface{}{ + "Date": date, + "Int": 1, + }, + wantOutput: fmt.Sprintf("Date = %s\nInt = 1\n", dateStr), + }, + "array fields": { + input: struct { + IntArray0 [0]int + IntArray3 [3]int + }{[0]int{}, [3]int{1, 2, 3}}, + wantOutput: "IntArray0 = []\nIntArray3 = [1, 2, 3]\n", + }, + "slice fields": { + input: struct{ IntSliceNil, IntSlice0, IntSlice3 []int }{ + nil, []int{}, []int{1, 2, 3}, + }, + wantOutput: "IntSlice0 = []\nIntSlice3 = [1, 2, 3]\n", + }, + "datetime slices": { + input: struct{ DatetimeSlice []time.Time }{ + []time.Time{date, date}, + }, + wantOutput: fmt.Sprintf("DatetimeSlice = [%s, %s]\n", + dateStr, dateStr), + }, + "nested arrays and slices": { + input: struct { + SliceOfArrays [][2]int + ArrayOfSlices [2][]int + SliceOfArraysOfSlices [][2][]int + ArrayOfSlicesOfArrays [2][][2]int + SliceOfMixedArrays [][2]interface{} + ArrayOfMixedSlices [2][]interface{} + }{ + [][2]int{{1, 2}, {3, 4}}, + [2][]int{{1, 2}, {3, 4}}, + [][2][]int{ + { + {1, 2}, {3, 4}, + }, + { + {5, 6}, {7, 8}, + }, + }, + [2][][2]int{ + { + {1, 2}, {3, 4}, + }, + { + {5, 6}, {7, 8}, + }, + }, + [][2]interface{}{ + {1, 2}, {"a", "b"}, + }, + [2][]interface{}{ + {1, 2}, {"a", "b"}, + }, + }, + wantOutput: `SliceOfArrays = [[1, 2], [3, 4]] +ArrayOfSlices = [[1, 2], [3, 4]] +SliceOfArraysOfSlices = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] +ArrayOfSlicesOfArrays = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] +SliceOfMixedArrays = [[1, 2], ["a", "b"]] +ArrayOfMixedSlices = [[1, 2], ["a", "b"]] +`, + }, + "empty slice": { + input: struct{ Empty []interface{} }{[]interface{}{}}, + wantOutput: "Empty = []\n", + }, + "(error) slice with element type mismatch (string and integer)": { + input: struct{ Mixed []interface{} }{[]interface{}{1, "a"}}, + wantError: errArrayMixedElementTypes, + }, + "(error) slice with element type mismatch (integer and float)": { + input: struct{ Mixed []interface{} }{[]interface{}{1, 2.5}}, + wantError: errArrayMixedElementTypes, + }, + "slice with elems of differing Go types, same TOML types": { + input: struct { + MixedInts []interface{} + MixedFloats []interface{} + }{ + []interface{}{ + int(1), int8(2), int16(3), int32(4), int64(5), + uint(1), uint8(2), uint16(3), uint32(4), uint64(5), + }, + []interface{}{float32(1.5), float64(2.5)}, + }, + wantOutput: "MixedInts = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]\n" + + "MixedFloats = [1.5, 2.5]\n", + }, + "(error) slice w/ element type mismatch (one is nested array)": { + input: struct{ Mixed []interface{} }{ + []interface{}{1, []interface{}{2}}, + }, + wantError: errArrayMixedElementTypes, + }, + "(error) slice with 1 nil element": { + input: struct{ NilElement1 []interface{} }{[]interface{}{nil}}, + wantError: errArrayNilElement, + }, + "(error) slice with 1 nil element (and other non-nil elements)": { + input: struct{ NilElement []interface{} }{ + []interface{}{1, nil}, + }, + wantError: errArrayNilElement, + }, + "simple map": { + input: map[string]int{"a": 1, "b": 2}, + wantOutput: "a = 1\nb = 2\n", + }, + "map with interface{} value type": { + input: map[string]interface{}{"a": 1, "b": "c"}, + wantOutput: "a = 1\nb = \"c\"\n", + }, + "map with interface{} value type, some of which are structs": { + input: map[string]interface{}{ + "a": struct{ Int int }{2}, + "b": 1, + }, + wantOutput: "b = 1\n\n[a]\n Int = 2\n", + }, + "nested map": { + input: map[string]map[string]int{ + "a": {"b": 1}, + "c": {"d": 2}, + }, + wantOutput: "[a]\n b = 1\n\n[c]\n d = 2\n", + }, + "nested struct": { + input: struct{ Struct struct{ Int int } }{ + struct{ Int int }{1}, + }, + wantOutput: "[Struct]\n Int = 1\n", + }, + "nested struct and non-struct field": { + input: struct { + Struct struct{ Int int } + Bool bool + }{struct{ Int int }{1}, true}, + wantOutput: "Bool = true\n\n[Struct]\n Int = 1\n", + }, + "2 nested structs": { + input: struct{ Struct1, Struct2 struct{ Int int } }{ + struct{ Int int }{1}, struct{ Int int }{2}, + }, + wantOutput: "[Struct1]\n Int = 1\n\n[Struct2]\n Int = 2\n", + }, + "deeply nested structs": { + input: struct { + Struct1, Struct2 struct{ Struct3 *struct{ Int int } } + }{ + struct{ Struct3 *struct{ Int int } }{&struct{ Int int }{1}}, + struct{ Struct3 *struct{ Int int } }{nil}, + }, + wantOutput: "[Struct1]\n [Struct1.Struct3]\n Int = 1" + + "\n\n[Struct2]\n", + }, + "nested struct with nil struct elem": { + input: struct { + Struct struct{ Inner *struct{ Int int } } + }{ + struct{ Inner *struct{ Int int } }{nil}, + }, + wantOutput: "[Struct]\n", + }, + "nested struct with no fields": { + input: struct { + Struct struct{ Inner struct{} } + }{ + struct{ Inner struct{} }{struct{}{}}, + }, + wantOutput: "[Struct]\n [Struct.Inner]\n", + }, + "struct with tags": { + input: struct { + Struct struct { + Int int `toml:"_int"` + } `toml:"_struct"` + Bool bool `toml:"_bool"` + }{ + struct { + Int int `toml:"_int"` + }{1}, true, + }, + wantOutput: "_bool = true\n\n[_struct]\n _int = 1\n", + }, + "embedded struct": { + input: struct{ Embedded }{Embedded{1}}, + wantOutput: "_int = 1\n", + }, + "embedded *struct": { + input: struct{ *Embedded }{&Embedded{1}}, + wantOutput: "_int = 1\n", + }, + "nested embedded struct": { + input: struct { + Struct struct{ Embedded } `toml:"_struct"` + }{struct{ Embedded }{Embedded{1}}}, + wantOutput: "[_struct]\n _int = 1\n", + }, + "nested embedded *struct": { + input: struct { + Struct struct{ *Embedded } `toml:"_struct"` + }{struct{ *Embedded }{&Embedded{1}}}, + wantOutput: "[_struct]\n _int = 1\n", + }, + "array of tables": { + input: struct { + Structs []*struct{ Int int } `toml:"struct"` + }{ + []*struct{ Int int }{{1}, {3}}, + }, + wantOutput: "[[struct]]\n Int = 1\n\n[[struct]]\n Int = 3\n", + }, + "array of tables order": { + input: map[string]interface{}{ + "map": map[string]interface{}{ + "zero": 5, + "arr": []map[string]int{ + map[string]int{ + "friend": 5, + }, + }, + }, + }, + wantOutput: "[map]\n zero = 5\n\n [[map.arr]]\n friend = 5\n", + }, + "(error) top-level slice": { + input: []struct{ Int int }{{1}, {2}, {3}}, + wantError: errNoKey, + }, + "(error) slice of slice": { + input: struct { + Slices [][]struct{ Int int } + }{ + [][]struct{ Int int }{{{1}}, {{2}}, {{3}}}, + }, + wantError: errArrayNoTable, + }, + "(error) map no string key": { + input: map[int]string{1: ""}, + wantError: errNonString, + }, + "(error) anonymous non-struct": { + input: struct{ NonStruct }{5}, + wantError: errAnonNonStruct, + }, + "(error) empty key name": { + input: map[string]int{"": 1}, + wantError: errAnything, + }, + "(error) empty map name": { + input: map[string]interface{}{ + "": map[string]int{"v": 1}, + }, + wantError: errAnything, + }, + } + for label, test := range tests { + encodeExpected(t, label, test.input, test.wantOutput, test.wantError) + } +} + +func TestEncodeNestedTableArrays(t *testing.T) { + type song struct { + Name string `toml:"name"` + } + type album struct { + Name string `toml:"name"` + Songs []song `toml:"songs"` + } + type springsteen struct { + Albums []album `toml:"albums"` + } + value := springsteen{ + []album{ + {"Born to Run", + []song{{"Jungleland"}, {"Meeting Across the River"}}}, + {"Born in the USA", + []song{{"Glory Days"}, {"Dancing in the Dark"}}}, + }, + } + expected := `[[albums]] + name = "Born to Run" + + [[albums.songs]] + name = "Jungleland" + + [[albums.songs]] + name = "Meeting Across the River" + +[[albums]] + name = "Born in the USA" + + [[albums.songs]] + name = "Glory Days" + + [[albums.songs]] + name = "Dancing in the Dark" +` + encodeExpected(t, "nested table arrays", value, expected, nil) +} + +func TestEncodeArrayHashWithNormalHashOrder(t *testing.T) { + type Alpha struct { + V int + } + type Beta struct { + V int + } + type Conf struct { + V int + A Alpha + B []Beta + } + + val := Conf{ + V: 1, + A: Alpha{2}, + B: []Beta{{3}}, + } + expected := "V = 1\n\n[A]\n V = 2\n\n[[B]]\n V = 3\n" + encodeExpected(t, "array hash with normal hash order", val, expected, nil) +} + +func TestEncodeWithOmitEmpty(t *testing.T) { + type simple struct { + User string `toml:"user"` + Pass string `toml:"password,omitempty"` + } + + value := simple{"Testing", ""} + expected := fmt.Sprintf("user = %q\n", value.User) + encodeExpected(t, "simple with omitempty, is empty", value, expected, nil) + value.Pass = "some password" + expected = fmt.Sprintf("user = %q\npassword = %q\n", value.User, value.Pass) + encodeExpected(t, "simple with omitempty, not empty", value, expected, nil) +} + +func TestEncodeWithOmitZero(t *testing.T) { + type simple struct { + Number int `toml:"number,omitzero"` + Real float64 `toml:"real,omitzero"` + Unsigned uint `toml:"unsigned,omitzero"` + } + + value := simple{0, 0.0, uint(0)} + expected := "" + + encodeExpected(t, "simple with omitzero, all zero", value, expected, nil) + + value.Number = 10 + value.Real = 20 + value.Unsigned = 5 + expected = `number = 10 +real = 20.0 +unsigned = 5 +` + encodeExpected(t, "simple with omitzero, non-zero", value, expected, nil) +} + +func encodeExpected( + t *testing.T, label string, val interface{}, wantStr string, wantErr error, +) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + err := enc.Encode(val) + if err != wantErr { + if wantErr != nil { + if wantErr == errAnything && err != nil { + return + } + t.Errorf("%s: want Encode error %v, got %v", label, wantErr, err) + } else { + t.Errorf("%s: Encode failed: %s", label, err) + } + } + if err != nil { + return + } + if got := buf.String(); wantStr != got { + t.Errorf("%s: want\n-----\n%q\n-----\nbut got\n-----\n%q\n-----\n", + label, wantStr, got) + } +} + +func ExampleEncoder_Encode() { + date, _ := time.Parse(time.RFC822, "14 Mar 10 18:00 UTC") + var config = map[string]interface{}{ + "date": date, + "counts": []int{1, 1, 2, 3, 5, 8}, + "hash": map[string]string{ + "key1": "val1", + "key2": "val2", + }, + } + buf := new(bytes.Buffer) + if err := NewEncoder(buf).Encode(config); err != nil { + log.Fatal(err) + } + fmt.Println(buf.String()) + + // Output: + // counts = [1, 1, 2, 3, 5, 8] + // date = 2010-03-14T18:00:00Z + // + // [hash] + // key1 = "val1" + // key2 = "val2" +} diff --git a/vendor/github.com/BurntSushi/toml/encoding_types.go b/vendor/github.com/BurntSushi/toml/encoding_types.go new file mode 100644 index 000000000..d36e1dd60 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encoding_types.go @@ -0,0 +1,19 @@ +// +build go1.2 + +package toml + +// In order to support Go 1.1, we define our own TextMarshaler and +// TextUnmarshaler types. For Go 1.2+, we just alias them with the +// standard library interfaces. + +import ( + "encoding" +) + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler encoding.TextMarshaler + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler encoding.TextUnmarshaler diff --git a/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go new file mode 100644 index 000000000..e8d503d04 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go @@ -0,0 +1,18 @@ +// +build !go1.2 + +package toml + +// These interfaces were introduced in Go 1.2, so we add them manually when +// compiling for Go 1.1. + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go new file mode 100644 index 000000000..08dce77ba --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -0,0 +1,872 @@ +package toml + +import ( + "fmt" + "strings" + "unicode/utf8" +) + +type itemType int + +const ( + itemError itemType = iota + itemNIL // used in the parser to indicate no type + itemEOF + itemText + itemString + itemRawString + itemMultilineString + itemRawMultilineString + itemBool + itemInteger + itemFloat + itemDatetime + itemArray // the start of an array + itemArrayEnd + itemTableStart + itemTableEnd + itemArrayTableStart + itemArrayTableEnd + itemKeyStart + itemCommentStart +) + +const ( + eof = 0 + tableStart = '[' + tableEnd = ']' + arrayTableStart = '[' + arrayTableEnd = ']' + tableSep = '.' + keySep = '=' + arrayStart = '[' + arrayEnd = ']' + arrayValTerm = ',' + commentStart = '#' + stringStart = '"' + stringEnd = '"' + rawStringStart = '\'' + rawStringEnd = '\'' +) + +type stateFn func(lx *lexer) stateFn + +type lexer struct { + input string + start int + pos int + width int + line int + state stateFn + items chan item + + // A stack of state functions used to maintain context. + // The idea is to reuse parts of the state machine in various places. + // For example, values can appear at the top level or within arbitrarily + // nested arrays. The last state on the stack is used after a value has + // been lexed. Similarly for comments. + stack []stateFn +} + +type item struct { + typ itemType + val string + line int +} + +func (lx *lexer) nextItem() item { + for { + select { + case item := <-lx.items: + return item + default: + lx.state = lx.state(lx) + } + } +} + +func lex(input string) *lexer { + lx := &lexer{ + input: input + "\n", + state: lexTop, + line: 1, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + } + return lx +} + +func (lx *lexer) push(state stateFn) { + lx.stack = append(lx.stack, state) +} + +func (lx *lexer) pop() stateFn { + if len(lx.stack) == 0 { + return lx.errorf("BUG in lexer: no states to pop.") + } + last := lx.stack[len(lx.stack)-1] + lx.stack = lx.stack[0 : len(lx.stack)-1] + return last +} + +func (lx *lexer) current() string { + return lx.input[lx.start:lx.pos] +} + +func (lx *lexer) emit(typ itemType) { + lx.items <- item{typ, lx.current(), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) emitTrim(typ itemType) { + lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) next() (r rune) { + if lx.pos >= len(lx.input) { + lx.width = 0 + return eof + } + + if lx.input[lx.pos] == '\n' { + lx.line++ + } + r, lx.width = utf8.DecodeRuneInString(lx.input[lx.pos:]) + lx.pos += lx.width + return r +} + +// ignore skips over the pending input before this point. +func (lx *lexer) ignore() { + lx.start = lx.pos +} + +// backup steps back one rune. Can be called only once per call of next. +func (lx *lexer) backup() { + lx.pos -= lx.width + if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { + lx.line-- + } +} + +// accept consumes the next rune if it's equal to `valid`. +func (lx *lexer) accept(valid rune) bool { + if lx.next() == valid { + return true + } + lx.backup() + return false +} + +// peek returns but does not consume the next rune in the input. +func (lx *lexer) peek() rune { + r := lx.next() + lx.backup() + return r +} + +// errorf stops all lexing by emitting an error and returning `nil`. +// Note that any value that is a character is escaped if it's a special +// character (new lines, tabs, etc.). +func (lx *lexer) errorf(format string, values ...interface{}) stateFn { + lx.items <- item{ + itemError, + fmt.Sprintf(format, values...), + lx.line, + } + return nil +} + +// lexTop consumes elements at the top level of TOML data. +func lexTop(lx *lexer) stateFn { + r := lx.next() + if isWhitespace(r) || isNL(r) { + return lexSkip(lx, lexTop) + } + + switch r { + case commentStart: + lx.push(lexTop) + return lexCommentStart + case tableStart: + return lexTableStart + case eof: + if lx.pos > lx.start { + return lx.errorf("Unexpected EOF.") + } + lx.emit(itemEOF) + return nil + } + + // At this point, the only valid item can be a key, so we back up + // and let the key lexer do the rest. + lx.backup() + lx.push(lexTopEnd) + return lexKeyStart +} + +// lexTopEnd is entered whenever a top-level item has been consumed. (A value +// or a table.) It must see only whitespace, and will turn back to lexTop +// upon a new line. If it sees EOF, it will quit the lexer successfully. +func lexTopEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case r == commentStart: + // a comment will read to a new line for us. + lx.push(lexTop) + return lexCommentStart + case isWhitespace(r): + return lexTopEnd + case isNL(r): + lx.ignore() + return lexTop + case r == eof: + lx.ignore() + return lexTop + } + return lx.errorf("Expected a top-level item to end with a new line, "+ + "comment or EOF, but got %q instead.", r) +} + +// lexTable lexes the beginning of a table. Namely, it makes sure that +// it starts with a character other than '.' and ']'. +// It assumes that '[' has already been consumed. +// It also handles the case that this is an item in an array of tables. +// e.g., '[[name]]'. +func lexTableStart(lx *lexer) stateFn { + if lx.peek() == arrayTableStart { + lx.next() + lx.emit(itemArrayTableStart) + lx.push(lexArrayTableEnd) + } else { + lx.emit(itemTableStart) + lx.push(lexTableEnd) + } + return lexTableNameStart +} + +func lexTableEnd(lx *lexer) stateFn { + lx.emit(itemTableEnd) + return lexTopEnd +} + +func lexArrayTableEnd(lx *lexer) stateFn { + if r := lx.next(); r != arrayTableEnd { + return lx.errorf("Expected end of table array name delimiter %q, "+ + "but got %q instead.", arrayTableEnd, r) + } + lx.emit(itemArrayTableEnd) + return lexTopEnd +} + +func lexTableNameStart(lx *lexer) stateFn { + switch r := lx.peek(); { + case r == tableEnd || r == eof: + return lx.errorf("Unexpected end of table name. (Table names cannot " + + "be empty.)") + case r == tableSep: + return lx.errorf("Unexpected table separator. (Table names cannot " + + "be empty.)") + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.push(lexTableNameEnd) + return lexValue // reuse string lexing + default: + return lexBareTableName + } +} + +// lexTableName lexes the name of a table. It assumes that at least one +// valid character for the table has already been read. +func lexBareTableName(lx *lexer) stateFn { + switch r := lx.next(); { + case isBareKeyChar(r): + return lexBareTableName + case r == tableSep || r == tableEnd: + lx.backup() + lx.emitTrim(itemText) + return lexTableNameEnd + default: + return lx.errorf("Bare keys cannot contain %q.", r) + } +} + +// lexTableNameEnd reads the end of a piece of a table name, optionally +// consuming whitespace. +func lexTableNameEnd(lx *lexer) stateFn { + switch r := lx.next(); { + case isWhitespace(r): + return lexTableNameEnd + case r == tableSep: + lx.ignore() + return lexTableNameStart + case r == tableEnd: + return lx.pop() + default: + return lx.errorf("Expected '.' or ']' to end table name, but got %q "+ + "instead.", r) + } +} + +// lexKeyStart consumes a key name up until the first non-whitespace character. +// lexKeyStart will ignore whitespace. +func lexKeyStart(lx *lexer) stateFn { + r := lx.peek() + switch { + case r == keySep: + return lx.errorf("Unexpected key separator %q.", keySep) + case isWhitespace(r) || isNL(r): + lx.next() + return lexSkip(lx, lexKeyStart) + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.emit(itemKeyStart) + lx.push(lexKeyEnd) + return lexValue // reuse string lexing + default: + lx.ignore() + lx.emit(itemKeyStart) + return lexBareKey + } +} + +// lexBareKey consumes the text of a bare key. Assumes that the first character +// (which is not whitespace) has not yet been consumed. +func lexBareKey(lx *lexer) stateFn { + switch r := lx.next(); { + case isBareKeyChar(r): + return lexBareKey + case isWhitespace(r): + lx.emitTrim(itemText) + return lexKeyEnd + case r == keySep: + lx.backup() + lx.emitTrim(itemText) + return lexKeyEnd + default: + return lx.errorf("Bare keys cannot contain %q.", r) + } +} + +// lexKeyEnd consumes the end of a key and trims whitespace (up to the key +// separator). +func lexKeyEnd(lx *lexer) stateFn { + switch r := lx.next(); { + case r == keySep: + return lexSkip(lx, lexValue) + case isWhitespace(r): + return lexSkip(lx, lexKeyEnd) + default: + return lx.errorf("Expected key separator %q, but got %q instead.", + keySep, r) + } +} + +// lexValue starts the consumption of a value anywhere a value is expected. +// lexValue will ignore whitespace. +// After a value is lexed, the last state on the next is popped and returned. +func lexValue(lx *lexer) stateFn { + // We allow whitespace to precede a value, but NOT new lines. + // In array syntax, the array states are responsible for ignoring new + // lines. + r := lx.next() + if isWhitespace(r) { + return lexSkip(lx, lexValue) + } + + switch { + case r == arrayStart: + lx.ignore() + lx.emit(itemArray) + return lexArrayValue + case r == stringStart: + if lx.accept(stringStart) { + if lx.accept(stringStart) { + lx.ignore() // Ignore """ + return lexMultilineString + } + lx.backup() + } + lx.ignore() // ignore the '"' + return lexString + case r == rawStringStart: + if lx.accept(rawStringStart) { + if lx.accept(rawStringStart) { + lx.ignore() // Ignore """ + return lexMultilineRawString + } + lx.backup() + } + lx.ignore() // ignore the "'" + return lexRawString + case r == 't': + return lexTrue + case r == 'f': + return lexFalse + case r == '-': + return lexNumberStart + case isDigit(r): + lx.backup() // avoid an extra state and use the same as above + return lexNumberOrDateStart + case r == '.': // special error case, be kind to users + return lx.errorf("Floats must start with a digit, not '.'.") + } + return lx.errorf("Expected value but found %q instead.", r) +} + +// lexArrayValue consumes one value in an array. It assumes that '[' or ',' +// have already been consumed. All whitespace and new lines are ignored. +func lexArrayValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValue) + case r == commentStart: + lx.push(lexArrayValue) + return lexCommentStart + case r == arrayValTerm: + return lx.errorf("Unexpected array value terminator %q.", + arrayValTerm) + case r == arrayEnd: + return lexArrayEnd + } + + lx.backup() + lx.push(lexArrayValueEnd) + return lexValue +} + +// lexArrayValueEnd consumes the cruft between values of an array. Namely, +// it ignores whitespace and expects either a ',' or a ']'. +func lexArrayValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValueEnd) + case r == commentStart: + lx.push(lexArrayValueEnd) + return lexCommentStart + case r == arrayValTerm: + lx.ignore() + return lexArrayValue // move on to the next value + case r == arrayEnd: + return lexArrayEnd + } + return lx.errorf("Expected an array value terminator %q or an array "+ + "terminator %q, but got %q instead.", arrayValTerm, arrayEnd, r) +} + +// lexArrayEnd finishes the lexing of an array. It assumes that a ']' has +// just been consumed. +func lexArrayEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemArrayEnd) + return lx.pop() +} + +// lexString consumes the inner contents of a string. It assumes that the +// beginning '"' has already been consumed and ignored. +func lexString(lx *lexer) stateFn { + r := lx.next() + switch { + case isNL(r): + return lx.errorf("Strings cannot contain new lines.") + case r == '\\': + lx.push(lexString) + return lexStringEscape + case r == stringEnd: + lx.backup() + lx.emit(itemString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexString +} + +// lexMultilineString consumes the inner contents of a string. It assumes that +// the beginning '"""' has already been consumed and ignored. +func lexMultilineString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == '\\': + return lexMultilineStringEscape + case r == stringEnd: + if lx.accept(stringEnd) { + if lx.accept(stringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineString +} + +// lexRawString consumes a raw string. Nothing can be escaped in such a string. +// It assumes that the beginning "'" has already been consumed and ignored. +func lexRawString(lx *lexer) stateFn { + r := lx.next() + switch { + case isNL(r): + return lx.errorf("Strings cannot contain new lines.") + case r == rawStringEnd: + lx.backup() + lx.emit(itemRawString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexRawString +} + +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such +// a string. It assumes that the beginning "'" has already been consumed and +// ignored. +func lexMultilineRawString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == rawStringEnd: + if lx.accept(rawStringEnd) { + if lx.accept(rawStringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemRawMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineRawString +} + +// lexMultilineStringEscape consumes an escaped character. It assumes that the +// preceding '\\' has already been consumed. +func lexMultilineStringEscape(lx *lexer) stateFn { + // Handle the special case first: + if isNL(lx.next()) { + lx.next() + return lexMultilineString + } else { + lx.backup() + lx.push(lexMultilineString) + return lexStringEscape(lx) + } +} + +func lexStringEscape(lx *lexer) stateFn { + r := lx.next() + switch r { + case 'b': + fallthrough + case 't': + fallthrough + case 'n': + fallthrough + case 'f': + fallthrough + case 'r': + fallthrough + case '"': + fallthrough + case '\\': + return lx.pop() + case 'u': + return lexShortUnicodeEscape + case 'U': + return lexLongUnicodeEscape + } + return lx.errorf("Invalid escape character %q. Only the following "+ + "escape characters are allowed: "+ + "\\b, \\t, \\n, \\f, \\r, \\\", \\/, \\\\, "+ + "\\uXXXX and \\UXXXXXXXX.", r) +} + +func lexShortUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 4; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf("Expected four hexadecimal digits after '\\u', "+ + "but got '%s' instead.", lx.current()) + } + } + return lx.pop() +} + +func lexLongUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 8; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf("Expected eight hexadecimal digits after '\\U', "+ + "but got '%s' instead.", lx.current()) + } + } + return lx.pop() +} + +// lexNumberOrDateStart consumes either a (positive) integer, float or +// datetime. It assumes that NO negative sign has been consumed. +func lexNumberOrDateStart(lx *lexer) stateFn { + r := lx.next() + if !isDigit(r) { + if r == '.' { + return lx.errorf("Floats must start with a digit, not '.'.") + } else { + return lx.errorf("Expected a digit but got %q.", r) + } + } + return lexNumberOrDate +} + +// lexNumberOrDate consumes either a (positive) integer, float or datetime. +func lexNumberOrDate(lx *lexer) stateFn { + r := lx.next() + switch { + case r == '-': + if lx.pos-lx.start != 5 { + return lx.errorf("All ISO8601 dates must be in full Zulu form.") + } + return lexDateAfterYear + case isDigit(r): + return lexNumberOrDate + case r == '.': + return lexFloatStart + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDateAfterYear consumes a full Zulu Datetime in ISO8601 format. +// It assumes that "YYYY-" has already been consumed. +func lexDateAfterYear(lx *lexer) stateFn { + formats := []rune{ + // digits are '0'. + // everything else is direct equality. + '0', '0', '-', '0', '0', + 'T', + '0', '0', ':', '0', '0', ':', '0', '0', + 'Z', + } + for _, f := range formats { + r := lx.next() + if f == '0' { + if !isDigit(r) { + return lx.errorf("Expected digit in ISO8601 datetime, "+ + "but found %q instead.", r) + } + } else if f != r { + return lx.errorf("Expected %q in ISO8601 datetime, "+ + "but found %q instead.", f, r) + } + } + lx.emit(itemDatetime) + return lx.pop() +} + +// lexNumberStart consumes either an integer or a float. It assumes that +// a negative sign has already been read, but that *no* digits have been +// consumed. lexNumberStart will move to the appropriate integer or float +// states. +func lexNumberStart(lx *lexer) stateFn { + // we MUST see a digit. Even floats have to start with a digit. + r := lx.next() + if !isDigit(r) { + if r == '.' { + return lx.errorf("Floats must start with a digit, not '.'.") + } else { + return lx.errorf("Expected a digit but got %q.", r) + } + } + return lexNumber +} + +// lexNumber consumes an integer or a float after seeing the first digit. +func lexNumber(lx *lexer) stateFn { + r := lx.next() + switch { + case isDigit(r): + return lexNumber + case r == '.': + return lexFloatStart + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexFloatStart starts the consumption of digits of a float after a '.'. +// Namely, at least one digit is required. +func lexFloatStart(lx *lexer) stateFn { + r := lx.next() + if !isDigit(r) { + return lx.errorf("Floats must have a digit after the '.', but got "+ + "%q instead.", r) + } + return lexFloat +} + +// lexFloat consumes the digits of a float after a '.'. +// Assumes that one digit has been consumed after a '.' already. +func lexFloat(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexFloat + } + + lx.backup() + lx.emit(itemFloat) + return lx.pop() +} + +// lexConst consumes the s[1:] in s. It assumes that s[0] has already been +// consumed. +func lexConst(lx *lexer, s string) stateFn { + for i := range s[1:] { + if r := lx.next(); r != rune(s[i+1]) { + return lx.errorf("Expected %q, but found %q instead.", s[:i+1], + s[:i]+string(r)) + } + } + return nil +} + +// lexTrue consumes the "rue" in "true". It assumes that 't' has already +// been consumed. +func lexTrue(lx *lexer) stateFn { + if fn := lexConst(lx, "true"); fn != nil { + return fn + } + lx.emit(itemBool) + return lx.pop() +} + +// lexFalse consumes the "alse" in "false". It assumes that 'f' has already +// been consumed. +func lexFalse(lx *lexer) stateFn { + if fn := lexConst(lx, "false"); fn != nil { + return fn + } + lx.emit(itemBool) + return lx.pop() +} + +// lexCommentStart begins the lexing of a comment. It will emit +// itemCommentStart and consume no characters, passing control to lexComment. +func lexCommentStart(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemCommentStart) + return lexComment +} + +// lexComment lexes an entire comment. It assumes that '#' has been consumed. +// It will consume *up to* the first new line character, and pass control +// back to the last state on the stack. +func lexComment(lx *lexer) stateFn { + r := lx.peek() + if isNL(r) || r == eof { + lx.emit(itemText) + return lx.pop() + } + lx.next() + return lexComment +} + +// lexSkip ignores all slurped input and moves on to the next state. +func lexSkip(lx *lexer, nextState stateFn) stateFn { + return func(lx *lexer) stateFn { + lx.ignore() + return nextState + } +} + +// isWhitespace returns true if `r` is a whitespace character according +// to the spec. +func isWhitespace(r rune) bool { + return r == '\t' || r == ' ' +} + +func isNL(r rune) bool { + return r == '\n' || r == '\r' +} + +func isDigit(r rune) bool { + return r >= '0' && r <= '9' +} + +func isHexadecimal(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} + +func isBareKeyChar(r rune) bool { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || + r == '-' +} + +func (itype itemType) String() string { + switch itype { + case itemError: + return "Error" + case itemNIL: + return "NIL" + case itemEOF: + return "EOF" + case itemText: + return "Text" + case itemString: + return "String" + case itemRawString: + return "String" + case itemMultilineString: + return "String" + case itemRawMultilineString: + return "String" + case itemBool: + return "Bool" + case itemInteger: + return "Integer" + case itemFloat: + return "Float" + case itemDatetime: + return "DateTime" + case itemTableStart: + return "TableStart" + case itemTableEnd: + return "TableEnd" + case itemKeyStart: + return "KeyStart" + case itemArray: + return "Array" + case itemArrayEnd: + return "ArrayEnd" + case itemCommentStart: + return "CommentStart" + } + panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) +} + +func (item item) String() string { + return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) +} diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go new file mode 100644 index 000000000..c6069be1f --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -0,0 +1,498 @@ +package toml + +import ( + "fmt" + "log" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +type parser struct { + mapping map[string]interface{} + types map[string]tomlType + lx *lexer + + // A list of keys in the order that they appear in the TOML data. + ordered []Key + + // the full key for the current hash in scope + context Key + + // the base key name for everything except hashes + currentKey string + + // rough approximation of line number + approxLine int + + // A map of 'key.group.names' to whether they were created implicitly. + implicits map[string]bool +} + +type parseError string + +func (pe parseError) Error() string { + return string(pe) +} + +func parse(data string) (p *parser, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(parseError); ok { + return + } + panic(r) + } + }() + + p = &parser{ + mapping: make(map[string]interface{}), + types: make(map[string]tomlType), + lx: lex(data), + ordered: make([]Key, 0), + implicits: make(map[string]bool), + } + for { + item := p.next() + if item.typ == itemEOF { + break + } + p.topLevel(item) + } + + return p, nil +} + +func (p *parser) panicf(format string, v ...interface{}) { + msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s", + p.approxLine, p.current(), fmt.Sprintf(format, v...)) + panic(parseError(msg)) +} + +func (p *parser) next() item { + it := p.lx.nextItem() + if it.typ == itemError { + p.panicf("%s", it.val) + } + return it +} + +func (p *parser) bug(format string, v ...interface{}) { + log.Fatalf("BUG: %s\n\n", fmt.Sprintf(format, v...)) +} + +func (p *parser) expect(typ itemType) item { + it := p.next() + p.assertEqual(typ, it.typ) + return it +} + +func (p *parser) assertEqual(expected, got itemType) { + if expected != got { + p.bug("Expected '%s' but got '%s'.", expected, got) + } +} + +func (p *parser) topLevel(item item) { + switch item.typ { + case itemCommentStart: + p.approxLine = item.line + p.expect(itemText) + case itemTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemTableEnd, kg.typ) + + p.establishContext(key, false) + p.setType("", tomlHash) + p.ordered = append(p.ordered, key) + case itemArrayTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemArrayTableEnd, kg.typ) + + p.establishContext(key, true) + p.setType("", tomlArrayHash) + p.ordered = append(p.ordered, key) + case itemKeyStart: + kname := p.next() + p.approxLine = kname.line + p.currentKey = p.keyString(kname) + + val, typ := p.value(p.next()) + p.setValue(p.currentKey, val) + p.setType(p.currentKey, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + p.currentKey = "" + default: + p.bug("Unexpected type at top level: %s", item.typ) + } +} + +// Gets a string for a key (or part of a key in a table name). +func (p *parser) keyString(it item) string { + switch it.typ { + case itemText: + return it.val + case itemString, itemMultilineString, + itemRawString, itemRawMultilineString: + s, _ := p.value(it) + return s.(string) + default: + p.bug("Unexpected key type: %s", it.typ) + panic("unreachable") + } +} + +// value translates an expected value from the lexer into a Go value wrapped +// as an empty interface. +func (p *parser) value(it item) (interface{}, tomlType) { + switch it.typ { + case itemString: + return p.replaceEscapes(it.val), p.typeOfPrimitive(it) + case itemMultilineString: + trimmed := stripFirstNewline(stripEscapedWhitespace(it.val)) + return p.replaceEscapes(trimmed), p.typeOfPrimitive(it) + case itemRawString: + return it.val, p.typeOfPrimitive(it) + case itemRawMultilineString: + return stripFirstNewline(it.val), p.typeOfPrimitive(it) + case itemBool: + switch it.val { + case "true": + return true, p.typeOfPrimitive(it) + case "false": + return false, p.typeOfPrimitive(it) + } + p.bug("Expected boolean value, but got '%s'.", it.val) + case itemInteger: + num, err := strconv.ParseInt(it.val, 10, 64) + if err != nil { + // See comment below for floats describing why we make a + // distinction between a bug and a user error. + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Integer '%s' is out of the range of 64-bit "+ + "signed integers.", it.val) + } else { + p.bug("Expected integer value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemFloat: + num, err := strconv.ParseFloat(it.val, 64) + if err != nil { + // Distinguish float values. Normally, it'd be a bug if the lexer + // provides an invalid float, but it's possible that the float is + // out of range of valid values (which the lexer cannot determine). + // So mark the former as a bug but the latter as a legitimate user + // error. + // + // This is also true for integers. + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Float '%s' is out of the range of 64-bit "+ + "IEEE-754 floating-point numbers.", it.val) + } else { + p.bug("Expected float value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemDatetime: + t, err := time.Parse("2006-01-02T15:04:05Z", it.val) + if err != nil { + p.bug("Expected Zulu formatted DateTime, but got '%s'.", it.val) + } + return t, p.typeOfPrimitive(it) + case itemArray: + array := make([]interface{}, 0) + types := make([]tomlType, 0) + + for it = p.next(); it.typ != itemArrayEnd; it = p.next() { + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + val, typ := p.value(it) + array = append(array, val) + types = append(types, typ) + } + return array, p.typeOfArray(types) + } + p.bug("Unexpected value type: %s", it.typ) + panic("unreachable") +} + +// establishContext sets the current context of the parser, +// where the context is either a hash or an array of hashes. Which one is +// set depends on the value of the `array` parameter. +// +// Establishing the context also makes sure that the key isn't a duplicate, and +// will create implicit hashes automatically. +func (p *parser) establishContext(key Key, array bool) { + var ok bool + + // Always start at the top level and drill down for our context. + hashContext := p.mapping + keyContext := make(Key, 0) + + // We only need implicit hashes for key[0:-1] + for _, k := range key[0 : len(key)-1] { + _, ok = hashContext[k] + keyContext = append(keyContext, k) + + // No key? Make an implicit hash and move on. + if !ok { + p.addImplicit(keyContext) + hashContext[k] = make(map[string]interface{}) + } + + // If the hash context is actually an array of tables, then set + // the hash context to the last element in that array. + // + // Otherwise, it better be a table, since this MUST be a key group (by + // virtue of it not being the last element in a key). + switch t := hashContext[k].(type) { + case []map[string]interface{}: + hashContext = t[len(t)-1] + case map[string]interface{}: + hashContext = t + default: + p.panicf("Key '%s' was already created as a hash.", keyContext) + } + } + + p.context = keyContext + if array { + // If this is the first element for this array, then allocate a new + // list of tables for it. + k := key[len(key)-1] + if _, ok := hashContext[k]; !ok { + hashContext[k] = make([]map[string]interface{}, 0, 5) + } + + // Add a new table. But make sure the key hasn't already been used + // for something else. + if hash, ok := hashContext[k].([]map[string]interface{}); ok { + hashContext[k] = append(hash, make(map[string]interface{})) + } else { + p.panicf("Key '%s' was already created and cannot be used as "+ + "an array.", keyContext) + } + } else { + p.setValue(key[len(key)-1], make(map[string]interface{})) + } + p.context = append(p.context, key[len(key)-1]) +} + +// setValue sets the given key to the given value in the current context. +// It will make sure that the key hasn't already been defined, account for +// implicit key groups. +func (p *parser) setValue(key string, value interface{}) { + var tmpHash interface{} + var ok bool + + hash := p.mapping + keyContext := make(Key, 0) + for _, k := range p.context { + keyContext = append(keyContext, k) + if tmpHash, ok = hash[k]; !ok { + p.bug("Context for key '%s' has not been established.", keyContext) + } + switch t := tmpHash.(type) { + case []map[string]interface{}: + // The context is a table of hashes. Pick the most recent table + // defined as the current hash. + hash = t[len(t)-1] + case map[string]interface{}: + hash = t + default: + p.bug("Expected hash to have type 'map[string]interface{}', but "+ + "it has '%T' instead.", tmpHash) + } + } + keyContext = append(keyContext, key) + + if _, ok := hash[key]; ok { + // Typically, if the given key has already been set, then we have + // to raise an error since duplicate keys are disallowed. However, + // it's possible that a key was previously defined implicitly. In this + // case, it is allowed to be redefined concretely. (See the + // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.) + // + // But we have to make sure to stop marking it as an implicit. (So that + // another redefinition provokes an error.) + // + // Note that since it has already been defined (as a hash), we don't + // want to overwrite it. So our business is done. + if p.isImplicit(keyContext) { + p.removeImplicit(keyContext) + return + } + + // Otherwise, we have a concrete key trying to override a previous + // key, which is *always* wrong. + p.panicf("Key '%s' has already been defined.", keyContext) + } + hash[key] = value +} + +// setType sets the type of a particular value at a given key. +// It should be called immediately AFTER setValue. +// +// Note that if `key` is empty, then the type given will be applied to the +// current context (which is either a table or an array of tables). +func (p *parser) setType(key string, typ tomlType) { + keyContext := make(Key, 0, len(p.context)+1) + for _, k := range p.context { + keyContext = append(keyContext, k) + } + if len(key) > 0 { // allow type setting for hashes + keyContext = append(keyContext, key) + } + p.types[keyContext.String()] = typ +} + +// addImplicit sets the given Key as having been created implicitly. +func (p *parser) addImplicit(key Key) { + p.implicits[key.String()] = true +} + +// removeImplicit stops tagging the given key as having been implicitly +// created. +func (p *parser) removeImplicit(key Key) { + p.implicits[key.String()] = false +} + +// isImplicit returns true if the key group pointed to by the key was created +// implicitly. +func (p *parser) isImplicit(key Key) bool { + return p.implicits[key.String()] +} + +// current returns the full key name of the current context. +func (p *parser) current() string { + if len(p.currentKey) == 0 { + return p.context.String() + } + if len(p.context) == 0 { + return p.currentKey + } + return fmt.Sprintf("%s.%s", p.context, p.currentKey) +} + +func stripFirstNewline(s string) string { + if len(s) == 0 || s[0] != '\n' { + return s + } + return s[1:len(s)] +} + +func stripEscapedWhitespace(s string) string { + esc := strings.Split(s, "\\\n") + if len(esc) > 1 { + for i := 1; i < len(esc); i++ { + esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace) + } + } + return strings.Join(esc, "") +} + +func (p *parser) replaceEscapes(str string) string { + var replaced []rune + s := []byte(str) + r := 0 + for r < len(s) { + if s[r] != '\\' { + c, size := utf8.DecodeRune(s[r:]) + r += size + replaced = append(replaced, c) + continue + } + r += 1 + if r >= len(s) { + p.bug("Escape sequence at end of string.") + return "" + } + switch s[r] { + default: + p.bug("Expected valid escape code after \\, but got %q.", s[r]) + return "" + case 'b': + replaced = append(replaced, rune(0x0008)) + r += 1 + case 't': + replaced = append(replaced, rune(0x0009)) + r += 1 + case 'n': + replaced = append(replaced, rune(0x000A)) + r += 1 + case 'f': + replaced = append(replaced, rune(0x000C)) + r += 1 + case 'r': + replaced = append(replaced, rune(0x000D)) + r += 1 + case '"': + replaced = append(replaced, rune(0x0022)) + r += 1 + case '\\': + replaced = append(replaced, rune(0x005C)) + r += 1 + case 'u': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+5). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) + replaced = append(replaced, escaped) + r += 5 + case 'U': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+9). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) + replaced = append(replaced, escaped) + r += 9 + } + } + return string(replaced) +} + +func (p *parser) asciiEscapeToUnicode(bs []byte) rune { + s := string(bs) + hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) + if err != nil { + p.bug("Could not parse '%s' as a hexadecimal number, but the "+ + "lexer claims it's OK: %s", s, err) + } + + // BUG(burntsushi) + // I honestly don't understand how this works. I can't seem + // to find a way to make this fail. I figured this would fail on invalid + // UTF-8 characters like U+DCFF, but it doesn't. + if !utf8.ValidString(string(rune(hex))) { + p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) + } + return rune(hex) +} + +func isStringType(ty itemType) bool { + return ty == itemString || ty == itemMultilineString || + ty == itemRawString || ty == itemRawMultilineString +} diff --git a/vendor/github.com/BurntSushi/toml/session.vim b/vendor/github.com/BurntSushi/toml/session.vim new file mode 100644 index 000000000..562164be0 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/session.vim @@ -0,0 +1 @@ +au BufWritePost *.go silent!make tags > /dev/null 2>&1 diff --git a/vendor/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/BurntSushi/toml/type_check.go new file mode 100644 index 000000000..c73f8afc1 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_check.go @@ -0,0 +1,91 @@ +package toml + +// tomlType represents any Go type that corresponds to a TOML type. +// While the first draft of the TOML spec has a simplistic type system that +// probably doesn't need this level of sophistication, we seem to be militating +// toward adding real composite types. +type tomlType interface { + typeString() string +} + +// typeEqual accepts any two types and returns true if they are equal. +func typeEqual(t1, t2 tomlType) bool { + if t1 == nil || t2 == nil { + return false + } + return t1.typeString() == t2.typeString() +} + +func typeIsHash(t tomlType) bool { + return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) +} + +type tomlBaseType string + +func (btype tomlBaseType) typeString() string { + return string(btype) +} + +func (btype tomlBaseType) String() string { + return btype.typeString() +} + +var ( + tomlInteger tomlBaseType = "Integer" + tomlFloat tomlBaseType = "Float" + tomlDatetime tomlBaseType = "Datetime" + tomlString tomlBaseType = "String" + tomlBool tomlBaseType = "Bool" + tomlArray tomlBaseType = "Array" + tomlHash tomlBaseType = "Hash" + tomlArrayHash tomlBaseType = "ArrayHash" +) + +// typeOfPrimitive returns a tomlType of any primitive value in TOML. +// Primitive values are: Integer, Float, Datetime, String and Bool. +// +// Passing a lexer item other than the following will cause a BUG message +// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. +func (p *parser) typeOfPrimitive(lexItem item) tomlType { + switch lexItem.typ { + case itemInteger: + return tomlInteger + case itemFloat: + return tomlFloat + case itemDatetime: + return tomlDatetime + case itemString: + return tomlString + case itemMultilineString: + return tomlString + case itemRawString: + return tomlString + case itemRawMultilineString: + return tomlString + case itemBool: + return tomlBool + } + p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) + panic("unreachable") +} + +// typeOfArray returns a tomlType for an array given a list of types of its +// values. +// +// In the current spec, if an array is homogeneous, then its type is always +// "Array". If the array is not homogeneous, an error is generated. +func (p *parser) typeOfArray(types []tomlType) tomlType { + // Empty arrays are cool. + if len(types) == 0 { + return tomlArray + } + + theType := types[0] + for _, t := range types[1:] { + if !typeEqual(theType, t) { + p.panicf("Array contains values of type '%s' and '%s', but "+ + "arrays must be homogeneous.", theType, t) + } + } + return tomlArray +} diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go new file mode 100644 index 000000000..7592f87a4 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_fields.go @@ -0,0 +1,241 @@ +package toml + +// Struct field handling is adapted from code in encoding/json: +// +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the Go distribution. + +import ( + "reflect" + "sort" + "sync" +) + +// A field represents a single field found in a struct. +type field struct { + name string // the name of the field (`toml` tag included) + tag bool // whether field has a `toml` tag + index []int // represents the depth of an anonymous field + typ reflect.Type // the type of the field +} + +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from toml tag", then +// breaking ties with index sequence. +type byName []field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false + } + if xik != x[j].index[k] { + return xik < x[j].index[k] + } + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that TOML should recognize for the given +// type. The algorithm is breadth-first search over the set of structs to +// include - the top struct and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} + + for _, f := range current { + if visited[f.typ] { + continue + } + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" { // unexported + continue + } + name := sf.Tag.Get("toml") + if name == "-" { + continue + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := name != "" + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + f := field{name: ft.Name(), index: index, typ: ft} + next = append(next, f) + } + } + } + } + + sort.Sort(byName(fields)) + + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with TOML tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. + out := fields[:0] + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + + fields = out + sort.Sort(byIndex(fields)) + + return fields +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// TOML tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f +} diff --git a/vendor/github.com/digitalocean/godo/.gitignore b/vendor/github.com/digitalocean/godo/.gitignore old mode 100755 new mode 100644 diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index 7ee72e705..b2ca55b31 100644 --- a/vendor/github.com/digitalocean/godo/CHANGELOG.md +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -1,8 +1,12 @@ # Change Log +## [v1.4.1] - 2018-08-23 + +- #176 Fix cdn flush cache API endpoint - @sunny-b + ## [v1.4.0] - 2018-08-22 -- #170 Add support for Spaces CDN - @sunny-b +- #175 Add support for Spaces CDN - @sunny-b ## [v1.3.0] - 2018-05-24 diff --git a/vendor/github.com/digitalocean/godo/cdn.go b/vendor/github.com/digitalocean/godo/cdn.go index 980c4bd09..217d90e52 100644 --- a/vendor/github.com/digitalocean/godo/cdn.go +++ b/vendor/github.com/digitalocean/godo/cdn.go @@ -164,7 +164,7 @@ func (c CDNServiceOp) FlushCache(ctx context.Context, id string, flushCacheReque return nil, NewArgError("id", "cannot be an empty string") } - path := fmt.Sprintf("%s/%s", cdnBasePath, id) + path := fmt.Sprintf("%s/%s/cache", cdnBasePath, id) req, err := c.client.NewRequest(ctx, http.MethodDelete, path, flushCacheRequest) if err != nil { diff --git a/vendor/github.com/digitalocean/godo/cdn_test.go b/vendor/github.com/digitalocean/godo/cdn_test.go index 9d35adceb..318962bd0 100644 --- a/vendor/github.com/digitalocean/godo/cdn_test.go +++ b/vendor/github.com/digitalocean/godo/cdn_test.go @@ -278,7 +278,7 @@ func TestCDN_FluchCacheCDN(t *testing.T) { setup() defer teardown() - mux.HandleFunc("/v2/cdn/endpoints/12345", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/v2/cdn/endpoints/12345/cache", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index 12eb11c64..f288995a0 100644 --- a/vendor/github.com/digitalocean/godo/godo.go +++ b/vendor/github.com/digitalocean/godo/godo.go @@ -18,7 +18,7 @@ import ( ) const ( - libraryVersion = "1.4.0" + libraryVersion = "1.4.1" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" diff --git a/vendor/github.com/fsnotify/fsnotify/.editorconfig b/vendor/github.com/fsnotify/fsnotify/.editorconfig deleted file mode 100644 index ba49e3c23..000000000 --- a/vendor/github.com/fsnotify/fsnotify/.editorconfig +++ /dev/null @@ -1,5 +0,0 @@ -root = true - -[*] -indent_style = tab -indent_size = 4 diff --git a/vendor/github.com/fsnotify/fsnotify/.github/ISSUE_TEMPLATE.md b/vendor/github.com/fsnotify/fsnotify/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 4ad1aed8f..000000000 --- a/vendor/github.com/fsnotify/fsnotify/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,11 +0,0 @@ -Before reporting an issue, please ensure you are using the latest release of fsnotify. - -### Which operating system (GOOS) and version are you using? - -Linux: lsb_release -a -macOS: sw_vers -Windows: systeminfo | findstr /B /C:OS - -### Please describe the issue that occurred. - -### Are you able to reproduce the issue? Please provide steps to reproduce and a code sample if possible. diff --git a/vendor/github.com/fsnotify/fsnotify/.github/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/fsnotify/fsnotify/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 64ddf7cef..000000000 --- a/vendor/github.com/fsnotify/fsnotify/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,8 +0,0 @@ -#### What does this pull request do? - - -#### Where should the reviewer start? - - -#### How should this be manually tested? - diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md deleted file mode 100644 index 399320741..000000000 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# File system notifications for Go - -[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify) - -fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running: - -```console -go get -u golang.org/x/sys/... -``` - -Cross platform: Windows, Linux, BSD and macOS. - -|Adapter |OS |Status | -|----------|----------|----------| -|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| -|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| -|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| -|fanotify |Linux 2.6.37+ | | -|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| -|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| - -\* Android and iOS are untested. - -Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. - -## API stability - -fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA). - -All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number. - -Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. - -## Contributing - -Please refer to [CONTRIBUTING][] before opening an issue or pull request. - -## Example - -See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). - -## FAQ - -**When a file is moved to another directory is it still being watched?** - -No (it shouldn't be, unless you are watching where it was moved to). - -**When I watch a directory, are all subdirectories watched as well?** - -No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]). - -**Do I have to watch the Error and Event channels in a separate goroutine?** - -As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7]) - -**Why am I receiving multiple events for the same file on OS X?** - -Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]). - -**How many files can be watched at once?** - -There are OS-specific limits as to how many watches can be created: -* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. -* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. - -[#62]: https://github.com/howeyc/fsnotify/issues/62 -[#18]: https://github.com/fsnotify/fsnotify/issues/18 -[#11]: https://github.com/fsnotify/fsnotify/issues/11 -[#7]: https://github.com/howeyc/fsnotify/issues/7 - -[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md - -## Related Projects - -* [notify](https://github.com/rjeczalik/notify) -* [fsevents](https://github.com/fsnotify/fsevents) - diff --git a/vendor/github.com/fsnotify/fsnotify/fen.go b/vendor/github.com/fsnotify/fsnotify/fen.go deleted file mode 100644 index ced39cb88..000000000 --- a/vendor/github.com/fsnotify/fsnotify/fen.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build solaris - -package fsnotify - -import ( - "errors" -) - -// Watcher watches a set of files, delivering events to a channel. -type Watcher struct { - Events chan Event - Errors chan error -} - -// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. -func NewWatcher() (*Watcher, error) { - return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") -} - -// Close removes all watches and closes the events channel. -func (w *Watcher) Close() error { - return nil -} - -// Add starts watching the named file or directory (non-recursively). -func (w *Watcher) Add(name string) error { - return nil -} - -// Remove stops watching the the named file or directory (non-recursively). -func (w *Watcher) Remove(name string) error { - return nil -} diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify_test.go b/vendor/github.com/fsnotify/fsnotify/fsnotify_test.go deleted file mode 100644 index f9771d9df..000000000 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 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. - -// +build !plan9 - -package fsnotify - -import ( - "os" - "testing" - "time" -) - -func TestEventStringWithValue(t *testing.T) { - for opMask, expectedString := range map[Op]string{ - Chmod | Create: `"/usr/someFile": CREATE|CHMOD`, - Rename: `"/usr/someFile": RENAME`, - Remove: `"/usr/someFile": REMOVE`, - Write | Chmod: `"/usr/someFile": WRITE|CHMOD`, - } { - event := Event{Name: "/usr/someFile", Op: opMask} - if event.String() != expectedString { - t.Fatalf("Expected %s, got: %v", expectedString, event.String()) - } - - } -} - -func TestEventOpStringWithValue(t *testing.T) { - expectedOpString := "WRITE|CHMOD" - event := Event{Name: "someFile", Op: Write | Chmod} - if event.Op.String() != expectedOpString { - t.Fatalf("Expected %s, got: %v", expectedOpString, event.Op.String()) - } -} - -func TestEventOpStringWithNoValue(t *testing.T) { - expectedOpString := "" - event := Event{Name: "testFile", Op: 0} - if event.Op.String() != expectedOpString { - t.Fatalf("Expected %s, got: %v", expectedOpString, event.Op.String()) - } -} - -// TestWatcherClose tests that the goroutine started by creating the watcher can be -// signalled to return at any time, even if there is no goroutine listening on the events -// or errors channels. -func TestWatcherClose(t *testing.T) { - t.Parallel() - - name := tempMkFile(t, "") - w := newWatcher(t) - err := w.Add(name) - if err != nil { - t.Fatal(err) - } - - err = os.Remove(name) - if err != nil { - t.Fatal(err) - } - // Allow the watcher to receive the event. - time.Sleep(time.Millisecond * 100) - - err = w.Close() - if err != nil { - t.Fatal(err) - } -} diff --git a/vendor/github.com/golang/protobuf/types/regen.sh b/vendor/github.com/golang/protobuf/types/regen.sh old mode 100755 new mode 100644 diff --git a/vendor/github.com/kr/pretty/.gitignore b/vendor/github.com/kr/pretty/.gitignore new file mode 100644 index 000000000..1f0a99f2f --- /dev/null +++ b/vendor/github.com/kr/pretty/.gitignore @@ -0,0 +1,4 @@ +[568].out +_go* +_test* +_obj diff --git a/vendor/github.com/pelletier/go-toml/LICENSE b/vendor/github.com/kr/pretty/License similarity index 86% rename from vendor/github.com/pelletier/go-toml/LICENSE rename to vendor/github.com/kr/pretty/License index 583bdae62..05c783ccf 100644 --- a/vendor/github.com/pelletier/go-toml/LICENSE +++ b/vendor/github.com/kr/pretty/License @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton +Copyright 2012 Keith Rarick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/kr/pretty/Readme b/vendor/github.com/kr/pretty/Readme new file mode 100644 index 000000000..c589fc622 --- /dev/null +++ b/vendor/github.com/kr/pretty/Readme @@ -0,0 +1,9 @@ +package pretty + + import "github.com/kr/pretty" + + Package pretty provides pretty-printing for Go values. + +Documentation + + http://godoc.org/github.com/kr/pretty diff --git a/vendor/github.com/kr/pretty/diff.go b/vendor/github.com/kr/pretty/diff.go new file mode 100644 index 000000000..8fe8e2405 --- /dev/null +++ b/vendor/github.com/kr/pretty/diff.go @@ -0,0 +1,158 @@ +package pretty + +import ( + "fmt" + "io" + "reflect" +) + +type sbuf []string + +func (s *sbuf) Write(b []byte) (int, error) { + *s = append(*s, string(b)) + return len(b), nil +} + +// Diff returns a slice where each element describes +// a difference between a and b. +func Diff(a, b interface{}) (desc []string) { + Fdiff((*sbuf)(&desc), a, b) + return desc +} + +// Fdiff writes to w a description of the differences between a and b. +func Fdiff(w io.Writer, a, b interface{}) { + diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b)) +} + +type diffWriter struct { + w io.Writer + l string // label +} + +func (w diffWriter) printf(f string, a ...interface{}) { + var l string + if w.l != "" { + l = w.l + ": " + } + fmt.Fprintf(w.w, l+f, a...) +} + +func (w diffWriter) diff(av, bv reflect.Value) { + if !av.IsValid() && bv.IsValid() { + w.printf("nil != %#v", bv.Interface()) + return + } + if av.IsValid() && !bv.IsValid() { + w.printf("%#v != nil", av.Interface()) + return + } + if !av.IsValid() && !bv.IsValid() { + return + } + + at := av.Type() + bt := bv.Type() + if at != bt { + w.printf("%v != %v", at, bt) + return + } + + // numeric types, including bool + if at.Kind() < reflect.Array { + a, b := av.Interface(), bv.Interface() + if a != b { + w.printf("%#v != %#v", a, b) + } + return + } + + switch at.Kind() { + case reflect.String: + a, b := av.Interface(), bv.Interface() + if a != b { + w.printf("%q != %q", a, b) + } + case reflect.Ptr: + switch { + case av.IsNil() && !bv.IsNil(): + w.printf("nil != %v", bv.Interface()) + case !av.IsNil() && bv.IsNil(): + w.printf("%v != nil", av.Interface()) + case !av.IsNil() && !bv.IsNil(): + w.diff(av.Elem(), bv.Elem()) + } + case reflect.Struct: + for i := 0; i < av.NumField(); i++ { + w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i)) + } + case reflect.Slice: + lenA := av.Len() + lenB := bv.Len() + if lenA != lenB { + w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB) + break + } + for i := 0; i < lenA; i++ { + w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i)) + } + case reflect.Map: + ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys()) + for _, k := range ak { + w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) + w.printf("%q != (missing)", av.MapIndex(k)) + } + for _, k := range both { + w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) + w.diff(av.MapIndex(k), bv.MapIndex(k)) + } + for _, k := range bk { + w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) + w.printf("(missing) != %q", bv.MapIndex(k)) + } + case reflect.Interface: + w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface())) + default: + if !reflect.DeepEqual(av.Interface(), bv.Interface()) { + w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface())) + } + } +} + +func (d diffWriter) relabel(name string) (d1 diffWriter) { + d1 = d + if d.l != "" && name[0] != '[' { + d1.l += "." + } + d1.l += name + return d1 +} + +func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) { + for _, av := range a { + inBoth := false + for _, bv := range b { + if reflect.DeepEqual(av.Interface(), bv.Interface()) { + inBoth = true + both = append(both, av) + break + } + } + if !inBoth { + ak = append(ak, av) + } + } + for _, bv := range b { + inBoth := false + for _, av := range a { + if reflect.DeepEqual(av.Interface(), bv.Interface()) { + inBoth = true + break + } + } + if !inBoth { + bk = append(bk, bv) + } + } + return +} diff --git a/vendor/github.com/kr/pretty/diff_test.go b/vendor/github.com/kr/pretty/diff_test.go new file mode 100644 index 000000000..3c388f13c --- /dev/null +++ b/vendor/github.com/kr/pretty/diff_test.go @@ -0,0 +1,74 @@ +package pretty + +import ( + "testing" +) + +type difftest struct { + a interface{} + b interface{} + exp []string +} + +type S struct { + A int + S *S + I interface{} + C []int +} + +var diffs = []difftest{ + {a: nil, b: nil}, + {a: S{A: 1}, b: S{A: 1}}, + + {0, "", []string{`int != string`}}, + {0, 1, []string{`0 != 1`}}, + {S{}, new(S), []string{`pretty.S != *pretty.S`}}, + {"a", "b", []string{`"a" != "b"`}}, + {S{}, S{A: 1}, []string{`A: 0 != 1`}}, + {new(S), &S{A: 1}, []string{`A: 0 != 1`}}, + {S{S: new(S)}, S{S: &S{A: 1}}, []string{`S.A: 0 != 1`}}, + {S{}, S{I: 0}, []string{`I: nil != 0`}}, + {S{I: 1}, S{I: "x"}, []string{`I: int != string`}}, + {S{}, S{C: []int{1}}, []string{`C: []int[0] != []int[1]`}}, + {S{C: []int{}}, S{C: []int{1}}, []string{`C: []int[0] != []int[1]`}}, + {S{C: []int{1, 2, 3}}, S{C: []int{1, 2, 4}}, []string{`C[2]: 3 != 4`}}, + {S{}, S{A: 1, S: new(S)}, []string{`A: 0 != 1`, `S: nil != &{0 []}`}}, +} + +func TestDiff(t *testing.T) { + for _, tt := range diffs { + got := Diff(tt.a, tt.b) + eq := len(got) == len(tt.exp) + if eq { + for i := range got { + eq = eq && got[i] == tt.exp[i] + } + } + if !eq { + t.Errorf("diffing % #v", tt.a) + t.Errorf("with % #v", tt.b) + diffdiff(t, got, tt.exp) + continue + } + } +} + +func diffdiff(t *testing.T, got, exp []string) { + minus(t, "unexpected:", got, exp) + minus(t, "missing:", exp, got) +} + +func minus(t *testing.T, s string, a, b []string) { + var i, j int + for i = 0; i < len(a); i++ { + for j = 0; j < len(b); j++ { + if a[i] == b[j] { + break + } + } + if j == len(b) { + t.Error(s, a[i]) + } + } +} diff --git a/vendor/github.com/kr/pretty/example_test.go b/vendor/github.com/kr/pretty/example_test.go new file mode 100644 index 000000000..ecf40f3fc --- /dev/null +++ b/vendor/github.com/kr/pretty/example_test.go @@ -0,0 +1,20 @@ +package pretty_test + +import ( + "fmt" + "github.com/kr/pretty" +) + +func Example() { + type myType struct { + a, b int + } + var x = []myType{{1, 2}, {3, 4}, {5, 6}} + fmt.Printf("%# v", pretty.Formatter(x)) + // output: + // []pretty_test.myType{ + // {a:1, b:2}, + // {a:3, b:4}, + // {a:5, b:6}, + // } +} diff --git a/vendor/github.com/kr/pretty/formatter.go b/vendor/github.com/kr/pretty/formatter.go new file mode 100644 index 000000000..8dacda25f --- /dev/null +++ b/vendor/github.com/kr/pretty/formatter.go @@ -0,0 +1,337 @@ +package pretty + +import ( + "fmt" + "io" + "reflect" + "strconv" + "text/tabwriter" + + "github.com/kr/text" +) + +const ( + limit = 50 +) + +type formatter struct { + x interface{} + force bool + quote bool +} + +// Formatter makes a wrapper, f, that will format x as go source with line +// breaks and tabs. Object f responds to the "%v" formatting verb when both the +// "#" and " " (space) flags are set, for example: +// +// fmt.Sprintf("%# v", Formatter(x)) +// +// If one of these two flags is not set, or any other verb is used, f will +// format x according to the usual rules of package fmt. +// In particular, if x satisfies fmt.Formatter, then x.Format will be called. +func Formatter(x interface{}) (f fmt.Formatter) { + return formatter{x: x, quote: true} +} + +func (fo formatter) String() string { + return fmt.Sprint(fo.x) // unwrap it +} + +func (fo formatter) passThrough(f fmt.State, c rune) { + s := "%" + for i := 0; i < 128; i++ { + if f.Flag(i) { + s += string(i) + } + } + if w, ok := f.Width(); ok { + s += fmt.Sprintf("%d", w) + } + if p, ok := f.Precision(); ok { + s += fmt.Sprintf(".%d", p) + } + s += string(c) + fmt.Fprintf(f, s, fo.x) +} + +func (fo formatter) Format(f fmt.State, c rune) { + if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { + w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) + p := &printer{tw: w, Writer: w, visited: make(map[visit]int)} + p.printValue(reflect.ValueOf(fo.x), true, fo.quote) + w.Flush() + return + } + fo.passThrough(f, c) +} + +type printer struct { + io.Writer + tw *tabwriter.Writer + visited map[visit]int + depth int +} + +func (p *printer) indent() *printer { + q := *p + q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) + q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) + return &q +} + +func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { + if showType { + io.WriteString(p, v.Type().String()) + fmt.Fprintf(p, "(%#v)", x) + } else { + fmt.Fprintf(p, "%#v", x) + } +} + +// printValue must keep track of already-printed pointer values to avoid +// infinite recursion. +type visit struct { + v uintptr + typ reflect.Type +} + +func (p *printer) printValue(v reflect.Value, showType, quote bool) { + if p.depth > 10 { + io.WriteString(p, "!%v(DEPTH EXCEEDED)") + return + } + + switch v.Kind() { + case reflect.Bool: + p.printInline(v, v.Bool(), showType) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p.printInline(v, v.Int(), showType) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p.printInline(v, v.Uint(), showType) + case reflect.Float32, reflect.Float64: + p.printInline(v, v.Float(), showType) + case reflect.Complex64, reflect.Complex128: + fmt.Fprintf(p, "%#v", v.Complex()) + case reflect.String: + p.fmtString(v.String(), quote) + case reflect.Map: + t := v.Type() + if showType { + io.WriteString(p, t.String()) + } + writeByte(p, '{') + if nonzero(v) { + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + keys := v.MapKeys() + for i := 0; i < v.Len(); i++ { + showTypeInStruct := true + k := keys[i] + mv := v.MapIndex(k) + pp.printValue(k, false, true) + writeByte(pp, ':') + if expand { + writeByte(pp, '\t') + } + showTypeInStruct = t.Elem().Kind() == reflect.Interface + pp.printValue(mv, showTypeInStruct, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.Len()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + } + writeByte(p, '}') + case reflect.Struct: + t := v.Type() + if v.CanAddr() { + addr := v.UnsafeAddr() + vis := visit{addr, t} + if vd, ok := p.visited[vis]; ok && vd < p.depth { + p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) + break // don't print v again + } + p.visited[vis] = p.depth + } + + if showType { + io.WriteString(p, t.String()) + } + writeByte(p, '{') + if nonzero(v) { + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + for i := 0; i < v.NumField(); i++ { + showTypeInStruct := true + if f := t.Field(i); f.Name != "" { + io.WriteString(pp, f.Name) + writeByte(pp, ':') + if expand { + writeByte(pp, '\t') + } + showTypeInStruct = labelType(f.Type) + } + pp.printValue(getField(v, i), showTypeInStruct, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.NumField()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + } + writeByte(p, '}') + case reflect.Interface: + switch e := v.Elem(); { + case e.Kind() == reflect.Invalid: + io.WriteString(p, "nil") + case e.IsValid(): + pp := *p + pp.depth++ + pp.printValue(e, showType, true) + default: + io.WriteString(p, v.Type().String()) + io.WriteString(p, "(nil)") + } + case reflect.Array, reflect.Slice: + t := v.Type() + if showType { + io.WriteString(p, t.String()) + } + if v.Kind() == reflect.Slice && v.IsNil() && showType { + io.WriteString(p, "(nil)") + break + } + if v.Kind() == reflect.Slice && v.IsNil() { + io.WriteString(p, "nil") + break + } + writeByte(p, '{') + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + for i := 0; i < v.Len(); i++ { + showTypeInSlice := t.Elem().Kind() == reflect.Interface + pp.printValue(v.Index(i), showTypeInSlice, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.Len()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + writeByte(p, '}') + case reflect.Ptr: + e := v.Elem() + if !e.IsValid() { + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + io.WriteString(p, ")(nil)") + } else { + pp := *p + pp.depth++ + writeByte(pp, '&') + pp.printValue(e, true, true) + } + case reflect.Chan: + x := v.Pointer() + if showType { + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + fmt.Fprintf(p, ")(%#v)", x) + } else { + fmt.Fprintf(p, "%#v", x) + } + case reflect.Func: + io.WriteString(p, v.Type().String()) + io.WriteString(p, " {...}") + case reflect.UnsafePointer: + p.printInline(v, v.Pointer(), showType) + case reflect.Invalid: + io.WriteString(p, "nil") + } +} + +func canInline(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map: + return !canExpand(t.Elem()) + case reflect.Struct: + for i := 0; i < t.NumField(); i++ { + if canExpand(t.Field(i).Type) { + return false + } + } + return true + case reflect.Interface: + return false + case reflect.Array, reflect.Slice: + return !canExpand(t.Elem()) + case reflect.Ptr: + return false + case reflect.Chan, reflect.Func, reflect.UnsafePointer: + return false + } + return true +} + +func canExpand(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map, reflect.Struct, + reflect.Interface, reflect.Array, reflect.Slice, + reflect.Ptr: + return true + } + return false +} + +func labelType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Interface, reflect.Struct: + return true + } + return false +} + +func (p *printer) fmtString(s string, quote bool) { + if quote { + s = strconv.Quote(s) + } + io.WriteString(p, s) +} + +func tryDeepEqual(a, b interface{}) bool { + defer func() { recover() }() + return reflect.DeepEqual(a, b) +} + +func writeByte(w io.Writer, b byte) { + w.Write([]byte{b}) +} + +func getField(v reflect.Value, i int) reflect.Value { + val := v.Field(i) + if val.Kind() == reflect.Interface && !val.IsNil() { + val = val.Elem() + } + return val +} diff --git a/vendor/github.com/kr/pretty/formatter_test.go b/vendor/github.com/kr/pretty/formatter_test.go new file mode 100644 index 000000000..5f3204e8e --- /dev/null +++ b/vendor/github.com/kr/pretty/formatter_test.go @@ -0,0 +1,261 @@ +package pretty + +import ( + "fmt" + "io" + "strings" + "testing" + "unsafe" +) + +type test struct { + v interface{} + s string +} + +type LongStructTypeName struct { + longFieldName interface{} + otherLongFieldName interface{} +} + +type SA struct { + t *T + v T +} + +type T struct { + x, y int +} + +type F int + +func (f F) Format(s fmt.State, c rune) { + fmt.Fprintf(s, "F(%d)", int(f)) +} + +var long = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +var gosyntax = []test{ + {nil, `nil`}, + {"", `""`}, + {"a", `"a"`}, + {1, "int(1)"}, + {1.0, "float64(1)"}, + {[]int(nil), "[]int(nil)"}, + {[0]int{}, "[0]int{}"}, + {complex(1, 0), "(1+0i)"}, + //{make(chan int), "(chan int)(0x1234)"}, + {unsafe.Pointer(uintptr(unsafe.Pointer(&long))), fmt.Sprintf("unsafe.Pointer(0x%02x)", uintptr(unsafe.Pointer(&long)))}, + {func(int) {}, "func(int) {...}"}, + {map[int]int{1: 1}, "map[int]int{1:1}"}, + {int32(1), "int32(1)"}, + {io.EOF, `&errors.errorString{s:"EOF"}`}, + {[]string{"a"}, `[]string{"a"}`}, + { + []string{long}, + `[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`, + }, + {F(5), "pretty.F(5)"}, + { + SA{&T{1, 2}, T{3, 4}}, + `pretty.SA{ + t: &pretty.T{x:1, y:2}, + v: pretty.T{x:3, y:4}, +}`, + }, + { + map[int][]byte{1: {}}, + `map[int][]uint8{ + 1: {}, +}`, + }, + { + map[int]T{1: {}}, + `map[int]pretty.T{ + 1: {}, +}`, + }, + { + long, + `"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`, + }, + { + LongStructTypeName{ + longFieldName: LongStructTypeName{}, + otherLongFieldName: long, + }, + `pretty.LongStructTypeName{ + longFieldName: pretty.LongStructTypeName{}, + otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", +}`, + }, + { + &LongStructTypeName{ + longFieldName: &LongStructTypeName{}, + otherLongFieldName: (*LongStructTypeName)(nil), + }, + `&pretty.LongStructTypeName{ + longFieldName: &pretty.LongStructTypeName{}, + otherLongFieldName: (*pretty.LongStructTypeName)(nil), +}`, + }, + { + []LongStructTypeName{ + {nil, nil}, + {3, 3}, + {long, nil}, + }, + `[]pretty.LongStructTypeName{ + {}, + { + longFieldName: int(3), + otherLongFieldName: int(3), + }, + { + longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + otherLongFieldName: nil, + }, +}`, + }, + { + []interface{}{ + LongStructTypeName{nil, nil}, + []byte{1, 2, 3}, + T{3, 4}, + LongStructTypeName{long, nil}, + }, + `[]interface {}{ + pretty.LongStructTypeName{}, + []uint8{0x1, 0x2, 0x3}, + pretty.T{x:3, y:4}, + pretty.LongStructTypeName{ + longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + otherLongFieldName: nil, + }, +}`, + }, +} + +func TestGoSyntax(t *testing.T) { + for _, tt := range gosyntax { + s := fmt.Sprintf("%# v", Formatter(tt.v)) + if tt.s != s { + t.Errorf("expected %q", tt.s) + t.Errorf("got %q", s) + t.Errorf("expraw\n%s", tt.s) + t.Errorf("gotraw\n%s", s) + } + } +} + +type I struct { + i int + R interface{} +} + +func (i *I) I() *I { return i.R.(*I) } + +func TestCycle(t *testing.T) { + type A struct{ *A } + v := &A{} + v.A = v + + // panics from stack overflow without cycle detection + t.Logf("Example cycle:\n%# v", Formatter(v)) + + p := &A{} + s := fmt.Sprintf("%# v", Formatter([]*A{p, p})) + if strings.Contains(s, "CYCLIC") { + t.Errorf("Repeated address detected as cyclic reference:\n%s", s) + } + + type R struct { + i int + *R + } + r := &R{ + i: 1, + R: &R{ + i: 2, + R: &R{ + i: 3, + }, + }, + } + r.R.R.R = r + t.Logf("Example longer cycle:\n%# v", Formatter(r)) + + r = &R{ + i: 1, + R: &R{ + i: 2, + R: &R{ + i: 3, + R: &R{ + i: 4, + R: &R{ + i: 5, + R: &R{ + i: 6, + R: &R{ + i: 7, + R: &R{ + i: 8, + R: &R{ + i: 9, + R: &R{ + i: 10, + R: &R{ + i: 11, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + // here be pirates + r.R.R.R.R.R.R.R.R.R.R.R = r + t.Logf("Example very long cycle:\n%# v", Formatter(r)) + + i := &I{ + i: 1, + R: &I{ + i: 2, + R: &I{ + i: 3, + R: &I{ + i: 4, + R: &I{ + i: 5, + R: &I{ + i: 6, + R: &I{ + i: 7, + R: &I{ + i: 8, + R: &I{ + i: 9, + R: &I{ + i: 10, + R: &I{ + i: 11, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + iv := i.I().I().I().I().I().I().I().I().I().I() + *iv = *i + t.Logf("Example long interface cycle:\n%# v", Formatter(i)) +} diff --git a/vendor/github.com/kr/pretty/pretty.go b/vendor/github.com/kr/pretty/pretty.go new file mode 100644 index 000000000..d3df8686c --- /dev/null +++ b/vendor/github.com/kr/pretty/pretty.go @@ -0,0 +1,98 @@ +// Package pretty provides pretty-printing for Go values. This is +// useful during debugging, to avoid wrapping long output lines in +// the terminal. +// +// It provides a function, Formatter, that can be used with any +// function that accepts a format string. It also provides +// convenience wrappers for functions in packages fmt and log. +package pretty + +import ( + "fmt" + "io" + "log" +) + +// Errorf is a convenience wrapper for fmt.Errorf. +// +// Calling Errorf(f, x, y) is equivalent to +// fmt.Errorf(f, Formatter(x), Formatter(y)). +func Errorf(format string, a ...interface{}) error { + return fmt.Errorf(format, wrap(a, false)...) +} + +// Fprintf is a convenience wrapper for fmt.Fprintf. +// +// Calling Fprintf(w, f, x, y) is equivalent to +// fmt.Fprintf(w, f, Formatter(x), Formatter(y)). +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) { + return fmt.Fprintf(w, format, wrap(a, false)...) +} + +// Log is a convenience wrapper for log.Printf. +// +// Calling Log(x, y) is equivalent to +// log.Print(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Log(a ...interface{}) { + log.Print(wrap(a, true)...) +} + +// Logf is a convenience wrapper for log.Printf. +// +// Calling Logf(f, x, y) is equivalent to +// log.Printf(f, Formatter(x), Formatter(y)). +func Logf(format string, a ...interface{}) { + log.Printf(format, wrap(a, false)...) +} + +// Logln is a convenience wrapper for log.Printf. +// +// Calling Logln(x, y) is equivalent to +// log.Println(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Logln(a ...interface{}) { + log.Println(wrap(a, true)...) +} + +// Print pretty-prints its operands and writes to standard output. +// +// Calling Print(x, y) is equivalent to +// fmt.Print(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Print(a ...interface{}) (n int, errno error) { + return fmt.Print(wrap(a, true)...) +} + +// Printf is a convenience wrapper for fmt.Printf. +// +// Calling Printf(f, x, y) is equivalent to +// fmt.Printf(f, Formatter(x), Formatter(y)). +func Printf(format string, a ...interface{}) (n int, errno error) { + return fmt.Printf(format, wrap(a, false)...) +} + +// Println pretty-prints its operands and writes to standard output. +// +// Calling Print(x, y) is equivalent to +// fmt.Println(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Println(a ...interface{}) (n int, errno error) { + return fmt.Println(wrap(a, true)...) +} + +// Sprintf is a convenience wrapper for fmt.Sprintf. +// +// Calling Sprintf(f, x, y) is equivalent to +// fmt.Sprintf(f, Formatter(x), Formatter(y)). +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, wrap(a, false)...) +} + +func wrap(a []interface{}, force bool) []interface{} { + w := make([]interface{}, len(a)) + for i, x := range a { + w[i] = formatter{x: x, force: force} + } + return w +} diff --git a/vendor/github.com/kr/pretty/zero.go b/vendor/github.com/kr/pretty/zero.go new file mode 100644 index 000000000..abb5b6fc1 --- /dev/null +++ b/vendor/github.com/kr/pretty/zero.go @@ -0,0 +1,41 @@ +package pretty + +import ( + "reflect" +) + +func nonzero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() != 0 + case reflect.Float32, reflect.Float64: + return v.Float() != 0 + case reflect.Complex64, reflect.Complex128: + return v.Complex() != complex(0, 0) + case reflect.String: + return v.String() != "" + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if nonzero(getField(v, i)) { + return true + } + } + return false + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if nonzero(v.Index(i)) { + return true + } + } + return false + case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func: + return !v.IsNil() + case reflect.UnsafePointer: + return v.Pointer() != 0 + } + return true +} diff --git a/vendor/github.com/kr/text/License b/vendor/github.com/kr/text/License new file mode 100644 index 000000000..480a32805 --- /dev/null +++ b/vendor/github.com/kr/text/License @@ -0,0 +1,19 @@ +Copyright 2012 Keith Rarick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/kr/text/Readme b/vendor/github.com/kr/text/Readme new file mode 100644 index 000000000..7e6e7c068 --- /dev/null +++ b/vendor/github.com/kr/text/Readme @@ -0,0 +1,3 @@ +This is a Go package for manipulating paragraphs of text. + +See http://go.pkgdoc.org/github.com/kr/text for full documentation. diff --git a/vendor/github.com/kr/text/colwriter/Readme b/vendor/github.com/kr/text/colwriter/Readme new file mode 100644 index 000000000..1c1f4e683 --- /dev/null +++ b/vendor/github.com/kr/text/colwriter/Readme @@ -0,0 +1,5 @@ +Package colwriter provides a write filter that formats +input lines in multiple columns. + +The package is a straightforward translation from +/src/cmd/draw/mc.c in Plan 9 from User Space. diff --git a/vendor/github.com/kr/text/colwriter/column.go b/vendor/github.com/kr/text/colwriter/column.go new file mode 100644 index 000000000..7302ce9f7 --- /dev/null +++ b/vendor/github.com/kr/text/colwriter/column.go @@ -0,0 +1,147 @@ +// Package colwriter provides a write filter that formats +// input lines in multiple columns. +// +// The package is a straightforward translation from +// /src/cmd/draw/mc.c in Plan 9 from User Space. +package colwriter + +import ( + "bytes" + "io" + "unicode/utf8" +) + +const ( + tab = 4 +) + +const ( + // Print each input line ending in a colon ':' separately. + BreakOnColon uint = 1 << iota +) + +// A Writer is a filter that arranges input lines in as many columns as will +// fit in its width. Tab '\t' chars in the input are translated to sequences +// of spaces ending at multiples of 4 positions. +// +// If BreakOnColon is set, each input line ending in a colon ':' is written +// separately. +// +// The Writer assumes that all Unicode code points have the same width; this +// may not be true in some fonts. +type Writer struct { + w io.Writer + buf []byte + width int + flag uint +} + +// NewWriter allocates and initializes a new Writer writing to w. +// Parameter width controls the total number of characters on each line +// across all columns. +func NewWriter(w io.Writer, width int, flag uint) *Writer { + return &Writer{ + w: w, + width: width, + flag: flag, + } +} + +// Write writes p to the writer w. The only errors returned are ones +// encountered while writing to the underlying output stream. +func (w *Writer) Write(p []byte) (n int, err error) { + var linelen int + var lastWasColon bool + for i, c := range p { + w.buf = append(w.buf, c) + linelen++ + if c == '\t' { + w.buf[len(w.buf)-1] = ' ' + for linelen%tab != 0 { + w.buf = append(w.buf, ' ') + linelen++ + } + } + if w.flag&BreakOnColon != 0 && c == ':' { + lastWasColon = true + } else if lastWasColon { + if c == '\n' { + pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'}) + if pos < 0 { + pos = 0 + } + line := w.buf[pos:] + w.buf = w.buf[:pos] + if err = w.columnate(); err != nil { + if len(line) < i { + return i - len(line), err + } + return 0, err + } + if n, err := w.w.Write(line); err != nil { + if r := len(line) - n; r < i { + return i - r, err + } + return 0, err + } + } + lastWasColon = false + } + if c == '\n' { + linelen = 0 + } + } + return len(p), nil +} + +// Flush should be called after the last call to Write to ensure that any data +// buffered in the Writer is written to output. +func (w *Writer) Flush() error { + return w.columnate() +} + +func (w *Writer) columnate() error { + words := bytes.Split(w.buf, []byte{'\n'}) + w.buf = nil + if len(words[len(words)-1]) == 0 { + words = words[:len(words)-1] + } + maxwidth := 0 + for _, wd := range words { + if n := utf8.RuneCount(wd); n > maxwidth { + maxwidth = n + } + } + maxwidth++ // space char + wordsPerLine := w.width / maxwidth + if wordsPerLine <= 0 { + wordsPerLine = 1 + } + nlines := (len(words) + wordsPerLine - 1) / wordsPerLine + for i := 0; i < nlines; i++ { + col := 0 + endcol := 0 + for j := i; j < len(words); j += nlines { + endcol += maxwidth + _, err := w.w.Write(words[j]) + if err != nil { + return err + } + col += utf8.RuneCount(words[j]) + if j+nlines < len(words) { + for col < endcol { + _, err := w.w.Write([]byte{' '}) + if err != nil { + return err + } + col++ + } + } + } + _, err := w.w.Write([]byte{'\n'}) + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/kr/text/colwriter/column_test.go b/vendor/github.com/kr/text/colwriter/column_test.go new file mode 100644 index 000000000..ce388f5a2 --- /dev/null +++ b/vendor/github.com/kr/text/colwriter/column_test.go @@ -0,0 +1,90 @@ +package colwriter + +import ( + "bytes" + "testing" +) + +var src = ` +.git +.gitignore +.godir +Procfile: +README.md +api.go +apps.go +auth.go +darwin.go +data.go +dyno.go: +env.go +git.go +help.go +hkdist +linux.go +ls.go +main.go +plugin.go +run.go +scale.go +ssh.go +tail.go +term +unix.go +update.go +version.go +windows.go +`[1:] + +var tests = []struct { + wid int + flag uint + src string + want string +}{ + {80, 0, "", ""}, + {80, 0, src, ` +.git README.md darwin.go git.go ls.go scale.go unix.go +.gitignore api.go data.go help.go main.go ssh.go update.go +.godir apps.go dyno.go: hkdist plugin.go tail.go version.go +Procfile: auth.go env.go linux.go run.go term windows.go +`[1:]}, + {80, BreakOnColon, src, ` +.git .gitignore .godir + +Procfile: +README.md api.go apps.go auth.go darwin.go data.go + +dyno.go: +env.go hkdist main.go scale.go term version.go +git.go linux.go plugin.go ssh.go unix.go windows.go +help.go ls.go run.go tail.go update.go +`[1:]}, + {20, 0, ` +Hello +Γειά σου +안녕 +今日は +`[1:], ` +Hello 안녕 +Γειά σου 今日は +`[1:]}, +} + +func TestWriter(t *testing.T) { + for _, test := range tests { + b := new(bytes.Buffer) + w := NewWriter(b, test.wid, test.flag) + if _, err := w.Write([]byte(test.src)); err != nil { + t.Error(err) + } + if err := w.Flush(); err != nil { + t.Error(err) + } + if g := b.String(); test.want != g { + t.Log("\n" + test.want) + t.Log("\n" + g) + t.Errorf("%q != %q", test.want, g) + } + } +} diff --git a/vendor/github.com/kr/text/doc.go b/vendor/github.com/kr/text/doc.go new file mode 100644 index 000000000..cf4c198f9 --- /dev/null +++ b/vendor/github.com/kr/text/doc.go @@ -0,0 +1,3 @@ +// Package text provides rudimentary functions for manipulating text in +// paragraphs. +package text diff --git a/vendor/github.com/kr/text/indent.go b/vendor/github.com/kr/text/indent.go new file mode 100644 index 000000000..4ebac45c0 --- /dev/null +++ b/vendor/github.com/kr/text/indent.go @@ -0,0 +1,74 @@ +package text + +import ( + "io" +) + +// Indent inserts prefix at the beginning of each non-empty line of s. The +// end-of-line marker is NL. +func Indent(s, prefix string) string { + return string(IndentBytes([]byte(s), []byte(prefix))) +} + +// IndentBytes inserts prefix at the beginning of each non-empty line of b. +// The end-of-line marker is NL. +func IndentBytes(b, prefix []byte) []byte { + var res []byte + bol := true + for _, c := range b { + if bol && c != '\n' { + res = append(res, prefix...) + } + res = append(res, c) + bol = c == '\n' + } + return res +} + +// Writer indents each line of its input. +type indentWriter struct { + w io.Writer + bol bool + pre [][]byte + sel int + off int +} + +// NewIndentWriter makes a new write filter that indents the input +// lines. Each line is prefixed in order with the corresponding +// element of pre. If there are more lines than elements, the last +// element of pre is repeated for each subsequent line. +func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer { + return &indentWriter{ + w: w, + pre: pre, + bol: true, + } +} + +// The only errors returned are from the underlying indentWriter. +func (w *indentWriter) Write(p []byte) (n int, err error) { + for _, c := range p { + if w.bol { + var i int + i, err = w.w.Write(w.pre[w.sel][w.off:]) + w.off += i + if err != nil { + return n, err + } + } + _, err = w.w.Write([]byte{c}) + if err != nil { + return n, err + } + n++ + w.bol = c == '\n' + if w.bol { + w.off = 0 + if w.sel < len(w.pre)-1 { + w.sel++ + } + } + } + return n, nil +} diff --git a/vendor/github.com/kr/text/indent_test.go b/vendor/github.com/kr/text/indent_test.go new file mode 100644 index 000000000..5c723eee8 --- /dev/null +++ b/vendor/github.com/kr/text/indent_test.go @@ -0,0 +1,119 @@ +package text + +import ( + "bytes" + "testing" +) + +type T struct { + inp, exp, pre string +} + +var tests = []T{ + { + "The quick brown fox\njumps over the lazy\ndog.\nBut not quickly.\n", + "xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\nxxxBut not quickly.\n", + "xxx", + }, + { + "The quick brown fox\njumps over the lazy\ndog.\n\nBut not quickly.", + "xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\n\nxxxBut not quickly.", + "xxx", + }, +} + +func TestIndent(t *testing.T) { + for _, test := range tests { + got := Indent(test.inp, test.pre) + if got != test.exp { + t.Errorf("mismatch %q != %q", got, test.exp) + } + } +} + +type IndentWriterTest struct { + inp, exp string + pre []string +} + +var ts = []IndentWriterTest{ + { + ` +The quick brown fox +jumps over the lazy +dog. +But not quickly. +`[1:], + ` +xxxThe quick brown fox +xxxjumps over the lazy +xxxdog. +xxxBut not quickly. +`[1:], + []string{"xxx"}, + }, + { + ` +The quick brown fox +jumps over the lazy +dog. +But not quickly. +`[1:], + ` +xxaThe quick brown fox +xxxjumps over the lazy +xxxdog. +xxxBut not quickly. +`[1:], + []string{"xxa", "xxx"}, + }, + { + ` +The quick brown fox +jumps over the lazy +dog. +But not quickly. +`[1:], + ` +xxaThe quick brown fox +xxbjumps over the lazy +xxcdog. +xxxBut not quickly. +`[1:], + []string{"xxa", "xxb", "xxc", "xxx"}, + }, + { + ` +The quick brown fox +jumps over the lazy +dog. + +But not quickly.`[1:], + ` +xxaThe quick brown fox +xxxjumps over the lazy +xxxdog. +xxx +xxxBut not quickly.`[1:], + []string{"xxa", "xxx"}, + }, +} + +func TestIndentWriter(t *testing.T) { + for _, test := range ts { + b := new(bytes.Buffer) + pre := make([][]byte, len(test.pre)) + for i := range test.pre { + pre[i] = []byte(test.pre[i]) + } + w := NewIndentWriter(b, pre...) + if _, err := w.Write([]byte(test.inp)); err != nil { + t.Error(err) + } + if got := b.String(); got != test.exp { + t.Errorf("mismatch %q != %q", got, test.exp) + t.Log(got) + t.Log(test.exp) + } + } +} diff --git a/vendor/github.com/kr/text/mc/Readme b/vendor/github.com/kr/text/mc/Readme new file mode 100644 index 000000000..519ddc00a --- /dev/null +++ b/vendor/github.com/kr/text/mc/Readme @@ -0,0 +1,9 @@ +Command mc prints in multiple columns. + + Usage: mc [-] [-N] [file...] + +Mc splits the input into as many columns as will fit in N +print positions. If the output is a tty, the default N is +the number of characters in a terminal line; otherwise the +default N is 80. Under option - each input line ending in +a colon ':' is printed separately. diff --git a/vendor/github.com/kr/text/mc/mc.go b/vendor/github.com/kr/text/mc/mc.go new file mode 100644 index 000000000..00169a30f --- /dev/null +++ b/vendor/github.com/kr/text/mc/mc.go @@ -0,0 +1,62 @@ +// Command mc prints in multiple columns. +// +// Usage: mc [-] [-N] [file...] +// +// Mc splits the input into as many columns as will fit in N +// print positions. If the output is a tty, the default N is +// the number of characters in a terminal line; otherwise the +// default N is 80. Under option - each input line ending in +// a colon ':' is printed separately. +package main + +import ( + "github.com/kr/pty" + "github.com/kr/text/colwriter" + "io" + "log" + "os" + "strconv" +) + +func main() { + var width int + var flag uint + args := os.Args[1:] + for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' { + if len(args[0]) > 1 { + width, _ = strconv.Atoi(args[0][1:]) + } else { + flag |= colwriter.BreakOnColon + } + args = args[1:] + } + if width < 1 { + _, width, _ = pty.Getsize(os.Stdout) + } + if width < 1 { + width = 80 + } + + w := colwriter.NewWriter(os.Stdout, width, flag) + if len(args) > 0 { + for _, s := range args { + if f, err := os.Open(s); err == nil { + copyin(w, f) + f.Close() + } else { + log.Println(err) + } + } + } else { + copyin(w, os.Stdin) + } +} + +func copyin(w *colwriter.Writer, r io.Reader) { + if _, err := io.Copy(w, r); err != nil { + log.Println(err) + } + if err := w.Flush(); err != nil { + log.Println(err) + } +} diff --git a/vendor/github.com/kr/text/wrap.go b/vendor/github.com/kr/text/wrap.go new file mode 100644 index 000000000..b09bb0373 --- /dev/null +++ b/vendor/github.com/kr/text/wrap.go @@ -0,0 +1,86 @@ +package text + +import ( + "bytes" + "math" +) + +var ( + nl = []byte{'\n'} + sp = []byte{' '} +) + +const defaultPenalty = 1e5 + +// Wrap wraps s into a paragraph of lines of length lim, with minimal +// raggedness. +func Wrap(s string, lim int) string { + return string(WrapBytes([]byte(s), lim)) +} + +// WrapBytes wraps b into a paragraph of lines of length lim, with minimal +// raggedness. +func WrapBytes(b []byte, lim int) []byte { + words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp) + var lines [][]byte + for _, line := range WrapWords(words, 1, lim, defaultPenalty) { + lines = append(lines, bytes.Join(line, sp)) + } + return bytes.Join(lines, nl) +} + +// WrapWords is the low-level line-breaking algorithm, useful if you need more +// control over the details of the text wrapping process. For most uses, either +// Wrap or WrapBytes will be sufficient and more convenient. +// +// WrapWords splits a list of words into lines with minimal "raggedness", +// treating each byte as one unit, accounting for spc units between adjacent +// words on each line, and attempting to limit lines to lim units. Raggedness +// is the total error over all lines, where error is the square of the +// difference of the length of the line and lim. Too-long lines (which only +// happen when a single word is longer than lim units) have pen penalty units +// added to the error. +func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte { + n := len(words) + + length := make([][]int, n) + for i := 0; i < n; i++ { + length[i] = make([]int, n) + length[i][i] = len(words[i]) + for j := i + 1; j < n; j++ { + length[i][j] = length[i][j-1] + spc + len(words[j]) + } + } + + nbrk := make([]int, n) + cost := make([]int, n) + for i := range cost { + cost[i] = math.MaxInt32 + } + for i := n - 1; i >= 0; i-- { + if length[i][n-1] <= lim || i == n-1 { + cost[i] = 0 + nbrk[i] = n + } else { + for j := i + 1; j < n; j++ { + d := lim - length[i][j-1] + c := d*d + cost[j] + if length[i][j-1] > lim { + c += pen // too-long lines get a worse penalty + } + if c < cost[i] { + cost[i] = c + nbrk[i] = j + } + } + } + } + + var lines [][][]byte + i := 0 + for i < n { + lines = append(lines, words[i:nbrk[i]]) + i = nbrk[i] + } + return lines +} diff --git a/vendor/github.com/kr/text/wrap_test.go b/vendor/github.com/kr/text/wrap_test.go new file mode 100644 index 000000000..634b6e8eb --- /dev/null +++ b/vendor/github.com/kr/text/wrap_test.go @@ -0,0 +1,62 @@ +package text + +import ( + "bytes" + "testing" +) + +var text = "The quick brown fox jumps over the lazy dog." + +func TestWrap(t *testing.T) { + exp := [][]string{ + {"The", "quick", "brown", "fox"}, + {"jumps", "over", "the", "lazy", "dog."}, + } + words := bytes.Split([]byte(text), sp) + got := WrapWords(words, 1, 24, defaultPenalty) + if len(exp) != len(got) { + t.Fail() + } + for i := range exp { + if len(exp[i]) != len(got[i]) { + t.Fail() + } + for j := range exp[i] { + if exp[i][j] != string(got[i][j]) { + t.Fatal(i, exp[i][j], got[i][j]) + } + } + } +} + +func TestWrapNarrow(t *testing.T) { + exp := "The\nquick\nbrown\nfox\njumps\nover\nthe\nlazy\ndog." + if Wrap(text, 5) != exp { + t.Fail() + } +} + +func TestWrapOneLine(t *testing.T) { + exp := "The quick brown fox jumps over the lazy dog." + if Wrap(text, 500) != exp { + t.Fail() + } +} + +func TestWrapBug1(t *testing.T) { + cases := []struct { + limit int + text string + want string + }{ + {4, "aaaaa", "aaaaa"}, + {4, "a aaaaa", "a\naaaaa"}, + } + + for _, test := range cases { + got := Wrap(test.text, test.limit) + if got != test.want { + t.Errorf("Wrap(%q, %d) = %q want %q", test.text, test.limit, got, test.want) + } + } +} diff --git a/vendor/github.com/pborman/uuid/dce.go b/vendor/github.com/pborman/uuid/dce.go old mode 100755 new mode 100644 diff --git a/vendor/github.com/pborman/uuid/doc.go b/vendor/github.com/pborman/uuid/doc.go old mode 100755 new mode 100644 diff --git a/vendor/github.com/pborman/uuid/node.go b/vendor/github.com/pborman/uuid/node.go old mode 100755 new mode 100644 diff --git a/vendor/github.com/pborman/uuid/time.go b/vendor/github.com/pborman/uuid/time.go old mode 100755 new mode 100644 diff --git a/vendor/github.com/pelletier/go-toml/.gitignore b/vendor/github.com/pelletier/go-toml/.gitignore deleted file mode 100644 index 99e38bbc5..000000000 --- a/vendor/github.com/pelletier/go-toml/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -test_program/test_program_bin -fuzz/ diff --git a/vendor/github.com/pelletier/go-toml/.travis.yml b/vendor/github.com/pelletier/go-toml/.travis.yml deleted file mode 100644 index c9fbf304b..000000000 --- a/vendor/github.com/pelletier/go-toml/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: false -language: go -go: - - 1.8.x - - 1.9.x - - 1.10.x - - tip -matrix: - allow_failures: - - go: tip - fast_finish: true -script: - - if [ -n "$(go fmt ./...)" ]; then exit 1; fi - - ./test.sh - - ./benchmark.sh $TRAVIS_BRANCH https://github.com/$TRAVIS_REPO_SLUG.git -before_install: - - go get github.com/axw/gocov/gocov - - go get github.com/mattn/goveralls - - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi -branches: - only: [master] -after_success: - - $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=coverage.out -repotoken $COVERALLS_TOKEN diff --git a/vendor/github.com/pelletier/go-toml/README.md b/vendor/github.com/pelletier/go-toml/README.md deleted file mode 100644 index 0d357acf3..000000000 --- a/vendor/github.com/pelletier/go-toml/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# go-toml - -Go library for the [TOML](https://github.com/mojombo/toml) format. - -This library supports TOML version -[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) - -[![GoDoc](https://godoc.org/github.com/pelletier/go-toml?status.svg)](http://godoc.org/github.com/pelletier/go-toml) -[![license](https://img.shields.io/github/license/pelletier/go-toml.svg)](https://github.com/pelletier/go-toml/blob/master/LICENSE) -[![Build Status](https://travis-ci.org/pelletier/go-toml.svg?branch=master)](https://travis-ci.org/pelletier/go-toml) -[![Coverage Status](https://coveralls.io/repos/github/pelletier/go-toml/badge.svg?branch=master)](https://coveralls.io/github/pelletier/go-toml?branch=master) -[![Go Report Card](https://goreportcard.com/badge/github.com/pelletier/go-toml)](https://goreportcard.com/report/github.com/pelletier/go-toml) - -## Features - -Go-toml provides the following features for using data parsed from TOML documents: - -* Load TOML documents from files and string data -* Easily navigate TOML structure using Tree -* Mashaling and unmarshaling to and from data structures -* Line & column position data for all parsed elements -* [Query support similar to JSON-Path](query/) -* Syntax errors contain line and column numbers - -## Import - -```go -import "github.com/pelletier/go-toml" -``` - -## Usage example - -Read a TOML document: - -```go -config, _ := toml.Load(` -[postgres] -user = "pelletier" -password = "mypassword"`) -// retrieve data directly -user := config.Get("postgres.user").(string) - -// or using an intermediate object -postgresConfig := config.Get("postgres").(*toml.Tree) -password := postgresConfig.Get("password").(string) -``` - -Or use Unmarshal: - -```go -type Postgres struct { - User string - Password string -} -type Config struct { - Postgres Postgres -} - -doc := []byte(` -[Postgres] -User = "pelletier" -Password = "mypassword"`) - -config := Config{} -toml.Unmarshal(doc, &config) -fmt.Println("user=", config.Postgres.User) -``` - -Or use a query: - -```go -// use a query to gather elements without walking the tree -q, _ := query.Compile("$..[user,password]") -results := q.Execute(config) -for ii, item := range results.Values() { - fmt.Println("Query result %d: %v", ii, item) -} -``` - -## Documentation - -The documentation and additional examples are available at -[godoc.org](http://godoc.org/github.com/pelletier/go-toml). - -## Tools - -Go-toml provides two handy command line tools: - -* `tomll`: Reads TOML files and lint them. - - ``` - go install github.com/pelletier/go-toml/cmd/tomll - tomll --help - ``` -* `tomljson`: Reads a TOML file and outputs its JSON representation. - - ``` - go install github.com/pelletier/go-toml/cmd/tomljson - tomljson --help - ``` - -## Contribute - -Feel free to report bugs and patches using GitHub's pull requests system on -[pelletier/go-toml](https://github.com/pelletier/go-toml). Any feedback would be -much appreciated! - -### Run tests - -You have to make sure two kind of tests run: - -1. The Go unit tests -2. The TOML examples base - -You can run both of them using `./test.sh`. - -### Fuzzing - -The script `./fuzz.sh` is available to -run [go-fuzz](https://github.com/dvyukov/go-fuzz) on go-toml. - -## Versioning - -Go-toml follows [Semantic Versioning](http://semver.org/). The supported version -of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of -this document. The last two major versions of Go are supported -(see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)). - -## License - -The MIT License (MIT). Read [LICENSE](LICENSE). diff --git a/vendor/github.com/pelletier/go-toml/benchmark.json b/vendor/github.com/pelletier/go-toml/benchmark.json deleted file mode 100644 index 86f99c6a8..000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "array": { - "key1": [ - 1, - 2, - 3 - ], - "key2": [ - "red", - "yellow", - "green" - ], - "key3": [ - [ - 1, - 2 - ], - [ - 3, - 4, - 5 - ] - ], - "key4": [ - [ - 1, - 2 - ], - [ - "a", - "b", - "c" - ] - ], - "key5": [ - 1, - 2, - 3 - ], - "key6": [ - 1, - 2 - ] - }, - "boolean": { - "False": false, - "True": true - }, - "datetime": { - "key1": "1979-05-27T07:32:00Z", - "key2": "1979-05-27T00:32:00-07:00", - "key3": "1979-05-27T00:32:00.999999-07:00" - }, - "float": { - "both": { - "key": 6.626e-34 - }, - "exponent": { - "key1": 5e+22, - "key2": 1000000, - "key3": -0.02 - }, - "fractional": { - "key1": 1, - "key2": 3.1415, - "key3": -0.01 - }, - "underscores": { - "key1": 9224617.445991227, - "key2": 1e+100 - } - }, - "fruit": [{ - "name": "apple", - "physical": { - "color": "red", - "shape": "round" - }, - "variety": [{ - "name": "red delicious" - }, - { - "name": "granny smith" - } - ] - }, - { - "name": "banana", - "variety": [{ - "name": "plantain" - }] - } - ], - "integer": { - "key1": 99, - "key2": 42, - "key3": 0, - "key4": -17, - "underscores": { - "key1": 1000, - "key2": 5349221, - "key3": 12345 - } - }, - "products": [{ - "name": "Hammer", - "sku": 738594937 - }, - {}, - { - "color": "gray", - "name": "Nail", - "sku": 284758393 - } - ], - "string": { - "basic": { - "basic": "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF." - }, - "literal": { - "multiline": { - "lines": "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", - "regex2": "I [dw]on't need \\d{2} apples" - }, - "quoted": "Tom \"Dubs\" Preston-Werner", - "regex": "\u003c\\i\\c*\\s*\u003e", - "winpath": "C:\\Users\\nodejs\\templates", - "winpath2": "\\\\ServerX\\admin$\\system32\\" - }, - "multiline": { - "continued": { - "key1": "The quick brown fox jumps over the lazy dog.", - "key2": "The quick brown fox jumps over the lazy dog.", - "key3": "The quick brown fox jumps over the lazy dog." - }, - "key1": "One\nTwo", - "key2": "One\nTwo", - "key3": "One\nTwo" - } - }, - "table": { - "inline": { - "name": { - "first": "Tom", - "last": "Preston-Werner" - }, - "point": { - "x": 1, - "y": 2 - } - }, - "key": "value", - "subtable": { - "key": "another value" - } - }, - "x": { - "y": { - "z": { - "w": {} - } - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/benchmark.sh b/vendor/github.com/pelletier/go-toml/benchmark.sh deleted file mode 100755 index 8b8bb528e..000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -set -e - -reference_ref=${1:-master} -reference_git=${2:-.} - -if ! `hash benchstat 2>/dev/null`; then - echo "Installing benchstat" - go get golang.org/x/perf/cmd/benchstat - go install golang.org/x/perf/cmd/benchstat -fi - -tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX` -ref_tempdir="${tempdir}/ref" -ref_benchmark="${ref_tempdir}/benchmark-`echo -n ${reference_ref}|tr -s '/' '-'`.txt" -local_benchmark="`pwd`/benchmark-local.txt" - -echo "=== ${reference_ref} (${ref_tempdir})" -git clone ${reference_git} ${ref_tempdir} >/dev/null 2>/dev/null -pushd ${ref_tempdir} >/dev/null -git checkout ${reference_ref} >/dev/null 2>/dev/null -go test -bench=. -benchmem | tee ${ref_benchmark} -popd >/dev/null - -echo "" -echo "=== local" -go test -bench=. -benchmem | tee ${local_benchmark} - -echo "" -echo "=== diff" -benchstat -delta-test=none ${ref_benchmark} ${local_benchmark} \ No newline at end of file diff --git a/vendor/github.com/pelletier/go-toml/benchmark.toml b/vendor/github.com/pelletier/go-toml/benchmark.toml deleted file mode 100644 index dfd77e096..000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.toml +++ /dev/null @@ -1,244 +0,0 @@ -################################################################################ -## Comment - -# Speak your mind with the hash symbol. They go from the symbol to the end of -# the line. - - -################################################################################ -## Table - -# Tables (also known as hash tables or dictionaries) are collections of -# key/value pairs. They appear in square brackets on a line by themselves. - -[table] - -key = "value" # Yeah, you can do this. - -# Nested tables are denoted by table names with dots in them. Name your tables -# whatever crap you please, just don't use #, ., [ or ]. - -[table.subtable] - -key = "another value" - -# You don't need to specify all the super-tables if you don't want to. TOML -# knows how to do it for you. - -# [x] you -# [x.y] don't -# [x.y.z] need these -[x.y.z.w] # for this to work - - -################################################################################ -## Inline Table - -# Inline tables provide a more compact syntax for expressing tables. They are -# especially useful for grouped data that can otherwise quickly become verbose. -# Inline tables are enclosed in curly braces `{` and `}`. No newlines are -# allowed between the curly braces unless they are valid within a value. - -[table.inline] - -name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 } - - -################################################################################ -## String - -# There are four ways to express strings: basic, multi-line basic, literal, and -# multi-line literal. All strings must contain only valid UTF-8 characters. - -[string.basic] - -basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." - -[string.multiline] - -# The following strings are byte-for-byte equivalent: -key1 = "One\nTwo" -key2 = """One\nTwo""" -key3 = """ -One -Two""" - -[string.multiline.continued] - -# The following strings are byte-for-byte equivalent: -key1 = "The quick brown fox jumps over the lazy dog." - -key2 = """ -The quick brown \ - - - fox jumps over \ - the lazy dog.""" - -key3 = """\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """ - -[string.literal] - -# What you see is what you get. -winpath = 'C:\Users\nodejs\templates' -winpath2 = '\\ServerX\admin$\system32\' -quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>' - - -[string.literal.multiline] - -regex2 = '''I [dw]on't need \d{2} apples''' -lines = ''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -''' - - -################################################################################ -## Integer - -# Integers are whole numbers. Positive numbers may be prefixed with a plus sign. -# Negative numbers are prefixed with a minus sign. - -[integer] - -key1 = +99 -key2 = 42 -key3 = 0 -key4 = -17 - -[integer.underscores] - -# For large numbers, you may use underscores to enhance readability. Each -# underscore must be surrounded by at least one digit. -key1 = 1_000 -key2 = 5_349_221 -key3 = 1_2_3_4_5 # valid but inadvisable - - -################################################################################ -## Float - -# A float consists of an integer part (which may be prefixed with a plus or -# minus sign) followed by a fractional part and/or an exponent part. - -[float.fractional] - -key1 = +1.0 -key2 = 3.1415 -key3 = -0.01 - -[float.exponent] - -key1 = 5e+22 -key2 = 1e6 -key3 = -2E-2 - -[float.both] - -key = 6.626e-34 - -[float.underscores] - -key1 = 9_224_617.445_991_228_313 -key2 = 1e1_00 - - -################################################################################ -## Boolean - -# Booleans are just the tokens you're used to. Always lowercase. - -[boolean] - -True = true -False = false - - -################################################################################ -## Datetime - -# Datetimes are RFC 3339 dates. - -[datetime] - -key1 = 1979-05-27T07:32:00Z -key2 = 1979-05-27T00:32:00-07:00 -key3 = 1979-05-27T00:32:00.999999-07:00 - - -################################################################################ -## Array - -# Arrays are square brackets with other primitives inside. Whitespace is -# ignored. Elements are separated by commas. Data types may not be mixed. - -[array] - -key1 = [ 1, 2, 3 ] -key2 = [ "red", "yellow", "green" ] -key3 = [ [ 1, 2 ], [3, 4, 5] ] -#key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok - -# Arrays can also be multiline. So in addition to ignoring whitespace, arrays -# also ignore newlines between the brackets. Terminating commas are ok before -# the closing bracket. - -key5 = [ - 1, 2, 3 -] -key6 = [ - 1, - 2, # this is ok -] - - -################################################################################ -## Array of Tables - -# These can be expressed by using a table name in double brackets. Each table -# with the same double bracketed name will be an element in the array. The -# tables are inserted in the order encountered. - -[[products]] - -name = "Hammer" -sku = 738594937 - -[[products]] - -[[products]] - -name = "Nail" -sku = 284758393 -color = "gray" - - -# You can create nested arrays of tables as well. - -[[fruit]] - name = "apple" - - [fruit.physical] - color = "red" - shape = "round" - - [[fruit.variety]] - name = "red delicious" - - [[fruit.variety]] - name = "granny smith" - -[[fruit]] - name = "banana" - - [[fruit.variety]] - name = "plantain" diff --git a/vendor/github.com/pelletier/go-toml/benchmark.yml b/vendor/github.com/pelletier/go-toml/benchmark.yml deleted file mode 100644 index 0bd19f08a..000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.yml +++ /dev/null @@ -1,121 +0,0 @@ ---- -array: - key1: - - 1 - - 2 - - 3 - key2: - - red - - yellow - - green - key3: - - - 1 - - 2 - - - 3 - - 4 - - 5 - key4: - - - 1 - - 2 - - - a - - b - - c - key5: - - 1 - - 2 - - 3 - key6: - - 1 - - 2 -boolean: - 'False': false - 'True': true -datetime: - key1: '1979-05-27T07:32:00Z' - key2: '1979-05-27T00:32:00-07:00' - key3: '1979-05-27T00:32:00.999999-07:00' -float: - both: - key: 6.626e-34 - exponent: - key1: 5.0e+22 - key2: 1000000 - key3: -0.02 - fractional: - key1: 1 - key2: 3.1415 - key3: -0.01 - underscores: - key1: 9224617.445991227 - key2: 1.0e+100 -fruit: -- name: apple - physical: - color: red - shape: round - variety: - - name: red delicious - - name: granny smith -- name: banana - variety: - - name: plantain -integer: - key1: 99 - key2: 42 - key3: 0 - key4: -17 - underscores: - key1: 1000 - key2: 5349221 - key3: 12345 -products: -- name: Hammer - sku: 738594937 -- {} -- color: gray - name: Nail - sku: 284758393 -string: - basic: - basic: "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF." - literal: - multiline: - lines: | - The first newline is - trimmed in raw strings. - All other whitespace - is preserved. - regex2: I [dw]on't need \d{2} apples - quoted: Tom "Dubs" Preston-Werner - regex: "<\\i\\c*\\s*>" - winpath: C:\Users\nodejs\templates - winpath2: "\\\\ServerX\\admin$\\system32\\" - multiline: - continued: - key1: The quick brown fox jumps over the lazy dog. - key2: The quick brown fox jumps over the lazy dog. - key3: The quick brown fox jumps over the lazy dog. - key1: |- - One - Two - key2: |- - One - Two - key3: |- - One - Two -table: - inline: - name: - first: Tom - last: Preston-Werner - point: - x: 1 - y: 2 - key: value - subtable: - key: another value -x: - y: - z: - w: {} diff --git a/vendor/github.com/pelletier/go-toml/benchmark_test.go b/vendor/github.com/pelletier/go-toml/benchmark_test.go deleted file mode 100644 index e1f209dfa..000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package toml - -import ( - "bytes" - "encoding/json" - "io/ioutil" - "testing" - "time" - - burntsushi "github.com/BurntSushi/toml" - yaml "gopkg.in/yaml.v2" -) - -type benchmarkDoc struct { - Table struct { - Key string - Subtable struct { - Key string - } - Inline struct { - Name struct { - First string - Last string - } - Point struct { - X int64 - U int64 - } - } - } - String struct { - Basic struct { - Basic string - } - Multiline struct { - Key1 string - Key2 string - Key3 string - Continued struct { - Key1 string - Key2 string - Key3 string - } - } - Literal struct { - Winpath string - Winpath2 string - Quoted string - Regex string - Multiline struct { - Regex2 string - Lines string - } - } - } - Integer struct { - Key1 int64 - Key2 int64 - Key3 int64 - Key4 int64 - Underscores struct { - Key1 int64 - Key2 int64 - Key3 int64 - } - } - Float struct { - Fractional struct { - Key1 float64 - Key2 float64 - Key3 float64 - } - Exponent struct { - Key1 float64 - Key2 float64 - Key3 float64 - } - Both struct { - Key float64 - } - Underscores struct { - Key1 float64 - Key2 float64 - } - } - Boolean struct { - True bool - False bool - } - Datetime struct { - Key1 time.Time - Key2 time.Time - Key3 time.Time - } - Array struct { - Key1 []int64 - Key2 []string - Key3 [][]int64 - // TODO: Key4 not supported by go-toml's Unmarshal - Key5 []int64 - Key6 []int64 - } - Products []struct { - Name string - Sku int64 - Color string - } - Fruit []struct { - Name string - Physical struct { - Color string - Shape string - Variety []struct { - Name string - } - } - } -} - -func BenchmarkParseToml(b *testing.B) { - fileBytes, err := ioutil.ReadFile("benchmark.toml") - if err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := LoadReader(bytes.NewReader(fileBytes)) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkUnmarshalToml(b *testing.B) { - bytes, err := ioutil.ReadFile("benchmark.toml") - if err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - target := benchmarkDoc{} - err := Unmarshal(bytes, &target) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkUnmarshalBurntSushiToml(b *testing.B) { - bytes, err := ioutil.ReadFile("benchmark.toml") - if err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - target := benchmarkDoc{} - err := burntsushi.Unmarshal(bytes, &target) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkUnmarshalJson(b *testing.B) { - bytes, err := ioutil.ReadFile("benchmark.json") - if err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - target := benchmarkDoc{} - err := json.Unmarshal(bytes, &target) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkUnmarshalYaml(b *testing.B) { - bytes, err := ioutil.ReadFile("benchmark.yml") - if err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - target := benchmarkDoc{} - err := yaml.Unmarshal(bytes, &target) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/cmd/tomljson/main.go b/vendor/github.com/pelletier/go-toml/cmd/tomljson/main.go deleted file mode 100644 index 322315b53..000000000 --- a/vendor/github.com/pelletier/go-toml/cmd/tomljson/main.go +++ /dev/null @@ -1,71 +0,0 @@ -// Tomljson reads TOML and converts to JSON. -// -// Usage: -// cat file.toml | tomljson > file.json -// tomljson file1.toml > file.json -package main - -import ( - "encoding/json" - "flag" - "fmt" - "io" - "os" - - "github.com/pelletier/go-toml" -) - -func main() { - flag.Usage = func() { - fmt.Fprintln(os.Stderr, "tomljson can be used in two ways:") - fmt.Fprintln(os.Stderr, "Writing to STDIN and reading from STDOUT:") - fmt.Fprintln(os.Stderr, " cat file.toml | tomljson > file.json") - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, "Reading from a file name:") - fmt.Fprintln(os.Stderr, " tomljson file.toml") - } - flag.Parse() - os.Exit(processMain(flag.Args(), os.Stdin, os.Stdout, os.Stderr)) -} - -func processMain(files []string, defaultInput io.Reader, output io.Writer, errorOutput io.Writer) int { - // read from stdin and print to stdout - inputReader := defaultInput - - if len(files) > 0 { - var err error - inputReader, err = os.Open(files[0]) - if err != nil { - printError(err, errorOutput) - return -1 - } - } - s, err := reader(inputReader) - if err != nil { - printError(err, errorOutput) - return -1 - } - io.WriteString(output, s+"\n") - return 0 -} - -func printError(err error, output io.Writer) { - io.WriteString(output, err.Error()+"\n") -} - -func reader(r io.Reader) (string, error) { - tree, err := toml.LoadReader(r) - if err != nil { - return "", err - } - return mapToJSON(tree) -} - -func mapToJSON(tree *toml.Tree) (string, error) { - treeMap := tree.ToMap() - bytes, err := json.MarshalIndent(treeMap, "", " ") - if err != nil { - return "", err - } - return string(bytes[:]), nil -} diff --git a/vendor/github.com/pelletier/go-toml/cmd/tomljson/main_test.go b/vendor/github.com/pelletier/go-toml/cmd/tomljson/main_test.go deleted file mode 100644 index 0b4bdbb11..000000000 --- a/vendor/github.com/pelletier/go-toml/cmd/tomljson/main_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "bytes" - "io/ioutil" - "os" - "strings" - "testing" -) - -func expectBufferEquality(t *testing.T, name string, buffer *bytes.Buffer, expected string) { - output := buffer.String() - if output != expected { - t.Errorf("incorrect %s:\n%s\n\nexpected %s:\n%s", name, output, name, expected) - t.Log([]rune(output)) - t.Log([]rune(expected)) - } -} - -func expectProcessMainResults(t *testing.T, input string, args []string, exitCode int, expectedOutput string, expectedError string) { - inputReader := strings.NewReader(input) - outputBuffer := new(bytes.Buffer) - errorBuffer := new(bytes.Buffer) - - returnCode := processMain(args, inputReader, outputBuffer, errorBuffer) - - expectBufferEquality(t, "output", outputBuffer, expectedOutput) - expectBufferEquality(t, "error", errorBuffer, expectedError) - - if returnCode != exitCode { - t.Error("incorrect return code:", returnCode, "expected", exitCode) - } -} - -func TestProcessMainReadFromStdin(t *testing.T) { - input := ` - [mytoml] - a = 42` - expectedOutput := `{ - "mytoml": { - "a": 42 - } -} -` - expectedError := `` - expectedExitCode := 0 - - expectProcessMainResults(t, input, []string{}, expectedExitCode, expectedOutput, expectedError) -} - -func TestProcessMainReadFromFile(t *testing.T) { - input := ` - [mytoml] - a = 42` - - tmpfile, err := ioutil.TempFile("", "example.toml") - if err != nil { - t.Fatal(err) - } - if _, err := tmpfile.Write([]byte(input)); err != nil { - t.Fatal(err) - } - - defer os.Remove(tmpfile.Name()) - - expectedOutput := `{ - "mytoml": { - "a": 42 - } -} -` - expectedError := `` - expectedExitCode := 0 - - expectProcessMainResults(t, ``, []string{tmpfile.Name()}, expectedExitCode, expectedOutput, expectedError) -} - -func TestProcessMainReadFromMissingFile(t *testing.T) { - expectedError := `open /this/file/does/not/exist: no such file or directory -` - expectProcessMainResults(t, ``, []string{"/this/file/does/not/exist"}, -1, ``, expectedError) -} diff --git a/vendor/github.com/pelletier/go-toml/cmd/tomll/main.go b/vendor/github.com/pelletier/go-toml/cmd/tomll/main.go deleted file mode 100644 index 93ab0c962..000000000 --- a/vendor/github.com/pelletier/go-toml/cmd/tomll/main.go +++ /dev/null @@ -1,65 +0,0 @@ -// Tomll is a linter for TOML -// -// Usage: -// cat file.toml | tomll > file_linted.toml -// tomll file1.toml file2.toml # lint the two files in place -package main - -import ( - "flag" - "fmt" - "io" - "io/ioutil" - "os" - - "github.com/pelletier/go-toml" -) - -func main() { - flag.Usage = func() { - fmt.Fprintln(os.Stderr, "tomll can be used in two ways:") - fmt.Fprintln(os.Stderr, "Writing to STDIN and reading from STDOUT:") - fmt.Fprintln(os.Stderr, " cat file.toml | tomll > file.toml") - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, "Reading and updating a list of files:") - fmt.Fprintln(os.Stderr, " tomll a.toml b.toml c.toml") - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, "When given a list of files, tomll will modify all files in place without asking.") - } - flag.Parse() - // read from stdin and print to stdout - if flag.NArg() == 0 { - s, err := lintReader(os.Stdin) - if err != nil { - io.WriteString(os.Stderr, err.Error()) - os.Exit(-1) - } - io.WriteString(os.Stdout, s) - } else { - // otherwise modify a list of files - for _, filename := range flag.Args() { - s, err := lintFile(filename) - if err != nil { - io.WriteString(os.Stderr, err.Error()) - os.Exit(-1) - } - ioutil.WriteFile(filename, []byte(s), 0644) - } - } -} - -func lintFile(filename string) (string, error) { - tree, err := toml.LoadFile(filename) - if err != nil { - return "", err - } - return tree.String(), nil -} - -func lintReader(r io.Reader) (string, error) { - tree, err := toml.LoadReader(r) - if err != nil { - return "", err - } - return tree.String(), nil -} diff --git a/vendor/github.com/pelletier/go-toml/doc.go b/vendor/github.com/pelletier/go-toml/doc.go deleted file mode 100644 index d5fd98c02..000000000 --- a/vendor/github.com/pelletier/go-toml/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -// Package toml is a TOML parser and manipulation library. -// -// This version supports the specification as described in -// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md -// -// Marshaling -// -// Go-toml can marshal and unmarshal TOML documents from and to data -// structures. -// -// TOML document as a tree -// -// Go-toml can operate on a TOML document as a tree. Use one of the Load* -// functions to parse TOML data and obtain a Tree instance, then one of its -// methods to manipulate the tree. -// -// JSONPath-like queries -// -// The package github.com/pelletier/go-toml/query implements a system -// similar to JSONPath to quickly retrieve elements of a TOML document using a -// single expression. See the package documentation for more information. -// -package toml diff --git a/vendor/github.com/pelletier/go-toml/doc_test.go b/vendor/github.com/pelletier/go-toml/doc_test.go deleted file mode 100644 index 3b8171b22..000000000 --- a/vendor/github.com/pelletier/go-toml/doc_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// code examples for godoc - -package toml_test - -import ( - "fmt" - "log" - - toml "github.com/pelletier/go-toml" -) - -func Example_tree() { - config, err := toml.LoadFile("config.toml") - - if err != nil { - fmt.Println("Error ", err.Error()) - } else { - // retrieve data directly - user := config.Get("postgres.user").(string) - password := config.Get("postgres.password").(string) - - // or using an intermediate object - configTree := config.Get("postgres").(*toml.Tree) - user = configTree.Get("user").(string) - password = configTree.Get("password").(string) - fmt.Println("User is", user, " and password is", password) - - // show where elements are in the file - fmt.Printf("User position: %v\n", configTree.GetPosition("user")) - fmt.Printf("Password position: %v\n", configTree.GetPosition("password")) - } -} - -func Example_unmarshal() { - type Employer struct { - Name string - Phone string - } - type Person struct { - Name string - Age int64 - Employer Employer - } - - document := []byte(` - name = "John" - age = 30 - [employer] - name = "Company Inc." - phone = "+1 234 567 89012" - `) - - person := Person{} - toml.Unmarshal(document, &person) - fmt.Println(person.Name, "is", person.Age, "and works at", person.Employer.Name) - // Output: - // John is 30 and works at Company Inc. -} - -func ExampleMarshal() { - type Postgres struct { - User string `toml:"user"` - Password string `toml:"password"` - Database string `toml:"db" commented:"true" comment:"not used anymore"` - } - type Config struct { - Postgres Postgres `toml:"postgres" comment:"Postgres configuration"` - } - - config := Config{Postgres{User: "pelletier", Password: "mypassword", Database: "old_database"}} - b, err := toml.Marshal(config) - if err != nil { - log.Fatal(err) - } - fmt.Println(string(b)) - // Output: - // # Postgres configuration - // [postgres] - // - // # not used anymore - // # db = "old_database" - // password = "mypassword" - // user = "pelletier" -} - -func ExampleUnmarshal() { - type Postgres struct { - User string - Password string - } - type Config struct { - Postgres Postgres - } - - doc := []byte(` - [postgres] - user = "pelletier" - password = "mypassword"`) - - config := Config{} - toml.Unmarshal(doc, &config) - fmt.Println("user=", config.Postgres.User) - // Output: - // user= pelletier -} diff --git a/vendor/github.com/pelletier/go-toml/fuzz.go b/vendor/github.com/pelletier/go-toml/fuzz.go deleted file mode 100644 index 14570c8d3..000000000 --- a/vendor/github.com/pelletier/go-toml/fuzz.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build gofuzz - -package toml - -func Fuzz(data []byte) int { - tree, err := LoadBytes(data) - if err != nil { - if tree != nil { - panic("tree must be nil if there is an error") - } - return 0 - } - - str, err := tree.ToTomlString() - if err != nil { - if str != "" { - panic(`str must be "" if there is an error`) - } - panic(err) - } - - tree, err = Load(str) - if err != nil { - if tree != nil { - panic("tree must be nil if there is an error") - } - return 0 - } - - return 1 -} diff --git a/vendor/github.com/pelletier/go-toml/fuzz.sh b/vendor/github.com/pelletier/go-toml/fuzz.sh deleted file mode 100755 index 3204b4c44..000000000 --- a/vendor/github.com/pelletier/go-toml/fuzz.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /bin/sh -set -eu - -go get github.com/dvyukov/go-fuzz/go-fuzz -go get github.com/dvyukov/go-fuzz/go-fuzz-build - -if [ ! -e toml-fuzz.zip ]; then - go-fuzz-build github.com/pelletier/go-toml -fi - -rm -fr fuzz -mkdir -p fuzz/corpus -cp *.toml fuzz/corpus - -go-fuzz -bin=toml-fuzz.zip -workdir=fuzz diff --git a/vendor/github.com/pelletier/go-toml/keysparsing.go b/vendor/github.com/pelletier/go-toml/keysparsing.go deleted file mode 100644 index 284db6467..000000000 --- a/vendor/github.com/pelletier/go-toml/keysparsing.go +++ /dev/null @@ -1,85 +0,0 @@ -// Parsing keys handling both bare and quoted keys. - -package toml - -import ( - "bytes" - "errors" - "fmt" - "unicode" -) - -// Convert the bare key group string to an array. -// The input supports double quotation to allow "." inside the key name, -// but escape sequences are not supported. Lexers must unescape them beforehand. -func parseKey(key string) ([]string, error) { - groups := []string{} - var buffer bytes.Buffer - inQuotes := false - wasInQuotes := false - ignoreSpace := true - expectDot := false - - for _, char := range key { - if ignoreSpace { - if char == ' ' { - continue - } - ignoreSpace = false - } - switch char { - case '"': - if inQuotes { - groups = append(groups, buffer.String()) - buffer.Reset() - wasInQuotes = true - } - inQuotes = !inQuotes - expectDot = false - case '.': - if inQuotes { - buffer.WriteRune(char) - } else { - if !wasInQuotes { - if buffer.Len() == 0 { - return nil, errors.New("empty table key") - } - groups = append(groups, buffer.String()) - buffer.Reset() - } - ignoreSpace = true - expectDot = false - wasInQuotes = false - } - case ' ': - if inQuotes { - buffer.WriteRune(char) - } else { - expectDot = true - } - default: - if !inQuotes && !isValidBareChar(char) { - return nil, fmt.Errorf("invalid bare character: %c", char) - } - if !inQuotes && expectDot { - return nil, errors.New("what?") - } - buffer.WriteRune(char) - expectDot = false - } - } - if inQuotes { - return nil, errors.New("mismatched quotes") - } - if buffer.Len() > 0 { - groups = append(groups, buffer.String()) - } - if len(groups) == 0 { - return nil, errors.New("empty key") - } - return groups, nil -} - -func isValidBareChar(r rune) bool { - return isAlphanumeric(r) || r == '-' || unicode.IsNumber(r) -} diff --git a/vendor/github.com/pelletier/go-toml/keysparsing_test.go b/vendor/github.com/pelletier/go-toml/keysparsing_test.go deleted file mode 100644 index 84cb82604..000000000 --- a/vendor/github.com/pelletier/go-toml/keysparsing_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package toml - -import ( - "fmt" - "testing" -) - -func testResult(t *testing.T, key string, expected []string) { - parsed, err := parseKey(key) - t.Logf("key=%s expected=%s parsed=%s", key, expected, parsed) - if err != nil { - t.Fatal("Unexpected error:", err) - } - if len(expected) != len(parsed) { - t.Fatal("Expected length", len(expected), "but", len(parsed), "parsed") - } - for index, expectedKey := range expected { - if expectedKey != parsed[index] { - t.Fatal("Expected", expectedKey, "at index", index, "but found", parsed[index]) - } - } -} - -func testError(t *testing.T, key string, expectedError string) { - res, err := parseKey(key) - if err == nil { - t.Fatalf("Expected error, but succesfully parsed key %s", res) - } - if fmt.Sprintf("%s", err) != expectedError { - t.Fatalf("Expected error \"%s\", but got \"%s\".", expectedError, err) - } -} - -func TestBareKeyBasic(t *testing.T) { - testResult(t, "test", []string{"test"}) -} - -func TestBareKeyDotted(t *testing.T) { - testResult(t, "this.is.a.key", []string{"this", "is", "a", "key"}) -} - -func TestDottedKeyBasic(t *testing.T) { - testResult(t, "\"a.dotted.key\"", []string{"a.dotted.key"}) -} - -func TestBaseKeyPound(t *testing.T) { - testError(t, "hello#world", "invalid bare character: #") -} - -func TestQuotedKeys(t *testing.T) { - testResult(t, `hello."foo".bar`, []string{"hello", "foo", "bar"}) - testResult(t, `"hello!"`, []string{"hello!"}) - testResult(t, `foo."ba.r".baz`, []string{"foo", "ba.r", "baz"}) - - // escape sequences must not be converted - testResult(t, `"hello\tworld"`, []string{`hello\tworld`}) -} - -func TestEmptyKey(t *testing.T) { - testError(t, "", "empty key") - testError(t, " ", "empty key") - testResult(t, `""`, []string{""}) -} diff --git a/vendor/github.com/pelletier/go-toml/lexer.go b/vendor/github.com/pelletier/go-toml/lexer.go deleted file mode 100644 index d11de4285..000000000 --- a/vendor/github.com/pelletier/go-toml/lexer.go +++ /dev/null @@ -1,750 +0,0 @@ -// TOML lexer. -// -// Written using the principles developed by Rob Pike in -// http://www.youtube.com/watch?v=HxaD_trXwRE - -package toml - -import ( - "bytes" - "errors" - "fmt" - "regexp" - "strconv" - "strings" -) - -var dateRegexp *regexp.Regexp - -// Define state functions -type tomlLexStateFn func() tomlLexStateFn - -// Define lexer -type tomlLexer struct { - inputIdx int - input []rune // Textual source - currentTokenStart int - currentTokenStop int - tokens []token - depth int - line int - col int - endbufferLine int - endbufferCol int -} - -// Basic read operations on input - -func (l *tomlLexer) read() rune { - r := l.peek() - if r == '\n' { - l.endbufferLine++ - l.endbufferCol = 1 - } else { - l.endbufferCol++ - } - l.inputIdx++ - return r -} - -func (l *tomlLexer) next() rune { - r := l.read() - - if r != eof { - l.currentTokenStop++ - } - return r -} - -func (l *tomlLexer) ignore() { - l.currentTokenStart = l.currentTokenStop - l.line = l.endbufferLine - l.col = l.endbufferCol -} - -func (l *tomlLexer) skip() { - l.next() - l.ignore() -} - -func (l *tomlLexer) fastForward(n int) { - for i := 0; i < n; i++ { - l.next() - } -} - -func (l *tomlLexer) emitWithValue(t tokenType, value string) { - l.tokens = append(l.tokens, token{ - Position: Position{l.line, l.col}, - typ: t, - val: value, - }) - l.ignore() -} - -func (l *tomlLexer) emit(t tokenType) { - l.emitWithValue(t, string(l.input[l.currentTokenStart:l.currentTokenStop])) -} - -func (l *tomlLexer) peek() rune { - if l.inputIdx >= len(l.input) { - return eof - } - return l.input[l.inputIdx] -} - -func (l *tomlLexer) peekString(size int) string { - maxIdx := len(l.input) - upperIdx := l.inputIdx + size // FIXME: potential overflow - if upperIdx > maxIdx { - upperIdx = maxIdx - } - return string(l.input[l.inputIdx:upperIdx]) -} - -func (l *tomlLexer) follow(next string) bool { - return next == l.peekString(len(next)) -} - -// Error management - -func (l *tomlLexer) errorf(format string, args ...interface{}) tomlLexStateFn { - l.tokens = append(l.tokens, token{ - Position: Position{l.line, l.col}, - typ: tokenError, - val: fmt.Sprintf(format, args...), - }) - return nil -} - -// State functions - -func (l *tomlLexer) lexVoid() tomlLexStateFn { - for { - next := l.peek() - switch next { - case '[': - return l.lexTableKey - case '#': - return l.lexComment(l.lexVoid) - case '=': - return l.lexEqual - case '\r': - fallthrough - case '\n': - l.skip() - continue - } - - if isSpace(next) { - l.skip() - } - - if l.depth > 0 { - return l.lexRvalue - } - - if isKeyStartChar(next) { - return l.lexKey - } - - if next == eof { - l.next() - break - } - } - - l.emit(tokenEOF) - return nil -} - -func (l *tomlLexer) lexRvalue() tomlLexStateFn { - for { - next := l.peek() - switch next { - case '.': - return l.errorf("cannot start float with a dot") - case '=': - return l.lexEqual - case '[': - l.depth++ - return l.lexLeftBracket - case ']': - l.depth-- - return l.lexRightBracket - case '{': - return l.lexLeftCurlyBrace - case '}': - return l.lexRightCurlyBrace - case '#': - return l.lexComment(l.lexRvalue) - case '"': - return l.lexString - case '\'': - return l.lexLiteralString - case ',': - return l.lexComma - case '\r': - fallthrough - case '\n': - l.skip() - if l.depth == 0 { - return l.lexVoid - } - return l.lexRvalue - case '_': - return l.errorf("cannot start number with underscore") - } - - if l.follow("true") { - return l.lexTrue - } - - if l.follow("false") { - return l.lexFalse - } - - if l.follow("inf") { - return l.lexInf - } - - if l.follow("nan") { - return l.lexNan - } - - if isSpace(next) { - l.skip() - continue - } - - if next == eof { - l.next() - break - } - - possibleDate := l.peekString(35) - dateMatch := dateRegexp.FindString(possibleDate) - if dateMatch != "" { - l.fastForward(len(dateMatch)) - return l.lexDate - } - - if next == '+' || next == '-' || isDigit(next) { - return l.lexNumber - } - - if isAlphanumeric(next) { - return l.lexKey - } - - return l.errorf("no value can start with %c", next) - } - - l.emit(tokenEOF) - return nil -} - -func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn { - l.next() - l.emit(tokenLeftCurlyBrace) - return l.lexRvalue -} - -func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn { - l.next() - l.emit(tokenRightCurlyBrace) - return l.lexRvalue -} - -func (l *tomlLexer) lexDate() tomlLexStateFn { - l.emit(tokenDate) - return l.lexRvalue -} - -func (l *tomlLexer) lexTrue() tomlLexStateFn { - l.fastForward(4) - l.emit(tokenTrue) - return l.lexRvalue -} - -func (l *tomlLexer) lexFalse() tomlLexStateFn { - l.fastForward(5) - l.emit(tokenFalse) - return l.lexRvalue -} - -func (l *tomlLexer) lexInf() tomlLexStateFn { - l.fastForward(3) - l.emit(tokenInf) - return l.lexRvalue -} - -func (l *tomlLexer) lexNan() tomlLexStateFn { - l.fastForward(3) - l.emit(tokenNan) - return l.lexRvalue -} - -func (l *tomlLexer) lexEqual() tomlLexStateFn { - l.next() - l.emit(tokenEqual) - return l.lexRvalue -} - -func (l *tomlLexer) lexComma() tomlLexStateFn { - l.next() - l.emit(tokenComma) - return l.lexRvalue -} - -// Parse the key and emits its value without escape sequences. -// bare keys, basic string keys and literal string keys are supported. -func (l *tomlLexer) lexKey() tomlLexStateFn { - growingString := "" - - for r := l.peek(); isKeyChar(r) || r == '\n' || r == '\r'; r = l.peek() { - if r == '"' { - l.next() - str, err := l.lexStringAsString(`"`, false, true) - if err != nil { - return l.errorf(err.Error()) - } - growingString += str - l.next() - continue - } else if r == '\'' { - l.next() - str, err := l.lexLiteralStringAsString(`'`, false) - if err != nil { - return l.errorf(err.Error()) - } - growingString += str - l.next() - continue - } else if r == '\n' { - return l.errorf("keys cannot contain new lines") - } else if isSpace(r) { - break - } else if !isValidBareChar(r) { - return l.errorf("keys cannot contain %c character", r) - } - growingString += string(r) - l.next() - } - l.emitWithValue(tokenKey, growingString) - return l.lexVoid -} - -func (l *tomlLexer) lexComment(previousState tomlLexStateFn) tomlLexStateFn { - return func() tomlLexStateFn { - for next := l.peek(); next != '\n' && next != eof; next = l.peek() { - if next == '\r' && l.follow("\r\n") { - break - } - l.next() - } - l.ignore() - return previousState - } -} - -func (l *tomlLexer) lexLeftBracket() tomlLexStateFn { - l.next() - l.emit(tokenLeftBracket) - return l.lexRvalue -} - -func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNewLine bool) (string, error) { - growingString := "" - - if discardLeadingNewLine { - if l.follow("\r\n") { - l.skip() - l.skip() - } else if l.peek() == '\n' { - l.skip() - } - } - - // find end of string - for { - if l.follow(terminator) { - return growingString, nil - } - - next := l.peek() - if next == eof { - break - } - growingString += string(l.next()) - } - - return "", errors.New("unclosed string") -} - -func (l *tomlLexer) lexLiteralString() tomlLexStateFn { - l.skip() - - // handle special case for triple-quote - terminator := "'" - discardLeadingNewLine := false - if l.follow("''") { - l.skip() - l.skip() - terminator = "'''" - discardLeadingNewLine = true - } - - str, err := l.lexLiteralStringAsString(terminator, discardLeadingNewLine) - if err != nil { - return l.errorf(err.Error()) - } - - l.emitWithValue(tokenString, str) - l.fastForward(len(terminator)) - l.ignore() - return l.lexRvalue -} - -// Lex a string and return the results as a string. -// Terminator is the substring indicating the end of the token. -// The resulting string does not include the terminator. -func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, acceptNewLines bool) (string, error) { - growingString := "" - - if discardLeadingNewLine { - if l.follow("\r\n") { - l.skip() - l.skip() - } else if l.peek() == '\n' { - l.skip() - } - } - - for { - if l.follow(terminator) { - return growingString, nil - } - - if l.follow("\\") { - l.next() - switch l.peek() { - case '\r': - fallthrough - case '\n': - fallthrough - case '\t': - fallthrough - case ' ': - // skip all whitespace chars following backslash - for strings.ContainsRune("\r\n\t ", l.peek()) { - l.next() - } - case '"': - growingString += "\"" - l.next() - case 'n': - growingString += "\n" - l.next() - case 'b': - growingString += "\b" - l.next() - case 'f': - growingString += "\f" - l.next() - case '/': - growingString += "/" - l.next() - case 't': - growingString += "\t" - l.next() - case 'r': - growingString += "\r" - l.next() - case '\\': - growingString += "\\" - l.next() - case 'u': - l.next() - code := "" - for i := 0; i < 4; i++ { - c := l.peek() - if !isHexDigit(c) { - return "", errors.New("unfinished unicode escape") - } - l.next() - code = code + string(c) - } - intcode, err := strconv.ParseInt(code, 16, 32) - if err != nil { - return "", errors.New("invalid unicode escape: \\u" + code) - } - growingString += string(rune(intcode)) - case 'U': - l.next() - code := "" - for i := 0; i < 8; i++ { - c := l.peek() - if !isHexDigit(c) { - return "", errors.New("unfinished unicode escape") - } - l.next() - code = code + string(c) - } - intcode, err := strconv.ParseInt(code, 16, 64) - if err != nil { - return "", errors.New("invalid unicode escape: \\U" + code) - } - growingString += string(rune(intcode)) - default: - return "", errors.New("invalid escape sequence: \\" + string(l.peek())) - } - } else { - r := l.peek() - - if 0x00 <= r && r <= 0x1F && !(acceptNewLines && (r == '\n' || r == '\r')) { - return "", fmt.Errorf("unescaped control character %U", r) - } - l.next() - growingString += string(r) - } - - if l.peek() == eof { - break - } - } - - return "", errors.New("unclosed string") -} - -func (l *tomlLexer) lexString() tomlLexStateFn { - l.skip() - - // handle special case for triple-quote - terminator := `"` - discardLeadingNewLine := false - acceptNewLines := false - if l.follow(`""`) { - l.skip() - l.skip() - terminator = `"""` - discardLeadingNewLine = true - acceptNewLines = true - } - - str, err := l.lexStringAsString(terminator, discardLeadingNewLine, acceptNewLines) - - if err != nil { - return l.errorf(err.Error()) - } - - l.emitWithValue(tokenString, str) - l.fastForward(len(terminator)) - l.ignore() - return l.lexRvalue -} - -func (l *tomlLexer) lexTableKey() tomlLexStateFn { - l.next() - - if l.peek() == '[' { - // token '[[' signifies an array of tables - l.next() - l.emit(tokenDoubleLeftBracket) - return l.lexInsideTableArrayKey - } - // vanilla table key - l.emit(tokenLeftBracket) - return l.lexInsideTableKey -} - -// Parse the key till "]]", but only bare keys are supported -func (l *tomlLexer) lexInsideTableArrayKey() tomlLexStateFn { - for r := l.peek(); r != eof; r = l.peek() { - switch r { - case ']': - if l.currentTokenStop > l.currentTokenStart { - l.emit(tokenKeyGroupArray) - } - l.next() - if l.peek() != ']' { - break - } - l.next() - l.emit(tokenDoubleRightBracket) - return l.lexVoid - case '[': - return l.errorf("table array key cannot contain ']'") - default: - l.next() - } - } - return l.errorf("unclosed table array key") -} - -// Parse the key till "]" but only bare keys are supported -func (l *tomlLexer) lexInsideTableKey() tomlLexStateFn { - for r := l.peek(); r != eof; r = l.peek() { - switch r { - case ']': - if l.currentTokenStop > l.currentTokenStart { - l.emit(tokenKeyGroup) - } - l.next() - l.emit(tokenRightBracket) - return l.lexVoid - case '[': - return l.errorf("table key cannot contain ']'") - default: - l.next() - } - } - return l.errorf("unclosed table key") -} - -func (l *tomlLexer) lexRightBracket() tomlLexStateFn { - l.next() - l.emit(tokenRightBracket) - return l.lexRvalue -} - -type validRuneFn func(r rune) bool - -func isValidHexRune(r rune) bool { - return r >= 'a' && r <= 'f' || - r >= 'A' && r <= 'F' || - r >= '0' && r <= '9' || - r == '_' -} - -func isValidOctalRune(r rune) bool { - return r >= '0' && r <= '7' || r == '_' -} - -func isValidBinaryRune(r rune) bool { - return r == '0' || r == '1' || r == '_' -} - -func (l *tomlLexer) lexNumber() tomlLexStateFn { - r := l.peek() - - if r == '0' { - follow := l.peekString(2) - if len(follow) == 2 { - var isValidRune validRuneFn - switch follow[1] { - case 'x': - isValidRune = isValidHexRune - case 'o': - isValidRune = isValidOctalRune - case 'b': - isValidRune = isValidBinaryRune - default: - if follow[1] >= 'a' && follow[1] <= 'z' || follow[1] >= 'A' && follow[1] <= 'Z' { - return l.errorf("unknown number base: %s. possible options are x (hex) o (octal) b (binary)", string(follow[1])) - } - } - - if isValidRune != nil { - l.next() - l.next() - digitSeen := false - for { - next := l.peek() - if !isValidRune(next) { - break - } - digitSeen = true - l.next() - } - - if !digitSeen { - return l.errorf("number needs at least one digit") - } - - l.emit(tokenInteger) - - return l.lexRvalue - } - } - } - - if r == '+' || r == '-' { - l.next() - if l.follow("inf") { - return l.lexInf - } - if l.follow("nan") { - return l.lexNan - } - } - - pointSeen := false - expSeen := false - digitSeen := false - for { - next := l.peek() - if next == '.' { - if pointSeen { - return l.errorf("cannot have two dots in one float") - } - l.next() - if !isDigit(l.peek()) { - return l.errorf("float cannot end with a dot") - } - pointSeen = true - } else if next == 'e' || next == 'E' { - expSeen = true - l.next() - r := l.peek() - if r == '+' || r == '-' { - l.next() - } - } else if isDigit(next) { - digitSeen = true - l.next() - } else if next == '_' { - l.next() - } else { - break - } - if pointSeen && !digitSeen { - return l.errorf("cannot start float with a dot") - } - } - - if !digitSeen { - return l.errorf("no digit in that number") - } - if pointSeen || expSeen { - l.emit(tokenFloat) - } else { - l.emit(tokenInteger) - } - return l.lexRvalue -} - -func (l *tomlLexer) run() { - for state := l.lexVoid; state != nil; { - state = state() - } -} - -func init() { - dateRegexp = regexp.MustCompile(`^\d{1,4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})`) -} - -// Entry point -func lexToml(inputBytes []byte) []token { - runes := bytes.Runes(inputBytes) - l := &tomlLexer{ - input: runes, - tokens: make([]token, 0, 256), - line: 1, - col: 1, - endbufferLine: 1, - endbufferCol: 1, - } - l.run() - return l.tokens -} diff --git a/vendor/github.com/pelletier/go-toml/lexer_test.go b/vendor/github.com/pelletier/go-toml/lexer_test.go deleted file mode 100644 index cb4913031..000000000 --- a/vendor/github.com/pelletier/go-toml/lexer_test.go +++ /dev/null @@ -1,750 +0,0 @@ -package toml - -import ( - "reflect" - "testing" -) - -func testFlow(t *testing.T, input string, expectedFlow []token) { - tokens := lexToml([]byte(input)) - if !reflect.DeepEqual(tokens, expectedFlow) { - t.Fatal("Different flows. Expected\n", expectedFlow, "\nGot:\n", tokens) - } -} - -func TestValidKeyGroup(t *testing.T) { - testFlow(t, "[hello world]", []token{ - {Position{1, 1}, tokenLeftBracket, "["}, - {Position{1, 2}, tokenKeyGroup, "hello world"}, - {Position{1, 13}, tokenRightBracket, "]"}, - {Position{1, 14}, tokenEOF, ""}, - }) -} - -func TestNestedQuotedUnicodeKeyGroup(t *testing.T) { - testFlow(t, `[ j . "ʞ" . l ]`, []token{ - {Position{1, 1}, tokenLeftBracket, "["}, - {Position{1, 2}, tokenKeyGroup, ` j . "ʞ" . l `}, - {Position{1, 15}, tokenRightBracket, "]"}, - {Position{1, 16}, tokenEOF, ""}, - }) -} - -func TestUnclosedKeyGroup(t *testing.T) { - testFlow(t, "[hello world", []token{ - {Position{1, 1}, tokenLeftBracket, "["}, - {Position{1, 2}, tokenError, "unclosed table key"}, - }) -} - -func TestComment(t *testing.T) { - testFlow(t, "# blahblah", []token{ - {Position{1, 11}, tokenEOF, ""}, - }) -} - -func TestKeyGroupComment(t *testing.T) { - testFlow(t, "[hello world] # blahblah", []token{ - {Position{1, 1}, tokenLeftBracket, "["}, - {Position{1, 2}, tokenKeyGroup, "hello world"}, - {Position{1, 13}, tokenRightBracket, "]"}, - {Position{1, 25}, tokenEOF, ""}, - }) -} - -func TestMultipleKeyGroupsComment(t *testing.T) { - testFlow(t, "[hello world] # blahblah\n[test]", []token{ - {Position{1, 1}, tokenLeftBracket, "["}, - {Position{1, 2}, tokenKeyGroup, "hello world"}, - {Position{1, 13}, tokenRightBracket, "]"}, - {Position{2, 1}, tokenLeftBracket, "["}, - {Position{2, 2}, tokenKeyGroup, "test"}, - {Position{2, 6}, tokenRightBracket, "]"}, - {Position{2, 7}, tokenEOF, ""}, - }) -} - -func TestSimpleWindowsCRLF(t *testing.T) { - testFlow(t, "a=4\r\nb=2", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 2}, tokenEqual, "="}, - {Position{1, 3}, tokenInteger, "4"}, - {Position{2, 1}, tokenKey, "b"}, - {Position{2, 2}, tokenEqual, "="}, - {Position{2, 3}, tokenInteger, "2"}, - {Position{2, 4}, tokenEOF, ""}, - }) -} - -func TestBasicKey(t *testing.T) { - testFlow(t, "hello", []token{ - {Position{1, 1}, tokenKey, "hello"}, - {Position{1, 6}, tokenEOF, ""}, - }) -} - -func TestBasicKeyWithUnderscore(t *testing.T) { - testFlow(t, "hello_hello", []token{ - {Position{1, 1}, tokenKey, "hello_hello"}, - {Position{1, 12}, tokenEOF, ""}, - }) -} - -func TestBasicKeyWithDash(t *testing.T) { - testFlow(t, "hello-world", []token{ - {Position{1, 1}, tokenKey, "hello-world"}, - {Position{1, 12}, tokenEOF, ""}, - }) -} - -func TestBasicKeyWithUppercaseMix(t *testing.T) { - testFlow(t, "helloHELLOHello", []token{ - {Position{1, 1}, tokenKey, "helloHELLOHello"}, - {Position{1, 16}, tokenEOF, ""}, - }) -} - -func TestBasicKeyWithInternationalCharacters(t *testing.T) { - testFlow(t, "héllÖ", []token{ - {Position{1, 1}, tokenKey, "héllÖ"}, - {Position{1, 6}, tokenEOF, ""}, - }) -} - -func TestBasicKeyAndEqual(t *testing.T) { - testFlow(t, "hello =", []token{ - {Position{1, 1}, tokenKey, "hello"}, - {Position{1, 7}, tokenEqual, "="}, - {Position{1, 8}, tokenEOF, ""}, - }) -} - -func TestKeyWithSharpAndEqual(t *testing.T) { - testFlow(t, "key#name = 5", []token{ - {Position{1, 1}, tokenError, "keys cannot contain # character"}, - }) -} - -func TestKeyWithSymbolsAndEqual(t *testing.T) { - testFlow(t, "~!@$^&*()_+-`1234567890[]\\|/?><.,;:' = 5", []token{ - {Position{1, 1}, tokenError, "keys cannot contain ~ character"}, - }) -} - -func TestKeyEqualStringEscape(t *testing.T) { - testFlow(t, `foo = "hello\""`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "hello\""}, - {Position{1, 16}, tokenEOF, ""}, - }) -} - -func TestKeyEqualStringUnfinished(t *testing.T) { - testFlow(t, `foo = "bar`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenError, "unclosed string"}, - }) -} - -func TestKeyEqualString(t *testing.T) { - testFlow(t, `foo = "bar"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "bar"}, - {Position{1, 12}, tokenEOF, ""}, - }) -} - -func TestKeyEqualTrue(t *testing.T) { - testFlow(t, "foo = true", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenTrue, "true"}, - {Position{1, 11}, tokenEOF, ""}, - }) -} - -func TestKeyEqualFalse(t *testing.T) { - testFlow(t, "foo = false", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenFalse, "false"}, - {Position{1, 12}, tokenEOF, ""}, - }) -} - -func TestArrayNestedString(t *testing.T) { - testFlow(t, `a = [ ["hello", "world"] ]`, []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenLeftBracket, "["}, - {Position{1, 7}, tokenLeftBracket, "["}, - {Position{1, 9}, tokenString, "hello"}, - {Position{1, 15}, tokenComma, ","}, - {Position{1, 18}, tokenString, "world"}, - {Position{1, 24}, tokenRightBracket, "]"}, - {Position{1, 26}, tokenRightBracket, "]"}, - {Position{1, 27}, tokenEOF, ""}, - }) -} - -func TestArrayNestedInts(t *testing.T) { - testFlow(t, "a = [ [42, 21], [10] ]", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenLeftBracket, "["}, - {Position{1, 7}, tokenLeftBracket, "["}, - {Position{1, 8}, tokenInteger, "42"}, - {Position{1, 10}, tokenComma, ","}, - {Position{1, 12}, tokenInteger, "21"}, - {Position{1, 14}, tokenRightBracket, "]"}, - {Position{1, 15}, tokenComma, ","}, - {Position{1, 17}, tokenLeftBracket, "["}, - {Position{1, 18}, tokenInteger, "10"}, - {Position{1, 20}, tokenRightBracket, "]"}, - {Position{1, 22}, tokenRightBracket, "]"}, - {Position{1, 23}, tokenEOF, ""}, - }) -} - -func TestArrayInts(t *testing.T) { - testFlow(t, "a = [ 42, 21, 10, ]", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenLeftBracket, "["}, - {Position{1, 7}, tokenInteger, "42"}, - {Position{1, 9}, tokenComma, ","}, - {Position{1, 11}, tokenInteger, "21"}, - {Position{1, 13}, tokenComma, ","}, - {Position{1, 15}, tokenInteger, "10"}, - {Position{1, 17}, tokenComma, ","}, - {Position{1, 19}, tokenRightBracket, "]"}, - {Position{1, 20}, tokenEOF, ""}, - }) -} - -func TestMultilineArrayComments(t *testing.T) { - testFlow(t, "a = [1, # wow\n2, # such items\n3, # so array\n]", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenLeftBracket, "["}, - {Position{1, 6}, tokenInteger, "1"}, - {Position{1, 7}, tokenComma, ","}, - {Position{2, 1}, tokenInteger, "2"}, - {Position{2, 2}, tokenComma, ","}, - {Position{3, 1}, tokenInteger, "3"}, - {Position{3, 2}, tokenComma, ","}, - {Position{4, 1}, tokenRightBracket, "]"}, - {Position{4, 2}, tokenEOF, ""}, - }) -} - -func TestNestedArraysComment(t *testing.T) { - toml := ` -someArray = [ -# does not work -["entry1"] -]` - testFlow(t, toml, []token{ - {Position{2, 1}, tokenKey, "someArray"}, - {Position{2, 11}, tokenEqual, "="}, - {Position{2, 13}, tokenLeftBracket, "["}, - {Position{4, 1}, tokenLeftBracket, "["}, - {Position{4, 3}, tokenString, "entry1"}, - {Position{4, 10}, tokenRightBracket, "]"}, - {Position{5, 1}, tokenRightBracket, "]"}, - {Position{5, 2}, tokenEOF, ""}, - }) -} - -func TestKeyEqualArrayBools(t *testing.T) { - testFlow(t, "foo = [true, false, true]", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenLeftBracket, "["}, - {Position{1, 8}, tokenTrue, "true"}, - {Position{1, 12}, tokenComma, ","}, - {Position{1, 14}, tokenFalse, "false"}, - {Position{1, 19}, tokenComma, ","}, - {Position{1, 21}, tokenTrue, "true"}, - {Position{1, 25}, tokenRightBracket, "]"}, - {Position{1, 26}, tokenEOF, ""}, - }) -} - -func TestKeyEqualArrayBoolsWithComments(t *testing.T) { - testFlow(t, "foo = [true, false, true] # YEAH", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenLeftBracket, "["}, - {Position{1, 8}, tokenTrue, "true"}, - {Position{1, 12}, tokenComma, ","}, - {Position{1, 14}, tokenFalse, "false"}, - {Position{1, 19}, tokenComma, ","}, - {Position{1, 21}, tokenTrue, "true"}, - {Position{1, 25}, tokenRightBracket, "]"}, - {Position{1, 33}, tokenEOF, ""}, - }) -} - -func TestDateRegexp(t *testing.T) { - if dateRegexp.FindString("1979-05-27T07:32:00Z") == "" { - t.Error("basic lexing") - } - if dateRegexp.FindString("1979-05-27T00:32:00-07:00") == "" { - t.Error("offset lexing") - } - if dateRegexp.FindString("1979-05-27T00:32:00.999999-07:00") == "" { - t.Error("nano precision lexing") - } -} - -func TestKeyEqualDate(t *testing.T) { - testFlow(t, "foo = 1979-05-27T07:32:00Z", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenDate, "1979-05-27T07:32:00Z"}, - {Position{1, 27}, tokenEOF, ""}, - }) - testFlow(t, "foo = 1979-05-27T00:32:00-07:00", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenDate, "1979-05-27T00:32:00-07:00"}, - {Position{1, 32}, tokenEOF, ""}, - }) - testFlow(t, "foo = 1979-05-27T00:32:00.999999-07:00", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenDate, "1979-05-27T00:32:00.999999-07:00"}, - {Position{1, 39}, tokenEOF, ""}, - }) -} - -func TestFloatEndingWithDot(t *testing.T) { - testFlow(t, "foo = 42.", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenError, "float cannot end with a dot"}, - }) -} - -func TestFloatWithTwoDots(t *testing.T) { - testFlow(t, "foo = 4.2.", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenError, "cannot have two dots in one float"}, - }) -} - -func TestFloatWithExponent1(t *testing.T) { - testFlow(t, "a = 5e+22", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenFloat, "5e+22"}, - {Position{1, 10}, tokenEOF, ""}, - }) -} - -func TestFloatWithExponent2(t *testing.T) { - testFlow(t, "a = 5E+22", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenFloat, "5E+22"}, - {Position{1, 10}, tokenEOF, ""}, - }) -} - -func TestFloatWithExponent3(t *testing.T) { - testFlow(t, "a = -5e+22", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenFloat, "-5e+22"}, - {Position{1, 11}, tokenEOF, ""}, - }) -} - -func TestFloatWithExponent4(t *testing.T) { - testFlow(t, "a = -5e-22", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenFloat, "-5e-22"}, - {Position{1, 11}, tokenEOF, ""}, - }) -} - -func TestFloatWithExponent5(t *testing.T) { - testFlow(t, "a = 6.626e-34", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenFloat, "6.626e-34"}, - {Position{1, 14}, tokenEOF, ""}, - }) -} - -func TestInvalidEsquapeSequence(t *testing.T) { - testFlow(t, `foo = "\x"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenError, "invalid escape sequence: \\x"}, - }) -} - -func TestNestedArrays(t *testing.T) { - testFlow(t, "foo = [[[]]]", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenLeftBracket, "["}, - {Position{1, 8}, tokenLeftBracket, "["}, - {Position{1, 9}, tokenLeftBracket, "["}, - {Position{1, 10}, tokenRightBracket, "]"}, - {Position{1, 11}, tokenRightBracket, "]"}, - {Position{1, 12}, tokenRightBracket, "]"}, - {Position{1, 13}, tokenEOF, ""}, - }) -} - -func TestKeyEqualNumber(t *testing.T) { - testFlow(t, "foo = 42", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenInteger, "42"}, - {Position{1, 9}, tokenEOF, ""}, - }) - - testFlow(t, "foo = +42", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenInteger, "+42"}, - {Position{1, 10}, tokenEOF, ""}, - }) - - testFlow(t, "foo = -42", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenInteger, "-42"}, - {Position{1, 10}, tokenEOF, ""}, - }) - - testFlow(t, "foo = 4.2", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenFloat, "4.2"}, - {Position{1, 10}, tokenEOF, ""}, - }) - - testFlow(t, "foo = +4.2", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenFloat, "+4.2"}, - {Position{1, 11}, tokenEOF, ""}, - }) - - testFlow(t, "foo = -4.2", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenFloat, "-4.2"}, - {Position{1, 11}, tokenEOF, ""}, - }) - - testFlow(t, "foo = 1_000", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenInteger, "1_000"}, - {Position{1, 12}, tokenEOF, ""}, - }) - - testFlow(t, "foo = 5_349_221", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenInteger, "5_349_221"}, - {Position{1, 16}, tokenEOF, ""}, - }) - - testFlow(t, "foo = 1_2_3_4_5", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenInteger, "1_2_3_4_5"}, - {Position{1, 16}, tokenEOF, ""}, - }) - - testFlow(t, "flt8 = 9_224_617.445_991_228_313", []token{ - {Position{1, 1}, tokenKey, "flt8"}, - {Position{1, 6}, tokenEqual, "="}, - {Position{1, 8}, tokenFloat, "9_224_617.445_991_228_313"}, - {Position{1, 33}, tokenEOF, ""}, - }) - - testFlow(t, "foo = +", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenError, "no digit in that number"}, - }) -} - -func TestMultiline(t *testing.T) { - testFlow(t, "foo = 42\nbar=21", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 7}, tokenInteger, "42"}, - {Position{2, 1}, tokenKey, "bar"}, - {Position{2, 4}, tokenEqual, "="}, - {Position{2, 5}, tokenInteger, "21"}, - {Position{2, 7}, tokenEOF, ""}, - }) -} - -func TestKeyEqualStringUnicodeEscape(t *testing.T) { - testFlow(t, `foo = "hello \u2665"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "hello ♥"}, - {Position{1, 21}, tokenEOF, ""}, - }) - testFlow(t, `foo = "hello \U000003B4"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "hello δ"}, - {Position{1, 25}, tokenEOF, ""}, - }) - testFlow(t, `foo = "\uabcd"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "\uabcd"}, - {Position{1, 15}, tokenEOF, ""}, - }) - testFlow(t, `foo = "\uABCD"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "\uABCD"}, - {Position{1, 15}, tokenEOF, ""}, - }) - testFlow(t, `foo = "\U000bcdef"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "\U000bcdef"}, - {Position{1, 19}, tokenEOF, ""}, - }) - testFlow(t, `foo = "\U000BCDEF"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "\U000BCDEF"}, - {Position{1, 19}, tokenEOF, ""}, - }) - testFlow(t, `foo = "\u2"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenError, "unfinished unicode escape"}, - }) - testFlow(t, `foo = "\U2"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenError, "unfinished unicode escape"}, - }) -} - -func TestKeyEqualStringNoEscape(t *testing.T) { - testFlow(t, "foo = \"hello \u0002\"", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenError, "unescaped control character U+0002"}, - }) - testFlow(t, "foo = \"hello \u001F\"", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenError, "unescaped control character U+001F"}, - }) -} - -func TestLiteralString(t *testing.T) { - testFlow(t, `foo = 'C:\Users\nodejs\templates'`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, `C:\Users\nodejs\templates`}, - {Position{1, 34}, tokenEOF, ""}, - }) - testFlow(t, `foo = '\\ServerX\admin$\system32\'`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, `\\ServerX\admin$\system32\`}, - {Position{1, 35}, tokenEOF, ""}, - }) - testFlow(t, `foo = 'Tom "Dubs" Preston-Werner'`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, `Tom "Dubs" Preston-Werner`}, - {Position{1, 34}, tokenEOF, ""}, - }) - testFlow(t, `foo = '<\i\c*\s*>'`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, `<\i\c*\s*>`}, - {Position{1, 19}, tokenEOF, ""}, - }) - testFlow(t, `foo = 'C:\Users\nodejs\unfinis`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenError, "unclosed string"}, - }) -} - -func TestMultilineLiteralString(t *testing.T) { - testFlow(t, `foo = '''hello 'literal' world'''`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 10}, tokenString, `hello 'literal' world`}, - {Position{1, 34}, tokenEOF, ""}, - }) - - testFlow(t, "foo = '''\nhello\n'literal'\nworld'''", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{2, 1}, tokenString, "hello\n'literal'\nworld"}, - {Position{4, 9}, tokenEOF, ""}, - }) - testFlow(t, "foo = '''\r\nhello\r\n'literal'\r\nworld'''", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{2, 1}, tokenString, "hello\r\n'literal'\r\nworld"}, - {Position{4, 9}, tokenEOF, ""}, - }) -} - -func TestMultilineString(t *testing.T) { - testFlow(t, `foo = """hello "literal" world"""`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 10}, tokenString, `hello "literal" world`}, - {Position{1, 34}, tokenEOF, ""}, - }) - - testFlow(t, "foo = \"\"\"\r\nhello\\\r\n\"literal\"\\\nworld\"\"\"", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{2, 1}, tokenString, "hello\"literal\"world"}, - {Position{4, 9}, tokenEOF, ""}, - }) - - testFlow(t, "foo = \"\"\"\\\n \\\n \\\n hello\\\nmultiline\\\nworld\"\"\"", []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 10}, tokenString, "hellomultilineworld"}, - {Position{6, 9}, tokenEOF, ""}, - }) - - testFlow(t, "key2 = \"\"\"\nThe quick brown \\\n\n\n fox jumps over \\\n the lazy dog.\"\"\"", []token{ - {Position{1, 1}, tokenKey, "key2"}, - {Position{1, 6}, tokenEqual, "="}, - {Position{2, 1}, tokenString, "The quick brown fox jumps over the lazy dog."}, - {Position{6, 21}, tokenEOF, ""}, - }) - - testFlow(t, "key2 = \"\"\"\\\n The quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", []token{ - {Position{1, 1}, tokenKey, "key2"}, - {Position{1, 6}, tokenEqual, "="}, - {Position{1, 11}, tokenString, "The quick brown fox jumps over the lazy dog."}, - {Position{5, 11}, tokenEOF, ""}, - }) - - testFlow(t, `key2 = "Roses are red\nViolets are blue"`, []token{ - {Position{1, 1}, tokenKey, "key2"}, - {Position{1, 6}, tokenEqual, "="}, - {Position{1, 9}, tokenString, "Roses are red\nViolets are blue"}, - {Position{1, 41}, tokenEOF, ""}, - }) - - testFlow(t, "key2 = \"\"\"\nRoses are red\nViolets are blue\"\"\"", []token{ - {Position{1, 1}, tokenKey, "key2"}, - {Position{1, 6}, tokenEqual, "="}, - {Position{2, 1}, tokenString, "Roses are red\nViolets are blue"}, - {Position{3, 20}, tokenEOF, ""}, - }) -} - -func TestUnicodeString(t *testing.T) { - testFlow(t, `foo = "hello ♥ world"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "hello ♥ world"}, - {Position{1, 22}, tokenEOF, ""}, - }) -} -func TestEscapeInString(t *testing.T) { - testFlow(t, `foo = "\b\f\/"`, []token{ - {Position{1, 1}, tokenKey, "foo"}, - {Position{1, 5}, tokenEqual, "="}, - {Position{1, 8}, tokenString, "\b\f/"}, - {Position{1, 15}, tokenEOF, ""}, - }) -} - -func TestKeyGroupArray(t *testing.T) { - testFlow(t, "[[foo]]", []token{ - {Position{1, 1}, tokenDoubleLeftBracket, "[["}, - {Position{1, 3}, tokenKeyGroupArray, "foo"}, - {Position{1, 6}, tokenDoubleRightBracket, "]]"}, - {Position{1, 8}, tokenEOF, ""}, - }) -} - -func TestQuotedKey(t *testing.T) { - testFlow(t, "\"a b\" = 42", []token{ - {Position{1, 1}, tokenKey, "a b"}, - {Position{1, 7}, tokenEqual, "="}, - {Position{1, 9}, tokenInteger, "42"}, - {Position{1, 11}, tokenEOF, ""}, - }) -} - -func TestKeyNewline(t *testing.T) { - testFlow(t, "a\n= 4", []token{ - {Position{1, 1}, tokenError, "keys cannot contain new lines"}, - }) -} - -func TestInvalidFloat(t *testing.T) { - testFlow(t, "a=7e1_", []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 2}, tokenEqual, "="}, - {Position{1, 3}, tokenFloat, "7e1_"}, - {Position{1, 7}, tokenEOF, ""}, - }) -} - -func TestLexUnknownRvalue(t *testing.T) { - testFlow(t, `a = !b`, []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenError, "no value can start with !"}, - }) - - testFlow(t, `a = \b`, []token{ - {Position{1, 1}, tokenKey, "a"}, - {Position{1, 3}, tokenEqual, "="}, - {Position{1, 5}, tokenError, `no value can start with \`}, - }) -} - -func BenchmarkLexer(b *testing.B) { - sample := `title = "Hugo: A Fast and Flexible Website Generator" -baseurl = "http://gohugo.io/" -MetaDataFormat = "yaml" -pluralizeListTitles = false - -[params] - description = "Documentation of Hugo, a fast and flexible static site generator built with love by spf13, bep and friends in Go" - author = "Steve Francia (spf13) and friends" - release = "0.22-DEV" - -[[menu.main]] - name = "Download Hugo" - pre = "" - url = "https://github.com/spf13/hugo/releases" - weight = -200 -` - b.ResetTimer() - for i := 0; i < b.N; i++ { - lexToml([]byte(sample)) - } -} diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go deleted file mode 100644 index 671da5564..000000000 --- a/vendor/github.com/pelletier/go-toml/marshal.go +++ /dev/null @@ -1,609 +0,0 @@ -package toml - -import ( - "bytes" - "errors" - "fmt" - "io" - "reflect" - "strconv" - "strings" - "time" -) - -const tagKeyMultiline = "multiline" - -type tomlOpts struct { - name string - comment string - commented bool - multiline bool - include bool - omitempty bool -} - -type encOpts struct { - quoteMapKeys bool - arraysOneElementPerLine bool -} - -var encOptsDefaults = encOpts{ - quoteMapKeys: false, -} - -var timeType = reflect.TypeOf(time.Time{}) -var marshalerType = reflect.TypeOf(new(Marshaler)).Elem() - -// Check if the given marshall type maps to a Tree primitive -func isPrimitive(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isPrimitive(mtype.Elem()) - case reflect.Bool: - return true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - case reflect.Float32, reflect.Float64: - return true - case reflect.String: - return true - case reflect.Struct: - return mtype == timeType || isCustomMarshaler(mtype) - default: - return false - } -} - -// Check if the given marshall type maps to a Tree slice -func isTreeSlice(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Slice: - return !isOtherSlice(mtype) - default: - return false - } -} - -// Check if the given marshall type maps to a non-Tree slice -func isOtherSlice(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Ptr: - return isOtherSlice(mtype.Elem()) - case reflect.Slice: - return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem()) - default: - return false - } -} - -// Check if the given marshall type maps to a Tree -func isTree(mtype reflect.Type) bool { - switch mtype.Kind() { - case reflect.Map: - return true - case reflect.Struct: - return !isPrimitive(mtype) - default: - return false - } -} - -func isCustomMarshaler(mtype reflect.Type) bool { - return mtype.Implements(marshalerType) -} - -func callCustomMarshaler(mval reflect.Value) ([]byte, error) { - return mval.Interface().(Marshaler).MarshalTOML() -} - -// Marshaler is the interface implemented by types that -// can marshal themselves into valid TOML. -type Marshaler interface { - MarshalTOML() ([]byte, error) -} - -/* -Marshal returns the TOML encoding of v. Behavior is similar to the Go json -encoder, except that there is no concept of a Marshaler interface or MarshalTOML -function for sub-structs, and currently only definite types can be marshaled -(i.e. no `interface{}`). - -The following struct annotations are supported: - - toml:"Field" Overrides the field's name to output. - omitempty When set, empty values and groups are not emitted. - comment:"comment" Emits a # comment on the same line. This supports new lines. - commented:"true" Emits the value as commented. - -Note that pointers are automatically assigned the "omitempty" option, as TOML -explicitly does not handle null values (saying instead the label should be -dropped). - -Tree structural types and corresponding marshal types: - - *Tree (*)struct, (*)map[string]interface{} - []*Tree (*)[](*)struct, (*)[](*)map[string]interface{} - []interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{}) - interface{} (*)primitive - -Tree primitive types and corresponding marshal types: - - uint64 uint, uint8-uint64, pointers to same - int64 int, int8-uint64, pointers to same - float64 float32, float64, pointers to same - string string, pointers to same - bool bool, pointers to same - time.Time time.Time{}, pointers to same -*/ -func Marshal(v interface{}) ([]byte, error) { - return NewEncoder(nil).marshal(v) -} - -// Encoder writes TOML values to an output stream. -type Encoder struct { - w io.Writer - encOpts -} - -// NewEncoder returns a new encoder that writes to w. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{ - w: w, - encOpts: encOptsDefaults, - } -} - -// Encode writes the TOML encoding of v to the stream. -// -// See the documentation for Marshal for details. -func (e *Encoder) Encode(v interface{}) error { - b, err := e.marshal(v) - if err != nil { - return err - } - if _, err := e.w.Write(b); err != nil { - return err - } - return nil -} - -// QuoteMapKeys sets up the encoder to encode -// maps with string type keys with quoted TOML keys. -// -// This relieves the character limitations on map keys. -func (e *Encoder) QuoteMapKeys(v bool) *Encoder { - e.quoteMapKeys = v - return e -} - -// ArraysWithOneElementPerLine sets up the encoder to encode arrays -// with more than one element on multiple lines instead of one. -// -// For example: -// -// A = [1,2,3] -// -// Becomes -// -// A = [ -// 1, -// 2, -// 3, -// ] -func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder { - e.arraysOneElementPerLine = v - return e -} - -func (e *Encoder) marshal(v interface{}) ([]byte, error) { - mtype := reflect.TypeOf(v) - if mtype.Kind() != reflect.Struct { - return []byte{}, errors.New("Only a struct can be marshaled to TOML") - } - sval := reflect.ValueOf(v) - if isCustomMarshaler(mtype) { - return callCustomMarshaler(sval) - } - t, err := e.valueToTree(mtype, sval) - if err != nil { - return []byte{}, err - } - - var buf bytes.Buffer - _, err = t.writeTo(&buf, "", "", 0, e.arraysOneElementPerLine) - - return buf.Bytes(), err -} - -// Convert given marshal struct or map value to toml tree -func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) { - if mtype.Kind() == reflect.Ptr { - return e.valueToTree(mtype.Elem(), mval.Elem()) - } - tval := newTree() - switch mtype.Kind() { - case reflect.Struct: - for i := 0; i < mtype.NumField(); i++ { - mtypef, mvalf := mtype.Field(i), mval.Field(i) - opts := tomlOptions(mtypef) - if opts.include && (!opts.omitempty || !isZero(mvalf)) { - val, err := e.valueToToml(mtypef.Type, mvalf) - if err != nil { - return nil, err - } - - tval.SetWithOptions(opts.name, SetOptions{ - Comment: opts.comment, - Commented: opts.commented, - Multiline: opts.multiline, - }, val) - } - } - case reflect.Map: - for _, key := range mval.MapKeys() { - mvalf := mval.MapIndex(key) - val, err := e.valueToToml(mtype.Elem(), mvalf) - if err != nil { - return nil, err - } - if e.quoteMapKeys { - keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine) - if err != nil { - return nil, err - } - tval.SetPath([]string{keyStr}, val) - } else { - tval.Set(key.String(), val) - } - } - } - return tval, nil -} - -// Convert given marshal slice to slice of Toml trees -func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) { - tval := make([]*Tree, mval.Len(), mval.Len()) - for i := 0; i < mval.Len(); i++ { - val, err := e.valueToTree(mtype.Elem(), mval.Index(i)) - if err != nil { - return nil, err - } - tval[i] = val - } - return tval, nil -} - -// Convert given marshal slice to slice of toml values -func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) { - tval := make([]interface{}, mval.Len(), mval.Len()) - for i := 0; i < mval.Len(); i++ { - val, err := e.valueToToml(mtype.Elem(), mval.Index(i)) - if err != nil { - return nil, err - } - tval[i] = val - } - return tval, nil -} - -// Convert given marshal value to toml value -func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) { - if mtype.Kind() == reflect.Ptr { - return e.valueToToml(mtype.Elem(), mval.Elem()) - } - switch { - case isCustomMarshaler(mtype): - return callCustomMarshaler(mval) - case isTree(mtype): - return e.valueToTree(mtype, mval) - case isTreeSlice(mtype): - return e.valueToTreeSlice(mtype, mval) - case isOtherSlice(mtype): - return e.valueToOtherSlice(mtype, mval) - default: - switch mtype.Kind() { - case reflect.Bool: - return mval.Bool(), nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return mval.Int(), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return mval.Uint(), nil - case reflect.Float32, reflect.Float64: - return mval.Float(), nil - case reflect.String: - return mval.String(), nil - case reflect.Struct: - return mval.Interface().(time.Time), nil - default: - return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind()) - } - } -} - -// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v. -// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for -// sub-structs, and only definite types can be unmarshaled. -func (t *Tree) Unmarshal(v interface{}) error { - d := Decoder{tval: t} - return d.unmarshal(v) -} - -// Marshal returns the TOML encoding of Tree. -// See Marshal() documentation for types mapping table. -func (t *Tree) Marshal() ([]byte, error) { - var buf bytes.Buffer - err := NewEncoder(&buf).Encode(t) - return buf.Bytes(), err -} - -// Unmarshal parses the TOML-encoded data and stores the result in the value -// pointed to by v. Behavior is similar to the Go json encoder, except that there -// is no concept of an Unmarshaler interface or UnmarshalTOML function for -// sub-structs, and currently only definite types can be unmarshaled to (i.e. no -// `interface{}`). -// -// The following struct annotations are supported: -// -// toml:"Field" Overrides the field's name to map to. -// -// See Marshal() documentation for types mapping table. -func Unmarshal(data []byte, v interface{}) error { - t, err := LoadReader(bytes.NewReader(data)) - if err != nil { - return err - } - return t.Unmarshal(v) -} - -// Decoder reads and decodes TOML values from an input stream. -type Decoder struct { - r io.Reader - tval *Tree - encOpts -} - -// NewDecoder returns a new decoder that reads from r. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{ - r: r, - encOpts: encOptsDefaults, - } -} - -// Decode reads a TOML-encoded value from it's input -// and unmarshals it in the value pointed at by v. -// -// See the documentation for Marshal for details. -func (d *Decoder) Decode(v interface{}) error { - var err error - d.tval, err = LoadReader(d.r) - if err != nil { - return err - } - return d.unmarshal(v) -} - -func (d *Decoder) unmarshal(v interface{}) error { - mtype := reflect.TypeOf(v) - if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct { - return errors.New("Only a pointer to struct can be unmarshaled from TOML") - } - - sval, err := d.valueFromTree(mtype.Elem(), d.tval) - if err != nil { - return err - } - reflect.ValueOf(v).Elem().Set(sval) - return nil -} - -// Convert toml tree to marshal struct or map, using marshal type -func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) { - if mtype.Kind() == reflect.Ptr { - return d.unwrapPointer(mtype, tval) - } - var mval reflect.Value - switch mtype.Kind() { - case reflect.Struct: - mval = reflect.New(mtype).Elem() - for i := 0; i < mtype.NumField(); i++ { - mtypef := mtype.Field(i) - opts := tomlOptions(mtypef) - if opts.include { - baseKey := opts.name - keysToTry := []string{baseKey, strings.ToLower(baseKey), strings.ToTitle(baseKey)} - for _, key := range keysToTry { - exists := tval.Has(key) - if !exists { - continue - } - val := tval.Get(key) - mvalf, err := d.valueFromToml(mtypef.Type, val) - if err != nil { - return mval, formatError(err, tval.GetPosition(key)) - } - mval.Field(i).Set(mvalf) - break - } - } - } - case reflect.Map: - mval = reflect.MakeMap(mtype) - for _, key := range tval.Keys() { - // TODO: path splits key - val := tval.GetPath([]string{key}) - mvalf, err := d.valueFromToml(mtype.Elem(), val) - if err != nil { - return mval, formatError(err, tval.GetPosition(key)) - } - mval.SetMapIndex(reflect.ValueOf(key), mvalf) - } - } - return mval, nil -} - -// Convert toml value to marshal struct/map slice, using marshal type -func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) { - mval := reflect.MakeSlice(mtype, len(tval), len(tval)) - for i := 0; i < len(tval); i++ { - val, err := d.valueFromTree(mtype.Elem(), tval[i]) - if err != nil { - return mval, err - } - mval.Index(i).Set(val) - } - return mval, nil -} - -// Convert toml value to marshal primitive slice, using marshal type -func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) { - mval := reflect.MakeSlice(mtype, len(tval), len(tval)) - for i := 0; i < len(tval); i++ { - val, err := d.valueFromToml(mtype.Elem(), tval[i]) - if err != nil { - return mval, err - } - mval.Index(i).Set(val) - } - return mval, nil -} - -// Convert toml value to marshal value, using marshal type -func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) { - if mtype.Kind() == reflect.Ptr { - return d.unwrapPointer(mtype, tval) - } - - switch tval.(type) { - case *Tree: - if isTree(mtype) { - return d.valueFromTree(mtype, tval.(*Tree)) - } - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval) - case []*Tree: - if isTreeSlice(mtype) { - return d.valueFromTreeSlice(mtype, tval.([]*Tree)) - } - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval) - case []interface{}: - if isOtherSlice(mtype) { - return d.valueFromOtherSlice(mtype, tval.([]interface{})) - } - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval) - default: - switch mtype.Kind() { - case reflect.Bool, reflect.Struct: - val := reflect.ValueOf(tval) - // if this passes for when mtype is reflect.Struct, tval is a time.Time - if !val.Type().ConvertibleTo(mtype) { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.String: - val := reflect.ValueOf(tval) - // stupidly, int64 is convertible to string. So special case this. - if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Int64 { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val := reflect.ValueOf(tval) - if !val.Type().ConvertibleTo(mtype) { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - if reflect.Indirect(reflect.New(mtype)).OverflowInt(val.Int()) { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - val := reflect.ValueOf(tval) - if !val.Type().ConvertibleTo(mtype) { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - if val.Int() < 0 { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) is negative so does not fit in %v", tval, tval, mtype.String()) - } - if reflect.Indirect(reflect.New(mtype)).OverflowUint(uint64(val.Int())) { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - case reflect.Float32, reflect.Float64: - val := reflect.ValueOf(tval) - if !val.Type().ConvertibleTo(mtype) { - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String()) - } - if reflect.Indirect(reflect.New(mtype)).OverflowFloat(val.Float()) { - return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String()) - } - - return val.Convert(mtype), nil - default: - return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind()) - } - } -} - -func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) { - val, err := d.valueFromToml(mtype.Elem(), tval) - if err != nil { - return reflect.ValueOf(nil), err - } - mval := reflect.New(mtype.Elem()) - mval.Elem().Set(val) - return mval, nil -} - -func tomlOptions(vf reflect.StructField) tomlOpts { - tag := vf.Tag.Get("toml") - parse := strings.Split(tag, ",") - var comment string - if c := vf.Tag.Get("comment"); c != "" { - comment = c - } - commented, _ := strconv.ParseBool(vf.Tag.Get("commented")) - multiline, _ := strconv.ParseBool(vf.Tag.Get(tagKeyMultiline)) - result := tomlOpts{name: vf.Name, comment: comment, commented: commented, multiline: multiline, include: true, omitempty: false} - if parse[0] != "" { - if parse[0] == "-" && len(parse) == 1 { - result.include = false - } else { - result.name = strings.Trim(parse[0], " ") - } - } - if vf.PkgPath != "" { - result.include = false - } - if len(parse) > 1 && strings.Trim(parse[1], " ") == "omitempty" { - result.omitempty = true - } - if vf.Type.Kind() == reflect.Ptr { - result.omitempty = true - } - return result -} - -func isZero(val reflect.Value) bool { - switch val.Type().Kind() { - case reflect.Map: - fallthrough - case reflect.Array: - fallthrough - case reflect.Slice: - return val.Len() == 0 - default: - return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface()) - } -} - -func formatError(err error, pos Position) error { - if err.Error()[0] == '(' { // Error already contains position information - return err - } - return fmt.Errorf("%s: %s", pos, err) -} diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.go b/vendor/github.com/pelletier/go-toml/marshal_test.go deleted file mode 100644 index 00cbbf31a..000000000 --- a/vendor/github.com/pelletier/go-toml/marshal_test.go +++ /dev/null @@ -1,806 +0,0 @@ -package toml - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "reflect" - "strings" - "testing" - "time" -) - -type basicMarshalTestStruct struct { - String string `toml:"string"` - StringList []string `toml:"strlist"` - Sub basicMarshalTestSubStruct `toml:"subdoc"` - SubList []basicMarshalTestSubStruct `toml:"sublist"` -} - -type basicMarshalTestSubStruct struct { - String2 string -} - -var basicTestData = basicMarshalTestStruct{ - String: "Hello", - StringList: []string{"Howdy", "Hey There"}, - Sub: basicMarshalTestSubStruct{"One"}, - SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, -} - -var basicTestToml = []byte(`string = "Hello" -strlist = ["Howdy","Hey There"] - -[subdoc] - String2 = "One" - -[[sublist]] - String2 = "Two" - -[[sublist]] - String2 = "Three" -`) - -func TestBasicMarshal(t *testing.T) { - result, err := Marshal(basicTestData) - if err != nil { - t.Fatal(err) - } - expected := basicTestToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestBasicUnmarshal(t *testing.T) { - result := basicMarshalTestStruct{} - err := Unmarshal(basicTestToml, &result) - expected := basicTestData - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Bad unmarshal: expected %v, got %v", expected, result) - } -} - -type testDoc struct { - Title string `toml:"title"` - Basics testDocBasics `toml:"basic"` - BasicLists testDocBasicLists `toml:"basic_lists"` - BasicMap map[string]string `toml:"basic_map"` - Subdocs testDocSubs `toml:"subdoc"` - SubDocList []testSubDoc `toml:"subdoclist"` - SubDocPtrs []*testSubDoc `toml:"subdocptrs"` - err int `toml:"shouldntBeHere"` - unexported int `toml:"shouldntBeHere"` - Unexported2 int `toml:"-"` -} - -type testDocBasics struct { - Bool bool `toml:"bool"` - Date time.Time `toml:"date"` - Float float32 `toml:"float"` - Int int `toml:"int"` - Uint uint `toml:"uint"` - String *string `toml:"string"` - unexported int `toml:"shouldntBeHere"` -} - -type testDocBasicLists struct { - Bools []bool `toml:"bools"` - Dates []time.Time `toml:"dates"` - Floats []*float32 `toml:"floats"` - Ints []int `toml:"ints"` - Strings []string `toml:"strings"` - UInts []uint `toml:"uints"` -} - -type testDocSubs struct { - First testSubDoc `toml:"first"` - Second *testSubDoc `toml:"second"` -} - -type testSubDoc struct { - Name string `toml:"name"` - unexported int `toml:"shouldntBeHere"` -} - -var biteMe = "Bite me" -var float1 float32 = 12.3 -var float2 float32 = 45.6 -var float3 float32 = 78.9 -var subdoc = testSubDoc{"Second", 0} - -var docData = testDoc{ - Title: "TOML Marshal Testing", - unexported: 0, - Unexported2: 0, - Basics: testDocBasics{ - Bool: true, - Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), - Float: 123.4, - Int: 5000, - Uint: 5001, - String: &biteMe, - unexported: 0, - }, - BasicLists: testDocBasicLists{ - Bools: []bool{true, false, true}, - Dates: []time.Time{ - time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), - time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC), - }, - Floats: []*float32{&float1, &float2, &float3}, - Ints: []int{8001, 8001, 8002}, - Strings: []string{"One", "Two", "Three"}, - UInts: []uint{5002, 5003}, - }, - BasicMap: map[string]string{ - "one": "one", - "two": "two", - }, - Subdocs: testDocSubs{ - First: testSubDoc{"First", 0}, - Second: &subdoc, - }, - SubDocList: []testSubDoc{ - {"List.First", 0}, - {"List.Second", 0}, - }, - SubDocPtrs: []*testSubDoc{&subdoc}, -} - -func TestDocMarshal(t *testing.T) { - result, err := Marshal(docData) - if err != nil { - t.Fatal(err) - } - expected, _ := ioutil.ReadFile("marshal_test.toml") - if !bytes.Equal(result, expected) { - t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestDocUnmarshal(t *testing.T) { - result := testDoc{} - tomlData, _ := ioutil.ReadFile("marshal_test.toml") - err := Unmarshal(tomlData, &result) - expected := docData - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - resStr, _ := json.MarshalIndent(result, "", " ") - expStr, _ := json.MarshalIndent(expected, "", " ") - t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr) - } -} - -func TestDocPartialUnmarshal(t *testing.T) { - result := testDocSubs{} - - tree, _ := LoadFile("marshal_test.toml") - subTree := tree.Get("subdoc").(*Tree) - err := subTree.Unmarshal(&result) - expected := docData.Subdocs - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - resStr, _ := json.MarshalIndent(result, "", " ") - expStr, _ := json.MarshalIndent(expected, "", " ") - t.Errorf("Bad partial unmartial: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr) - } -} - -type tomlTypeCheckTest struct { - name string - item interface{} - typ int //0=primitive, 1=otherslice, 2=treeslice, 3=tree -} - -func TestTypeChecks(t *testing.T) { - tests := []tomlTypeCheckTest{ - {"integer", 2, 0}, - {"time", time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), 0}, - {"stringlist", []string{"hello", "hi"}, 1}, - {"timelist", []time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1}, - {"objectlist", []tomlTypeCheckTest{}, 2}, - {"object", tomlTypeCheckTest{}, 3}, - } - - for _, test := range tests { - expected := []bool{false, false, false, false} - expected[test.typ] = true - result := []bool{ - isPrimitive(reflect.TypeOf(test.item)), - isOtherSlice(reflect.TypeOf(test.item)), - isTreeSlice(reflect.TypeOf(test.item)), - isTree(reflect.TypeOf(test.item)), - } - if !reflect.DeepEqual(expected, result) { - t.Errorf("Bad type check on %q: expected %v, got %v", test.name, expected, result) - } - } -} - -type unexportedMarshalTestStruct struct { - String string `toml:"string"` - StringList []string `toml:"strlist"` - Sub basicMarshalTestSubStruct `toml:"subdoc"` - SubList []basicMarshalTestSubStruct `toml:"sublist"` - unexported int `toml:"shouldntBeHere"` - Unexported2 int `toml:"-"` -} - -var unexportedTestData = unexportedMarshalTestStruct{ - String: "Hello", - StringList: []string{"Howdy", "Hey There"}, - Sub: basicMarshalTestSubStruct{"One"}, - SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, - unexported: 0, - Unexported2: 0, -} - -var unexportedTestToml = []byte(`string = "Hello" -strlist = ["Howdy","Hey There"] -unexported = 1 -shouldntBeHere = 2 - -[subdoc] - String2 = "One" - -[[sublist]] - String2 = "Two" - -[[sublist]] - String2 = "Three" -`) - -func TestUnexportedUnmarshal(t *testing.T) { - result := unexportedMarshalTestStruct{} - err := Unmarshal(unexportedTestToml, &result) - expected := unexportedTestData - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Bad unexported unmarshal: expected %v, got %v", expected, result) - } -} - -type errStruct struct { - Bool bool `toml:"bool"` - Date time.Time `toml:"date"` - Float float64 `toml:"float"` - Int int16 `toml:"int"` - String *string `toml:"string"` -} - -var errTomls = []string{ - "bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", - "bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", - "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"", - "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"", - "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me", - "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me", - "bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", - "bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", - "bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"", - "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"", - "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1", -} - -type mapErr struct { - Vals map[string]float64 -} - -type intErr struct { - Int1 int - Int2 int8 - Int3 int16 - Int4 int32 - Int5 int64 - UInt1 uint - UInt2 uint8 - UInt3 uint16 - UInt4 uint32 - UInt5 uint64 - Flt1 float32 - Flt2 float64 -} - -var intErrTomls = []string{ - "Int1 = []\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = []\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = []\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = []\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = []\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = []\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = []\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = []\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = []\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = []\nFlt1 = 1.0\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0", - "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []", -} - -func TestErrUnmarshal(t *testing.T) { - for ind, toml := range errTomls { - result := errStruct{} - err := Unmarshal([]byte(toml), &result) - if err == nil { - t.Errorf("Expected err from case %d\n", ind) - } - } - result2 := mapErr{} - err := Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2) - if err == nil { - t.Errorf("Expected err from map") - } - for ind, toml := range intErrTomls { - result3 := intErr{} - err := Unmarshal([]byte(toml), &result3) - if err == nil { - t.Errorf("Expected int err from case %d\n", ind) - } - } -} - -type emptyMarshalTestStruct struct { - Title string `toml:"title"` - Bool bool `toml:"bool"` - Int int `toml:"int"` - String string `toml:"string"` - StringList []string `toml:"stringlist"` - Ptr *basicMarshalTestStruct `toml:"ptr"` - Map map[string]string `toml:"map"` -} - -var emptyTestData = emptyMarshalTestStruct{ - Title: "Placeholder", - Bool: false, - Int: 0, - String: "", - StringList: []string{}, - Ptr: nil, - Map: map[string]string{}, -} - -var emptyTestToml = []byte(`bool = false -int = 0 -string = "" -stringlist = [] -title = "Placeholder" - -[map] -`) - -type emptyMarshalTestStruct2 struct { - Title string `toml:"title"` - Bool bool `toml:"bool,omitempty"` - Int int `toml:"int, omitempty"` - String string `toml:"string,omitempty "` - StringList []string `toml:"stringlist,omitempty"` - Ptr *basicMarshalTestStruct `toml:"ptr,omitempty"` - Map map[string]string `toml:"map,omitempty"` -} - -var emptyTestData2 = emptyMarshalTestStruct2{ - Title: "Placeholder", - Bool: false, - Int: 0, - String: "", - StringList: []string{}, - Ptr: nil, - Map: map[string]string{}, -} - -var emptyTestToml2 = []byte(`title = "Placeholder" -`) - -func TestEmptyMarshal(t *testing.T) { - result, err := Marshal(emptyTestData) - if err != nil { - t.Fatal(err) - } - expected := emptyTestToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad empty marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestEmptyMarshalOmit(t *testing.T) { - result, err := Marshal(emptyTestData2) - if err != nil { - t.Fatal(err) - } - expected := emptyTestToml2 - if !bytes.Equal(result, expected) { - t.Errorf("Bad empty omit marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestEmptyUnmarshal(t *testing.T) { - result := emptyMarshalTestStruct{} - err := Unmarshal(emptyTestToml, &result) - expected := emptyTestData - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Bad empty unmarshal: expected %v, got %v", expected, result) - } -} - -func TestEmptyUnmarshalOmit(t *testing.T) { - result := emptyMarshalTestStruct2{} - err := Unmarshal(emptyTestToml, &result) - expected := emptyTestData2 - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Bad empty omit unmarshal: expected %v, got %v", expected, result) - } -} - -type pointerMarshalTestStruct struct { - Str *string - List *[]string - ListPtr *[]*string - Map *map[string]string - MapPtr *map[string]*string - EmptyStr *string - EmptyList *[]string - EmptyMap *map[string]string - DblPtr *[]*[]*string -} - -var pointerStr = "Hello" -var pointerList = []string{"Hello back"} -var pointerListPtr = []*string{&pointerStr} -var pointerMap = map[string]string{"response": "Goodbye"} -var pointerMapPtr = map[string]*string{"alternate": &pointerStr} -var pointerTestData = pointerMarshalTestStruct{ - Str: &pointerStr, - List: &pointerList, - ListPtr: &pointerListPtr, - Map: &pointerMap, - MapPtr: &pointerMapPtr, - EmptyStr: nil, - EmptyList: nil, - EmptyMap: nil, -} - -var pointerTestToml = []byte(`List = ["Hello back"] -ListPtr = ["Hello"] -Str = "Hello" - -[Map] - response = "Goodbye" - -[MapPtr] - alternate = "Hello" -`) - -func TestPointerMarshal(t *testing.T) { - result, err := Marshal(pointerTestData) - if err != nil { - t.Fatal(err) - } - expected := pointerTestToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad pointer marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestPointerUnmarshal(t *testing.T) { - result := pointerMarshalTestStruct{} - err := Unmarshal(pointerTestToml, &result) - expected := pointerTestData - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Bad pointer unmarshal: expected %v, got %v", expected, result) - } -} - -func TestUnmarshalTypeMismatch(t *testing.T) { - result := pointerMarshalTestStruct{} - err := Unmarshal([]byte("List = 123"), &result) - if !strings.HasPrefix(err.Error(), "(1, 1): Can't convert 123(int64) to []string(slice)") { - t.Errorf("Type mismatch must be reported: got %v", err.Error()) - } -} - -type nestedMarshalTestStruct struct { - String [][]string - //Struct [][]basicMarshalTestSubStruct - StringPtr *[]*[]*string - // StructPtr *[]*[]*basicMarshalTestSubStruct -} - -var str1 = "Three" -var str2 = "Four" -var strPtr = []*string{&str1, &str2} -var strPtr2 = []*[]*string{&strPtr} - -var nestedTestData = nestedMarshalTestStruct{ - String: [][]string{{"Five", "Six"}, {"One", "Two"}}, - StringPtr: &strPtr2, -} - -var nestedTestToml = []byte(`String = [["Five","Six"],["One","Two"]] -StringPtr = [["Three","Four"]] -`) - -func TestNestedMarshal(t *testing.T) { - result, err := Marshal(nestedTestData) - if err != nil { - t.Fatal(err) - } - expected := nestedTestToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad nested marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestNestedUnmarshal(t *testing.T) { - result := nestedMarshalTestStruct{} - err := Unmarshal(nestedTestToml, &result) - expected := nestedTestData - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Bad nested unmarshal: expected %v, got %v", expected, result) - } -} - -type customMarshalerParent struct { - Self customMarshaler `toml:"me"` - Friends []customMarshaler `toml:"friends"` -} - -type customMarshaler struct { - FirsName string - LastName string -} - -func (c customMarshaler) MarshalTOML() ([]byte, error) { - fullName := fmt.Sprintf("%s %s", c.FirsName, c.LastName) - return []byte(fullName), nil -} - -var customMarshalerData = customMarshaler{FirsName: "Sally", LastName: "Fields"} -var customMarshalerToml = []byte(`Sally Fields`) -var nestedCustomMarshalerData = customMarshalerParent{ - Self: customMarshaler{FirsName: "Maiku", LastName: "Suteda"}, - Friends: []customMarshaler{customMarshalerData}, -} -var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"] -me = "Maiku Suteda" -`) - -func TestCustomMarshaler(t *testing.T) { - result, err := Marshal(customMarshalerData) - if err != nil { - t.Fatal(err) - } - expected := customMarshalerToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestNestedCustomMarshaler(t *testing.T) { - result, err := Marshal(nestedCustomMarshalerData) - if err != nil { - t.Fatal(err) - } - expected := nestedCustomMarshalerToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad nested custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -var commentTestToml = []byte(` -# it's a comment on type -[postgres] - # isCommented = "dvalue" - noComment = "cvalue" - - # A comment on AttrB with a - # break line - password = "bvalue" - - # A comment on AttrA - user = "avalue" - - [[postgres.My]] - - # a comment on my on typeC - My = "Foo" - - [[postgres.My]] - - # a comment on my on typeC - My = "Baar" -`) - -func TestMarshalComment(t *testing.T) { - type TypeC struct { - My string `comment:"a comment on my on typeC"` - } - type TypeB struct { - AttrA string `toml:"user" comment:"A comment on AttrA"` - AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"` - AttrC string `toml:"noComment"` - AttrD string `toml:"isCommented" commented:"true"` - My []TypeC - } - type TypeA struct { - TypeB TypeB `toml:"postgres" comment:"it's a comment on type"` - } - - ta := []TypeC{{My: "Foo"}, {My: "Baar"}} - config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}} - result, err := Marshal(config) - if err != nil { - t.Fatal(err) - } - expected := commentTestToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -type mapsTestStruct struct { - Simple map[string]string - Paths map[string]string - Other map[string]float64 - X struct { - Y struct { - Z map[string]bool - } - } -} - -var mapsTestData = mapsTestStruct{ - Simple: map[string]string{ - "one plus one": "two", - "next": "three", - }, - Paths: map[string]string{ - "/this/is/a/path": "/this/is/also/a/path", - "/heloo.txt": "/tmp/lololo.txt", - }, - Other: map[string]float64{ - "testing": 3.9999, - }, - X: struct{ Y struct{ Z map[string]bool } }{ - Y: struct{ Z map[string]bool }{ - Z: map[string]bool{ - "is.Nested": true, - }, - }, - }, -} -var mapsTestToml = []byte(` -[Other] - "testing" = 3.9999 - -[Paths] - "/heloo.txt" = "/tmp/lololo.txt" - "/this/is/a/path" = "/this/is/also/a/path" - -[Simple] - "next" = "three" - "one plus one" = "two" - -[X] - - [X.Y] - - [X.Y.Z] - "is.Nested" = true -`) - -func TestEncodeQuotedMapKeys(t *testing.T) { - var buf bytes.Buffer - if err := NewEncoder(&buf).QuoteMapKeys(true).Encode(mapsTestData); err != nil { - t.Fatal(err) - } - result := buf.Bytes() - expected := mapsTestToml - if !bytes.Equal(result, expected) { - t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result) - } -} - -func TestDecodeQuotedMapKeys(t *testing.T) { - result := mapsTestStruct{} - err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result) - expected := mapsTestData - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(result, expected) { - t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result) - } -} - -type structArrayNoTag struct { - A struct { - B []int64 - C []int64 - } -} - -func TestMarshalArray(t *testing.T) { - expected := []byte(` -[A] - B = [1,2,3] - C = [1] -`) - - m := structArrayNoTag{ - A: struct { - B []int64 - C []int64 - }{ - B: []int64{1, 2, 3}, - C: []int64{1}, - }, - } - - b, err := Marshal(m) - - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(b, expected) { - t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b) - } -} - -func TestMarshalArrayOnePerLine(t *testing.T) { - expected := []byte(` -[A] - B = [ - 1, - 2, - 3, - ] - C = [1] -`) - - m := structArrayNoTag{ - A: struct { - B []int64 - C []int64 - }{ - B: []int64{1, 2, 3}, - C: []int64{1}, - }, - } - - var buf bytes.Buffer - encoder := NewEncoder(&buf).ArraysWithOneElementPerLine(true) - err := encoder.Encode(m) - - if err != nil { - t.Fatal(err) - } - - b := buf.Bytes() - - if !bytes.Equal(b, expected) { - t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b) - } -} diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.toml b/vendor/github.com/pelletier/go-toml/marshal_test.toml deleted file mode 100644 index 1c5f98e7a..000000000 --- a/vendor/github.com/pelletier/go-toml/marshal_test.toml +++ /dev/null @@ -1,38 +0,0 @@ -title = "TOML Marshal Testing" - -[basic] - bool = true - date = 1979-05-27T07:32:00Z - float = 123.4 - int = 5000 - string = "Bite me" - uint = 5001 - -[basic_lists] - bools = [true,false,true] - dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z] - floats = [12.3,45.6,78.9] - ints = [8001,8001,8002] - strings = ["One","Two","Three"] - uints = [5002,5003] - -[basic_map] - one = "one" - two = "two" - -[subdoc] - - [subdoc.first] - name = "First" - - [subdoc.second] - name = "Second" - -[[subdoclist]] - name = "List.First" - -[[subdoclist]] - name = "List.Second" - -[[subdocptrs]] - name = "Second" diff --git a/vendor/github.com/pelletier/go-toml/parser.go b/vendor/github.com/pelletier/go-toml/parser.go deleted file mode 100644 index 2d27599a9..000000000 --- a/vendor/github.com/pelletier/go-toml/parser.go +++ /dev/null @@ -1,430 +0,0 @@ -// TOML Parser. - -package toml - -import ( - "errors" - "fmt" - "math" - "reflect" - "regexp" - "strconv" - "strings" - "time" -) - -type tomlParser struct { - flowIdx int - flow []token - tree *Tree - currentTable []string - seenTableKeys []string -} - -type tomlParserStateFn func() tomlParserStateFn - -// Formats and panics an error message based on a token -func (p *tomlParser) raiseError(tok *token, msg string, args ...interface{}) { - panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...)) -} - -func (p *tomlParser) run() { - for state := p.parseStart; state != nil; { - state = state() - } -} - -func (p *tomlParser) peek() *token { - if p.flowIdx >= len(p.flow) { - return nil - } - return &p.flow[p.flowIdx] -} - -func (p *tomlParser) assume(typ tokenType) { - tok := p.getToken() - if tok == nil { - p.raiseError(tok, "was expecting token %s, but token stream is empty", tok) - } - if tok.typ != typ { - p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok) - } -} - -func (p *tomlParser) getToken() *token { - tok := p.peek() - if tok == nil { - return nil - } - p.flowIdx++ - return tok -} - -func (p *tomlParser) parseStart() tomlParserStateFn { - tok := p.peek() - - // end of stream, parsing is finished - if tok == nil { - return nil - } - - switch tok.typ { - case tokenDoubleLeftBracket: - return p.parseGroupArray - case tokenLeftBracket: - return p.parseGroup - case tokenKey: - return p.parseAssign - case tokenEOF: - return nil - default: - p.raiseError(tok, "unexpected token") - } - return nil -} - -func (p *tomlParser) parseGroupArray() tomlParserStateFn { - startToken := p.getToken() // discard the [[ - key := p.getToken() - if key.typ != tokenKeyGroupArray { - p.raiseError(key, "unexpected token %s, was expecting a table array key", key) - } - - // get or create table array element at the indicated part in the path - keys, err := parseKey(key.val) - if err != nil { - p.raiseError(key, "invalid table array key: %s", err) - } - p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries - destTree := p.tree.GetPath(keys) - var array []*Tree - if destTree == nil { - array = make([]*Tree, 0) - } else if target, ok := destTree.([]*Tree); ok && target != nil { - array = destTree.([]*Tree) - } else { - p.raiseError(key, "key %s is already assigned and not of type table array", key) - } - p.currentTable = keys - - // add a new tree to the end of the table array - newTree := newTree() - newTree.position = startToken.Position - array = append(array, newTree) - p.tree.SetPath(p.currentTable, array) - - // remove all keys that were children of this table array - prefix := key.val + "." - found := false - for ii := 0; ii < len(p.seenTableKeys); { - tableKey := p.seenTableKeys[ii] - if strings.HasPrefix(tableKey, prefix) { - p.seenTableKeys = append(p.seenTableKeys[:ii], p.seenTableKeys[ii+1:]...) - } else { - found = (tableKey == key.val) - ii++ - } - } - - // keep this key name from use by other kinds of assignments - if !found { - p.seenTableKeys = append(p.seenTableKeys, key.val) - } - - // move to next parser state - p.assume(tokenDoubleRightBracket) - return p.parseStart -} - -func (p *tomlParser) parseGroup() tomlParserStateFn { - startToken := p.getToken() // discard the [ - key := p.getToken() - if key.typ != tokenKeyGroup { - p.raiseError(key, "unexpected token %s, was expecting a table key", key) - } - for _, item := range p.seenTableKeys { - if item == key.val { - p.raiseError(key, "duplicated tables") - } - } - - p.seenTableKeys = append(p.seenTableKeys, key.val) - keys, err := parseKey(key.val) - if err != nil { - p.raiseError(key, "invalid table array key: %s", err) - } - if err := p.tree.createSubTree(keys, startToken.Position); err != nil { - p.raiseError(key, "%s", err) - } - p.assume(tokenRightBracket) - p.currentTable = keys - return p.parseStart -} - -func (p *tomlParser) parseAssign() tomlParserStateFn { - key := p.getToken() - p.assume(tokenEqual) - - value := p.parseRvalue() - var tableKey []string - if len(p.currentTable) > 0 { - tableKey = p.currentTable - } else { - tableKey = []string{} - } - - // find the table to assign, looking out for arrays of tables - var targetNode *Tree - switch node := p.tree.GetPath(tableKey).(type) { - case []*Tree: - targetNode = node[len(node)-1] - case *Tree: - targetNode = node - default: - p.raiseError(key, "Unknown table type for path: %s", - strings.Join(tableKey, ".")) - } - - // assign value to the found table - keyVals := []string{key.val} - if len(keyVals) != 1 { - p.raiseError(key, "Invalid key") - } - keyVal := keyVals[0] - localKey := []string{keyVal} - finalKey := append(tableKey, keyVal) - if targetNode.GetPath(localKey) != nil { - p.raiseError(key, "The following key was defined twice: %s", - strings.Join(finalKey, ".")) - } - var toInsert interface{} - - switch value.(type) { - case *Tree, []*Tree: - toInsert = value - default: - toInsert = &tomlValue{value: value, position: key.Position} - } - targetNode.values[keyVal] = toInsert - return p.parseStart -} - -var numberUnderscoreInvalidRegexp *regexp.Regexp -var hexNumberUnderscoreInvalidRegexp *regexp.Regexp - -func numberContainsInvalidUnderscore(value string) error { - if numberUnderscoreInvalidRegexp.MatchString(value) { - return errors.New("invalid use of _ in number") - } - return nil -} - -func hexNumberContainsInvalidUnderscore(value string) error { - if hexNumberUnderscoreInvalidRegexp.MatchString(value) { - return errors.New("invalid use of _ in hex number") - } - return nil -} - -func cleanupNumberToken(value string) string { - cleanedVal := strings.Replace(value, "_", "", -1) - return cleanedVal -} - -func (p *tomlParser) parseRvalue() interface{} { - tok := p.getToken() - if tok == nil || tok.typ == tokenEOF { - p.raiseError(tok, "expecting a value") - } - - switch tok.typ { - case tokenString: - return tok.val - case tokenTrue: - return true - case tokenFalse: - return false - case tokenInf: - if tok.val[0] == '-' { - return math.Inf(-1) - } - return math.Inf(1) - case tokenNan: - return math.NaN() - case tokenInteger: - cleanedVal := cleanupNumberToken(tok.val) - var err error - var val int64 - if len(cleanedVal) >= 3 && cleanedVal[0] == '0' { - switch cleanedVal[1] { - case 'x': - err = hexNumberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal[2:], 16, 64) - case 'o': - err = numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal[2:], 8, 64) - case 'b': - err = numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal[2:], 2, 64) - default: - panic("invalid base") // the lexer should catch this first - } - } else { - err = numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - val, err = strconv.ParseInt(cleanedVal, 10, 64) - } - if err != nil { - p.raiseError(tok, "%s", err) - } - return val - case tokenFloat: - err := numberContainsInvalidUnderscore(tok.val) - if err != nil { - p.raiseError(tok, "%s", err) - } - cleanedVal := cleanupNumberToken(tok.val) - val, err := strconv.ParseFloat(cleanedVal, 64) - if err != nil { - p.raiseError(tok, "%s", err) - } - return val - case tokenDate: - val, err := time.ParseInLocation(time.RFC3339Nano, tok.val, time.UTC) - if err != nil { - p.raiseError(tok, "%s", err) - } - return val - case tokenLeftBracket: - return p.parseArray() - case tokenLeftCurlyBrace: - return p.parseInlineTable() - case tokenEqual: - p.raiseError(tok, "cannot have multiple equals for the same key") - case tokenError: - p.raiseError(tok, "%s", tok) - } - - p.raiseError(tok, "never reached") - - return nil -} - -func tokenIsComma(t *token) bool { - return t != nil && t.typ == tokenComma -} - -func (p *tomlParser) parseInlineTable() *Tree { - tree := newTree() - var previous *token -Loop: - for { - follow := p.peek() - if follow == nil || follow.typ == tokenEOF { - p.raiseError(follow, "unterminated inline table") - } - switch follow.typ { - case tokenRightCurlyBrace: - p.getToken() - break Loop - case tokenKey: - if !tokenIsComma(previous) && previous != nil { - p.raiseError(follow, "comma expected between fields in inline table") - } - key := p.getToken() - p.assume(tokenEqual) - value := p.parseRvalue() - tree.Set(key.val, value) - case tokenComma: - if previous == nil { - p.raiseError(follow, "inline table cannot start with a comma") - } - if tokenIsComma(previous) { - p.raiseError(follow, "need field between two commas in inline table") - } - p.getToken() - default: - p.raiseError(follow, "unexpected token type in inline table: %s", follow.String()) - } - previous = follow - } - if tokenIsComma(previous) { - p.raiseError(previous, "trailing comma at the end of inline table") - } - return tree -} - -func (p *tomlParser) parseArray() interface{} { - var array []interface{} - arrayType := reflect.TypeOf(nil) - for { - follow := p.peek() - if follow == nil || follow.typ == tokenEOF { - p.raiseError(follow, "unterminated array") - } - if follow.typ == tokenRightBracket { - p.getToken() - break - } - val := p.parseRvalue() - if arrayType == nil { - arrayType = reflect.TypeOf(val) - } - if reflect.TypeOf(val) != arrayType { - p.raiseError(follow, "mixed types in array") - } - array = append(array, val) - follow = p.peek() - if follow == nil || follow.typ == tokenEOF { - p.raiseError(follow, "unterminated array") - } - if follow.typ != tokenRightBracket && follow.typ != tokenComma { - p.raiseError(follow, "missing comma") - } - if follow.typ == tokenComma { - p.getToken() - } - } - // An array of Trees is actually an array of inline - // tables, which is a shorthand for a table array. If the - // array was not converted from []interface{} to []*Tree, - // the two notations would not be equivalent. - if arrayType == reflect.TypeOf(newTree()) { - tomlArray := make([]*Tree, len(array)) - for i, v := range array { - tomlArray[i] = v.(*Tree) - } - return tomlArray - } - return array -} - -func parseToml(flow []token) *Tree { - result := newTree() - result.position = Position{1, 1} - parser := &tomlParser{ - flowIdx: 0, - flow: flow, - tree: result, - currentTable: make([]string, 0), - seenTableKeys: make([]string, 0), - } - parser.run() - return result -} - -func init() { - numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d])|_$|^_`) - hexNumberUnderscoreInvalidRegexp = regexp.MustCompile(`(^0x_)|([^\da-f]_|_[^\da-f])|_$|^_`) -} diff --git a/vendor/github.com/pelletier/go-toml/parser_test.go b/vendor/github.com/pelletier/go-toml/parser_test.go deleted file mode 100644 index ca29c442e..000000000 --- a/vendor/github.com/pelletier/go-toml/parser_test.go +++ /dev/null @@ -1,899 +0,0 @@ -package toml - -import ( - "fmt" - "math" - "reflect" - "testing" - "time" - - "github.com/davecgh/go-spew/spew" -) - -func assertSubTree(t *testing.T, path []string, tree *Tree, err error, ref map[string]interface{}) { - if err != nil { - t.Error("Non-nil error:", err.Error()) - return - } - for k, v := range ref { - nextPath := append(path, k) - t.Log("asserting path", nextPath) - // NOTE: directly access key instead of resolve by path - // NOTE: see TestSpecialKV - switch node := tree.GetPath([]string{k}).(type) { - case []*Tree: - t.Log("\tcomparing key", nextPath, "by array iteration") - for idx, item := range node { - assertSubTree(t, nextPath, item, err, v.([]map[string]interface{})[idx]) - } - case *Tree: - t.Log("\tcomparing key", nextPath, "by subtree assestion") - assertSubTree(t, nextPath, node, err, v.(map[string]interface{})) - default: - t.Log("\tcomparing key", nextPath, "by string representation because it's of type", reflect.TypeOf(node)) - if fmt.Sprintf("%v", node) != fmt.Sprintf("%v", v) { - t.Errorf("was expecting %v at %v but got %v", v, k, node) - } - } - } -} - -func assertTree(t *testing.T, tree *Tree, err error, ref map[string]interface{}) { - t.Log("Asserting tree:\n", spew.Sdump(tree)) - assertSubTree(t, []string{}, tree, err, ref) - t.Log("Finished tree assertion.") -} - -func TestCreateSubTree(t *testing.T) { - tree := newTree() - tree.createSubTree([]string{"a", "b", "c"}, Position{}) - tree.Set("a.b.c", 42) - if tree.Get("a.b.c") != 42 { - t.Fail() - } -} - -func TestSimpleKV(t *testing.T) { - tree, err := Load("a = 42") - assertTree(t, tree, err, map[string]interface{}{ - "a": int64(42), - }) - - tree, _ = Load("a = 42\nb = 21") - assertTree(t, tree, err, map[string]interface{}{ - "a": int64(42), - "b": int64(21), - }) -} - -func TestNumberInKey(t *testing.T) { - tree, err := Load("hello2 = 42") - assertTree(t, tree, err, map[string]interface{}{ - "hello2": int64(42), - }) -} - -func TestIncorrectKeyExtraSquareBracket(t *testing.T) { - _, err := Load(`[a]b] -zyx = 42`) - if err == nil { - t.Error("Error should have been returned.") - } - if err.Error() != "(1, 4): unexpected token" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestSimpleNumbers(t *testing.T) { - tree, err := Load("a = +42\nb = -21\nc = +4.2\nd = -2.1") - assertTree(t, tree, err, map[string]interface{}{ - "a": int64(42), - "b": int64(-21), - "c": float64(4.2), - "d": float64(-2.1), - }) -} - -func TestSpecialFloats(t *testing.T) { - tree, err := Load(` -normalinf = inf -plusinf = +inf -minusinf = -inf -normalnan = nan -plusnan = +nan -minusnan = -nan -`) - assertTree(t, tree, err, map[string]interface{}{ - "normalinf": math.Inf(1), - "plusinf": math.Inf(1), - "minusinf": math.Inf(-1), - "normalnan": math.NaN(), - "plusnan": math.NaN(), - "minusnan": math.NaN(), - }) -} - -func TestHexIntegers(t *testing.T) { - tree, err := Load(`a = 0xDEADBEEF`) - assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) - - tree, err = Load(`a = 0xdeadbeef`) - assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) - - tree, err = Load(`a = 0xdead_beef`) - assertTree(t, tree, err, map[string]interface{}{"a": int64(3735928559)}) - - _, err = Load(`a = 0x_1`) - if err.Error() != "(1, 5): invalid use of _ in hex number" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestOctIntegers(t *testing.T) { - tree, err := Load(`a = 0o01234567`) - assertTree(t, tree, err, map[string]interface{}{"a": int64(342391)}) - - tree, err = Load(`a = 0o755`) - assertTree(t, tree, err, map[string]interface{}{"a": int64(493)}) - - _, err = Load(`a = 0o_1`) - if err.Error() != "(1, 5): invalid use of _ in number" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestBinIntegers(t *testing.T) { - tree, err := Load(`a = 0b11010110`) - assertTree(t, tree, err, map[string]interface{}{"a": int64(214)}) - - _, err = Load(`a = 0b_1`) - if err.Error() != "(1, 5): invalid use of _ in number" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestBadIntegerBase(t *testing.T) { - _, err := Load(`a = 0k1`) - if err.Error() != "(1, 5): unknown number base: k. possible options are x (hex) o (octal) b (binary)" { - t.Error("Error should have been returned.") - } -} - -func TestIntegerNoDigit(t *testing.T) { - _, err := Load(`a = 0b`) - if err.Error() != "(1, 5): number needs at least one digit" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestNumbersWithUnderscores(t *testing.T) { - tree, err := Load("a = 1_000") - assertTree(t, tree, err, map[string]interface{}{ - "a": int64(1000), - }) - - tree, err = Load("a = 5_349_221") - assertTree(t, tree, err, map[string]interface{}{ - "a": int64(5349221), - }) - - tree, err = Load("a = 1_2_3_4_5") - assertTree(t, tree, err, map[string]interface{}{ - "a": int64(12345), - }) - - tree, err = Load("flt8 = 9_224_617.445_991_228_313") - assertTree(t, tree, err, map[string]interface{}{ - "flt8": float64(9224617.445991228313), - }) - - tree, err = Load("flt9 = 1e1_00") - assertTree(t, tree, err, map[string]interface{}{ - "flt9": float64(1e100), - }) -} - -func TestFloatsWithExponents(t *testing.T) { - tree, err := Load("a = 5e+22\nb = 5E+22\nc = -5e+22\nd = -5e-22\ne = 6.626e-34") - assertTree(t, tree, err, map[string]interface{}{ - "a": float64(5e+22), - "b": float64(5E+22), - "c": float64(-5e+22), - "d": float64(-5e-22), - "e": float64(6.626e-34), - }) -} - -func TestSimpleDate(t *testing.T) { - tree, err := Load("a = 1979-05-27T07:32:00Z") - assertTree(t, tree, err, map[string]interface{}{ - "a": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), - }) -} - -func TestDateOffset(t *testing.T) { - tree, err := Load("a = 1979-05-27T00:32:00-07:00") - assertTree(t, tree, err, map[string]interface{}{ - "a": time.Date(1979, time.May, 27, 0, 32, 0, 0, time.FixedZone("", -7*60*60)), - }) -} - -func TestDateNano(t *testing.T) { - tree, err := Load("a = 1979-05-27T00:32:00.999999999-07:00") - assertTree(t, tree, err, map[string]interface{}{ - "a": time.Date(1979, time.May, 27, 0, 32, 0, 999999999, time.FixedZone("", -7*60*60)), - }) -} - -func TestSimpleString(t *testing.T) { - tree, err := Load("a = \"hello world\"") - assertTree(t, tree, err, map[string]interface{}{ - "a": "hello world", - }) -} - -func TestSpaceKey(t *testing.T) { - tree, err := Load("\"a b\" = \"hello world\"") - assertTree(t, tree, err, map[string]interface{}{ - "a b": "hello world", - }) -} - -func TestDoubleQuotedKey(t *testing.T) { - tree, err := Load(` - "key" = "a" - "\t" = "b" - "\U0001F914" = "c" - "\u2764" = "d" - `) - assertTree(t, tree, err, map[string]interface{}{ - "key": "a", - "\t": "b", - "\U0001F914": "c", - "\u2764": "d", - }) -} - -func TestSingleQuotedKey(t *testing.T) { - tree, err := Load(` - 'key' = "a" - '\t' = "b" - '\U0001F914' = "c" - '\u2764' = "d" - `) - assertTree(t, tree, err, map[string]interface{}{ - `key`: "a", - `\t`: "b", - `\U0001F914`: "c", - `\u2764`: "d", - }) -} - -func TestStringEscapables(t *testing.T) { - tree, err := Load("a = \"a \\n b\"") - assertTree(t, tree, err, map[string]interface{}{ - "a": "a \n b", - }) - - tree, err = Load("a = \"a \\t b\"") - assertTree(t, tree, err, map[string]interface{}{ - "a": "a \t b", - }) - - tree, err = Load("a = \"a \\r b\"") - assertTree(t, tree, err, map[string]interface{}{ - "a": "a \r b", - }) - - tree, err = Load("a = \"a \\\\ b\"") - assertTree(t, tree, err, map[string]interface{}{ - "a": "a \\ b", - }) -} - -func TestEmptyQuotedString(t *testing.T) { - tree, err := Load(`[""] -"" = 1`) - assertTree(t, tree, err, map[string]interface{}{ - "": map[string]interface{}{ - "": int64(1), - }, - }) -} - -func TestBools(t *testing.T) { - tree, err := Load("a = true\nb = false") - assertTree(t, tree, err, map[string]interface{}{ - "a": true, - "b": false, - }) -} - -func TestNestedKeys(t *testing.T) { - tree, err := Load("[a.b.c]\nd = 42") - assertTree(t, tree, err, map[string]interface{}{ - "a": map[string]interface{}{ - "b": map[string]interface{}{ - "c": map[string]interface{}{ - "d": int64(42), - }, - }, - }, - }) -} - -func TestNestedQuotedUnicodeKeys(t *testing.T) { - tree, err := Load("[ j . \"ʞ\" . l ]\nd = 42") - assertTree(t, tree, err, map[string]interface{}{ - "j": map[string]interface{}{ - "ʞ": map[string]interface{}{ - "l": map[string]interface{}{ - "d": int64(42), - }, - }, - }, - }) - - tree, err = Load("[ g . h . i ]\nd = 42") - assertTree(t, tree, err, map[string]interface{}{ - "g": map[string]interface{}{ - "h": map[string]interface{}{ - "i": map[string]interface{}{ - "d": int64(42), - }, - }, - }, - }) - - tree, err = Load("[ d.e.f ]\nk = 42") - assertTree(t, tree, err, map[string]interface{}{ - "d": map[string]interface{}{ - "e": map[string]interface{}{ - "f": map[string]interface{}{ - "k": int64(42), - }, - }, - }, - }) -} - -func TestArrayOne(t *testing.T) { - tree, err := Load("a = [1]") - assertTree(t, tree, err, map[string]interface{}{ - "a": []int64{int64(1)}, - }) -} - -func TestArrayZero(t *testing.T) { - tree, err := Load("a = []") - assertTree(t, tree, err, map[string]interface{}{ - "a": []interface{}{}, - }) -} - -func TestArraySimple(t *testing.T) { - tree, err := Load("a = [42, 21, 10]") - assertTree(t, tree, err, map[string]interface{}{ - "a": []int64{int64(42), int64(21), int64(10)}, - }) - - tree, _ = Load("a = [42, 21, 10,]") - assertTree(t, tree, err, map[string]interface{}{ - "a": []int64{int64(42), int64(21), int64(10)}, - }) -} - -func TestArrayMultiline(t *testing.T) { - tree, err := Load("a = [42,\n21, 10,]") - assertTree(t, tree, err, map[string]interface{}{ - "a": []int64{int64(42), int64(21), int64(10)}, - }) -} - -func TestArrayNested(t *testing.T) { - tree, err := Load("a = [[42, 21], [10]]") - assertTree(t, tree, err, map[string]interface{}{ - "a": [][]int64{{int64(42), int64(21)}, {int64(10)}}, - }) -} - -func TestNestedArrayComment(t *testing.T) { - tree, err := Load(` -someArray = [ -# does not work -["entry1"] -]`) - assertTree(t, tree, err, map[string]interface{}{ - "someArray": [][]string{{"entry1"}}, - }) -} - -func TestNestedEmptyArrays(t *testing.T) { - tree, err := Load("a = [[[]]]") - assertTree(t, tree, err, map[string]interface{}{ - "a": [][][]interface{}{{{}}}, - }) -} - -func TestArrayMixedTypes(t *testing.T) { - _, err := Load("a = [42, 16.0]") - if err.Error() != "(1, 10): mixed types in array" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("a = [42, \"hello\"]") - if err.Error() != "(1, 11): mixed types in array" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestArrayNestedStrings(t *testing.T) { - tree, err := Load("data = [ [\"gamma\", \"delta\"], [\"Foo\"] ]") - assertTree(t, tree, err, map[string]interface{}{ - "data": [][]string{{"gamma", "delta"}, {"Foo"}}, - }) -} - -func TestParseUnknownRvalue(t *testing.T) { - _, err := Load("a = !bssss") - if err == nil { - t.Error("Expecting a parse error") - } - - _, err = Load("a = /b") - if err == nil { - t.Error("Expecting a parse error") - } -} - -func TestMissingValue(t *testing.T) { - _, err := Load("a = ") - if err.Error() != "(1, 5): expecting a value" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestUnterminatedArray(t *testing.T) { - _, err := Load("a = [1,") - if err.Error() != "(1, 8): unterminated array" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("a = [1") - if err.Error() != "(1, 7): unterminated array" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("a = [1 2") - if err.Error() != "(1, 8): missing comma" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestNewlinesInArrays(t *testing.T) { - tree, err := Load("a = [1,\n2,\n3]") - assertTree(t, tree, err, map[string]interface{}{ - "a": []int64{int64(1), int64(2), int64(3)}, - }) -} - -func TestArrayWithExtraComma(t *testing.T) { - tree, err := Load("a = [1,\n2,\n3,\n]") - assertTree(t, tree, err, map[string]interface{}{ - "a": []int64{int64(1), int64(2), int64(3)}, - }) -} - -func TestArrayWithExtraCommaComment(t *testing.T) { - tree, err := Load("a = [1, # wow\n2, # such items\n3, # so array\n]") - assertTree(t, tree, err, map[string]interface{}{ - "a": []int64{int64(1), int64(2), int64(3)}, - }) -} - -func TestSimpleInlineGroup(t *testing.T) { - tree, err := Load("key = {a = 42}") - assertTree(t, tree, err, map[string]interface{}{ - "key": map[string]interface{}{ - "a": int64(42), - }, - }) -} - -func TestDoubleInlineGroup(t *testing.T) { - tree, err := Load("key = {a = 42, b = \"foo\"}") - assertTree(t, tree, err, map[string]interface{}{ - "key": map[string]interface{}{ - "a": int64(42), - "b": "foo", - }, - }) -} - -func TestExampleInlineGroup(t *testing.T) { - tree, err := Load(`name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 }`) - assertTree(t, tree, err, map[string]interface{}{ - "name": map[string]interface{}{ - "first": "Tom", - "last": "Preston-Werner", - }, - "point": map[string]interface{}{ - "x": int64(1), - "y": int64(2), - }, - }) -} - -func TestExampleInlineGroupInArray(t *testing.T) { - tree, err := Load(`points = [{ x = 1, y = 2 }]`) - assertTree(t, tree, err, map[string]interface{}{ - "points": []map[string]interface{}{ - { - "x": int64(1), - "y": int64(2), - }, - }, - }) -} - -func TestInlineTableUnterminated(t *testing.T) { - _, err := Load("foo = {") - if err.Error() != "(1, 8): unterminated inline table" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestInlineTableCommaExpected(t *testing.T) { - _, err := Load("foo = {hello = 53 test = foo}") - if err.Error() != "(1, 19): comma expected between fields in inline table" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestInlineTableCommaStart(t *testing.T) { - _, err := Load("foo = {, hello = 53}") - if err.Error() != "(1, 8): inline table cannot start with a comma" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestInlineTableDoubleComma(t *testing.T) { - _, err := Load("foo = {hello = 53,, foo = 17}") - if err.Error() != "(1, 19): need field between two commas in inline table" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestDuplicateGroups(t *testing.T) { - _, err := Load("[foo]\na=2\n[foo]b=3") - if err.Error() != "(3, 2): duplicated tables" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestDuplicateKeys(t *testing.T) { - _, err := Load("foo = 2\nfoo = 3") - if err.Error() != "(2, 1): The following key was defined twice: foo" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestEmptyIntermediateTable(t *testing.T) { - _, err := Load("[foo..bar]") - if err.Error() != "(1, 2): invalid table array key: empty table key" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestImplicitDeclarationBefore(t *testing.T) { - tree, err := Load("[a.b.c]\nanswer = 42\n[a]\nbetter = 43") - assertTree(t, tree, err, map[string]interface{}{ - "a": map[string]interface{}{ - "b": map[string]interface{}{ - "c": map[string]interface{}{ - "answer": int64(42), - }, - }, - "better": int64(43), - }, - }) -} - -func TestFloatsWithoutLeadingZeros(t *testing.T) { - _, err := Load("a = .42") - if err.Error() != "(1, 5): cannot start float with a dot" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("a = -.42") - if err.Error() != "(1, 5): cannot start float with a dot" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestMissingFile(t *testing.T) { - _, err := LoadFile("foo.toml") - if err.Error() != "open foo.toml: no such file or directory" && - err.Error() != "open foo.toml: The system cannot find the file specified." { - t.Error("Bad error message:", err.Error()) - } -} - -func TestParseFile(t *testing.T) { - tree, err := LoadFile("example.toml") - - assertTree(t, tree, err, map[string]interface{}{ - "title": "TOML Example", - "owner": map[string]interface{}{ - "name": "Tom Preston-Werner", - "organization": "GitHub", - "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", - "dob": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), - }, - "database": map[string]interface{}{ - "server": "192.168.1.1", - "ports": []int64{8001, 8001, 8002}, - "connection_max": 5000, - "enabled": true, - }, - "servers": map[string]interface{}{ - "alpha": map[string]interface{}{ - "ip": "10.0.0.1", - "dc": "eqdc10", - }, - "beta": map[string]interface{}{ - "ip": "10.0.0.2", - "dc": "eqdc10", - }, - }, - "clients": map[string]interface{}{ - "data": []interface{}{ - []string{"gamma", "delta"}, - []int64{1, 2}, - }, - }, - }) -} - -func TestParseFileCRLF(t *testing.T) { - tree, err := LoadFile("example-crlf.toml") - - assertTree(t, tree, err, map[string]interface{}{ - "title": "TOML Example", - "owner": map[string]interface{}{ - "name": "Tom Preston-Werner", - "organization": "GitHub", - "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", - "dob": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), - }, - "database": map[string]interface{}{ - "server": "192.168.1.1", - "ports": []int64{8001, 8001, 8002}, - "connection_max": 5000, - "enabled": true, - }, - "servers": map[string]interface{}{ - "alpha": map[string]interface{}{ - "ip": "10.0.0.1", - "dc": "eqdc10", - }, - "beta": map[string]interface{}{ - "ip": "10.0.0.2", - "dc": "eqdc10", - }, - }, - "clients": map[string]interface{}{ - "data": []interface{}{ - []string{"gamma", "delta"}, - []int64{1, 2}, - }, - }, - }) -} - -func TestParseKeyGroupArray(t *testing.T) { - tree, err := Load("[[foo.bar]] a = 42\n[[foo.bar]] a = 69") - assertTree(t, tree, err, map[string]interface{}{ - "foo": map[string]interface{}{ - "bar": []map[string]interface{}{ - {"a": int64(42)}, - {"a": int64(69)}, - }, - }, - }) -} - -func TestParseKeyGroupArrayUnfinished(t *testing.T) { - _, err := Load("[[foo.bar]\na = 42") - if err.Error() != "(1, 10): was expecting token [[, but got unclosed table array key instead" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("[[foo.[bar]\na = 42") - if err.Error() != "(1, 3): unexpected token table array key cannot contain ']', was expecting a table array key" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestParseKeyGroupArrayQueryExample(t *testing.T) { - tree, err := Load(` - [[book]] - title = "The Stand" - author = "Stephen King" - [[book]] - title = "For Whom the Bell Tolls" - author = "Ernest Hemmingway" - [[book]] - title = "Neuromancer" - author = "William Gibson" - `) - - assertTree(t, tree, err, map[string]interface{}{ - "book": []map[string]interface{}{ - {"title": "The Stand", "author": "Stephen King"}, - {"title": "For Whom the Bell Tolls", "author": "Ernest Hemmingway"}, - {"title": "Neuromancer", "author": "William Gibson"}, - }, - }) -} - -func TestParseKeyGroupArraySpec(t *testing.T) { - tree, err := Load("[[fruit]]\n name=\"apple\"\n [fruit.physical]\n color=\"red\"\n shape=\"round\"\n [[fruit]]\n name=\"banana\"") - assertTree(t, tree, err, map[string]interface{}{ - "fruit": []map[string]interface{}{ - {"name": "apple", "physical": map[string]interface{}{"color": "red", "shape": "round"}}, - {"name": "banana"}, - }, - }) -} - -func TestTomlValueStringRepresentation(t *testing.T) { - for idx, item := range []struct { - Value interface{} - Expect string - }{ - {int64(12345), "12345"}, - {uint64(50), "50"}, - {float64(123.45), "123.45"}, - {true, "true"}, - {"hello world", "\"hello world\""}, - {"\b\t\n\f\r\"\\", "\"\\b\\t\\n\\f\\r\\\"\\\\\""}, - {"\x05", "\"\\u0005\""}, - {time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), - "1979-05-27T07:32:00Z"}, - {[]interface{}{"gamma", "delta"}, - "[\"gamma\",\"delta\"]"}, - {nil, ""}, - } { - result, err := tomlValueStringRepresentation(item.Value, "", false) - if err != nil { - t.Errorf("Test %d - unexpected error: %s", idx, err) - } - if result != item.Expect { - t.Errorf("Test %d - got '%s', expected '%s'", idx, result, item.Expect) - } - } -} - -func TestToStringMapStringString(t *testing.T) { - tree, err := TreeFromMap(map[string]interface{}{"m": map[string]interface{}{"v": "abc"}}) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - want := "\n[m]\n v = \"abc\"\n" - got := tree.String() - - if got != want { - t.Errorf("want:\n%q\ngot:\n%q", want, got) - } -} - -func assertPosition(t *testing.T, text string, ref map[string]Position) { - tree, err := Load(text) - if err != nil { - t.Errorf("Error loading document text: `%v`", text) - t.Errorf("Error: %v", err) - } - for path, pos := range ref { - testPos := tree.GetPosition(path) - if testPos.Invalid() { - t.Errorf("Failed to query tree path or path has invalid position: %s", path) - } else if pos != testPos { - t.Errorf("Expected position %v, got %v instead", pos, testPos) - } - } -} - -func TestDocumentPositions(t *testing.T) { - assertPosition(t, - "[foo]\nbar=42\nbaz=69", - map[string]Position{ - "": {1, 1}, - "foo": {1, 1}, - "foo.bar": {2, 1}, - "foo.baz": {3, 1}, - }) -} - -func TestDocumentPositionsWithSpaces(t *testing.T) { - assertPosition(t, - " [foo]\n bar=42\n baz=69", - map[string]Position{ - "": {1, 1}, - "foo": {1, 3}, - "foo.bar": {2, 3}, - "foo.baz": {3, 3}, - }) -} - -func TestDocumentPositionsWithGroupArray(t *testing.T) { - assertPosition(t, - "[[foo]]\nbar=42\nbaz=69", - map[string]Position{ - "": {1, 1}, - "foo": {1, 1}, - "foo.bar": {2, 1}, - "foo.baz": {3, 1}, - }) -} - -func TestNestedTreePosition(t *testing.T) { - assertPosition(t, - "[foo.bar]\na=42\nb=69", - map[string]Position{ - "": {1, 1}, - "foo": {1, 1}, - "foo.bar": {1, 1}, - "foo.bar.a": {2, 1}, - "foo.bar.b": {3, 1}, - }) -} - -func TestInvalidGroupArray(t *testing.T) { - _, err := Load("[table#key]\nanswer = 42") - if err == nil { - t.Error("Should error") - } - - _, err = Load("[foo.[bar]\na = 42") - if err.Error() != "(1, 2): unexpected token table key cannot contain ']', was expecting a table key" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestDoubleEqual(t *testing.T) { - _, err := Load("foo= = 2") - if err.Error() != "(1, 6): cannot have multiple equals for the same key" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestGroupArrayReassign(t *testing.T) { - _, err := Load("[hello]\n[[hello]]") - if err.Error() != "(2, 3): key \"hello\" is already assigned and not of type table array" { - t.Error("Bad error message:", err.Error()) - } -} - -func TestInvalidFloatParsing(t *testing.T) { - _, err := Load("a=1e_2") - if err.Error() != "(1, 3): invalid use of _ in number" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("a=1e2_") - if err.Error() != "(1, 3): invalid use of _ in number" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("a=1__2") - if err.Error() != "(1, 3): invalid use of _ in number" { - t.Error("Bad error message:", err.Error()) - } - - _, err = Load("a=_1_2") - if err.Error() != "(1, 3): cannot start number with underscore" { - t.Error("Bad error message:", err.Error()) - } -} diff --git a/vendor/github.com/pelletier/go-toml/position.go b/vendor/github.com/pelletier/go-toml/position.go deleted file mode 100644 index c17bff87b..000000000 --- a/vendor/github.com/pelletier/go-toml/position.go +++ /dev/null @@ -1,29 +0,0 @@ -// Position support for go-toml - -package toml - -import ( - "fmt" -) - -// Position of a document element within a TOML document. -// -// Line and Col are both 1-indexed positions for the element's line number and -// column number, respectively. Values of zero or less will cause Invalid(), -// to return true. -type Position struct { - Line int // line within the document - Col int // column within the line -} - -// String representation of the position. -// Displays 1-indexed line and column numbers. -func (p Position) String() string { - return fmt.Sprintf("(%d, %d)", p.Line, p.Col) -} - -// Invalid returns whether or not the position is valid (i.e. with negative or -// null values) -func (p Position) Invalid() bool { - return p.Line <= 0 || p.Col <= 0 -} diff --git a/vendor/github.com/pelletier/go-toml/position_test.go b/vendor/github.com/pelletier/go-toml/position_test.go deleted file mode 100644 index 63ad1afc8..000000000 --- a/vendor/github.com/pelletier/go-toml/position_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// Testing support for go-toml - -package toml - -import ( - "testing" -) - -func TestPositionString(t *testing.T) { - p := Position{123, 456} - expected := "(123, 456)" - value := p.String() - - if value != expected { - t.Errorf("Expected %v, got %v instead", expected, value) - } -} - -func TestInvalid(t *testing.T) { - for i, v := range []Position{ - {0, 1234}, - {1234, 0}, - {0, 0}, - } { - if !v.Invalid() { - t.Errorf("Position at %v is valid: %v", i, v) - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/query/doc.go b/vendor/github.com/pelletier/go-toml/query/doc.go deleted file mode 100644 index ed63c1109..000000000 --- a/vendor/github.com/pelletier/go-toml/query/doc.go +++ /dev/null @@ -1,175 +0,0 @@ -// Package query performs JSONPath-like queries on a TOML document. -// -// The query path implementation is based loosely on the JSONPath specification: -// http://goessner.net/articles/JsonPath/. -// -// The idea behind a query path is to allow quick access to any element, or set -// of elements within TOML document, with a single expression. -// -// result, err := query.CompileAndExecute("$.foo.bar.baz", tree) -// -// This is roughly equivalent to: -// -// next := tree.Get("foo") -// if next != nil { -// next = next.Get("bar") -// if next != nil { -// next = next.Get("baz") -// } -// } -// result := next -// -// err is nil if any parsing exception occurs. -// -// If no node in the tree matches the query, result will simply contain an empty list of -// items. -// -// As illustrated above, the query path is much more efficient, especially since -// the structure of the TOML file can vary. Rather than making assumptions about -// a document's structure, a query allows the programmer to make structured -// requests into the document, and get zero or more values as a result. -// -// Query syntax -// -// The syntax of a query begins with a root token, followed by any number -// sub-expressions: -// -// $ -// Root of the TOML tree. This must always come first. -// .name -// Selects child of this node, where 'name' is a TOML key -// name. -// ['name'] -// Selects child of this node, where 'name' is a string -// containing a TOML key name. -// [index] -// Selcts child array element at 'index'. -// ..expr -// Recursively selects all children, filtered by an a union, -// index, or slice expression. -// ..* -// Recursive selection of all nodes at this point in the -// tree. -// .* -// Selects all children of the current node. -// [expr,expr] -// Union operator - a logical 'or' grouping of two or more -// sub-expressions: index, key name, or filter. -// [start:end:step] -// Slice operator - selects array elements from start to -// end-1, at the given step. All three arguments are -// optional. -// [?(filter)] -// Named filter expression - the function 'filter' is -// used to filter children at this node. -// -// Query Indexes And Slices -// -// Index expressions perform no bounds checking, and will contribute no -// values to the result set if the provided index or index range is invalid. -// Negative indexes represent values from the end of the array, counting backwards. -// -// // select the last index of the array named 'foo' -// query.CompileAndExecute("$.foo[-1]", tree) -// -// Slice expressions are supported, by using ':' to separate a start/end index pair. -// -// // select up to the first five elements in the array -// query.CompileAndExecute("$.foo[0:5]", tree) -// -// Slice expressions also allow negative indexes for the start and stop -// arguments. -// -// // select all array elements. -// query.CompileAndExecute("$.foo[0:-1]", tree) -// -// Slice expressions may have an optional stride/step parameter: -// -// // select every other element -// query.CompileAndExecute("$.foo[0:-1:2]", tree) -// -// Slice start and end parameters are also optional: -// -// // these are all equivalent and select all the values in the array -// query.CompileAndExecute("$.foo[:]", tree) -// query.CompileAndExecute("$.foo[0:]", tree) -// query.CompileAndExecute("$.foo[:-1]", tree) -// query.CompileAndExecute("$.foo[0:-1:]", tree) -// query.CompileAndExecute("$.foo[::1]", tree) -// query.CompileAndExecute("$.foo[0::1]", tree) -// query.CompileAndExecute("$.foo[:-1:1]", tree) -// query.CompileAndExecute("$.foo[0:-1:1]", tree) -// -// Query Filters -// -// Query filters are used within a Union [,] or single Filter [] expression. -// A filter only allows nodes that qualify through to the next expression, -// and/or into the result set. -// -// // returns children of foo that are permitted by the 'bar' filter. -// query.CompileAndExecute("$.foo[?(bar)]", tree) -// -// There are several filters provided with the library: -// -// tree -// Allows nodes of type Tree. -// int -// Allows nodes of type int64. -// float -// Allows nodes of type float64. -// string -// Allows nodes of type string. -// time -// Allows nodes of type time.Time. -// bool -// Allows nodes of type bool. -// -// Query Results -// -// An executed query returns a Result object. This contains the nodes -// in the TOML tree that qualify the query expression. Position information -// is also available for each value in the set. -// -// // display the results of a query -// results := query.CompileAndExecute("$.foo.bar.baz", tree) -// for idx, value := results.Values() { -// fmt.Println("%v: %v", results.Positions()[idx], value) -// } -// -// Compiled Queries -// -// Queries may be executed directly on a Tree object, or compiled ahead -// of time and executed discretely. The former is more convenient, but has the -// penalty of having to recompile the query expression each time. -// -// // basic query -// results := query.CompileAndExecute("$.foo.bar.baz", tree) -// -// // compiled query -// query, err := toml.Compile("$.foo.bar.baz") -// results := query.Execute(tree) -// -// // run the compiled query again on a different tree -// moreResults := query.Execute(anotherTree) -// -// User Defined Query Filters -// -// Filter expressions may also be user defined by using the SetFilter() -// function on the Query object. The function must return true/false, which -// signifies if the passed node is kept or discarded, respectively. -// -// // create a query that references a user-defined filter -// query, _ := query.Compile("$[?(bazOnly)]") -// -// // define the filter, and assign it to the query -// query.SetFilter("bazOnly", func(node interface{}) bool{ -// if tree, ok := node.(*Tree); ok { -// return tree.Has("baz") -// } -// return false // reject all other node types -// }) -// -// // run the query -// query.Execute(tree) -// -package query diff --git a/vendor/github.com/pelletier/go-toml/query/lexer.go b/vendor/github.com/pelletier/go-toml/query/lexer.go deleted file mode 100644 index 2dc319408..000000000 --- a/vendor/github.com/pelletier/go-toml/query/lexer.go +++ /dev/null @@ -1,357 +0,0 @@ -// TOML JSONPath lexer. -// -// Written using the principles developed by Rob Pike in -// http://www.youtube.com/watch?v=HxaD_trXwRE - -package query - -import ( - "fmt" - "github.com/pelletier/go-toml" - "strconv" - "strings" - "unicode/utf8" -) - -// Lexer state function -type queryLexStateFn func() queryLexStateFn - -// Lexer definition -type queryLexer struct { - input string - start int - pos int - width int - tokens chan token - depth int - line int - col int - stringTerm string -} - -func (l *queryLexer) run() { - for state := l.lexVoid; state != nil; { - state = state() - } - close(l.tokens) -} - -func (l *queryLexer) nextStart() { - // iterate by runes (utf8 characters) - // search for newlines and advance line/col counts - for i := l.start; i < l.pos; { - r, width := utf8.DecodeRuneInString(l.input[i:]) - if r == '\n' { - l.line++ - l.col = 1 - } else { - l.col++ - } - i += width - } - // advance start position to next token - l.start = l.pos -} - -func (l *queryLexer) emit(t tokenType) { - l.tokens <- token{ - Position: toml.Position{Line: l.line, Col: l.col}, - typ: t, - val: l.input[l.start:l.pos], - } - l.nextStart() -} - -func (l *queryLexer) emitWithValue(t tokenType, value string) { - l.tokens <- token{ - Position: toml.Position{Line: l.line, Col: l.col}, - typ: t, - val: value, - } - l.nextStart() -} - -func (l *queryLexer) next() rune { - if l.pos >= len(l.input) { - l.width = 0 - return eof - } - var r rune - r, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) - l.pos += l.width - return r -} - -func (l *queryLexer) ignore() { - l.nextStart() -} - -func (l *queryLexer) backup() { - l.pos -= l.width -} - -func (l *queryLexer) errorf(format string, args ...interface{}) queryLexStateFn { - l.tokens <- token{ - Position: toml.Position{Line: l.line, Col: l.col}, - typ: tokenError, - val: fmt.Sprintf(format, args...), - } - return nil -} - -func (l *queryLexer) peek() rune { - r := l.next() - l.backup() - return r -} - -func (l *queryLexer) accept(valid string) bool { - if strings.ContainsRune(valid, l.next()) { - return true - } - l.backup() - return false -} - -func (l *queryLexer) follow(next string) bool { - return strings.HasPrefix(l.input[l.pos:], next) -} - -func (l *queryLexer) lexVoid() queryLexStateFn { - for { - next := l.peek() - switch next { - case '$': - l.pos++ - l.emit(tokenDollar) - continue - case '.': - if l.follow("..") { - l.pos += 2 - l.emit(tokenDotDot) - } else { - l.pos++ - l.emit(tokenDot) - } - continue - case '[': - l.pos++ - l.emit(tokenLeftBracket) - continue - case ']': - l.pos++ - l.emit(tokenRightBracket) - continue - case ',': - l.pos++ - l.emit(tokenComma) - continue - case '*': - l.pos++ - l.emit(tokenStar) - continue - case '(': - l.pos++ - l.emit(tokenLeftParen) - continue - case ')': - l.pos++ - l.emit(tokenRightParen) - continue - case '?': - l.pos++ - l.emit(tokenQuestion) - continue - case ':': - l.pos++ - l.emit(tokenColon) - continue - case '\'': - l.ignore() - l.stringTerm = string(next) - return l.lexString - case '"': - l.ignore() - l.stringTerm = string(next) - return l.lexString - } - - if isSpace(next) { - l.next() - l.ignore() - continue - } - - if isAlphanumeric(next) { - return l.lexKey - } - - if next == '+' || next == '-' || isDigit(next) { - return l.lexNumber - } - - if l.next() == eof { - break - } - - return l.errorf("unexpected char: '%v'", next) - } - l.emit(tokenEOF) - return nil -} - -func (l *queryLexer) lexKey() queryLexStateFn { - for { - next := l.peek() - if !isAlphanumeric(next) { - l.emit(tokenKey) - return l.lexVoid - } - - if l.next() == eof { - break - } - } - l.emit(tokenEOF) - return nil -} - -func (l *queryLexer) lexString() queryLexStateFn { - l.pos++ - l.ignore() - growingString := "" - - for { - if l.follow(l.stringTerm) { - l.emitWithValue(tokenString, growingString) - l.pos++ - l.ignore() - return l.lexVoid - } - - if l.follow("\\\"") { - l.pos++ - growingString += "\"" - } else if l.follow("\\'") { - l.pos++ - growingString += "'" - } else if l.follow("\\n") { - l.pos++ - growingString += "\n" - } else if l.follow("\\b") { - l.pos++ - growingString += "\b" - } else if l.follow("\\f") { - l.pos++ - growingString += "\f" - } else if l.follow("\\/") { - l.pos++ - growingString += "/" - } else if l.follow("\\t") { - l.pos++ - growingString += "\t" - } else if l.follow("\\r") { - l.pos++ - growingString += "\r" - } else if l.follow("\\\\") { - l.pos++ - growingString += "\\" - } else if l.follow("\\u") { - l.pos += 2 - code := "" - for i := 0; i < 4; i++ { - c := l.peek() - l.pos++ - if !isHexDigit(c) { - return l.errorf("unfinished unicode escape") - } - code = code + string(c) - } - l.pos-- - intcode, err := strconv.ParseInt(code, 16, 32) - if err != nil { - return l.errorf("invalid unicode escape: \\u" + code) - } - growingString += string(rune(intcode)) - } else if l.follow("\\U") { - l.pos += 2 - code := "" - for i := 0; i < 8; i++ { - c := l.peek() - l.pos++ - if !isHexDigit(c) { - return l.errorf("unfinished unicode escape") - } - code = code + string(c) - } - l.pos-- - intcode, err := strconv.ParseInt(code, 16, 32) - if err != nil { - return l.errorf("invalid unicode escape: \\u" + code) - } - growingString += string(rune(intcode)) - } else if l.follow("\\") { - l.pos++ - return l.errorf("invalid escape sequence: \\" + string(l.peek())) - } else { - growingString += string(l.peek()) - } - - if l.next() == eof { - break - } - } - - return l.errorf("unclosed string") -} - -func (l *queryLexer) lexNumber() queryLexStateFn { - l.ignore() - if !l.accept("+") { - l.accept("-") - } - pointSeen := false - digitSeen := false - for { - next := l.next() - if next == '.' { - if pointSeen { - return l.errorf("cannot have two dots in one float") - } - if !isDigit(l.peek()) { - return l.errorf("float cannot end with a dot") - } - pointSeen = true - } else if isDigit(next) { - digitSeen = true - } else { - l.backup() - break - } - if pointSeen && !digitSeen { - return l.errorf("cannot start float with a dot") - } - } - - if !digitSeen { - return l.errorf("no digit in that number") - } - if pointSeen { - l.emit(tokenFloat) - } else { - l.emit(tokenInteger) - } - return l.lexVoid -} - -// Entry point -func lexQuery(input string) chan token { - l := &queryLexer{ - input: input, - tokens: make(chan token), - line: 1, - col: 1, - } - go l.run() - return l.tokens -} diff --git a/vendor/github.com/pelletier/go-toml/query/lexer_test.go b/vendor/github.com/pelletier/go-toml/query/lexer_test.go deleted file mode 100644 index 8ce0501fe..000000000 --- a/vendor/github.com/pelletier/go-toml/query/lexer_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package query - -import ( - "github.com/pelletier/go-toml" - "testing" -) - -func testQLFlow(t *testing.T, input string, expectedFlow []token) { - ch := lexQuery(input) - for idx, expected := range expectedFlow { - token := <-ch - if token != expected { - t.Log("While testing #", idx, ":", input) - t.Log("compared (got)", token, "to (expected)", expected) - t.Log("\tvalue:", token.val, "<->", expected.val) - t.Log("\tvalue as bytes:", []byte(token.val), "<->", []byte(expected.val)) - t.Log("\ttype:", token.typ.String(), "<->", expected.typ.String()) - t.Log("\tline:", token.Line, "<->", expected.Line) - t.Log("\tcolumn:", token.Col, "<->", expected.Col) - t.Log("compared", token, "to", expected) - t.FailNow() - } - } - - tok, ok := <-ch - if ok { - t.Log("channel is not closed!") - t.Log(len(ch)+1, "tokens remaining:") - - t.Log("token ->", tok) - for token := range ch { - t.Log("token ->", token) - } - t.FailNow() - } -} - -func TestLexSpecialChars(t *testing.T) { - testQLFlow(t, " .$[]..()?*", []token{ - {toml.Position{1, 2}, tokenDot, "."}, - {toml.Position{1, 3}, tokenDollar, "$"}, - {toml.Position{1, 4}, tokenLeftBracket, "["}, - {toml.Position{1, 5}, tokenRightBracket, "]"}, - {toml.Position{1, 6}, tokenDotDot, ".."}, - {toml.Position{1, 8}, tokenLeftParen, "("}, - {toml.Position{1, 9}, tokenRightParen, ")"}, - {toml.Position{1, 10}, tokenQuestion, "?"}, - {toml.Position{1, 11}, tokenStar, "*"}, - {toml.Position{1, 12}, tokenEOF, ""}, - }) -} - -func TestLexString(t *testing.T) { - testQLFlow(t, "'foo\n'", []token{ - {toml.Position{1, 2}, tokenString, "foo\n"}, - {toml.Position{2, 2}, tokenEOF, ""}, - }) -} - -func TestLexDoubleString(t *testing.T) { - testQLFlow(t, `"bar"`, []token{ - {toml.Position{1, 2}, tokenString, "bar"}, - {toml.Position{1, 6}, tokenEOF, ""}, - }) -} - -func TestLexStringEscapes(t *testing.T) { - testQLFlow(t, `"foo \" \' \b \f \/ \t \r \\ \u03A9 \U00012345 \n bar"`, []token{ - {toml.Position{1, 2}, tokenString, "foo \" ' \b \f / \t \r \\ \u03A9 \U00012345 \n bar"}, - {toml.Position{1, 55}, tokenEOF, ""}, - }) -} - -func TestLexStringUnfinishedUnicode4(t *testing.T) { - testQLFlow(t, `"\u000"`, []token{ - {toml.Position{1, 2}, tokenError, "unfinished unicode escape"}, - }) -} - -func TestLexStringUnfinishedUnicode8(t *testing.T) { - testQLFlow(t, `"\U0000"`, []token{ - {toml.Position{1, 2}, tokenError, "unfinished unicode escape"}, - }) -} - -func TestLexStringInvalidEscape(t *testing.T) { - testQLFlow(t, `"\x"`, []token{ - {toml.Position{1, 2}, tokenError, "invalid escape sequence: \\x"}, - }) -} - -func TestLexStringUnfinished(t *testing.T) { - testQLFlow(t, `"bar`, []token{ - {toml.Position{1, 2}, tokenError, "unclosed string"}, - }) -} - -func TestLexKey(t *testing.T) { - testQLFlow(t, "foo", []token{ - {toml.Position{1, 1}, tokenKey, "foo"}, - {toml.Position{1, 4}, tokenEOF, ""}, - }) -} - -func TestLexRecurse(t *testing.T) { - testQLFlow(t, "$..*", []token{ - {toml.Position{1, 1}, tokenDollar, "$"}, - {toml.Position{1, 2}, tokenDotDot, ".."}, - {toml.Position{1, 4}, tokenStar, "*"}, - {toml.Position{1, 5}, tokenEOF, ""}, - }) -} - -func TestLexBracketKey(t *testing.T) { - testQLFlow(t, "$[foo]", []token{ - {toml.Position{1, 1}, tokenDollar, "$"}, - {toml.Position{1, 2}, tokenLeftBracket, "["}, - {toml.Position{1, 3}, tokenKey, "foo"}, - {toml.Position{1, 6}, tokenRightBracket, "]"}, - {toml.Position{1, 7}, tokenEOF, ""}, - }) -} - -func TestLexSpace(t *testing.T) { - testQLFlow(t, "foo bar baz", []token{ - {toml.Position{1, 1}, tokenKey, "foo"}, - {toml.Position{1, 5}, tokenKey, "bar"}, - {toml.Position{1, 9}, tokenKey, "baz"}, - {toml.Position{1, 12}, tokenEOF, ""}, - }) -} - -func TestLexInteger(t *testing.T) { - testQLFlow(t, "100 +200 -300", []token{ - {toml.Position{1, 1}, tokenInteger, "100"}, - {toml.Position{1, 5}, tokenInteger, "+200"}, - {toml.Position{1, 10}, tokenInteger, "-300"}, - {toml.Position{1, 14}, tokenEOF, ""}, - }) -} - -func TestLexFloat(t *testing.T) { - testQLFlow(t, "100.0 +200.0 -300.0", []token{ - {toml.Position{1, 1}, tokenFloat, "100.0"}, - {toml.Position{1, 7}, tokenFloat, "+200.0"}, - {toml.Position{1, 14}, tokenFloat, "-300.0"}, - {toml.Position{1, 20}, tokenEOF, ""}, - }) -} - -func TestLexFloatWithMultipleDots(t *testing.T) { - testQLFlow(t, "4.2.", []token{ - {toml.Position{1, 1}, tokenError, "cannot have two dots in one float"}, - }) -} - -func TestLexFloatLeadingDot(t *testing.T) { - testQLFlow(t, "+.1", []token{ - {toml.Position{1, 1}, tokenError, "cannot start float with a dot"}, - }) -} - -func TestLexFloatWithTrailingDot(t *testing.T) { - testQLFlow(t, "42.", []token{ - {toml.Position{1, 1}, tokenError, "float cannot end with a dot"}, - }) -} - -func TestLexNumberWithoutDigit(t *testing.T) { - testQLFlow(t, "+", []token{ - {toml.Position{1, 1}, tokenError, "no digit in that number"}, - }) -} - -func TestLexUnknown(t *testing.T) { - testQLFlow(t, "^", []token{ - {toml.Position{1, 1}, tokenError, "unexpected char: '94'"}, - }) -} diff --git a/vendor/github.com/pelletier/go-toml/query/match.go b/vendor/github.com/pelletier/go-toml/query/match.go deleted file mode 100644 index d7bb15a45..000000000 --- a/vendor/github.com/pelletier/go-toml/query/match.go +++ /dev/null @@ -1,232 +0,0 @@ -package query - -import ( - "fmt" - "github.com/pelletier/go-toml" -) - -// base match -type matchBase struct { - next pathFn -} - -func (f *matchBase) setNext(next pathFn) { - f.next = next -} - -// terminating functor - gathers results -type terminatingFn struct { - // empty -} - -func newTerminatingFn() *terminatingFn { - return &terminatingFn{} -} - -func (f *terminatingFn) setNext(next pathFn) { - // do nothing -} - -func (f *terminatingFn) call(node interface{}, ctx *queryContext) { - ctx.result.appendResult(node, ctx.lastPosition) -} - -// match single key -type matchKeyFn struct { - matchBase - Name string -} - -func newMatchKeyFn(name string) *matchKeyFn { - return &matchKeyFn{Name: name} -} - -func (f *matchKeyFn) call(node interface{}, ctx *queryContext) { - if array, ok := node.([]*toml.Tree); ok { - for _, tree := range array { - item := tree.Get(f.Name) - if item != nil { - ctx.lastPosition = tree.GetPosition(f.Name) - f.next.call(item, ctx) - } - } - } else if tree, ok := node.(*toml.Tree); ok { - item := tree.Get(f.Name) - if item != nil { - ctx.lastPosition = tree.GetPosition(f.Name) - f.next.call(item, ctx) - } - } -} - -// match single index -type matchIndexFn struct { - matchBase - Idx int -} - -func newMatchIndexFn(idx int) *matchIndexFn { - return &matchIndexFn{Idx: idx} -} - -func (f *matchIndexFn) call(node interface{}, ctx *queryContext) { - if arr, ok := node.([]interface{}); ok { - if f.Idx < len(arr) && f.Idx >= 0 { - if treesArray, ok := node.([]*toml.Tree); ok { - if len(treesArray) > 0 { - ctx.lastPosition = treesArray[0].Position() - } - } - f.next.call(arr[f.Idx], ctx) - } - } -} - -// filter by slicing -type matchSliceFn struct { - matchBase - Start, End, Step int -} - -func newMatchSliceFn(start, end, step int) *matchSliceFn { - return &matchSliceFn{Start: start, End: end, Step: step} -} - -func (f *matchSliceFn) call(node interface{}, ctx *queryContext) { - if arr, ok := node.([]interface{}); ok { - // adjust indexes for negative values, reverse ordering - realStart, realEnd := f.Start, f.End - if realStart < 0 { - realStart = len(arr) + realStart - } - if realEnd < 0 { - realEnd = len(arr) + realEnd - } - if realEnd < realStart { - realEnd, realStart = realStart, realEnd // swap - } - // loop and gather - for idx := realStart; idx < realEnd; idx += f.Step { - if treesArray, ok := node.([]*toml.Tree); ok { - if len(treesArray) > 0 { - ctx.lastPosition = treesArray[0].Position() - } - } - f.next.call(arr[idx], ctx) - } - } -} - -// match anything -type matchAnyFn struct { - matchBase -} - -func newMatchAnyFn() *matchAnyFn { - return &matchAnyFn{} -} - -func (f *matchAnyFn) call(node interface{}, ctx *queryContext) { - if tree, ok := node.(*toml.Tree); ok { - for _, k := range tree.Keys() { - v := tree.Get(k) - ctx.lastPosition = tree.GetPosition(k) - f.next.call(v, ctx) - } - } -} - -// filter through union -type matchUnionFn struct { - Union []pathFn -} - -func (f *matchUnionFn) setNext(next pathFn) { - for _, fn := range f.Union { - fn.setNext(next) - } -} - -func (f *matchUnionFn) call(node interface{}, ctx *queryContext) { - for _, fn := range f.Union { - fn.call(node, ctx) - } -} - -// match every single last node in the tree -type matchRecursiveFn struct { - matchBase -} - -func newMatchRecursiveFn() *matchRecursiveFn { - return &matchRecursiveFn{} -} - -func (f *matchRecursiveFn) call(node interface{}, ctx *queryContext) { - originalPosition := ctx.lastPosition - if tree, ok := node.(*toml.Tree); ok { - var visit func(tree *toml.Tree) - visit = func(tree *toml.Tree) { - for _, k := range tree.Keys() { - v := tree.Get(k) - ctx.lastPosition = tree.GetPosition(k) - f.next.call(v, ctx) - switch node := v.(type) { - case *toml.Tree: - visit(node) - case []*toml.Tree: - for _, subtree := range node { - visit(subtree) - } - } - } - } - ctx.lastPosition = originalPosition - f.next.call(tree, ctx) - visit(tree) - } -} - -// match based on an externally provided functional filter -type matchFilterFn struct { - matchBase - Pos toml.Position - Name string -} - -func newMatchFilterFn(name string, pos toml.Position) *matchFilterFn { - return &matchFilterFn{Name: name, Pos: pos} -} - -func (f *matchFilterFn) call(node interface{}, ctx *queryContext) { - fn, ok := (*ctx.filters)[f.Name] - if !ok { - panic(fmt.Sprintf("%s: query context does not have filter '%s'", - f.Pos.String(), f.Name)) - } - switch castNode := node.(type) { - case *toml.Tree: - for _, k := range castNode.Keys() { - v := castNode.Get(k) - if fn(v) { - ctx.lastPosition = castNode.GetPosition(k) - f.next.call(v, ctx) - } - } - case []*toml.Tree: - for _, v := range castNode { - if fn(v) { - if len(castNode) > 0 { - ctx.lastPosition = castNode[0].Position() - } - f.next.call(v, ctx) - } - } - case []interface{}: - for _, v := range castNode { - if fn(v) { - f.next.call(v, ctx) - } - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/query/match_test.go b/vendor/github.com/pelletier/go-toml/query/match_test.go deleted file mode 100644 index 429b8f6b9..000000000 --- a/vendor/github.com/pelletier/go-toml/query/match_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package query - -import ( - "fmt" - "github.com/pelletier/go-toml" - "testing" -) - -// dump path tree to a string -func pathString(root pathFn) string { - result := fmt.Sprintf("%T:", root) - switch fn := root.(type) { - case *terminatingFn: - result += "{}" - case *matchKeyFn: - result += fmt.Sprintf("{%s}", fn.Name) - result += pathString(fn.next) - case *matchIndexFn: - result += fmt.Sprintf("{%d}", fn.Idx) - result += pathString(fn.next) - case *matchSliceFn: - result += fmt.Sprintf("{%d:%d:%d}", - fn.Start, fn.End, fn.Step) - result += pathString(fn.next) - case *matchAnyFn: - result += "{}" - result += pathString(fn.next) - case *matchUnionFn: - result += "{[" - for _, v := range fn.Union { - result += pathString(v) + ", " - } - result += "]}" - case *matchRecursiveFn: - result += "{}" - result += pathString(fn.next) - case *matchFilterFn: - result += fmt.Sprintf("{%s}", fn.Name) - result += pathString(fn.next) - } - return result -} - -func assertPathMatch(t *testing.T, path, ref *Query) bool { - pathStr := pathString(path.root) - refStr := pathString(ref.root) - if pathStr != refStr { - t.Errorf("paths do not match") - t.Log("test:", pathStr) - t.Log("ref: ", refStr) - return false - } - return true -} - -func assertPath(t *testing.T, query string, ref *Query) { - path, _ := parseQuery(lexQuery(query)) - assertPathMatch(t, path, ref) -} - -func buildPath(parts ...pathFn) *Query { - query := newQuery() - for _, v := range parts { - query.appendPath(v) - } - return query -} - -func TestPathRoot(t *testing.T) { - assertPath(t, - "$", - buildPath( - // empty - )) -} - -func TestPathKey(t *testing.T) { - assertPath(t, - "$.foo", - buildPath( - newMatchKeyFn("foo"), - )) -} - -func TestPathBracketKey(t *testing.T) { - assertPath(t, - "$[foo]", - buildPath( - newMatchKeyFn("foo"), - )) -} - -func TestPathBracketStringKey(t *testing.T) { - assertPath(t, - "$['foo']", - buildPath( - newMatchKeyFn("foo"), - )) -} - -func TestPathIndex(t *testing.T) { - assertPath(t, - "$[123]", - buildPath( - newMatchIndexFn(123), - )) -} - -func TestPathSliceStart(t *testing.T) { - assertPath(t, - "$[123:]", - buildPath( - newMatchSliceFn(123, maxInt, 1), - )) -} - -func TestPathSliceStartEnd(t *testing.T) { - assertPath(t, - "$[123:456]", - buildPath( - newMatchSliceFn(123, 456, 1), - )) -} - -func TestPathSliceStartEndColon(t *testing.T) { - assertPath(t, - "$[123:456:]", - buildPath( - newMatchSliceFn(123, 456, 1), - )) -} - -func TestPathSliceStartStep(t *testing.T) { - assertPath(t, - "$[123::7]", - buildPath( - newMatchSliceFn(123, maxInt, 7), - )) -} - -func TestPathSliceEndStep(t *testing.T) { - assertPath(t, - "$[:456:7]", - buildPath( - newMatchSliceFn(0, 456, 7), - )) -} - -func TestPathSliceStep(t *testing.T) { - assertPath(t, - "$[::7]", - buildPath( - newMatchSliceFn(0, maxInt, 7), - )) -} - -func TestPathSliceAll(t *testing.T) { - assertPath(t, - "$[123:456:7]", - buildPath( - newMatchSliceFn(123, 456, 7), - )) -} - -func TestPathAny(t *testing.T) { - assertPath(t, - "$.*", - buildPath( - newMatchAnyFn(), - )) -} - -func TestPathUnion(t *testing.T) { - assertPath(t, - "$[foo, bar, baz]", - buildPath( - &matchUnionFn{[]pathFn{ - newMatchKeyFn("foo"), - newMatchKeyFn("bar"), - newMatchKeyFn("baz"), - }}, - )) -} - -func TestPathRecurse(t *testing.T) { - assertPath(t, - "$..*", - buildPath( - newMatchRecursiveFn(), - )) -} - -func TestPathFilterExpr(t *testing.T) { - assertPath(t, - "$[?('foo'),?(bar)]", - buildPath( - &matchUnionFn{[]pathFn{ - newMatchFilterFn("foo", toml.Position{}), - newMatchFilterFn("bar", toml.Position{}), - }}, - )) -} diff --git a/vendor/github.com/pelletier/go-toml/query/parser.go b/vendor/github.com/pelletier/go-toml/query/parser.go deleted file mode 100644 index 5f69b70d4..000000000 --- a/vendor/github.com/pelletier/go-toml/query/parser.go +++ /dev/null @@ -1,275 +0,0 @@ -/* - Based on the "jsonpath" spec/concept. - - http://goessner.net/articles/JsonPath/ - https://code.google.com/p/json-path/ -*/ - -package query - -import ( - "fmt" -) - -const maxInt = int(^uint(0) >> 1) - -type queryParser struct { - flow chan token - tokensBuffer []token - query *Query - union []pathFn - err error -} - -type queryParserStateFn func() queryParserStateFn - -// Formats and panics an error message based on a token -func (p *queryParser) parseError(tok *token, msg string, args ...interface{}) queryParserStateFn { - p.err = fmt.Errorf(tok.Position.String()+": "+msg, args...) - return nil // trigger parse to end -} - -func (p *queryParser) run() { - for state := p.parseStart; state != nil; { - state = state() - } -} - -func (p *queryParser) backup(tok *token) { - p.tokensBuffer = append(p.tokensBuffer, *tok) -} - -func (p *queryParser) peek() *token { - if len(p.tokensBuffer) != 0 { - return &(p.tokensBuffer[0]) - } - - tok, ok := <-p.flow - if !ok { - return nil - } - p.backup(&tok) - return &tok -} - -func (p *queryParser) lookahead(types ...tokenType) bool { - result := true - buffer := []token{} - - for _, typ := range types { - tok := p.getToken() - if tok == nil { - result = false - break - } - buffer = append(buffer, *tok) - if tok.typ != typ { - result = false - break - } - } - // add the tokens back to the buffer, and return - p.tokensBuffer = append(p.tokensBuffer, buffer...) - return result -} - -func (p *queryParser) getToken() *token { - if len(p.tokensBuffer) != 0 { - tok := p.tokensBuffer[0] - p.tokensBuffer = p.tokensBuffer[1:] - return &tok - } - tok, ok := <-p.flow - if !ok { - return nil - } - return &tok -} - -func (p *queryParser) parseStart() queryParserStateFn { - tok := p.getToken() - - if tok == nil || tok.typ == tokenEOF { - return nil - } - - if tok.typ != tokenDollar { - return p.parseError(tok, "Expected '$' at start of expression") - } - - return p.parseMatchExpr -} - -// handle '.' prefix, '[]', and '..' -func (p *queryParser) parseMatchExpr() queryParserStateFn { - tok := p.getToken() - switch tok.typ { - case tokenDotDot: - p.query.appendPath(&matchRecursiveFn{}) - // nested parse for '..' - tok := p.getToken() - switch tok.typ { - case tokenKey: - p.query.appendPath(newMatchKeyFn(tok.val)) - return p.parseMatchExpr - case tokenLeftBracket: - return p.parseBracketExpr - case tokenStar: - // do nothing - the recursive predicate is enough - return p.parseMatchExpr - } - - case tokenDot: - // nested parse for '.' - tok := p.getToken() - switch tok.typ { - case tokenKey: - p.query.appendPath(newMatchKeyFn(tok.val)) - return p.parseMatchExpr - case tokenStar: - p.query.appendPath(&matchAnyFn{}) - return p.parseMatchExpr - } - - case tokenLeftBracket: - return p.parseBracketExpr - - case tokenEOF: - return nil // allow EOF at this stage - } - return p.parseError(tok, "expected match expression") -} - -func (p *queryParser) parseBracketExpr() queryParserStateFn { - if p.lookahead(tokenInteger, tokenColon) { - return p.parseSliceExpr - } - if p.peek().typ == tokenColon { - return p.parseSliceExpr - } - return p.parseUnionExpr -} - -func (p *queryParser) parseUnionExpr() queryParserStateFn { - var tok *token - - // this state can be traversed after some sub-expressions - // so be careful when setting up state in the parser - if p.union == nil { - p.union = []pathFn{} - } - -loop: // labeled loop for easy breaking - for { - if len(p.union) > 0 { - // parse delimiter or terminator - tok = p.getToken() - switch tok.typ { - case tokenComma: - // do nothing - case tokenRightBracket: - break loop - default: - return p.parseError(tok, "expected ',' or ']', not '%s'", tok.val) - } - } - - // parse sub expression - tok = p.getToken() - switch tok.typ { - case tokenInteger: - p.union = append(p.union, newMatchIndexFn(tok.Int())) - case tokenKey: - p.union = append(p.union, newMatchKeyFn(tok.val)) - case tokenString: - p.union = append(p.union, newMatchKeyFn(tok.val)) - case tokenQuestion: - return p.parseFilterExpr - default: - return p.parseError(tok, "expected union sub expression, not '%s', %d", tok.val, len(p.union)) - } - } - - // if there is only one sub-expression, use that instead - if len(p.union) == 1 { - p.query.appendPath(p.union[0]) - } else { - p.query.appendPath(&matchUnionFn{p.union}) - } - - p.union = nil // clear out state - return p.parseMatchExpr -} - -func (p *queryParser) parseSliceExpr() queryParserStateFn { - // init slice to grab all elements - start, end, step := 0, maxInt, 1 - - // parse optional start - tok := p.getToken() - if tok.typ == tokenInteger { - start = tok.Int() - tok = p.getToken() - } - if tok.typ != tokenColon { - return p.parseError(tok, "expected ':'") - } - - // parse optional end - tok = p.getToken() - if tok.typ == tokenInteger { - end = tok.Int() - tok = p.getToken() - } - if tok.typ == tokenRightBracket { - p.query.appendPath(newMatchSliceFn(start, end, step)) - return p.parseMatchExpr - } - if tok.typ != tokenColon { - return p.parseError(tok, "expected ']' or ':'") - } - - // parse optional step - tok = p.getToken() - if tok.typ == tokenInteger { - step = tok.Int() - if step < 0 { - return p.parseError(tok, "step must be a positive value") - } - tok = p.getToken() - } - if tok.typ != tokenRightBracket { - return p.parseError(tok, "expected ']'") - } - - p.query.appendPath(newMatchSliceFn(start, end, step)) - return p.parseMatchExpr -} - -func (p *queryParser) parseFilterExpr() queryParserStateFn { - tok := p.getToken() - if tok.typ != tokenLeftParen { - return p.parseError(tok, "expected left-parenthesis for filter expression") - } - tok = p.getToken() - if tok.typ != tokenKey && tok.typ != tokenString { - return p.parseError(tok, "expected key or string for filter function name") - } - name := tok.val - tok = p.getToken() - if tok.typ != tokenRightParen { - return p.parseError(tok, "expected right-parenthesis for filter expression") - } - p.union = append(p.union, newMatchFilterFn(name, tok.Position)) - return p.parseUnionExpr -} - -func parseQuery(flow chan token) (*Query, error) { - parser := &queryParser{ - flow: flow, - tokensBuffer: []token{}, - query: newQuery(), - } - parser.run() - return parser.query, parser.err -} diff --git a/vendor/github.com/pelletier/go-toml/query/parser_test.go b/vendor/github.com/pelletier/go-toml/query/parser_test.go deleted file mode 100644 index 312f51ab5..000000000 --- a/vendor/github.com/pelletier/go-toml/query/parser_test.go +++ /dev/null @@ -1,482 +0,0 @@ -package query - -import ( - "fmt" - "io/ioutil" - "sort" - "strings" - "testing" - "time" - - "github.com/pelletier/go-toml" -) - -type queryTestNode struct { - value interface{} - position toml.Position -} - -func valueString(root interface{}) string { - result := "" //fmt.Sprintf("%T:", root) - switch node := root.(type) { - case *Result: - items := []string{} - for i, v := range node.Values() { - items = append(items, fmt.Sprintf("%s:%s", - node.Positions()[i].String(), valueString(v))) - } - sort.Strings(items) - result = "[" + strings.Join(items, ", ") + "]" - case queryTestNode: - result = fmt.Sprintf("%s:%s", - node.position.String(), valueString(node.value)) - case []interface{}: - items := []string{} - for _, v := range node { - items = append(items, valueString(v)) - } - sort.Strings(items) - result = "[" + strings.Join(items, ", ") + "]" - case *toml.Tree: - // workaround for unreliable map key ordering - items := []string{} - for _, k := range node.Keys() { - v := node.GetPath([]string{k}) - items = append(items, k+":"+valueString(v)) - } - sort.Strings(items) - result = "{" + strings.Join(items, ", ") + "}" - case map[string]interface{}: - // workaround for unreliable map key ordering - items := []string{} - for k, v := range node { - items = append(items, k+":"+valueString(v)) - } - sort.Strings(items) - result = "{" + strings.Join(items, ", ") + "}" - case int64: - result += fmt.Sprintf("%d", node) - case string: - result += "'" + node + "'" - case float64: - result += fmt.Sprintf("%f", node) - case bool: - result += fmt.Sprintf("%t", node) - case time.Time: - result += fmt.Sprintf("'%v'", node) - } - return result -} - -func assertValue(t *testing.T, result, ref interface{}) { - pathStr := valueString(result) - refStr := valueString(ref) - if pathStr != refStr { - t.Errorf("values do not match") - t.Log("test:", pathStr) - t.Log("ref: ", refStr) - } -} - -func assertQueryPositions(t *testing.T, tomlDoc string, query string, ref []interface{}) { - tree, err := toml.Load(tomlDoc) - if err != nil { - t.Errorf("Non-nil toml parse error: %v", err) - return - } - q, err := Compile(query) - if err != nil { - t.Error(err) - return - } - results := q.Execute(tree) - assertValue(t, results, ref) -} - -func TestQueryRoot(t *testing.T) { - assertQueryPositions(t, - "a = 42", - "$", - []interface{}{ - queryTestNode{ - map[string]interface{}{ - "a": int64(42), - }, toml.Position{1, 1}, - }, - }) -} - -func TestQueryKey(t *testing.T) { - assertQueryPositions(t, - "[foo]\na = 42", - "$.foo.a", - []interface{}{ - queryTestNode{ - int64(42), toml.Position{2, 1}, - }, - }) -} - -func TestQueryKeyString(t *testing.T) { - assertQueryPositions(t, - "[foo]\na = 42", - "$.foo['a']", - []interface{}{ - queryTestNode{ - int64(42), toml.Position{2, 1}, - }, - }) -} - -func TestQueryIndex(t *testing.T) { - assertQueryPositions(t, - "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", - "$.foo.a[5]", - []interface{}{ - queryTestNode{ - int64(6), toml.Position{2, 1}, - }, - }) -} - -func TestQuerySliceRange(t *testing.T) { - assertQueryPositions(t, - "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", - "$.foo.a[0:5]", - []interface{}{ - queryTestNode{ - int64(1), toml.Position{2, 1}, - }, - queryTestNode{ - int64(2), toml.Position{2, 1}, - }, - queryTestNode{ - int64(3), toml.Position{2, 1}, - }, - queryTestNode{ - int64(4), toml.Position{2, 1}, - }, - queryTestNode{ - int64(5), toml.Position{2, 1}, - }, - }) -} - -func TestQuerySliceStep(t *testing.T) { - assertQueryPositions(t, - "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", - "$.foo.a[0:5:2]", - []interface{}{ - queryTestNode{ - int64(1), toml.Position{2, 1}, - }, - queryTestNode{ - int64(3), toml.Position{2, 1}, - }, - queryTestNode{ - int64(5), toml.Position{2, 1}, - }, - }) -} - -func TestQueryAny(t *testing.T) { - assertQueryPositions(t, - "[foo.bar]\na=1\nb=2\n[foo.baz]\na=3\nb=4", - "$.foo.*", - []interface{}{ - queryTestNode{ - map[string]interface{}{ - "a": int64(1), - "b": int64(2), - }, toml.Position{1, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(3), - "b": int64(4), - }, toml.Position{4, 1}, - }, - }) -} -func TestQueryUnionSimple(t *testing.T) { - assertQueryPositions(t, - "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", - "$.*[bar,foo]", - []interface{}{ - queryTestNode{ - map[string]interface{}{ - "a": int64(1), - "b": int64(2), - }, toml.Position{1, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(3), - "b": int64(4), - }, toml.Position{4, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(5), - "b": int64(6), - }, toml.Position{7, 1}, - }, - }) -} - -func TestQueryRecursionAll(t *testing.T) { - assertQueryPositions(t, - "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", - "$..*", - []interface{}{ - queryTestNode{ - map[string]interface{}{ - "foo": map[string]interface{}{ - "bar": map[string]interface{}{ - "a": int64(1), - "b": int64(2), - }, - }, - "baz": map[string]interface{}{ - "foo": map[string]interface{}{ - "a": int64(3), - "b": int64(4), - }, - }, - "gorf": map[string]interface{}{ - "foo": map[string]interface{}{ - "a": int64(5), - "b": int64(6), - }, - }, - }, toml.Position{1, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "bar": map[string]interface{}{ - "a": int64(1), - "b": int64(2), - }, - }, toml.Position{1, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(1), - "b": int64(2), - }, toml.Position{1, 1}, - }, - queryTestNode{ - int64(1), toml.Position{2, 1}, - }, - queryTestNode{ - int64(2), toml.Position{3, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "foo": map[string]interface{}{ - "a": int64(3), - "b": int64(4), - }, - }, toml.Position{4, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(3), - "b": int64(4), - }, toml.Position{4, 1}, - }, - queryTestNode{ - int64(3), toml.Position{5, 1}, - }, - queryTestNode{ - int64(4), toml.Position{6, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "foo": map[string]interface{}{ - "a": int64(5), - "b": int64(6), - }, - }, toml.Position{7, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(5), - "b": int64(6), - }, toml.Position{7, 1}, - }, - queryTestNode{ - int64(5), toml.Position{8, 1}, - }, - queryTestNode{ - int64(6), toml.Position{9, 1}, - }, - }) -} - -func TestQueryRecursionUnionSimple(t *testing.T) { - assertQueryPositions(t, - "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", - "$..['foo','bar']", - []interface{}{ - queryTestNode{ - map[string]interface{}{ - "bar": map[string]interface{}{ - "a": int64(1), - "b": int64(2), - }, - }, toml.Position{1, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(3), - "b": int64(4), - }, toml.Position{4, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(1), - "b": int64(2), - }, toml.Position{1, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "a": int64(5), - "b": int64(6), - }, toml.Position{7, 1}, - }, - }) -} - -func TestQueryFilterFn(t *testing.T) { - buff, err := ioutil.ReadFile("../example.toml") - if err != nil { - t.Error(err) - return - } - - assertQueryPositions(t, string(buff), - "$..[?(int)]", - []interface{}{ - queryTestNode{ - int64(8001), toml.Position{13, 1}, - }, - queryTestNode{ - int64(8001), toml.Position{13, 1}, - }, - queryTestNode{ - int64(8002), toml.Position{13, 1}, - }, - queryTestNode{ - int64(5000), toml.Position{14, 1}, - }, - }) - - assertQueryPositions(t, string(buff), - "$..[?(string)]", - []interface{}{ - queryTestNode{ - "TOML Example", toml.Position{3, 1}, - }, - queryTestNode{ - "Tom Preston-Werner", toml.Position{6, 1}, - }, - queryTestNode{ - "GitHub", toml.Position{7, 1}, - }, - queryTestNode{ - "GitHub Cofounder & CEO\nLikes tater tots and beer.", - toml.Position{8, 1}, - }, - queryTestNode{ - "192.168.1.1", toml.Position{12, 1}, - }, - queryTestNode{ - "10.0.0.1", toml.Position{21, 3}, - }, - queryTestNode{ - "eqdc10", toml.Position{22, 3}, - }, - queryTestNode{ - "10.0.0.2", toml.Position{25, 3}, - }, - queryTestNode{ - "eqdc10", toml.Position{26, 3}, - }, - }) - - assertQueryPositions(t, string(buff), - "$..[?(float)]", - []interface{}{ // no float values in document - }) - - tv, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") - assertQueryPositions(t, string(buff), - "$..[?(tree)]", - []interface{}{ - queryTestNode{ - map[string]interface{}{ - "name": "Tom Preston-Werner", - "organization": "GitHub", - "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", - "dob": tv, - }, toml.Position{5, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "server": "192.168.1.1", - "ports": []interface{}{int64(8001), int64(8001), int64(8002)}, - "connection_max": int64(5000), - "enabled": true, - }, toml.Position{11, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "alpha": map[string]interface{}{ - "ip": "10.0.0.1", - "dc": "eqdc10", - }, - "beta": map[string]interface{}{ - "ip": "10.0.0.2", - "dc": "eqdc10", - }, - }, toml.Position{17, 1}, - }, - queryTestNode{ - map[string]interface{}{ - "ip": "10.0.0.1", - "dc": "eqdc10", - }, toml.Position{20, 3}, - }, - queryTestNode{ - map[string]interface{}{ - "ip": "10.0.0.2", - "dc": "eqdc10", - }, toml.Position{24, 3}, - }, - queryTestNode{ - map[string]interface{}{ - "data": []interface{}{ - []interface{}{"gamma", "delta"}, - []interface{}{int64(1), int64(2)}, - }, - }, toml.Position{28, 1}, - }, - }) - - assertQueryPositions(t, string(buff), - "$..[?(time)]", - []interface{}{ - queryTestNode{ - tv, toml.Position{9, 1}, - }, - }) - - assertQueryPositions(t, string(buff), - "$..[?(bool)]", - []interface{}{ - queryTestNode{ - true, toml.Position{15, 1}, - }, - }) -} diff --git a/vendor/github.com/pelletier/go-toml/query/query.go b/vendor/github.com/pelletier/go-toml/query/query.go deleted file mode 100644 index 1c6cd8014..000000000 --- a/vendor/github.com/pelletier/go-toml/query/query.go +++ /dev/null @@ -1,158 +0,0 @@ -package query - -import ( - "time" - - "github.com/pelletier/go-toml" -) - -// NodeFilterFn represents a user-defined filter function, for use with -// Query.SetFilter(). -// -// The return value of the function must indicate if 'node' is to be included -// at this stage of the TOML path. Returning true will include the node, and -// returning false will exclude it. -// -// NOTE: Care should be taken to write script callbacks such that they are safe -// to use from multiple goroutines. -type NodeFilterFn func(node interface{}) bool - -// Result is the result of Executing a Query. -type Result struct { - items []interface{} - positions []toml.Position -} - -// appends a value/position pair to the result set. -func (r *Result) appendResult(node interface{}, pos toml.Position) { - r.items = append(r.items, node) - r.positions = append(r.positions, pos) -} - -// Values is a set of values within a Result. The order of values is not -// guaranteed to be in document order, and may be different each time a query is -// executed. -func (r Result) Values() []interface{} { - return r.items -} - -// Positions is a set of positions for values within a Result. Each index -// in Positions() corresponds to the entry in Value() of the same index. -func (r Result) Positions() []toml.Position { - return r.positions -} - -// runtime context for executing query paths -type queryContext struct { - result *Result - filters *map[string]NodeFilterFn - lastPosition toml.Position -} - -// generic path functor interface -type pathFn interface { - setNext(next pathFn) - // it is the caller's responsibility to set the ctx.lastPosition before invoking call() - // node can be one of: *toml.Tree, []*toml.Tree, or a scalar - call(node interface{}, ctx *queryContext) -} - -// A Query is the representation of a compiled TOML path. A Query is safe -// for concurrent use by multiple goroutines. -type Query struct { - root pathFn - tail pathFn - filters *map[string]NodeFilterFn -} - -func newQuery() *Query { - return &Query{ - root: nil, - tail: nil, - filters: &defaultFilterFunctions, - } -} - -func (q *Query) appendPath(next pathFn) { - if q.root == nil { - q.root = next - } else { - q.tail.setNext(next) - } - q.tail = next - next.setNext(newTerminatingFn()) // init the next functor -} - -// Compile compiles a TOML path expression. The returned Query can be used -// to match elements within a Tree and its descendants. See Execute. -func Compile(path string) (*Query, error) { - return parseQuery(lexQuery(path)) -} - -// Execute executes a query against a Tree, and returns the result of the query. -func (q *Query) Execute(tree *toml.Tree) *Result { - result := &Result{ - items: []interface{}{}, - positions: []toml.Position{}, - } - if q.root == nil { - result.appendResult(tree, tree.GetPosition("")) - } else { - ctx := &queryContext{ - result: result, - filters: q.filters, - } - ctx.lastPosition = tree.Position() - q.root.call(tree, ctx) - } - return result -} - -// CompileAndExecute is a shorthand for Compile(path) followed by Execute(tree). -func CompileAndExecute(path string, tree *toml.Tree) (*Result, error) { - query, err := Compile(path) - if err != nil { - return nil, err - } - return query.Execute(tree), nil -} - -// SetFilter sets a user-defined filter function. These may be used inside -// "?(..)" query expressions to filter TOML document elements within a query. -func (q *Query) SetFilter(name string, fn NodeFilterFn) { - if q.filters == &defaultFilterFunctions { - // clone the static table - q.filters = &map[string]NodeFilterFn{} - for k, v := range defaultFilterFunctions { - (*q.filters)[k] = v - } - } - (*q.filters)[name] = fn -} - -var defaultFilterFunctions = map[string]NodeFilterFn{ - "tree": func(node interface{}) bool { - _, ok := node.(*toml.Tree) - return ok - }, - "int": func(node interface{}) bool { - _, ok := node.(int64) - return ok - }, - "float": func(node interface{}) bool { - _, ok := node.(float64) - return ok - }, - "string": func(node interface{}) bool { - _, ok := node.(string) - return ok - }, - "time": func(node interface{}) bool { - _, ok := node.(time.Time) - return ok - }, - "bool": func(node interface{}) bool { - _, ok := node.(bool) - return ok - }, -} diff --git a/vendor/github.com/pelletier/go-toml/query/query_test.go b/vendor/github.com/pelletier/go-toml/query/query_test.go deleted file mode 100644 index 903a8dc73..000000000 --- a/vendor/github.com/pelletier/go-toml/query/query_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package query - -import ( - "fmt" - "testing" - - "github.com/pelletier/go-toml" -) - -func assertArrayContainsInAnyOrder(t *testing.T, array []interface{}, objects ...interface{}) { - if len(array) != len(objects) { - t.Fatalf("array contains %d objects but %d are expected", len(array), len(objects)) - } - - for _, o := range objects { - found := false - for _, a := range array { - if a == o { - found = true - break - } - } - if !found { - t.Fatal(o, "not found in array", array) - } - } -} - -func TestQueryExample(t *testing.T) { - config, _ := toml.Load(` - [[book]] - title = "The Stand" - author = "Stephen King" - [[book]] - title = "For Whom the Bell Tolls" - author = "Ernest Hemmingway" - [[book]] - title = "Neuromancer" - author = "William Gibson" - `) - authors, err := CompileAndExecute("$.book.author", config) - if err != nil { - t.Fatal("unexpected error:", err) - } - names := authors.Values() - if len(names) != 3 { - t.Fatalf("query should return 3 names but returned %d", len(names)) - } - assertArrayContainsInAnyOrder(t, names, "Stephen King", "Ernest Hemmingway", "William Gibson") -} - -func TestQueryReadmeExample(t *testing.T) { - config, _ := toml.Load(` -[postgres] -user = "pelletier" -password = "mypassword" -`) - - query, err := Compile("$..[user,password]") - if err != nil { - t.Fatal("unexpected error:", err) - } - results := query.Execute(config) - values := results.Values() - if len(values) != 2 { - t.Fatalf("query should return 2 values but returned %d", len(values)) - } - assertArrayContainsInAnyOrder(t, values, "pelletier", "mypassword") -} - -func TestQueryPathNotPresent(t *testing.T) { - config, _ := toml.Load(`a = "hello"`) - query, err := Compile("$.foo.bar") - if err != nil { - t.Fatal("unexpected error:", err) - } - results := query.Execute(config) - if err != nil { - t.Fatalf("err should be nil. got %s instead", err) - } - if len(results.items) != 0 { - t.Fatalf("no items should be matched. %d matched instead", len(results.items)) - } -} - -func ExampleNodeFilterFn_filterExample() { - tree, _ := toml.Load(` - [struct_one] - foo = "foo" - bar = "bar" - - [struct_two] - baz = "baz" - gorf = "gorf" - `) - - // create a query that references a user-defined-filter - query, _ := Compile("$[?(bazOnly)]") - - // define the filter, and assign it to the query - query.SetFilter("bazOnly", func(node interface{}) bool { - if tree, ok := node.(*toml.Tree); ok { - return tree.Has("baz") - } - return false // reject all other node types - }) - - // results contain only the 'struct_two' Tree - query.Execute(tree) -} - -func ExampleQuery_queryExample() { - config, _ := toml.Load(` - [[book]] - title = "The Stand" - author = "Stephen King" - [[book]] - title = "For Whom the Bell Tolls" - author = "Ernest Hemmingway" - [[book]] - title = "Neuromancer" - author = "William Gibson" - `) - - // find and print all the authors in the document - query, _ := Compile("$.book.author") - authors := query.Execute(config) - for _, name := range authors.Values() { - fmt.Println(name) - } -} - -func TestTomlQuery(t *testing.T) { - tree, err := toml.Load("[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6") - if err != nil { - t.Error(err) - return - } - query, err := Compile("$.foo.bar") - if err != nil { - t.Error(err) - return - } - result := query.Execute(tree) - values := result.Values() - if len(values) != 1 { - t.Errorf("Expected resultset of 1, got %d instead: %v", len(values), values) - } - - if tt, ok := values[0].(*toml.Tree); !ok { - t.Errorf("Expected type of Tree: %T", values[0]) - } else if tt.Get("a") != int64(1) { - t.Errorf("Expected 'a' with a value 1: %v", tt.Get("a")) - } else if tt.Get("b") != int64(2) { - t.Errorf("Expected 'b' with a value 2: %v", tt.Get("b")) - } -} diff --git a/vendor/github.com/pelletier/go-toml/query/tokens.go b/vendor/github.com/pelletier/go-toml/query/tokens.go deleted file mode 100644 index 9ae579de2..000000000 --- a/vendor/github.com/pelletier/go-toml/query/tokens.go +++ /dev/null @@ -1,106 +0,0 @@ -package query - -import ( - "fmt" - "github.com/pelletier/go-toml" - "strconv" - "unicode" -) - -// Define tokens -type tokenType int - -const ( - eof = -(iota + 1) -) - -const ( - tokenError tokenType = iota - tokenEOF - tokenKey - tokenString - tokenInteger - tokenFloat - tokenLeftBracket - tokenRightBracket - tokenLeftParen - tokenRightParen - tokenComma - tokenColon - tokenDollar - tokenStar - tokenQuestion - tokenDot - tokenDotDot -) - -var tokenTypeNames = []string{ - "Error", - "EOF", - "Key", - "String", - "Integer", - "Float", - "[", - "]", - "(", - ")", - ",", - ":", - "$", - "*", - "?", - ".", - "..", -} - -type token struct { - toml.Position - typ tokenType - val string -} - -func (tt tokenType) String() string { - idx := int(tt) - if idx < len(tokenTypeNames) { - return tokenTypeNames[idx] - } - return "Unknown" -} - -func (t token) Int() int { - if result, err := strconv.Atoi(t.val); err != nil { - panic(err) - } else { - return result - } -} - -func (t token) String() string { - switch t.typ { - case tokenEOF: - return "EOF" - case tokenError: - return t.val - } - - return fmt.Sprintf("%q", t.val) -} - -func isSpace(r rune) bool { - return r == ' ' || r == '\t' -} - -func isAlphanumeric(r rune) bool { - return unicode.IsLetter(r) || r == '_' -} - -func isDigit(r rune) bool { - return unicode.IsNumber(r) -} - -func isHexDigit(r rune) bool { - return isDigit(r) || - (r >= 'a' && r <= 'f') || - (r >= 'A' && r <= 'F') -} diff --git a/vendor/github.com/pelletier/go-toml/test.sh b/vendor/github.com/pelletier/go-toml/test.sh deleted file mode 100755 index ba6adf3fc..000000000 --- a/vendor/github.com/pelletier/go-toml/test.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/bash -# fail out of the script if anything here fails -set -e -set -o pipefail - -# set the path to the present working directory -export GOPATH=`pwd` - -function git_clone() { - path=$1 - branch=$2 - version=$3 - if [ ! -d "src/$path" ]; then - mkdir -p src/$path - git clone https://$path.git src/$path - fi - pushd src/$path - git checkout "$branch" - git reset --hard "$version" - popd -} - -# Remove potential previous runs -rm -rf src test_program_bin toml-test - -go get github.com/pelletier/go-buffruneio -go get github.com/davecgh/go-spew/spew -go get gopkg.in/yaml.v2 -go get github.com/BurntSushi/toml - -# get code for BurntSushi TOML validation -# pinning all to 'HEAD' for version 0.3.x work (TODO: pin to commit hash when tests stabilize) -git_clone github.com/BurntSushi/toml master HEAD -git_clone github.com/BurntSushi/toml-test master HEAD #was: 0.2.0 HEAD - -# build the BurntSushi test application -go build -o toml-test github.com/BurntSushi/toml-test - -# vendorize the current lib for testing -# NOTE: this basically mocks an install without having to go back out to github for code -mkdir -p src/github.com/pelletier/go-toml/cmd -mkdir -p src/github.com/pelletier/go-toml/query -cp *.go *.toml src/github.com/pelletier/go-toml -cp -R cmd/* src/github.com/pelletier/go-toml/cmd -cp -R query/* src/github.com/pelletier/go-toml/query -go build -o test_program_bin src/github.com/pelletier/go-toml/cmd/test_program.go - -# Run basic unit tests -go test github.com/pelletier/go-toml -covermode=count -coverprofile=coverage.out -go test github.com/pelletier/go-toml/cmd/tomljson -go test github.com/pelletier/go-toml/query - -# run the entire BurntSushi test suite -if [[ $# -eq 0 ]] ; then - echo "Running all BurntSushi tests" - ./toml-test ./test_program_bin | tee test_out -else - # run a specific test - test=$1 - test_path='src/github.com/BurntSushi/toml-test/tests' - valid_test="$test_path/valid/$test" - invalid_test="$test_path/invalid/$test" - - if [ -e "$valid_test.toml" ]; then - echo "Valid Test TOML for $test:" - echo "====" - cat "$valid_test.toml" - - echo "Valid Test JSON for $test:" - echo "====" - cat "$valid_test.json" - - echo "Go-TOML Output for $test:" - echo "====" - cat "$valid_test.toml" | ./test_program_bin - fi - - if [ -e "$invalid_test.toml" ]; then - echo "Invalid Test TOML for $test:" - echo "====" - cat "$invalid_test.toml" - - echo "Go-TOML Output for $test:" - echo "====" - echo "go-toml Output:" - cat "$invalid_test.toml" | ./test_program_bin - fi -fi diff --git a/vendor/github.com/pelletier/go-toml/token.go b/vendor/github.com/pelletier/go-toml/token.go deleted file mode 100644 index 1a9081346..000000000 --- a/vendor/github.com/pelletier/go-toml/token.go +++ /dev/null @@ -1,144 +0,0 @@ -package toml - -import ( - "fmt" - "strconv" - "unicode" -) - -// Define tokens -type tokenType int - -const ( - eof = -(iota + 1) -) - -const ( - tokenError tokenType = iota - tokenEOF - tokenComment - tokenKey - tokenString - tokenInteger - tokenTrue - tokenFalse - tokenFloat - tokenInf - tokenNan - tokenEqual - tokenLeftBracket - tokenRightBracket - tokenLeftCurlyBrace - tokenRightCurlyBrace - tokenLeftParen - tokenRightParen - tokenDoubleLeftBracket - tokenDoubleRightBracket - tokenDate - tokenKeyGroup - tokenKeyGroupArray - tokenComma - tokenColon - tokenDollar - tokenStar - tokenQuestion - tokenDot - tokenDotDot - tokenEOL -) - -var tokenTypeNames = []string{ - "Error", - "EOF", - "Comment", - "Key", - "String", - "Integer", - "True", - "False", - "Float", - "Inf", - "NaN", - "=", - "[", - "]", - "{", - "}", - "(", - ")", - "]]", - "[[", - "Date", - "KeyGroup", - "KeyGroupArray", - ",", - ":", - "$", - "*", - "?", - ".", - "..", - "EOL", -} - -type token struct { - Position - typ tokenType - val string -} - -func (tt tokenType) String() string { - idx := int(tt) - if idx < len(tokenTypeNames) { - return tokenTypeNames[idx] - } - return "Unknown" -} - -func (t token) Int() int { - if result, err := strconv.Atoi(t.val); err != nil { - panic(err) - } else { - return result - } -} - -func (t token) String() string { - switch t.typ { - case tokenEOF: - return "EOF" - case tokenError: - return t.val - } - - return fmt.Sprintf("%q", t.val) -} - -func isSpace(r rune) bool { - return r == ' ' || r == '\t' -} - -func isAlphanumeric(r rune) bool { - return unicode.IsLetter(r) || r == '_' -} - -func isKeyChar(r rune) bool { - // Keys start with the first character that isn't whitespace or [ and end - // with the last non-whitespace character before the equals sign. Keys - // cannot contain a # character." - return !(r == '\r' || r == '\n' || r == eof || r == '=') -} - -func isKeyStartChar(r rune) bool { - return !(isSpace(r) || r == '\r' || r == '\n' || r == eof || r == '[') -} - -func isDigit(r rune) bool { - return unicode.IsNumber(r) -} - -func isHexDigit(r rune) bool { - return isDigit(r) || - (r >= 'a' && r <= 'f') || - (r >= 'A' && r <= 'F') -} diff --git a/vendor/github.com/pelletier/go-toml/token_test.go b/vendor/github.com/pelletier/go-toml/token_test.go deleted file mode 100644 index 20b560d51..000000000 --- a/vendor/github.com/pelletier/go-toml/token_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package toml - -import "testing" - -func TestTokenStringer(t *testing.T) { - var tests = []struct { - tt tokenType - expect string - }{ - {tokenError, "Error"}, - {tokenEOF, "EOF"}, - {tokenComment, "Comment"}, - {tokenKey, "Key"}, - {tokenString, "String"}, - {tokenInteger, "Integer"}, - {tokenTrue, "True"}, - {tokenFalse, "False"}, - {tokenFloat, "Float"}, - {tokenEqual, "="}, - {tokenLeftBracket, "["}, - {tokenRightBracket, "]"}, - {tokenLeftCurlyBrace, "{"}, - {tokenRightCurlyBrace, "}"}, - {tokenLeftParen, "("}, - {tokenRightParen, ")"}, - {tokenDoubleLeftBracket, "]]"}, - {tokenDoubleRightBracket, "[["}, - {tokenDate, "Date"}, - {tokenKeyGroup, "KeyGroup"}, - {tokenKeyGroupArray, "KeyGroupArray"}, - {tokenComma, ","}, - {tokenColon, ":"}, - {tokenDollar, "$"}, - {tokenStar, "*"}, - {tokenQuestion, "?"}, - {tokenDot, "."}, - {tokenDotDot, ".."}, - {tokenEOL, "EOL"}, - {tokenEOL + 1, "Unknown"}, - } - - for i, test := range tests { - got := test.tt.String() - if got != test.expect { - t.Errorf("[%d] invalid string of token type; got %q, expected %q", i, got, test.expect) - } - } -} - -func TestTokenString(t *testing.T) { - var tests = []struct { - tok token - expect string - }{ - {token{Position{1, 1}, tokenEOF, ""}, "EOF"}, - {token{Position{1, 1}, tokenError, "Δt"}, "Δt"}, - {token{Position{1, 1}, tokenString, "bar"}, `"bar"`}, - {token{Position{1, 1}, tokenString, "123456789012345"}, `"123456789012345"`}, - } - - for i, test := range tests { - got := test.tok.String() - if got != test.expect { - t.Errorf("[%d] invalid of string token; got %q, expected %q", i, got, test.expect) - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/toml.go b/vendor/github.com/pelletier/go-toml/toml.go deleted file mode 100644 index 98c185ad0..000000000 --- a/vendor/github.com/pelletier/go-toml/toml.go +++ /dev/null @@ -1,367 +0,0 @@ -package toml - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "runtime" - "strings" -) - -type tomlValue struct { - value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list - comment string - commented bool - multiline bool - position Position -} - -// Tree is the result of the parsing of a TOML file. -type Tree struct { - values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree - comment string - commented bool - position Position -} - -func newTree() *Tree { - return &Tree{ - values: make(map[string]interface{}), - position: Position{}, - } -} - -// TreeFromMap initializes a new Tree object using the given map. -func TreeFromMap(m map[string]interface{}) (*Tree, error) { - result, err := toTree(m) - if err != nil { - return nil, err - } - return result.(*Tree), nil -} - -// Position returns the position of the tree. -func (t *Tree) Position() Position { - return t.position -} - -// Has returns a boolean indicating if the given key exists. -func (t *Tree) Has(key string) bool { - if key == "" { - return false - } - return t.HasPath(strings.Split(key, ".")) -} - -// HasPath returns true if the given path of keys exists, false otherwise. -func (t *Tree) HasPath(keys []string) bool { - return t.GetPath(keys) != nil -} - -// Keys returns the keys of the toplevel tree (does not recurse). -func (t *Tree) Keys() []string { - keys := make([]string, len(t.values)) - i := 0 - for k := range t.values { - keys[i] = k - i++ - } - return keys -} - -// Get the value at key in the Tree. -// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings. -// If you need to retrieve non-bare keys, use GetPath. -// Returns nil if the path does not exist in the tree. -// If keys is of length zero, the current tree is returned. -func (t *Tree) Get(key string) interface{} { - if key == "" { - return t - } - return t.GetPath(strings.Split(key, ".")) -} - -// GetPath returns the element in the tree indicated by 'keys'. -// If keys is of length zero, the current tree is returned. -func (t *Tree) GetPath(keys []string) interface{} { - if len(keys) == 0 { - return t - } - subtree := t - for _, intermediateKey := range keys[:len(keys)-1] { - value, exists := subtree.values[intermediateKey] - if !exists { - return nil - } - switch node := value.(type) { - case *Tree: - subtree = node - case []*Tree: - // go to most recent element - if len(node) == 0 { - return nil - } - subtree = node[len(node)-1] - default: - return nil // cannot navigate through other node types - } - } - // branch based on final node type - switch node := subtree.values[keys[len(keys)-1]].(type) { - case *tomlValue: - return node.value - default: - return node - } -} - -// GetPosition returns the position of the given key. -func (t *Tree) GetPosition(key string) Position { - if key == "" { - return t.position - } - return t.GetPositionPath(strings.Split(key, ".")) -} - -// GetPositionPath returns the element in the tree indicated by 'keys'. -// If keys is of length zero, the current tree is returned. -func (t *Tree) GetPositionPath(keys []string) Position { - if len(keys) == 0 { - return t.position - } - subtree := t - for _, intermediateKey := range keys[:len(keys)-1] { - value, exists := subtree.values[intermediateKey] - if !exists { - return Position{0, 0} - } - switch node := value.(type) { - case *Tree: - subtree = node - case []*Tree: - // go to most recent element - if len(node) == 0 { - return Position{0, 0} - } - subtree = node[len(node)-1] - default: - return Position{0, 0} - } - } - // branch based on final node type - switch node := subtree.values[keys[len(keys)-1]].(type) { - case *tomlValue: - return node.position - case *Tree: - return node.position - case []*Tree: - // go to most recent element - if len(node) == 0 { - return Position{0, 0} - } - return node[len(node)-1].position - default: - return Position{0, 0} - } -} - -// GetDefault works like Get but with a default value -func (t *Tree) GetDefault(key string, def interface{}) interface{} { - val := t.Get(key) - if val == nil { - return def - } - return val -} - -// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour. -// The default values within the struct are valid default options. -type SetOptions struct { - Comment string - Commented bool - Multiline bool -} - -// SetWithOptions is the same as Set, but allows you to provide formatting -// instructions to the key, that will be used by Marshal(). -func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) { - t.SetPathWithOptions(strings.Split(key, "."), opts, value) -} - -// SetPathWithOptions is the same as SetPath, but allows you to provide -// formatting instructions to the key, that will be reused by Marshal(). -func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) { - subtree := t - for _, intermediateKey := range keys[:len(keys)-1] { - nextTree, exists := subtree.values[intermediateKey] - if !exists { - nextTree = newTree() - subtree.values[intermediateKey] = nextTree // add new element here - } - switch node := nextTree.(type) { - case *Tree: - subtree = node - case []*Tree: - // go to most recent element - if len(node) == 0 { - // create element if it does not exist - subtree.values[intermediateKey] = append(node, newTree()) - } - subtree = node[len(node)-1] - } - } - - var toInsert interface{} - - switch value.(type) { - case *Tree: - tt := value.(*Tree) - tt.comment = opts.Comment - toInsert = value - case []*Tree: - toInsert = value - case *tomlValue: - tt := value.(*tomlValue) - tt.comment = opts.Comment - toInsert = tt - default: - toInsert = &tomlValue{value: value, comment: opts.Comment, commented: opts.Commented, multiline: opts.Multiline} - } - - subtree.values[keys[len(keys)-1]] = toInsert -} - -// Set an element in the tree. -// Key is a dot-separated path (e.g. a.b.c). -// Creates all necessary intermediate trees, if needed. -func (t *Tree) Set(key string, value interface{}) { - t.SetWithComment(key, "", false, value) -} - -// SetWithComment is the same as Set, but allows you to provide comment -// information to the key, that will be reused by Marshal(). -func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) { - t.SetPathWithComment(strings.Split(key, "."), comment, commented, value) -} - -// SetPath sets an element in the tree. -// Keys is an array of path elements (e.g. {"a","b","c"}). -// Creates all necessary intermediate trees, if needed. -func (t *Tree) SetPath(keys []string, value interface{}) { - t.SetPathWithComment(keys, "", false, value) -} - -// SetPathWithComment is the same as SetPath, but allows you to provide comment -// information to the key, that will be reused by Marshal(). -func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) { - subtree := t - for _, intermediateKey := range keys[:len(keys)-1] { - nextTree, exists := subtree.values[intermediateKey] - if !exists { - nextTree = newTree() - subtree.values[intermediateKey] = nextTree // add new element here - } - switch node := nextTree.(type) { - case *Tree: - subtree = node - case []*Tree: - // go to most recent element - if len(node) == 0 { - // create element if it does not exist - subtree.values[intermediateKey] = append(node, newTree()) - } - subtree = node[len(node)-1] - } - } - - var toInsert interface{} - - switch value.(type) { - case *Tree: - tt := value.(*Tree) - tt.comment = comment - toInsert = value - case []*Tree: - toInsert = value - case *tomlValue: - tt := value.(*tomlValue) - tt.comment = comment - toInsert = tt - default: - toInsert = &tomlValue{value: value, comment: comment, commented: commented} - } - - subtree.values[keys[len(keys)-1]] = toInsert -} - -// createSubTree takes a tree and a key and create the necessary intermediate -// subtrees to create a subtree at that point. In-place. -// -// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b] -// and tree[a][b][c] -// -// Returns nil on success, error object on failure -func (t *Tree) createSubTree(keys []string, pos Position) error { - subtree := t - for _, intermediateKey := range keys { - nextTree, exists := subtree.values[intermediateKey] - if !exists { - tree := newTree() - tree.position = pos - subtree.values[intermediateKey] = tree - nextTree = tree - } - - switch node := nextTree.(type) { - case []*Tree: - subtree = node[len(node)-1] - case *Tree: - subtree = node - default: - return fmt.Errorf("unknown type for path %s (%s): %T (%#v)", - strings.Join(keys, "."), intermediateKey, nextTree, nextTree) - } - } - return nil -} - -// LoadBytes creates a Tree from a []byte. -func LoadBytes(b []byte) (tree *Tree, err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = errors.New(r.(string)) - } - }() - tree = parseToml(lexToml(b)) - return -} - -// LoadReader creates a Tree from any io.Reader. -func LoadReader(reader io.Reader) (tree *Tree, err error) { - inputBytes, err := ioutil.ReadAll(reader) - if err != nil { - return - } - tree, err = LoadBytes(inputBytes) - return -} - -// Load creates a Tree from a string. -func Load(content string) (tree *Tree, err error) { - return LoadBytes([]byte(content)) -} - -// LoadFile creates a Tree from a file. -func LoadFile(path string) (tree *Tree, err error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - return LoadReader(file) -} diff --git a/vendor/github.com/pelletier/go-toml/toml_test.go b/vendor/github.com/pelletier/go-toml/toml_test.go deleted file mode 100644 index ab9c24253..000000000 --- a/vendor/github.com/pelletier/go-toml/toml_test.go +++ /dev/null @@ -1,106 +0,0 @@ -// Testing support for go-toml - -package toml - -import ( - "testing" -) - -func TestTomlHas(t *testing.T) { - tree, _ := Load(` - [test] - key = "value" - `) - - if !tree.Has("test.key") { - t.Errorf("Has - expected test.key to exists") - } - - if tree.Has("") { - t.Errorf("Should return false if the key is not provided") - } -} - -func TestTomlGet(t *testing.T) { - tree, _ := Load(` - [test] - key = "value" - `) - - if tree.Get("") != tree { - t.Errorf("Get should return the tree itself when given an empty path") - } - - if tree.Get("test.key") != "value" { - t.Errorf("Get should return the value") - } - if tree.Get(`\`) != nil { - t.Errorf("should return nil when the key is malformed") - } -} - -func TestTomlGetDefault(t *testing.T) { - tree, _ := Load(` - [test] - key = "value" - `) - - if tree.GetDefault("", "hello") != tree { - t.Error("GetDefault should return the tree itself when given an empty path") - } - - if tree.GetDefault("test.key", "hello") != "value" { - t.Error("Get should return the value") - } - - if tree.GetDefault("whatever", "hello") != "hello" { - t.Error("GetDefault should return the default value if the key does not exist") - } -} - -func TestTomlHasPath(t *testing.T) { - tree, _ := Load(` - [test] - key = "value" - `) - - if !tree.HasPath([]string{"test", "key"}) { - t.Errorf("HasPath - expected test.key to exists") - } -} - -func TestTomlGetPath(t *testing.T) { - node := newTree() - //TODO: set other node data - - for idx, item := range []struct { - Path []string - Expected *Tree - }{ - { // empty path test - []string{}, - node, - }, - } { - result := node.GetPath(item.Path) - if result != item.Expected { - t.Errorf("GetPath[%d] %v - expected %v, got %v instead.", idx, item.Path, item.Expected, result) - } - } - - tree, _ := Load("[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6") - if tree.GetPath([]string{"whatever"}) != nil { - t.Error("GetPath should return nil when the key does not exist") - } -} - -func TestTomlFromMap(t *testing.T) { - simpleMap := map[string]interface{}{"hello": 42} - tree, err := TreeFromMap(simpleMap) - if err != nil { - t.Fatal("unexpected error:", err) - } - if tree.Get("hello") != int64(42) { - t.Fatal("hello should be 42, not", tree.Get("hello")) - } -} diff --git a/vendor/github.com/pelletier/go-toml/tomltree_create.go b/vendor/github.com/pelletier/go-toml/tomltree_create.go deleted file mode 100644 index 79610e9b3..000000000 --- a/vendor/github.com/pelletier/go-toml/tomltree_create.go +++ /dev/null @@ -1,142 +0,0 @@ -package toml - -import ( - "fmt" - "reflect" - "time" -) - -var kindToType = [reflect.String + 1]reflect.Type{ - reflect.Bool: reflect.TypeOf(true), - reflect.String: reflect.TypeOf(""), - reflect.Float32: reflect.TypeOf(float64(1)), - reflect.Float64: reflect.TypeOf(float64(1)), - reflect.Int: reflect.TypeOf(int64(1)), - reflect.Int8: reflect.TypeOf(int64(1)), - reflect.Int16: reflect.TypeOf(int64(1)), - reflect.Int32: reflect.TypeOf(int64(1)), - reflect.Int64: reflect.TypeOf(int64(1)), - reflect.Uint: reflect.TypeOf(uint64(1)), - reflect.Uint8: reflect.TypeOf(uint64(1)), - reflect.Uint16: reflect.TypeOf(uint64(1)), - reflect.Uint32: reflect.TypeOf(uint64(1)), - reflect.Uint64: reflect.TypeOf(uint64(1)), -} - -// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found. -// supported values: -// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32 -func typeFor(k reflect.Kind) reflect.Type { - if k > 0 && int(k) < len(kindToType) { - return kindToType[k] - } - return nil -} - -func simpleValueCoercion(object interface{}) (interface{}, error) { - switch original := object.(type) { - case string, bool, int64, uint64, float64, time.Time: - return original, nil - case int: - return int64(original), nil - case int8: - return int64(original), nil - case int16: - return int64(original), nil - case int32: - return int64(original), nil - case uint: - return uint64(original), nil - case uint8: - return uint64(original), nil - case uint16: - return uint64(original), nil - case uint32: - return uint64(original), nil - case float32: - return float64(original), nil - case fmt.Stringer: - return original.String(), nil - default: - return nil, fmt.Errorf("cannot convert type %T to Tree", object) - } -} - -func sliceToTree(object interface{}) (interface{}, error) { - // arrays are a bit tricky, since they can represent either a - // collection of simple values, which is represented by one - // *tomlValue, or an array of tables, which is represented by an - // array of *Tree. - - // holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice - value := reflect.ValueOf(object) - insideType := value.Type().Elem() - length := value.Len() - if length > 0 { - insideType = reflect.ValueOf(value.Index(0).Interface()).Type() - } - if insideType.Kind() == reflect.Map { - // this is considered as an array of tables - tablesArray := make([]*Tree, 0, length) - for i := 0; i < length; i++ { - table := value.Index(i) - tree, err := toTree(table.Interface()) - if err != nil { - return nil, err - } - tablesArray = append(tablesArray, tree.(*Tree)) - } - return tablesArray, nil - } - - sliceType := typeFor(insideType.Kind()) - if sliceType == nil { - sliceType = insideType - } - - arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length) - - for i := 0; i < length; i++ { - val := value.Index(i).Interface() - simpleValue, err := simpleValueCoercion(val) - if err != nil { - return nil, err - } - arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) - } - return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil -} - -func toTree(object interface{}) (interface{}, error) { - value := reflect.ValueOf(object) - - if value.Kind() == reflect.Map { - values := map[string]interface{}{} - keys := value.MapKeys() - for _, key := range keys { - if key.Kind() != reflect.String { - if _, ok := key.Interface().(string); !ok { - return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind()) - } - } - - v := value.MapIndex(key) - newValue, err := toTree(v.Interface()) - if err != nil { - return nil, err - } - values[key.String()] = newValue - } - return &Tree{values: values, position: Position{}}, nil - } - - if value.Kind() == reflect.Array || value.Kind() == reflect.Slice { - return sliceToTree(object) - } - - simpleValue, err := simpleValueCoercion(object) - if err != nil { - return nil, err - } - return &tomlValue{value: simpleValue, position: Position{}}, nil -} diff --git a/vendor/github.com/pelletier/go-toml/tomltree_create_test.go b/vendor/github.com/pelletier/go-toml/tomltree_create_test.go deleted file mode 100644 index 3465a1066..000000000 --- a/vendor/github.com/pelletier/go-toml/tomltree_create_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package toml - -import ( - "strconv" - "testing" - "time" -) - -type customString string - -type stringer struct{} - -func (s stringer) String() string { - return "stringer" -} - -func validate(t *testing.T, path string, object interface{}) { - switch o := object.(type) { - case *Tree: - for key, tree := range o.values { - validate(t, path+"."+key, tree) - } - case []*Tree: - for index, tree := range o { - validate(t, path+"."+strconv.Itoa(index), tree) - } - case *tomlValue: - switch o.value.(type) { - case int64, uint64, bool, string, float64, time.Time, - []int64, []uint64, []bool, []string, []float64, []time.Time: - default: - t.Fatalf("tomlValue at key %s containing incorrect type %T", path, o.value) - } - default: - t.Fatalf("value at key %s is of incorrect type %T", path, object) - } - t.Logf("validation ok %s as %T", path, object) -} - -func validateTree(t *testing.T, tree *Tree) { - validate(t, "", tree) -} - -func TestTreeCreateToTree(t *testing.T) { - data := map[string]interface{}{ - "a_string": "bar", - "an_int": 42, - "time": time.Now(), - "int8": int8(2), - "int16": int16(2), - "int32": int32(2), - "uint8": uint8(2), - "uint16": uint16(2), - "uint32": uint32(2), - "float32": float32(2), - "a_bool": false, - "stringer": stringer{}, - "nested": map[string]interface{}{ - "foo": "bar", - }, - "array": []string{"a", "b", "c"}, - "array_uint": []uint{uint(1), uint(2)}, - "array_table": []map[string]interface{}{{"sub_map": 52}}, - "array_times": []time.Time{time.Now(), time.Now()}, - "map_times": map[string]time.Time{"now": time.Now()}, - "custom_string_map_key": map[customString]interface{}{customString("custom"): "custom"}, - } - tree, err := TreeFromMap(data) - if err != nil { - t.Fatal("unexpected error:", err) - } - validateTree(t, tree) -} - -func TestTreeCreateToTreeInvalidLeafType(t *testing.T) { - _, err := TreeFromMap(map[string]interface{}{"foo": t}) - expected := "cannot convert type *testing.T to Tree" - if err.Error() != expected { - t.Fatalf("expected error %s, got %s", expected, err.Error()) - } -} - -func TestTreeCreateToTreeInvalidMapKeyType(t *testing.T) { - _, err := TreeFromMap(map[string]interface{}{"foo": map[int]interface{}{2: 1}}) - expected := "map key needs to be a string, not int (int)" - if err.Error() != expected { - t.Fatalf("expected error %s, got %s", expected, err.Error()) - } -} - -func TestTreeCreateToTreeInvalidArrayMemberType(t *testing.T) { - _, err := TreeFromMap(map[string]interface{}{"foo": []*testing.T{t}}) - expected := "cannot convert type *testing.T to Tree" - if err.Error() != expected { - t.Fatalf("expected error %s, got %s", expected, err.Error()) - } -} - -func TestTreeCreateToTreeInvalidTableGroupType(t *testing.T) { - _, err := TreeFromMap(map[string]interface{}{"foo": []map[string]interface{}{{"hello": t}}}) - expected := "cannot convert type *testing.T to Tree" - if err.Error() != expected { - t.Fatalf("expected error %s, got %s", expected, err.Error()) - } -} - -func TestRoundTripArrayOfTables(t *testing.T) { - orig := "\n[[stuff]]\n name = \"foo\"\n things = [\"a\",\"b\"]\n" - tree, err := Load(orig) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - m := tree.ToMap() - - tree, err = TreeFromMap(m) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - want := orig - got := tree.String() - - if got != want { - t.Errorf("want:\n%s\ngot:\n%s", want, got) - } -} diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go deleted file mode 100644 index e4049e29f..000000000 --- a/vendor/github.com/pelletier/go-toml/tomltree_write.go +++ /dev/null @@ -1,333 +0,0 @@ -package toml - -import ( - "bytes" - "fmt" - "io" - "math" - "reflect" - "sort" - "strconv" - "strings" - "time" -) - -// Encodes a string to a TOML-compliant multi-line string value -// This function is a clone of the existing encodeTomlString function, except that whitespace characters -// are preserved. Quotation marks and backslashes are also not escaped. -func encodeMultilineTomlString(value string) string { - var b bytes.Buffer - - for _, rr := range value { - switch rr { - case '\b': - b.WriteString(`\b`) - case '\t': - b.WriteString("\t") - case '\n': - b.WriteString("\n") - case '\f': - b.WriteString(`\f`) - case '\r': - b.WriteString("\r") - case '"': - b.WriteString(`"`) - case '\\': - b.WriteString(`\`) - default: - intRr := uint16(rr) - if intRr < 0x001F { - b.WriteString(fmt.Sprintf("\\u%0.4X", intRr)) - } else { - b.WriteRune(rr) - } - } - } - return b.String() -} - -// Encodes a string to a TOML-compliant string value -func encodeTomlString(value string) string { - var b bytes.Buffer - - for _, rr := range value { - switch rr { - case '\b': - b.WriteString(`\b`) - case '\t': - b.WriteString(`\t`) - case '\n': - b.WriteString(`\n`) - case '\f': - b.WriteString(`\f`) - case '\r': - b.WriteString(`\r`) - case '"': - b.WriteString(`\"`) - case '\\': - b.WriteString(`\\`) - default: - intRr := uint16(rr) - if intRr < 0x001F { - b.WriteString(fmt.Sprintf("\\u%0.4X", intRr)) - } else { - b.WriteRune(rr) - } - } - } - return b.String() -} - -func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) { - // this interface check is added to dereference the change made in the writeTo function. - // That change was made to allow this function to see formatting options. - tv, ok := v.(*tomlValue) - if ok { - v = tv.value - } else { - tv = &tomlValue{} - } - - switch value := v.(type) { - case uint64: - return strconv.FormatUint(value, 10), nil - case int64: - return strconv.FormatInt(value, 10), nil - case float64: - // Ensure a round float does contain a decimal point. Otherwise feeding - // the output back to the parser would convert to an integer. - if math.Trunc(value) == value { - return strings.ToLower(strconv.FormatFloat(value, 'f', 1, 32)), nil - } - return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil - case string: - if tv.multiline { - return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil - } - return "\"" + encodeTomlString(value) + "\"", nil - case []byte: - b, _ := v.([]byte) - return tomlValueStringRepresentation(string(b), indent, arraysOneElementPerLine) - case bool: - if value { - return "true", nil - } - return "false", nil - case time.Time: - return value.Format(time.RFC3339), nil - case nil: - return "", nil - } - - rv := reflect.ValueOf(v) - - if rv.Kind() == reflect.Slice { - var values []string - for i := 0; i < rv.Len(); i++ { - item := rv.Index(i).Interface() - itemRepr, err := tomlValueStringRepresentation(item, indent, arraysOneElementPerLine) - if err != nil { - return "", err - } - values = append(values, itemRepr) - } - if arraysOneElementPerLine && len(values) > 1 { - stringBuffer := bytes.Buffer{} - valueIndent := indent + ` ` // TODO: move that to a shared encoder state - - stringBuffer.WriteString("[\n") - - for _, value := range values { - stringBuffer.WriteString(valueIndent) - stringBuffer.WriteString(value) - stringBuffer.WriteString(`,`) - stringBuffer.WriteString("\n") - } - - stringBuffer.WriteString(indent + "]") - - return stringBuffer.String(), nil - } - return "[" + strings.Join(values, ",") + "]", nil - } - return "", fmt.Errorf("unsupported value type %T: %v", v, v) -} - -func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) { - simpleValuesKeys := make([]string, 0) - complexValuesKeys := make([]string, 0) - - for k := range t.values { - v := t.values[k] - switch v.(type) { - case *Tree, []*Tree: - complexValuesKeys = append(complexValuesKeys, k) - default: - simpleValuesKeys = append(simpleValuesKeys, k) - } - } - - sort.Strings(simpleValuesKeys) - sort.Strings(complexValuesKeys) - - for _, k := range simpleValuesKeys { - v, ok := t.values[k].(*tomlValue) - if !ok { - return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) - } - - repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine) - if err != nil { - return bytesCount, err - } - - if v.comment != "" { - comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1) - start := "# " - if strings.HasPrefix(comment, "#") { - start = "" - } - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n") - bytesCount += int64(writtenBytesCountComment) - if errc != nil { - return bytesCount, errc - } - } - - var commented string - if v.commented { - commented = "# " - } - writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n") - bytesCount += int64(writtenBytesCount) - if err != nil { - return bytesCount, err - } - } - - for _, k := range complexValuesKeys { - v := t.values[k] - - combinedKey := k - if keyspace != "" { - combinedKey = keyspace + "." + combinedKey - } - var commented string - if t.commented { - commented = "# " - } - - switch node := v.(type) { - // node has to be of those two types given how keys are sorted above - case *Tree: - tv, ok := t.values[k].(*Tree) - if !ok { - return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k]) - } - if tv.comment != "" { - comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1) - start := "# " - if strings.HasPrefix(comment, "#") { - start = "" - } - writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment) - bytesCount += int64(writtenBytesCountComment) - if errc != nil { - return bytesCount, errc - } - } - writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n") - bytesCount += int64(writtenBytesCount) - if err != nil { - return bytesCount, err - } - bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine) - if err != nil { - return bytesCount, err - } - case []*Tree: - for _, subTree := range node { - writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n") - bytesCount += int64(writtenBytesCount) - if err != nil { - return bytesCount, err - } - - bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine) - if err != nil { - return bytesCount, err - } - } - } - } - - return bytesCount, nil -} - -func writeStrings(w io.Writer, s ...string) (int, error) { - var n int - for i := range s { - b, err := io.WriteString(w, s[i]) - n += b - if err != nil { - return n, err - } - } - return n, nil -} - -// WriteTo encode the Tree as Toml and writes it to the writer w. -// Returns the number of bytes written in case of success, or an error if anything happened. -func (t *Tree) WriteTo(w io.Writer) (int64, error) { - return t.writeTo(w, "", "", 0, false) -} - -// ToTomlString generates a human-readable representation of the current tree. -// Output spans multiple lines, and is suitable for ingest by a TOML parser. -// If the conversion cannot be performed, ToString returns a non-nil error. -func (t *Tree) ToTomlString() (string, error) { - var buf bytes.Buffer - _, err := t.WriteTo(&buf) - if err != nil { - return "", err - } - return buf.String(), nil -} - -// String generates a human-readable representation of the current tree. -// Alias of ToString. Present to implement the fmt.Stringer interface. -func (t *Tree) String() string { - result, _ := t.ToTomlString() - return result -} - -// ToMap recursively generates a representation of the tree using Go built-in structures. -// The following types are used: -// -// * bool -// * float64 -// * int64 -// * string -// * uint64 -// * time.Time -// * map[string]interface{} (where interface{} is any of this list) -// * []interface{} (where interface{} is any of this list) -func (t *Tree) ToMap() map[string]interface{} { - result := map[string]interface{}{} - - for k, v := range t.values { - switch node := v.(type) { - case []*Tree: - var array []interface{} - for _, item := range node { - array = append(array, item.ToMap()) - } - result[k] = array - case *Tree: - result[k] = node.ToMap() - case *tomlValue: - result[k] = node.value - } - } - return result -} diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write_test.go b/vendor/github.com/pelletier/go-toml/tomltree_write_test.go deleted file mode 100644 index 206203b88..000000000 --- a/vendor/github.com/pelletier/go-toml/tomltree_write_test.go +++ /dev/null @@ -1,376 +0,0 @@ -package toml - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" - "testing" - "time" -) - -type failingWriter struct { - failAt int - written int - buffer bytes.Buffer -} - -func (f *failingWriter) Write(p []byte) (n int, err error) { - count := len(p) - toWrite := f.failAt - (count + f.written) - if toWrite < 0 { - toWrite = 0 - } - if toWrite > count { - f.written += count - f.buffer.Write(p) - return count, nil - } - - f.buffer.Write(p[:toWrite]) - f.written = f.failAt - return toWrite, fmt.Errorf("failingWriter failed after writing %d bytes", f.written) -} - -func assertErrorString(t *testing.T, expected string, err error) { - expectedErr := errors.New(expected) - if err == nil || err.Error() != expectedErr.Error() { - t.Errorf("expecting error %s, but got %s instead", expected, err) - } -} - -func TestTreeWriteToEmptyTable(t *testing.T) { - doc := `[[empty-tables]] -[[empty-tables]]` - - toml, err := Load(doc) - if err != nil { - t.Fatal("Unexpected Load error:", err) - } - tomlString, err := toml.ToTomlString() - if err != nil { - t.Fatal("Unexpected ToTomlString error:", err) - } - - expected := ` -[[empty-tables]] - -[[empty-tables]] -` - - if tomlString != expected { - t.Fatalf("Expected:\n%s\nGot:\n%s", expected, tomlString) - } -} - -func TestTreeWriteToTomlString(t *testing.T) { - toml, err := Load(`name = { first = "Tom", last = "Preston-Werner" } -points = { x = 1, y = 2 }`) - - if err != nil { - t.Fatal("Unexpected error:", err) - } - - tomlString, _ := toml.ToTomlString() - reparsedTree, err := Load(tomlString) - - assertTree(t, reparsedTree, err, map[string]interface{}{ - "name": map[string]interface{}{ - "first": "Tom", - "last": "Preston-Werner", - }, - "points": map[string]interface{}{ - "x": int64(1), - "y": int64(2), - }, - }) -} - -func TestTreeWriteToTomlStringSimple(t *testing.T) { - tree, err := Load("[foo]\n\n[[foo.bar]]\na = 42\n\n[[foo.bar]]\na = 69\n") - if err != nil { - t.Errorf("Test failed to parse: %v", err) - return - } - result, err := tree.ToTomlString() - if err != nil { - t.Errorf("Unexpected error: %s", err) - } - expected := "\n[foo]\n\n [[foo.bar]]\n a = 42\n\n [[foo.bar]]\n a = 69\n" - if result != expected { - t.Errorf("Expected got '%s', expected '%s'", result, expected) - } -} - -func TestTreeWriteToTomlStringKeysOrders(t *testing.T) { - for i := 0; i < 100; i++ { - tree, _ := Load(` - foobar = true - bar = "baz" - foo = 1 - [qux] - foo = 1 - bar = "baz2"`) - - stringRepr, _ := tree.ToTomlString() - - t.Log("Intermediate string representation:") - t.Log(stringRepr) - - r := strings.NewReader(stringRepr) - toml, err := LoadReader(r) - - if err != nil { - t.Fatal("Unexpected error:", err) - } - - assertTree(t, toml, err, map[string]interface{}{ - "foobar": true, - "bar": "baz", - "foo": 1, - "qux": map[string]interface{}{ - "foo": 1, - "bar": "baz2", - }, - }) - } -} - -func testMaps(t *testing.T, actual, expected map[string]interface{}) { - if !reflect.DeepEqual(actual, expected) { - t.Fatal("trees aren't equal.\n", "Expected:\n", expected, "\nActual:\n", actual) - } -} - -func TestTreeWriteToMapSimple(t *testing.T) { - tree, _ := Load("a = 42\nb = 17") - - expected := map[string]interface{}{ - "a": int64(42), - "b": int64(17), - } - - testMaps(t, tree.ToMap(), expected) -} - -func TestTreeWriteToInvalidTreeSimpleValue(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": int8(1)}} - _, err := tree.ToTomlString() - assertErrorString(t, "invalid value type at foo: int8", err) -} - -func TestTreeWriteToInvalidTreeTomlValue(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: "", position: Position{}}}} - _, err := tree.ToTomlString() - assertErrorString(t, "unsupported value type int8: 1", err) -} - -func TestTreeWriteToInvalidTreeTomlValueArray(t *testing.T) { - tree := Tree{values: map[string]interface{}{"foo": &tomlValue{value: int8(1), comment: "", position: Position{}}}} - _, err := tree.ToTomlString() - assertErrorString(t, "unsupported value type int8: 1", err) -} - -func TestTreeWriteToFailingWriterInSimpleValue(t *testing.T) { - toml, _ := Load(`a = 2`) - writer := failingWriter{failAt: 0, written: 0} - _, err := toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writing 0 bytes", err) -} - -func TestTreeWriteToFailingWriterInTable(t *testing.T) { - toml, _ := Load(` -[b] -a = 2`) - writer := failingWriter{failAt: 2, written: 0} - _, err := toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writing 2 bytes", err) - - writer = failingWriter{failAt: 13, written: 0} - _, err = toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writing 13 bytes", err) -} - -func TestTreeWriteToFailingWriterInArray(t *testing.T) { - toml, _ := Load(` -[[b]] -a = 2`) - writer := failingWriter{failAt: 2, written: 0} - _, err := toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writing 2 bytes", err) - - writer = failingWriter{failAt: 15, written: 0} - _, err = toml.WriteTo(&writer) - assertErrorString(t, "failingWriter failed after writing 15 bytes", err) -} - -func TestTreeWriteToMapExampleFile(t *testing.T) { - tree, _ := LoadFile("example.toml") - expected := map[string]interface{}{ - "title": "TOML Example", - "owner": map[string]interface{}{ - "name": "Tom Preston-Werner", - "organization": "GitHub", - "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", - "dob": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC), - }, - "database": map[string]interface{}{ - "server": "192.168.1.1", - "ports": []interface{}{int64(8001), int64(8001), int64(8002)}, - "connection_max": int64(5000), - "enabled": true, - }, - "servers": map[string]interface{}{ - "alpha": map[string]interface{}{ - "ip": "10.0.0.1", - "dc": "eqdc10", - }, - "beta": map[string]interface{}{ - "ip": "10.0.0.2", - "dc": "eqdc10", - }, - }, - "clients": map[string]interface{}{ - "data": []interface{}{ - []interface{}{"gamma", "delta"}, - []interface{}{int64(1), int64(2)}, - }, - }, - } - testMaps(t, tree.ToMap(), expected) -} - -func TestTreeWriteToMapWithTablesInMultipleChunks(t *testing.T) { - tree, _ := Load(` - [[menu.main]] - a = "menu 1" - b = "menu 2" - [[menu.main]] - c = "menu 3" - d = "menu 4"`) - expected := map[string]interface{}{ - "menu": map[string]interface{}{ - "main": []interface{}{ - map[string]interface{}{"a": "menu 1", "b": "menu 2"}, - map[string]interface{}{"c": "menu 3", "d": "menu 4"}, - }, - }, - } - treeMap := tree.ToMap() - - testMaps(t, treeMap, expected) -} - -func TestTreeWriteToMapWithArrayOfInlineTables(t *testing.T) { - tree, _ := Load(` - [params] - language_tabs = [ - { key = "shell", name = "Shell" }, - { key = "ruby", name = "Ruby" }, - { key = "python", name = "Python" } - ]`) - - expected := map[string]interface{}{ - "params": map[string]interface{}{ - "language_tabs": []interface{}{ - map[string]interface{}{ - "key": "shell", - "name": "Shell", - }, - map[string]interface{}{ - "key": "ruby", - "name": "Ruby", - }, - map[string]interface{}{ - "key": "python", - "name": "Python", - }, - }, - }, - } - - treeMap := tree.ToMap() - testMaps(t, treeMap, expected) -} - -func TestTreeWriteToFloat(t *testing.T) { - tree, err := Load(`a = 3.0`) - if err != nil { - t.Fatal(err) - } - str, err := tree.ToTomlString() - if err != nil { - t.Fatal(err) - } - expected := `a = 3.0` - if strings.TrimSpace(str) != strings.TrimSpace(expected) { - t.Fatalf("Expected:\n%s\nGot:\n%s", expected, str) - } -} - -func TestTreeWriteToSpecialFloat(t *testing.T) { - expected := `a = +inf -b = -inf -c = nan` - - tree, err := Load(expected) - if err != nil { - t.Fatal(err) - } - str, err := tree.ToTomlString() - if err != nil { - t.Fatal(err) - } - if strings.TrimSpace(str) != strings.TrimSpace(expected) { - t.Fatalf("Expected:\n%s\nGot:\n%s", expected, str) - } -} - -func BenchmarkTreeToTomlString(b *testing.B) { - toml, err := Load(sampleHard) - if err != nil { - b.Fatal("Unexpected error:", err) - } - - for i := 0; i < b.N; i++ { - _, err := toml.ToTomlString() - if err != nil { - b.Fatal(err) - } - } -} - -var sampleHard = `# Test file for TOML -# Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate -# This part you'll really hate - -[the] -test_string = "You'll hate me after this - #" # " Annoying, isn't it? - - [the.hard] - test_array = [ "] ", " # "] # ] There you go, parse this! - test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ] - # You didn't think it'd as easy as chucking out the last #, did you? - another_test_string = " Same thing, but with a string #" - harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too" - # Things will get harder - - [the.hard."bit#"] - "what?" = "You don't think some user won't do that?" - multi_line_array = [ - "]", - # ] Oh yes I did - ] - -# Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test - -#[error] if you didn't catch this, your parser is broken -#string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this -#array = [ -# "This might most likely happen in multiline arrays", -# Like here, -# "or here, -# and here" -# ] End of array comment, forgot the # -#number = 3.14 pi <--again forgot the # ` diff --git a/vendor/github.com/spf13/cast/.gitignore b/vendor/github.com/spf13/cast/.gitignore index 53053a8ac..836562412 100644 --- a/vendor/github.com/spf13/cast/.gitignore +++ b/vendor/github.com/spf13/cast/.gitignore @@ -21,5 +21,3 @@ _testmain.go *.exe *.test - -*.bench diff --git a/vendor/github.com/spf13/cast/.travis.yml b/vendor/github.com/spf13/cast/.travis.yml deleted file mode 100644 index 4da976684..000000000 --- a/vendor/github.com/spf13/cast/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go -sudo: required -go: - - 1.7.5 - - 1.8 - - tip -os: - - linux -matrix: - allow_failures: - - go: tip - fast_finish: true -script: - - make check diff --git a/vendor/github.com/spf13/cast/Makefile b/vendor/github.com/spf13/cast/Makefile deleted file mode 100644 index 7ccf8930b..000000000 --- a/vendor/github.com/spf13/cast/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html - -.PHONY: check fmt lint test test-race vet test-cover-html help -.DEFAULT_GOAL := help - -check: test-race fmt vet lint ## Run tests and linters - -test: ## Run tests - go test ./... - -test-race: ## Run tests with race detector - go test -race ./... - -fmt: ## Run gofmt linter - @for d in `go list` ; do \ - if [ "`gofmt -l -s $$GOPATH/src/$$d | tee /dev/stderr`" ]; then \ - echo "^ improperly formatted go files" && echo && exit 1; \ - fi \ - done - -lint: ## Run golint linter - @for d in `go list` ; do \ - if [ "`golint $$d | tee /dev/stderr`" ]; then \ - echo "^ golint errors!" && echo && exit 1; \ - fi \ - done - -vet: ## Run go vet linter - @if [ "`go vet | tee /dev/stderr`" ]; then \ - echo "^ go vet errors!" && echo && exit 1; \ - fi - -test-cover-html: ## Generate test coverage report - go test -coverprofile=coverage.out -covermode=count - go tool cover -func=coverage.out - -help: - @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/vendor/github.com/spf13/cast/README.md b/vendor/github.com/spf13/cast/README.md index e6939397d..af7a1fdc0 100644 --- a/vendor/github.com/spf13/cast/README.md +++ b/vendor/github.com/spf13/cast/README.md @@ -1,8 +1,5 @@ cast ==== -[![GoDoc](https://godoc.org/github.com/spf13/cast?status.svg)](https://godoc.org/github.com/spf13/cast) -[![Build Status](https://api.travis-ci.org/spf13/cast.svg?branch=master)](https://travis-ci.org/spf13/cast) -[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cast)](https://goreportcard.com/report/github.com/spf13/cast) Easy and safe casting from one type to another in Go diff --git a/vendor/github.com/spf13/cast/cast.go b/vendor/github.com/spf13/cast/cast.go index 8b8c208be..0bc8d48c0 100644 --- a/vendor/github.com/spf13/cast/cast.go +++ b/vendor/github.com/spf13/cast/cast.go @@ -3,157 +3,71 @@ // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. -// Package cast provides easy and safe casting in Go. package cast import "time" -// ToBool casts an interface to a bool type. func ToBool(i interface{}) bool { v, _ := ToBoolE(i) return v } -// ToTime casts an interface to a time.Time type. func ToTime(i interface{}) time.Time { v, _ := ToTimeE(i) return v } -// ToDuration casts an interface to a time.Duration type. func ToDuration(i interface{}) time.Duration { v, _ := ToDurationE(i) return v } -// ToFloat64 casts an interface to a float64 type. func ToFloat64(i interface{}) float64 { v, _ := ToFloat64E(i) return v } -// ToFloat32 casts an interface to a float32 type. -func ToFloat32(i interface{}) float32 { - v, _ := ToFloat32E(i) - return v -} - -// ToInt64 casts an interface to an int64 type. -func ToInt64(i interface{}) int64 { - v, _ := ToInt64E(i) - return v -} - -// ToInt32 casts an interface to an int32 type. -func ToInt32(i interface{}) int32 { - v, _ := ToInt32E(i) - return v -} - -// ToInt16 casts an interface to an int16 type. -func ToInt16(i interface{}) int16 { - v, _ := ToInt16E(i) - return v -} - -// ToInt8 casts an interface to an int8 type. -func ToInt8(i interface{}) int8 { - v, _ := ToInt8E(i) - return v -} - -// ToInt casts an interface to an int type. func ToInt(i interface{}) int { v, _ := ToIntE(i) return v } -// ToUint casts an interface to a uint type. -func ToUint(i interface{}) uint { - v, _ := ToUintE(i) - return v -} - -// ToUint64 casts an interface to a uint64 type. -func ToUint64(i interface{}) uint64 { - v, _ := ToUint64E(i) - return v -} - -// ToUint32 casts an interface to a uint32 type. -func ToUint32(i interface{}) uint32 { - v, _ := ToUint32E(i) - return v -} - -// ToUint16 casts an interface to a uint16 type. -func ToUint16(i interface{}) uint16 { - v, _ := ToUint16E(i) - return v -} - -// ToUint8 casts an interface to a uint8 type. -func ToUint8(i interface{}) uint8 { - v, _ := ToUint8E(i) - return v -} - -// ToString casts an interface to a string type. func ToString(i interface{}) string { v, _ := ToStringE(i) return v } -// ToStringMapString casts an interface to a map[string]string type. func ToStringMapString(i interface{}) map[string]string { v, _ := ToStringMapStringE(i) return v } -// ToStringMapStringSlice casts an interface to a map[string][]string type. func ToStringMapStringSlice(i interface{}) map[string][]string { v, _ := ToStringMapStringSliceE(i) return v } -// ToStringMapBool casts an interface to a map[string]bool type. func ToStringMapBool(i interface{}) map[string]bool { v, _ := ToStringMapBoolE(i) return v } -// ToStringMap casts an interface to a map[string]interface{} type. func ToStringMap(i interface{}) map[string]interface{} { v, _ := ToStringMapE(i) return v } -// ToSlice casts an interface to a []interface{} type. func ToSlice(i interface{}) []interface{} { v, _ := ToSliceE(i) return v } -// ToBoolSlice casts an interface to a []bool type. -func ToBoolSlice(i interface{}) []bool { - v, _ := ToBoolSliceE(i) - return v -} - -// ToStringSlice casts an interface to a []string type. func ToStringSlice(i interface{}) []string { v, _ := ToStringSliceE(i) return v } -// ToIntSlice casts an interface to a []int type. func ToIntSlice(i interface{}) []int { v, _ := ToIntSliceE(i) return v } - -// ToDurationSlice casts an interface to a []time.Duration type. -func ToDurationSlice(i interface{}) []time.Duration { - v, _ := ToDurationSliceE(i) - return v -} diff --git a/vendor/github.com/spf13/cast/cast_test.go b/vendor/github.com/spf13/cast/cast_test.go index d9b0b013d..e08887b33 100644 --- a/vendor/github.com/spf13/cast/cast_test.go +++ b/vendor/github.com/spf13/cast/cast_test.go @@ -6,7 +6,6 @@ package cast import ( - "fmt" "html/template" "testing" "time" @@ -14,638 +13,35 @@ import ( "github.com/stretchr/testify/assert" ) -func TestToUintE(t *testing.T) { - tests := []struct { - input interface{} - expect uint - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToUintE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test: - v = ToUint(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToUint64E(t *testing.T) { - tests := []struct { - input interface{} - expect uint64 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToUint64E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test: - v = ToUint64(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToUint32E(t *testing.T) { - tests := []struct { - input interface{} - expect uint32 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToUint32E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test: - v = ToUint32(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToUint16E(t *testing.T) { - tests := []struct { - input interface{} - expect uint16 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToUint16E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToUint16(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToUint8E(t *testing.T) { - tests := []struct { - input interface{} - expect uint8 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {int(-8), 0, true}, - {int8(-8), 0, true}, - {int16(-8), 0, true}, - {int32(-8), 0, true}, - {int64(-8), 0, true}, - {float32(-8.31), 0, true}, - {float64(-8.31), 0, true}, - {"-8", 0, true}, - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToUint8E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToUint8(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToIntE(t *testing.T) { - tests := []struct { - input interface{} - expect int - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToIntE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToInt(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToInt64E(t *testing.T) { - tests := []struct { - input interface{} - expect int64 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToInt64E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToInt64(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToInt32E(t *testing.T) { - tests := []struct { - input interface{} - expect int32 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToInt32E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToInt32(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToInt16E(t *testing.T) { - tests := []struct { - input interface{} - expect int16 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToInt16E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToInt16(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToInt8E(t *testing.T) { - tests := []struct { - input interface{} - expect int8 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8, false}, - {float64(8.31), 8, false}, - {true, 1, false}, - {false, 0, false}, - {"8", 8, false}, - {nil, 0, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToInt8E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToInt8(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToFloat64E(t *testing.T) { - tests := []struct { - input interface{} - expect float64 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8), 8, false}, - {float64(8.31), 8.31, false}, - {"8", 8, false}, - {true, 1, false}, - {false, 0, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToFloat64E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToFloat64(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToFloat32E(t *testing.T) { - tests := []struct { - input interface{} - expect float32 - iserr bool - }{ - {int(8), 8, false}, - {int8(8), 8, false}, - {int16(8), 8, false}, - {int32(8), 8, false}, - {int64(8), 8, false}, - {uint(8), 8, false}, - {uint8(8), 8, false}, - {uint16(8), 8, false}, - {uint32(8), 8, false}, - {uint64(8), 8, false}, - {float32(8.31), 8.31, false}, - {float64(8.31), 8.31, false}, - {"8", 8, false}, - {true, 1, false}, - {false, 0, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToFloat32E(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToFloat32(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToStringE(t *testing.T) { - type Key struct { - k string - } - key := &Key{"foo"} - - tests := []struct { - input interface{} - expect string - iserr bool - }{ - {int(8), "8", false}, - {int8(8), "8", false}, - {int16(8), "8", false}, - {int32(8), "8", false}, - {int64(8), "8", false}, - {uint(8), "8", false}, - {uint8(8), "8", false}, - {uint16(8), "8", false}, - {uint32(8), "8", false}, - {uint64(8), "8", false}, - {float32(8.31), "8.31", false}, - {float64(8.31), "8.31", false}, - {true, "true", false}, - {false, "false", false}, - {nil, "", false}, - {[]byte("one time"), "one time", false}, - {"one more time", "one more time", false}, - {template.HTML("one time"), "one time", false}, - {template.URL("http://somehost.foo"), "http://somehost.foo", false}, - {template.JS("(1+2)"), "(1+2)", false}, - {template.CSS("a"), "a", false}, - {template.HTMLAttr("a"), "a", false}, - // errors - {testing.T{}, "", true}, - {key, "", true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToStringE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToString(test.input) - assert.Equal(t, test.expect, v, errmsg) - } +func TestToInt(t *testing.T) { + var eight interface{} = 8 + assert.Equal(t, ToInt(8), 8) + assert.Equal(t, ToInt(8.31), 8) + assert.Equal(t, ToInt("8"), 8) + assert.Equal(t, ToInt(true), 1) + assert.Equal(t, ToInt(false), 0) + assert.Equal(t, ToInt(eight), 8) +} + +func TestToFloat64(t *testing.T) { + var eight interface{} = 8 + assert.Equal(t, ToFloat64(8), 8.00) + assert.Equal(t, ToFloat64(8.31), 8.31) + assert.Equal(t, ToFloat64("8.31"), 8.31) + assert.Equal(t, ToFloat64(eight), 8.0) +} + +func TestToString(t *testing.T) { + var foo interface{} = "one more time" + assert.Equal(t, ToString(8), "8") + assert.Equal(t, ToString(8.12), "8.12") + assert.Equal(t, ToString([]byte("one time")), "one time") + assert.Equal(t, ToString(template.HTML("one time")), "one time") + assert.Equal(t, ToString(template.URL("http://somehost.foo")), "http://somehost.foo") + assert.Equal(t, ToString(foo), "one more time") + assert.Equal(t, ToString(nil), "") + assert.Equal(t, ToString(true), "true") + assert.Equal(t, ToString(false), "false") } type foo struct { @@ -657,6 +53,7 @@ func (x foo) String() string { } func TestStringerToString(t *testing.T) { + var x foo x.val = "bar" assert.Equal(t, "bar", ToString(x)) @@ -676,7 +73,10 @@ func TestErrorToString(t *testing.T) { assert.Equal(t, "bar", ToString(x)) } -func TestStringMapStringSliceE(t *testing.T) { +func TestMaps(t *testing.T) { + var taxonomies = map[interface{}]interface{}{"tag": "tags", "group": "groups"} + var stringMapBool = map[interface{}]interface{}{"v1": true, "v2": false} + // ToStringMapString inputs/outputs var stringMapString = map[string]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var stringMapInterface = map[string]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} @@ -684,407 +84,65 @@ func TestStringMapStringSliceE(t *testing.T) { var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} // ToStringMapStringSlice inputs/outputs - var stringMapStringSlice = map[string][]string{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} - var stringMapInterfaceSlice = map[string][]interface{}{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} - var stringMapInterfaceInterfaceSlice = map[string]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}} - var stringMapStringSingleSliceFieldsResult = map[string][]string{"key 1": {"value", "1"}, "key 2": {"value", "2"}, "key 3": {"value", "3"}} - var interfaceMapStringSlice = map[interface{}][]string{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} - var interfaceMapInterfaceSlice = map[interface{}][]interface{}{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} - - var stringMapStringSliceMultiple = map[string][]string{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} - var stringMapStringSliceSingle = map[string][]string{"key 1": {"value 1"}, "key 2": {"value 2"}, "key 3": {"value 3"}} - - var stringMapInterface1 = map[string]interface{}{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}} - var stringMapInterfaceResult1 = map[string][]string{"key 1": {"value 1"}, "key 2": {"value 2"}} - - var jsonStringMapString = `{"key 1": "value 1", "key 2": "value 2"}` - var jsonStringMapStringArray = `{"key 1": ["value 1"], "key 2": ["value 2", "value 3"]}` - var jsonStringMapStringArrayResult = map[string][]string{"key 1": {"value 1"}, "key 2": {"value 2", "value 3"}} - - type Key struct { - k string - } - - tests := []struct { - input interface{} - expect map[string][]string - iserr bool - }{ - {stringMapStringSlice, stringMapStringSlice, false}, - {stringMapInterfaceSlice, stringMapStringSlice, false}, - {stringMapInterfaceInterfaceSlice, stringMapStringSlice, false}, - {stringMapStringSliceMultiple, stringMapStringSlice, false}, - {stringMapStringSliceMultiple, stringMapStringSlice, false}, - {stringMapString, stringMapStringSliceSingle, false}, - {stringMapInterface, stringMapStringSliceSingle, false}, - {stringMapInterface1, stringMapInterfaceResult1, false}, - {interfaceMapStringSlice, stringMapStringSlice, false}, - {interfaceMapInterfaceSlice, stringMapStringSlice, false}, - {interfaceMapString, stringMapStringSingleSliceFieldsResult, false}, - {interfaceMapInterface, stringMapStringSingleSliceFieldsResult, false}, - {jsonStringMapStringArray, jsonStringMapStringArrayResult, false}, - - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - {map[interface{}]interface{}{"foo": testing.T{}}, nil, true}, - {map[interface{}]interface{}{Key{"foo"}: "bar"}, nil, true}, // ToStringE(Key{"foo"}) should fail - {jsonStringMapString, nil, true}, - {"", nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToStringMapStringSliceE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToStringMapStringSlice(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToStringMapE(t *testing.T) { - tests := []struct { - input interface{} - expect map[string]interface{} - iserr bool - }{ - {map[interface{}]interface{}{"tag": "tags", "group": "groups"}, map[string]interface{}{"tag": "tags", "group": "groups"}, false}, - {map[string]interface{}{"tag": "tags", "group": "groups"}, map[string]interface{}{"tag": "tags", "group": "groups"}, false}, - {`{"tag": "tags", "group": "groups"}`, map[string]interface{}{"tag": "tags", "group": "groups"}, false}, - {`{"tag": "tags", "group": true}`, map[string]interface{}{"tag": "tags", "group": true}, false}, - - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - {"", nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToStringMapE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToStringMap(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToStringMapBoolE(t *testing.T) { - tests := []struct { - input interface{} - expect map[string]bool - iserr bool - }{ - {map[interface{}]interface{}{"v1": true, "v2": false}, map[string]bool{"v1": true, "v2": false}, false}, - {map[string]interface{}{"v1": true, "v2": false}, map[string]bool{"v1": true, "v2": false}, false}, - {map[string]bool{"v1": true, "v2": false}, map[string]bool{"v1": true, "v2": false}, false}, - {`{"v1": true, "v2": false}`, map[string]bool{"v1": true, "v2": false}, false}, - - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - {"", nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToStringMapBoolE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToStringMapBool(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToStringMapStringE(t *testing.T) { - var stringMapString = map[string]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} - var stringMapInterface = map[string]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} - var interfaceMapString = map[interface{}]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} - var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} - var jsonString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}` - var invalidJsonString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"` - var emptyString = "" - - tests := []struct { - input interface{} - expect map[string]string - iserr bool - }{ - {stringMapString, stringMapString, false}, - {stringMapInterface, stringMapString, false}, - {interfaceMapString, stringMapString, false}, - {interfaceMapInterface, stringMapString, false}, - {jsonString, stringMapString, false}, - - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - {invalidJsonString, nil, true}, - {emptyString, nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToStringMapStringE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToStringMapString(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToBoolSliceE(t *testing.T) { - tests := []struct { - input interface{} - expect []bool - iserr bool - }{ - {[]bool{true, false, true}, []bool{true, false, true}, false}, - {[]interface{}{true, false, true}, []bool{true, false, true}, false}, - {[]int{1, 0, 1}, []bool{true, false, true}, false}, - {[]string{"true", "false", "true"}, []bool{true, false, true}, false}, - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - {[]string{"foo", "bar"}, nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToBoolSliceE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToBoolSlice(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToIntSliceE(t *testing.T) { - tests := []struct { - input interface{} - expect []int - iserr bool - }{ - {[]int{1, 3}, []int{1, 3}, false}, - {[]interface{}{1.2, 3.2}, []int{1, 3}, false}, - {[]string{"2", "3"}, []int{2, 3}, false}, - {[2]string{"2", "3"}, []int{2, 3}, false}, - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - {[]string{"foo", "bar"}, nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToIntSliceE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToIntSlice(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToSliceE(t *testing.T) { - tests := []struct { - input interface{} - expect []interface{} - iserr bool - }{ - {[]interface{}{1, 3}, []interface{}{1, 3}, false}, - {[]map[string]interface{}{{"k1": 1}, {"k2": 2}}, []interface{}{map[string]interface{}{"k1": 1}, map[string]interface{}{"k2": 2}}, false}, - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToSliceE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToSlice(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToStringSliceE(t *testing.T) { - tests := []struct { - input interface{} - expect []string - iserr bool - }{ - {[]string{"a", "b"}, []string{"a", "b"}, false}, - {[]interface{}{1, 3}, []string{"1", "3"}, false}, - {interface{}(1), []string{"1"}, false}, - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToStringSliceE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToStringSlice(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToDurationSliceE(t *testing.T) { - tests := []struct { - input interface{} - expect []time.Duration - iserr bool - }{ - {[]string{"1s", "1m"}, []time.Duration{time.Second, time.Minute}, false}, - {[]int{1, 2}, []time.Duration{1, 2}, false}, - {[]interface{}{1, 3}, []time.Duration{1, 3}, false}, - {[]time.Duration{1, 3}, []time.Duration{1, 3}, false}, - - // errors - {nil, nil, true}, - {testing.T{}, nil, true}, - {[]string{"invalid"}, nil, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToDurationSliceE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToDurationSlice(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func TestToBoolE(t *testing.T) { - tests := []struct { - input interface{} - expect bool - iserr bool - }{ - {0, false, false}, - {nil, false, false}, - {"false", false, false}, - {"FALSE", false, false}, - {"False", false, false}, - {"f", false, false}, - {"F", false, false}, - {false, false, false}, - - {"true", true, false}, - {"TRUE", true, false}, - {"True", true, false}, - {"t", true, false}, - {"T", true, false}, - {1, true, false}, - {true, true, false}, - {-1, true, false}, - - // errors - {"test", false, true}, - {testing.T{}, false, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToBoolE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToBool(test.input) - assert.Equal(t, test.expect, v, errmsg) - } -} - -func BenchmarkTooBool(b *testing.B) { - for i := 0; i < b.N; i++ { - if !ToBool(true) { - b.Fatal("ToBool returned false") - } - } + var stringMapStringSlice = map[string][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}} + var stringMapInterfaceSlice = map[string][]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}} + var stringMapStringSingleSliceFieldsResult = map[string][]string{"key 1": []string{"value", "1"}, "key 2": []string{"value", "2"}, "key 3": []string{"value", "3"}} + var interfaceMapStringSlice = map[interface{}][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}} + var interfaceMapInterfaceSlice = map[interface{}][]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}} + + var stringMapStringSliceMultiple = map[string][]string{"key 1": []string{"value 1", "value 2", "value 3"}, "key 2": []string{"value 1", "value 2", "value 3"}, "key 3": []string{"value 1", "value 2", "value 3"}} + var stringMapStringSliceSingle = map[string][]string{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}, "key 3": []string{"value 3"}} + + assert.Equal(t, ToStringMap(taxonomies), map[string]interface{}{"tag": "tags", "group": "groups"}) + assert.Equal(t, ToStringMapBool(stringMapBool), map[string]bool{"v1": true, "v2": false}) + + // ToStringMapString tests + assert.Equal(t, ToStringMapString(stringMapString), stringMapString) + assert.Equal(t, ToStringMapString(stringMapInterface), stringMapString) + assert.Equal(t, ToStringMapString(interfaceMapString), stringMapString) + assert.Equal(t, ToStringMapString(interfaceMapInterface), stringMapString) + + // ToStringMapStringSlice tests + assert.Equal(t, ToStringMapStringSlice(stringMapStringSlice), stringMapStringSlice) + assert.Equal(t, ToStringMapStringSlice(stringMapInterfaceSlice), stringMapStringSlice) + assert.Equal(t, ToStringMapStringSlice(stringMapStringSliceMultiple), stringMapStringSlice) + assert.Equal(t, ToStringMapStringSlice(stringMapStringSliceMultiple), stringMapStringSlice) + assert.Equal(t, ToStringMapStringSlice(stringMapString), stringMapStringSliceSingle) + assert.Equal(t, ToStringMapStringSlice(stringMapInterface), stringMapStringSliceSingle) + assert.Equal(t, ToStringMapStringSlice(interfaceMapStringSlice), stringMapStringSlice) + assert.Equal(t, ToStringMapStringSlice(interfaceMapInterfaceSlice), stringMapStringSlice) + assert.Equal(t, ToStringMapStringSlice(interfaceMapString), stringMapStringSingleSliceFieldsResult) + assert.Equal(t, ToStringMapStringSlice(interfaceMapInterface), stringMapStringSingleSliceFieldsResult) +} + +func TestSlices(t *testing.T) { + assert.Equal(t, []string{"a", "b"}, ToStringSlice([]string{"a", "b"})) + assert.Equal(t, []string{"1", "3"}, ToStringSlice([]interface{}{1, 3})) + assert.Equal(t, []int{1, 3}, ToIntSlice([]int{1, 3})) + assert.Equal(t, []int{1, 3}, ToIntSlice([]interface{}{1.2, 3.2})) + assert.Equal(t, []int{2, 3}, ToIntSlice([]string{"2", "3"})) + assert.Equal(t, []int{2, 3}, ToIntSlice([2]string{"2", "3"})) +} + +func TestToBool(t *testing.T) { + assert.Equal(t, ToBool(0), false) + assert.Equal(t, ToBool(nil), false) + assert.Equal(t, ToBool("false"), false) + assert.Equal(t, ToBool("FALSE"), false) + assert.Equal(t, ToBool("False"), false) + assert.Equal(t, ToBool("f"), false) + assert.Equal(t, ToBool("F"), false) + assert.Equal(t, ToBool(false), false) + assert.Equal(t, ToBool("foo"), false) + + assert.Equal(t, ToBool("true"), true) + assert.Equal(t, ToBool("TRUE"), true) + assert.Equal(t, ToBool("True"), true) + assert.Equal(t, ToBool("t"), true) + assert.Equal(t, ToBool("T"), true) + assert.Equal(t, ToBool(1), true) + assert.Equal(t, ToBool(true), true) + assert.Equal(t, ToBool(-1), true) } func TestIndirectPointers(t *testing.T) { @@ -1096,113 +154,11 @@ func TestIndirectPointers(t *testing.T) { assert.Equal(t, ToInt(z), 13) } -func TestToTimeEE(t *testing.T) { - tests := []struct { - input interface{} - expect time.Time - iserr bool - }{ - {"2009-11-10 23:00:00 +0000 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // Time.String() - {"Tue Nov 10 23:00:00 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // ANSIC - {"Tue Nov 10 23:00:00 UTC 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // UnixDate - {"Tue Nov 10 23:00:00 +0000 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RubyDate - {"10 Nov 09 23:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC822 - {"10 Nov 09 23:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC822Z - {"Tuesday, 10-Nov-09 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC850 - {"Tue, 10 Nov 2009 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC1123 - {"Tue, 10 Nov 2009 23:00:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC1123Z - {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC3339 - {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC3339Nano - {"11:00PM", time.Date(0, 1, 1, 23, 0, 0, 0, time.UTC), false}, // Kitchen - {"Nov 10 23:00:00", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // Stamp - {"Nov 10 23:00:00.000", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // StampMilli - {"Nov 10 23:00:00.000000", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // StampMicro - {"Nov 10 23:00:00.000000000", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // StampNano - {"2016-03-06 15:28:01-00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, // RFC3339 without T - {"2016-03-06 15:28:01", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, - {"2016-03-06 15:28:01 -0000", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, - {"2016-03-06 15:28:01 -00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, - {"2006-01-02", time.Date(2006, 1, 2, 0, 0, 0, 0, time.UTC), false}, - {"02 Jan 2006", time.Date(2006, 1, 2, 0, 0, 0, 0, time.UTC), false}, - {1472574600, time.Date(2016, 8, 30, 16, 30, 0, 0, time.UTC), false}, - {int(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, time.UTC), false}, - {int64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, - {int32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, - {uint(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, time.UTC), false}, - {uint64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, - {uint32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, - {time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, - // errors - {"2006", time.Time{}, true}, - {testing.T{}, time.Time{}, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToTimeE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v.UTC(), errmsg) - - // Non-E test - v = ToTime(test.input) - assert.Equal(t, test.expect, v.UTC(), errmsg) - } -} - -func TestToDurationE(t *testing.T) { - var td time.Duration = 5 - - tests := []struct { - input interface{} - expect time.Duration - iserr bool - }{ - {time.Duration(5), td, false}, - {int(5), td, false}, - {int64(5), td, false}, - {int32(5), td, false}, - {int16(5), td, false}, - {int8(5), td, false}, - {uint(5), td, false}, - {uint64(5), td, false}, - {uint32(5), td, false}, - {uint16(5), td, false}, - {uint8(5), td, false}, - {float64(5), td, false}, - {float32(5), td, false}, - {string("5"), td, false}, - {string("5ns"), td, false}, - {string("5us"), time.Microsecond * td, false}, - {string("5µs"), time.Microsecond * td, false}, - {string("5ms"), time.Millisecond * td, false}, - {string("5s"), time.Second * td, false}, - {string("5m"), time.Minute * td, false}, - {string("5h"), time.Hour * td, false}, - // errors - {"test", 0, true}, - {testing.T{}, 0, true}, - } - - for i, test := range tests { - errmsg := fmt.Sprintf("i = %d", i) // assert helper message - - v, err := ToDurationE(test.input) - if test.iserr { - assert.Error(t, err, errmsg) - continue - } - - assert.NoError(t, err, errmsg) - assert.Equal(t, test.expect, v, errmsg) - - // Non-E test - v = ToDuration(test.input) - assert.Equal(t, test.expect, v, errmsg) - } +func TestToDuration(t *testing.T) { + a := time.Second * 5 + ai := int64(a) + b := time.Second * 5 + bf := float64(b) + assert.Equal(t, ToDuration(ai), a) + assert.Equal(t, ToDuration(bf), b) } diff --git a/vendor/github.com/spf13/cast/caste.go b/vendor/github.com/spf13/cast/caste.go index 4fe192893..3ac4cb7a8 100644 --- a/vendor/github.com/spf13/cast/caste.go +++ b/vendor/github.com/spf13/cast/caste.go @@ -6,73 +6,62 @@ package cast import ( - "encoding/json" - "errors" "fmt" "html/template" "reflect" "strconv" "strings" "time" -) -var errNegativeNotAllowed = errors.New("unable to cast negative value") + jww "github.com/spf13/jwalterweatherman" +) -// ToTimeE casts an interface to a time.Time type. +// ToTimeE casts an empty interface to time.Time. func ToTimeE(i interface{}) (tim time.Time, err error) { i = indirect(i) + jww.DEBUG.Println("ToTimeE called on type:", reflect.TypeOf(i)) - switch v := i.(type) { + switch s := i.(type) { case time.Time: - return v, nil + return s, nil case string: - return StringToDate(v) - case int: - return time.Unix(int64(v), 0), nil - case int64: - return time.Unix(v, 0), nil - case int32: - return time.Unix(int64(v), 0), nil - case uint: - return time.Unix(int64(v), 0), nil - case uint64: - return time.Unix(int64(v), 0), nil - case uint32: - return time.Unix(int64(v), 0), nil + d, e := StringToDate(s) + if e == nil { + return d, nil + } + return time.Time{}, fmt.Errorf("Could not parse Date/Time format: %v\n", e) default: - return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i) + return time.Time{}, fmt.Errorf("Unable to Cast %#v to Time\n", i) } } -// ToDurationE casts an interface to a time.Duration type. +// ToDurationE casts an empty interface to time.Duration. func ToDurationE(i interface{}) (d time.Duration, err error) { i = indirect(i) + jww.DEBUG.Println("ToDurationE called on type:", reflect.TypeOf(i)) switch s := i.(type) { case time.Duration: return s, nil - case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8: - d = time.Duration(ToInt64(s)) + case int64: + d = time.Duration(s) return - case float32, float64: - d = time.Duration(ToFloat64(s)) + case float64: + d = time.Duration(s) return case string: - if strings.ContainsAny(s, "nsuµmh") { - d, err = time.ParseDuration(s) - } else { - d, err = time.ParseDuration(s + "ns") - } + d, err = time.ParseDuration(s) return default: - err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i) + err = fmt.Errorf("Unable to Cast %#v to Duration\n", i) return } } -// ToBoolE casts an interface to a bool type. +// ToBoolE casts an empty interface to a bool. func ToBoolE(i interface{}) (bool, error) { i = indirect(i) + jww.DEBUG.Println("ToBoolE called on type:", reflect.TypeOf(i)) switch b := i.(type) { case bool: @@ -87,21 +76,20 @@ func ToBoolE(i interface{}) (bool, error) { case string: return strconv.ParseBool(i.(string)) default: - return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i) + return false, fmt.Errorf("Unable to Cast %#v to bool", i) } } -// ToFloat64E casts an interface to a float64 type. +// ToFloat64E casts an empty interface to a float64. func ToFloat64E(i interface{}) (float64, error) { i = indirect(i) + jww.DEBUG.Println("ToFloat64E called on type:", reflect.TypeOf(i)) switch s := i.(type) { case float64: return s, nil case float32: return float64(s), nil - case int: - return float64(s), nil case int64: return float64(s), nil case int32: @@ -110,268 +98,23 @@ func ToFloat64E(i interface{}) (float64, error) { return float64(s), nil case int8: return float64(s), nil - case uint: - return float64(s), nil - case uint64: - return float64(s), nil - case uint32: - return float64(s), nil - case uint16: - return float64(s), nil - case uint8: + case int: return float64(s), nil case string: v, err := strconv.ParseFloat(s, 64) if err == nil { - return v, nil - } - return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) - case bool: - if s { - return 1, nil - } - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) - } -} - -// ToFloat32E casts an interface to a float32 type. -func ToFloat32E(i interface{}) (float32, error) { - i = indirect(i) - - switch s := i.(type) { - case float64: - return float32(s), nil - case float32: - return s, nil - case int: - return float32(s), nil - case int64: - return float32(s), nil - case int32: - return float32(s), nil - case int16: - return float32(s), nil - case int8: - return float32(s), nil - case uint: - return float32(s), nil - case uint64: - return float32(s), nil - case uint32: - return float32(s), nil - case uint16: - return float32(s), nil - case uint8: - return float32(s), nil - case string: - v, err := strconv.ParseFloat(s, 32) - if err == nil { - return float32(v), nil + return float64(v), nil } - return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) - case bool: - if s { - return 1, nil - } - return 0, nil + return 0.0, fmt.Errorf("Unable to Cast %#v to float", i) default: - return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) + return 0.0, fmt.Errorf("Unable to Cast %#v to float", i) } } -// ToInt64E casts an interface to an int64 type. -func ToInt64E(i interface{}) (int64, error) { - i = indirect(i) - - switch s := i.(type) { - case int: - return int64(s), nil - case int64: - return s, nil - case int32: - return int64(s), nil - case int16: - return int64(s), nil - case int8: - return int64(s), nil - case uint: - return int64(s), nil - case uint64: - return int64(s), nil - case uint32: - return int64(s), nil - case uint16: - return int64(s), nil - case uint8: - return int64(s), nil - case float64: - return int64(s), nil - case float32: - return int64(s), nil - case string: - v, err := strconv.ParseInt(s, 0, 0) - if err == nil { - return v, nil - } - return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) - } -} - -// ToInt32E casts an interface to an int32 type. -func ToInt32E(i interface{}) (int32, error) { - i = indirect(i) - - switch s := i.(type) { - case int: - return int32(s), nil - case int64: - return int32(s), nil - case int32: - return s, nil - case int16: - return int32(s), nil - case int8: - return int32(s), nil - case uint: - return int32(s), nil - case uint64: - return int32(s), nil - case uint32: - return int32(s), nil - case uint16: - return int32(s), nil - case uint8: - return int32(s), nil - case float64: - return int32(s), nil - case float32: - return int32(s), nil - case string: - v, err := strconv.ParseInt(s, 0, 0) - if err == nil { - return int32(v), nil - } - return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) - } -} - -// ToInt16E casts an interface to an int16 type. -func ToInt16E(i interface{}) (int16, error) { - i = indirect(i) - - switch s := i.(type) { - case int: - return int16(s), nil - case int64: - return int16(s), nil - case int32: - return int16(s), nil - case int16: - return s, nil - case int8: - return int16(s), nil - case uint: - return int16(s), nil - case uint64: - return int16(s), nil - case uint32: - return int16(s), nil - case uint16: - return int16(s), nil - case uint8: - return int16(s), nil - case float64: - return int16(s), nil - case float32: - return int16(s), nil - case string: - v, err := strconv.ParseInt(s, 0, 0) - if err == nil { - return int16(v), nil - } - return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) - } -} - -// ToInt8E casts an interface to an int8 type. -func ToInt8E(i interface{}) (int8, error) { - i = indirect(i) - - switch s := i.(type) { - case int: - return int8(s), nil - case int64: - return int8(s), nil - case int32: - return int8(s), nil - case int16: - return int8(s), nil - case int8: - return s, nil - case uint: - return int8(s), nil - case uint64: - return int8(s), nil - case uint32: - return int8(s), nil - case uint16: - return int8(s), nil - case uint8: - return int8(s), nil - case float64: - return int8(s), nil - case float32: - return int8(s), nil - case string: - v, err := strconv.ParseInt(s, 0, 0) - if err == nil { - return int8(v), nil - } - return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) - } -} - -// ToIntE casts an interface to an int type. +// ToIntE casts an empty interface to an int. func ToIntE(i interface{}) (int, error) { i = indirect(i) + jww.DEBUG.Println("ToIntE called on type:", reflect.TypeOf(i)) switch s := i.(type) { case int: @@ -384,375 +127,23 @@ func ToIntE(i interface{}) (int, error) { return int(s), nil case int8: return int(s), nil - case uint: - return int(s), nil - case uint64: - return int(s), nil - case uint32: - return int(s), nil - case uint16: - return int(s), nil - case uint8: - return int(s), nil - case float64: - return int(s), nil - case float32: - return int(s), nil case string: v, err := strconv.ParseInt(s, 0, 0) if err == nil { return int(v), nil } - return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i) - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i) - } -} - -// ToUintE casts an interface to a uint type. -func ToUintE(i interface{}) (uint, error) { - i = indirect(i) - - switch s := i.(type) { - case string: - v, err := strconv.ParseUint(s, 0, 0) - if err == nil { - return uint(v), nil - } - return 0, fmt.Errorf("unable to cast %#v to uint: %s", i, err) - case int: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint(s), nil - case int64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint(s), nil - case int32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint(s), nil - case int16: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint(s), nil - case int8: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint(s), nil - case uint: - return s, nil - case uint64: - return uint(s), nil - case uint32: - return uint(s), nil - case uint16: - return uint(s), nil - case uint8: - return uint(s), nil - case float64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint(s), nil - case float32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint(s), nil - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i) - } -} - -// ToUint64E casts an interface to a uint64 type. -func ToUint64E(i interface{}) (uint64, error) { - i = indirect(i) - - switch s := i.(type) { - case string: - v, err := strconv.ParseUint(s, 0, 64) - if err == nil { - return v, nil - } - return 0, fmt.Errorf("unable to cast %#v to uint64: %s", i, err) - case int: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint64(s), nil - case int64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint64(s), nil - case int32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint64(s), nil - case int16: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint64(s), nil - case int8: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint64(s), nil - case uint: - return uint64(s), nil - case uint64: - return s, nil - case uint32: - return uint64(s), nil - case uint16: - return uint64(s), nil - case uint8: - return uint64(s), nil - case float32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint64(s), nil + return 0, fmt.Errorf("Unable to Cast %#v to int", i) case float64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint64(s), nil - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i) - } -} - -// ToUint32E casts an interface to a uint32 type. -func ToUint32E(i interface{}) (uint32, error) { - i = indirect(i) - - switch s := i.(type) { - case string: - v, err := strconv.ParseUint(s, 0, 32) - if err == nil { - return uint32(v), nil - } - return 0, fmt.Errorf("unable to cast %#v to uint32: %s", i, err) - case int: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint32(s), nil - case int64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint32(s), nil - case int32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint32(s), nil - case int16: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint32(s), nil - case int8: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint32(s), nil - case uint: - return uint32(s), nil - case uint64: - return uint32(s), nil - case uint32: - return s, nil - case uint16: - return uint32(s), nil - case uint8: - return uint32(s), nil - case float64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint32(s), nil - case float32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint32(s), nil - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i) - } -} - -// ToUint16E casts an interface to a uint16 type. -func ToUint16E(i interface{}) (uint16, error) { - i = indirect(i) - - switch s := i.(type) { - case string: - v, err := strconv.ParseUint(s, 0, 16) - if err == nil { - return uint16(v), nil - } - return 0, fmt.Errorf("unable to cast %#v to uint16: %s", i, err) - case int: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint16(s), nil - case int64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint16(s), nil - case int32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint16(s), nil - case int16: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint16(s), nil - case int8: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint16(s), nil - case uint: - return uint16(s), nil - case uint64: - return uint16(s), nil - case uint32: - return uint16(s), nil - case uint16: - return s, nil - case uint8: - return uint16(s), nil - case float64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint16(s), nil - case float32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint16(s), nil - case bool: - if s { - return 1, nil - } - return 0, nil - case nil: - return 0, nil - default: - return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i) - } -} - -// ToUint8E casts an interface to a uint type. -func ToUint8E(i interface{}) (uint8, error) { - i = indirect(i) - - switch s := i.(type) { - case string: - v, err := strconv.ParseUint(s, 0, 8) - if err == nil { - return uint8(v), nil - } - return 0, fmt.Errorf("unable to cast %#v to uint8: %s", i, err) - case int: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint8(s), nil - case int64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint8(s), nil - case int32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint8(s), nil - case int16: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint8(s), nil - case int8: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint8(s), nil - case uint: - return uint8(s), nil - case uint64: - return uint8(s), nil - case uint32: - return uint8(s), nil - case uint16: - return uint8(s), nil - case uint8: - return s, nil - case float64: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint8(s), nil - case float32: - if s < 0 { - return 0, errNegativeNotAllowed - } - return uint8(s), nil + return int(s), nil case bool: - if s { + if bool(s) { return 1, nil } return 0, nil case nil: return 0, nil default: - return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i) + return 0, fmt.Errorf("Unable to Cast %#v to int", i) } } @@ -795,9 +186,10 @@ func indirectToStringerOrError(a interface{}) interface{} { return v.Interface() } -// ToStringE casts an interface to a string type. +// ToStringE casts an empty interface to a string. func ToStringE(i interface{}) (string, error) { i = indirectToStringerOrError(i) + jww.DEBUG.Println("ToStringE called on type:", reflect.TypeOf(i)) switch s := i.(type) { case string: @@ -805,41 +197,15 @@ func ToStringE(i interface{}) (string, error) { case bool: return strconv.FormatBool(s), nil case float64: - return strconv.FormatFloat(s, 'f', -1, 64), nil - case float32: - return strconv.FormatFloat(float64(s), 'f', -1, 32), nil + return strconv.FormatFloat(i.(float64), 'f', -1, 64), nil case int: - return strconv.Itoa(s), nil - case int64: - return strconv.FormatInt(s, 10), nil - case int32: - return strconv.Itoa(int(s)), nil - case int16: - return strconv.FormatInt(int64(s), 10), nil - case int8: - return strconv.FormatInt(int64(s), 10), nil - case uint: - return strconv.FormatInt(int64(s), 10), nil - case uint64: - return strconv.FormatInt(int64(s), 10), nil - case uint32: - return strconv.FormatInt(int64(s), 10), nil - case uint16: - return strconv.FormatInt(int64(s), 10), nil - case uint8: - return strconv.FormatInt(int64(s), 10), nil + return strconv.FormatInt(int64(i.(int)), 10), nil case []byte: return string(s), nil case template.HTML: return string(s), nil case template.URL: return string(s), nil - case template.JS: - return string(s), nil - case template.CSS: - return string(s), nil - case template.HTMLAttr: - return string(s), nil case nil: return "", nil case fmt.Stringer: @@ -847,12 +213,14 @@ func ToStringE(i interface{}) (string, error) { case error: return s.Error(), nil default: - return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i) + return "", fmt.Errorf("Unable to Cast %#v to string", i) } } -// ToStringMapStringE casts an interface to a map[string]string type. +// ToStringMapStringE casts an empty interface to a map[string]string. func ToStringMapStringE(i interface{}) (map[string]string, error) { + jww.DEBUG.Println("ToStringMapStringE called on type:", reflect.TypeOf(i)) + var m = map[string]string{} switch v := i.(type) { @@ -873,16 +241,15 @@ func ToStringMapStringE(i interface{}) (map[string]string, error) { m[ToString(k)] = ToString(val) } return m, nil - case string: - err := jsonStringToObject(v, &m) - return m, err default: - return m, fmt.Errorf("unable to cast %#v of type %T to map[string]string", i, i) + return m, fmt.Errorf("Unable to Cast %#v to map[string]string", i) } } -// ToStringMapStringSliceE casts an interface to a map[string][]string type. +// ToStringMapStringSliceE casts an empty interface to a map[string][]string. func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) { + jww.DEBUG.Println("ToStringMapStringSliceE called on type:", reflect.TypeOf(i)) + var m = map[string][]string{} switch v := i.(type) { @@ -899,14 +266,7 @@ func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) { } case map[string]interface{}: for k, val := range v { - switch vt := val.(type) { - case []interface{}: - m[ToString(k)] = ToStringSlice(vt) - case []string: - m[ToString(k)] = vt - default: - m[ToString(k)] = []string{ToString(val)} - } + m[ToString(k)] = []string{ToString(val)} } return m, nil case map[interface{}][]string: @@ -928,25 +288,24 @@ func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) { for k, val := range v { key, err := ToStringE(k) if err != nil { - return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) + return m, fmt.Errorf("Unable to Cast %#v to map[string][]string", i) } value, err := ToStringSliceE(val) if err != nil { - return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) + return m, fmt.Errorf("Unable to Cast %#v to map[string][]string", i) } m[key] = value } - case string: - err := jsonStringToObject(v, &m) - return m, err default: - return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) + return m, fmt.Errorf("Unable to Cast %#v to map[string][]string", i) } return m, nil } -// ToStringMapBoolE casts an interface to a map[string]bool type. +// ToStringMapBoolE casts an empty interface to a map[string]bool. func ToStringMapBoolE(i interface{}) (map[string]bool, error) { + jww.DEBUG.Println("ToStringMapBoolE called on type:", reflect.TypeOf(i)) + var m = map[string]bool{} switch v := i.(type) { @@ -962,16 +321,15 @@ func ToStringMapBoolE(i interface{}) (map[string]bool, error) { return m, nil case map[string]bool: return v, nil - case string: - err := jsonStringToObject(v, &m) - return m, err default: - return m, fmt.Errorf("unable to cast %#v of type %T to map[string]bool", i, i) + return m, fmt.Errorf("Unable to Cast %#v to map[string]bool", i) } } -// ToStringMapE casts an interface to a map[string]interface{} type. +// ToStringMapE casts an empty interface to a map[string]interface{}. func ToStringMapE(i interface{}) (map[string]interface{}, error) { + jww.DEBUG.Println("ToStringMapE called on type:", reflect.TypeOf(i)) + var m = map[string]interface{}{} switch v := i.(type) { @@ -982,62 +340,37 @@ func ToStringMapE(i interface{}) (map[string]interface{}, error) { return m, nil case map[string]interface{}: return v, nil - case string: - err := jsonStringToObject(v, &m) - return m, err default: - return m, fmt.Errorf("unable to cast %#v of type %T to map[string]interface{}", i, i) + return m, fmt.Errorf("Unable to Cast %#v to map[string]interface{}", i) } } -// ToSliceE casts an interface to a []interface{} type. +// ToSliceE casts an empty interface to a []interface{}. func ToSliceE(i interface{}) ([]interface{}, error) { + jww.DEBUG.Println("ToSliceE called on type:", reflect.TypeOf(i)) + var s []interface{} switch v := i.(type) { case []interface{}: - return append(s, v...), nil - case []map[string]interface{}: for _, u := range v { s = append(s, u) } return s, nil - default: - return s, fmt.Errorf("unable to cast %#v of type %T to []interface{}", i, i) - } -} - -// ToBoolSliceE casts an interface to a []bool type. -func ToBoolSliceE(i interface{}) ([]bool, error) { - if i == nil { - return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) - } - - switch v := i.(type) { - case []bool: - return v, nil - } - - kind := reflect.TypeOf(i).Kind() - switch kind { - case reflect.Slice, reflect.Array: - s := reflect.ValueOf(i) - a := make([]bool, s.Len()) - for j := 0; j < s.Len(); j++ { - val, err := ToBoolE(s.Index(j).Interface()) - if err != nil { - return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) - } - a[j] = val + case []map[string]interface{}: + for _, u := range v { + s = append(s, u) } - return a, nil + return s, nil default: - return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) + return s, fmt.Errorf("Unable to Cast %#v of type %v to []interface{}", i, reflect.TypeOf(i)) } } -// ToStringSliceE casts an interface to a []string type. +// ToStringSliceE casts an empty interface to a []string. func ToStringSliceE(i interface{}) ([]string, error) { + jww.DEBUG.Println("ToStringSliceE called on type:", reflect.TypeOf(i)) + var a []string switch v := i.(type) { @@ -1053,18 +386,20 @@ func ToStringSliceE(i interface{}) ([]string, error) { case interface{}: str, err := ToStringE(v) if err != nil { - return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i) + return a, fmt.Errorf("Unable to Cast %#v to []string", i) } return []string{str}, nil default: - return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i) + return a, fmt.Errorf("Unable to Cast %#v to []string", i) } } -// ToIntSliceE casts an interface to a []int type. +// ToIntSliceE casts an empty interface to a []int. func ToIntSliceE(i interface{}) ([]int, error) { + jww.DEBUG.Println("ToIntSliceE called on type:", reflect.TypeOf(i)) + if i == nil { - return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) + return []int{}, fmt.Errorf("Unable to Cast %#v to []int", i) } switch v := i.(type) { @@ -1080,48 +415,17 @@ func ToIntSliceE(i interface{}) ([]int, error) { for j := 0; j < s.Len(); j++ { val, err := ToIntE(s.Index(j).Interface()) if err != nil { - return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) + return []int{}, fmt.Errorf("Unable to Cast %#v to []int", i) } a[j] = val } return a, nil default: - return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) + return []int{}, fmt.Errorf("Unable to Cast %#v to []int", i) } } -// ToDurationSliceE casts an interface to a []time.Duration type. -func ToDurationSliceE(i interface{}) ([]time.Duration, error) { - if i == nil { - return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) - } - - switch v := i.(type) { - case []time.Duration: - return v, nil - } - - kind := reflect.TypeOf(i).Kind() - switch kind { - case reflect.Slice, reflect.Array: - s := reflect.ValueOf(i) - a := make([]time.Duration, s.Len()) - for j := 0; j < s.Len(); j++ { - val, err := ToDurationE(s.Index(j).Interface()) - if err != nil { - return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) - } - a[j] = val - } - return a, nil - default: - return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) - } -} - -// StringToDate attempts to parse a string into a time.Time type using a -// predefined list of formats. If no suitable format is found, an error is -// returned. +// StringToDate casts an empty interface to a time.Time. func StringToDate(s string) (time.Time, error) { return parseDateWith(s, []string{ time.RFC3339, @@ -1130,22 +434,15 @@ func StringToDate(s string) (time.Time, error) { time.RFC1123, time.RFC822Z, time.RFC822, - time.RFC850, time.ANSIC, time.UnixDate, time.RubyDate, - "2006-01-02 15:04:05.999999999 -0700 MST", // Time.String() + "2006-01-02 15:04:05Z07:00", + "02 Jan 06 15:04 MST", "2006-01-02", "02 Jan 2006", "2006-01-02 15:04:05 -07:00", "2006-01-02 15:04:05 -0700", - "2006-01-02 15:04:05Z07:00", // RFC3339 without T - "2006-01-02 15:04:05", - time.Kitchen, - time.Stamp, - time.StampMilli, - time.StampMicro, - time.StampNano, }) } @@ -1155,12 +452,5 @@ func parseDateWith(s string, dates []string) (d time.Time, e error) { return } } - return d, fmt.Errorf("unable to parse date: %s", s) -} - -// jsonStringToObject attempts to unmarshall a string as JSON into -// the object passed as pointer. -func jsonStringToObject(s string, v interface{}) error { - data := []byte(s) - return json.Unmarshal(data, v) + return d, fmt.Errorf("Unable to parse date: %s", s) } diff --git a/vendor/github.com/spf13/pflag/verify/all.sh b/vendor/github.com/spf13/pflag/verify/all.sh old mode 100755 new mode 100644 diff --git a/vendor/github.com/spf13/pflag/verify/gofmt.sh b/vendor/github.com/spf13/pflag/verify/gofmt.sh old mode 100755 new mode 100644 diff --git a/vendor/github.com/spf13/pflag/verify/golint.sh b/vendor/github.com/spf13/pflag/verify/golint.sh old mode 100755 new mode 100644 diff --git a/vendor/github.com/spf13/viper/.gitignore b/vendor/github.com/spf13/viper/.gitignore index 352a34a56..836562412 100644 --- a/vendor/github.com/spf13/viper/.gitignore +++ b/vendor/github.com/spf13/viper/.gitignore @@ -21,4 +21,3 @@ _testmain.go *.exe *.test -*.bench \ No newline at end of file diff --git a/vendor/github.com/spf13/viper/.travis.yml b/vendor/github.com/spf13/viper/.travis.yml index fa3980550..9e6d62116 100644 --- a/vendor/github.com/spf13/viper/.travis.yml +++ b/vendor/github.com/spf13/viper/.travis.yml @@ -1,27 +1,9 @@ -go_import_path: github.com/spf13/viper - language: go go: - - 1.9.x - - 1.10.x + - 1.3 + - release - tip -os: - - linux - - osx - -matrix: - allow_failures: - - go: tip - fast_finish: true - script: - - go install ./... - - diff -u <(echo -n) <(gofmt -d .) - go test -v ./... - -after_success: - - go get -u -d github.com/spf13/hugo - - cd $GOPATH/src/github.com/spf13/hugo && make && ./hugo -s docs && cd - - sudo: false diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index d75282228..cad242d1b 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -6,19 +6,18 @@ Many Go projects are built using Viper including: * [Hugo](http://gohugo.io) * [EMC RexRay](http://rexray.readthedocs.org/en/stable/) -* [Imgur’s Incus](https://github.com/Imgur/incus) +* [Imgur's Incus](https://github.com/Imgur/incus) * [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) * [Docker Notary](https://github.com/docker/Notary) * [BloomApi](https://www.bloomapi.com/) -* [doctl](https://github.com/digitalocean/doctl) -* [Clairctl](https://github.com/jgsqware/clairctl) +* [DOIt](https://github.com/bryanl/doit) -[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper) + [![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## What is Viper? -Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed +Viper is a complete configuration solution for go applications including 12 factor apps. It is designed to work within an application, and can handle all types of configuration needs and formats. It supports: @@ -69,7 +68,7 @@ Viper configuration keys are case insensitive. ### Establishing Defaults A good configuration system will support default values. A default value is not -required for a key, but it’s useful in the event that a key hasn’t been set via +required for a key, but it's useful in the event that a key hasn’t been set via config file, environment variable, remote configuration or flag. Examples: @@ -111,16 +110,16 @@ Gone are the days of needing to restart a server to have a config take effect, viper powered applications can read an update to a config file while running and not miss a beat. -Simply tell the viper instance to watchConfig. +Simply tell the viper instance to watchConfig. Optionally you can provide a function for Viper to run each time a change occurs. **Make sure you add all of the configPaths prior to calling `WatchConfig()`** ```go -viper.WatchConfig() -viper.OnConfigChange(func(e fsnotify.Event) { - fmt.Println("Config file changed:", e.Name) -}) + viper.WatchConfig() + viper.OnConfigChange(func(e fsnotify.Event) { + fmt.Println("Config file changed:", e.Name) + }) ``` ### Reading Config from io.Reader @@ -185,13 +184,13 @@ with ENV: * `AutomaticEnv()` * `BindEnv(string...) : error` * `SetEnvPrefix(string)` - * `SetEnvKeyReplacer(string...) *strings.Replacer` + * `SetEnvReplacer(string...) *strings.Replacer` _When working with ENV variables, it’s important to recognize that Viper treats ENV variables as case sensitive._ Viper provides a mechanism to try to ensure that ENV variables are unique. By -using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from +using `SetEnvPrefix`, you can tell Viper to use add a prefix while reading from the environment variables. Both `BindEnv` and `AutomaticEnv` will use this prefix. @@ -212,7 +211,7 @@ time a `viper.Get` request is made. It will apply the following rules. It will check for a environment variable with a name matching the key uppercased and prefixed with the `EnvPrefix` if set. -`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env +`SetEnvReplacer` allows you to use a `strings.Replacer` object to rewrite Env keys to an extent. This is useful if you want to use `-` or something in your `Get()` calls, but want your environmental variables to use `_` delimiters. An example of using it can be found in `viper_test.go`. @@ -237,7 +236,7 @@ Like `BindEnv`, the value is not set when the binding method is called, but when it is accessed. This means you can bind as early as you want, even in an `init()` function. -For individual flags, the `BindPFlag()` method provides this functionality. +The `BindPFlag()` method provides this functionality. Example: @@ -246,19 +245,6 @@ serverCmd.Flags().Int("port", 1138, "Port to run Application server on") viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) ``` -You can also bind an existing set of pflags (pflag.FlagSet): - -Example: - -```go -pflag.Int("flagname", 1234, "help message for flagname") - -pflag.Parse() -viper.BindPFlags(pflag.CommandLine) - -i := viper.GetInt("flagname") // retrieve values from viper instead of pflag -``` - The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude the use of other packages that use the [flag](https://golang.org/pkg/flag/) package from the standard library. The pflag package can handle the flags @@ -277,32 +263,24 @@ import ( ) func main() { - - // using standard library "flag" package - flag.Int("flagname", 1234, "help message for flagname") - pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() - viper.BindPFlags(pflag.CommandLine) - - i := viper.GetInt("flagname") // retrieve value from viper - - ... + ... } ``` #### Flag interfaces -Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`. +Viper provides two Go interfaces to bind other flag systems if you don't use `Pflags`. `FlagValue` represents a single flag. This is a very simple example on how to implement this interface: ```go type myFlag struct {} -func (f myFlag) HasChanged() bool { return false } -func (f myFlag) Name() string { return "my-flag-name" } -func (f myFlag) ValueString() string { return "my-flag-value" } -func (f myFlag) ValueType() string { return "string" } +func (f myFlag) IsChanged() { return false } +func (f myFlag) Name() { return "my-flag-name" } +func (f myFlag) ValueString() { return "my-flag-value" } +func (f myFlag) ValueType() { return "string" } ``` Once your flag implements this interface, you can simply tell Viper to bind it: @@ -320,7 +298,7 @@ type myFlagSet struct { func (f myFlagSet) VisitAll(fn func(FlagValue)) { for _, flag := range flags { - fn(flag) + fn(flag) } } ``` @@ -423,7 +401,7 @@ go func(){ ## Getting Values From Viper -In Viper, there are a few ways to get a value depending on the value’s type. +In Viper, there are a few ways to get a value depending on the value's type. The following functions and methods exist: * `Get(key string) : interface{}` @@ -437,7 +415,6 @@ The following functions and methods exist: * `GetTime(key string) : time.Time` * `GetDuration(key string) : time.Duration` * `IsSet(key string) : bool` - * `AllSettings() : map[string]interface{}` One important thing to recognize is that each Get function will return a zero value if it’s not found. To check if a given key exists, the `IsSet()` method @@ -481,17 +458,16 @@ Viper can access a nested field by passing a `.` delimited path of keys: GetString("datastore.metric.host") // (returns "127.0.0.1") ``` -This obeys the precedence rules established above; the search for the path -will cascade through the remaining configuration registries until found. +This obeys the precedence rules established above; the search for the root key +(in this example, `datastore`) will cascade through the remaining configuration +registries until found. The search for the sub-keys (`metric` and `host`), +however, will not. -For example, given this configuration file, both `datastore.metric.host` and -`datastore.metric.port` are already defined (and may be overridden). If in addition -`datastore.metric.protocol` was defined in the defaults, Viper would also find it. +For example, if the `metric` key was not defined in the configuration loaded +from file, but was defined in the defaults, Viper would return the zero value. -However, if `datastore.metric` was overridden (by a flag, an environment variable, -the `Set()` method, …) with an immediate value, then all sub-keys of -`datastore.metric` become undefined, they are “shadowed” by the higher-priority -configuration level. +On the other hand, if the primary key was not defined, Viper would go through +the remaining registries looking for it. Lastly, if there exists a key that matches the delimited key path, its value will be returned instead. E.g. @@ -515,7 +491,7 @@ will be returned instead. E.g. } } -GetString("datastore.metric.host") // returns "0.0.0.0" +GetString("datastore.metric.host") //returns "0.0.0.0" ``` ### Extract sub-tree @@ -554,7 +530,7 @@ func NewCache(cfg *Viper) *Cache {...} ``` which creates a cache based on config information formatted as `subv`. -Now it’s easy to create these 2 caches separately as: +Now it's easy to create these 2 caches separately as: ```go cfg1 := viper.Sub("app.cache1") @@ -591,27 +567,6 @@ if err != nil { } ``` -### Marshalling to string - -You may need to marhsal all the settings held in viper into a string rather than write them to a file. -You can use your favorite format's marshaller with the config returned by `AllSettings()`. - -```go -import ( - yaml "gopkg.in/yaml.v2" - // ... -) - -func yamlStringSettings() string { - c := viper.AllSettings() - bs, err := yaml.Marshal(c) - if err != nil { - t.Fatalf("unable to marshal config to YAML: %v", err) - } - return string(bs) -} -``` - ## Viper or Vipers? Viper comes ready to use out of the box. There is no configuration or @@ -619,13 +574,13 @@ initialization needed to begin using Viper. Since most applications will want to use a single central repository for their configuration, the viper package provides this. It is similar to a singleton. -In all of the examples above, they demonstrate using viper in its singleton +In all of the examples above, they demonstrate using viper in it's singleton style approach. ### Working with multiple vipers You can also create many different vipers for use in your application. Each will -have its own unique set of configurations and values. Each can read from a +have it’s own unique set of configurations and values. Each can read from a different config file, key value store, etc. All of the functions that viper package supports are mirrored as methods on a viper. diff --git a/vendor/github.com/spf13/viper/flags_test.go b/vendor/github.com/spf13/viper/flags_test.go index 0b976b605..5489278db 100644 --- a/vendor/github.com/spf13/viper/flags_test.go +++ b/vendor/github.com/spf13/viper/flags_test.go @@ -22,7 +22,7 @@ func TestBindFlagValueSet(t *testing.T) { "endpoint": "/public", } - for name := range testValues { + for name, _ := range testValues { testValues[name] = flagSet.String(name, "", "test") } @@ -62,4 +62,5 @@ func TestBindFlagValue(t *testing.T) { flag.Changed = true //hack for pflag usage assert.Equal(t, "testing_mutate", Get("testvalue")) + } diff --git a/vendor/github.com/spf13/viper/overrides_test.go b/vendor/github.com/spf13/viper/overrides_test.go deleted file mode 100644 index dd2aa9b0d..000000000 --- a/vendor/github.com/spf13/viper/overrides_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package viper - -import ( - "fmt" - "strings" - "testing" - - "github.com/spf13/cast" - "github.com/stretchr/testify/assert" -) - -type layer int - -const ( - defaultLayer layer = iota + 1 - overrideLayer -) - -func TestNestedOverrides(t *testing.T) { - assert := assert.New(t) - var v *Viper - - // Case 0: value overridden by a value - overrideDefault(assert, "tom", 10, "tom", 20) // "tom" is first given 10 as default value, then overridden by 20 - override(assert, "tom", 10, "tom", 20) // "tom" is first given value 10, then overridden by 20 - overrideDefault(assert, "tom.age", 10, "tom.age", 20) - override(assert, "tom.age", 10, "tom.age", 20) - overrideDefault(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20) - override(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20) - - // Case 1: key:value overridden by a value - v = overrideDefault(assert, "tom.age", 10, "tom", "boy") // "tom.age" is first given 10 as default value, then "tom" is overridden by "boy" - assert.Nil(v.Get("tom.age")) // "tom.age" should not exist anymore - v = override(assert, "tom.age", 10, "tom", "boy") - assert.Nil(v.Get("tom.age")) - - // Case 2: value overridden by a key:value - overrideDefault(assert, "tom", "boy", "tom.age", 10) // "tom" is first given "boy" as default value, then "tom" is overridden by map{"age":10} - override(assert, "tom.age", 10, "tom", "boy") - - // Case 3: key:value overridden by a key:value - v = overrideDefault(assert, "tom.size", 4, "tom.age", 10) - assert.Equal(4, v.Get("tom.size")) // value should still be reachable - v = override(assert, "tom.size", 4, "tom.age", 10) - assert.Equal(4, v.Get("tom.size")) - deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4) - - // Case 4: key:value overridden by a map - v = overrideDefault(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10} - assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable - assert.Equal(10, v.Get("tom.age")) // new value should be there - deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there - v = override(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) - assert.Nil(v.Get("tom.size")) - assert.Equal(10, v.Get("tom.age")) - deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) - - // Case 5: array overridden by a value - overrideDefault(assert, "tom", []int{10, 20}, "tom", 30) - override(assert, "tom", []int{10, 20}, "tom", 30) - overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", 30) - override(assert, "tom.age", []int{10, 20}, "tom.age", 30) - - // Case 6: array overridden by an array - overrideDefault(assert, "tom", []int{10, 20}, "tom", []int{30, 40}) - override(assert, "tom", []int{10, 20}, "tom", []int{30, 40}) - overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40}) - v = override(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40}) - // explicit array merge: - s, ok := v.Get("tom.age").([]int) - if assert.True(ok, "tom[\"age\"] is not a slice") { - v.Set("tom.age", append(s, []int{50, 60}...)) - assert.Equal([]int{30, 40, 50, 60}, v.Get("tom.age")) - deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, []int{30, 40, 50, 60}) - } -} - -func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { - return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue) -} -func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { - return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue) -} - -// overrideFromLayer performs the sequential override and low-level checks. -// -// First assignment is made on layer l for path firstPath with value firstValue, -// the second one on the override layer (i.e., with the Set() function) -// for path secondPath with value secondValue. -// -// firstPath and secondPath can include an arbitrary number of dots to indicate -// a nested element. -// -// After each assignment, the value is checked, retrieved both by its full path -// and by its key sequence (successive maps). -func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper { - v := New() - firstKeys := strings.Split(firstPath, v.keyDelim) - if assert == nil || - len(firstKeys) == 0 || len(firstKeys[0]) == 0 { - return v - } - - // Set and check first value - switch l { - case defaultLayer: - v.SetDefault(firstPath, firstValue) - case overrideLayer: - v.Set(firstPath, firstValue) - default: - return v - } - assert.Equal(firstValue, v.Get(firstPath)) - deepCheckValue(assert, v, l, firstKeys, firstValue) - - // Override and check new value - secondKeys := strings.Split(secondPath, v.keyDelim) - if len(secondKeys) == 0 || len(secondKeys[0]) == 0 { - return v - } - v.Set(secondPath, secondValue) - assert.Equal(secondValue, v.Get(secondPath)) - deepCheckValue(assert, v, overrideLayer, secondKeys, secondValue) - - return v -} - -// deepCheckValue checks that all given keys correspond to a valid path in the -// configuration map of the given layer, and that the final value equals the one given -func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value interface{}) { - if assert == nil || v == nil || - len(keys) == 0 || len(keys[0]) == 0 { - return - } - - // init - var val interface{} - var ms string - switch l { - case defaultLayer: - val = v.defaults - ms = "v.defaults" - case overrideLayer: - val = v.override - ms = "v.override" - } - - // loop through map - var m map[string]interface{} - err := false - for _, k := range keys { - if val == nil { - assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms)) - return - } - - // deep scan of the map to get the final value - switch val.(type) { - case map[interface{}]interface{}: - m = cast.ToStringMap(val) - case map[string]interface{}: - m = val.(map[string]interface{}) - default: - assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms)) - return - } - ms = ms + "[\"" + k + "\"]" - val = m[k] - } - if !err { - assert.Equal(value, val) - } -} diff --git a/vendor/github.com/spf13/viper/remote/remote.go b/vendor/github.com/spf13/viper/remote/remote.go index 810d0702e..faaf3b366 100644 --- a/vendor/github.com/spf13/viper/remote/remote.go +++ b/vendor/github.com/spf13/viper/remote/remote.go @@ -8,11 +8,10 @@ package remote import ( "bytes" - "io" - "os" - "github.com/spf13/viper" crypt "github.com/xordataexchange/crypt/config" + "io" + "os" ) type remoteConfigProvider struct{} @@ -34,45 +33,17 @@ func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) if err != nil { return nil, err } - resp, err := cm.Get(rp.Path()) + resp := <-cm.Watch(rp.Path(), nil) + err = resp.Error if err != nil { return nil, err } - return bytes.NewReader(resp), nil -} - -func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) { - cm, err := getConfigManager(rp) - if err != nil { - return nil, nil - } - quit := make(chan bool) - quitwc := make(chan bool) - viperResponsCh := make(chan *viper.RemoteResponse) - cryptoResponseCh := cm.Watch(rp.Path(), quit) - // need this function to convert the Channel response form crypt.Response to viper.Response - go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) { - for { - select { - case <-quitwc: - quit <- true - return - case resp := <-cr: - vr <- &viper.RemoteResponse{ - Error: resp.Error, - Value: resp.Value, - } - - } - - } - }(cryptoResponseCh, viperResponsCh, quitwc, quit) - - return viperResponsCh, quitwc + return bytes.NewReader(resp.Value), nil } func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) { + var cm crypt.ConfigManager var err error @@ -98,6 +69,7 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) { return nil, err } return cm, nil + } func init() { diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go index 952cad44c..0cc455346 100644 --- a/vendor/github.com/spf13/viper/util.go +++ b/vendor/github.com/spf13/viper/util.go @@ -11,80 +11,41 @@ package viper import ( + "bytes" + "encoding/json" "fmt" + "io" "os" "path/filepath" "runtime" "strings" "unicode" - "github.com/spf13/afero" + "github.com/BurntSushi/toml" + "github.com/hashicorp/hcl" + "github.com/magiconair/properties" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" + "gopkg.in/yaml.v2" ) -// ConfigParseError denotes failing to parse configuration file. +// Denotes failing to parse configuration file. type ConfigParseError struct { err error } -// Error returns the formatted configuration error. +// Returns the formatted configuration error. func (pe ConfigParseError) Error() string { return fmt.Sprintf("While parsing config: %s", pe.err.Error()) } -// toCaseInsensitiveValue checks if the value is a map; -// if so, create a copy and lower-case the keys recursively. -func toCaseInsensitiveValue(value interface{}) interface{} { - switch v := value.(type) { - case map[interface{}]interface{}: - value = copyAndInsensitiviseMap(cast.ToStringMap(v)) - case map[string]interface{}: - value = copyAndInsensitiviseMap(v) - } - - return value -} - -// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of -// any map it makes case insensitive. -func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} { - nm := make(map[string]interface{}) - - for key, val := range m { - lkey := strings.ToLower(key) - switch v := val.(type) { - case map[interface{}]interface{}: - nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v)) - case map[string]interface{}: - nm[lkey] = copyAndInsensitiviseMap(v) - default: - nm[lkey] = v - } - } - - return nm -} - func insensitiviseMap(m map[string]interface{}) { for key, val := range m { - switch val.(type) { - case map[interface{}]interface{}: - // nested map: cast and recursively insensitivise - val = cast.ToStringMap(val) - insensitiviseMap(val.(map[string]interface{})) - case map[string]interface{}: - // nested map: recursively insensitivise - insensitiviseMap(val.(map[string]interface{})) - } - lower := strings.ToLower(key) if key != lower { - // remove old key (not lower-cased) delete(m, key) + m[lower] = val } - // update map - m[lower] = val } } @@ -107,16 +68,16 @@ func absPathify(inPath string) string { p, err := filepath.Abs(inPath) if err == nil { return filepath.Clean(p) + } else { + jww.ERROR.Println("Couldn't discover absolute path") + jww.ERROR.Println(err) } - - jww.ERROR.Println("Couldn't discover absolute path") - jww.ERROR.Println(err) return "" } // Check if File / Directory Exists -func exists(fs afero.Fs, path string) (bool, error) { - _, err := fs.Stat(path) +func exists(path string) (bool, error) { + _, err := os.Stat(path) if err == nil { return true, nil } @@ -146,6 +107,74 @@ func userHomeDir() string { return os.Getenv("HOME") } +func findCWD() (string, error) { + serverFile, err := filepath.Abs(os.Args[0]) + + if err != nil { + return "", fmt.Errorf("Can't get absolute path for executable: %v", err) + } + + path := filepath.Dir(serverFile) + realFile, err := filepath.EvalSymlinks(serverFile) + + if err != nil { + if _, err = os.Stat(serverFile + ".exe"); err == nil { + realFile = filepath.Clean(serverFile + ".exe") + } + } + + if err == nil && realFile != serverFile { + path = filepath.Dir(realFile) + } + + return path, nil +} + +func unmarshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error { + buf := new(bytes.Buffer) + buf.ReadFrom(in) + + switch strings.ToLower(configType) { + case "yaml", "yml": + if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil { + return ConfigParseError{err} + } + + case "json": + if err := json.Unmarshal(buf.Bytes(), &c); err != nil { + return ConfigParseError{err} + } + + case "hcl": + obj, err := hcl.Parse(string(buf.Bytes())) + if err != nil { + return ConfigParseError{err} + } + if err = hcl.DecodeObject(&c, obj); err != nil { + return ConfigParseError{err} + } + + case "toml": + if _, err := toml.Decode(buf.String(), &c); err != nil { + return ConfigParseError{err} + } + + case "properties", "props", "prop": + var p *properties.Properties + var err error + if p, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil { + return ConfigParseError{err} + } + for _, key := range p.Keys() { + value, _ := p.Get(key) + c[key] = value + } + } + + insensitiviseMap(c) + return nil +} + func safeMul(a, b uint) uint { c := a * b if a > 1 && b > 1 && c/b != a { @@ -188,34 +217,3 @@ func parseSizeInBytes(sizeStr string) uint { return safeMul(uint(size), multiplier) } - -// deepSearch scans deep maps, following the key indexes listed in the -// sequence "path". -// The last value is expected to be another map, and is returned. -// -// In case intermediate keys do not exist, or map to a non-map value, -// a new map is created and inserted, and the search continues from there: -// the initial map "m" may be modified! -func deepSearch(m map[string]interface{}, path []string) map[string]interface{} { - for _, k := range path { - m2, ok := m[k] - if !ok { - // intermediate key does not exist - // => create it and continue from there - m3 := make(map[string]interface{}) - m[k] = m3 - m = m3 - continue - } - m3, ok := m2.(map[string]interface{}) - if !ok { - // intermediate key is a value - // => replace with a new map - m3 = make(map[string]interface{}) - m[k] = m3 - } - // continue search from here - m = m3 - } - return m -} diff --git a/vendor/github.com/spf13/viper/util_test.go b/vendor/github.com/spf13/viper/util_test.go deleted file mode 100644 index 0af80bb63..000000000 --- a/vendor/github.com/spf13/viper/util_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © 2016 Steve Francia . -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// Viper is a application configuration system. -// It believes that applications can be configured a variety of ways -// via flags, ENVIRONMENT variables, configuration files retrieved -// from the file system, or a remote key/value store. - -package viper - -import ( - "reflect" - "testing" -) - -func TestCopyAndInsensitiviseMap(t *testing.T) { - var ( - given = map[string]interface{}{ - "Foo": 32, - "Bar": map[interface{}]interface { - }{ - "ABc": "A", - "cDE": "B"}, - } - expected = map[string]interface{}{ - "foo": 32, - "bar": map[string]interface { - }{ - "abc": "A", - "cde": "B"}, - } - ) - - got := copyAndInsensitiviseMap(given) - - if !reflect.DeepEqual(got, expected) { - t.Fatalf("Got %q\nexpected\n%q", got, expected) - } - - if _, ok := given["foo"]; ok { - t.Fatal("Input map changed") - } - - if _, ok := given["bar"]; ok { - t.Fatal("Input map changed") - } - - m := given["Bar"].(map[interface{}]interface{}) - if _, ok := m["ABc"]; !ok { - t.Fatal("Input map changed") - } -} diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index f657b201d..7a49a0a9f 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -21,10 +21,9 @@ package viper import ( "bytes" - "encoding/csv" - "encoding/json" "fmt" "io" + "io/ioutil" "log" "os" "path/filepath" @@ -32,37 +31,16 @@ import ( "strings" "time" - yaml "gopkg.in/yaml.v2" - - "github.com/fsnotify/fsnotify" - "github.com/hashicorp/hcl" - "github.com/hashicorp/hcl/hcl/printer" - "github.com/magiconair/properties" + "github.com/kr/pretty" "github.com/mitchellh/mapstructure" - toml "github.com/pelletier/go-toml" - "github.com/spf13/afero" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/pflag" + "gopkg.in/fsnotify.v1" ) -// ConfigMarshalError happens when failing to marshal the configuration. -type ConfigMarshalError struct { - err error -} - -// Error returns the formatted configuration error. -func (e ConfigMarshalError) Error() string { - return fmt.Sprintf("While marshaling config: %s", e.err.Error()) -} - var v *Viper -type RemoteResponse struct { - Value []byte - Error error -} - func init() { v = New() } @@ -70,66 +48,49 @@ func init() { type remoteConfigFactory interface { Get(rp RemoteProvider) (io.Reader, error) Watch(rp RemoteProvider) (io.Reader, error) - WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) } // RemoteConfig is optional, see the remote package var RemoteConfig remoteConfigFactory -// UnsupportedConfigError denotes encountering an unsupported +// Denotes encountering an unsupported // configuration filetype. type UnsupportedConfigError string -// Error returns the formatted configuration error. +// Returns the formatted configuration error. func (str UnsupportedConfigError) Error() string { return fmt.Sprintf("Unsupported Config Type %q", string(str)) } -// UnsupportedRemoteProviderError denotes encountering an unsupported remote -// provider. Currently only etcd and Consul are supported. +// Denotes encountering an unsupported remote +// provider. Currently only etcd and Consul are +// supported. type UnsupportedRemoteProviderError string -// Error returns the formatted remote provider error. +// Returns the formatted remote provider error. func (str UnsupportedRemoteProviderError) Error() string { return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str)) } -// RemoteConfigError denotes encountering an error while trying to +// Denotes encountering an error while trying to // pull the configuration from the remote provider. type RemoteConfigError string -// Error returns the formatted remote provider error +// Returns the formatted remote provider error func (rce RemoteConfigError) Error() string { return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) } -// ConfigFileNotFoundError denotes failing to find configuration file. +// Denotes failing to find configuration file. type ConfigFileNotFoundError struct { name, locations string } -// Error returns the formatted configuration error. +// Returns the formatted configuration error. func (fnfe ConfigFileNotFoundError) Error() string { return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) } -// A DecoderConfigOption can be passed to viper.Unmarshal to configure -// mapstructure.DecoderConfig options -type DecoderConfigOption func(*mapstructure.DecoderConfig) - -// DecodeHook returns a DecoderConfigOption which overrides the default -// DecoderConfig.DecodeHook value, the default is: -// -// mapstructure.ComposeDecodeHookFunc( -// mapstructure.StringToTimeDurationHookFunc(), -// mapstructure.StringToSliceHookFunc(","), -// ) -func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption { - return func(c *mapstructure.DecoderConfig) { - c.DecodeHook = hook - } -} - // Viper is a prioritized configuration registry. It // maintains a set of configuration sources, fetches // values to populate those, and provides them according @@ -147,11 +108,11 @@ func DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOption { // Defaults : { // "secret": "", // "user": "default", -// "endpoint": "https://localhost" +// "endpoint": "https://localhost" // } // Config : { // "user": "root" -// "secret": "defaultsecret" +// "secret": "defaultsecret" // } // Env : { // "secret": "somesecretkey" @@ -172,9 +133,6 @@ type Viper struct { // A set of paths to look for the config file in configPaths []string - // The filesystem to read config from. - fs afero.Fs - // A set of remote providers to search for the configuration remoteProviders []*defaultRemoteProvider @@ -196,19 +154,14 @@ type Viper struct { aliases map[string]string typeByDefValue bool - // Store read properties on the object so that we can write back in order with comments. - // This will only be used if the configuration read is a properties file. - properties *properties.Properties - onConfigChange func(fsnotify.Event) } -// New returns an initialized Viper instance. +// Returns an initialized Viper instance. func New() *Viper { v := new(Viper) v.keyDelim = "." v.configName = "config" - v.fs = afero.NewOsFs() v.config = make(map[string]interface{}) v.override = make(map[string]interface{}) v.defaults = make(map[string]interface{}) @@ -226,7 +179,7 @@ func New() *Viper { // can use it in their testing as well. func Reset() { v = New() - SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} + SupportedExts = []string{"json", "toml", "yaml", "yml", "hcl"} SupportedRemoteProviders = []string{"etcd", "consul"} } @@ -264,11 +217,11 @@ type RemoteProvider interface { SecretKeyring() string } -// SupportedExts are universally supported extensions. -var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} +// Universally supported extensions. +var SupportedExts []string = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl"} -// SupportedRemoteProviders are universally supported remote providers. -var SupportedRemoteProviders = []string{"etcd", "consul"} +// Universally supported remote providers. +var SupportedRemoteProviders []string = []string{"etcd", "consul"} func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { @@ -285,13 +238,7 @@ func (v *Viper) WatchConfig() { defer watcher.Close() // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way - filename, err := v.getConfigFile() - if err != nil { - log.Println("error:", err) - return - } - - configFile := filepath.Clean(filename) + configFile := filepath.Clean(v.getConfigFile()) configDir, _ := filepath.Split(configFile) done := make(chan bool) @@ -320,8 +267,8 @@ func (v *Viper) WatchConfig() { }() } -// SetConfigFile explicitly defines the path, name and extension of the config file. -// Viper will use this and not check any of the config paths. +// Explicitly define the path, name and extension of the config file +// Viper will use this and not check any of the config paths func SetConfigFile(in string) { v.SetConfigFile(in) } func (v *Viper) SetConfigFile(in string) { if in != "" { @@ -329,9 +276,9 @@ func (v *Viper) SetConfigFile(in string) { } } -// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use. -// E.g. if your prefix is "spf", the env registry will look for env -// variables that start with "SPF_". +// Define a prefix that ENVIRONMENT variables will use. +// E.g. if your prefix is "spf", the env registry +// will look for env. variables that start with "SPF_" func SetEnvPrefix(in string) { v.SetEnvPrefix(in) } func (v *Viper) SetEnvPrefix(in string) { if in != "" { @@ -349,11 +296,11 @@ func (v *Viper) mergeWithEnvPrefix(in string) string { // TODO: should getEnv logic be moved into find(). Can generalize the use of // rewriting keys many things, Ex: Get('someKey') -> some_key -// (camel case to snake case for JSON keys perhaps) +// (cammel case to snake case for JSON keys perhaps) -// getEnv is a wrapper around os.Getenv which replaces characters in the original -// key. This allows env vars which have different keys than the config object -// keys. +// getEnv s a wrapper around os.Getenv which replaces characters in the original +// key. This allows env vars which have different keys then the config object +// keys func (v *Viper) getEnv(key string) string { if v.envKeyReplacer != nil { key = v.envKeyReplacer.Replace(key) @@ -361,11 +308,11 @@ func (v *Viper) getEnv(key string) string { return os.Getenv(key) } -// ConfigFileUsed returns the file used to populate the config registry. +// Return the file used to populate the config registry func ConfigFileUsed() string { return v.ConfigFileUsed() } func (v *Viper) ConfigFileUsed() string { return v.configFile } -// AddConfigPath adds a path for Viper to search for the config file in. +// Add a path for Viper to search for the config file in. // Can be called multiple times to define multiple search paths. func AddConfigPath(in string) { v.AddConfigPath(in) } func (v *Viper) AddConfigPath(in string) { @@ -449,22 +396,23 @@ func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool { return false } -// searchMap recursively searches for a value for path in source map. -// Returns nil if not found. -// Note: This assumes that the path entries and map keys are lower cased. func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} { + if len(path) == 0 { return source } - next, ok := source[path[0]] - if ok { - // Fast path - if len(path) == 1 { - return next + var ok bool + var next interface{} + for k, v := range source { + if strings.ToLower(k) == strings.ToLower(path[0]) { + ok = true + next = v + break } + } - // Nested case + if ok { switch next.(type) { case map[interface{}]interface{}: return v.searchMap(cast.ToStringMap(next), path[1:]) @@ -473,126 +421,11 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac // if the type of `next` is the same as the type being asserted return v.searchMap(next.(map[string]interface{}), path[1:]) default: - // got a value but nested key expected, return "nil" for not found - return nil - } - } - return nil -} - -// searchMapWithPathPrefixes recursively searches for a value for path in source map. -// -// While searchMap() considers each path element as a single map key, this -// function searches for, and prioritizes, merged path elements. -// e.g., if in the source, "foo" is defined with a sub-key "bar", and "foo.bar" -// is also defined, this latter value is returned for path ["foo", "bar"]. -// -// This should be useful only at config level (other maps may not contain dots -// in their keys). -// -// Note: This assumes that the path entries and map keys are lower cased. -func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []string) interface{} { - if len(path) == 0 { - return source - } - - // search for path prefixes, starting from the longest one - for i := len(path); i > 0; i-- { - prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim)) - - next, ok := source[prefixKey] - if ok { - // Fast path - if i == len(path) { - return next - } - - // Nested case - var val interface{} - switch next.(type) { - case map[interface{}]interface{}: - val = v.searchMapWithPathPrefixes(cast.ToStringMap(next), path[i:]) - case map[string]interface{}: - // Type assertion is safe here since it is only reached - // if the type of `next` is the same as the type being asserted - val = v.searchMapWithPathPrefixes(next.(map[string]interface{}), path[i:]) - default: - // got a value but nested key expected, do nothing and look for next prefix - } - if val != nil { - return val - } - } - } - - // not found - return nil -} - -// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere -// on its path in the map. -// e.g., if "foo.bar" has a value in the given map, it “shadows” -// "foo.bar.baz" in a lower-priority map -func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string { - var parentVal interface{} - for i := 1; i < len(path); i++ { - parentVal = v.searchMap(m, path[0:i]) - if parentVal == nil { - // not found, no need to add more path elements - return "" - } - switch parentVal.(type) { - case map[interface{}]interface{}: - continue - case map[string]interface{}: - continue - default: - // parentVal is a regular value which shadows "path" - return strings.Join(path[0:i], v.keyDelim) - } - } - return "" -} - -// isPathShadowedInFlatMap makes sure the given path is not shadowed somewhere -// in a sub-path of the map. -// e.g., if "foo.bar" has a value in the given map, it “shadows” -// "foo.bar.baz" in a lower-priority map -func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string { - // unify input map - var m map[string]interface{} - switch mi.(type) { - case map[string]string, map[string]FlagValue: - m = cast.ToStringMap(mi) - default: - return "" - } - - // scan paths - var parentKey string - for i := 1; i < len(path); i++ { - parentKey = strings.Join(path[0:i], v.keyDelim) - if _, ok := m[parentKey]; ok { - return parentKey - } - } - return "" -} - -// isPathShadowedInAutoEnv makes sure the given path is not shadowed somewhere -// in the environment, when automatic env is on. -// e.g., if "foo.bar" has a value in the environment, it “shadows” -// "foo.bar.baz" in a lower-priority map -func (v *Viper) isPathShadowedInAutoEnv(path []string) string { - var parentKey string - var val string - for i := 1; i < len(path); i++ { - parentKey = strings.Join(path[0:i], v.keyDelim) - if val = v.getEnv(v.mergeWithEnvPrefix(parentKey)); val != "" { - return parentKey + return next } + } else { + return nil } - return "" } // SetTypeByDefaultValue enables or disables the inference of a key value's @@ -614,13 +447,8 @@ func (v *Viper) SetTypeByDefaultValue(enable bool) { v.typeByDefValue = enable } -// GetViper gets the global Viper instance. -func GetViper() *Viper { - return v -} - -// Get can retrieve any value given the key to use. -// Get is case-insensitive for a key. +// Viper is essentially repository for configurations +// Get can retrieve any value given the key to use // Get has the behavior of returning the value associated with the first // place from where it is set. Viper will check in the following order: // override, flag, env, config file, key/value store, default @@ -628,132 +456,145 @@ func GetViper() *Viper { // Get returns an interface. For a specific value use one of the Get____ methods. func Get(key string) interface{} { return v.Get(key) } func (v *Viper) Get(key string) interface{} { + path := strings.Split(key, v.keyDelim) + lcaseKey := strings.ToLower(key) val := v.find(lcaseKey) + if val == nil { - return nil + source := v.find(strings.ToLower(path[0])) + if source != nil { + if reflect.TypeOf(source).Kind() == reflect.Map { + val = v.searchMap(cast.ToStringMap(source), path[1:]) + } + } } - if v.typeByDefValue { - // TODO(bep) this branch isn't covered by a single test. - valType := val - path := strings.Split(lcaseKey, v.keyDelim) - defVal := v.searchMap(v.defaults, path) - if defVal != nil { - valType = defVal + // if no other value is returned and a flag does exist for the value, + // get the flag's value even if the flag's value has not changed + if val == nil { + if flag, exists := v.pflags[lcaseKey]; exists { + jww.TRACE.Println(key, "get pflag default", val) + switch flag.ValueType() { + case "int", "int8", "int16", "int32", "int64": + val = cast.ToInt(flag.ValueString()) + case "bool": + val = cast.ToBool(flag.ValueString()) + default: + val = flag.ValueString() + } } + } + + if val == nil { + return nil + } - switch valType.(type) { - case bool: - return cast.ToBool(val) - case string: - return cast.ToString(val) - case int64, int32, int16, int8, int: - return cast.ToInt(val) - case float64, float32: - return cast.ToFloat64(val) - case time.Time: - return cast.ToTime(val) - case time.Duration: - return cast.ToDuration(val) - case []string: - return cast.ToStringSlice(val) + var valType interface{} + if !v.typeByDefValue { + valType = val + } else { + defVal, defExists := v.defaults[lcaseKey] + if defExists { + valType = defVal + } else { + valType = val } } + switch valType.(type) { + case bool: + return cast.ToBool(val) + case string: + return cast.ToString(val) + case int64, int32, int16, int8, int: + return cast.ToInt(val) + case float64, float32: + return cast.ToFloat64(val) + case time.Time: + return cast.ToTime(val) + case time.Duration: + return cast.ToDuration(val) + case []string: + return cast.ToStringSlice(val) + } return val } -// Sub returns new Viper instance representing a sub tree of this instance. -// Sub is case-insensitive for a key. +// Returns new Viper instance representing a sub tree of this instance func Sub(key string) *Viper { return v.Sub(key) } func (v *Viper) Sub(key string) *Viper { subv := New() data := v.Get(key) - if data == nil { - return nil - } - if reflect.TypeOf(data).Kind() == reflect.Map { subv.config = cast.ToStringMap(data) return subv + } else { + return nil } - return nil } -// GetString returns the value associated with the key as a string. +// Returns the value associated with the key as a string func GetString(key string) string { return v.GetString(key) } func (v *Viper) GetString(key string) string { return cast.ToString(v.Get(key)) } -// GetBool returns the value associated with the key as a boolean. +// Returns the value associated with the key asa boolean func GetBool(key string) bool { return v.GetBool(key) } func (v *Viper) GetBool(key string) bool { return cast.ToBool(v.Get(key)) } -// GetInt returns the value associated with the key as an integer. +// Returns the value associated with the key as an integer func GetInt(key string) int { return v.GetInt(key) } func (v *Viper) GetInt(key string) int { return cast.ToInt(v.Get(key)) } -// GetInt32 returns the value associated with the key as an integer. -func GetInt32(key string) int32 { return v.GetInt32(key) } -func (v *Viper) GetInt32(key string) int32 { - return cast.ToInt32(v.Get(key)) -} - -// GetInt64 returns the value associated with the key as an integer. -func GetInt64(key string) int64 { return v.GetInt64(key) } -func (v *Viper) GetInt64(key string) int64 { - return cast.ToInt64(v.Get(key)) -} - -// GetFloat64 returns the value associated with the key as a float64. +// Returns the value associated with the key as a float64 func GetFloat64(key string) float64 { return v.GetFloat64(key) } func (v *Viper) GetFloat64(key string) float64 { return cast.ToFloat64(v.Get(key)) } -// GetTime returns the value associated with the key as time. +// Returns the value associated with the key as time func GetTime(key string) time.Time { return v.GetTime(key) } func (v *Viper) GetTime(key string) time.Time { return cast.ToTime(v.Get(key)) } -// GetDuration returns the value associated with the key as a duration. +// Returns the value associated with the key as a duration func GetDuration(key string) time.Duration { return v.GetDuration(key) } func (v *Viper) GetDuration(key string) time.Duration { return cast.ToDuration(v.Get(key)) } -// GetStringSlice returns the value associated with the key as a slice of strings. +// Returns the value associated with the key as a slice of strings func GetStringSlice(key string) []string { return v.GetStringSlice(key) } func (v *Viper) GetStringSlice(key string) []string { return cast.ToStringSlice(v.Get(key)) } -// GetStringMap returns the value associated with the key as a map of interfaces. +// Returns the value associated with the key as a map of interfaces func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) } func (v *Viper) GetStringMap(key string) map[string]interface{} { return cast.ToStringMap(v.Get(key)) } -// GetStringMapString returns the value associated with the key as a map of strings. +// Returns the value associated with the key as a map of strings func GetStringMapString(key string) map[string]string { return v.GetStringMapString(key) } func (v *Viper) GetStringMapString(key string) map[string]string { return cast.ToStringMapString(v.Get(key)) } -// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. +// Returns the value associated with the key as a map to a slice of strings. func GetStringMapStringSlice(key string) map[string][]string { return v.GetStringMapStringSlice(key) } func (v *Viper) GetStringMapStringSlice(key string) map[string][]string { return cast.ToStringMapStringSlice(v.Get(key)) } -// GetSizeInBytes returns the size of the value associated with the given key +// Returns the size of the value associated with the given key // in bytes. func GetSizeInBytes(key string) uint { return v.GetSizeInBytes(key) } func (v *Viper) GetSizeInBytes(key string) uint { @@ -761,29 +602,17 @@ func (v *Viper) GetSizeInBytes(key string) uint { return parseSizeInBytes(sizeStr) } -// UnmarshalKey takes a single key and unmarshals it into a Struct. -func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { - return v.UnmarshalKey(key, rawVal, opts...) -} -func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error { - err := decode(v.Get(key), defaultDecoderConfig(rawVal, opts...)) - - if err != nil { - return err - } - - v.insensitiviseMaps() - - return nil +// Takes a single key and unmarshals it into a Struct +func UnmarshalKey(key string, rawVal interface{}) error { return v.UnmarshalKey(key, rawVal) } +func (v *Viper) UnmarshalKey(key string, rawVal interface{}) error { + return mapstructure.Decode(v.Get(key), rawVal) } -// Unmarshal unmarshals the config into a Struct. Make sure that the tags +// Unmarshals the config into a Struct. Make sure that the tags // on the fields of the structure are properly set. -func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { - return v.Unmarshal(rawVal, opts...) -} -func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error { - err := decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...)) +func Unmarshal(rawVal interface{}) error { return v.Unmarshal(rawVal) } +func (v *Viper) Unmarshal(rawVal interface{}) error { + err := mapstructure.WeakDecode(v.AllSettings(), rawVal) if err != nil { return err @@ -794,26 +623,16 @@ func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error return nil } -// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot -// of time.Duration values & string slices -func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { - c := &mapstructure.DecoderConfig{ +// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality +// while erroring on non existing vals in the destination struct +func weakDecodeExact(input, output interface{}) error { + config := &mapstructure.DecoderConfig{ + ErrorUnused: true, Metadata: nil, Result: output, WeaklyTypedInput: true, - DecodeHook: mapstructure.ComposeDecodeHookFunc( - mapstructure.StringToTimeDurationHookFunc(), - mapstructure.StringToSliceHookFunc(","), - ), - } - for _, opt := range opts { - opt(c) } - return c -} -// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality -func decode(input interface{}, config *mapstructure.DecoderConfig) error { decoder, err := mapstructure.NewDecoder(config) if err != nil { return err @@ -821,13 +640,10 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error { return decoder.Decode(input) } -// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent -// in the destination struct. +// Unmarshals the config into a Struct, erroring if a field is non-existant +// in the destination struct func (v *Viper) UnmarshalExact(rawVal interface{}) error { - config := defaultDecoderConfig(rawVal) - config.ErrorUnused = true - - err := decode(v.AllSettings(), config) + err := weakDecodeExact(v.AllSettings(), rawVal) if err != nil { return err @@ -838,27 +654,27 @@ func (v *Viper) UnmarshalExact(rawVal interface{}) error { return nil } -// BindPFlags binds a full flag set to the configuration, using each flag's long +// Bind a full flag set to the configuration, using each flag's long // name as the config key. -func BindPFlags(flags *pflag.FlagSet) error { return v.BindPFlags(flags) } -func (v *Viper) BindPFlags(flags *pflag.FlagSet) error { +func BindPFlags(flags *pflag.FlagSet) (err error) { return v.BindPFlags(flags) } +func (v *Viper) BindPFlags(flags *pflag.FlagSet) (err error) { return v.BindFlagValues(pflagValueSet{flags}) } -// BindPFlag binds a specific key to a pflag (as used by cobra). -// Example (where serverCmd is a Cobra instance): +// Bind a specific key to a pflag (as used by cobra) +// Example(where serverCmd is a Cobra instance): // // serverCmd.Flags().Int("port", 1138, "Port to run Application server on") // Viper.BindPFlag("port", serverCmd.Flags().Lookup("port")) // -func BindPFlag(key string, flag *pflag.Flag) error { return v.BindPFlag(key, flag) } -func (v *Viper) BindPFlag(key string, flag *pflag.Flag) error { +func BindPFlag(key string, flag *pflag.Flag) (err error) { return v.BindPFlag(key, flag) } +func (v *Viper) BindPFlag(key string, flag *pflag.Flag) (err error) { return v.BindFlagValue(key, pflagValue{flag}) } -// BindFlagValues binds a full FlagValue set to the configuration, using each flag's long +// Bind a full FlagValue set to the configuration, using each flag's long // name as the config key. -func BindFlagValues(flags FlagValueSet) error { return v.BindFlagValues(flags) } +func BindFlagValues(flags FlagValueSet) (err error) { return v.BindFlagValues(flags) } func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { flags.VisitAll(func(flag FlagValue) { if err = v.BindFlagValue(flag.Name(), flag); err != nil { @@ -868,14 +684,14 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { return nil } -// BindFlagValue binds a specific key to a FlagValue. -// Example (where serverCmd is a Cobra instance): +// Bind a specific key to a FlagValue. +// Example(where serverCmd is a Cobra instance): // // serverCmd.Flags().Int("port", 1138, "Port to run Application server on") // Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port")) // -func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) } -func (v *Viper) BindFlagValue(key string, flag FlagValue) error { +func BindFlagValue(key string, flag FlagValue) (err error) { return v.BindFlagValue(key, flag) } +func (v *Viper) BindFlagValue(key string, flag FlagValue) (err error) { if flag == nil { return fmt.Errorf("flag for %q is nil", key) } @@ -883,12 +699,12 @@ func (v *Viper) BindFlagValue(key string, flag FlagValue) error { return nil } -// BindEnv binds a Viper key to a ENV variable. -// ENV variables are case sensitive. +// Binds a Viper key to a ENV variable +// ENV variables are case sensitive // If only a key is provided, it will use the env key matching the key, uppercased. // EnvPrefix will be used when set when env name is not provided. -func BindEnv(input ...string) error { return v.BindEnv(input...) } -func (v *Viper) BindEnv(input ...string) error { +func BindEnv(input ...string) (err error) { return v.BindEnv(input...) } +func (v *Viper) BindEnv(input ...string) (err error) { var key, envkey string if len(input) == 0 { return fmt.Errorf("BindEnv missing key to bind to") @@ -907,149 +723,113 @@ func (v *Viper) BindEnv(input ...string) error { return nil } -// Given a key, find the value. +// Given a key, find the value // Viper will check in the following order: -// flag, env, config file, key/value store, default. -// Viper will check to see if an alias exists first. -// Note: this assumes a lower-cased key given. -func (v *Viper) find(lcaseKey string) interface{} { - - var ( - val interface{} - exists bool - path = strings.Split(lcaseKey, v.keyDelim) - nested = len(path) > 1 - ) - - // compute the path through the nested maps to the nested value - if nested && v.isPathShadowedInDeepMap(path, castMapStringToMapInterface(v.aliases)) != "" { - return nil - } +// flag, env, config file, key/value store, default +// Viper will check to see if an alias exists first +func (v *Viper) find(key string) interface{} { + var val interface{} + var exists bool // if the requested key is an alias, then return the proper key - lcaseKey = v.realKey(lcaseKey) - path = strings.Split(lcaseKey, v.keyDelim) - nested = len(path) > 1 - - // Set() override first - val = v.searchMap(v.override, path) - if val != nil { - return val - } - if nested && v.isPathShadowedInDeepMap(path, v.override) != "" { - return nil - } + key = v.realKey(key) - // PFlag override next - flag, exists := v.pflags[lcaseKey] + // PFlag Override first + flag, exists := v.pflags[key] if exists && flag.HasChanged() { + jww.TRACE.Println(key, "found in override (via pflag):", flag.ValueString()) switch flag.ValueType() { case "int", "int8", "int16", "int32", "int64": return cast.ToInt(flag.ValueString()) case "bool": return cast.ToBool(flag.ValueString()) - case "stringSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return res default: return flag.ValueString() } } - if nested && v.isPathShadowedInFlatMap(path, v.pflags) != "" { - return nil + + val, exists = v.override[key] + if exists { + jww.TRACE.Println(key, "found in override:", val) + return val } - // Env override next if v.automaticEnvApplied { // even if it hasn't been registered, if automaticEnv is used, // check any Get request - if val = v.getEnv(v.mergeWithEnvPrefix(lcaseKey)); val != "" { + if val = v.getEnv(v.mergeWithEnvPrefix(key)); val != "" { + jww.TRACE.Println(key, "found in environment with val:", val) return val } - if nested && v.isPathShadowedInAutoEnv(path) != "" { - return nil - } } - envkey, exists := v.env[lcaseKey] + + envkey, exists := v.env[key] if exists { + jww.TRACE.Println(key, "registered as env var", envkey) if val = v.getEnv(envkey); val != "" { + jww.TRACE.Println(envkey, "found in environment with val:", val) return val + } else { + jww.TRACE.Println(envkey, "env value unset:") } } - if nested && v.isPathShadowedInFlatMap(path, v.env) != "" { - return nil - } - // Config file next - val = v.searchMapWithPathPrefixes(v.config, path) - if val != nil { + val, exists = v.config[key] + if exists { + jww.TRACE.Println(key, "found in config:", val) return val } - if nested && v.isPathShadowedInDeepMap(path, v.config) != "" { - return nil - } - // K/V store next - val = v.searchMap(v.kvstore, path) - if val != nil { - return val - } - if nested && v.isPathShadowedInDeepMap(path, v.kvstore) != "" { - return nil + // Test for nested config parameter + if strings.Contains(key, v.keyDelim) { + path := strings.Split(key, v.keyDelim) + + source := v.find(path[0]) + if source != nil { + if reflect.TypeOf(source).Kind() == reflect.Map { + val := v.searchMap(cast.ToStringMap(source), path[1:]) + jww.TRACE.Println(key, "found in nested config:", val) + return val + } + } } - // Default next - val = v.searchMap(v.defaults, path) - if val != nil { + val, exists = v.kvstore[key] + if exists { + jww.TRACE.Println(key, "found in key/value store:", val) return val } - if nested && v.isPathShadowedInDeepMap(path, v.defaults) != "" { - return nil - } - // last chance: if no other value is returned and a flag does exist for the value, - // get the flag's value even if the flag's value has not changed - if flag, exists := v.pflags[lcaseKey]; exists { - switch flag.ValueType() { - case "int", "int8", "int16", "int32", "int64": - return cast.ToInt(flag.ValueString()) - case "bool": - return cast.ToBool(flag.ValueString()) - case "stringSlice": - s := strings.TrimPrefix(flag.ValueString(), "[") - s = strings.TrimSuffix(s, "]") - res, _ := readAsCSV(s) - return res - default: - return flag.ValueString() - } + val, exists = v.defaults[key] + if exists { + jww.TRACE.Println(key, "found in defaults:", val) + return val } - // last item, no need to check shadowing return nil } -func readAsCSV(val string) ([]string, error) { - if val == "" { - return []string{}, nil - } - stringReader := strings.NewReader(val) - csvReader := csv.NewReader(stringReader) - return csvReader.Read() -} - -// IsSet checks to see if the key has been set in any of the data locations. -// IsSet is case-insensitive for a key. +// Check to see if the key has been set in any of the data locations func IsSet(key string) bool { return v.IsSet(key) } func (v *Viper) IsSet(key string) bool { + path := strings.Split(key, v.keyDelim) + lcaseKey := strings.ToLower(key) val := v.find(lcaseKey) + + if val == nil { + source := v.find(strings.ToLower(path[0])) + if source != nil { + if reflect.TypeOf(source).Kind() == reflect.Map { + val = v.searchMap(cast.ToStringMap(source), path[1:]) + } + } + } + return val != nil } -// AutomaticEnv has Viper check ENV variables for all. +// Have Viper check ENV variables for all // keys set in config, default & flags func AutomaticEnv() { v.AutomaticEnv() } func (v *Viper) AutomaticEnv() { @@ -1108,11 +888,12 @@ func (v *Viper) realKey(key string) string { if exists { jww.DEBUG.Println("Alias", key, "to", newkey) return v.realKey(newkey) + } else { + return key } - return key } -// InConfig checks to see if the given key (or an alias) is in the config file. +// Check to see if the given key (or an alias) is in the config file func InConfig(key string) bool { return v.InConfig(key) } func (v *Viper) InConfig(key string) bool { // if the requested key is an alias, then return the proper key @@ -1122,86 +903,53 @@ func (v *Viper) InConfig(key string) bool { return exists } -// SetDefault sets the default value for this key. -// SetDefault is case-insensitive for a key. +// Set the default value for this key. // Default only used when no value is provided by the user via flag, config or ENV. func SetDefault(key string, value interface{}) { v.SetDefault(key, value) } func (v *Viper) SetDefault(key string, value interface{}) { // If alias passed in, then set the proper default key = v.realKey(strings.ToLower(key)) - value = toCaseInsensitiveValue(value) - - path := strings.Split(key, v.keyDelim) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(v.defaults, path[0:len(path)-1]) - - // set innermost value - deepestMap[lastKey] = value + v.defaults[key] = value } -// Set sets the value for the key in the override regiser. -// Set is case-insensitive for a key. +// Sets the value for the key in the override regiser. // Will be used instead of values obtained via -// flags, config file, ENV, default, or key/value store. +// flags, config file, ENV, default, or key/value store func Set(key string, value interface{}) { v.Set(key, value) } func (v *Viper) Set(key string, value interface{}) { // If alias passed in, then set the proper override key = v.realKey(strings.ToLower(key)) - value = toCaseInsensitiveValue(value) - - path := strings.Split(key, v.keyDelim) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(v.override, path[0:len(path)-1]) - - // set innermost value - deepestMap[lastKey] = value + v.override[key] = value } -// ReadInConfig will discover and load the configuration file from disk +// Viper will discover and load the configuration file from disk // and key/value stores, searching in one of the defined paths. func ReadInConfig() error { return v.ReadInConfig() } func (v *Viper) ReadInConfig() error { jww.INFO.Println("Attempting to read in config file") - filename, err := v.getConfigFile() - if err != nil { - return err - } - if !stringInSlice(v.getConfigType(), SupportedExts) { return UnsupportedConfigError(v.getConfigType()) } - jww.DEBUG.Println("Reading file: ", filename) - file, err := afero.ReadFile(v.fs, filename) + file, err := ioutil.ReadFile(v.getConfigFile()) if err != nil { return err } - config := make(map[string]interface{}) - - err = v.unmarshalReader(bytes.NewReader(file), config) - if err != nil { - return err - } + v.config = make(map[string]interface{}) - v.config = config - return nil + return v.unmarshalReader(bytes.NewReader(file), v.config) } // MergeInConfig merges a new configuration with an existing config. func MergeInConfig() error { return v.MergeInConfig() } func (v *Viper) MergeInConfig() error { jww.INFO.Println("Attempting to merge in config file") - filename, err := v.getConfigFile() - if err != nil { - return err - } - if !stringInSlice(v.getConfigType(), SupportedExts) { return UnsupportedConfigError(v.getConfigType()) } - file, err := afero.ReadFile(v.fs, filename) + file, err := ioutil.ReadFile(v.getConfigFile()) if err != nil { return err } @@ -1209,7 +957,7 @@ func (v *Viper) MergeInConfig() error { return v.MergeConfig(bytes.NewReader(file)) } -// ReadConfig will read a configuration file, setting existing keys to nil if the +// Viper will read a configuration file, setting existing keys to nil if the // key does not exist in the file. func ReadConfig(in io.Reader) error { return v.ReadConfig(in) } func (v *Viper) ReadConfig(in io.Reader) error { @@ -1231,195 +979,6 @@ func (v *Viper) MergeConfig(in io.Reader) error { return nil } -// WriteConfig writes the current configuration to a file. -func WriteConfig() error { return v.WriteConfig() } -func (v *Viper) WriteConfig() error { - filename, err := v.getConfigFile() - if err != nil { - return err - } - return v.writeConfig(filename, true) -} - -// SafeWriteConfig writes current configuration to file only if the file does not exist. -func SafeWriteConfig() error { return v.SafeWriteConfig() } -func (v *Viper) SafeWriteConfig() error { - filename, err := v.getConfigFile() - if err != nil { - return err - } - return v.writeConfig(filename, false) -} - -// WriteConfigAs writes current configuration to a given filename. -func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } -func (v *Viper) WriteConfigAs(filename string) error { - return v.writeConfig(filename, true) -} - -// SafeWriteConfigAs writes current configuration to a given filename if it does not exist. -func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } -func (v *Viper) SafeWriteConfigAs(filename string) error { - return v.writeConfig(filename, false) -} - -func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) } -func (v *Viper) writeConfig(filename string, force bool) error { - jww.INFO.Println("Attempting to write configuration to file.") - ext := filepath.Ext(filename) - if len(ext) <= 1 { - return fmt.Errorf("Filename: %s requires valid extension.", filename) - } - configType := ext[1:] - if !stringInSlice(configType, SupportedExts) { - return UnsupportedConfigError(configType) - } - if v.config == nil { - v.config = make(map[string]interface{}) - } - var flags int - if force == true { - flags = os.O_CREATE | os.O_TRUNC | os.O_WRONLY - } else { - if _, err := os.Stat(filename); os.IsNotExist(err) { - flags = os.O_WRONLY - } else { - return fmt.Errorf("File: %s exists. Use WriteConfig to overwrite.", filename) - } - } - f, err := v.fs.OpenFile(filename, flags, os.FileMode(0644)) - if err != nil { - return err - } - return v.marshalWriter(f, configType) -} - -// Unmarshal a Reader into a map. -// Should probably be an unexported function. -func unmarshalReader(in io.Reader, c map[string]interface{}) error { - return v.unmarshalReader(in, c) -} -func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { - buf := new(bytes.Buffer) - buf.ReadFrom(in) - - switch strings.ToLower(v.getConfigType()) { - case "yaml", "yml": - if err := yaml.Unmarshal(buf.Bytes(), &c); err != nil { - return ConfigParseError{err} - } - - case "json": - if err := json.Unmarshal(buf.Bytes(), &c); err != nil { - return ConfigParseError{err} - } - - case "hcl": - obj, err := hcl.Parse(string(buf.Bytes())) - if err != nil { - return ConfigParseError{err} - } - if err = hcl.DecodeObject(&c, obj); err != nil { - return ConfigParseError{err} - } - - case "toml": - tree, err := toml.LoadReader(buf) - if err != nil { - return ConfigParseError{err} - } - tmap := tree.ToMap() - for k, v := range tmap { - c[k] = v - } - - case "properties", "props", "prop": - v.properties = properties.NewProperties() - var err error - if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil { - return ConfigParseError{err} - } - for _, key := range v.properties.Keys() { - value, _ := v.properties.Get(key) - // recursively build nested maps - path := strings.Split(key, ".") - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(c, path[0:len(path)-1]) - // set innermost value - deepestMap[lastKey] = value - } - } - - insensitiviseMap(c) - return nil -} - -// Marshal a map into Writer. -func marshalWriter(f afero.File, configType string) error { - return v.marshalWriter(f, configType) -} -func (v *Viper) marshalWriter(f afero.File, configType string) error { - c := v.AllSettings() - switch configType { - case "json": - b, err := json.MarshalIndent(c, "", " ") - if err != nil { - return ConfigMarshalError{err} - } - _, err = f.WriteString(string(b)) - if err != nil { - return ConfigMarshalError{err} - } - - case "hcl": - b, err := json.Marshal(c) - ast, err := hcl.Parse(string(b)) - if err != nil { - return ConfigMarshalError{err} - } - err = printer.Fprint(f, ast.Node) - if err != nil { - return ConfigMarshalError{err} - } - - case "prop", "props", "properties": - if v.properties == nil { - v.properties = properties.NewProperties() - } - p := v.properties - for _, key := range v.AllKeys() { - _, _, err := p.Set(key, v.GetString(key)) - if err != nil { - return ConfigMarshalError{err} - } - } - _, err := p.WriteComment(f, "#", properties.UTF8) - if err != nil { - return ConfigMarshalError{err} - } - - case "toml": - t, err := toml.TreeFromMap(c) - if err != nil { - return ConfigMarshalError{err} - } - s := t.String() - if _, err := f.WriteString(s); err != nil { - return ConfigMarshalError{err} - } - - case "yaml", "yml": - b, err := yaml.Marshal(c) - if err != nil { - return ConfigMarshalError{err} - } - if _, err = f.WriteString(string(b)); err != nil { - return ConfigMarshalError{err} - } - } - return nil -} - func keyExists(k string, m map[string]interface{}) string { lk := strings.ToLower(k) for mk := range m { @@ -1440,22 +999,6 @@ func castToMapStringInterface( return tgt } -func castMapStringToMapInterface(src map[string]string) map[string]interface{} { - tgt := map[string]interface{}{} - for k, v := range src { - tgt[k] = v - } - return tgt -} - -func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} { - tgt := map[string]interface{}{} - for k, v := range src { - tgt[k] = v - } - return tgt -} - // mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's // insistence on parsing nested structures as `map[interface{}]interface{}` // instead of using a `string` as the key for nest structures beyond one level @@ -1516,20 +1059,40 @@ func mergeMaps( } } -// ReadRemoteConfig attempts to get configuration from a remote source +// func ReadBufConfig(buf *bytes.Buffer) error { return v.ReadBufConfig(buf) } +// func (v *Viper) ReadBufConfig(buf *bytes.Buffer) error { +// v.config = make(map[string]interface{}) +// return v.unmarshalReader(buf, v.config) +// } + +// Attempts to get configuration from a remote source // and read it in the remote configuration registry. func ReadRemoteConfig() error { return v.ReadRemoteConfig() } func (v *Viper) ReadRemoteConfig() error { - return v.getKeyValueConfig() + err := v.getKeyValueConfig() + if err != nil { + return err + } + return nil } func WatchRemoteConfig() error { return v.WatchRemoteConfig() } func (v *Viper) WatchRemoteConfig() error { - return v.watchKeyValueConfig() + err := v.watchKeyValueConfig() + if err != nil { + return err + } + return nil +} + +// Unmarshall a Reader into a map +// Should probably be an unexported function +func unmarshalReader(in io.Reader, c map[string]interface{}) error { + return v.unmarshalReader(in, c) } -func (v *Viper) WatchRemoteConfigOnChannel() error { - return v.watchKeyValueConfigOnChannel() +func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { + return unmarshallConfigReader(in, c, v.getConfigType()) } func (v *Viper) insensitiviseMaps() { @@ -1539,7 +1102,7 @@ func (v *Viper) insensitiviseMaps() { insensitiviseMap(v.kvstore) } -// Retrieve the first found remote configuration. +// retrieve the first found remote configuration func (v *Viper) getKeyValueConfig() error { if RemoteConfig == nil { return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'") @@ -1556,7 +1119,8 @@ func (v *Viper) getKeyValueConfig() error { return RemoteConfigError("No Files Found") } -func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { +func (v *Viper) getRemoteConfig(provider *defaultRemoteProvider) (map[string]interface{}, error) { + reader, err := RemoteConfig.Get(provider) if err != nil { return nil, err @@ -1565,24 +1129,7 @@ func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{} return v.kvstore, err } -// Retrieve the first found remote configuration. -func (v *Viper) watchKeyValueConfigOnChannel() error { - for _, rp := range v.remoteProviders { - respc, _ := RemoteConfig.WatchChannel(rp) - //Todo: Add quit channel - go func(rc <-chan *RemoteResponse) { - for { - b := <-rc - reader := bytes.NewReader(b.Value) - v.unmarshalReader(reader, v.kvstore) - } - }(respc) - return nil - } - return RemoteConfigError("No Files Found") -} - -// Retrieve the first found remote configuration. +// retrieve the first found remote configuration func (v *Viper) watchKeyValueConfig() error { for _, rp := range v.remoteProviders { val, err := v.watchRemoteConfig(rp) @@ -1595,7 +1142,7 @@ func (v *Viper) watchKeyValueConfig() error { return RemoteConfigError("No Files Found") } -func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) { +func (v *Viper) watchRemoteConfig(provider *defaultRemoteProvider) (map[string]interface{}, error) { reader, err := RemoteConfig.Watch(provider) if err != nil { return nil, err @@ -1604,125 +1151,68 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface return v.kvstore, err } -// AllKeys returns all keys holding a value, regardless of where they are set. -// Nested keys are returned with a v.keyDelim (= ".") separator +// Return all keys regardless where they are set func AllKeys() []string { return v.AllKeys() } func (v *Viper) AllKeys() []string { - m := map[string]bool{} - // add all paths, by order of descending priority to ensure correct shadowing - m = v.flattenAndMergeMap(m, castMapStringToMapInterface(v.aliases), "") - m = v.flattenAndMergeMap(m, v.override, "") - m = v.mergeFlatMap(m, castMapFlagToMapInterface(v.pflags)) - m = v.mergeFlatMap(m, castMapStringToMapInterface(v.env)) - m = v.flattenAndMergeMap(m, v.config, "") - m = v.flattenAndMergeMap(m, v.kvstore, "") - m = v.flattenAndMergeMap(m, v.defaults, "") - - // convert set of paths to list - a := []string{} - for x := range m { - a = append(a, x) + m := map[string]struct{}{} + + for key, _ := range v.defaults { + m[strings.ToLower(key)] = struct{}{} } - return a -} -// flattenAndMergeMap recursively flattens the given map into a map[string]bool -// of key paths (used as a set, easier to manipulate than a []string): -// - each path is merged into a single key string, delimited with v.keyDelim (= ".") -// - if a path is shadowed by an earlier value in the initial shadow map, -// it is skipped. -// The resulting set of paths is merged to the given shadow set at the same time. -func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool { - if shadow != nil && prefix != "" && shadow[prefix] { - // prefix is shadowed => nothing more to flatten - return shadow + for key, _ := range v.pflags { + m[strings.ToLower(key)] = struct{}{} } - if shadow == nil { - shadow = make(map[string]bool) + + for key, _ := range v.env { + m[strings.ToLower(key)] = struct{}{} } - var m2 map[string]interface{} - if prefix != "" { - prefix += v.keyDelim + for key, _ := range v.config { + m[strings.ToLower(key)] = struct{}{} } - for k, val := range m { - fullKey := prefix + k - switch val.(type) { - case map[string]interface{}: - m2 = val.(map[string]interface{}) - case map[interface{}]interface{}: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = true - continue - } - // recursively merge to shadow map - shadow = v.flattenAndMergeMap(shadow, m2, fullKey) - } - return shadow -} - -// mergeFlatMap merges the given maps, excluding values of the second map -// shadowed by values from the first map. -func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool { - // scan keys -outer: - for k, _ := range m { - path := strings.Split(k, v.keyDelim) - // scan intermediate paths - var parentKey string - for i := 1; i < len(path); i++ { - parentKey = strings.Join(path[0:i], v.keyDelim) - if shadow[parentKey] { - // path is shadowed, continue - continue outer - } - } - // add key - shadow[strings.ToLower(k)] = true + + for key, _ := range v.kvstore { + m[strings.ToLower(key)] = struct{}{} + } + + for key, _ := range v.override { + m[strings.ToLower(key)] = struct{}{} + } + + for key, _ := range v.aliases { + m[strings.ToLower(key)] = struct{}{} } - return shadow + + a := []string{} + for x, _ := range m { + a = append(a, x) + } + + return a } -// AllSettings merges all settings and returns them as a map[string]interface{}. +// Return all settings as a map[string]interface{} func AllSettings() map[string]interface{} { return v.AllSettings() } func (v *Viper) AllSettings() map[string]interface{} { m := map[string]interface{}{} - // start from the list of keys, and construct the map one value at a time - for _, k := range v.AllKeys() { - value := v.Get(k) - if value == nil { - // should not happen, since AllKeys() returns only keys holding a value, - // check just in case anything changes - continue - } - path := strings.Split(k, v.keyDelim) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(m, path[0:len(path)-1]) - // set innermost value - deepestMap[lastKey] = value + for _, x := range v.AllKeys() { + m[x] = v.Get(x) } - return m -} -// SetFs sets the filesystem to use to read configuration. -func SetFs(fs afero.Fs) { v.SetFs(fs) } -func (v *Viper) SetFs(fs afero.Fs) { - v.fs = fs + return m } -// SetConfigName sets name for the config file. +// Name for the config file. // Does not include extension. func SetConfigName(in string) { v.SetConfigName(in) } func (v *Viper) SetConfigName(in string) { if in != "" { v.configName = in - v.configFile = "" } } -// SetConfigType sets the type of the configuration returned by the +// Sets the type of the configuration returned by the // remote source, e.g. "json". func SetConfigType(in string) { v.SetConfigType(in) } func (v *Viper) SetConfigType(in string) { @@ -1736,36 +1226,36 @@ func (v *Viper) getConfigType() string { return v.configType } - cf, err := v.getConfigFile() - if err != nil { - return "" - } - + cf := v.getConfigFile() ext := filepath.Ext(cf) if len(ext) > 1 { return ext[1:] + } else { + return "" } - - return "" } -func (v *Viper) getConfigFile() (string, error) { - if v.configFile == "" { - cf, err := v.findConfigFile() - if err != nil { - return "", err - } - v.configFile = cf +func (v *Viper) getConfigFile() string { + // if explicitly set, then use it + if v.configFile != "" { + return v.configFile } - return v.configFile, nil + + cf, err := v.findConfigFile() + if err != nil { + return "" + } + + v.configFile = cf + return v.getConfigFile() } func (v *Viper) searchInPath(in string) (filename string) { jww.DEBUG.Println("Searching for config in ", in) for _, ext := range SupportedExts { jww.DEBUG.Println("Checking for", filepath.Join(in, v.configName+"."+ext)) - if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b { + if b, _ := exists(filepath.Join(in, v.configName+"."+ext)); b { jww.DEBUG.Println("Found: ", filepath.Join(in, v.configName+"."+ext)) return filepath.Join(in, v.configName+"."+ext) } @@ -1774,9 +1264,10 @@ func (v *Viper) searchInPath(in string) (filename string) { return "" } -// Search all configPaths for any config file. -// Returns the first path that exists (and is a config file). +// search all configPaths for any config file. +// Returns the first path that exists (and is a config file) func (v *Viper) findConfigFile() (string, error) { + jww.INFO.Println("Searching for config in ", v.configPaths) for _, cp := range v.configPaths { @@ -1788,15 +1279,22 @@ func (v *Viper) findConfigFile() (string, error) { return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} } -// Debug prints all configuration registries for debugging +// Prints all configuration registries for debugging // purposes. func Debug() { v.Debug() } func (v *Viper) Debug() { - fmt.Printf("Aliases:\n%#v\n", v.aliases) - fmt.Printf("Override:\n%#v\n", v.override) - fmt.Printf("PFlags:\n%#v\n", v.pflags) - fmt.Printf("Env:\n%#v\n", v.env) - fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore) - fmt.Printf("Config:\n%#v\n", v.config) - fmt.Printf("Defaults:\n%#v\n", v.defaults) + fmt.Println("Aliases:") + pretty.Println(v.aliases) + fmt.Println("Override:") + pretty.Println(v.override) + fmt.Println("PFlags") + pretty.Println(v.pflags) + fmt.Println("Env:") + pretty.Println(v.env) + fmt.Println("Key/Value Store:") + pretty.Println(v.kvstore) + fmt.Println("Config:") + pretty.Println(v.config) + fmt.Println("Defaults:") + pretty.Println(v.defaults) } diff --git a/vendor/github.com/spf13/viper/viper_test.go b/vendor/github.com/spf13/viper/viper_test.go index be11f3e0b..858caff2f 100644 --- a/vendor/github.com/spf13/viper/viper_test.go +++ b/vendor/github.com/spf13/viper/viper_test.go @@ -7,9 +7,7 @@ package viper import ( "bytes" - "encoding/json" "fmt" - "io" "io/ioutil" "os" "path" @@ -19,10 +17,6 @@ import ( "testing" "time" - "github.com/mitchellh/mapstructure" - "github.com/spf13/afero" - "github.com/spf13/cast" - "github.com/spf13/pflag" "github.com/stretchr/testify/assert" ) @@ -110,9 +104,8 @@ var remoteExample = []byte(`{ func initConfigs() { Reset() - var r io.Reader SetConfigType("yaml") - r = bytes.NewReader(yamlExample) + r := bytes.NewReader(yamlExample) unmarshalReader(r, v.config) SetConfigType("json") @@ -136,18 +129,12 @@ func initConfigs() { unmarshalReader(remote, v.kvstore) } -func initConfig(typ, config string) { +func initYAML() { Reset() - SetConfigType(typ) - r := strings.NewReader(config) - - if err := unmarshalReader(r, v.config); err != nil { - panic(err) - } -} + SetConfigType("yaml") + r := bytes.NewReader(yamlExample) -func initYAML() { - initConfig("yaml", string(yamlExample)) + unmarshalReader(r, v.config) } func initJSON() { @@ -246,9 +233,7 @@ func (s *stringValue) String() string { func TestBasics(t *testing.T) { SetConfigFile("/tmp/config.yaml") - filename, err := v.getConfigFile() - assert.Equal(t, "/tmp/config.yaml", filename) - assert.NoError(t, err) + assert.Equal(t, "/tmp/config.yaml", v.getConfigFile()) } func TestDefault(t *testing.T) { @@ -265,7 +250,7 @@ func TestDefault(t *testing.T) { assert.Equal(t, "leather", Get("clothing.jacket")) } -func TestUnmarshaling(t *testing.T) { +func TestUnmarshalling(t *testing.T) { SetConfigType("yaml") r := bytes.NewReader(yamlExample) @@ -274,7 +259,7 @@ func TestUnmarshaling(t *testing.T) { assert.False(t, InConfig("state")) assert.Equal(t, "steve", Get("name")) assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies")) - assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing")) + assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[interface{}]interface{}{"size": "large"}}, Get("clothing")) assert.Equal(t, 35, Get("age")) } @@ -420,7 +405,7 @@ func TestAutoEnvWithPrefix(t *testing.T) { assert.Equal(t, "13", Get("bar")) } -func TestSetEnvKeyReplacer(t *testing.T) { +func TestSetEnvReplacer(t *testing.T) { Reset() AutomaticEnv() @@ -435,9 +420,9 @@ func TestSetEnvKeyReplacer(t *testing.T) { func TestAllKeys(t *testing.T) { initConfigs() - ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"} + ks := sort.StringSlice{"title", "newkey", "owner", "name", "beard", "ppu", "batters", "hobbies", "clothing", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"} dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") - all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}} + all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[interface{}]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[interface{}]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters.batter.type": "Regular", "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}} var allkeys sort.StringSlice allkeys = AllKeys() @@ -448,25 +433,13 @@ func TestAllKeys(t *testing.T) { assert.Equal(t, all, AllSettings()) } -func TestAllKeysWithEnv(t *testing.T) { - v := New() - - // bind and define environment variables (including a nested one) - v.BindEnv("id") - v.BindEnv("foo.bar") - v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - os.Setenv("ID", "13") - os.Setenv("FOO_BAR", "baz") - - expectedKeys := sort.StringSlice{"id", "foo.bar"} - expectedKeys.Sort() - keys := sort.StringSlice(v.AllKeys()) - keys.Sort() - assert.Equal(t, expectedKeys, keys) +func TestCaseInSensitive(t *testing.T) { + assert.Equal(t, true, Get("hacker")) + Set("Title", "Checking Case") + assert.Equal(t, "Checking Case", Get("tItle")) } func TestAliasesOfAliases(t *testing.T) { - Set("Title", "Checking Case") RegisterAlias("Foo", "Bar") RegisterAlias("Bar", "Title") assert.Equal(t, "Checking Case", Get("FOO")) @@ -480,12 +453,10 @@ func TestRecursiveAliases(t *testing.T) { func TestUnmarshal(t *testing.T) { SetDefault("port", 1313) Set("name", "Steve") - Set("duration", "1s1ms") type config struct { - Port int - Name string - Duration time.Duration + Port int + Name string } var C config @@ -495,54 +466,17 @@ func TestUnmarshal(t *testing.T) { t.Fatalf("unable to decode into struct, %v", err) } - assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C) + assert.Equal(t, &C, &config{Name: "Steve", Port: 1313}) Set("port", 1234) err = Unmarshal(&C) if err != nil { t.Fatalf("unable to decode into struct, %v", err) } - assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C) -} - -func TestUnmarshalWithDecoderOptions(t *testing.T) { - Set("credentials", "{\"foo\":\"bar\"}") - - opt := DecodeHook(mapstructure.ComposeDecodeHookFunc( - mapstructure.StringToTimeDurationHookFunc(), - mapstructure.StringToSliceHookFunc(","), - // Custom Decode Hook Function - func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) { - if rf != reflect.String || rt != reflect.Map { - return data, nil - } - m := map[string]string{} - raw := data.(string) - if raw == "" { - return m, nil - } - return m, json.Unmarshal([]byte(raw), &m) - }, - )) - - type config struct { - Credentials map[string]string - } - - var C config - - err := Unmarshal(&C, opt) - if err != nil { - t.Fatalf("unable to decode into struct, %v", err) - } - - assert.Equal(t, &config{ - Credentials: map[string]string{"foo": "bar"}, - }, &C) + assert.Equal(t, &C, &config{Name: "Steve", Port: 1234}) } func TestBindPFlags(t *testing.T) { - v := New() // create independent Viper object flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) var testValues = map[string]*string{ @@ -557,11 +491,11 @@ func TestBindPFlags(t *testing.T) { "endpoint": "/public", } - for name := range testValues { + for name, _ := range testValues { testValues[name] = flagSet.String(name, "", "test") } - err := v.BindPFlags(flagSet) + err := BindPFlags(flagSet) if err != nil { t.Fatalf("error binding flag set, %v", err) } @@ -572,49 +506,11 @@ func TestBindPFlags(t *testing.T) { }) for name, expected := range mutatedTestValues { - assert.Equal(t, expected, v.Get(name)) + assert.Equal(t, Get(name), expected) } } -func TestBindPFlagsStringSlice(t *testing.T) { - for _, testValue := range []struct { - Expected []string - Value string - }{ - {[]string{}, ""}, - {[]string{"jeden"}, "jeden"}, - {[]string{"dwa", "trzy"}, "dwa,trzy"}, - {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} { - - for _, changed := range []bool{true, false} { - v := New() // create independent Viper object - flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) - flagSet.StringSlice("stringslice", testValue.Expected, "test") - flagSet.Visit(func(f *pflag.Flag) { - if len(testValue.Value) > 0 { - f.Value.Set(testValue.Value) - f.Changed = changed - } - }) - - err := v.BindPFlags(flagSet) - if err != nil { - t.Fatalf("error binding flag set, %v", err) - } - - type TestStr struct { - StringSlice []string - } - val := &TestStr{} - if err := v.Unmarshal(val); err != nil { - t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err) - } - assert.Equal(t, testValue.Expected, val.StringSlice) - } - } -} - func TestBindPFlag(t *testing.T) { var testString = "testing" var testValue = newStringValue(testString, &testString) @@ -637,6 +533,7 @@ func TestBindPFlag(t *testing.T) { } func TestBoundCaseSensitivity(t *testing.T) { + assert.Equal(t, "brown", Get("eyes")) BindEnv("eYEs", "TURTLE_EYES") @@ -733,19 +630,19 @@ func TestFindsNestedKeys(t *testing.T) { "age": 35, "owner": map[string]interface{}{ "organization": "MongoDB", - "bio": "MongoDB Chief Developer Advocate & Hacker at Large", + "Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob, }, - "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large", + "owner.Bio": "MongoDB Chief Developer Advocate & Hacker at Large", "type": "donut", "id": "0001", "name": "Cake", "hacker": true, "ppu": 0.55, - "clothing": map[string]interface{}{ + "clothing": map[interface{}]interface{}{ "jacket": "leather", "trousers": "denim", - "pants": map[string]interface{}{ + "pants": map[interface{}]interface{}{ "size": "large", }, }, @@ -791,7 +688,7 @@ func TestReadBufConfig(t *testing.T) { assert.False(t, v.InConfig("state")) assert.Equal(t, "steve", v.Get("name")) assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies")) - assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing")) + assert.Equal(t, map[interface{}]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[interface{}]interface{}{"size": "large"}}, v.Get("clothing")) assert.Equal(t, 35, v.Get("age")) } @@ -841,27 +738,7 @@ func TestWrongDirsSearchNotFound(t *testing.T) { v.AddConfigPath(`thispathaintthere`) err := v.ReadInConfig() - assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) - - // Even though config did not load and the error might have - // been ignored by the client, the default still loads - assert.Equal(t, `default`, v.GetString(`key`)) -} - -func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { - - _, config, cleanup := initDirs(t) - defer cleanup() - - v := New() - v.SetConfigName(config) - v.SetDefault(`key`, `default`) - - v.AddConfigPath(`whattayoutalkingbout`) - v.AddConfigPath(`thispathaintthere`) - - err := v.MergeInConfig() - assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) + assert.Equal(t, reflect.TypeOf(UnsupportedConfigError("")), reflect.TypeOf(err)) // Even though config did not load and the error might have // been ignored by the client, the default still loads @@ -880,200 +757,12 @@ func TestSub(t *testing.T) { assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) subv = v.Sub("clothing.pants.size") - assert.Equal(t, (*Viper)(nil), subv) - - subv = v.Sub("missing.key") - assert.Equal(t, (*Viper)(nil), subv) -} - -var hclWriteExpected = []byte(`"foos" = { - "foo" = { - "key" = 1 - } - - "foo" = { - "key" = 2 - } - - "foo" = { - "key" = 3 - } - - "foo" = { - "key" = 4 - } -} - -"id" = "0001" - -"name" = "Cake" - -"ppu" = 0.55 - -"type" = "donut"`) - -func TestWriteConfigHCL(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("hcl") - err := v.ReadConfig(bytes.NewBuffer(hclExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.hcl"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.hcl") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, hclWriteExpected, read) -} - -var jsonWriteExpected = []byte(`{ - "batters": { - "batter": [ - { - "type": "Regular" - }, - { - "type": "Chocolate" - }, - { - "type": "Blueberry" - }, - { - "type": "Devil's Food" - } - ] - }, - "id": "0001", - "name": "Cake", - "ppu": 0.55, - "type": "donut" -}`) - -func TestWriteConfigJson(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("json") - err := v.ReadConfig(bytes.NewBuffer(jsonExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.json"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.json") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, jsonWriteExpected, read) -} - -var propertiesWriteExpected = []byte(`p_id = 0001 -p_type = donut -p_name = Cake -p_ppu = 0.55 -p_batters.batter.type = Regular -`) - -func TestWriteConfigProperties(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("properties") - err := v.ReadConfig(bytes.NewBuffer(propertiesExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.properties"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.properties") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, propertiesWriteExpected, read) -} - -func TestWriteConfigTOML(t *testing.T) { - fs := afero.NewMemMapFs() - v := New() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("toml") - err := v.ReadConfig(bytes.NewBuffer(tomlExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.toml"); err != nil { - t.Fatal(err) - } - - // The TOML String method does not order the contents. - // Therefore, we must read the generated file and compare the data. - v2 := New() - v2.SetFs(fs) - v2.SetConfigName("c") - v2.SetConfigType("toml") - v2.SetConfigFile("c.toml") - err = v2.ReadInConfig() - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, v.GetString("title"), v2.GetString("title")) - assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio")) - assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob")) - assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization")) -} - -var yamlWriteExpected = []byte(`age: 35 -beard: true -clothing: - jacket: leather - pants: - size: large - trousers: denim -eyes: brown -hacker: true -hobbies: -- skateboarding -- snowboarding -- go -name: steve -`) - -func TestWriteConfigYAML(t *testing.T) { - v := New() - fs := afero.NewMemMapFs() - v.SetFs(fs) - v.SetConfigName("c") - v.SetConfigType("yaml") - err := v.ReadConfig(bytes.NewBuffer(yamlExample)) - if err != nil { - t.Fatal(err) - } - if err := v.WriteConfigAs("c.yaml"); err != nil { - t.Fatal(err) - } - read, err := afero.ReadFile(fs, "c.yaml") - if err != nil { - t.Fatal(err) - } - assert.Equal(t, yamlWriteExpected, read) + assert.Equal(t, subv, (*Viper)(nil)) } var yamlMergeExampleTgt = []byte(` hello: pop: 37890 - lagrenum: 765432101234567 world: - us - uk @@ -1084,7 +773,6 @@ hello: var yamlMergeExampleSrc = []byte(` hello: pop: 45000 - lagrenum: 7654321001234567 universe: - mw - ad @@ -1102,18 +790,6 @@ func TestMergeConfig(t *testing.T) { t.Fatalf("pop != 37890, = %d", pop) } - if pop := v.GetInt("hello.lagrenum"); pop != 765432101234567 { - t.Fatalf("lagrenum != 765432101234567, = %d", pop) - } - - if pop := v.GetInt32("hello.pop"); pop != int32(37890) { - t.Fatalf("pop != 37890, = %d", pop) - } - - if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { - t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) - } - if world := v.GetStringSlice("hello.world"); len(world) != 4 { t.Fatalf("len(world) != 4, = %d", len(world)) } @@ -1130,18 +806,6 @@ func TestMergeConfig(t *testing.T) { t.Fatalf("pop != 45000, = %d", pop) } - if pop := v.GetInt("hello.lagrenum"); pop != 7654321001234567 { - t.Fatalf("lagrenum != 7654321001234567, = %d", pop) - } - - if pop := v.GetInt32("hello.pop"); pop != int32(45000) { - t.Fatalf("pop != 45000, = %d", pop) - } - - if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { - t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) - } - if world := v.GetStringSlice("hello.world"); len(world) != 4 { t.Fatalf("len(world) != 4, = %d", len(world)) } @@ -1196,257 +860,26 @@ func TestMergeConfigNoMerge(t *testing.T) { } func TestUnmarshalingWithAliases(t *testing.T) { - v := New() - v.SetDefault("ID", 1) - v.Set("name", "Steve") - v.Set("lastname", "Owen") + SetDefault("Id", 1) + Set("name", "Steve") + Set("lastname", "Owen") - v.RegisterAlias("UserID", "ID") - v.RegisterAlias("Firstname", "name") - v.RegisterAlias("Surname", "lastname") + RegisterAlias("UserID", "Id") + RegisterAlias("Firstname", "name") + RegisterAlias("Surname", "lastname") type config struct { - ID int + Id int FirstName string Surname string } var C config - err := v.Unmarshal(&C) - if err != nil { - t.Fatalf("unable to decode into struct, %v", err) - } - assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) -} - -func TestSetConfigNameClearsFileCache(t *testing.T) { - SetConfigFile("/tmp/config.yaml") - SetConfigName("default") - f, err := v.getConfigFile() - if err == nil { - t.Fatalf("config file cache should have been cleared") - } - assert.Empty(t, f) -} - -func TestShadowedNestedValue(t *testing.T) { - - config := `name: steve -clothing: - jacket: leather - trousers: denim - pants: - size: large -` - initConfig("yaml", config) - - assert.Equal(t, "steve", GetString("name")) - - polyester := "polyester" - SetDefault("clothing.shirt", polyester) - SetDefault("clothing.jacket.price", 100) - - assert.Equal(t, "leather", GetString("clothing.jacket")) - assert.Nil(t, Get("clothing.jacket.price")) - assert.Equal(t, polyester, GetString("clothing.shirt")) - - clothingSettings := AllSettings()["clothing"].(map[string]interface{}) - assert.Equal(t, "leather", clothingSettings["jacket"]) - assert.Equal(t, polyester, clothingSettings["shirt"]) -} - -func TestDotParameter(t *testing.T) { - initJSON() - // shoud take precedence over batters defined in jsonExample - r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) - unmarshalReader(r, v.config) - - actual := Get("batters.batter") - expected := []interface{}{map[string]interface{}{"type": "Small"}} - assert.Equal(t, expected, actual) -} - -func TestCaseInsensitive(t *testing.T) { - for _, config := range []struct { - typ string - content string - }{ - {"yaml", ` -aBcD: 1 -eF: - gH: 2 - iJk: 3 - Lm: - nO: 4 - P: - Q: 5 - R: 6 -`}, - {"json", `{ - "aBcD": 1, - "eF": { - "iJk": 3, - "Lm": { - "P": { - "Q": 5, - "R": 6 - }, - "nO": 4 - }, - "gH": 2 - } -}`}, - {"toml", `aBcD = 1 -[eF] -gH = 2 -iJk = 3 -[eF.Lm] -nO = 4 -[eF.Lm.P] -Q = 5 -R = 6 -`}, - } { - doTestCaseInsensitive(t, config.typ, config.content) - } -} - -func TestCaseInsensitiveSet(t *testing.T) { - Reset() - m1 := map[string]interface{}{ - "Foo": 32, - "Bar": map[interface{}]interface { - }{ - "ABc": "A", - "cDE": "B"}, - } - - m2 := map[string]interface{}{ - "Foo": 52, - "Bar": map[interface{}]interface { - }{ - "bCd": "A", - "eFG": "B"}, - } - - Set("Given1", m1) - Set("Number1", 42) - - SetDefault("Given2", m2) - SetDefault("Number2", 52) - - // Verify SetDefault - if v := Get("number2"); v != 52 { - t.Fatalf("Expected 52 got %q", v) - } - - if v := Get("given2.foo"); v != 52 { - t.Fatalf("Expected 52 got %q", v) - } - - if v := Get("given2.bar.bcd"); v != "A" { - t.Fatalf("Expected A got %q", v) - } - - if _, ok := m2["Foo"]; !ok { - t.Fatal("Input map changed") - } - - // Verify Set - if v := Get("number1"); v != 42 { - t.Fatalf("Expected 42 got %q", v) - } - - if v := Get("given1.foo"); v != 32 { - t.Fatalf("Expected 32 got %q", v) - } - - if v := Get("given1.bar.abc"); v != "A" { - t.Fatalf("Expected A got %q", v) - } - - if _, ok := m1["Foo"]; !ok { - t.Fatal("Input map changed") - } -} - -func TestParseNested(t *testing.T) { - type duration struct { - Delay time.Duration - } - - type item struct { - Name string - Delay time.Duration - Nested duration - } - - config := `[[parent]] - delay="100ms" - [parent.nested] - delay="200ms" -` - initConfig("toml", config) - - var items []item - err := v.UnmarshalKey("parent", &items) + err := Unmarshal(&C) if err != nil { t.Fatalf("unable to decode into struct, %v", err) } - assert.Equal(t, 1, len(items)) - assert.Equal(t, 100*time.Millisecond, items[0].Delay) - assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) -} - -func doTestCaseInsensitive(t *testing.T, typ, config string) { - initConfig(typ, config) - Set("RfD", true) - assert.Equal(t, true, Get("rfd")) - assert.Equal(t, true, Get("rFD")) - assert.Equal(t, 1, cast.ToInt(Get("abcd"))) - assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) - assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) - assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) - assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) - assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) - -} - -func BenchmarkGetBool(b *testing.B) { - key := "BenchmarkGetBool" - v = New() - v.Set(key, true) - - for i := 0; i < b.N; i++ { - if !v.GetBool(key) { - b.Fatal("GetBool returned false") - } - } -} - -func BenchmarkGet(b *testing.B) { - key := "BenchmarkGet" - v = New() - v.Set(key, true) - - for i := 0; i < b.N; i++ { - if !v.Get(key).(bool) { - b.Fatal("Get returned false") - } - } -} - -// This is the "perfect result" for the above. -func BenchmarkGetBoolFromMap(b *testing.B) { - m := make(map[string]bool) - key := "BenchmarkGetBool" - m[key] = true - - for i := 0; i < b.N; i++ { - if !m[key] { - b.Fatal("Map value was false") - } - } + assert.Equal(t, &C, &config{Id: 1, FirstName: "Steve", Surname: "Owen"}) } diff --git a/vendor/golang.org/x/sys/plan9/mkall.sh b/vendor/golang.org/x/sys/plan9/mkall.sh old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/plan9/mkerrors.sh b/vendor/golang.org/x/sys/plan9/mkerrors.sh old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/plan9/mksyscall.pl b/vendor/golang.org/x/sys/plan9/mksyscall.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh b/vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/linux/mksysnum.pl b/vendor/golang.org/x/sys/unix/linux/mksysnum.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksyscall.pl b/vendor/golang.org/x/sys/unix/mksyscall.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksyscall_solaris.pl b/vendor/golang.org/x/sys/unix/mksyscall_solaris.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksysctl_openbsd.pl b/vendor/golang.org/x/sys/unix/mksysctl_openbsd.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksysnum_darwin.pl b/vendor/golang.org/x/sys/unix/mksysnum_darwin.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksysnum_dragonfly.pl b/vendor/golang.org/x/sys/unix/mksysnum_dragonfly.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksysnum_freebsd.pl b/vendor/golang.org/x/sys/unix/mksysnum_freebsd.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksysnum_netbsd.pl b/vendor/golang.org/x/sys/unix/mksysnum_netbsd.pl old mode 100755 new mode 100644 diff --git a/vendor/golang.org/x/sys/unix/mksysnum_openbsd.pl b/vendor/golang.org/x/sys/unix/mksysnum_openbsd.pl old mode 100755 new mode 100644 diff --git a/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto old mode 100755 new mode 100644 diff --git a/vendor/google.golang.org/appengine/internal/regen.sh b/vendor/google.golang.org/appengine/internal/regen.sh old mode 100755 new mode 100644 diff --git a/vendor/github.com/fsnotify/fsnotify/.gitignore b/vendor/gopkg.in/fsnotify.v1/.gitignore similarity index 100% rename from vendor/github.com/fsnotify/fsnotify/.gitignore rename to vendor/gopkg.in/fsnotify.v1/.gitignore diff --git a/vendor/github.com/fsnotify/fsnotify/.travis.yml b/vendor/gopkg.in/fsnotify.v1/.travis.yml similarity index 78% rename from vendor/github.com/fsnotify/fsnotify/.travis.yml rename to vendor/gopkg.in/fsnotify.v1/.travis.yml index 981d1bb81..70988769c 100644 --- a/vendor/github.com/fsnotify/fsnotify/.travis.yml +++ b/vendor/gopkg.in/fsnotify.v1/.travis.yml @@ -2,14 +2,7 @@ sudo: false language: go go: - - 1.8.x - - 1.9.x - - tip - -matrix: - allow_failures: - - go: tip - fast_finish: true + - 1.5.2 before_script: - go get -u github.com/golang/lint/golint diff --git a/vendor/github.com/fsnotify/fsnotify/AUTHORS b/vendor/gopkg.in/fsnotify.v1/AUTHORS similarity index 75% rename from vendor/github.com/fsnotify/fsnotify/AUTHORS rename to vendor/gopkg.in/fsnotify.v1/AUTHORS index 5ab5d41c5..2b9d1fdd6 100644 --- a/vendor/github.com/fsnotify/fsnotify/AUTHORS +++ b/vendor/gopkg.in/fsnotify.v1/AUTHORS @@ -8,12 +8,7 @@ # Please keep the list sorted. -Aaron L Adrien Bustany -Amit Krishnan -Anmol Sethi -Bjørn Erik Pedersen -Bruno Bigras Caleb Spare Case Nelson Chris Howey @@ -28,23 +23,16 @@ Kelvin Fo Ken-ichirou MATSUZAWA Matt Layher Nathan Youngman -Nickolai Zeldovich -Patrick Paul Hammond Pawel Knap Pieter Droogendijk Pursuit92 Riku Voipio Rob Figueiredo -Rodrigo Chiossi -Slawek Ligus Soge Zhang -Tiffany Jernigan Tilak Sharma -Tom Payne Travis Cline Tudor Golubenco -Vahe Khachikyan Yukang bronze1man debrando diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md similarity index 71% rename from vendor/github.com/fsnotify/fsnotify/CHANGELOG.md rename to vendor/gopkg.in/fsnotify.v1/CHANGELOG.md index be4d7ea2c..d37247e0b 100644 --- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md +++ b/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md @@ -1,106 +1,72 @@ # Changelog -## v1.4.7 / 2018-01-09 - -* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine) -* Tests: Fix missing verb on format string (thanks @rchiossi) -* Linux: Fix deadlock in Remove (thanks @aarondl) -* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne) -* Docs: Moved FAQ into the README (thanks @vahe) -* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich) -* Docs: replace references to OS X with macOS - -## v1.4.2 / 2016-10-10 - -* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack) - -## v1.4.1 / 2016-10-04 - -* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack) - -## v1.4.0 / 2016-10-01 - -* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie) - -## v1.3.1 / 2016-06-28 - -* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc) - -## v1.3.0 / 2016-04-19 - -* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135) - -## v1.2.10 / 2016-03-02 - -* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj) - ## v1.2.9 / 2016-01-13 -kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep) +kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/go-fsnotify/fsnotify/pull/111) (thanks @bep) ## v1.2.8 / 2015-12-17 -* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test) +* kqueue: fix race condition in Close [#105](https://github.com/go-fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test) * inotify: fix race in test * enable race detection for continuous integration (Linux, Mac, Windows) ## v1.2.5 / 2015-10-17 -* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki) -* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken) -* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie) -* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion) +* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/go-fsnotify/fsnotify/pull/100) (thanks @suihkulokki) +* inotify: fix path leaks [#73](https://github.com/go-fsnotify/fsnotify/pull/73) (thanks @chamaken) +* kqueue: watch for rename events on subdirectories [#83](https://github.com/go-fsnotify/fsnotify/pull/83) (thanks @guotie) +* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/go-fsnotify/fsnotify/pull/101) (thanks @illicitonion) ## v1.2.1 / 2015-10-14 -* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx) +* kqueue: don't watch named pipes [#98](https://github.com/go-fsnotify/fsnotify/pull/98) (thanks @evanphx) ## v1.2.0 / 2015-02-08 -* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD) -* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD) -* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59) +* inotify: use epoll to wake up readEvents [#66](https://github.com/go-fsnotify/fsnotify/pull/66) (thanks @PieterD) +* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/go-fsnotify/fsnotify/pull/63) (thanks @PieterD) +* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/go-fsnotify/fsnotify/issues/59) ## v1.1.1 / 2015-02-05 -* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD) +* inotify: Retry read on EINTR [#61](https://github.com/go-fsnotify/fsnotify/issues/61) (thanks @PieterD) ## v1.1.0 / 2014-12-12 -* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43) +* kqueue: rework internals [#43](https://github.com/go-fsnotify/fsnotify/pull/43) * add low-level functions * only need to store flags on directories - * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13) + * less mutexes [#13](https://github.com/go-fsnotify/fsnotify/issues/13) * done can be an unbuffered channel * remove calls to os.NewSyscallError -* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher) -* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48) -* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) +* More efficient string concatenation for Event.String() [#52](https://github.com/go-fsnotify/fsnotify/pull/52) (thanks @mdlayher) +* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/go-fsnotify/fsnotify/issues/48) +* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) ## v1.0.4 / 2014-09-07 * kqueue: add dragonfly to the build tags. * Rename source code files, rearrange code so exported APIs are at the top. -* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang) +* Add done channel to example code. [#37](https://github.com/go-fsnotify/fsnotify/pull/37) (thanks @chenyukang) ## v1.0.3 / 2014-08-19 -* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36) +* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/go-fsnotify/fsnotify/issues/36) ## v1.0.2 / 2014-08-17 -* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) +* [Fix] Missing create events on OS X. [#14](https://github.com/go-fsnotify/fsnotify/issues/14) (thanks @zhsso) * [Fix] Make ./path and path equivalent. (thanks @zhsso) ## v1.0.0 / 2014-08-15 * [API] Remove AddWatch on Windows, use Add. -* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30) +* Improve documentation for exported identifiers. [#30](https://github.com/go-fsnotify/fsnotify/issues/30) * Minor updates based on feedback from golint. ## dev / 2014-07-09 -* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify). +* Moved to [github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify). * Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno) ## dev / 2014-07-04 @@ -110,7 +76,7 @@ kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsn ## dev / 2014-06-28 -* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4) +* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/go-fsnotify/fsnotify/issues/4) * Fix for String() method on Event (thanks Alex Brainman) * Don't build on Plan 9 or Solaris (thanks @4ad) @@ -148,11 +114,11 @@ kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsn ## v0.9.3 / 2014-12-31 -* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) +* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) ## v0.9.2 / 2014-08-17 -* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) +* [Backport] Fix missing create events on OS X. [#14](https://github.com/go-fsnotify/fsnotify/issues/14) (thanks @zhsso) ## v0.9.1 / 2014-06-12 @@ -171,7 +137,7 @@ kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsn ## v0.8.11 / 2013-11-02 * [Doc] Add Changelog [#72][] (thanks @nathany) -* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond) +* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond) ## v0.8.10 / 2013-10-19 diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/gopkg.in/fsnotify.v1/CONTRIBUTING.md similarity index 76% rename from vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md rename to vendor/gopkg.in/fsnotify.v1/CONTRIBUTING.md index 828a60b24..0f377f341 100644 --- a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md +++ b/vendor/gopkg.in/fsnotify.v1/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Issues -* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues). +* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/go-fsnotify/fsnotify/issues). * Please indicate the platform you are using fsnotify on. * A code example to reproduce the problem is appreciated. @@ -10,14 +10,14 @@ ### Contributor License Agreement -fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). +fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/go-fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/go-fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). Please indicate that you have signed the CLA in your pull request. ### How fsnotify is Developed * Development is done on feature branches. -* Tests are run on BSD, Linux, macOS and Windows. +* Tests are run on BSD, Linux, OS X and Windows. * Pull requests are reviewed and [applied to master][am] using [hub][]. * Maintainers may modify or squash commits rather than asking contributors to. * To issue a new release, the maintainers will: @@ -28,7 +28,7 @@ Please indicate that you have signed the CLA in your pull request. For smooth sailing, always use the original import path. Installing with `go get` makes this easy. -1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`) +1. Install from GitHub (`go get -u github.com/go-fsnotify/fsnotify`) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Ensure everything works and the tests pass (see below) 4. Commit your changes (`git commit -am 'Add some feature'`) @@ -40,11 +40,11 @@ Contribute upstream: 3. Push to the branch (`git push fork my-new-feature`) 4. Create a new Pull Request on GitHub -This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/). +This workflow is [thoroughly explained by Katrina Owen](https://blog.splice.com/contributing-open-source-git-repositories-go/). ### Testing -fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows. +fsnotify uses build tags to compile different code on Linux, BSD, OS X, and Windows. Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. @@ -53,12 +53,12 @@ To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. * Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) * Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. * Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password) -* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`. +* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd go-fsnotify/fsnotify; go test'`. * When you're done, you will want to halt or destroy the Vagrant boxes. Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. -Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). +Right now there is no equivalent solution for Windows and OS X, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). ### Maintainers diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/gopkg.in/fsnotify.v1/LICENSE similarity index 100% rename from vendor/github.com/fsnotify/fsnotify/LICENSE rename to vendor/gopkg.in/fsnotify.v1/LICENSE diff --git a/vendor/gopkg.in/fsnotify.v1/README.md b/vendor/gopkg.in/fsnotify.v1/README.md new file mode 100644 index 000000000..f2b432e96 --- /dev/null +++ b/vendor/gopkg.in/fsnotify.v1/README.md @@ -0,0 +1,64 @@ +# File system notifications for Go + +[![GoDoc](https://godoc.org/gopkg.in/fsnotify.v1?status.svg)](https://godoc.org/gopkg.in/fsnotify.v1) [![Coverage](http://gocover.io/_badge/github.com/go-fsnotify/fsnotify)](http://gocover.io/github.com/go-fsnotify/fsnotify) + +Go 1.3+ required. + +Cross platform: Windows, Linux, BSD and OS X. + +|Adapter |OS |Status | +|----------|----------|----------| +|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/go-fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/go-fsnotify/fsnotify)| +|kqueue |BSD, OS X, iOS\*|Supported [![Build Status](https://travis-ci.org/go-fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/go-fsnotify/fsnotify)| +|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| +|FSEvents |OS X |[Planned](https://github.com/go-fsnotify/fsnotify/issues/11)| +|FEN |Solaris 11 |[Planned](https://github.com/go-fsnotify/fsnotify/issues/12)| +|fanotify |Linux 2.6.37+ | | +|USN Journals |Windows |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/53)| +|Polling |*All* |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/9)| + +\* Android and iOS are untested. + +Please see [the documentation](https://godoc.org/gopkg.in/fsnotify.v1) for usage. Consult the [Wiki](https://github.com/go-fsnotify/fsnotify/wiki) for the FAQ and further information. + +## API stability + +Two major versions of fsnotify exist. + +**[fsnotify.v0](https://gopkg.in/fsnotify.v0)** is API-compatible with [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify). Bugfixes *may* be backported, but I recommend upgrading to v1. + +```go +import "gopkg.in/fsnotify.v0" +``` + +\* Refer to the package as fsnotify (without the .v0 suffix). + +**[fsnotify.v1](https://gopkg.in/fsnotify.v1)** provides [a new API](https://godoc.org/gopkg.in/fsnotify.v1) based on [this design document](http://goo.gl/MrYxyA). You can import v1 with: + +```go +import "gopkg.in/fsnotify.v1" +``` + +Further API changes are [planned](https://github.com/go-fsnotify/fsnotify/milestones), but a new major revision will be tagged, so you can depend on the v1 API. + +**Master** may have unreleased changes. Use it to test the very latest code or when [contributing][], but don't expect it to remain API-compatible: + +```go +import "github.com/go-fsnotify/fsnotify" +``` + +## Contributing + +Please refer to [CONTRIBUTING][] before opening an issue or pull request. + +## Example + +See [example_test.go](https://github.com/go-fsnotify/fsnotify/blob/master/example_test.go). + +[contributing]: https://github.com/go-fsnotify/fsnotify/blob/master/CONTRIBUTING.md + +## Related Projects + +* [notify](https://github.com/rjeczalik/notify) +* [fsevents](https://github.com/go-fsnotify/fsevents) + diff --git a/vendor/github.com/fsnotify/fsnotify/example_test.go b/vendor/gopkg.in/fsnotify.v1/example_test.go similarity index 92% rename from vendor/github.com/fsnotify/fsnotify/example_test.go rename to vendor/gopkg.in/fsnotify.v1/example_test.go index 700502cb3..306379660 100644 --- a/vendor/github.com/fsnotify/fsnotify/example_test.go +++ b/vendor/gopkg.in/fsnotify.v1/example_test.go @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !plan9 +// +build !plan9,!solaris package fsnotify_test import ( "log" - "github.com/fsnotify/fsnotify" + "github.com/go-fsnotify/fsnotify" ) func ExampleNewWatcher() { diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/gopkg.in/fsnotify.v1/fsnotify.go similarity index 72% rename from vendor/github.com/fsnotify/fsnotify/fsnotify.go rename to vendor/gopkg.in/fsnotify.v1/fsnotify.go index 190bf0de5..c899ee008 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/gopkg.in/fsnotify.v1/fsnotify.go @@ -2,14 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !plan9 +// +build !plan9,!solaris // Package fsnotify provides a platform-independent interface for file system notifications. package fsnotify import ( "bytes" - "errors" "fmt" ) @@ -31,36 +30,33 @@ const ( Chmod ) -func (op Op) String() string { +// String returns a string representation of the event in the form +// "file: REMOVE|WRITE|..." +func (e Event) String() string { // Use a buffer for efficient string concatenation var buffer bytes.Buffer - if op&Create == Create { + if e.Op&Create == Create { buffer.WriteString("|CREATE") } - if op&Remove == Remove { + if e.Op&Remove == Remove { buffer.WriteString("|REMOVE") } - if op&Write == Write { + if e.Op&Write == Write { buffer.WriteString("|WRITE") } - if op&Rename == Rename { + if e.Op&Rename == Rename { buffer.WriteString("|RENAME") } - if op&Chmod == Chmod { + if e.Op&Chmod == Chmod { buffer.WriteString("|CHMOD") } + + // If buffer remains empty, return no event names if buffer.Len() == 0 { - return "" + return fmt.Sprintf("%q: ", e.Name) } - return buffer.String()[1:] // Strip leading pipe -} -// String returns a string representation of the event in the form -// "file: REMOVE|WRITE|..." -func (e Event) String() string { - return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) + // Return a list of event names, with leading pipe character stripped + return fmt.Sprintf("%q: %s", e.Name, buffer.String()[1:]) } - -// Common errors that can be reported by a watcher -var ErrEventOverflow = errors.New("fsnotify queue overflow") diff --git a/vendor/github.com/fsnotify/fsnotify/inotify.go b/vendor/gopkg.in/fsnotify.v1/inotify.go similarity index 71% rename from vendor/github.com/fsnotify/fsnotify/inotify.go rename to vendor/gopkg.in/fsnotify.v1/inotify.go index d9fd1b88a..06f4bba88 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify.go +++ b/vendor/gopkg.in/fsnotify.v1/inotify.go @@ -14,9 +14,8 @@ import ( "path/filepath" "strings" "sync" + "syscall" "unsafe" - - "golang.org/x/sys/unix" ) // Watcher watches a set of files, delivering events to a channel. @@ -24,6 +23,7 @@ type Watcher struct { Events chan Event Errors chan error mu sync.Mutex // Map access + cv *sync.Cond // sync removing on rm_watch with IN_IGNORE fd int poller *fdPoller watches map[string]*watch // Map of inotify watches (key: path) @@ -35,14 +35,14 @@ type Watcher struct { // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. func NewWatcher() (*Watcher, error) { // Create inotify fd - fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) + fd, errno := syscall.InotifyInit() if fd == -1 { return nil, errno } // Create epoll poller, err := newFdPoller(fd) if err != nil { - unix.Close(fd) + syscall.Close(fd) return nil, err } w := &Watcher{ @@ -55,6 +55,7 @@ func NewWatcher() (*Watcher, error) { done: make(chan struct{}), doneResp: make(chan struct{}), } + w.cv = sync.NewCond(&w.mu) go w.readEvents() return w, nil @@ -94,30 +95,28 @@ func (w *Watcher) Add(name string) error { return errors.New("inotify instance already closed") } - const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | - unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | - unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF + const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM | + syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY | + syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF var flags uint32 = agnosticEvents w.mu.Lock() - defer w.mu.Unlock() - watchEntry := w.watches[name] - if watchEntry != nil { - flags |= watchEntry.flags | unix.IN_MASK_ADD + watchEntry, found := w.watches[name] + w.mu.Unlock() + if found { + watchEntry.flags |= flags + flags |= syscall.IN_MASK_ADD } - wd, errno := unix.InotifyAddWatch(w.fd, name, flags) + wd, errno := syscall.InotifyAddWatch(w.fd, name, flags) if wd == -1 { return errno } - if watchEntry == nil { - w.watches[name] = &watch{wd: uint32(wd), flags: flags} - w.paths[wd] = name - } else { - watchEntry.wd = uint32(wd) - watchEntry.flags = flags - } + w.mu.Lock() + w.watches[name] = &watch{wd: uint32(wd), flags: flags} + w.paths[wd] = name + w.mu.Unlock() return nil } @@ -135,20 +134,13 @@ func (w *Watcher) Remove(name string) error { if !ok { return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) } - - // We successfully removed the watch if InotifyRmWatch doesn't return an - // error, we need to clean up our internal state to ensure it matches - // inotify's kernel state. - delete(w.paths, int(watch.wd)) - delete(w.watches, name) - // inotify_rm_watch will return EINVAL if the file has been deleted; // the inotify will already have been removed. // watches and pathes are deleted in ignoreLinux() implicitly and asynchronously // by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE // so that EINVAL means that the wd is being rm_watch()ed or its file removed // by another thread and we have not received IN_IGNORE event. - success, errno := unix.InotifyRmWatch(w.fd, watch.wd) + success, errno := syscall.InotifyRmWatch(w.fd, watch.wd) if success == -1 { // TODO: Perhaps it's not helpful to return an error here in every case. // the only two possible errors are: @@ -159,6 +151,13 @@ func (w *Watcher) Remove(name string) error { return errno } + // wait until ignoreLinux() deleting maps + exists := true + for exists { + w.cv.Wait() + _, exists = w.watches[name] + } + return nil } @@ -171,16 +170,16 @@ type watch struct { // received events into Event objects and sends them via the Events channel func (w *Watcher) readEvents() { var ( - buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events - n int // Number of bytes read with read() - errno error // Syscall errno - ok bool // For poller.wait + buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events + n int // Number of bytes read with read() + errno error // Syscall errno + ok bool // For poller.wait ) defer close(w.doneResp) defer close(w.Errors) defer close(w.Events) - defer unix.Close(w.fd) + defer syscall.Close(w.fd) defer w.poller.close() for { @@ -203,26 +202,26 @@ func (w *Watcher) readEvents() { continue } - n, errno = unix.Read(w.fd, buf[:]) + n, errno = syscall.Read(w.fd, buf[:]) // If a signal interrupted execution, see if we've been asked to close, and try again. // http://man7.org/linux/man-pages/man7/signal.7.html : // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" - if errno == unix.EINTR { + if errno == syscall.EINTR { continue } - // unix.Read might have been woken up by Close. If so, we're done. + // syscall.Read might have been woken up by Close. If so, we're done. if w.isClosed() { return } - if n < unix.SizeofInotifyEvent { + if n < syscall.SizeofInotifyEvent { var err error if n == 0 { // If EOF is received. This should really never happen. err = io.EOF } else if n < 0 { - // If an error occurred while reading. + // If an error occured while reading. err = errno } else { // Read was too short. @@ -239,40 +238,22 @@ func (w *Watcher) readEvents() { var offset uint32 // We don't know how many events we just read into the buffer // While the offset points to at least one whole event... - for offset <= uint32(n-unix.SizeofInotifyEvent) { + for offset <= uint32(n-syscall.SizeofInotifyEvent) { // Point "raw" to the event in the buffer - raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) + raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset])) mask := uint32(raw.Mask) nameLen := uint32(raw.Len) - - if mask&unix.IN_Q_OVERFLOW != 0 { - select { - case w.Errors <- ErrEventOverflow: - case <-w.done: - return - } - } - // If the event happened to the watched directory or the watched file, the kernel // doesn't append the filename to the event, but we would like to always fill the // the "Name" field with a valid filename. We retrieve the path of the watch from // the "paths" map. w.mu.Lock() - name, ok := w.paths[int(raw.Wd)] - // IN_DELETE_SELF occurs when the file/directory being watched is removed. - // This is a sign to clean up the maps, otherwise we are no longer in sync - // with the inotify kernel state which has already deleted the watch - // automatically. - if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { - delete(w.paths, int(raw.Wd)) - delete(w.watches, name) - } + name := w.paths[int(raw.Wd)] w.mu.Unlock() - if nameLen > 0 { // Point "bytes" at the first byte of the filename - bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) + bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent])) // The filename is padded with NULL bytes. TrimRight() gets rid of those. name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") } @@ -280,7 +261,7 @@ func (w *Watcher) readEvents() { event := newEvent(name, mask) // Send the events that are not ignored on the events channel - if !event.ignoreLinux(mask) { + if !event.ignoreLinux(w, raw.Wd, mask) { select { case w.Events <- event: case <-w.done: @@ -289,7 +270,7 @@ func (w *Watcher) readEvents() { } // Move to the next event in the buffer - offset += unix.SizeofInotifyEvent + nameLen + offset += syscall.SizeofInotifyEvent + nameLen } } } @@ -297,9 +278,15 @@ func (w *Watcher) readEvents() { // Certain types of events can be "ignored" and not sent over the Events // channel. Such as events marked ignore by the kernel, or MODIFY events // against files that do not exist. -func (e *Event) ignoreLinux(mask uint32) bool { +func (e *Event) ignoreLinux(w *Watcher, wd int32, mask uint32) bool { // Ignore anything the inotify API says to ignore - if mask&unix.IN_IGNORED == unix.IN_IGNORED { + if mask&syscall.IN_IGNORED == syscall.IN_IGNORED { + w.mu.Lock() + defer w.mu.Unlock() + name := w.paths[int(wd)] + delete(w.paths, int(wd)) + delete(w.watches, name) + w.cv.Broadcast() return true } @@ -318,19 +305,19 @@ func (e *Event) ignoreLinux(mask uint32) bool { // newEvent returns an platform-independent Event based on an inotify mask. func newEvent(name string, mask uint32) Event { e := Event{Name: name} - if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { + if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO { e.Op |= Create } - if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { + if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE { e.Op |= Remove } - if mask&unix.IN_MODIFY == unix.IN_MODIFY { + if mask&syscall.IN_MODIFY == syscall.IN_MODIFY { e.Op |= Write } - if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { + if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM { e.Op |= Rename } - if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { + if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB { e.Op |= Chmod } return e diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/gopkg.in/fsnotify.v1/inotify_poller.go similarity index 76% rename from vendor/github.com/fsnotify/fsnotify/inotify_poller.go rename to vendor/gopkg.in/fsnotify.v1/inotify_poller.go index cc7db4b22..23a5ca146 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go +++ b/vendor/gopkg.in/fsnotify.v1/inotify_poller.go @@ -8,8 +8,7 @@ package fsnotify import ( "errors" - - "golang.org/x/sys/unix" + "syscall" ) type fdPoller struct { @@ -40,32 +39,32 @@ func newFdPoller(fd int) (*fdPoller, error) { poller.fd = fd // Create epoll fd - poller.epfd, errno = unix.EpollCreate1(0) + poller.epfd, errno = syscall.EpollCreate1(0) if poller.epfd == -1 { return nil, errno } // Create pipe; pipe[0] is the read end, pipe[1] the write end. - errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) + errno = syscall.Pipe2(poller.pipe[:], syscall.O_NONBLOCK) if errno != nil { return nil, errno } // Register inotify fd with epoll - event := unix.EpollEvent{ + event := syscall.EpollEvent{ Fd: int32(poller.fd), - Events: unix.EPOLLIN, + Events: syscall.EPOLLIN, } - errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) + errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.fd, &event) if errno != nil { return nil, errno } // Register pipe fd with epoll - event = unix.EpollEvent{ + event = syscall.EpollEvent{ Fd: int32(poller.pipe[0]), - Events: unix.EPOLLIN, + Events: syscall.EPOLLIN, } - errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) + errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.pipe[0], &event) if errno != nil { return nil, errno } @@ -81,11 +80,11 @@ func (poller *fdPoller) wait() (bool, error) { // I don't know whether epoll_wait returns the number of events returned, // or the total number of events ready. // I decided to catch both by making the buffer one larger than the maximum. - events := make([]unix.EpollEvent, 7) + events := make([]syscall.EpollEvent, 7) for { - n, errno := unix.EpollWait(poller.epfd, events, -1) + n, errno := syscall.EpollWait(poller.epfd, events, -1) if n == -1 { - if errno == unix.EINTR { + if errno == syscall.EINTR { continue } return false, errno @@ -104,31 +103,31 @@ func (poller *fdPoller) wait() (bool, error) { epollin := false for _, event := range ready { if event.Fd == int32(poller.fd) { - if event.Events&unix.EPOLLHUP != 0 { + if event.Events&syscall.EPOLLHUP != 0 { // This should not happen, but if it does, treat it as a wakeup. epollhup = true } - if event.Events&unix.EPOLLERR != 0 { + if event.Events&syscall.EPOLLERR != 0 { // If an error is waiting on the file descriptor, we should pretend - // something is ready to read, and let unix.Read pick up the error. + // something is ready to read, and let syscall.Read pick up the error. epollerr = true } - if event.Events&unix.EPOLLIN != 0 { + if event.Events&syscall.EPOLLIN != 0 { // There is data to read. epollin = true } } if event.Fd == int32(poller.pipe[0]) { - if event.Events&unix.EPOLLHUP != 0 { + if event.Events&syscall.EPOLLHUP != 0 { // Write pipe descriptor was closed, by us. This means we're closing down the // watcher, and we should wake up. } - if event.Events&unix.EPOLLERR != 0 { + if event.Events&syscall.EPOLLERR != 0 { // If an error is waiting on the pipe file descriptor. // This is an absolute mystery, and should never ever happen. return false, errors.New("Error on the pipe descriptor.") } - if event.Events&unix.EPOLLIN != 0 { + if event.Events&syscall.EPOLLIN != 0 { // This is a regular wakeup, so we have to clear the buffer. err := poller.clearWake() if err != nil { @@ -148,9 +147,9 @@ func (poller *fdPoller) wait() (bool, error) { // Close the write end of the poller. func (poller *fdPoller) wake() error { buf := make([]byte, 1) - n, errno := unix.Write(poller.pipe[1], buf) + n, errno := syscall.Write(poller.pipe[1], buf) if n == -1 { - if errno == unix.EAGAIN { + if errno == syscall.EAGAIN { // Buffer is full, poller will wake. return nil } @@ -162,9 +161,9 @@ func (poller *fdPoller) wake() error { func (poller *fdPoller) clearWake() error { // You have to be woken up a LOT in order to get to 100! buf := make([]byte, 100) - n, errno := unix.Read(poller.pipe[0], buf) + n, errno := syscall.Read(poller.pipe[0], buf) if n == -1 { - if errno == unix.EAGAIN { + if errno == syscall.EAGAIN { // Buffer is empty, someone else cleared our wake. return nil } @@ -176,12 +175,12 @@ func (poller *fdPoller) clearWake() error { // Close all poller file descriptors, but not the one passed to it. func (poller *fdPoller) close() { if poller.pipe[1] != -1 { - unix.Close(poller.pipe[1]) + syscall.Close(poller.pipe[1]) } if poller.pipe[0] != -1 { - unix.Close(poller.pipe[0]) + syscall.Close(poller.pipe[0]) } if poller.epfd != -1 { - unix.Close(poller.epfd) + syscall.Close(poller.epfd) } } diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller_test.go b/vendor/gopkg.in/fsnotify.v1/inotify_poller_test.go similarity index 94% rename from vendor/github.com/fsnotify/fsnotify/inotify_poller_test.go rename to vendor/gopkg.in/fsnotify.v1/inotify_poller_test.go index 26623efef..af9f407f8 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify_poller_test.go +++ b/vendor/gopkg.in/fsnotify.v1/inotify_poller_test.go @@ -7,17 +7,16 @@ package fsnotify import ( + "syscall" "testing" "time" - - "golang.org/x/sys/unix" ) type testFd [2]int func makeTestFd(t *testing.T) testFd { var tfd testFd - errno := unix.Pipe(tfd[:]) + errno := syscall.Pipe(tfd[:]) if errno != nil { t.Fatalf("Failed to create pipe: %v", errno) } @@ -29,7 +28,7 @@ func (tfd testFd) fd() int { } func (tfd testFd) closeWrite(t *testing.T) { - errno := unix.Close(tfd[1]) + errno := syscall.Close(tfd[1]) if errno != nil { t.Fatalf("Failed to close write end of pipe: %v", errno) } @@ -37,7 +36,7 @@ func (tfd testFd) closeWrite(t *testing.T) { func (tfd testFd) put(t *testing.T) { buf := make([]byte, 10) - _, errno := unix.Write(tfd[1], buf) + _, errno := syscall.Write(tfd[1], buf) if errno != nil { t.Fatalf("Failed to write to pipe: %v", errno) } @@ -45,15 +44,15 @@ func (tfd testFd) put(t *testing.T) { func (tfd testFd) get(t *testing.T) { buf := make([]byte, 10) - _, errno := unix.Read(tfd[0], buf) + _, errno := syscall.Read(tfd[0], buf) if errno != nil { t.Fatalf("Failed to read from pipe: %v", errno) } } func (tfd testFd) close() { - unix.Close(tfd[1]) - unix.Close(tfd[0]) + syscall.Close(tfd[1]) + syscall.Close(tfd[0]) } func makePoller(t *testing.T) (testFd, *fdPoller) { @@ -67,7 +66,7 @@ func makePoller(t *testing.T) (testFd, *fdPoller) { func TestPollerWithBadFd(t *testing.T) { _, err := newFdPoller(-1) - if err != unix.EBADF { + if err != syscall.EBADF { t.Fatalf("Expected EBADF, got: %v", err) } } diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_test.go b/vendor/gopkg.in/fsnotify.v1/inotify_test.go similarity index 55% rename from vendor/github.com/fsnotify/fsnotify/inotify_test.go rename to vendor/gopkg.in/fsnotify.v1/inotify_test.go index 54f3f00eb..31660f8e1 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify_test.go +++ b/vendor/gopkg.in/fsnotify.v1/inotify_test.go @@ -10,7 +10,7 @@ import ( "fmt" "os" "path/filepath" - "strings" + "syscall" "testing" "time" ) @@ -21,7 +21,7 @@ func TestInotifyCloseRightAway(t *testing.T) { t.Fatalf("Failed to create watcher") } - // Close immediately; it won't even reach the first unix.Read. + // Close immediately; it won't even reach the first syscall.Read. w.Close() // Wait for the close to complete. @@ -35,7 +35,7 @@ func TestInotifyCloseSlightlyLater(t *testing.T) { t.Fatalf("Failed to create watcher") } - // Wait until readEvents has reached unix.Read, and Close. + // Wait until readEvents has reached syscall.Read, and Close. <-time.After(50 * time.Millisecond) w.Close() @@ -54,7 +54,7 @@ func TestInotifyCloseSlightlyLaterWithWatch(t *testing.T) { } w.Add(testDir) - // Wait until readEvents has reached unix.Read, and Close. + // Wait until readEvents has reached syscall.Read, and Close. <-time.After(50 * time.Millisecond) w.Close() @@ -137,7 +137,7 @@ func TestInotifyCloseCreate(t *testing.T) { } // At this point, we've received one event, so the goroutine is ready. - // It's also blocking on unix.Read. + // It's also blocking on syscall.Read. // Now we try to swap the file descriptor under its nose. w.Close() w, err = NewWatcher() @@ -153,14 +153,10 @@ func TestInotifyCloseCreate(t *testing.T) { } } -// This test verifies the watcher can keep up with file creations/deletions -// when under load. func TestInotifyStress(t *testing.T) { - maxNumToCreate := 1000 - testDir := tempMkdir(t) defer os.RemoveAll(testDir) - testFilePrefix := filepath.Join(testDir, "testfile") + testFile := filepath.Join(testDir, "testfile") w, err := NewWatcher() if err != nil { @@ -168,85 +164,84 @@ func TestInotifyStress(t *testing.T) { } defer w.Close() + killchan := make(chan struct{}) + defer close(killchan) + err = w.Add(testDir) if err != nil { t.Fatalf("Failed to add testDir: %v", err) } - doneChan := make(chan struct{}) - // The buffer ensures that the file generation goroutine is never blocked. - errChan := make(chan error, 2*maxNumToCreate) + proc, err := os.FindProcess(os.Getpid()) + if err != nil { + t.Fatalf("Error finding process: %v", err) + } go func() { - for i := 0; i < maxNumToCreate; i++ { - testFile := fmt.Sprintf("%s%d", testFilePrefix, i) - - handle, err := os.Create(testFile) - if err != nil { - errChan <- fmt.Errorf("Create failed: %v", err) - continue + for { + select { + case <-time.After(5 * time.Millisecond): + err := proc.Signal(syscall.SIGUSR1) + if err != nil { + t.Fatalf("Signal failed: %v", err) + } + case <-killchan: + return } + } + }() - err = handle.Close() - if err != nil { - errChan <- fmt.Errorf("Close failed: %v", err) - continue + go func() { + for { + select { + case <-time.After(11 * time.Millisecond): + err := w.poller.wake() + if err != nil { + t.Fatalf("Wake failed: %v", err) + } + case <-killchan: + return } } + }() - // If we delete a newly created file too quickly, inotify will skip the - // create event and only send the delete event. - time.Sleep(100 * time.Millisecond) - - for i := 0; i < maxNumToCreate; i++ { - testFile := fmt.Sprintf("%s%d", testFilePrefix, i) - err = os.Remove(testFile) - if err != nil { - errChan <- fmt.Errorf("Remove failed: %v", err) + go func() { + for { + select { + case <-killchan: + return + default: + handle, err := os.Create(testFile) + if err != nil { + t.Fatalf("Create failed: %v", err) + } + handle.Close() + time.Sleep(time.Millisecond) + err = os.Remove(testFile) + if err != nil { + t.Fatalf("Remove failed: %v", err) + } } } - - close(doneChan) }() creates := 0 removes := 0 - - finished := false - after := time.After(10 * time.Second) - for !finished { + after := time.After(5 * time.Second) + for { select { case <-after: - t.Fatalf("Not done") - case <-doneChan: - finished = true - case err := <-errChan: - t.Fatalf("Got an error from file creator goroutine: %v", err) - case err := <-w.Errors: - t.Fatalf("Got an error from watcher: %v", err) - case evt := <-w.Events: - if !strings.HasPrefix(evt.Name, testFilePrefix) { - t.Fatalf("Got an event for an unknown file: %s", evt.Name) + if creates-removes > 1 || creates-removes < -1 { + t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes) } - if evt.Op == Create { - creates++ - } - if evt.Op == Remove { - removes++ + if creates < 50 { + t.Fatalf("Expected at least 50 creates, got %d", creates) } - } - } - - // Drain remaining events from channels - count := 0 - for count < 10 { - select { - case err := <-errChan: - t.Fatalf("Got an error from file creator goroutine: %v", err) + return case err := <-w.Errors: t.Fatalf("Got an error from watcher: %v", err) case evt := <-w.Events: - if !strings.HasPrefix(evt.Name, testFilePrefix) { + if evt.Name != testFile { t.Fatalf("Got an event for an unknown file: %s", evt.Name) } if evt.Op == Create { @@ -255,20 +250,8 @@ func TestInotifyStress(t *testing.T) { if evt.Op == Remove { removes++ } - count = 0 - default: - count++ - // Give the watcher chances to fill the channels. - time.Sleep(time.Millisecond) } } - - if creates-removes > 1 || creates-removes < -1 { - t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes) - } - if creates < 50 { - t.Fatalf("Expected at least 50 creates, got %d", creates) - } } func TestInotifyRemoveTwice(t *testing.T) { @@ -293,23 +276,25 @@ func TestInotifyRemoveTwice(t *testing.T) { t.Fatalf("Failed to add testFile: %v", err) } - err = w.Remove(testFile) + err = os.Remove(testFile) if err != nil { - t.Fatalf("wanted successful remove but got: %v", err) + t.Fatalf("Failed to remove testFile: %v", err) } err = w.Remove(testFile) if err == nil { t.Fatalf("no error on removing invalid file") } + s1 := fmt.Sprintf("%s", err) - w.mu.Lock() - defer w.mu.Unlock() - if len(w.watches) != 0 { - t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches) + err = w.Remove(testFile) + if err == nil { + t.Fatalf("no error on removing invalid file") } - if len(w.paths) != 0 { - t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths) + s2 := fmt.Sprintf("%s", err) + + if s1 != s2 { + t.Fatalf("receive different error - %s / %s", s1, s2) } } @@ -356,94 +341,3 @@ func TestInotifyInnerMapLength(t *testing.T) { t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths) } } - -func TestInotifyOverflow(t *testing.T) { - // We need to generate many more events than the - // fs.inotify.max_queued_events sysctl setting. - // We use multiple goroutines (one per directory) - // to speed up file creation. - numDirs := 128 - numFiles := 1024 - - testDir := tempMkdir(t) - defer os.RemoveAll(testDir) - - w, err := NewWatcher() - if err != nil { - t.Fatalf("Failed to create watcher: %v", err) - } - defer w.Close() - - for dn := 0; dn < numDirs; dn++ { - testSubdir := fmt.Sprintf("%s/%d", testDir, dn) - - err := os.Mkdir(testSubdir, 0777) - if err != nil { - t.Fatalf("Cannot create subdir: %v", err) - } - - err = w.Add(testSubdir) - if err != nil { - t.Fatalf("Failed to add subdir: %v", err) - } - } - - errChan := make(chan error, numDirs*numFiles) - - for dn := 0; dn < numDirs; dn++ { - testSubdir := fmt.Sprintf("%s/%d", testDir, dn) - - go func() { - for fn := 0; fn < numFiles; fn++ { - testFile := fmt.Sprintf("%s/%d", testSubdir, fn) - - handle, err := os.Create(testFile) - if err != nil { - errChan <- fmt.Errorf("Create failed: %v", err) - continue - } - - err = handle.Close() - if err != nil { - errChan <- fmt.Errorf("Close failed: %v", err) - continue - } - } - }() - } - - creates := 0 - overflows := 0 - - after := time.After(10 * time.Second) - for overflows == 0 && creates < numDirs*numFiles { - select { - case <-after: - t.Fatalf("Not done") - case err := <-errChan: - t.Fatalf("Got an error from file creator goroutine: %v", err) - case err := <-w.Errors: - if err == ErrEventOverflow { - overflows++ - } else { - t.Fatalf("Got an error from watcher: %v", err) - } - case evt := <-w.Events: - if !strings.HasPrefix(evt.Name, testDir) { - t.Fatalf("Got an event for an unknown file: %s", evt.Name) - } - if evt.Op == Create { - creates++ - } - } - } - - if creates == numDirs*numFiles { - t.Fatalf("Could not trigger overflow") - } - - if overflows == 0 { - t.Fatalf("No overflow and not enough creates (expected %d, got %d)", - numDirs*numFiles, creates) - } -} diff --git a/vendor/github.com/fsnotify/fsnotify/integration_darwin_test.go b/vendor/gopkg.in/fsnotify.v1/integration_darwin_test.go similarity index 94% rename from vendor/github.com/fsnotify/fsnotify/integration_darwin_test.go rename to vendor/gopkg.in/fsnotify.v1/integration_darwin_test.go index cd6adc273..1b8982fe1 100644 --- a/vendor/github.com/fsnotify/fsnotify/integration_darwin_test.go +++ b/vendor/gopkg.in/fsnotify.v1/integration_darwin_test.go @@ -7,15 +7,14 @@ package fsnotify import ( "os" "path/filepath" + "syscall" "testing" "time" - - "golang.org/x/sys/unix" ) -// testExchangedataForWatcher tests the watcher with the exchangedata operation on macOS. +// testExchangedataForWatcher tests the watcher with the exchangedata operation on OS X. // -// This is widely used for atomic saves on macOS, e.g. TextMate and in Apple's NSDocument. +// This is widely used for atomic saves on OS X, e.g. TextMate and in Apple's NSDocument. // // See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/exchangedata.2.html // Also see: https://github.com/textmate/textmate/blob/cd016be29489eba5f3c09b7b70b06da134dda550/Frameworks/io/src/swap_file_data.cc#L20 @@ -88,7 +87,7 @@ func testExchangedataForWatcher(t *testing.T, watchDir bool) { createAndSyncFile(t, intermediate) // 1. Swap - if err := unix.Exchangedata(intermediate, resolved, 0); err != nil { + if err := syscall.Exchangedata(intermediate, resolved, 0); err != nil { t.Fatalf("[%d] exchangedata failed: %s", i, err) } diff --git a/vendor/github.com/fsnotify/fsnotify/integration_test.go b/vendor/gopkg.in/fsnotify.v1/integration_test.go similarity index 100% rename from vendor/github.com/fsnotify/fsnotify/integration_test.go rename to vendor/gopkg.in/fsnotify.v1/integration_test.go diff --git a/vendor/github.com/fsnotify/fsnotify/kqueue.go b/vendor/gopkg.in/fsnotify.v1/kqueue.go similarity index 85% rename from vendor/github.com/fsnotify/fsnotify/kqueue.go rename to vendor/gopkg.in/fsnotify.v1/kqueue.go index 86e76a3d6..b8ea30846 100644 --- a/vendor/github.com/fsnotify/fsnotify/kqueue.go +++ b/vendor/gopkg.in/fsnotify.v1/kqueue.go @@ -13,16 +13,15 @@ import ( "os" "path/filepath" "sync" + "syscall" "time" - - "golang.org/x/sys/unix" ) // Watcher watches a set of files, delivering events to a channel. type Watcher struct { Events chan Event Errors chan error - done chan struct{} // Channel for sending a "quit message" to the reader goroutine + done chan bool // Channel for sending a "quit message" to the reader goroutine kq int // File descriptor (as returned by the kqueue() syscall). @@ -56,7 +55,7 @@ func NewWatcher() (*Watcher, error) { externalWatches: make(map[string]bool), Events: make(chan Event), Errors: make(chan error), - done: make(chan struct{}), + done: make(chan bool), } go w.readEvents() @@ -71,8 +70,10 @@ func (w *Watcher) Close() error { return nil } w.isClosed = true + w.mu.Unlock() // copy paths to remove while locked + w.mu.Lock() var pathsToRemove = make([]string, 0, len(w.watches)) for name := range w.watches { pathsToRemove = append(pathsToRemove, name) @@ -80,12 +81,15 @@ func (w *Watcher) Close() error { w.mu.Unlock() // unlock before calling Remove, which also locks + var err error for _, name := range pathsToRemove { - w.Remove(name) + if e := w.Remove(name); e != nil && err == nil { + err = e + } } - // send a "quit" message to the reader goroutine - close(w.done) + // Send "quit" message to the reader goroutine: + w.done <- true return nil } @@ -109,12 +113,12 @@ func (w *Watcher) Remove(name string) error { return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) } - const registerRemove = unix.EV_DELETE + const registerRemove = syscall.EV_DELETE if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { return err } - unix.Close(watchfd) + syscall.Close(watchfd) w.mu.Lock() isDir := w.paths[watchfd].isDir @@ -148,7 +152,7 @@ func (w *Watcher) Remove(name string) error { } // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) -const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME +const noteAllEvents = syscall.NOTE_DELETE | syscall.NOTE_WRITE | syscall.NOTE_ATTRIB | syscall.NOTE_RENAME // keventWaitTime to block on each read from kevent var keventWaitTime = durationToTimespec(100 * time.Millisecond) @@ -215,7 +219,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) { } } - watchfd, err = unix.Open(name, openMode, 0700) + watchfd, err = syscall.Open(name, openMode, 0700) if watchfd == -1 { return "", err } @@ -223,9 +227,9 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) { isDir = fi.IsDir() } - const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE + const registerAdd = syscall.EV_ADD | syscall.EV_CLEAR | syscall.EV_ENABLE if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { - unix.Close(watchfd) + syscall.Close(watchfd) return "", err } @@ -241,8 +245,8 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) { // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) w.mu.Lock() - watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && - (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) + watchDir := (flags&syscall.NOTE_WRITE) == syscall.NOTE_WRITE && + (!alreadyWatching || (w.dirFlags[name]&syscall.NOTE_WRITE) != syscall.NOTE_WRITE) // Store flags so this watch can be updated later w.dirFlags[name] = flags w.mu.Unlock() @@ -259,26 +263,27 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) { // readEvents reads from kqueue and converts the received kevents into // Event values that it sends down the Events channel. func (w *Watcher) readEvents() { - eventBuffer := make([]unix.Kevent_t, 10) + eventBuffer := make([]syscall.Kevent_t, 10) -loop: for { // See if there is a message on the "done" channel select { case <-w.done: - break loop + err := syscall.Close(w.kq) + if err != nil { + w.Errors <- err + } + close(w.Events) + close(w.Errors) + return default: } // Get new events kevents, err := read(w.kq, eventBuffer, &keventWaitTime) // EINTR is okay, the syscall was interrupted before timeout expired. - if err != nil && err != unix.EINTR { - select { - case w.Errors <- err: - case <-w.done: - break loop - } + if err != nil && err != syscall.EINTR { + w.Errors <- err continue } @@ -313,12 +318,8 @@ loop: if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { w.sendDirectoryChangeEvents(event.Name) } else { - // Send the event on the Events channel. - select { - case w.Events <- event: - case <-w.done: - break loop - } + // Send the event on the Events channel + w.Events <- event } if event.Op&Remove == Remove { @@ -350,33 +351,21 @@ loop: kevents = kevents[1:] } } - - // cleanup - err := unix.Close(w.kq) - if err != nil { - // only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. - select { - case w.Errors <- err: - default: - } - } - close(w.Events) - close(w.Errors) } // newEvent returns an platform-independent Event based on kqueue Fflags. func newEvent(name string, mask uint32) Event { e := Event{Name: name} - if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { + if mask&syscall.NOTE_DELETE == syscall.NOTE_DELETE { e.Op |= Remove } - if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { + if mask&syscall.NOTE_WRITE == syscall.NOTE_WRITE { e.Op |= Write } - if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { + if mask&syscall.NOTE_RENAME == syscall.NOTE_RENAME { e.Op |= Rename } - if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { + if mask&syscall.NOTE_ATTRIB == syscall.NOTE_ATTRIB { e.Op |= Chmod } return e @@ -417,11 +406,7 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { // Get all files files, err := ioutil.ReadDir(dirPath) if err != nil { - select { - case w.Errors <- err: - case <-w.done: - return - } + w.Errors <- err } // Search for new files @@ -442,11 +427,7 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf w.mu.Unlock() if !doesExist { // Send create event - select { - case w.Events <- newCreateEvent(filePath): - case <-w.done: - return - } + w.Events <- newCreateEvent(filePath) } // like watchDirectoryFiles (but without doing another ReadDir) @@ -470,7 +451,7 @@ func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, erro flags := w.dirFlags[name] w.mu.Unlock() - flags |= unix.NOTE_DELETE | unix.NOTE_RENAME + flags |= syscall.NOTE_DELETE | syscall.NOTE_RENAME return w.addWatch(name, flags) } @@ -480,7 +461,7 @@ func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, erro // kqueue creates a new kernel event queue and returns a descriptor. func kqueue() (kq int, err error) { - kq, err = unix.Kqueue() + kq, err = syscall.Kqueue() if kq == -1 { return kq, err } @@ -489,16 +470,16 @@ func kqueue() (kq int, err error) { // register events with the queue func register(kq int, fds []int, flags int, fflags uint32) error { - changes := make([]unix.Kevent_t, len(fds)) + changes := make([]syscall.Kevent_t, len(fds)) for i, fd := range fds { // SetKevent converts int to the platform-specific types: - unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) + syscall.SetKevent(&changes[i], fd, syscall.EVFILT_VNODE, flags) changes[i].Fflags = fflags } // register the events - success, err := unix.Kevent(kq, changes, nil, nil) + success, err := syscall.Kevent(kq, changes, nil, nil) if success == -1 { return err } @@ -507,8 +488,8 @@ func register(kq int, fds []int, flags int, fflags uint32) error { // read retrieves pending events, or waits until an event occurs. // A timeout of nil blocks indefinitely, while 0 polls the queue. -func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) { - n, err := unix.Kevent(kq, nil, events, timeout) +func read(kq int, events []syscall.Kevent_t, timeout *syscall.Timespec) ([]syscall.Kevent_t, error) { + n, err := syscall.Kevent(kq, nil, events, timeout) if err != nil { return nil, err } @@ -516,6 +497,6 @@ func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent } // durationToTimespec prepares a timeout value -func durationToTimespec(d time.Duration) unix.Timespec { - return unix.NsecToTimespec(d.Nanoseconds()) +func durationToTimespec(d time.Duration) syscall.Timespec { + return syscall.NsecToTimespec(d.Nanoseconds()) } diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/gopkg.in/fsnotify.v1/open_mode_bsd.go similarity index 73% rename from vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go rename to vendor/gopkg.in/fsnotify.v1/open_mode_bsd.go index 7d8de1451..c57ccb427 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go +++ b/vendor/gopkg.in/fsnotify.v1/open_mode_bsd.go @@ -6,6 +6,6 @@ package fsnotify -import "golang.org/x/sys/unix" +import "syscall" -const openMode = unix.O_NONBLOCK | unix.O_RDONLY +const openMode = syscall.O_NONBLOCK | syscall.O_RDONLY diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/gopkg.in/fsnotify.v1/open_mode_darwin.go similarity index 79% rename from vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go rename to vendor/gopkg.in/fsnotify.v1/open_mode_darwin.go index 9139e1716..174b2c331 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go +++ b/vendor/gopkg.in/fsnotify.v1/open_mode_darwin.go @@ -6,7 +6,7 @@ package fsnotify -import "golang.org/x/sys/unix" +import "syscall" // note: this constant is not defined on BSD -const openMode = unix.O_EVTONLY +const openMode = syscall.O_EVTONLY diff --git a/vendor/github.com/fsnotify/fsnotify/windows.go b/vendor/gopkg.in/fsnotify.v1/windows.go similarity index 86% rename from vendor/github.com/fsnotify/fsnotify/windows.go rename to vendor/gopkg.in/fsnotify.v1/windows.go index 09436f31d..811585227 100644 --- a/vendor/github.com/fsnotify/fsnotify/windows.go +++ b/vendor/gopkg.in/fsnotify.v1/windows.go @@ -71,7 +71,7 @@ func (w *Watcher) Add(name string) error { in := &input{ op: opAddWatch, path: filepath.Clean(name), - flags: sysFSALLEVENTS, + flags: sys_FS_ALL_EVENTS, reply: make(chan error), } w.input <- in @@ -97,43 +97,43 @@ func (w *Watcher) Remove(name string) error { const ( // Options for AddWatch - sysFSONESHOT = 0x80000000 - sysFSONLYDIR = 0x1000000 + sys_FS_ONESHOT = 0x80000000 + sys_FS_ONLYDIR = 0x1000000 // Events - sysFSACCESS = 0x1 - sysFSALLEVENTS = 0xfff - sysFSATTRIB = 0x4 - sysFSCLOSE = 0x18 - sysFSCREATE = 0x100 - sysFSDELETE = 0x200 - sysFSDELETESELF = 0x400 - sysFSMODIFY = 0x2 - sysFSMOVE = 0xc0 - sysFSMOVEDFROM = 0x40 - sysFSMOVEDTO = 0x80 - sysFSMOVESELF = 0x800 + sys_FS_ACCESS = 0x1 + sys_FS_ALL_EVENTS = 0xfff + sys_FS_ATTRIB = 0x4 + sys_FS_CLOSE = 0x18 + sys_FS_CREATE = 0x100 + sys_FS_DELETE = 0x200 + sys_FS_DELETE_SELF = 0x400 + sys_FS_MODIFY = 0x2 + sys_FS_MOVE = 0xc0 + sys_FS_MOVED_FROM = 0x40 + sys_FS_MOVED_TO = 0x80 + sys_FS_MOVE_SELF = 0x800 // Special events - sysFSIGNORED = 0x8000 - sysFSQOVERFLOW = 0x4000 + sys_FS_IGNORED = 0x8000 + sys_FS_Q_OVERFLOW = 0x4000 ) func newEvent(name string, mask uint32) Event { e := Event{Name: name} - if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { + if mask&sys_FS_CREATE == sys_FS_CREATE || mask&sys_FS_MOVED_TO == sys_FS_MOVED_TO { e.Op |= Create } - if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { + if mask&sys_FS_DELETE == sys_FS_DELETE || mask&sys_FS_DELETE_SELF == sys_FS_DELETE_SELF { e.Op |= Remove } - if mask&sysFSMODIFY == sysFSMODIFY { + if mask&sys_FS_MODIFY == sys_FS_MODIFY { e.Op |= Write } - if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { + if mask&sys_FS_MOVE == sys_FS_MOVE || mask&sys_FS_MOVE_SELF == sys_FS_MOVE_SELF || mask&sys_FS_MOVED_FROM == sys_FS_MOVED_FROM { e.Op |= Rename } - if mask&sysFSATTRIB == sysFSATTRIB { + if mask&sys_FS_ATTRIB == sys_FS_ATTRIB { e.Op |= Chmod } return e @@ -242,7 +242,7 @@ func (w *Watcher) addWatch(pathname string, flags uint64) error { if err != nil { return err } - if flags&sysFSONLYDIR != 0 && pathname != dir { + if flags&sys_FS_ONLYDIR != 0 && pathname != dir { return nil } ino, err := getIno(dir) @@ -302,11 +302,11 @@ func (w *Watcher) remWatch(pathname string) error { return fmt.Errorf("can't remove non-existent watch for: %s", pathname) } if pathname == dir { - w.sendEvent(watch.path, watch.mask&sysFSIGNORED) + w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) watch.mask = 0 } else { name := filepath.Base(pathname) - w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) + w.sendEvent(watch.path+"\\"+name, watch.names[name]&sys_FS_IGNORED) delete(watch.names, name) } return w.startRead(watch) @@ -316,13 +316,13 @@ func (w *Watcher) remWatch(pathname string) error { func (w *Watcher) deleteWatch(watch *watch) { for name, mask := range watch.names { if mask&provisional == 0 { - w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) + w.sendEvent(watch.path+"\\"+name, mask&sys_FS_IGNORED) } delete(watch.names, name) } if watch.mask != 0 { if watch.mask&provisional == 0 { - w.sendEvent(watch.path, watch.mask&sysFSIGNORED) + w.sendEvent(watch.path, watch.mask&sys_FS_IGNORED) } watch.mask = 0 } @@ -353,8 +353,8 @@ func (w *Watcher) startRead(watch *watch) error { err := os.NewSyscallError("ReadDirectoryChanges", e) if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { // Watched directory was probably removed - if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) { - if watch.mask&sysFSONESHOT != 0 { + if w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) { + if watch.mask&sys_FS_ONESHOT != 0 { watch.mask = 0 } } @@ -428,7 +428,7 @@ func (w *Watcher) readEvents() { } case syscall.ERROR_ACCESS_DENIED: // Watched directory was probably removed - w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) + w.sendEvent(watch.path, watch.mask&sys_FS_DELETE_SELF) w.deleteWatch(watch) w.startRead(watch) continue @@ -444,7 +444,7 @@ func (w *Watcher) readEvents() { var offset uint32 for { if n == 0 { - w.Events <- newEvent("", sysFSQOVERFLOW) + w.Events <- newEvent("", sys_FS_Q_OVERFLOW) w.Errors <- errors.New("short read in readEvents()") break } @@ -453,27 +453,27 @@ func (w *Watcher) readEvents() { raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) - fullname := filepath.Join(watch.path, name) + fullname := watch.path + "\\" + name var mask uint64 switch raw.Action { case syscall.FILE_ACTION_REMOVED: - mask = sysFSDELETESELF + mask = sys_FS_DELETE_SELF case syscall.FILE_ACTION_MODIFIED: - mask = sysFSMODIFY + mask = sys_FS_MODIFY case syscall.FILE_ACTION_RENAMED_OLD_NAME: watch.rename = name case syscall.FILE_ACTION_RENAMED_NEW_NAME: if watch.names[watch.rename] != 0 { watch.names[name] |= watch.names[watch.rename] delete(watch.names, watch.rename) - mask = sysFSMOVESELF + mask = sys_FS_MOVE_SELF } } sendNameEvent := func() { if w.sendEvent(fullname, watch.names[name]&mask) { - if watch.names[name]&sysFSONESHOT != 0 { + if watch.names[name]&sys_FS_ONESHOT != 0 { delete(watch.names, name) } } @@ -482,16 +482,16 @@ func (w *Watcher) readEvents() { sendNameEvent() } if raw.Action == syscall.FILE_ACTION_REMOVED { - w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) + w.sendEvent(fullname, watch.names[name]&sys_FS_IGNORED) delete(watch.names, name) } if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { - if watch.mask&sysFSONESHOT != 0 { + if watch.mask&sys_FS_ONESHOT != 0 { watch.mask = 0 } } if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { - fullname = filepath.Join(watch.path, watch.rename) + fullname = watch.path + "\\" + watch.rename sendNameEvent() } @@ -529,16 +529,16 @@ func (w *Watcher) sendEvent(name string, mask uint64) bool { func toWindowsFlags(mask uint64) uint32 { var m uint32 - if mask&sysFSACCESS != 0 { + if mask&sys_FS_ACCESS != 0 { m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS } - if mask&sysFSMODIFY != 0 { + if mask&sys_FS_MODIFY != 0 { m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE } - if mask&sysFSATTRIB != 0 { + if mask&sys_FS_ATTRIB != 0 { m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES } - if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { + if mask&(sys_FS_MOVE|sys_FS_CREATE|sys_FS_DELETE) != 0 { m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME } return m @@ -547,15 +547,15 @@ func toWindowsFlags(mask uint64) uint32 { func toFSnotifyFlags(action uint32) uint64 { switch action { case syscall.FILE_ACTION_ADDED: - return sysFSCREATE + return sys_FS_CREATE case syscall.FILE_ACTION_REMOVED: - return sysFSDELETE + return sys_FS_DELETE case syscall.FILE_ACTION_MODIFIED: - return sysFSMODIFY + return sys_FS_MODIFY case syscall.FILE_ACTION_RENAMED_OLD_NAME: - return sysFSMOVEDFROM + return sys_FS_MOVED_FROM case syscall.FILE_ACTION_RENAMED_NEW_NAME: - return sysFSMOVEDTO + return sys_FS_MOVED_TO } return 0 }