Migrate to Go 1.13 and gomodules (#187)
This commit is contained in:
parent
637c82f407
commit
5b1e51534f
@ -1,6 +1,6 @@
|
|||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.11
|
- 1.13
|
||||||
env:
|
env:
|
||||||
- "PATH=/home/travis/gopath/bin:$PATH"
|
- "PATH=/home/travis/gopath/bin:$PATH"
|
||||||
before_install:
|
before_install:
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.11.4-alpine3.8 AS builder
|
FROM golang:1.13-alpine AS builder
|
||||||
LABEL maintainer="joona@kuori.org"
|
LABEL maintainer="joona@kuori.org"
|
||||||
|
|
||||||
RUN apk add --update gcc musl-dev git
|
RUN apk add --update gcc musl-dev git
|
||||||
|
|||||||
367
Gopkg.lock
generated
367
Gopkg.lock
generated
@ -1,367 +0,0 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:e4b30804a381d7603b8a344009987c1ba351c26043501b23b8c7ce21f0b67474"
|
|
||||||
name = "github.com/BurntSushi/toml"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
|
|
||||||
version = "v0.3.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:9ceecb4271682fd824475d451b6abf02b99ad04e72f8de3408a9d8b7fd15b933"
|
|
||||||
name = "github.com/ajg/form"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "cc2954064ec9ea8d93917f0f87456e11d7b881ad"
|
|
||||||
version = "v1.5"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:0deddd908b6b4b768cfc272c16ee61e7088a60f7fe2f06c547bd3d8e1f8b8e77"
|
|
||||||
name = "github.com/davecgh/go-spew"
|
|
||||||
packages = ["spew"]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
|
||||||
version = "v1.1.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:358f9731027e87f8ae3a7e52f0473031be47d1e041295b3c0afc84dc779c0666"
|
|
||||||
name = "github.com/erikstmartin/go-testdb"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "8d10e4a1bae52cd8b81ffdec3445890d6dccab3d"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:2defd14040b1ca18840524e9d0f9cfab0ea8d2f2d396b343d221f8bc8e21ab9d"
|
|
||||||
name = "github.com/fatih/structs"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "4966fc68f5b7593aafa6cbbba2d65ec6e1416047"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:7a98bc5cfa8e0c82156a33edb7ed582a9e700af089994e1b89bdb8517cd2db58"
|
|
||||||
name = "github.com/gavv/httpexpect"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "bdde308713130a703436e014ff782958c251d20a"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:44e52e928b91a686e2b9518f7eea82ba729d227d94b69574416bbe2d9d418b33"
|
|
||||||
name = "github.com/gavv/monotime"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "6f8212e8d10df7383609d3c377ca08884d8f3ec0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:cea4aa2038169ee558bf507d5ea02c94ca85bcca28a4c7bb99fd59b31e43a686"
|
|
||||||
name = "github.com/google/go-querystring"
|
|
||||||
packages = ["query"]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "44c6ddd0a2342c386950e880b658017258da92fc"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:a25a2c5ae694b01713fb6cd03c3b1ac1ccc1902b9f0a922680a88ec254f968e1"
|
|
||||||
name = "github.com/google/uuid"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:788735d9cba7f51b7cc86e6c7ba7b40b0b7c7b374bf3a0b5fa7c929fa2af2da8"
|
|
||||||
name = "github.com/imkira/go-interpol"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "5accad8134979a6ac504d456a6c7f1c53da237ca"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:deeb6536ebbcb66ab11213ecfaac9daadfcd004dd057446a4980ffd6c15c36e2"
|
|
||||||
name = "github.com/julienschmidt/httprouter"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "348b672cd90d8190f8240323e372ecd1e66b59dc"
|
|
||||||
version = "v1.2.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:064eb4d2277fb6a23944380644268b99acc0ae0576c72d0b6d7288d3c2fe4a1e"
|
|
||||||
name = "github.com/klauspost/compress"
|
|
||||||
packages = [
|
|
||||||
"flate",
|
|
||||||
"gzip",
|
|
||||||
"zlib",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "30be6041bed523c18e269a700ebd9c2ea9328574"
|
|
||||||
version = "v1.4.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:198af4ad42013a3779477b597458f860a6a99e57e1674fa4f61eb04b8b5b768f"
|
|
||||||
name = "github.com/klauspost/cpuid"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "e7e905edc00ea8827e58662220139109efea09db"
|
|
||||||
version = "v1.2.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:6a874e3ddfb9db2b42bd8c85b6875407c702fa868eed20634ff489bc896ccfd3"
|
|
||||||
name = "github.com/konsorten/go-windows-terminal-sequences"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242"
|
|
||||||
version = "v1.0.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:bc36cd980d0069137800b505af4937c6109f4dc7cefe39d7c2efc7c8d51528d6"
|
|
||||||
name = "github.com/lib/pq"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"oid",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "9eb73efc1fcc404148b56765b0d3f61d9a5ef8ee"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:8bbdb2b3dce59271877770d6fe7dcbb8362438fa7d2e1e1f688e4bf2aac72706"
|
|
||||||
name = "github.com/mattn/go-sqlite3"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "c7c4067b79cc51e6dfdcef5c702e74b1e0fa7c75"
|
|
||||||
version = "v1.10.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:103c1001b79009329824bc60f93f8f7150bf1ad31f18d29a476deb8a2de0f002"
|
|
||||||
name = "github.com/miekg/dns"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "8fc2e5773bbd308ca2fcc962fd8d25c1bd0f6743"
|
|
||||||
version = "v1.1.4"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:fe67641b990bdc1802f8a1e462a4924210a8762a8a17b72e09656049c906b871"
|
|
||||||
name = "github.com/moul/http2curl"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411"
|
|
||||||
name = "github.com/pmezard/go-difflib"
|
|
||||||
packages = ["difflib"]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:5f47c69f85311c4dc292be6cc995a0a3fe8337a6ce38ef4f71e5b7efd5ad42e0"
|
|
||||||
name = "github.com/rs/cors"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "9a47f48565a795472d43519dd49aac781f3034fb"
|
|
||||||
version = "v1.6.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:3962f553b77bf6c03fc07cd687a22dd3b00fe11aa14d31194f5505f5bb65cdc8"
|
|
||||||
name = "github.com/sergi/go-diff"
|
|
||||||
packages = ["diffmatchpatch"]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "1744e2970ca51c86172c8190fadad617561ed6e7"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:9a3c631555e0351fdc4e696577bb63afd90c399d782a8462dba9d100d7021db3"
|
|
||||||
name = "github.com/sirupsen/logrus"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"hooks/test",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "e1e72e9de974bd926e5c56f83753fba2df402ce5"
|
|
||||||
version = "v1.3.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:381bcbeb112a51493d9d998bbba207a529c73dbb49b3fd789e48c63fac1f192c"
|
|
||||||
name = "github.com/stretchr/testify"
|
|
||||||
packages = [
|
|
||||||
"assert",
|
|
||||||
"require",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
|
|
||||||
version = "v1.3.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:857a9ecd5cb13379ecc8f798f6e6b6b574c98b9355657d91e068275f1120aaf7"
|
|
||||||
name = "github.com/valyala/bytebufferpool"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:5838606075eb2e11d5b99d4526e36863a9315f006d322abfbf5a7fff73250271"
|
|
||||||
name = "github.com/valyala/fasthttp"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"fasthttputil",
|
|
||||||
"stackless",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "aaec9b0fe2d01bd3ff761345ba47bb94a9b0dc59"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:e6338f2518362ff701a556bd76afd90a2168d1c658ec5d1ea1e9c5ef30a7d157"
|
|
||||||
name = "github.com/xeipuuv/gojsonpointer"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "4e3ac2762d5f479393488629ee9370b50873b3a6"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:604f98a38394d2805a78c462396a4992b93fdd5b7306130add330f1a99ac6b0a"
|
|
||||||
name = "github.com/xeipuuv/gojsonreference"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "bd5ef7bd5415a7ac448318e64f11a24cd21e594b"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:c2c74271189fe48e23c25216570008b7ecc5782688d6efd81325886ac6f62ad0"
|
|
||||||
name = "github.com/xeipuuv/gojsonschema"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "f971f3cd73b2899de6923801c147f075263e0c50"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:81daf39130b8efb47ab2b841ee42adedeaaf20ab3675236f577722ae78d37728"
|
|
||||||
name = "github.com/yalp/jsonpath"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "5cc68e5049a040829faef3a44c00ec4332f6dec7"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:529ed3f98838f69e13761788d0cc71b44e130058fab13bae2ce09f7a176bced4"
|
|
||||||
name = "github.com/yudai/gojsondiff"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"formatter",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "7b1b7adf999dab73a6eb02669c3d82dbb27a3dd6"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:9857bb2293f372b2181004d8b62179bbdb4ab0982ec6f762abe6cf2bfedaff85"
|
|
||||||
name = "github.com/yudai/golcs"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "ecda9a501e8220fae3b4b600c3db4b0ba22cfc68"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:cbc1ebc01ec2ceb2c3cc7b9e33357dbc3eff173335f02d9f01603f14102602a7"
|
|
||||||
name = "golang.org/x/crypto"
|
|
||||||
packages = [
|
|
||||||
"acme",
|
|
||||||
"acme/autocert",
|
|
||||||
"bcrypt",
|
|
||||||
"blowfish",
|
|
||||||
"ed25519",
|
|
||||||
"ed25519/internal/edwards25519",
|
|
||||||
"ssh/terminal",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "b8fe1690c61389d7d2a8074a507d1d40c5d30448"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:1e9704e5379e68ac473c28aeb3f7e7cd4036ae8a246bf0285b5ebdbb8e0cfacf"
|
|
||||||
name = "golang.org/x/net"
|
|
||||||
packages = [
|
|
||||||
"bpf",
|
|
||||||
"idna",
|
|
||||||
"internal/iana",
|
|
||||||
"internal/socket",
|
|
||||||
"ipv4",
|
|
||||||
"ipv6",
|
|
||||||
"publicsuffix",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "d26f9f9a57f3fab6a695bec0d84433c2c50f8bbf"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:4d6f20f5bc52af99569b1be5ea55d4e22646d5e022b92f2d3d75347628614734"
|
|
||||||
name = "golang.org/x/sys"
|
|
||||||
packages = [
|
|
||||||
"unix",
|
|
||||||
"windows",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "7ae0202eb74c2b534255c71b5a15fa4115aabbcc"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4"
|
|
||||||
name = "golang.org/x/text"
|
|
||||||
packages = [
|
|
||||||
"collate",
|
|
||||||
"collate/build",
|
|
||||||
"internal/colltab",
|
|
||||||
"internal/gen",
|
|
||||||
"internal/tag",
|
|
||||||
"internal/triegen",
|
|
||||||
"internal/ucd",
|
|
||||||
"language",
|
|
||||||
"secure/bidirule",
|
|
||||||
"transform",
|
|
||||||
"unicode/bidi",
|
|
||||||
"unicode/cldr",
|
|
||||||
"unicode/norm",
|
|
||||||
"unicode/rangetable",
|
|
||||||
]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
|
||||||
version = "v0.3.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:4014584c076f25aaf35d9de36c79ae2d208bba32c780f405e3395dad79292e22"
|
|
||||||
name = "gopkg.in/DATA-DOG/go-sqlmock.v1"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "d76b18b42f285b792bf985118980ce9eacea9d10"
|
|
||||||
version = "v1.3.0"
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
input-imports = [
|
|
||||||
"github.com/BurntSushi/toml",
|
|
||||||
"github.com/erikstmartin/go-testdb",
|
|
||||||
"github.com/gavv/httpexpect",
|
|
||||||
"github.com/google/uuid",
|
|
||||||
"github.com/julienschmidt/httprouter",
|
|
||||||
"github.com/lib/pq",
|
|
||||||
"github.com/mattn/go-sqlite3",
|
|
||||||
"github.com/miekg/dns",
|
|
||||||
"github.com/rs/cors",
|
|
||||||
"github.com/sirupsen/logrus",
|
|
||||||
"github.com/sirupsen/logrus/hooks/test",
|
|
||||||
"github.com/valyala/fasthttp",
|
|
||||||
"golang.org/x/crypto/acme/autocert",
|
|
||||||
"golang.org/x/crypto/bcrypt",
|
|
||||||
"gopkg.in/DATA-DOG/go-sqlmock.v1",
|
|
||||||
]
|
|
||||||
solver-name = "gps-cdcl"
|
|
||||||
solver-version = 1
|
|
||||||
72
Gopkg.toml
72
Gopkg.toml
@ -1,72 +0,0 @@
|
|||||||
|
|
||||||
# Gopkg.toml example
|
|
||||||
#
|
|
||||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
|
||||||
# for detailed Gopkg.toml documentation.
|
|
||||||
#
|
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
|
||||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project"
|
|
||||||
# version = "1.0.0"
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project2"
|
|
||||||
# branch = "dev"
|
|
||||||
# source = "github.com/myfork/project2"
|
|
||||||
#
|
|
||||||
# [[override]]
|
|
||||||
# name = "github.com/x/y"
|
|
||||||
# version = "2.4.0"
|
|
||||||
|
|
||||||
# Need to pin fasthttp so it doesn't get the old version from branch names
|
|
||||||
required = ["github.com/valyala/fasthttp"]
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/valyala/fasthttp"
|
|
||||||
branch = "master"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/BurntSushi/toml"
|
|
||||||
version = "0.3.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/erikstmartin/go-testdb"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/gavv/httpexpect"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/julienschmidt/httprouter"
|
|
||||||
version = "1.1.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/lib/pq"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/mattn/go-sqlite3"
|
|
||||||
version = "1.6.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/miekg/dns"
|
|
||||||
version = "1.0.3"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/rs/cors"
|
|
||||||
version = "1.2.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/sirupsen/logrus"
|
|
||||||
version = "1.0.4"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/crypto"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "gopkg.in/DATA-DOG/go-sqlmock.v1"
|
|
||||||
version = "1.3.0"
|
|
||||||
@ -133,7 +133,7 @@ See the INSTALL section for information on how to do this.
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1) Install [Go 1.11 or newer](https://golang.org/doc/install).
|
1) Install [Go 1.13 or newer](https://golang.org/doc/install).
|
||||||
|
|
||||||
2) Install acme-dns: `go get github.com/joohoi/acme-dns/...`. This will install acme-dns to `~/go/bin/acme-dns`.
|
2) Install acme-dns: `go get github.com/joohoi/acme-dns/...`. This will install acme-dns to `~/go/bin/acme-dns`.
|
||||||
|
|
||||||
|
|||||||
@ -8,11 +8,11 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
"github.com/gavv/httpexpect"
|
"github.com/gavv/httpexpect"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
"gopkg.in/DATA-DOG/go-sqlmock.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// noAuth function to write ACMETxt model to context while not preforming any validation
|
// noAuth function to write ACMETxt model to context while not preforming any validation
|
||||||
|
|||||||
33
go.mod
Normal file
33
go.mod
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
module github.com/joohoi/acme-dns
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v0.3.1
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.3.3
|
||||||
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
|
||||||
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
|
github.com/gavv/httpexpect v2.0.0+incompatible
|
||||||
|
github.com/google/go-querystring v1.0.0 // indirect
|
||||||
|
github.com/google/uuid v1.1.1
|
||||||
|
github.com/gorilla/websocket v1.4.1 // indirect
|
||||||
|
github.com/imkira/go-interpol v1.1.0 // indirect
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||||
|
github.com/lib/pq v1.2.0
|
||||||
|
github.com/mattn/go-sqlite3 v1.11.0
|
||||||
|
github.com/miekg/dns v1.1.22
|
||||||
|
github.com/moul/http2curl v1.0.0 // indirect
|
||||||
|
github.com/rs/cors v1.7.0
|
||||||
|
github.com/sergi/go-diff v1.0.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.4.2
|
||||||
|
github.com/valyala/fasthttp v1.5.0 // indirect
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
|
||||||
|
github.com/yudai/gojsondiff v1.0.0 // indirect
|
||||||
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||||
|
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||||
|
)
|
||||||
98
go.sum
Normal file
98
go.sum
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
|
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
|
||||||
|
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||||
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||||
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||||
|
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
|
github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
|
||||||
|
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||||
|
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
|
||||||
|
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc=
|
||||||
|
github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||||
|
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||||
|
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||||
|
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||||
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasthttp v1.5.0 h1:dhq+O9pmNZFF6qAXpasMO1xSm7dL4qEz2ylfZN8BG9w=
|
||||||
|
github.com/valyala/fasthttp v1.5.0/go.mod h1:eriCz9OhZjKCGfJ185a/IDgNl0bg9IbzfpcslMZXU1c=
|
||||||
|
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||||
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||||
|
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||||
|
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||||
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||||
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
|
||||||
|
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||||
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||||
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
5
vendor/github.com/BurntSushi/toml/.gitignore
generated
vendored
5
vendor/github.com/BurntSushi/toml/.gitignore
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
TAGS
|
|
||||||
tags
|
|
||||||
.*.swp
|
|
||||||
tomlcheck/tomlcheck
|
|
||||||
toml.test
|
|
||||||
15
vendor/github.com/BurntSushi/toml/.travis.yml
generated
vendored
15
vendor/github.com/BurntSushi/toml/.travis.yml
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.1
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
install:
|
|
||||||
- go install ./...
|
|
||||||
- go get github.com/BurntSushi/toml-test
|
|
||||||
script:
|
|
||||||
- export PATH="$PATH:$HOME/gopath/bin"
|
|
||||||
- make test
|
|
||||||
3
vendor/github.com/BurntSushi/toml/COMPATIBLE
generated
vendored
3
vendor/github.com/BurntSushi/toml/COMPATIBLE
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
Compatible with TOML version
|
|
||||||
[v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md)
|
|
||||||
|
|
||||||
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
19
vendor/github.com/BurntSushi/toml/Makefile
generated
vendored
19
vendor/github.com/BurntSushi/toml/Makefile
generated
vendored
@ -1,19 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
218
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
218
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
@ -1,218 +0,0 @@
|
|||||||
## 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/toml-lang/toml
|
|
||||||
|
|
||||||
Compatible with TOML version
|
|
||||||
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
|
||||||
|
|
||||||
Documentation: https://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
|
|
||||||
```
|
|
||||||
|
|
||||||
[](https://travis-ci.org/BurntSushi/toml) [](https://godoc.org/github.com/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}`.
|
|
||||||
61
vendor/github.com/BurntSushi/toml/_examples/example.go
generated
vendored
61
vendor/github.com/BurntSushi/toml/_examples/example.go
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
35
vendor/github.com/BurntSushi/toml/_examples/example.toml
generated
vendored
35
vendor/github.com/BurntSushi/toml/_examples/example.toml
generated
vendored
@ -1,35 +0,0 @@
|
|||||||
# 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"
|
|
||||||
]
|
|
||||||
22
vendor/github.com/BurntSushi/toml/_examples/hard.toml
generated
vendored
22
vendor/github.com/BurntSushi/toml/_examples/hard.toml
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
# 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
|
|
||||||
]
|
|
||||||
|
|
||||||
4
vendor/github.com/BurntSushi/toml/_examples/implicit.toml
generated
vendored
4
vendor/github.com/BurntSushi/toml/_examples/implicit.toml
generated
vendored
@ -1,4 +0,0 @@
|
|||||||
# [x] you
|
|
||||||
# [x.y] don't
|
|
||||||
# [x.y.z] need these
|
|
||||||
[x.y.z.w] # for this to work
|
|
||||||
6
vendor/github.com/BurntSushi/toml/_examples/invalid-apples.toml
generated
vendored
6
vendor/github.com/BurntSushi/toml/_examples/invalid-apples.toml
generated
vendored
@ -1,6 +0,0 @@
|
|||||||
# DO NOT WANT
|
|
||||||
[fruit]
|
|
||||||
type = "apple"
|
|
||||||
|
|
||||||
[fruit.type]
|
|
||||||
apple = "yes"
|
|
||||||
35
vendor/github.com/BurntSushi/toml/_examples/invalid.toml
generated
vendored
35
vendor/github.com/BurntSushi/toml/_examples/invalid.toml
generated
vendored
@ -1,35 +0,0 @@
|
|||||||
# 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"
|
|
||||||
]
|
|
||||||
5
vendor/github.com/BurntSushi/toml/_examples/readme1.toml
generated
vendored
5
vendor/github.com/BurntSushi/toml/_examples/readme1.toml
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
Age = 25
|
|
||||||
Cats = [ "Cauchy", "Plato" ]
|
|
||||||
Pi = 3.14
|
|
||||||
Perfection = [ 6, 28, 496, 8128 ]
|
|
||||||
DOB = 1987-07-05T05:45:00Z
|
|
||||||
1
vendor/github.com/BurntSushi/toml/_examples/readme2.toml
generated
vendored
1
vendor/github.com/BurntSushi/toml/_examples/readme2.toml
generated
vendored
@ -1 +0,0 @@
|
|||||||
some_key_NAME = "wat"
|
|
||||||
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
13
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md
generated
vendored
13
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/README.md
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
# 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.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
|
||||||
|
|
||||||
Compatible with `toml-test` version
|
|
||||||
[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0)
|
|
||||||
90
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go
generated
vendored
90
vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/main.go
generated
vendored
@ -1,90 +0,0 @@
|
|||||||
// 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"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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() {
|
|
||||||
if flag.NArg() != 0 {
|
|
||||||
flag.Usage()
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmp interface{}
|
|
||||||
if _, err := toml.DecodeReader(os.Stdin, &tmp); err != nil {
|
|
||||||
log.Fatalf("Error decoding TOML: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
typedTmp := translate(tmp)
|
|
||||||
if err := json.NewEncoder(os.Stdout).Encode(typedTmp); err != nil {
|
|
||||||
log.Fatalf("Error encoding JSON: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func translate(tomlData interface{}) interface{} {
|
|
||||||
switch orig := tomlData.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
typed := make(map[string]interface{}, len(orig))
|
|
||||||
for k, v := range orig {
|
|
||||||
typed[k] = translate(v)
|
|
||||||
}
|
|
||||||
return typed
|
|
||||||
case []map[string]interface{}:
|
|
||||||
typed := make([]map[string]interface{}, len(orig))
|
|
||||||
for i, v := range orig {
|
|
||||||
typed[i] = translate(v).(map[string]interface{})
|
|
||||||
}
|
|
||||||
return typed
|
|
||||||
case []interface{}:
|
|
||||||
typed := make([]interface{}, len(orig))
|
|
||||||
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"))
|
|
||||||
case bool:
|
|
||||||
return tag("bool", fmt.Sprintf("%v", orig))
|
|
||||||
case int64:
|
|
||||||
return tag("integer", fmt.Sprintf("%d", orig))
|
|
||||||
case float64:
|
|
||||||
return tag("float", fmt.Sprintf("%v", orig))
|
|
||||||
case string:
|
|
||||||
return tag("string", orig)
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Sprintf("Unknown type: %T", tomlData))
|
|
||||||
}
|
|
||||||
|
|
||||||
func tag(typeName string, data interface{}) map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"type": typeName,
|
|
||||||
"value": data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
13
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md
generated
vendored
13
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/README.md
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
# 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.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
|
||||||
|
|
||||||
Compatible with `toml-test` version
|
|
||||||
[v0.2.0](https://github.com/BurntSushi/toml-test/tree/v0.2.0)
|
|
||||||
131
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go
generated
vendored
131
vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/main.go
generated
vendored
@ -1,131 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
||||||
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/README.md
generated
vendored
21
vendor/github.com/BurntSushi/toml/cmd/tomlv/README.md
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
# 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.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
|
||||||
61
vendor/github.com/BurntSushi/toml/cmd/tomlv/main.go
generated
vendored
61
vendor/github.com/BurntSushi/toml/cmd/tomlv/main.go
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
// 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()
|
|
||||||
}
|
|
||||||
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
@ -1,509 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func e(format string, args ...interface{}) error {
|
|
||||||
return fmt.Errorf("toml: "+format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaler is the interface implemented by objects that can unmarshal a
|
|
||||||
// TOML description of themselves.
|
|
||||||
type Unmarshaler interface {
|
|
||||||
UnmarshalTOML(interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
|
|
||||||
func Unmarshal(p []byte, v interface{}) error {
|
|
||||||
_, err := Decode(string(p), v)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primitive is a TOML value that hasn't been decoded into a Go value.
|
|
||||||
// When using the various `Decode*` functions, the type `Primitive` may
|
|
||||||
// be given to any value, and its decoding will be delayed.
|
|
||||||
//
|
|
||||||
// A `Primitive` value can be decoded using the `PrimitiveDecode` function.
|
|
||||||
//
|
|
||||||
// The underlying representation of a `Primitive` value is subject to change.
|
|
||||||
// Do not rely on it.
|
|
||||||
//
|
|
||||||
// N.B. Primitive values are still parsed, so using them will only avoid
|
|
||||||
// the overhead of reflection. They can be useful when you don't know the
|
|
||||||
// exact type of TOML data until run time.
|
|
||||||
type Primitive struct {
|
|
||||||
undecoded interface{}
|
|
||||||
context Key
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEPRECATED!
|
|
||||||
//
|
|
||||||
// Use MetaData.PrimitiveDecode instead.
|
|
||||||
func PrimitiveDecode(primValue Primitive, v interface{}) error {
|
|
||||||
md := MetaData{decoded: make(map[string]bool)}
|
|
||||||
return md.unify(primValue.undecoded, rvalue(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrimitiveDecode is just like the other `Decode*` functions, except it
|
|
||||||
// decodes a TOML value that has already been parsed. Valid primitive values
|
|
||||||
// can *only* be obtained from values filled by the decoder functions,
|
|
||||||
// including this method. (i.e., `v` may contain more `Primitive`
|
|
||||||
// values.)
|
|
||||||
//
|
|
||||||
// Meta data for primitive values is included in the meta data returned by
|
|
||||||
// the `Decode*` functions with one exception: keys returned by the Undecoded
|
|
||||||
// method will only reflect keys that were decoded. Namely, any keys hidden
|
|
||||||
// behind a Primitive will be considered undecoded. Executing this method will
|
|
||||||
// update the undecoded keys in the meta data. (See the example.)
|
|
||||||
func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
|
|
||||||
md.context = primValue.context
|
|
||||||
defer func() { md.context = nil }()
|
|
||||||
return md.unify(primValue.undecoded, rvalue(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode will decode the contents of `data` in TOML format into a pointer
|
|
||||||
// `v`.
|
|
||||||
//
|
|
||||||
// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
|
|
||||||
// used interchangeably.)
|
|
||||||
//
|
|
||||||
// TOML arrays of tables correspond to either a slice of structs or a slice
|
|
||||||
// of maps.
|
|
||||||
//
|
|
||||||
// TOML datetimes correspond to Go `time.Time` values.
|
|
||||||
//
|
|
||||||
// All other TOML types (float, string, int, bool and array) correspond
|
|
||||||
// to the obvious Go types.
|
|
||||||
//
|
|
||||||
// An exception to the above rules is if a type implements the
|
|
||||||
// encoding.TextUnmarshaler interface. In this case, any primitive TOML value
|
|
||||||
// (floats, strings, integers, booleans and datetimes) will be converted to
|
|
||||||
// a byte string and given to the value's UnmarshalText method. See the
|
|
||||||
// Unmarshaler example for a demonstration with time duration strings.
|
|
||||||
//
|
|
||||||
// Key mapping
|
|
||||||
//
|
|
||||||
// TOML keys can map to either keys in a Go map or field names in a Go
|
|
||||||
// struct. The special `toml` struct tag may be used to map TOML keys to
|
|
||||||
// struct fields that don't match the key name exactly. (See the example.)
|
|
||||||
// A case insensitive match to struct names will be tried if an exact match
|
|
||||||
// can't be found.
|
|
||||||
//
|
|
||||||
// The mapping between TOML values and Go values is loose. That is, there
|
|
||||||
// may exist TOML values that cannot be placed into your representation, and
|
|
||||||
// there may be parts of your representation that do not correspond to
|
|
||||||
// TOML values. This loose mapping can be made stricter by using the IsDefined
|
|
||||||
// and/or Undecoded methods on the MetaData returned.
|
|
||||||
//
|
|
||||||
// This decoder will not handle cyclic types. If a cyclic type is passed,
|
|
||||||
// `Decode` will not terminate.
|
|
||||||
func Decode(data string, v interface{}) (MetaData, error) {
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if rv.Kind() != reflect.Ptr {
|
|
||||||
return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
|
|
||||||
}
|
|
||||||
if rv.IsNil() {
|
|
||||||
return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
|
|
||||||
}
|
|
||||||
p, err := parse(data)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
md := MetaData{
|
|
||||||
p.mapping, p.types, p.ordered,
|
|
||||||
make(map[string]bool, len(p.ordered)), nil,
|
|
||||||
}
|
|
||||||
return md, md.unify(p.mapping, indirect(rv))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFile is just like Decode, except it will automatically read the
|
|
||||||
// contents of the file at `fpath` and decode it for you.
|
|
||||||
func DecodeFile(fpath string, v interface{}) (MetaData, error) {
|
|
||||||
bs, err := ioutil.ReadFile(fpath)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
return Decode(string(bs), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeReader is just like Decode, except it will consume all bytes
|
|
||||||
// from the reader and decode it for you.
|
|
||||||
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
|
|
||||||
bs, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
return Decode(string(bs), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unify performs a sort of type unification based on the structure of `rv`,
|
|
||||||
// which is the client representation.
|
|
||||||
//
|
|
||||||
// Any type mismatch produces an error. Finding a type that we don't know
|
|
||||||
// how to handle produces an unsupported type error.
|
|
||||||
func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
|
|
||||||
|
|
||||||
// Special case. Look for a `Primitive` value.
|
|
||||||
if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
|
|
||||||
// Save the undecoded data and the key context into the primitive
|
|
||||||
// value.
|
|
||||||
context := make(Key, len(md.context))
|
|
||||||
copy(context, md.context)
|
|
||||||
rv.Set(reflect.ValueOf(Primitive{
|
|
||||||
undecoded: data,
|
|
||||||
context: context,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Unmarshaler Interface support.
|
|
||||||
if rv.CanAddr() {
|
|
||||||
if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
|
|
||||||
return v.UnmarshalTOML(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Handle time.Time values specifically.
|
|
||||||
// TODO: Remove this code when we decide to drop support for Go 1.1.
|
|
||||||
// This isn't necessary in Go 1.2 because time.Time satisfies the encoding
|
|
||||||
// interfaces.
|
|
||||||
if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) {
|
|
||||||
return md.unifyDatetime(data, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Look for a value satisfying the TextUnmarshaler interface.
|
|
||||||
if v, ok := rv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return md.unifyText(data, v)
|
|
||||||
}
|
|
||||||
// BUG(burntsushi)
|
|
||||||
// The behavior here is incorrect whenever a Go type satisfies the
|
|
||||||
// encoding.TextUnmarshaler interface but also corresponds to a TOML
|
|
||||||
// hash or array. In particular, the unmarshaler should only be applied
|
|
||||||
// to primitive TOML values. But at this point, it will be applied to
|
|
||||||
// all kinds of values and produce an incorrect error whenever those values
|
|
||||||
// are hashes or arrays (including arrays of tables).
|
|
||||||
|
|
||||||
k := rv.Kind()
|
|
||||||
|
|
||||||
// laziness
|
|
||||||
if k >= reflect.Int && k <= reflect.Uint64 {
|
|
||||||
return md.unifyInt(data, rv)
|
|
||||||
}
|
|
||||||
switch k {
|
|
||||||
case reflect.Ptr:
|
|
||||||
elem := reflect.New(rv.Type().Elem())
|
|
||||||
err := md.unify(data, reflect.Indirect(elem))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rv.Set(elem)
|
|
||||||
return nil
|
|
||||||
case reflect.Struct:
|
|
||||||
return md.unifyStruct(data, rv)
|
|
||||||
case reflect.Map:
|
|
||||||
return md.unifyMap(data, rv)
|
|
||||||
case reflect.Array:
|
|
||||||
return md.unifyArray(data, rv)
|
|
||||||
case reflect.Slice:
|
|
||||||
return md.unifySlice(data, rv)
|
|
||||||
case reflect.String:
|
|
||||||
return md.unifyString(data, rv)
|
|
||||||
case reflect.Bool:
|
|
||||||
return md.unifyBool(data, rv)
|
|
||||||
case reflect.Interface:
|
|
||||||
// we only support empty interfaces.
|
|
||||||
if rv.NumMethod() > 0 {
|
|
||||||
return e("unsupported type %s", rv.Type())
|
|
||||||
}
|
|
||||||
return md.unifyAnything(data, rv)
|
|
||||||
case reflect.Float32:
|
|
||||||
fallthrough
|
|
||||||
case reflect.Float64:
|
|
||||||
return md.unifyFloat64(data, rv)
|
|
||||||
}
|
|
||||||
return e("unsupported type %s", rv.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
|
|
||||||
tmap, ok := mapping.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
if mapping == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return e("type mismatch for %s: expected table but found %T",
|
|
||||||
rv.Type().String(), mapping)
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, datum := range tmap {
|
|
||||||
var f *field
|
|
||||||
fields := cachedTypeFields(rv.Type())
|
|
||||||
for i := range fields {
|
|
||||||
ff := &fields[i]
|
|
||||||
if ff.name == key {
|
|
||||||
f = ff
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if f == nil && strings.EqualFold(ff.name, key) {
|
|
||||||
f = ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f != nil {
|
|
||||||
subv := rv
|
|
||||||
for _, i := range f.index {
|
|
||||||
subv = indirect(subv.Field(i))
|
|
||||||
}
|
|
||||||
if isUnifiable(subv) {
|
|
||||||
md.decoded[md.context.add(key).String()] = true
|
|
||||||
md.context = append(md.context, key)
|
|
||||||
if err := md.unify(datum, subv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md.context = md.context[0 : len(md.context)-1]
|
|
||||||
} else if f.name != "" {
|
|
||||||
// Bad user! No soup for you!
|
|
||||||
return e("cannot write unexported field %s.%s",
|
|
||||||
rv.Type().String(), f.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
|
|
||||||
tmap, ok := mapping.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
if tmap == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("map", mapping)
|
|
||||||
}
|
|
||||||
if rv.IsNil() {
|
|
||||||
rv.Set(reflect.MakeMap(rv.Type()))
|
|
||||||
}
|
|
||||||
for k, v := range tmap {
|
|
||||||
md.decoded[md.context.add(k).String()] = true
|
|
||||||
md.context = append(md.context, k)
|
|
||||||
|
|
||||||
rvkey := indirect(reflect.New(rv.Type().Key()))
|
|
||||||
rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
|
|
||||||
if err := md.unify(v, rvval); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md.context = md.context[0 : len(md.context)-1]
|
|
||||||
|
|
||||||
rvkey.SetString(k)
|
|
||||||
rv.SetMapIndex(rvkey, rvval)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
|
|
||||||
datav := reflect.ValueOf(data)
|
|
||||||
if datav.Kind() != reflect.Slice {
|
|
||||||
if !datav.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("slice", data)
|
|
||||||
}
|
|
||||||
sliceLen := datav.Len()
|
|
||||||
if sliceLen != rv.Len() {
|
|
||||||
return e("expected array length %d; got TOML array of length %d",
|
|
||||||
rv.Len(), sliceLen)
|
|
||||||
}
|
|
||||||
return md.unifySliceArray(datav, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
|
|
||||||
datav := reflect.ValueOf(data)
|
|
||||||
if datav.Kind() != reflect.Slice {
|
|
||||||
if !datav.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("slice", data)
|
|
||||||
}
|
|
||||||
n := datav.Len()
|
|
||||||
if rv.IsNil() || rv.Cap() < n {
|
|
||||||
rv.Set(reflect.MakeSlice(rv.Type(), n, n))
|
|
||||||
}
|
|
||||||
rv.SetLen(n)
|
|
||||||
return md.unifySliceArray(datav, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
|
|
||||||
sliceLen := data.Len()
|
|
||||||
for i := 0; i < sliceLen; i++ {
|
|
||||||
v := data.Index(i).Interface()
|
|
||||||
sliceval := indirect(rv.Index(i))
|
|
||||||
if err := md.unify(v, sliceval); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error {
|
|
||||||
if _, ok := data.(time.Time); ok {
|
|
||||||
rv.Set(reflect.ValueOf(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("time.Time", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
|
|
||||||
if s, ok := data.(string); ok {
|
|
||||||
rv.SetString(s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("string", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
|
|
||||||
if num, ok := data.(float64); ok {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Float32:
|
|
||||||
fallthrough
|
|
||||||
case reflect.Float64:
|
|
||||||
rv.SetFloat(num)
|
|
||||||
default:
|
|
||||||
panic("bug")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("float", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
|
|
||||||
if num, ok := data.(int64); ok {
|
|
||||||
if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Int, reflect.Int64:
|
|
||||||
// No bounds checking necessary.
|
|
||||||
case reflect.Int8:
|
|
||||||
if num < math.MinInt8 || num > math.MaxInt8 {
|
|
||||||
return e("value %d is out of range for int8", num)
|
|
||||||
}
|
|
||||||
case reflect.Int16:
|
|
||||||
if num < math.MinInt16 || num > math.MaxInt16 {
|
|
||||||
return e("value %d is out of range for int16", num)
|
|
||||||
}
|
|
||||||
case reflect.Int32:
|
|
||||||
if num < math.MinInt32 || num > math.MaxInt32 {
|
|
||||||
return e("value %d is out of range for int32", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rv.SetInt(num)
|
|
||||||
} else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
|
|
||||||
unum := uint64(num)
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
// No bounds checking necessary.
|
|
||||||
case reflect.Uint8:
|
|
||||||
if num < 0 || unum > math.MaxUint8 {
|
|
||||||
return e("value %d is out of range for uint8", num)
|
|
||||||
}
|
|
||||||
case reflect.Uint16:
|
|
||||||
if num < 0 || unum > math.MaxUint16 {
|
|
||||||
return e("value %d is out of range for uint16", num)
|
|
||||||
}
|
|
||||||
case reflect.Uint32:
|
|
||||||
if num < 0 || unum > math.MaxUint32 {
|
|
||||||
return e("value %d is out of range for uint32", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rv.SetUint(unum)
|
|
||||||
} else {
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("integer", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
|
|
||||||
if b, ok := data.(bool); ok {
|
|
||||||
rv.SetBool(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("boolean", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
|
|
||||||
rv.Set(reflect.ValueOf(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error {
|
|
||||||
var s string
|
|
||||||
switch sdata := data.(type) {
|
|
||||||
case TextMarshaler:
|
|
||||||
text, err := sdata.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s = string(text)
|
|
||||||
case fmt.Stringer:
|
|
||||||
s = sdata.String()
|
|
||||||
case string:
|
|
||||||
s = sdata
|
|
||||||
case bool:
|
|
||||||
s = fmt.Sprintf("%v", sdata)
|
|
||||||
case int64:
|
|
||||||
s = fmt.Sprintf("%d", sdata)
|
|
||||||
case float64:
|
|
||||||
s = fmt.Sprintf("%f", sdata)
|
|
||||||
default:
|
|
||||||
return badtype("primitive (string-like)", data)
|
|
||||||
}
|
|
||||||
if err := v.UnmarshalText([]byte(s)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// rvalue returns a reflect.Value of `v`. All pointers are resolved.
|
|
||||||
func rvalue(v interface{}) reflect.Value {
|
|
||||||
return indirect(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// indirect returns the value pointed to by a pointer.
|
|
||||||
// Pointers are followed until the value is not a pointer.
|
|
||||||
// New values are allocated for each nil pointer.
|
|
||||||
//
|
|
||||||
// An exception to this rule is if the value satisfies an interface of
|
|
||||||
// interest to us (like encoding.TextUnmarshaler).
|
|
||||||
func indirect(v reflect.Value) reflect.Value {
|
|
||||||
if v.Kind() != reflect.Ptr {
|
|
||||||
if v.CanSet() {
|
|
||||||
pv := v.Addr()
|
|
||||||
if _, ok := pv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
if v.IsNil() {
|
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
|
||||||
}
|
|
||||||
return indirect(reflect.Indirect(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func isUnifiable(rv reflect.Value) bool {
|
|
||||||
if rv.CanSet() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := rv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func badtype(expected string, data interface{}) error {
|
|
||||||
return e("cannot load TOML value of type %T into a Go %s", data, expected)
|
|
||||||
}
|
|
||||||
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
@ -1,121 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// MetaData allows access to meta information about TOML data that may not
|
|
||||||
// be inferrable via reflection. In particular, whether a key has been defined
|
|
||||||
// and the TOML type of a key.
|
|
||||||
type MetaData struct {
|
|
||||||
mapping map[string]interface{}
|
|
||||||
types map[string]tomlType
|
|
||||||
keys []Key
|
|
||||||
decoded map[string]bool
|
|
||||||
context Key // Used only during decoding.
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDefined returns true if the key given exists in the TOML data. The key
|
|
||||||
// should be specified hierarchially. e.g.,
|
|
||||||
//
|
|
||||||
// // access the TOML key 'a.b.c'
|
|
||||||
// IsDefined("a", "b", "c")
|
|
||||||
//
|
|
||||||
// IsDefined will return false if an empty key given. Keys are case sensitive.
|
|
||||||
func (md *MetaData) IsDefined(key ...string) bool {
|
|
||||||
if len(key) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash map[string]interface{}
|
|
||||||
var ok bool
|
|
||||||
var hashOrVal interface{} = md.mapping
|
|
||||||
for _, k := range key {
|
|
||||||
if hash, ok = hashOrVal.(map[string]interface{}); !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if hashOrVal, ok = hash[k]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns a string representation of the type of the key specified.
|
|
||||||
//
|
|
||||||
// Type will return the empty string if given an empty key or a key that
|
|
||||||
// does not exist. Keys are case sensitive.
|
|
||||||
func (md *MetaData) Type(key ...string) string {
|
|
||||||
fullkey := strings.Join(key, ".")
|
|
||||||
if typ, ok := md.types[fullkey]; ok {
|
|
||||||
return typ.typeString()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
|
|
||||||
// to get values of this type.
|
|
||||||
type Key []string
|
|
||||||
|
|
||||||
func (k Key) String() string {
|
|
||||||
return strings.Join(k, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) maybeQuotedAll() string {
|
|
||||||
var ss []string
|
|
||||||
for i := range k {
|
|
||||||
ss = append(ss, k.maybeQuoted(i))
|
|
||||||
}
|
|
||||||
return strings.Join(ss, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) maybeQuoted(i int) string {
|
|
||||||
quote := false
|
|
||||||
for _, c := range k[i] {
|
|
||||||
if !isBareKeyChar(c) {
|
|
||||||
quote = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if quote {
|
|
||||||
return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
|
|
||||||
}
|
|
||||||
return k[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) add(piece string) Key {
|
|
||||||
newKey := make(Key, len(k)+1)
|
|
||||||
copy(newKey, k)
|
|
||||||
newKey[len(k)] = piece
|
|
||||||
return newKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys returns a slice of every key in the TOML data, including key groups.
|
|
||||||
// Each key is itself a slice, where the first element is the top of the
|
|
||||||
// hierarchy and the last is the most specific.
|
|
||||||
//
|
|
||||||
// The list will have the same order as the keys appeared in the TOML data.
|
|
||||||
//
|
|
||||||
// All keys returned are non-empty.
|
|
||||||
func (md *MetaData) Keys() []Key {
|
|
||||||
return md.keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Undecoded returns all keys that have not been decoded in the order in which
|
|
||||||
// they appear in the original TOML document.
|
|
||||||
//
|
|
||||||
// This includes keys that haven't been decoded because of a Primitive value.
|
|
||||||
// Once the Primitive value is decoded, the keys will be considered decoded.
|
|
||||||
//
|
|
||||||
// Also note that decoding into an empty interface will result in no decoding,
|
|
||||||
// and so no keys will be considered decoded.
|
|
||||||
//
|
|
||||||
// In this sense, the Undecoded keys correspond to keys in the TOML document
|
|
||||||
// that do not have a concrete type in your representation.
|
|
||||||
func (md *MetaData) Undecoded() []Key {
|
|
||||||
undecoded := make([]Key, 0, len(md.keys))
|
|
||||||
for _, key := range md.keys {
|
|
||||||
if !md.decoded[key.String()] {
|
|
||||||
undecoded = append(undecoded, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undecoded
|
|
||||||
}
|
|
||||||
1461
vendor/github.com/BurntSushi/toml/decode_test.go
generated
vendored
1461
vendor/github.com/BurntSushi/toml/decode_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
Package toml provides facilities for decoding and encoding TOML configuration
|
|
||||||
files via reflection. There is also support for delaying decoding with
|
|
||||||
the Primitive type, and querying the set of keys in a TOML document with the
|
|
||||||
MetaData type.
|
|
||||||
|
|
||||||
The specification implemented: https://github.com/toml-lang/toml
|
|
||||||
|
|
||||||
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
|
||||||
whether a file is a valid TOML document. It can also be used to print the
|
|
||||||
type of each key in a TOML document.
|
|
||||||
|
|
||||||
Testing
|
|
||||||
|
|
||||||
There are two important types of tests used for this package. The first is
|
|
||||||
contained inside '*_test.go' files and uses the standard Go unit testing
|
|
||||||
framework. These tests are primarily devoted to holistically testing the
|
|
||||||
decoder and encoder.
|
|
||||||
|
|
||||||
The second type of testing is used to verify the implementation's adherence
|
|
||||||
to the TOML specification. These tests have been factored into their own
|
|
||||||
project: https://github.com/BurntSushi/toml-test
|
|
||||||
|
|
||||||
The reason the tests are in a separate project is so that they can be used by
|
|
||||||
any implementation of TOML. Namely, it is language agnostic.
|
|
||||||
*/
|
|
||||||
package toml
|
|
||||||
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
@ -1,568 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tomlEncodeError struct{ error }
|
|
||||||
|
|
||||||
var (
|
|
||||||
errArrayMixedElementTypes = errors.New(
|
|
||||||
"toml: cannot encode array with mixed element types")
|
|
||||||
errArrayNilElement = errors.New(
|
|
||||||
"toml: cannot encode array with nil element")
|
|
||||||
errNonString = errors.New(
|
|
||||||
"toml: cannot encode a map with non-string key type")
|
|
||||||
errAnonNonStruct = errors.New(
|
|
||||||
"toml: cannot encode an anonymous field that is not a struct")
|
|
||||||
errArrayNoTable = errors.New(
|
|
||||||
"toml: TOML array element cannot contain a table")
|
|
||||||
errNoKey = errors.New(
|
|
||||||
"toml: top-level values must be Go maps or structs")
|
|
||||||
errAnything = errors.New("") // used in testing
|
|
||||||
)
|
|
||||||
|
|
||||||
var quotedReplacer = strings.NewReplacer(
|
|
||||||
"\t", "\\t",
|
|
||||||
"\n", "\\n",
|
|
||||||
"\r", "\\r",
|
|
||||||
"\"", "\\\"",
|
|
||||||
"\\", "\\\\",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encoder controls the encoding of Go values to a TOML document to some
|
|
||||||
// io.Writer.
|
|
||||||
//
|
|
||||||
// The indentation level can be controlled with the Indent field.
|
|
||||||
type Encoder struct {
|
|
||||||
// A single indentation level. By default it is two spaces.
|
|
||||||
Indent string
|
|
||||||
|
|
||||||
// hasWritten is whether we have written any output to w yet.
|
|
||||||
hasWritten bool
|
|
||||||
w *bufio.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
|
|
||||||
// given. By default, a single indentation level is 2 spaces.
|
|
||||||
func NewEncoder(w io.Writer) *Encoder {
|
|
||||||
return &Encoder{
|
|
||||||
w: bufio.NewWriter(w),
|
|
||||||
Indent: " ",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes a TOML representation of the Go value to the underlying
|
|
||||||
// io.Writer. If the value given cannot be encoded to a valid TOML document,
|
|
||||||
// then an error is returned.
|
|
||||||
//
|
|
||||||
// The mapping between Go values and TOML values should be precisely the same
|
|
||||||
// as for the Decode* functions. Similarly, the TextMarshaler interface is
|
|
||||||
// supported by encoding the resulting bytes as strings. (If you want to write
|
|
||||||
// arbitrary binary data then you will need to use something like base64 since
|
|
||||||
// TOML does not have any binary types.)
|
|
||||||
//
|
|
||||||
// When encoding TOML hashes (i.e., Go maps or structs), keys without any
|
|
||||||
// sub-hashes are encoded first.
|
|
||||||
//
|
|
||||||
// If a Go map is encoded, then its keys are sorted alphabetically for
|
|
||||||
// deterministic output. More control over this behavior may be provided if
|
|
||||||
// there is demand for it.
|
|
||||||
//
|
|
||||||
// Encoding Go values without a corresponding TOML representation---like map
|
|
||||||
// types with non-string keys---will cause an error to be returned. Similarly
|
|
||||||
// for mixed arrays/slices, arrays/slices with nil elements, embedded
|
|
||||||
// non-struct types and nested slices containing maps or structs.
|
|
||||||
// (e.g., [][]map[string]string is not allowed but []map[string]string is OK
|
|
||||||
// and so is []map[string][]string.)
|
|
||||||
func (enc *Encoder) Encode(v interface{}) error {
|
|
||||||
rv := eindirect(reflect.ValueOf(v))
|
|
||||||
if err := enc.safeEncode(Key([]string{}), rv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return enc.w.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if terr, ok := r.(tomlEncodeError); ok {
|
|
||||||
err = terr.error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
enc.encode(key, rv)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) encode(key Key, rv reflect.Value) {
|
|
||||||
// Special case. Time needs to be in ISO8601 format.
|
|
||||||
// Special case. If we can marshal the type to text, then we used that.
|
|
||||||
// Basically, this prevents the encoder for handling these types as
|
|
||||||
// generic structs (or whatever the underlying type of a TextMarshaler is).
|
|
||||||
switch rv.Interface().(type) {
|
|
||||||
case time.Time, TextMarshaler:
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
k := rv.Kind()
|
|
||||||
switch k {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
|
||||||
reflect.Uint64,
|
|
||||||
reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
|
|
||||||
enc.eArrayOfTables(key, rv)
|
|
||||||
} else {
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.encode(key, rv.Elem())
|
|
||||||
case reflect.Map:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.eTable(key, rv)
|
|
||||||
case reflect.Ptr:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.encode(key, rv.Elem())
|
|
||||||
case reflect.Struct:
|
|
||||||
enc.eTable(key, rv)
|
|
||||||
default:
|
|
||||||
panic(e("unsupported type for key '%s': %s", key, k))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eElement encodes any value that can be an array element (primitives and
|
|
||||||
// arrays).
|
|
||||||
func (enc *Encoder) eElement(rv reflect.Value) {
|
|
||||||
switch v := rv.Interface().(type) {
|
|
||||||
case time.Time:
|
|
||||||
// Special case time.Time as a primitive. Has to come before
|
|
||||||
// TextMarshaler below because time.Time implements
|
|
||||||
// encoding.TextMarshaler, but we need to always use UTC.
|
|
||||||
enc.wf(v.UTC().Format("2006-01-02T15:04:05Z"))
|
|
||||||
return
|
|
||||||
case TextMarshaler:
|
|
||||||
// Special case. Use text marshaler if it's available for this value.
|
|
||||||
if s, err := v.MarshalText(); err != nil {
|
|
||||||
encPanic(err)
|
|
||||||
} else {
|
|
||||||
enc.writeQuoted(string(s))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
enc.wf(strconv.FormatBool(rv.Bool()))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64:
|
|
||||||
enc.wf(strconv.FormatInt(rv.Int(), 10))
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
|
||||||
reflect.Uint32, reflect.Uint64:
|
|
||||||
enc.wf(strconv.FormatUint(rv.Uint(), 10))
|
|
||||||
case reflect.Float32:
|
|
||||||
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
|
|
||||||
case reflect.Float64:
|
|
||||||
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
enc.eArrayOrSliceElement(rv)
|
|
||||||
case reflect.Interface:
|
|
||||||
enc.eElement(rv.Elem())
|
|
||||||
case reflect.String:
|
|
||||||
enc.writeQuoted(rv.String())
|
|
||||||
default:
|
|
||||||
panic(e("unexpected primitive type: %s", rv.Kind()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// By the TOML spec, all floats must have a decimal with at least one
|
|
||||||
// number on either side.
|
|
||||||
func floatAddDecimal(fstr string) string {
|
|
||||||
if !strings.Contains(fstr, ".") {
|
|
||||||
return fstr + ".0"
|
|
||||||
}
|
|
||||||
return fstr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) writeQuoted(s string) {
|
|
||||||
enc.wf("\"%s\"", quotedReplacer.Replace(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
|
|
||||||
length := rv.Len()
|
|
||||||
enc.wf("[")
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
elem := rv.Index(i)
|
|
||||||
enc.eElement(elem)
|
|
||||||
if i != length-1 {
|
|
||||||
enc.wf(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enc.wf("]")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
|
||||||
if len(key) == 0 {
|
|
||||||
encPanic(errNoKey)
|
|
||||||
}
|
|
||||||
for i := 0; i < rv.Len(); i++ {
|
|
||||||
trv := rv.Index(i)
|
|
||||||
if isNil(trv) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
enc.newline()
|
|
||||||
enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
|
|
||||||
enc.newline()
|
|
||||||
enc.eMapOrStruct(key, trv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
if len(key) == 1 {
|
|
||||||
// Output an extra newline between top-level tables.
|
|
||||||
// (The newline isn't written if nothing else has been written though.)
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
if len(key) > 0 {
|
|
||||||
enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
enc.eMapOrStruct(key, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
|
|
||||||
switch rv := eindirect(rv); rv.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
enc.eMap(key, rv)
|
|
||||||
case reflect.Struct:
|
|
||||||
enc.eStruct(key, rv)
|
|
||||||
default:
|
|
||||||
panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eMap(key Key, rv reflect.Value) {
|
|
||||||
rt := rv.Type()
|
|
||||||
if rt.Key().Kind() != reflect.String {
|
|
||||||
encPanic(errNonString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort keys so that we have deterministic output. And write keys directly
|
|
||||||
// underneath this key first, before writing sub-structs or sub-maps.
|
|
||||||
var mapKeysDirect, mapKeysSub []string
|
|
||||||
for _, mapKey := range rv.MapKeys() {
|
|
||||||
k := mapKey.String()
|
|
||||||
if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
|
|
||||||
mapKeysSub = append(mapKeysSub, k)
|
|
||||||
} else {
|
|
||||||
mapKeysDirect = append(mapKeysDirect, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var writeMapKeys = func(mapKeys []string) {
|
|
||||||
sort.Strings(mapKeys)
|
|
||||||
for _, mapKey := range mapKeys {
|
|
||||||
mrv := rv.MapIndex(reflect.ValueOf(mapKey))
|
|
||||||
if isNil(mrv) {
|
|
||||||
// Don't write anything for nil fields.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enc.encode(key.add(mapKey), mrv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeMapKeys(mapKeysDirect)
|
|
||||||
writeMapKeys(mapKeysSub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
|
|
||||||
// Write keys for fields directly under this key first, because if we write
|
|
||||||
// a field that creates a new table, then all keys under it will be in that
|
|
||||||
// table (not the one we're writing here).
|
|
||||||
rt := rv.Type()
|
|
||||||
var fieldsDirect, fieldsSub [][]int
|
|
||||||
var addFields func(rt reflect.Type, rv reflect.Value, start []int)
|
|
||||||
addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
|
|
||||||
for i := 0; i < rt.NumField(); i++ {
|
|
||||||
f := rt.Field(i)
|
|
||||||
// skip unexported fields
|
|
||||||
if f.PkgPath != "" && !f.Anonymous {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
frv := rv.Field(i)
|
|
||||||
if f.Anonymous {
|
|
||||||
t := f.Type
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
// Treat anonymous struct fields with
|
|
||||||
// tag names as though they are not
|
|
||||||
// anonymous, like encoding/json does.
|
|
||||||
if getOptions(f.Tag).name == "" {
|
|
||||||
addFields(t, frv, f.Index)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
if t.Elem().Kind() == reflect.Struct &&
|
|
||||||
getOptions(f.Tag).name == "" {
|
|
||||||
if !frv.IsNil() {
|
|
||||||
addFields(t.Elem(), frv.Elem(), f.Index)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Fall through to the normal field encoding logic below
|
|
||||||
// for non-struct anonymous fields.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if typeIsHash(tomlTypeOfGo(frv)) {
|
|
||||||
fieldsSub = append(fieldsSub, append(start, f.Index...))
|
|
||||||
} else {
|
|
||||||
fieldsDirect = append(fieldsDirect, append(start, f.Index...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addFields(rt, rv, nil)
|
|
||||||
|
|
||||||
var writeFields = func(fields [][]int) {
|
|
||||||
for _, fieldIndex := range fields {
|
|
||||||
sft := rt.FieldByIndex(fieldIndex)
|
|
||||||
sf := rv.FieldByIndex(fieldIndex)
|
|
||||||
if isNil(sf) {
|
|
||||||
// Don't write anything for nil fields.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := getOptions(sft.Tag)
|
|
||||||
if opts.skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
keyName := sft.Name
|
|
||||||
if opts.name != "" {
|
|
||||||
keyName = opts.name
|
|
||||||
}
|
|
||||||
if opts.omitempty && isEmpty(sf) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if opts.omitzero && isZero(sf) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
enc.encode(key.add(keyName), sf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeFields(fieldsDirect)
|
|
||||||
writeFields(fieldsSub)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tomlTypeName returns the TOML type name of the Go value's type. It is
|
|
||||||
// used to determine whether the types of array elements are mixed (which is
|
|
||||||
// forbidden). If the Go value is nil, then it is illegal for it to be an array
|
|
||||||
// element, and valueIsNil is returned as true.
|
|
||||||
|
|
||||||
// Returns the TOML type of a Go value. The type may be `nil`, which means
|
|
||||||
// no concrete TOML type could be found.
|
|
||||||
func tomlTypeOfGo(rv reflect.Value) tomlType {
|
|
||||||
if isNil(rv) || !rv.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return tomlBool
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
|
||||||
reflect.Uint64:
|
|
||||||
return tomlInteger
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return tomlFloat
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if typeEqual(tomlHash, tomlArrayType(rv)) {
|
|
||||||
return tomlArrayHash
|
|
||||||
}
|
|
||||||
return tomlArray
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return tomlTypeOfGo(rv.Elem())
|
|
||||||
case reflect.String:
|
|
||||||
return tomlString
|
|
||||||
case reflect.Map:
|
|
||||||
return tomlHash
|
|
||||||
case reflect.Struct:
|
|
||||||
switch rv.Interface().(type) {
|
|
||||||
case time.Time:
|
|
||||||
return tomlDatetime
|
|
||||||
case TextMarshaler:
|
|
||||||
return tomlString
|
|
||||||
default:
|
|
||||||
return tomlHash
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unexpected reflect.Kind: " + rv.Kind().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tomlArrayType returns the element type of a TOML array. The type returned
|
|
||||||
// may be nil if it cannot be determined (e.g., a nil slice or a zero length
|
|
||||||
// slize). This function may also panic if it finds a type that cannot be
|
|
||||||
// expressed in TOML (such as nil elements, heterogeneous arrays or directly
|
|
||||||
// nested arrays of tables).
|
|
||||||
func tomlArrayType(rv reflect.Value) tomlType {
|
|
||||||
if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
firstType := tomlTypeOfGo(rv.Index(0))
|
|
||||||
if firstType == nil {
|
|
||||||
encPanic(errArrayNilElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
rvlen := rv.Len()
|
|
||||||
for i := 1; i < rvlen; i++ {
|
|
||||||
elem := rv.Index(i)
|
|
||||||
switch elemType := tomlTypeOfGo(elem); {
|
|
||||||
case elemType == nil:
|
|
||||||
encPanic(errArrayNilElement)
|
|
||||||
case !typeEqual(firstType, elemType):
|
|
||||||
encPanic(errArrayMixedElementTypes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we have a nested array, then we must make sure that the nested
|
|
||||||
// array contains ONLY primitives.
|
|
||||||
// This checks arbitrarily nested arrays.
|
|
||||||
if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
|
|
||||||
nest := tomlArrayType(eindirect(rv.Index(0)))
|
|
||||||
if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
|
|
||||||
encPanic(errArrayNoTable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstType
|
|
||||||
}
|
|
||||||
|
|
||||||
type tagOptions struct {
|
|
||||||
skip bool // "-"
|
|
||||||
name string
|
|
||||||
omitempty bool
|
|
||||||
omitzero bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOptions(tag reflect.StructTag) tagOptions {
|
|
||||||
t := tag.Get("toml")
|
|
||||||
if t == "-" {
|
|
||||||
return tagOptions{skip: true}
|
|
||||||
}
|
|
||||||
var opts tagOptions
|
|
||||||
parts := strings.Split(t, ",")
|
|
||||||
opts.name = parts[0]
|
|
||||||
for _, s := range parts[1:] {
|
|
||||||
switch s {
|
|
||||||
case "omitempty":
|
|
||||||
opts.omitempty = true
|
|
||||||
case "omitzero":
|
|
||||||
opts.omitzero = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func isZero(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return rv.Int() == 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
return rv.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return rv.Float() == 0.0
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEmpty(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
|
||||||
return rv.Len() == 0
|
|
||||||
case reflect.Bool:
|
|
||||||
return !rv.Bool()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) newline() {
|
|
||||||
if enc.hasWritten {
|
|
||||||
enc.wf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
|
|
||||||
if len(key) == 0 {
|
|
||||||
encPanic(errNoKey)
|
|
||||||
}
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
|
||||||
enc.eElement(val)
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) wf(format string, v ...interface{}) {
|
|
||||||
if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
|
|
||||||
encPanic(err)
|
|
||||||
}
|
|
||||||
enc.hasWritten = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) indentStr(key Key) string {
|
|
||||||
return strings.Repeat(enc.Indent, len(key)-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encPanic(err error) {
|
|
||||||
panic(tomlEncodeError{err})
|
|
||||||
}
|
|
||||||
|
|
||||||
func eindirect(v reflect.Value) reflect.Value {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return eindirect(v.Elem())
|
|
||||||
default:
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNil(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return rv.IsNil()
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func panicIfInvalidKey(key Key) {
|
|
||||||
for _, k := range key {
|
|
||||||
if len(k) == 0 {
|
|
||||||
encPanic(e("Key '%s' is not a valid table name. Key names "+
|
|
||||||
"cannot be empty.", key.maybeQuotedAll()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidKeyName(s string) bool {
|
|
||||||
return len(s) != 0
|
|
||||||
}
|
|
||||||
615
vendor/github.com/BurntSushi/toml/encode_test.go
generated
vendored
615
vendor/github.com/BurntSushi/toml/encode_test.go
generated
vendored
@ -1,615 +0,0 @@
|
|||||||
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 {
|
|
||||||
t.Logf("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",
|
|
||||||
},
|
|
||||||
"embedded non-struct": {
|
|
||||||
input: struct{ NonStruct }{5},
|
|
||||||
wantOutput: "NonStruct = 5\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{
|
|
||||||
{
|
|
||||||
"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) 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 {
|
|
||||||
Bool bool `toml:"bool,omitempty"`
|
|
||||||
String string `toml:"string,omitempty"`
|
|
||||||
Array [0]byte `toml:"array,omitempty"`
|
|
||||||
Slice []int `toml:"slice,omitempty"`
|
|
||||||
Map map[string]string `toml:"map,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var v simple
|
|
||||||
encodeExpected(t, "fields with omitempty are omitted when empty", v, "", nil)
|
|
||||||
v = simple{
|
|
||||||
Bool: true,
|
|
||||||
String: " ",
|
|
||||||
Slice: []int{2, 3, 4},
|
|
||||||
Map: map[string]string{"foo": "bar"},
|
|
||||||
}
|
|
||||||
expected := `bool = true
|
|
||||||
string = " "
|
|
||||||
slice = [2, 3, 4]
|
|
||||||
|
|
||||||
[map]
|
|
||||||
foo = "bar"
|
|
||||||
`
|
|
||||||
encodeExpected(t, "fields with omitempty are not omitted when non-empty",
|
|
||||||
v, 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 TestEncodeOmitemptyWithEmptyName(t *testing.T) {
|
|
||||||
type simple struct {
|
|
||||||
S []int `toml:",omitempty"`
|
|
||||||
}
|
|
||||||
v := simple{[]int{1, 2, 3}}
|
|
||||||
expected := "S = [1, 2, 3]\n"
|
|
||||||
encodeExpected(t, "simple with omitempty, no name, non-empty field",
|
|
||||||
v, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeAnonymousStruct(t *testing.T) {
|
|
||||||
type Inner struct{ N int }
|
|
||||||
type Outer0 struct{ Inner }
|
|
||||||
type Outer1 struct {
|
|
||||||
Inner `toml:"inner"`
|
|
||||||
}
|
|
||||||
|
|
||||||
v0 := Outer0{Inner{3}}
|
|
||||||
expected := "N = 3\n"
|
|
||||||
encodeExpected(t, "embedded anonymous untagged struct", v0, expected, nil)
|
|
||||||
|
|
||||||
v1 := Outer1{Inner{3}}
|
|
||||||
expected = "[inner]\n N = 3\n"
|
|
||||||
encodeExpected(t, "embedded anonymous tagged struct", v1, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeAnonymousStructPointerField(t *testing.T) {
|
|
||||||
type Inner struct{ N int }
|
|
||||||
type Outer0 struct{ *Inner }
|
|
||||||
type Outer1 struct {
|
|
||||||
*Inner `toml:"inner"`
|
|
||||||
}
|
|
||||||
|
|
||||||
v0 := Outer0{}
|
|
||||||
expected := ""
|
|
||||||
encodeExpected(t, "nil anonymous untagged struct pointer field", v0, expected, nil)
|
|
||||||
|
|
||||||
v0 = Outer0{&Inner{3}}
|
|
||||||
expected = "N = 3\n"
|
|
||||||
encodeExpected(t, "non-nil anonymous untagged struct pointer field", v0, expected, nil)
|
|
||||||
|
|
||||||
v1 := Outer1{}
|
|
||||||
expected = ""
|
|
||||||
encodeExpected(t, "nil anonymous tagged struct pointer field", v1, expected, nil)
|
|
||||||
|
|
||||||
v1 = Outer1{&Inner{3}}
|
|
||||||
expected = "[inner]\n N = 3\n"
|
|
||||||
encodeExpected(t, "non-nil anonymous tagged struct pointer field", v1, expected, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeIgnoredFields(t *testing.T) {
|
|
||||||
type simple struct {
|
|
||||||
Number int `toml:"-"`
|
|
||||||
}
|
|
||||||
value := simple{}
|
|
||||||
expected := ""
|
|
||||||
encodeExpected(t, "ignored field", 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"
|
|
||||||
}
|
|
||||||
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
@ -1,19 +0,0 @@
|
|||||||
// +build go1.2
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
// In order to support Go 1.1, we define our own TextMarshaler and
|
|
||||||
// TextUnmarshaler types. For Go 1.2+, we just alias them with the
|
|
||||||
// standard library interfaces.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
|
||||||
// so that Go 1.1 can be supported.
|
|
||||||
type TextMarshaler encoding.TextMarshaler
|
|
||||||
|
|
||||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
|
||||||
// here so that Go 1.1 can be supported.
|
|
||||||
type TextUnmarshaler encoding.TextUnmarshaler
|
|
||||||
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
// +build !go1.2
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
// These interfaces were introduced in Go 1.2, so we add them manually when
|
|
||||||
// compiling for Go 1.1.
|
|
||||||
|
|
||||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
|
||||||
// so that Go 1.1 can be supported.
|
|
||||||
type TextMarshaler interface {
|
|
||||||
MarshalText() (text []byte, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
|
||||||
// here so that Go 1.1 can be supported.
|
|
||||||
type TextUnmarshaler interface {
|
|
||||||
UnmarshalText(text []byte) error
|
|
||||||
}
|
|
||||||
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
@ -1,953 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type itemType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
itemError itemType = iota
|
|
||||||
itemNIL // used in the parser to indicate no type
|
|
||||||
itemEOF
|
|
||||||
itemText
|
|
||||||
itemString
|
|
||||||
itemRawString
|
|
||||||
itemMultilineString
|
|
||||||
itemRawMultilineString
|
|
||||||
itemBool
|
|
||||||
itemInteger
|
|
||||||
itemFloat
|
|
||||||
itemDatetime
|
|
||||||
itemArray // the start of an array
|
|
||||||
itemArrayEnd
|
|
||||||
itemTableStart
|
|
||||||
itemTableEnd
|
|
||||||
itemArrayTableStart
|
|
||||||
itemArrayTableEnd
|
|
||||||
itemKeyStart
|
|
||||||
itemCommentStart
|
|
||||||
itemInlineTableStart
|
|
||||||
itemInlineTableEnd
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
eof = 0
|
|
||||||
comma = ','
|
|
||||||
tableStart = '['
|
|
||||||
tableEnd = ']'
|
|
||||||
arrayTableStart = '['
|
|
||||||
arrayTableEnd = ']'
|
|
||||||
tableSep = '.'
|
|
||||||
keySep = '='
|
|
||||||
arrayStart = '['
|
|
||||||
arrayEnd = ']'
|
|
||||||
commentStart = '#'
|
|
||||||
stringStart = '"'
|
|
||||||
stringEnd = '"'
|
|
||||||
rawStringStart = '\''
|
|
||||||
rawStringEnd = '\''
|
|
||||||
inlineTableStart = '{'
|
|
||||||
inlineTableEnd = '}'
|
|
||||||
)
|
|
||||||
|
|
||||||
type stateFn func(lx *lexer) stateFn
|
|
||||||
|
|
||||||
type lexer struct {
|
|
||||||
input string
|
|
||||||
start int
|
|
||||||
pos int
|
|
||||||
line int
|
|
||||||
state stateFn
|
|
||||||
items chan item
|
|
||||||
|
|
||||||
// Allow for backing up up to three runes.
|
|
||||||
// This is necessary because TOML contains 3-rune tokens (""" and ''').
|
|
||||||
prevWidths [3]int
|
|
||||||
nprev int // how many of prevWidths are in use
|
|
||||||
// If we emit an eof, we can still back up, but it is not OK to call
|
|
||||||
// next again.
|
|
||||||
atEOF bool
|
|
||||||
|
|
||||||
// A stack of state functions used to maintain context.
|
|
||||||
// The idea is to reuse parts of the state machine in various places.
|
|
||||||
// For example, values can appear at the top level or within arbitrarily
|
|
||||||
// nested arrays. The last state on the stack is used after a value has
|
|
||||||
// been lexed. Similarly for comments.
|
|
||||||
stack []stateFn
|
|
||||||
}
|
|
||||||
|
|
||||||
type item struct {
|
|
||||||
typ itemType
|
|
||||||
val string
|
|
||||||
line int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) nextItem() item {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case item := <-lx.items:
|
|
||||||
return item
|
|
||||||
default:
|
|
||||||
lx.state = lx.state(lx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lex(input string) *lexer {
|
|
||||||
lx := &lexer{
|
|
||||||
input: input,
|
|
||||||
state: lexTop,
|
|
||||||
line: 1,
|
|
||||||
items: make(chan item, 10),
|
|
||||||
stack: make([]stateFn, 0, 10),
|
|
||||||
}
|
|
||||||
return lx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) push(state stateFn) {
|
|
||||||
lx.stack = append(lx.stack, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) pop() stateFn {
|
|
||||||
if len(lx.stack) == 0 {
|
|
||||||
return lx.errorf("BUG in lexer: no states to pop")
|
|
||||||
}
|
|
||||||
last := lx.stack[len(lx.stack)-1]
|
|
||||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
|
||||||
return last
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) current() string {
|
|
||||||
return lx.input[lx.start:lx.pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) emit(typ itemType) {
|
|
||||||
lx.items <- item{typ, lx.current(), lx.line}
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) emitTrim(typ itemType) {
|
|
||||||
lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line}
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) next() (r rune) {
|
|
||||||
if lx.atEOF {
|
|
||||||
panic("next called after EOF")
|
|
||||||
}
|
|
||||||
if lx.pos >= len(lx.input) {
|
|
||||||
lx.atEOF = true
|
|
||||||
return eof
|
|
||||||
}
|
|
||||||
|
|
||||||
if lx.input[lx.pos] == '\n' {
|
|
||||||
lx.line++
|
|
||||||
}
|
|
||||||
lx.prevWidths[2] = lx.prevWidths[1]
|
|
||||||
lx.prevWidths[1] = lx.prevWidths[0]
|
|
||||||
if lx.nprev < 3 {
|
|
||||||
lx.nprev++
|
|
||||||
}
|
|
||||||
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
|
|
||||||
lx.prevWidths[0] = w
|
|
||||||
lx.pos += w
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore skips over the pending input before this point.
|
|
||||||
func (lx *lexer) ignore() {
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup steps back one rune. Can be called only twice between calls to next.
|
|
||||||
func (lx *lexer) backup() {
|
|
||||||
if lx.atEOF {
|
|
||||||
lx.atEOF = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if lx.nprev < 1 {
|
|
||||||
panic("backed up too far")
|
|
||||||
}
|
|
||||||
w := lx.prevWidths[0]
|
|
||||||
lx.prevWidths[0] = lx.prevWidths[1]
|
|
||||||
lx.prevWidths[1] = lx.prevWidths[2]
|
|
||||||
lx.nprev--
|
|
||||||
lx.pos -= w
|
|
||||||
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
|
|
||||||
lx.line--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// accept consumes the next rune if it's equal to `valid`.
|
|
||||||
func (lx *lexer) accept(valid rune) bool {
|
|
||||||
if lx.next() == valid {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek returns but does not consume the next rune in the input.
|
|
||||||
func (lx *lexer) peek() rune {
|
|
||||||
r := lx.next()
|
|
||||||
lx.backup()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip ignores all input that matches the given predicate.
|
|
||||||
func (lx *lexer) skip(pred func(rune) bool) {
|
|
||||||
for {
|
|
||||||
r := lx.next()
|
|
||||||
if pred(r) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.ignore()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf stops all lexing by emitting an error and returning `nil`.
|
|
||||||
// Note that any value that is a character is escaped if it's a special
|
|
||||||
// character (newlines, tabs, etc.).
|
|
||||||
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
|
||||||
lx.items <- item{
|
|
||||||
itemError,
|
|
||||||
fmt.Sprintf(format, values...),
|
|
||||||
lx.line,
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTop consumes elements at the top level of TOML data.
|
|
||||||
func lexTop(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isWhitespace(r) || isNL(r) {
|
|
||||||
return lexSkip(lx, lexTop)
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case commentStart:
|
|
||||||
lx.push(lexTop)
|
|
||||||
return lexCommentStart
|
|
||||||
case tableStart:
|
|
||||||
return lexTableStart
|
|
||||||
case eof:
|
|
||||||
if lx.pos > lx.start {
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
}
|
|
||||||
lx.emit(itemEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, the only valid item can be a key, so we back up
|
|
||||||
// and let the key lexer do the rest.
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexTopEnd)
|
|
||||||
return lexKeyStart
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
|
|
||||||
// or a table.) It must see only whitespace, and will turn back to lexTop
|
|
||||||
// upon a newline. If it sees EOF, it will quit the lexer successfully.
|
|
||||||
func lexTopEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == commentStart:
|
|
||||||
// a comment will read to a newline for us.
|
|
||||||
lx.push(lexTop)
|
|
||||||
return lexCommentStart
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexTopEnd
|
|
||||||
case isNL(r):
|
|
||||||
lx.ignore()
|
|
||||||
return lexTop
|
|
||||||
case r == eof:
|
|
||||||
lx.emit(itemEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a top-level item to end with a newline, "+
|
|
||||||
"comment, or EOF, but got %q instead", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTable lexes the beginning of a table. Namely, it makes sure that
|
|
||||||
// it starts with a character other than '.' and ']'.
|
|
||||||
// It assumes that '[' has already been consumed.
|
|
||||||
// It also handles the case that this is an item in an array of tables.
|
|
||||||
// e.g., '[[name]]'.
|
|
||||||
func lexTableStart(lx *lexer) stateFn {
|
|
||||||
if lx.peek() == arrayTableStart {
|
|
||||||
lx.next()
|
|
||||||
lx.emit(itemArrayTableStart)
|
|
||||||
lx.push(lexArrayTableEnd)
|
|
||||||
} else {
|
|
||||||
lx.emit(itemTableStart)
|
|
||||||
lx.push(lexTableEnd)
|
|
||||||
}
|
|
||||||
return lexTableNameStart
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexTableEnd(lx *lexer) stateFn {
|
|
||||||
lx.emit(itemTableEnd)
|
|
||||||
return lexTopEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexArrayTableEnd(lx *lexer) stateFn {
|
|
||||||
if r := lx.next(); r != arrayTableEnd {
|
|
||||||
return lx.errorf("expected end of table array name delimiter %q, "+
|
|
||||||
"but got %q instead", arrayTableEnd, r)
|
|
||||||
}
|
|
||||||
lx.emit(itemArrayTableEnd)
|
|
||||||
return lexTopEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexTableNameStart(lx *lexer) stateFn {
|
|
||||||
lx.skip(isWhitespace)
|
|
||||||
switch r := lx.peek(); {
|
|
||||||
case r == tableEnd || r == eof:
|
|
||||||
return lx.errorf("unexpected end of table name " +
|
|
||||||
"(table names cannot be empty)")
|
|
||||||
case r == tableSep:
|
|
||||||
return lx.errorf("unexpected table separator " +
|
|
||||||
"(table names cannot be empty)")
|
|
||||||
case r == stringStart || r == rawStringStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.push(lexTableNameEnd)
|
|
||||||
return lexValue // reuse string lexing
|
|
||||||
default:
|
|
||||||
return lexBareTableName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBareTableName lexes the name of a table. It assumes that at least one
|
|
||||||
// valid character for the table has already been read.
|
|
||||||
func lexBareTableName(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isBareKeyChar(r) {
|
|
||||||
return lexBareTableName
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexTableNameEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTableNameEnd reads the end of a piece of a table name, optionally
|
|
||||||
// consuming whitespace.
|
|
||||||
func lexTableNameEnd(lx *lexer) stateFn {
|
|
||||||
lx.skip(isWhitespace)
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexTableNameEnd
|
|
||||||
case r == tableSep:
|
|
||||||
lx.ignore()
|
|
||||||
return lexTableNameStart
|
|
||||||
case r == tableEnd:
|
|
||||||
return lx.pop()
|
|
||||||
default:
|
|
||||||
return lx.errorf("expected '.' or ']' to end table name, "+
|
|
||||||
"but got %q instead", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexKeyStart consumes a key name up until the first non-whitespace character.
|
|
||||||
// lexKeyStart will ignore whitespace.
|
|
||||||
func lexKeyStart(lx *lexer) stateFn {
|
|
||||||
r := lx.peek()
|
|
||||||
switch {
|
|
||||||
case r == keySep:
|
|
||||||
return lx.errorf("unexpected key separator %q", keySep)
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
lx.next()
|
|
||||||
return lexSkip(lx, lexKeyStart)
|
|
||||||
case r == stringStart || r == rawStringStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemKeyStart)
|
|
||||||
lx.push(lexKeyEnd)
|
|
||||||
return lexValue // reuse string lexing
|
|
||||||
default:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemKeyStart)
|
|
||||||
return lexBareKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBareKey consumes the text of a bare key. Assumes that the first character
|
|
||||||
// (which is not whitespace) has not yet been consumed.
|
|
||||||
func lexBareKey(lx *lexer) stateFn {
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case isBareKeyChar(r):
|
|
||||||
return lexBareKey
|
|
||||||
case isWhitespace(r):
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexKeyEnd
|
|
||||||
case r == keySep:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexKeyEnd
|
|
||||||
default:
|
|
||||||
return lx.errorf("bare keys cannot contain %q", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexKeyEnd consumes the end of a key and trims whitespace (up to the key
|
|
||||||
// separator).
|
|
||||||
func lexKeyEnd(lx *lexer) stateFn {
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case r == keySep:
|
|
||||||
return lexSkip(lx, lexValue)
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexKeyEnd)
|
|
||||||
default:
|
|
||||||
return lx.errorf("expected key separator %q, but got %q instead",
|
|
||||||
keySep, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexValue starts the consumption of a value anywhere a value is expected.
|
|
||||||
// lexValue will ignore whitespace.
|
|
||||||
// After a value is lexed, the last state on the next is popped and returned.
|
|
||||||
func lexValue(lx *lexer) stateFn {
|
|
||||||
// We allow whitespace to precede a value, but NOT newlines.
|
|
||||||
// In array syntax, the array states are responsible for ignoring newlines.
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexValue)
|
|
||||||
case isDigit(r):
|
|
||||||
lx.backup() // avoid an extra state and use the same as above
|
|
||||||
return lexNumberOrDateStart
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case arrayStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemArray)
|
|
||||||
return lexArrayValue
|
|
||||||
case inlineTableStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemInlineTableStart)
|
|
||||||
return lexInlineTableValue
|
|
||||||
case stringStart:
|
|
||||||
if lx.accept(stringStart) {
|
|
||||||
if lx.accept(stringStart) {
|
|
||||||
lx.ignore() // Ignore """
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
lx.ignore() // ignore the '"'
|
|
||||||
return lexString
|
|
||||||
case rawStringStart:
|
|
||||||
if lx.accept(rawStringStart) {
|
|
||||||
if lx.accept(rawStringStart) {
|
|
||||||
lx.ignore() // Ignore """
|
|
||||||
return lexMultilineRawString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
lx.ignore() // ignore the "'"
|
|
||||||
return lexRawString
|
|
||||||
case '+', '-':
|
|
||||||
return lexNumberStart
|
|
||||||
case '.': // special error case, be kind to users
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
if unicode.IsLetter(r) {
|
|
||||||
// Be permissive here; lexBool will give a nice error if the
|
|
||||||
// user wrote something like
|
|
||||||
// x = foo
|
|
||||||
// (i.e. not 'true' or 'false' but is something else word-like.)
|
|
||||||
lx.backup()
|
|
||||||
return lexBool
|
|
||||||
}
|
|
||||||
return lx.errorf("expected value but found %q instead", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
|
|
||||||
// have already been consumed. All whitespace and newlines are ignored.
|
|
||||||
func lexArrayValue(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
return lexSkip(lx, lexArrayValue)
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexArrayValue)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
return lx.errorf("unexpected comma")
|
|
||||||
case r == arrayEnd:
|
|
||||||
// NOTE(caleb): The spec isn't clear about whether you can have
|
|
||||||
// a trailing comma or not, so we'll allow it.
|
|
||||||
return lexArrayEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexArrayValueEnd)
|
|
||||||
return lexValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayValueEnd consumes everything between the end of an array value and
|
|
||||||
// the next value (or the end of the array): it ignores whitespace and newlines
|
|
||||||
// and expects either a ',' or a ']'.
|
|
||||||
func lexArrayValueEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
return lexSkip(lx, lexArrayValueEnd)
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexArrayValueEnd)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
lx.ignore()
|
|
||||||
return lexArrayValue // move on to the next value
|
|
||||||
case r == arrayEnd:
|
|
||||||
return lexArrayEnd
|
|
||||||
}
|
|
||||||
return lx.errorf(
|
|
||||||
"expected a comma or array terminator %q, but got %q instead",
|
|
||||||
arrayEnd, r,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayEnd finishes the lexing of an array.
|
|
||||||
// It assumes that a ']' has just been consumed.
|
|
||||||
func lexArrayEnd(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemArrayEnd)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableValue consumes one key/value pair in an inline table.
|
|
||||||
// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
|
|
||||||
func lexInlineTableValue(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexInlineTableValue)
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("newlines not allowed within inline tables")
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexInlineTableValue)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
return lx.errorf("unexpected comma")
|
|
||||||
case r == inlineTableEnd:
|
|
||||||
return lexInlineTableEnd
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexInlineTableValueEnd)
|
|
||||||
return lexKeyStart
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableValueEnd consumes everything between the end of an inline table
|
|
||||||
// key/value pair and the next pair (or the end of the table):
|
|
||||||
// it ignores whitespace and expects either a ',' or a '}'.
|
|
||||||
func lexInlineTableValueEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexInlineTableValueEnd)
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("newlines not allowed within inline tables")
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexInlineTableValueEnd)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
lx.ignore()
|
|
||||||
return lexInlineTableValue
|
|
||||||
case r == inlineTableEnd:
|
|
||||||
return lexInlineTableEnd
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a comma or an inline table terminator %q, "+
|
|
||||||
"but got %q instead", inlineTableEnd, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableEnd finishes the lexing of an inline table.
|
|
||||||
// It assumes that a '}' has just been consumed.
|
|
||||||
func lexInlineTableEnd(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemInlineTableEnd)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexString consumes the inner contents of a string. It assumes that the
|
|
||||||
// beginning '"' has already been consumed and ignored.
|
|
||||||
func lexString(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("strings cannot contain newlines")
|
|
||||||
case r == '\\':
|
|
||||||
lx.push(lexString)
|
|
||||||
return lexStringEscape
|
|
||||||
case r == stringEnd:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemString)
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lexString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineString consumes the inner contents of a string. It assumes that
|
|
||||||
// the beginning '"""' has already been consumed and ignored.
|
|
||||||
func lexMultilineString(lx *lexer) stateFn {
|
|
||||||
switch lx.next() {
|
|
||||||
case eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case '\\':
|
|
||||||
return lexMultilineStringEscape
|
|
||||||
case stringEnd:
|
|
||||||
if lx.accept(stringEnd) {
|
|
||||||
if lx.accept(stringEnd) {
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemMultilineString)
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexRawString consumes a raw string. Nothing can be escaped in such a string.
|
|
||||||
// It assumes that the beginning "'" has already been consumed and ignored.
|
|
||||||
func lexRawString(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("strings cannot contain newlines")
|
|
||||||
case r == rawStringEnd:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemRawString)
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lexRawString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
|
|
||||||
// a string. It assumes that the beginning "'''" has already been consumed and
|
|
||||||
// ignored.
|
|
||||||
func lexMultilineRawString(lx *lexer) stateFn {
|
|
||||||
switch lx.next() {
|
|
||||||
case eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case rawStringEnd:
|
|
||||||
if lx.accept(rawStringEnd) {
|
|
||||||
if lx.accept(rawStringEnd) {
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemRawMultilineString)
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexMultilineRawString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineStringEscape consumes an escaped character. It assumes that the
|
|
||||||
// preceding '\\' has already been consumed.
|
|
||||||
func lexMultilineStringEscape(lx *lexer) stateFn {
|
|
||||||
// Handle the special case first:
|
|
||||||
if isNL(lx.next()) {
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexMultilineString)
|
|
||||||
return lexStringEscape(lx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexStringEscape(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch r {
|
|
||||||
case 'b':
|
|
||||||
fallthrough
|
|
||||||
case 't':
|
|
||||||
fallthrough
|
|
||||||
case 'n':
|
|
||||||
fallthrough
|
|
||||||
case 'f':
|
|
||||||
fallthrough
|
|
||||||
case 'r':
|
|
||||||
fallthrough
|
|
||||||
case '"':
|
|
||||||
fallthrough
|
|
||||||
case '\\':
|
|
||||||
return lx.pop()
|
|
||||||
case 'u':
|
|
||||||
return lexShortUnicodeEscape
|
|
||||||
case 'U':
|
|
||||||
return lexLongUnicodeEscape
|
|
||||||
}
|
|
||||||
return lx.errorf("invalid escape character %q; only the following "+
|
|
||||||
"escape characters are allowed: "+
|
|
||||||
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexShortUnicodeEscape(lx *lexer) stateFn {
|
|
||||||
var r rune
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
r = lx.next()
|
|
||||||
if !isHexadecimal(r) {
|
|
||||||
return lx.errorf(`expected four hexadecimal digits after '\u', `+
|
|
||||||
"but got %q instead", lx.current())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexLongUnicodeEscape(lx *lexer) stateFn {
|
|
||||||
var r rune
|
|
||||||
for i := 0; i < 8; i++ {
|
|
||||||
r = lx.next()
|
|
||||||
if !isHexadecimal(r) {
|
|
||||||
return lx.errorf(`expected eight hexadecimal digits after '\U', `+
|
|
||||||
"but got %q instead", lx.current())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberOrDateStart consumes either an integer, a float, or datetime.
|
|
||||||
func lexNumberOrDateStart(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumberOrDate
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
case '.':
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a digit but got %q", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberOrDate consumes either an integer, float or datetime.
|
|
||||||
func lexNumberOrDate(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumberOrDate
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '-':
|
|
||||||
return lexDatetime
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemInteger)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexDatetime consumes a Datetime, to a first approximation.
|
|
||||||
// The parser validates that it matches one of the accepted formats.
|
|
||||||
func lexDatetime(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexDatetime
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '-', 'T', ':', '.', 'Z', '+':
|
|
||||||
return lexDatetime
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemDatetime)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberStart consumes either an integer or a float. It assumes that a sign
|
|
||||||
// has already been read, but that *no* digits have been consumed.
|
|
||||||
// lexNumberStart will move to the appropriate integer or float states.
|
|
||||||
func lexNumberStart(lx *lexer) stateFn {
|
|
||||||
// We MUST see a digit. Even floats have to start with a digit.
|
|
||||||
r := lx.next()
|
|
||||||
if !isDigit(r) {
|
|
||||||
if r == '.' {
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a digit but got %q", r)
|
|
||||||
}
|
|
||||||
return lexNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumber consumes an integer or a float after seeing the first digit.
|
|
||||||
func lexNumber(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumber
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemInteger)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexFloat consumes the elements of a float. It allows any sequence of
|
|
||||||
// float-like characters, so floats emitted by the lexer are only a first
|
|
||||||
// approximation and must be validated by the parser.
|
|
||||||
func lexFloat(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_', '.', '-', '+', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemFloat)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBool consumes a bool string: 'true' or 'false.
|
|
||||||
func lexBool(lx *lexer) stateFn {
|
|
||||||
var rs []rune
|
|
||||||
for {
|
|
||||||
r := lx.next()
|
|
||||||
if !unicode.IsLetter(r) {
|
|
||||||
lx.backup()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rs = append(rs, r)
|
|
||||||
}
|
|
||||||
s := string(rs)
|
|
||||||
switch s {
|
|
||||||
case "true", "false":
|
|
||||||
lx.emit(itemBool)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lx.errorf("expected value but found %q instead", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexCommentStart begins the lexing of a comment. It will emit
|
|
||||||
// itemCommentStart and consume no characters, passing control to lexComment.
|
|
||||||
func lexCommentStart(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemCommentStart)
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
|
|
||||||
// It will consume *up to* the first newline character, and pass control
|
|
||||||
// back to the last state on the stack.
|
|
||||||
func lexComment(lx *lexer) stateFn {
|
|
||||||
r := lx.peek()
|
|
||||||
if isNL(r) || r == eof {
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.next()
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexSkip ignores all slurped input and moves on to the next state.
|
|
||||||
func lexSkip(lx *lexer, nextState stateFn) stateFn {
|
|
||||||
return func(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
return nextState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isWhitespace returns true if `r` is a whitespace character according
|
|
||||||
// to the spec.
|
|
||||||
func isWhitespace(r rune) bool {
|
|
||||||
return r == '\t' || r == ' '
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNL(r rune) bool {
|
|
||||||
return r == '\n' || r == '\r'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDigit(r rune) bool {
|
|
||||||
return r >= '0' && r <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHexadecimal(r rune) bool {
|
|
||||||
return (r >= '0' && r <= '9') ||
|
|
||||||
(r >= 'a' && r <= 'f') ||
|
|
||||||
(r >= 'A' && r <= 'F')
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBareKeyChar(r rune) bool {
|
|
||||||
return (r >= 'A' && r <= 'Z') ||
|
|
||||||
(r >= 'a' && r <= 'z') ||
|
|
||||||
(r >= '0' && r <= '9') ||
|
|
||||||
r == '_' ||
|
|
||||||
r == '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
func (itype itemType) String() string {
|
|
||||||
switch itype {
|
|
||||||
case itemError:
|
|
||||||
return "Error"
|
|
||||||
case itemNIL:
|
|
||||||
return "NIL"
|
|
||||||
case itemEOF:
|
|
||||||
return "EOF"
|
|
||||||
case itemText:
|
|
||||||
return "Text"
|
|
||||||
case itemString, itemRawString, itemMultilineString, itemRawMultilineString:
|
|
||||||
return "String"
|
|
||||||
case itemBool:
|
|
||||||
return "Bool"
|
|
||||||
case itemInteger:
|
|
||||||
return "Integer"
|
|
||||||
case itemFloat:
|
|
||||||
return "Float"
|
|
||||||
case itemDatetime:
|
|
||||||
return "DateTime"
|
|
||||||
case itemTableStart:
|
|
||||||
return "TableStart"
|
|
||||||
case itemTableEnd:
|
|
||||||
return "TableEnd"
|
|
||||||
case itemKeyStart:
|
|
||||||
return "KeyStart"
|
|
||||||
case itemArray:
|
|
||||||
return "Array"
|
|
||||||
case itemArrayEnd:
|
|
||||||
return "ArrayEnd"
|
|
||||||
case itemCommentStart:
|
|
||||||
return "CommentStart"
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (item item) String() string {
|
|
||||||
return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val)
|
|
||||||
}
|
|
||||||
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
@ -1,592 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type parser struct {
|
|
||||||
mapping map[string]interface{}
|
|
||||||
types map[string]tomlType
|
|
||||||
lx *lexer
|
|
||||||
|
|
||||||
// A list of keys in the order that they appear in the TOML data.
|
|
||||||
ordered []Key
|
|
||||||
|
|
||||||
// the full key for the current hash in scope
|
|
||||||
context Key
|
|
||||||
|
|
||||||
// the base key name for everything except hashes
|
|
||||||
currentKey string
|
|
||||||
|
|
||||||
// rough approximation of line number
|
|
||||||
approxLine int
|
|
||||||
|
|
||||||
// A map of 'key.group.names' to whether they were created implicitly.
|
|
||||||
implicits map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type parseError string
|
|
||||||
|
|
||||||
func (pe parseError) Error() string {
|
|
||||||
return string(pe)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(data string) (p *parser, err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
var ok bool
|
|
||||||
if err, ok = r.(parseError); ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
p = &parser{
|
|
||||||
mapping: make(map[string]interface{}),
|
|
||||||
types: make(map[string]tomlType),
|
|
||||||
lx: lex(data),
|
|
||||||
ordered: make([]Key, 0),
|
|
||||||
implicits: make(map[string]bool),
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
item := p.next()
|
|
||||||
if item.typ == itemEOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.topLevel(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) panicf(format string, v ...interface{}) {
|
|
||||||
msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
|
|
||||||
p.approxLine, p.current(), fmt.Sprintf(format, v...))
|
|
||||||
panic(parseError(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) next() item {
|
|
||||||
it := p.lx.nextItem()
|
|
||||||
if it.typ == itemError {
|
|
||||||
p.panicf("%s", it.val)
|
|
||||||
}
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) bug(format string, v ...interface{}) {
|
|
||||||
panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) expect(typ itemType) item {
|
|
||||||
it := p.next()
|
|
||||||
p.assertEqual(typ, it.typ)
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) assertEqual(expected, got itemType) {
|
|
||||||
if expected != got {
|
|
||||||
p.bug("Expected '%s' but got '%s'.", expected, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) topLevel(item item) {
|
|
||||||
switch item.typ {
|
|
||||||
case itemCommentStart:
|
|
||||||
p.approxLine = item.line
|
|
||||||
p.expect(itemText)
|
|
||||||
case itemTableStart:
|
|
||||||
kg := p.next()
|
|
||||||
p.approxLine = kg.line
|
|
||||||
|
|
||||||
var key Key
|
|
||||||
for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
|
|
||||||
key = append(key, p.keyString(kg))
|
|
||||||
}
|
|
||||||
p.assertEqual(itemTableEnd, kg.typ)
|
|
||||||
|
|
||||||
p.establishContext(key, false)
|
|
||||||
p.setType("", tomlHash)
|
|
||||||
p.ordered = append(p.ordered, key)
|
|
||||||
case itemArrayTableStart:
|
|
||||||
kg := p.next()
|
|
||||||
p.approxLine = kg.line
|
|
||||||
|
|
||||||
var key Key
|
|
||||||
for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
|
|
||||||
key = append(key, p.keyString(kg))
|
|
||||||
}
|
|
||||||
p.assertEqual(itemArrayTableEnd, kg.typ)
|
|
||||||
|
|
||||||
p.establishContext(key, true)
|
|
||||||
p.setType("", tomlArrayHash)
|
|
||||||
p.ordered = append(p.ordered, key)
|
|
||||||
case itemKeyStart:
|
|
||||||
kname := p.next()
|
|
||||||
p.approxLine = kname.line
|
|
||||||
p.currentKey = p.keyString(kname)
|
|
||||||
|
|
||||||
val, typ := p.value(p.next())
|
|
||||||
p.setValue(p.currentKey, val)
|
|
||||||
p.setType(p.currentKey, typ)
|
|
||||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
|
||||||
p.currentKey = ""
|
|
||||||
default:
|
|
||||||
p.bug("Unexpected type at top level: %s", item.typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets a string for a key (or part of a key in a table name).
|
|
||||||
func (p *parser) keyString(it item) string {
|
|
||||||
switch it.typ {
|
|
||||||
case itemText:
|
|
||||||
return it.val
|
|
||||||
case itemString, itemMultilineString,
|
|
||||||
itemRawString, itemRawMultilineString:
|
|
||||||
s, _ := p.value(it)
|
|
||||||
return s.(string)
|
|
||||||
default:
|
|
||||||
p.bug("Unexpected key type: %s", it.typ)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// value translates an expected value from the lexer into a Go value wrapped
|
|
||||||
// as an empty interface.
|
|
||||||
func (p *parser) value(it item) (interface{}, tomlType) {
|
|
||||||
switch it.typ {
|
|
||||||
case itemString:
|
|
||||||
return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
|
|
||||||
case itemMultilineString:
|
|
||||||
trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
|
|
||||||
return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
|
|
||||||
case itemRawString:
|
|
||||||
return it.val, p.typeOfPrimitive(it)
|
|
||||||
case itemRawMultilineString:
|
|
||||||
return stripFirstNewline(it.val), p.typeOfPrimitive(it)
|
|
||||||
case itemBool:
|
|
||||||
switch it.val {
|
|
||||||
case "true":
|
|
||||||
return true, p.typeOfPrimitive(it)
|
|
||||||
case "false":
|
|
||||||
return false, p.typeOfPrimitive(it)
|
|
||||||
}
|
|
||||||
p.bug("Expected boolean value, but got '%s'.", it.val)
|
|
||||||
case itemInteger:
|
|
||||||
if !numUnderscoresOK(it.val) {
|
|
||||||
p.panicf("Invalid integer %q: underscores must be surrounded by digits",
|
|
||||||
it.val)
|
|
||||||
}
|
|
||||||
val := strings.Replace(it.val, "_", "", -1)
|
|
||||||
num, err := strconv.ParseInt(val, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
// Distinguish integer values. Normally, it'd be a bug if the lexer
|
|
||||||
// provides an invalid integer, but it's possible that the number is
|
|
||||||
// out of range of valid values (which the lexer cannot determine).
|
|
||||||
// So mark the former as a bug but the latter as a legitimate user
|
|
||||||
// error.
|
|
||||||
if e, ok := err.(*strconv.NumError); ok &&
|
|
||||||
e.Err == strconv.ErrRange {
|
|
||||||
|
|
||||||
p.panicf("Integer '%s' is out of the range of 64-bit "+
|
|
||||||
"signed integers.", it.val)
|
|
||||||
} else {
|
|
||||||
p.bug("Expected integer value, but got '%s'.", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num, p.typeOfPrimitive(it)
|
|
||||||
case itemFloat:
|
|
||||||
parts := strings.FieldsFunc(it.val, func(r rune) bool {
|
|
||||||
switch r {
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
for _, part := range parts {
|
|
||||||
if !numUnderscoresOK(part) {
|
|
||||||
p.panicf("Invalid float %q: underscores must be "+
|
|
||||||
"surrounded by digits", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !numPeriodsOK(it.val) {
|
|
||||||
// As a special case, numbers like '123.' or '1.e2',
|
|
||||||
// which are valid as far as Go/strconv are concerned,
|
|
||||||
// must be rejected because TOML says that a fractional
|
|
||||||
// part consists of '.' followed by 1+ digits.
|
|
||||||
p.panicf("Invalid float %q: '.' must be followed "+
|
|
||||||
"by one or more digits", it.val)
|
|
||||||
}
|
|
||||||
val := strings.Replace(it.val, "_", "", -1)
|
|
||||||
num, err := strconv.ParseFloat(val, 64)
|
|
||||||
if err != nil {
|
|
||||||
if e, ok := err.(*strconv.NumError); ok &&
|
|
||||||
e.Err == strconv.ErrRange {
|
|
||||||
|
|
||||||
p.panicf("Float '%s' is out of the range of 64-bit "+
|
|
||||||
"IEEE-754 floating-point numbers.", it.val)
|
|
||||||
} else {
|
|
||||||
p.panicf("Invalid float value: %q", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num, p.typeOfPrimitive(it)
|
|
||||||
case itemDatetime:
|
|
||||||
var t time.Time
|
|
||||||
var ok bool
|
|
||||||
var err error
|
|
||||||
for _, format := range []string{
|
|
||||||
"2006-01-02T15:04:05Z07:00",
|
|
||||||
"2006-01-02T15:04:05",
|
|
||||||
"2006-01-02",
|
|
||||||
} {
|
|
||||||
t, err = time.ParseInLocation(format, it.val, time.Local)
|
|
||||||
if err == nil {
|
|
||||||
ok = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
p.panicf("Invalid TOML Datetime: %q.", it.val)
|
|
||||||
}
|
|
||||||
return t, p.typeOfPrimitive(it)
|
|
||||||
case itemArray:
|
|
||||||
array := make([]interface{}, 0)
|
|
||||||
types := make([]tomlType, 0)
|
|
||||||
|
|
||||||
for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
|
|
||||||
if it.typ == itemCommentStart {
|
|
||||||
p.expect(itemText)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val, typ := p.value(it)
|
|
||||||
array = append(array, val)
|
|
||||||
types = append(types, typ)
|
|
||||||
}
|
|
||||||
return array, p.typeOfArray(types)
|
|
||||||
case itemInlineTableStart:
|
|
||||||
var (
|
|
||||||
hash = make(map[string]interface{})
|
|
||||||
outerContext = p.context
|
|
||||||
outerKey = p.currentKey
|
|
||||||
)
|
|
||||||
|
|
||||||
p.context = append(p.context, p.currentKey)
|
|
||||||
p.currentKey = ""
|
|
||||||
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
|
|
||||||
if it.typ != itemKeyStart {
|
|
||||||
p.bug("Expected key start but instead found %q, around line %d",
|
|
||||||
it.val, p.approxLine)
|
|
||||||
}
|
|
||||||
if it.typ == itemCommentStart {
|
|
||||||
p.expect(itemText)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve key
|
|
||||||
k := p.next()
|
|
||||||
p.approxLine = k.line
|
|
||||||
kname := p.keyString(k)
|
|
||||||
|
|
||||||
// retrieve value
|
|
||||||
p.currentKey = kname
|
|
||||||
val, typ := p.value(p.next())
|
|
||||||
// make sure we keep metadata up to date
|
|
||||||
p.setType(kname, typ)
|
|
||||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
|
||||||
hash[kname] = val
|
|
||||||
}
|
|
||||||
p.context = outerContext
|
|
||||||
p.currentKey = outerKey
|
|
||||||
return hash, tomlHash
|
|
||||||
}
|
|
||||||
p.bug("Unexpected value type: %s", it.typ)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// numUnderscoresOK checks whether each underscore in s is surrounded by
|
|
||||||
// characters that are not underscores.
|
|
||||||
func numUnderscoresOK(s string) bool {
|
|
||||||
accept := false
|
|
||||||
for _, r := range s {
|
|
||||||
if r == '_' {
|
|
||||||
if !accept {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
accept = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
accept = true
|
|
||||||
}
|
|
||||||
return accept
|
|
||||||
}
|
|
||||||
|
|
||||||
// numPeriodsOK checks whether every period in s is followed by a digit.
|
|
||||||
func numPeriodsOK(s string) bool {
|
|
||||||
period := false
|
|
||||||
for _, r := range s {
|
|
||||||
if period && !isDigit(r) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
period = r == '.'
|
|
||||||
}
|
|
||||||
return !period
|
|
||||||
}
|
|
||||||
|
|
||||||
// establishContext sets the current context of the parser,
|
|
||||||
// where the context is either a hash or an array of hashes. Which one is
|
|
||||||
// set depends on the value of the `array` parameter.
|
|
||||||
//
|
|
||||||
// Establishing the context also makes sure that the key isn't a duplicate, and
|
|
||||||
// will create implicit hashes automatically.
|
|
||||||
func (p *parser) establishContext(key Key, array bool) {
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
// Always start at the top level and drill down for our context.
|
|
||||||
hashContext := p.mapping
|
|
||||||
keyContext := make(Key, 0)
|
|
||||||
|
|
||||||
// We only need implicit hashes for key[0:-1]
|
|
||||||
for _, k := range key[0 : len(key)-1] {
|
|
||||||
_, ok = hashContext[k]
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
|
|
||||||
// No key? Make an implicit hash and move on.
|
|
||||||
if !ok {
|
|
||||||
p.addImplicit(keyContext)
|
|
||||||
hashContext[k] = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the hash context is actually an array of tables, then set
|
|
||||||
// the hash context to the last element in that array.
|
|
||||||
//
|
|
||||||
// Otherwise, it better be a table, since this MUST be a key group (by
|
|
||||||
// virtue of it not being the last element in a key).
|
|
||||||
switch t := hashContext[k].(type) {
|
|
||||||
case []map[string]interface{}:
|
|
||||||
hashContext = t[len(t)-1]
|
|
||||||
case map[string]interface{}:
|
|
||||||
hashContext = t
|
|
||||||
default:
|
|
||||||
p.panicf("Key '%s' was already created as a hash.", keyContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.context = keyContext
|
|
||||||
if array {
|
|
||||||
// If this is the first element for this array, then allocate a new
|
|
||||||
// list of tables for it.
|
|
||||||
k := key[len(key)-1]
|
|
||||||
if _, ok := hashContext[k]; !ok {
|
|
||||||
hashContext[k] = make([]map[string]interface{}, 0, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a new table. But make sure the key hasn't already been used
|
|
||||||
// for something else.
|
|
||||||
if hash, ok := hashContext[k].([]map[string]interface{}); ok {
|
|
||||||
hashContext[k] = append(hash, make(map[string]interface{}))
|
|
||||||
} else {
|
|
||||||
p.panicf("Key '%s' was already created and cannot be used as "+
|
|
||||||
"an array.", keyContext)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.setValue(key[len(key)-1], make(map[string]interface{}))
|
|
||||||
}
|
|
||||||
p.context = append(p.context, key[len(key)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// setValue sets the given key to the given value in the current context.
|
|
||||||
// It will make sure that the key hasn't already been defined, account for
|
|
||||||
// implicit key groups.
|
|
||||||
func (p *parser) setValue(key string, value interface{}) {
|
|
||||||
var tmpHash interface{}
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
hash := p.mapping
|
|
||||||
keyContext := make(Key, 0)
|
|
||||||
for _, k := range p.context {
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
if tmpHash, ok = hash[k]; !ok {
|
|
||||||
p.bug("Context for key '%s' has not been established.", keyContext)
|
|
||||||
}
|
|
||||||
switch t := tmpHash.(type) {
|
|
||||||
case []map[string]interface{}:
|
|
||||||
// The context is a table of hashes. Pick the most recent table
|
|
||||||
// defined as the current hash.
|
|
||||||
hash = t[len(t)-1]
|
|
||||||
case map[string]interface{}:
|
|
||||||
hash = t
|
|
||||||
default:
|
|
||||||
p.bug("Expected hash to have type 'map[string]interface{}', but "+
|
|
||||||
"it has '%T' instead.", tmpHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyContext = append(keyContext, key)
|
|
||||||
|
|
||||||
if _, ok := hash[key]; ok {
|
|
||||||
// Typically, if the given key has already been set, then we have
|
|
||||||
// to raise an error since duplicate keys are disallowed. However,
|
|
||||||
// it's possible that a key was previously defined implicitly. In this
|
|
||||||
// case, it is allowed to be redefined concretely. (See the
|
|
||||||
// `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
|
|
||||||
//
|
|
||||||
// But we have to make sure to stop marking it as an implicit. (So that
|
|
||||||
// another redefinition provokes an error.)
|
|
||||||
//
|
|
||||||
// Note that since it has already been defined (as a hash), we don't
|
|
||||||
// want to overwrite it. So our business is done.
|
|
||||||
if p.isImplicit(keyContext) {
|
|
||||||
p.removeImplicit(keyContext)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we have a concrete key trying to override a previous
|
|
||||||
// key, which is *always* wrong.
|
|
||||||
p.panicf("Key '%s' has already been defined.", keyContext)
|
|
||||||
}
|
|
||||||
hash[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// setType sets the type of a particular value at a given key.
|
|
||||||
// It should be called immediately AFTER setValue.
|
|
||||||
//
|
|
||||||
// Note that if `key` is empty, then the type given will be applied to the
|
|
||||||
// current context (which is either a table or an array of tables).
|
|
||||||
func (p *parser) setType(key string, typ tomlType) {
|
|
||||||
keyContext := make(Key, 0, len(p.context)+1)
|
|
||||||
for _, k := range p.context {
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
}
|
|
||||||
if len(key) > 0 { // allow type setting for hashes
|
|
||||||
keyContext = append(keyContext, key)
|
|
||||||
}
|
|
||||||
p.types[keyContext.String()] = typ
|
|
||||||
}
|
|
||||||
|
|
||||||
// addImplicit sets the given Key as having been created implicitly.
|
|
||||||
func (p *parser) addImplicit(key Key) {
|
|
||||||
p.implicits[key.String()] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeImplicit stops tagging the given key as having been implicitly
|
|
||||||
// created.
|
|
||||||
func (p *parser) removeImplicit(key Key) {
|
|
||||||
p.implicits[key.String()] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// isImplicit returns true if the key group pointed to by the key was created
|
|
||||||
// implicitly.
|
|
||||||
func (p *parser) isImplicit(key Key) bool {
|
|
||||||
return p.implicits[key.String()]
|
|
||||||
}
|
|
||||||
|
|
||||||
// current returns the full key name of the current context.
|
|
||||||
func (p *parser) current() string {
|
|
||||||
if len(p.currentKey) == 0 {
|
|
||||||
return p.context.String()
|
|
||||||
}
|
|
||||||
if len(p.context) == 0 {
|
|
||||||
return p.currentKey
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s.%s", p.context, p.currentKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripFirstNewline(s string) string {
|
|
||||||
if len(s) == 0 || s[0] != '\n' {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripEscapedWhitespace(s string) string {
|
|
||||||
esc := strings.Split(s, "\\\n")
|
|
||||||
if len(esc) > 1 {
|
|
||||||
for i := 1; i < len(esc); i++ {
|
|
||||||
esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(esc, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) replaceEscapes(str string) string {
|
|
||||||
var replaced []rune
|
|
||||||
s := []byte(str)
|
|
||||||
r := 0
|
|
||||||
for r < len(s) {
|
|
||||||
if s[r] != '\\' {
|
|
||||||
c, size := utf8.DecodeRune(s[r:])
|
|
||||||
r += size
|
|
||||||
replaced = append(replaced, c)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r += 1
|
|
||||||
if r >= len(s) {
|
|
||||||
p.bug("Escape sequence at end of string.")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
switch s[r] {
|
|
||||||
default:
|
|
||||||
p.bug("Expected valid escape code after \\, but got %q.", s[r])
|
|
||||||
return ""
|
|
||||||
case 'b':
|
|
||||||
replaced = append(replaced, rune(0x0008))
|
|
||||||
r += 1
|
|
||||||
case 't':
|
|
||||||
replaced = append(replaced, rune(0x0009))
|
|
||||||
r += 1
|
|
||||||
case 'n':
|
|
||||||
replaced = append(replaced, rune(0x000A))
|
|
||||||
r += 1
|
|
||||||
case 'f':
|
|
||||||
replaced = append(replaced, rune(0x000C))
|
|
||||||
r += 1
|
|
||||||
case 'r':
|
|
||||||
replaced = append(replaced, rune(0x000D))
|
|
||||||
r += 1
|
|
||||||
case '"':
|
|
||||||
replaced = append(replaced, rune(0x0022))
|
|
||||||
r += 1
|
|
||||||
case '\\':
|
|
||||||
replaced = append(replaced, rune(0x005C))
|
|
||||||
r += 1
|
|
||||||
case 'u':
|
|
||||||
// At this point, we know we have a Unicode escape of the form
|
|
||||||
// `uXXXX` at [r, r+5). (Because the lexer guarantees this
|
|
||||||
// for us.)
|
|
||||||
escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
|
|
||||||
replaced = append(replaced, escaped)
|
|
||||||
r += 5
|
|
||||||
case 'U':
|
|
||||||
// At this point, we know we have a Unicode escape of the form
|
|
||||||
// `uXXXX` at [r, r+9). (Because the lexer guarantees this
|
|
||||||
// for us.)
|
|
||||||
escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
|
|
||||||
replaced = append(replaced, escaped)
|
|
||||||
r += 9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(replaced)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
|
|
||||||
s := string(bs)
|
|
||||||
hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
p.bug("Could not parse '%s' as a hexadecimal number, but the "+
|
|
||||||
"lexer claims it's OK: %s", s, err)
|
|
||||||
}
|
|
||||||
if !utf8.ValidRune(rune(hex)) {
|
|
||||||
p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
|
|
||||||
}
|
|
||||||
return rune(hex)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isStringType(ty itemType) bool {
|
|
||||||
return ty == itemString || ty == itemMultilineString ||
|
|
||||||
ty == itemRawString || ty == itemRawMultilineString
|
|
||||||
}
|
|
||||||
1
vendor/github.com/BurntSushi/toml/session.vim
generated
vendored
1
vendor/github.com/BurntSushi/toml/session.vim
generated
vendored
@ -1 +0,0 @@
|
|||||||
au BufWritePost *.go silent!make tags > /dev/null 2>&1
|
|
||||||
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
@ -1,91 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
// tomlType represents any Go type that corresponds to a TOML type.
|
|
||||||
// While the first draft of the TOML spec has a simplistic type system that
|
|
||||||
// probably doesn't need this level of sophistication, we seem to be militating
|
|
||||||
// toward adding real composite types.
|
|
||||||
type tomlType interface {
|
|
||||||
typeString() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeEqual accepts any two types and returns true if they are equal.
|
|
||||||
func typeEqual(t1, t2 tomlType) bool {
|
|
||||||
if t1 == nil || t2 == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return t1.typeString() == t2.typeString()
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeIsHash(t tomlType) bool {
|
|
||||||
return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tomlBaseType string
|
|
||||||
|
|
||||||
func (btype tomlBaseType) typeString() string {
|
|
||||||
return string(btype)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (btype tomlBaseType) String() string {
|
|
||||||
return btype.typeString()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tomlInteger tomlBaseType = "Integer"
|
|
||||||
tomlFloat tomlBaseType = "Float"
|
|
||||||
tomlDatetime tomlBaseType = "Datetime"
|
|
||||||
tomlString tomlBaseType = "String"
|
|
||||||
tomlBool tomlBaseType = "Bool"
|
|
||||||
tomlArray tomlBaseType = "Array"
|
|
||||||
tomlHash tomlBaseType = "Hash"
|
|
||||||
tomlArrayHash tomlBaseType = "ArrayHash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// typeOfPrimitive returns a tomlType of any primitive value in TOML.
|
|
||||||
// Primitive values are: Integer, Float, Datetime, String and Bool.
|
|
||||||
//
|
|
||||||
// Passing a lexer item other than the following will cause a BUG message
|
|
||||||
// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
|
|
||||||
func (p *parser) typeOfPrimitive(lexItem item) tomlType {
|
|
||||||
switch lexItem.typ {
|
|
||||||
case itemInteger:
|
|
||||||
return tomlInteger
|
|
||||||
case itemFloat:
|
|
||||||
return tomlFloat
|
|
||||||
case itemDatetime:
|
|
||||||
return tomlDatetime
|
|
||||||
case itemString:
|
|
||||||
return tomlString
|
|
||||||
case itemMultilineString:
|
|
||||||
return tomlString
|
|
||||||
case itemRawString:
|
|
||||||
return tomlString
|
|
||||||
case itemRawMultilineString:
|
|
||||||
return tomlString
|
|
||||||
case itemBool:
|
|
||||||
return tomlBool
|
|
||||||
}
|
|
||||||
p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeOfArray returns a tomlType for an array given a list of types of its
|
|
||||||
// values.
|
|
||||||
//
|
|
||||||
// In the current spec, if an array is homogeneous, then its type is always
|
|
||||||
// "Array". If the array is not homogeneous, an error is generated.
|
|
||||||
func (p *parser) typeOfArray(types []tomlType) tomlType {
|
|
||||||
// Empty arrays are cool.
|
|
||||||
if len(types) == 0 {
|
|
||||||
return tomlArray
|
|
||||||
}
|
|
||||||
|
|
||||||
theType := types[0]
|
|
||||||
for _, t := range types[1:] {
|
|
||||||
if !typeEqual(theType, t) {
|
|
||||||
p.panicf("Array contains values of type '%s' and '%s', but "+
|
|
||||||
"arrays must be homogeneous.", theType, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tomlArray
|
|
||||||
}
|
|
||||||
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
@ -1,242 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
// Struct field handling is adapted from code in encoding/json:
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the Go distribution.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A field represents a single field found in a struct.
|
|
||||||
type field struct {
|
|
||||||
name string // the name of the field (`toml` tag included)
|
|
||||||
tag bool // whether field has a `toml` tag
|
|
||||||
index []int // represents the depth of an anonymous field
|
|
||||||
typ reflect.Type // the type of the field
|
|
||||||
}
|
|
||||||
|
|
||||||
// byName sorts field by name, breaking ties with depth,
|
|
||||||
// then breaking ties with "name came from toml tag", then
|
|
||||||
// breaking ties with index sequence.
|
|
||||||
type byName []field
|
|
||||||
|
|
||||||
func (x byName) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byName) Less(i, j int) bool {
|
|
||||||
if x[i].name != x[j].name {
|
|
||||||
return x[i].name < x[j].name
|
|
||||||
}
|
|
||||||
if len(x[i].index) != len(x[j].index) {
|
|
||||||
return len(x[i].index) < len(x[j].index)
|
|
||||||
}
|
|
||||||
if x[i].tag != x[j].tag {
|
|
||||||
return x[i].tag
|
|
||||||
}
|
|
||||||
return byIndex(x).Less(i, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
// byIndex sorts field by index sequence.
|
|
||||||
type byIndex []field
|
|
||||||
|
|
||||||
func (x byIndex) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byIndex) Less(i, j int) bool {
|
|
||||||
for k, xik := range x[i].index {
|
|
||||||
if k >= len(x[j].index) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if xik != x[j].index[k] {
|
|
||||||
return xik < x[j].index[k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(x[i].index) < len(x[j].index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeFields returns a list of fields that TOML should recognize for the given
|
|
||||||
// type. The algorithm is breadth-first search over the set of structs to
|
|
||||||
// include - the top struct and then any reachable anonymous structs.
|
|
||||||
func typeFields(t reflect.Type) []field {
|
|
||||||
// Anonymous fields to explore at the current level and the next.
|
|
||||||
current := []field{}
|
|
||||||
next := []field{{typ: t}}
|
|
||||||
|
|
||||||
// Count of queued names for current level and the next.
|
|
||||||
count := map[reflect.Type]int{}
|
|
||||||
nextCount := map[reflect.Type]int{}
|
|
||||||
|
|
||||||
// Types already visited at an earlier level.
|
|
||||||
visited := map[reflect.Type]bool{}
|
|
||||||
|
|
||||||
// Fields found.
|
|
||||||
var fields []field
|
|
||||||
|
|
||||||
for len(next) > 0 {
|
|
||||||
current, next = next, current[:0]
|
|
||||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
|
||||||
|
|
||||||
for _, f := range current {
|
|
||||||
if visited[f.typ] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
visited[f.typ] = true
|
|
||||||
|
|
||||||
// Scan f.typ for fields to include.
|
|
||||||
for i := 0; i < f.typ.NumField(); i++ {
|
|
||||||
sf := f.typ.Field(i)
|
|
||||||
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
opts := getOptions(sf.Tag)
|
|
||||||
if opts.skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
index := make([]int, len(f.index)+1)
|
|
||||||
copy(index, f.index)
|
|
||||||
index[len(f.index)] = i
|
|
||||||
|
|
||||||
ft := sf.Type
|
|
||||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
|
||||||
// Follow pointer.
|
|
||||||
ft = ft.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record found field and index sequence.
|
|
||||||
if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
|
||||||
tagged := opts.name != ""
|
|
||||||
name := opts.name
|
|
||||||
if name == "" {
|
|
||||||
name = sf.Name
|
|
||||||
}
|
|
||||||
fields = append(fields, field{name, tagged, index, ft})
|
|
||||||
if count[f.typ] > 1 {
|
|
||||||
// If there were multiple instances, add a second,
|
|
||||||
// so that the annihilation code will see a duplicate.
|
|
||||||
// It only cares about the distinction between 1 or 2,
|
|
||||||
// so don't bother generating any more copies.
|
|
||||||
fields = append(fields, fields[len(fields)-1])
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record new anonymous struct to explore in next round.
|
|
||||||
nextCount[ft]++
|
|
||||||
if nextCount[ft] == 1 {
|
|
||||||
f := field{name: ft.Name(), index: index, typ: ft}
|
|
||||||
next = append(next, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(byName(fields))
|
|
||||||
|
|
||||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
|
||||||
// except that fields with TOML tags are promoted.
|
|
||||||
|
|
||||||
// The fields are sorted in primary order of name, secondary order
|
|
||||||
// of field index length. Loop over names; for each name, delete
|
|
||||||
// hidden fields by choosing the one dominant field that survives.
|
|
||||||
out := fields[:0]
|
|
||||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
|
||||||
// One iteration per name.
|
|
||||||
// Find the sequence of fields with the name of this first field.
|
|
||||||
fi := fields[i]
|
|
||||||
name := fi.name
|
|
||||||
for advance = 1; i+advance < len(fields); advance++ {
|
|
||||||
fj := fields[i+advance]
|
|
||||||
if fj.name != name {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if advance == 1 { // Only one field with this name
|
|
||||||
out = append(out, fi)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dominant, ok := dominantField(fields[i : i+advance])
|
|
||||||
if ok {
|
|
||||||
out = append(out, dominant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = out
|
|
||||||
sort.Sort(byIndex(fields))
|
|
||||||
|
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
// dominantField looks through the fields, all of which are known to
|
|
||||||
// have the same name, to find the single field that dominates the
|
|
||||||
// others using Go's embedding rules, modified by the presence of
|
|
||||||
// TOML tags. If there are multiple top-level fields, the boolean
|
|
||||||
// will be false: This condition is an error in Go and we skip all
|
|
||||||
// the fields.
|
|
||||||
func dominantField(fields []field) (field, bool) {
|
|
||||||
// The fields are sorted in increasing index-length order. The winner
|
|
||||||
// must therefore be one with the shortest index length. Drop all
|
|
||||||
// longer entries, which is easy: just truncate the slice.
|
|
||||||
length := len(fields[0].index)
|
|
||||||
tagged := -1 // Index of first tagged field.
|
|
||||||
for i, f := range fields {
|
|
||||||
if len(f.index) > length {
|
|
||||||
fields = fields[:i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if f.tag {
|
|
||||||
if tagged >= 0 {
|
|
||||||
// Multiple tagged fields at the same level: conflict.
|
|
||||||
// Return no field.
|
|
||||||
return field{}, false
|
|
||||||
}
|
|
||||||
tagged = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tagged >= 0 {
|
|
||||||
return fields[tagged], true
|
|
||||||
}
|
|
||||||
// All remaining fields have the same length. If there's more than one,
|
|
||||||
// we have a conflict (two fields named "X" at the same level) and we
|
|
||||||
// return no field.
|
|
||||||
if len(fields) > 1 {
|
|
||||||
return field{}, false
|
|
||||||
}
|
|
||||||
return fields[0], true
|
|
||||||
}
|
|
||||||
|
|
||||||
var fieldCache struct {
|
|
||||||
sync.RWMutex
|
|
||||||
m map[reflect.Type][]field
|
|
||||||
}
|
|
||||||
|
|
||||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
|
||||||
func cachedTypeFields(t reflect.Type) []field {
|
|
||||||
fieldCache.RLock()
|
|
||||||
f := fieldCache.m[t]
|
|
||||||
fieldCache.RUnlock()
|
|
||||||
if f != nil {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute fields without lock.
|
|
||||||
// Might duplicate effort but won't hold other computations back.
|
|
||||||
f = typeFields(t)
|
|
||||||
if f == nil {
|
|
||||||
f = []field{}
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldCache.Lock()
|
|
||||||
if fieldCache.m == nil {
|
|
||||||
fieldCache.m = map[reflect.Type][]field{}
|
|
||||||
}
|
|
||||||
fieldCache.m[t] = f
|
|
||||||
fieldCache.Unlock()
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
25
vendor/github.com/ajg/form/.travis.yml
generated
vendored
25
vendor/github.com/ajg/form/.travis.yml
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
## Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
## Use of this source code is governed by a BSD-style
|
|
||||||
## license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- tip
|
|
||||||
- 1.6
|
|
||||||
- 1.5
|
|
||||||
- 1.4
|
|
||||||
- 1.3
|
|
||||||
- 1.2
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
# - go get -v golang.org/x/tools/cmd/cover
|
|
||||||
# - go get -v golang.org/x/tools/cmd/vet
|
|
||||||
# - go get -v github.com/golang/lint/golint
|
|
||||||
- export PATH=$PATH:/home/travis/gopath/bin
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go build -v ./...
|
|
||||||
- go test -v -cover ./...
|
|
||||||
- go vet ./...
|
|
||||||
# - golint .
|
|
||||||
27
vendor/github.com/ajg/form/LICENSE
generated
vendored
27
vendor/github.com/ajg/form/LICENSE
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
246
vendor/github.com/ajg/form/README.md
generated
vendored
246
vendor/github.com/ajg/form/README.md
generated
vendored
@ -1,246 +0,0 @@
|
|||||||
form
|
|
||||||
====
|
|
||||||
|
|
||||||
A Form Encoding & Decoding Package for Go, written by [Alvaro J. Genial](http://alva.ro).
|
|
||||||
|
|
||||||
[](https://travis-ci.org/ajg/form)
|
|
||||||
[](https://godoc.org/github.com/ajg/form)
|
|
||||||
|
|
||||||
Synopsis
|
|
||||||
--------
|
|
||||||
|
|
||||||
This library is designed to allow seamless, high-fidelity encoding and decoding of arbitrary data in `application/x-www-form-urlencoded` format and as [`url.Values`](http://golang.org/pkg/net/url/#Values). It is intended to be useful primarily in dealing with web forms and URI query strings, both of which natively employ said format.
|
|
||||||
|
|
||||||
Unsurprisingly, `form` is modeled after other Go [`encoding`](http://golang.org/pkg/encoding/) packages, in particular [`encoding/json`](http://golang.org/pkg/encoding/json/), and follows the same conventions (see below for more.) It aims to automatically handle any kind of concrete Go [data value](#values) (i.e., not functions, channels, etc.) while providing mechanisms for custom behavior.
|
|
||||||
|
|
||||||
Status
|
|
||||||
------
|
|
||||||
|
|
||||||
The implementation is in usable shape and is fairly well tested with its accompanying test suite. The API is unlikely to change much, but still may. Lastly, the code has not yet undergone a security review to ensure it is free of vulnerabilities. Please file an issue or send a pull request for fixes & improvements.
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
------------
|
|
||||||
|
|
||||||
The only requirement is [Go 1.2](http://golang.org/doc/go1.2) or later.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/ajg/form"
|
|
||||||
```
|
|
||||||
|
|
||||||
Given a type like the following...
|
|
||||||
|
|
||||||
```go
|
|
||||||
type User struct {
|
|
||||||
Name string `form:"name"`
|
|
||||||
Email string `form:"email"`
|
|
||||||
Joined time.Time `form:"joined,omitempty"`
|
|
||||||
Posts []int `form:"posts"`
|
|
||||||
Preferences map[string]string `form:"prefs"`
|
|
||||||
Avatar []byte `form:"avatar"`
|
|
||||||
PasswordHash int64 `form:"-"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
...it is easy to encode data of that type...
|
|
||||||
|
|
||||||
|
|
||||||
```go
|
|
||||||
func PostUser(url string, u User) error {
|
|
||||||
var c http.Client
|
|
||||||
_, err := c.PostForm(url, form.EncodeToValues(u))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
...as well as decode it...
|
|
||||||
|
|
||||||
|
|
||||||
```go
|
|
||||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var u User
|
|
||||||
|
|
||||||
d := form.NewDecoder(r.Body)
|
|
||||||
if err := d.Decode(&u); err != nil {
|
|
||||||
http.Error(w, "Form could not be decoded", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "Decoded: %#v", u)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
...without having to do any grunt work.
|
|
||||||
|
|
||||||
Field Tags
|
|
||||||
----------
|
|
||||||
|
|
||||||
Like other encoding packages, `form` supports the following options for fields:
|
|
||||||
|
|
||||||
- `` `form:"-"` ``: Causes the field to be ignored during encoding and decoding.
|
|
||||||
- `` `form:"<name>"` ``: Overrides the field's name; useful especially when dealing with external identifiers in camelCase, as are commonly found on the web.
|
|
||||||
- `` `form:",omitempty"` ``: Elides the field during encoding if it is empty (typically meaning equal to the type's zero value.)
|
|
||||||
- `` `form:"<name>,omitempty"` ``: The way to combine the two options above.
|
|
||||||
|
|
||||||
Values
|
|
||||||
------
|
|
||||||
|
|
||||||
### Simple Values
|
|
||||||
|
|
||||||
Values of the following types are all considered simple:
|
|
||||||
|
|
||||||
- `bool`
|
|
||||||
- `int`, `int8`, `int16`, `int32`, `int64`, `rune`
|
|
||||||
- `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `byte`
|
|
||||||
- `float32`, `float64`
|
|
||||||
- `complex64`, `complex128`
|
|
||||||
- `string`
|
|
||||||
- `[]byte` (see note)
|
|
||||||
- [`time.Time`](http://golang.org/pkg/time/#Time)
|
|
||||||
- [`url.URL`](http://golang.org/pkg/net/url/#URL)
|
|
||||||
- An alias of any of the above
|
|
||||||
- A pointer to any of the above
|
|
||||||
|
|
||||||
### Composite Values
|
|
||||||
|
|
||||||
A composite value is one that can contain other values. Values of the following kinds...
|
|
||||||
|
|
||||||
- Maps
|
|
||||||
- Slices; except `[]byte` (see note)
|
|
||||||
- Structs; except [`time.Time`](http://golang.org/pkg/time/#Time) and [`url.URL`](http://golang.org/pkg/net/url/#URL)
|
|
||||||
- Arrays
|
|
||||||
- An alias of any of the above
|
|
||||||
- A pointer to any of the above
|
|
||||||
|
|
||||||
...are considered composites in general, unless they implement custom marshaling/unmarshaling. Composite values are encoded as a flat mapping of paths to values, where the paths are constructed by joining the parent and child paths with a period (`.`).
|
|
||||||
|
|
||||||
(Note: a byte slice is treated as a `string` by default because it's more efficient, but can also be decoded as a slice—i.e., with indexes.)
|
|
||||||
|
|
||||||
### Untyped Values
|
|
||||||
|
|
||||||
While encouraged, it is not necessary to define a type (e.g. a `struct`) in order to use `form`, since it is able to encode and decode untyped data generically using the following rules:
|
|
||||||
|
|
||||||
- Simple values will be treated as a `string`.
|
|
||||||
- Composite values will be treated as a `map[string]interface{}`, itself able to contain nested values (both scalar and compound) ad infinitum.
|
|
||||||
- However, if there is a value (of any supported type) already present in a map for a given key, then it will be used when possible, rather than being replaced with a generic value as specified above; this makes it possible to handle partially typed, dynamic or schema-less values.
|
|
||||||
|
|
||||||
### Zero Values
|
|
||||||
|
|
||||||
By default, and without custom marshaling, zero values (also known as empty/default values) are encoded as the empty string. To disable this behavior, meaning to keep zero values in their literal form (e.g. `0` for integral types), `Encoder` offers a `KeepZeros` setter method, which will do just that when set to `true`.
|
|
||||||
|
|
||||||
### Unsupported Values
|
|
||||||
|
|
||||||
Values of the following kinds aren't supported and, if present, must be ignored.
|
|
||||||
|
|
||||||
- Channel
|
|
||||||
- Function
|
|
||||||
- Unsafe pointer
|
|
||||||
- An alias of any of the above
|
|
||||||
- A pointer to any of the above
|
|
||||||
|
|
||||||
Custom Marshaling
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
There is a default (generally lossless) marshaling & unmarshaling scheme for any concrete data value in Go, which is good enough in most cases. However, it is possible to override it and use a custom scheme. For instance, a "binary" field could be marshaled more efficiently using [base64](http://golang.org/pkg/encoding/base64/) to prevent it from being percent-escaped during serialization to `application/x-www-form-urlencoded` format.
|
|
||||||
|
|
||||||
Because `form` provides support for [`encoding.TextMarshaler`](http://golang.org/pkg/encoding/#TextMarshaler) and [`encoding.TextUnmarshaler`](http://golang.org/pkg/encoding/#TextUnmarshaler) it is easy to do that; for instance, like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "encoding"
|
|
||||||
|
|
||||||
type Binary []byte
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ encoding.TextMarshaler = &Binary{}
|
|
||||||
_ encoding.TextUnmarshaler = &Binary{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (b Binary) MarshalText() ([]byte, error) {
|
|
||||||
return []byte(base64.URLEncoding.EncodeToString([]byte(b))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Binary) UnmarshalText(text []byte) error {
|
|
||||||
bs, err := base64.URLEncoding.DecodeString(string(text))
|
|
||||||
if err == nil {
|
|
||||||
*b = Binary(bs)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now any value with type `Binary` will automatically be encoded using the [URL](http://golang.org/pkg/encoding/base64/#URLEncoding) variant of base64. It is left as an exercise to the reader to improve upon this scheme by eliminating the need for padding (which, besides being superfluous, uses `=`, a character that will end up percent-escaped.)
|
|
||||||
|
|
||||||
Keys
|
|
||||||
----
|
|
||||||
|
|
||||||
In theory any value can be a key as long as it has a string representation. However, by default, periods have special meaning to `form`, and thus, under the hood (i.e. in encoded form) they are transparently escaped using a preceding backslash (`\`). Backslashes within keys, themselves, are also escaped in this manner (e.g. as `\\`) in order to permit representing `\.` itself (as `\\\.`).
|
|
||||||
|
|
||||||
(Note: it is normally unnecessary to deal with this issue unless keys are being constructed manually—e.g. literally embedded in HTML or in a URI.)
|
|
||||||
|
|
||||||
The default delimiter and escape characters used for encoding and decoding composite keys can be changed using the `DelimitWith` and `EscapeWith` setter methods of `Encoder` and `Decoder`, respectively. For example...
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ajg/form"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
type B struct {
|
|
||||||
Qux string `form:"qux"`
|
|
||||||
}
|
|
||||||
type A struct {
|
|
||||||
FooBar B `form:"foo.bar"`
|
|
||||||
}
|
|
||||||
a := A{FooBar: B{"XYZ"}}
|
|
||||||
os.Stdout.WriteString("Default: ")
|
|
||||||
form.NewEncoder(os.Stdout).Encode(a)
|
|
||||||
os.Stdout.WriteString("\nCustom: ")
|
|
||||||
form.NewEncoder(os.Stdout).DelimitWith('/').Encode(a)
|
|
||||||
os.Stdout.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
...will produce...
|
|
||||||
|
|
||||||
```
|
|
||||||
Default: foo%5C.bar.qux=XYZ
|
|
||||||
Custom: foo.bar%2Fqux=XYZ
|
|
||||||
```
|
|
||||||
|
|
||||||
(`%5C` and `%2F` represent `\` and `/`, respectively.)
|
|
||||||
|
|
||||||
Limitations
|
|
||||||
-----------
|
|
||||||
|
|
||||||
- Circular (self-referential) values are untested.
|
|
||||||
|
|
||||||
Future Work
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The following items would be nice to have in the future—though they are not being worked on yet:
|
|
||||||
|
|
||||||
- An option to treat all values as if they had been tagged with `omitempty`.
|
|
||||||
- An option to automatically treat all field names in `camelCase` or `underscore_case`.
|
|
||||||
- Built-in support for the types in [`math/big`](http://golang.org/pkg/math/big/).
|
|
||||||
- Built-in support for the types in [`image/color`](http://golang.org/pkg/image/color/).
|
|
||||||
- Improve encoding/decoding by reading/writing directly from/to the `io.Reader`/`io.Writer` when possible, rather than going through an intermediate representation (i.e. `node`) which requires more memory.
|
|
||||||
|
|
||||||
(Feel free to implement any of these and then send a pull request.)
|
|
||||||
|
|
||||||
Related Work
|
|
||||||
------------
|
|
||||||
|
|
||||||
- Package [gorilla/schema](https://github.com/gorilla/schema), which only implements decoding.
|
|
||||||
- Package [google/go-querystring](https://github.com/google/go-querystring), which only implements encoding.
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
This library is distributed under a BSD-style [LICENSE](./LICENSE).
|
|
||||||
4
vendor/github.com/ajg/form/TODO.md
generated
vendored
4
vendor/github.com/ajg/form/TODO.md
generated
vendored
@ -1,4 +0,0 @@
|
|||||||
TODO
|
|
||||||
====
|
|
||||||
|
|
||||||
- Document IgnoreCase and IgnoreUnknownKeys in README.
|
|
||||||
370
vendor/github.com/ajg/form/decode.go
generated
vendored
370
vendor/github.com/ajg/form/decode.go
generated
vendored
@ -1,370 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package form
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDecoder returns a new form Decoder.
|
|
||||||
func NewDecoder(r io.Reader) *Decoder {
|
|
||||||
return &Decoder{r, defaultDelimiter, defaultEscape, false, false}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decoder decodes data from a form (application/x-www-form-urlencoded).
|
|
||||||
type Decoder struct {
|
|
||||||
r io.Reader
|
|
||||||
d rune
|
|
||||||
e rune
|
|
||||||
ignoreUnknown bool
|
|
||||||
ignoreCase bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// DelimitWith sets r as the delimiter used for composite keys by Decoder d and returns the latter; it is '.' by default.
|
|
||||||
func (d *Decoder) DelimitWith(r rune) *Decoder {
|
|
||||||
d.d = r
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Decoder d and returns the latter; it is '\\' by default.
|
|
||||||
func (d *Decoder) EscapeWith(r rune) *Decoder {
|
|
||||||
d.e = r
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode reads in and decodes form-encoded data into dst.
|
|
||||||
func (d Decoder) Decode(dst interface{}) error {
|
|
||||||
bs, err := ioutil.ReadAll(d.r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
vs, err := url.ParseQuery(string(bs))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := reflect.ValueOf(dst)
|
|
||||||
return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IgnoreUnknownKeys if set to true it will make the Decoder ignore values
|
|
||||||
// that are not found in the destination object instead of returning an error.
|
|
||||||
func (d *Decoder) IgnoreUnknownKeys(ignoreUnknown bool) {
|
|
||||||
d.ignoreUnknown = ignoreUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
// IgnoreCase if set to true it will make the Decoder try to set values in the
|
|
||||||
// destination object even if the case does not match.
|
|
||||||
func (d *Decoder) IgnoreCase(ignoreCase bool) {
|
|
||||||
d.ignoreCase = ignoreCase
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeString decodes src into dst.
|
|
||||||
func (d Decoder) DecodeString(dst interface{}, src string) error {
|
|
||||||
vs, err := url.ParseQuery(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := reflect.ValueOf(dst)
|
|
||||||
return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeValues decodes vs into dst.
|
|
||||||
func (d Decoder) DecodeValues(dst interface{}, vs url.Values) error {
|
|
||||||
v := reflect.ValueOf(dst)
|
|
||||||
return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeString decodes src into dst.
|
|
||||||
func DecodeString(dst interface{}, src string) error {
|
|
||||||
return NewDecoder(nil).DecodeString(dst, src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeValues decodes vs into dst.
|
|
||||||
func DecodeValues(dst interface{}, vs url.Values) error {
|
|
||||||
return NewDecoder(nil).DecodeValues(dst, vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeNode(v reflect.Value, n node) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
err = fmt.Errorf("%v", e)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if v.Kind() == reflect.Slice {
|
|
||||||
return fmt.Errorf("could not decode directly into slice; use pointer to slice")
|
|
||||||
}
|
|
||||||
d.decodeValue(v, n)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeValue(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
k := v.Kind()
|
|
||||||
|
|
||||||
if k == reflect.Ptr && v.IsNil() {
|
|
||||||
v.Set(reflect.New(t.Elem()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if unmarshalValue(v, x) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
empty := isEmpty(x)
|
|
||||||
|
|
||||||
switch k {
|
|
||||||
case reflect.Ptr:
|
|
||||||
d.decodeValue(v.Elem(), x)
|
|
||||||
return
|
|
||||||
case reflect.Interface:
|
|
||||||
if !v.IsNil() {
|
|
||||||
d.decodeValue(v.Elem(), x)
|
|
||||||
return
|
|
||||||
|
|
||||||
} else if empty {
|
|
||||||
return // Allow nil interfaces only if empty.
|
|
||||||
} else {
|
|
||||||
panic("form: cannot decode non-empty value into into nil interface")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if empty {
|
|
||||||
v.Set(reflect.Zero(t)) // Treat the empty string as the zero value.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch k {
|
|
||||||
case reflect.Struct:
|
|
||||||
if t.ConvertibleTo(timeType) {
|
|
||||||
d.decodeTime(v, x)
|
|
||||||
} else if t.ConvertibleTo(urlType) {
|
|
||||||
d.decodeURL(v, x)
|
|
||||||
} else {
|
|
||||||
d.decodeStruct(v, x)
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
d.decodeSlice(v, x)
|
|
||||||
case reflect.Array:
|
|
||||||
d.decodeArray(v, x)
|
|
||||||
case reflect.Map:
|
|
||||||
d.decodeMap(v, x)
|
|
||||||
case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
|
||||||
panic(t.String() + " has unsupported kind " + k.String())
|
|
||||||
default:
|
|
||||||
d.decodeBasic(v, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeStruct(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
for k, c := range getNode(x) {
|
|
||||||
if f, ok := findField(v, k, d.ignoreCase); !ok && k == "" {
|
|
||||||
panic(getString(x) + " cannot be decoded as " + t.String())
|
|
||||||
} else if !ok {
|
|
||||||
if !d.ignoreUnknown {
|
|
||||||
panic(k + " doesn't exist in " + t.String())
|
|
||||||
}
|
|
||||||
} else if !f.CanSet() {
|
|
||||||
panic(k + " cannot be set in " + t.String())
|
|
||||||
} else {
|
|
||||||
d.decodeValue(f, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeMap(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
if v.IsNil() {
|
|
||||||
v.Set(reflect.MakeMap(t))
|
|
||||||
}
|
|
||||||
for k, c := range getNode(x) {
|
|
||||||
i := reflect.New(t.Key()).Elem()
|
|
||||||
d.decodeValue(i, k)
|
|
||||||
|
|
||||||
w := v.MapIndex(i)
|
|
||||||
if w.IsValid() { // We have an actual element value to decode into.
|
|
||||||
if w.Kind() == reflect.Interface {
|
|
||||||
w = w.Elem()
|
|
||||||
}
|
|
||||||
w = reflect.New(w.Type()).Elem()
|
|
||||||
} else if t.Elem().Kind() != reflect.Interface { // The map's element type is concrete.
|
|
||||||
w = reflect.New(t.Elem()).Elem()
|
|
||||||
} else {
|
|
||||||
// The best we can do here is to decode as either a string (for scalars) or a map[string]interface {} (for the rest).
|
|
||||||
// We could try to guess the type based on the string (e.g. true/false => bool) but that'll get ugly fast,
|
|
||||||
// especially if we have to guess the kind (slice vs. array vs. map) and index type (e.g. string, int, etc.)
|
|
||||||
switch c.(type) {
|
|
||||||
case node:
|
|
||||||
w = reflect.MakeMap(stringMapType)
|
|
||||||
case string:
|
|
||||||
w = reflect.New(stringType).Elem()
|
|
||||||
default:
|
|
||||||
panic("value is neither node nor string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.decodeValue(w, c)
|
|
||||||
v.SetMapIndex(i, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeArray(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
for k, c := range getNode(x) {
|
|
||||||
i, err := strconv.Atoi(k)
|
|
||||||
if err != nil {
|
|
||||||
panic(k + " is not a valid index for type " + t.String())
|
|
||||||
}
|
|
||||||
if l := v.Len(); i >= l {
|
|
||||||
panic("index is above array size")
|
|
||||||
}
|
|
||||||
d.decodeValue(v.Index(i), c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeSlice(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
if t.Elem().Kind() == reflect.Uint8 {
|
|
||||||
// Allow, but don't require, byte slices to be encoded as a single string.
|
|
||||||
if s, ok := x.(string); ok {
|
|
||||||
v.SetBytes([]byte(s))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Implicit indexing is currently done at the parseValues level,
|
|
||||||
// so if if an implicitKey reaches here it will always replace the last.
|
|
||||||
implicit := 0
|
|
||||||
for k, c := range getNode(x) {
|
|
||||||
var i int
|
|
||||||
if k == implicitKey {
|
|
||||||
i = implicit
|
|
||||||
implicit++
|
|
||||||
} else {
|
|
||||||
explicit, err := strconv.Atoi(k)
|
|
||||||
if err != nil {
|
|
||||||
panic(k + " is not a valid index for type " + t.String())
|
|
||||||
}
|
|
||||||
i = explicit
|
|
||||||
implicit = explicit + 1
|
|
||||||
}
|
|
||||||
// "Extend" the slice if it's too short.
|
|
||||||
if l := v.Len(); i >= l {
|
|
||||||
delta := i - l + 1
|
|
||||||
v.Set(reflect.AppendSlice(v, reflect.MakeSlice(t, delta, delta)))
|
|
||||||
}
|
|
||||||
d.decodeValue(v.Index(i), c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeBasic(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
switch k, s := t.Kind(), getString(x); k {
|
|
||||||
case reflect.Bool:
|
|
||||||
if b, e := strconv.ParseBool(s); e == nil {
|
|
||||||
v.SetBool(b)
|
|
||||||
} else {
|
|
||||||
panic("could not parse bool from " + strconv.Quote(s))
|
|
||||||
}
|
|
||||||
case reflect.Int,
|
|
||||||
reflect.Int8,
|
|
||||||
reflect.Int16,
|
|
||||||
reflect.Int32,
|
|
||||||
reflect.Int64:
|
|
||||||
if i, e := strconv.ParseInt(s, 10, 64); e == nil {
|
|
||||||
v.SetInt(i)
|
|
||||||
} else {
|
|
||||||
panic("could not parse int from " + strconv.Quote(s))
|
|
||||||
}
|
|
||||||
case reflect.Uint,
|
|
||||||
reflect.Uint8,
|
|
||||||
reflect.Uint16,
|
|
||||||
reflect.Uint32,
|
|
||||||
reflect.Uint64:
|
|
||||||
if u, e := strconv.ParseUint(s, 10, 64); e == nil {
|
|
||||||
v.SetUint(u)
|
|
||||||
} else {
|
|
||||||
panic("could not parse uint from " + strconv.Quote(s))
|
|
||||||
}
|
|
||||||
case reflect.Float32,
|
|
||||||
reflect.Float64:
|
|
||||||
if f, e := strconv.ParseFloat(s, 64); e == nil {
|
|
||||||
v.SetFloat(f)
|
|
||||||
} else {
|
|
||||||
panic("could not parse float from " + strconv.Quote(s))
|
|
||||||
}
|
|
||||||
case reflect.Complex64,
|
|
||||||
reflect.Complex128:
|
|
||||||
var c complex128
|
|
||||||
if n, err := fmt.Sscanf(s, "%g", &c); n == 1 && err == nil {
|
|
||||||
v.SetComplex(c)
|
|
||||||
} else {
|
|
||||||
panic("could not parse complex from " + strconv.Quote(s))
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
v.SetString(s)
|
|
||||||
default:
|
|
||||||
panic(t.String() + " has unsupported kind " + k.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeTime(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
s := getString(x)
|
|
||||||
// TODO: Find a more efficient way to do this.
|
|
||||||
for _, f := range allowedTimeFormats {
|
|
||||||
if p, err := time.Parse(f, s); err == nil {
|
|
||||||
v.Set(reflect.ValueOf(p).Convert(v.Type()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("cannot decode string `" + s + "` as " + t.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Decoder) decodeURL(v reflect.Value, x interface{}) {
|
|
||||||
t := v.Type()
|
|
||||||
s := getString(x)
|
|
||||||
if u, err := url.Parse(s); err == nil {
|
|
||||||
v.Set(reflect.ValueOf(*u).Convert(v.Type()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic("cannot decode string `" + s + "` as " + t.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowedTimeFormats = []string{
|
|
||||||
"2006-01-02T15:04:05.999999999Z07:00",
|
|
||||||
"2006-01-02T15:04:05.999999999Z07",
|
|
||||||
"2006-01-02T15:04:05.999999999Z",
|
|
||||||
"2006-01-02T15:04:05.999999999",
|
|
||||||
"2006-01-02T15:04:05Z07:00",
|
|
||||||
"2006-01-02T15:04:05Z07",
|
|
||||||
"2006-01-02T15:04:05Z",
|
|
||||||
"2006-01-02T15:04:05",
|
|
||||||
"2006-01-02T15:04Z",
|
|
||||||
"2006-01-02T15:04",
|
|
||||||
"2006-01-02T15Z",
|
|
||||||
"2006-01-02T15",
|
|
||||||
"2006-01-02",
|
|
||||||
"2006-01",
|
|
||||||
"2006",
|
|
||||||
"15:04:05.999999999Z07:00",
|
|
||||||
"15:04:05.999999999Z07",
|
|
||||||
"15:04:05.999999999Z",
|
|
||||||
"15:04:05.999999999",
|
|
||||||
"15:04:05Z07:00",
|
|
||||||
"15:04:05Z07",
|
|
||||||
"15:04:05Z",
|
|
||||||
"15:04:05",
|
|
||||||
"15:04Z",
|
|
||||||
"15:04",
|
|
||||||
"15Z",
|
|
||||||
"15",
|
|
||||||
}
|
|
||||||
114
vendor/github.com/ajg/form/decode_test.go
generated
vendored
114
vendor/github.com/ajg/form/decode_test.go
generated
vendored
@ -1,114 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package form
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDecodeString(t *testing.T) {
|
|
||||||
for _, c := range testCases(decOnly) {
|
|
||||||
if err := DecodeString(c.a, c.s); err != nil {
|
|
||||||
t.Errorf("DecodeString(%#v): %s", c.s, err)
|
|
||||||
} else if !reflect.DeepEqual(c.a, c.b) {
|
|
||||||
t.Errorf("DecodeString(%#v)\n want (%#v)\n have (%#v)", c.s, c.b, c.a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeValues(t *testing.T) {
|
|
||||||
for _, c := range testCases(decOnly) {
|
|
||||||
vs := mustParseQuery(c.s)
|
|
||||||
|
|
||||||
if err := DecodeValues(c.a, vs); err != nil {
|
|
||||||
t.Errorf("DecodeValues(%#v): %s", vs, err)
|
|
||||||
} else if !reflect.DeepEqual(c.a, c.b) {
|
|
||||||
t.Errorf("DecodeValues(%#v)\n want (%#v)\n have (%#v)", vs, c.b, c.a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecode(t *testing.T) {
|
|
||||||
for _, c := range testCases(decOnly) {
|
|
||||||
r := strings.NewReader(c.s)
|
|
||||||
d := NewDecoder(r)
|
|
||||||
|
|
||||||
if err := d.Decode(c.a); err != nil {
|
|
||||||
t.Errorf("Decode(%#v): %s", r, err)
|
|
||||||
} else if !reflect.DeepEqual(c.a, c.b) {
|
|
||||||
t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", r, c.b, c.a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeIgnoreUnknown(t *testing.T) {
|
|
||||||
type simpleStruct struct{ A string }
|
|
||||||
var dst simpleStruct
|
|
||||||
values := url.Values{
|
|
||||||
"b": []string{"2"},
|
|
||||||
"A": []string{"1"},
|
|
||||||
}
|
|
||||||
expected := simpleStruct{A: "1"}
|
|
||||||
d := NewDecoder(nil)
|
|
||||||
err := d.DecodeValues(&dst, values)
|
|
||||||
if err == nil || err.Error() != "b doesn't exist in form.simpleStruct" {
|
|
||||||
t.Errorf("Decode(%#v): expected error got nil", values)
|
|
||||||
}
|
|
||||||
d.IgnoreUnknownKeys(true)
|
|
||||||
err = d.DecodeValues(&dst, values)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Decode(%#v): %s", values, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(dst, expected) {
|
|
||||||
t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", values, expected, dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeIgnoreCase(t *testing.T) {
|
|
||||||
type simpleStruct struct{ AaAA string }
|
|
||||||
var dst simpleStruct
|
|
||||||
values := url.Values{
|
|
||||||
"aAaA": []string{"1"},
|
|
||||||
}
|
|
||||||
expected := simpleStruct{AaAA: "1"}
|
|
||||||
d := NewDecoder(nil)
|
|
||||||
err := d.DecodeValues(&dst, values)
|
|
||||||
if err == nil || err.Error() != "aAaA doesn't exist in form.simpleStruct" {
|
|
||||||
t.Errorf("Decode(%#v): expected error got nil", values)
|
|
||||||
}
|
|
||||||
d.IgnoreCase(true)
|
|
||||||
err = d.DecodeValues(&dst, values)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Decode(%#v): %s", values, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(dst, expected) {
|
|
||||||
t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", values, expected, dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeIgnoreCasePriority(t *testing.T) {
|
|
||||||
type simpleStruct struct {
|
|
||||||
Aaa string
|
|
||||||
AaA string
|
|
||||||
AAA string
|
|
||||||
}
|
|
||||||
var dst simpleStruct
|
|
||||||
values := url.Values{
|
|
||||||
"AaA": []string{"1"},
|
|
||||||
}
|
|
||||||
expected := simpleStruct{AaA: "1"}
|
|
||||||
d := NewDecoder(nil)
|
|
||||||
d.IgnoreCase(true)
|
|
||||||
err := d.DecodeValues(&dst, values)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Decode(%#v): %s", values, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(dst, expected) {
|
|
||||||
t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", values, expected, dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
388
vendor/github.com/ajg/form/encode.go
generated
vendored
388
vendor/github.com/ajg/form/encode.go
generated
vendored
@ -1,388 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package form
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewEncoder returns a new form Encoder.
|
|
||||||
func NewEncoder(w io.Writer) *Encoder {
|
|
||||||
return &Encoder{w, defaultDelimiter, defaultEscape, false}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoder provides a way to encode to a Writer.
|
|
||||||
type Encoder struct {
|
|
||||||
w io.Writer
|
|
||||||
d rune
|
|
||||||
e rune
|
|
||||||
z bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// DelimitWith sets r as the delimiter used for composite keys by Encoder e and returns the latter; it is '.' by default.
|
|
||||||
func (e *Encoder) DelimitWith(r rune) *Encoder {
|
|
||||||
e.d = r
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Encoder e and returns the latter; it is '\\' by default.
|
|
||||||
func (e *Encoder) EscapeWith(r rune) *Encoder {
|
|
||||||
e.e = r
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeepZeros sets whether Encoder e should keep zero (default) values in their literal form when encoding, and returns the former; by default zero values are not kept, but are rather encoded as the empty string.
|
|
||||||
func (e *Encoder) KeepZeros(z bool) *Encoder {
|
|
||||||
e.z = z
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encodes dst as form and writes it out using the Encoder's Writer.
|
|
||||||
func (e Encoder) Encode(dst interface{}) error {
|
|
||||||
v := reflect.ValueOf(dst)
|
|
||||||
n, err := encodeToNode(v, e.z)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s := n.values(e.d, e.e).Encode()
|
|
||||||
l, err := io.WriteString(e.w, s)
|
|
||||||
switch {
|
|
||||||
case err != nil:
|
|
||||||
return err
|
|
||||||
case l != len(s):
|
|
||||||
return errors.New("could not write data completely")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeToString encodes dst as a form and returns it as a string.
|
|
||||||
func EncodeToString(dst interface{}) (string, error) {
|
|
||||||
v := reflect.ValueOf(dst)
|
|
||||||
n, err := encodeToNode(v, false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
vs := n.values(defaultDelimiter, defaultEscape)
|
|
||||||
return vs.Encode(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeToValues encodes dst as a form and returns it as Values.
|
|
||||||
func EncodeToValues(dst interface{}) (url.Values, error) {
|
|
||||||
v := reflect.ValueOf(dst)
|
|
||||||
n, err := encodeToNode(v, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
vs := n.values(defaultDelimiter, defaultEscape)
|
|
||||||
return vs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeToNode(v reflect.Value, z bool) (n node, err error) {
|
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
err = fmt.Errorf("%v", e)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return getNode(encodeValue(v, z)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeValue(v reflect.Value, z bool) interface{} {
|
|
||||||
t := v.Type()
|
|
||||||
k := v.Kind()
|
|
||||||
|
|
||||||
if s, ok := marshalValue(v); ok {
|
|
||||||
return s
|
|
||||||
} else if !z && isEmptyValue(v) {
|
|
||||||
return "" // Treat the zero value as the empty string.
|
|
||||||
}
|
|
||||||
|
|
||||||
switch k {
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return encodeValue(v.Elem(), z)
|
|
||||||
case reflect.Struct:
|
|
||||||
if t.ConvertibleTo(timeType) {
|
|
||||||
return encodeTime(v)
|
|
||||||
} else if t.ConvertibleTo(urlType) {
|
|
||||||
return encodeURL(v)
|
|
||||||
}
|
|
||||||
return encodeStruct(v, z)
|
|
||||||
case reflect.Slice:
|
|
||||||
return encodeSlice(v, z)
|
|
||||||
case reflect.Array:
|
|
||||||
return encodeArray(v, z)
|
|
||||||
case reflect.Map:
|
|
||||||
return encodeMap(v, z)
|
|
||||||
case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
|
||||||
panic(t.String() + " has unsupported kind " + t.Kind().String())
|
|
||||||
default:
|
|
||||||
return encodeBasic(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeStruct(v reflect.Value, z bool) interface{} {
|
|
||||||
t := v.Type()
|
|
||||||
n := node{}
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
k, oe := fieldInfo(f)
|
|
||||||
|
|
||||||
if k == "-" {
|
|
||||||
continue
|
|
||||||
} else if fv := v.Field(i); oe && isEmptyValue(fv) {
|
|
||||||
delete(n, k)
|
|
||||||
} else {
|
|
||||||
n[k] = encodeValue(fv, z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeMap(v reflect.Value, z bool) interface{} {
|
|
||||||
n := node{}
|
|
||||||
for _, i := range v.MapKeys() {
|
|
||||||
k := getString(encodeValue(i, z))
|
|
||||||
n[k] = encodeValue(v.MapIndex(i), z)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeArray(v reflect.Value, z bool) interface{} {
|
|
||||||
n := node{}
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
n[strconv.Itoa(i)] = encodeValue(v.Index(i), z)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeSlice(v reflect.Value, z bool) interface{} {
|
|
||||||
t := v.Type()
|
|
||||||
if t.Elem().Kind() == reflect.Uint8 {
|
|
||||||
return string(v.Bytes()) // Encode byte slices as a single string by default.
|
|
||||||
}
|
|
||||||
n := node{}
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
n[strconv.Itoa(i)] = encodeValue(v.Index(i), z)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeTime(v reflect.Value) string {
|
|
||||||
t := v.Convert(timeType).Interface().(time.Time)
|
|
||||||
if t.Year() == 0 && (t.Month() == 0 || t.Month() == 1) && (t.Day() == 0 || t.Day() == 1) {
|
|
||||||
return t.Format("15:04:05.999999999Z07:00")
|
|
||||||
} else if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 && t.Nanosecond() == 0 {
|
|
||||||
return t.Format("2006-01-02")
|
|
||||||
}
|
|
||||||
return t.Format("2006-01-02T15:04:05.999999999Z07:00")
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeURL(v reflect.Value) string {
|
|
||||||
u := v.Convert(urlType).Interface().(url.URL)
|
|
||||||
return u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeBasic(v reflect.Value) string {
|
|
||||||
t := v.Type()
|
|
||||||
switch k := t.Kind(); k {
|
|
||||||
case reflect.Bool:
|
|
||||||
return strconv.FormatBool(v.Bool())
|
|
||||||
case reflect.Int,
|
|
||||||
reflect.Int8,
|
|
||||||
reflect.Int16,
|
|
||||||
reflect.Int32,
|
|
||||||
reflect.Int64:
|
|
||||||
return strconv.FormatInt(v.Int(), 10)
|
|
||||||
case reflect.Uint,
|
|
||||||
reflect.Uint8,
|
|
||||||
reflect.Uint16,
|
|
||||||
reflect.Uint32,
|
|
||||||
reflect.Uint64:
|
|
||||||
return strconv.FormatUint(v.Uint(), 10)
|
|
||||||
case reflect.Float32:
|
|
||||||
return strconv.FormatFloat(v.Float(), 'g', -1, 32)
|
|
||||||
case reflect.Float64:
|
|
||||||
return strconv.FormatFloat(v.Float(), 'g', -1, 64)
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
s := fmt.Sprintf("%g", v.Complex())
|
|
||||||
return strings.TrimSuffix(strings.TrimPrefix(s, "("), ")")
|
|
||||||
case reflect.String:
|
|
||||||
return v.String()
|
|
||||||
}
|
|
||||||
panic(t.String() + " has unsupported kind " + t.Kind().String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEmptyValue(v reflect.Value) bool {
|
|
||||||
switch t := v.Type(); v.Kind() {
|
|
||||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
||||||
return v.Len() == 0
|
|
||||||
case reflect.Bool:
|
|
||||||
return !v.Bool()
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return v.Int() == 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
return v.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v.Float() == 0
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
return v.Complex() == 0
|
|
||||||
case reflect.Interface, reflect.Ptr:
|
|
||||||
return v.IsNil()
|
|
||||||
case reflect.Struct:
|
|
||||||
if t.ConvertibleTo(timeType) {
|
|
||||||
return v.Convert(timeType).Interface().(time.Time).IsZero()
|
|
||||||
}
|
|
||||||
return reflect.DeepEqual(v, reflect.Zero(t))
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// canIndexOrdinally returns whether a value contains an ordered sequence of elements.
|
|
||||||
func canIndexOrdinally(v reflect.Value) bool {
|
|
||||||
if !v.IsValid() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch t := v.Type(); t.Kind() {
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return canIndexOrdinally(v.Elem())
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func fieldInfo(f reflect.StructField) (k string, oe bool) {
|
|
||||||
if f.PkgPath != "" { // Skip private fields.
|
|
||||||
return omittedKey, oe
|
|
||||||
}
|
|
||||||
|
|
||||||
k = f.Name
|
|
||||||
tag := f.Tag.Get("form")
|
|
||||||
if tag == "" {
|
|
||||||
return k, oe
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := strings.SplitN(tag, ",", 2)
|
|
||||||
if ps[0] != "" {
|
|
||||||
k = ps[0]
|
|
||||||
}
|
|
||||||
if len(ps) == 2 {
|
|
||||||
oe = ps[1] == "omitempty"
|
|
||||||
}
|
|
||||||
return k, oe
|
|
||||||
}
|
|
||||||
|
|
||||||
func findField(v reflect.Value, n string, ignoreCase bool) (reflect.Value, bool) {
|
|
||||||
t := v.Type()
|
|
||||||
l := v.NumField()
|
|
||||||
|
|
||||||
var lowerN string
|
|
||||||
caseInsensitiveMatch := -1
|
|
||||||
if ignoreCase {
|
|
||||||
lowerN = strings.ToLower(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First try named fields.
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
k, _ := fieldInfo(f)
|
|
||||||
if k == omittedKey {
|
|
||||||
continue
|
|
||||||
} else if n == k {
|
|
||||||
return v.Field(i), true
|
|
||||||
} else if ignoreCase && lowerN == strings.ToLower(k) {
|
|
||||||
caseInsensitiveMatch = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no exact match was found try case insensitive match.
|
|
||||||
if caseInsensitiveMatch != -1 {
|
|
||||||
return v.Field(caseInsensitiveMatch), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then try anonymous (embedded) fields.
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
k, _ := fieldInfo(f)
|
|
||||||
if k == omittedKey || !f.Anonymous { // || k != "" ?
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fv := v.Field(i)
|
|
||||||
fk := fv.Kind()
|
|
||||||
for fk == reflect.Ptr || fk == reflect.Interface {
|
|
||||||
fv = fv.Elem()
|
|
||||||
fk = fv.Kind()
|
|
||||||
}
|
|
||||||
|
|
||||||
if fk != reflect.Struct {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ev, ok := findField(fv, n, ignoreCase); ok {
|
|
||||||
return ev, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return reflect.Value{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
stringType = reflect.TypeOf(string(""))
|
|
||||||
stringMapType = reflect.TypeOf(map[string]interface{}{})
|
|
||||||
timeType = reflect.TypeOf(time.Time{})
|
|
||||||
timePtrType = reflect.TypeOf(&time.Time{})
|
|
||||||
urlType = reflect.TypeOf(url.URL{})
|
|
||||||
)
|
|
||||||
|
|
||||||
func skipTextMarshalling(t reflect.Type) bool {
|
|
||||||
/*// Skip time.Time because its text unmarshaling is overly rigid:
|
|
||||||
return t == timeType || t == timePtrType*/
|
|
||||||
// Skip time.Time & convertibles because its text unmarshaling is overly rigid:
|
|
||||||
return t.ConvertibleTo(timeType) || t.ConvertibleTo(timePtrType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalValue(v reflect.Value, x interface{}) bool {
|
|
||||||
if skipTextMarshalling(v.Type()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
tu, ok := v.Interface().(encoding.TextUnmarshaler)
|
|
||||||
if !ok && !v.CanAddr() {
|
|
||||||
return false
|
|
||||||
} else if !ok {
|
|
||||||
return unmarshalValue(v.Addr(), x)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := getString(x)
|
|
||||||
if err := tu.UnmarshalText([]byte(s)); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalValue(v reflect.Value) (string, bool) {
|
|
||||||
if skipTextMarshalling(v.Type()) {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
tm, ok := v.Interface().(encoding.TextMarshaler)
|
|
||||||
if !ok && !v.CanAddr() {
|
|
||||||
return "", false
|
|
||||||
} else if !ok {
|
|
||||||
return marshalValue(v.Addr())
|
|
||||||
}
|
|
||||||
|
|
||||||
bs, err := tm.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return string(bs), true
|
|
||||||
}
|
|
||||||
101
vendor/github.com/ajg/form/encode_test.go
generated
vendored
101
vendor/github.com/ajg/form/encode_test.go
generated
vendored
@ -1,101 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package form
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeToString(t *testing.T) {
|
|
||||||
for _, c := range testCases(encOnly) {
|
|
||||||
if s, err := EncodeToString(c.b); err != nil {
|
|
||||||
t.Errorf("EncodeToString(%#v): %s", c.b, err)
|
|
||||||
} else if !reflect.DeepEqual(c.s, s) {
|
|
||||||
t.Errorf("EncodeToString(%#v)\n want (%#v)\n have (%#v)", c.b, c.s, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeToValues(t *testing.T) {
|
|
||||||
for _, c := range testCases(encOnly) {
|
|
||||||
cvs := mustParseQuery(c.s)
|
|
||||||
if vs, err := EncodeToValues(c.b); err != nil {
|
|
||||||
t.Errorf("EncodeToValues(%#v): %s", c.b, err)
|
|
||||||
} else if !reflect.DeepEqual(cvs, vs) {
|
|
||||||
t.Errorf("EncodeToValues(%#v)\n want (%#v)\n have (%#v)", c.b, cvs, vs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncode(t *testing.T) {
|
|
||||||
for _, c := range testCases(encOnly) {
|
|
||||||
var w bytes.Buffer
|
|
||||||
e := NewEncoder(&w)
|
|
||||||
|
|
||||||
if err := e.Encode(c.b); err != nil {
|
|
||||||
t.Errorf("Encode(%#v): %s", c.b, err)
|
|
||||||
} else if s := w.String(); !reflect.DeepEqual(c.s, s) {
|
|
||||||
t.Errorf("Encode(%#v)\n want (%#v)\n have (%#v)", c.b, c.s, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Thing1 struct {
|
|
||||||
String string `form:"name,omitempty"`
|
|
||||||
Integer *uint `form:"num,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Thing2 struct {
|
|
||||||
String string `form:"name,omitempty"`
|
|
||||||
Integer uint `form:"num,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Thing3 struct {
|
|
||||||
String string `form:"name"`
|
|
||||||
Integer *uint `form:"num"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Thing4 struct {
|
|
||||||
String string `form:"name"`
|
|
||||||
Integer uint `form:"num"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncode_KeepZero(t *testing.T) {
|
|
||||||
num := uint(0)
|
|
||||||
for _, c := range []struct {
|
|
||||||
b interface{}
|
|
||||||
s string
|
|
||||||
z bool
|
|
||||||
}{
|
|
||||||
{Thing1{"test", &num}, "name=test&num=", false},
|
|
||||||
{Thing1{"test", &num}, "name=test&num=0", true},
|
|
||||||
{Thing2{"test", num}, "name=test", false},
|
|
||||||
{Thing2{"test", num}, "name=test", true},
|
|
||||||
{Thing3{"test", &num}, "name=test&num=", false},
|
|
||||||
{Thing3{"test", &num}, "name=test&num=0", true},
|
|
||||||
{Thing4{"test", num}, "name=test&num=", false},
|
|
||||||
{Thing4{"test", num}, "name=test&num=0", true},
|
|
||||||
{Thing1{"", &num}, "num=", false},
|
|
||||||
{Thing1{"", &num}, "num=0", true},
|
|
||||||
{Thing2{"", num}, "", false},
|
|
||||||
{Thing2{"", num}, "", true},
|
|
||||||
{Thing3{"", &num}, "name=&num=", false},
|
|
||||||
{Thing3{"", &num}, "name=&num=0", true},
|
|
||||||
{Thing4{"", num}, "name=&num=", false},
|
|
||||||
{Thing4{"", num}, "name=&num=0", true},
|
|
||||||
} {
|
|
||||||
|
|
||||||
var w bytes.Buffer
|
|
||||||
e := NewEncoder(&w)
|
|
||||||
|
|
||||||
if err := e.KeepZeros(c.z).Encode(c.b); err != nil {
|
|
||||||
t.Errorf("KeepZeros(%#v).Encode(%#v): %s", c.z, c.b, err)
|
|
||||||
} else if s := w.String(); c.s != s {
|
|
||||||
t.Errorf("KeepZeros(%#v).Encode(%#v)\n want (%#v)\n have (%#v)", c.z, c.b, c.s, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
14
vendor/github.com/ajg/form/form.go
generated
vendored
14
vendor/github.com/ajg/form/form.go
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package form implements encoding and decoding of application/x-www-form-urlencoded data.
|
|
||||||
package form
|
|
||||||
|
|
||||||
const (
|
|
||||||
implicitKey = "_"
|
|
||||||
omittedKey = "-"
|
|
||||||
|
|
||||||
defaultDelimiter = '.'
|
|
||||||
defaultEscape = '\\'
|
|
||||||
)
|
|
||||||
342
vendor/github.com/ajg/form/form_test.go
generated
vendored
342
vendor/github.com/ajg/form/form_test.go
generated
vendored
@ -1,342 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package form
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Struct struct {
|
|
||||||
B bool
|
|
||||||
I int `form:"life"`
|
|
||||||
F float64
|
|
||||||
C complex128
|
|
||||||
R rune `form:",omitempty"` // For testing when non-empty.
|
|
||||||
Re rune `form:",omitempty"` // For testing when empty.
|
|
||||||
S string
|
|
||||||
T time.Time
|
|
||||||
U url.URL
|
|
||||||
A Array
|
|
||||||
M Map
|
|
||||||
Y interface{} `form:"-"` // For testing when non-empty.
|
|
||||||
Ye interface{} `form:"-"` // For testing when empty.
|
|
||||||
Zs Slice
|
|
||||||
E // Embedded.
|
|
||||||
P P `form:"P.D\\Q.B"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SXs map[string]interface{}
|
|
||||||
type E struct {
|
|
||||||
Bytes1 []byte // For testing explicit (qualified by embedder) name, e.g. "E.Bytes1".
|
|
||||||
Bytes2 []byte // For testing implicit (unqualified) name, e.g. just "Bytes2"
|
|
||||||
}
|
|
||||||
|
|
||||||
type Z time.Time // Defined as such to test conversions.
|
|
||||||
|
|
||||||
func (z Z) String() string { return time.Time(z).String() }
|
|
||||||
|
|
||||||
type Array [3]string
|
|
||||||
type Map map[string]int
|
|
||||||
type Slice []struct {
|
|
||||||
Z Z
|
|
||||||
Q Q
|
|
||||||
Qp *Q
|
|
||||||
Q2 Q `form:"-"`
|
|
||||||
E `form:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom marshaling
|
|
||||||
type Q struct {
|
|
||||||
a, b uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ encoding.TextMarshaler = &Q{}
|
|
||||||
_ encoding.TextUnmarshaler = &Q{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (u Q) MarshalText() ([]byte, error) {
|
|
||||||
return []byte(fmt.Sprintf("%d_%d", u.a, u.b)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Q) UnmarshalText(bs []byte) error {
|
|
||||||
_, err := fmt.Sscanf(string(bs), "%d_%d", &u.a, &u.b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepopulate(sxs SXs) SXs {
|
|
||||||
var B bool
|
|
||||||
var I int
|
|
||||||
var F float64
|
|
||||||
var C complex128
|
|
||||||
var R rune
|
|
||||||
var S string
|
|
||||||
var T time.Time
|
|
||||||
var U url.URL
|
|
||||||
var A Array
|
|
||||||
var M Map
|
|
||||||
// Y is ignored.
|
|
||||||
// Ye is ignored.
|
|
||||||
var Zs Slice
|
|
||||||
var E E
|
|
||||||
var P P
|
|
||||||
sxs["B"] = B
|
|
||||||
sxs["life"] = I
|
|
||||||
sxs["F"] = F
|
|
||||||
sxs["C"] = C
|
|
||||||
sxs["R"] = R
|
|
||||||
// Re is omitted.
|
|
||||||
sxs["S"] = S
|
|
||||||
sxs["T"] = T
|
|
||||||
sxs["U"] = U
|
|
||||||
sxs["A"] = A
|
|
||||||
sxs["M"] = M
|
|
||||||
// Y is ignored.
|
|
||||||
// Ye is ignored.
|
|
||||||
sxs["Zs"] = Zs
|
|
||||||
sxs["E"] = E
|
|
||||||
sxs["P.D\\Q.B"] = P
|
|
||||||
return sxs
|
|
||||||
}
|
|
||||||
|
|
||||||
type P struct {
|
|
||||||
A, B string
|
|
||||||
}
|
|
||||||
|
|
||||||
type direction int
|
|
||||||
|
|
||||||
const (
|
|
||||||
encOnly = 1
|
|
||||||
decOnly = 2
|
|
||||||
rndTrip = encOnly | decOnly
|
|
||||||
)
|
|
||||||
|
|
||||||
func testCases(dir direction) (cs []testCase) {
|
|
||||||
var B bool
|
|
||||||
var I int
|
|
||||||
var F float64
|
|
||||||
var C complex128
|
|
||||||
var R rune
|
|
||||||
var S string
|
|
||||||
var T time.Time
|
|
||||||
var U url.URL
|
|
||||||
const canonical = `A.0=x&A.1=y&A.2=z&B=true&C=42%2B6.6i&E.Bytes1=%00%01%02&E.Bytes2=%03%04%05&F=6.6&M.Bar=8&M.Foo=7&M.Qux=9&P%5C.D%5C%5CQ%5C.B.A=P%2FD&P%5C.D%5C%5CQ%5C.B.B=Q-B&R=8734&S=Hello%2C+there.&T=2013-10-01T07%3A05%3A34.000000088Z&U=http%3A%2F%2Fexample.org%2Ffoo%23bar&Zs.0.Q=11_22&Zs.0.Qp=33_44&Zs.0.Z=2006-12-01&life=42`
|
|
||||||
const variation = `;C=42%2B6.6i;A.0=x;M.Bar=8;F=6.6;A.1=y;R=8734;A.2=z;Zs.0.Qp=33_44;B=true;M.Foo=7;T=2013-10-01T07:05:34.000000088Z;E.Bytes1=%00%01%02;Bytes2=%03%04%05;Zs.0.Q=11_22;Zs.0.Z=2006-12-01;M.Qux=9;life=42;S=Hello,+there.;P\.D\\Q\.B.A=P/D;P\.D\\Q\.B.B=Q-B;U=http%3A%2F%2Fexample.org%2Ffoo%23bar;`
|
|
||||||
|
|
||||||
for _, c := range []testCase{
|
|
||||||
// Bools
|
|
||||||
{rndTrip, &B, "=", b(false)},
|
|
||||||
{rndTrip, &B, "=true", b(true)},
|
|
||||||
{decOnly, &B, "=false", b(false)},
|
|
||||||
|
|
||||||
// Ints
|
|
||||||
{rndTrip, &I, "=", i(0)},
|
|
||||||
{rndTrip, &I, "=42", i(42)},
|
|
||||||
{rndTrip, &I, "=-42", i(-42)},
|
|
||||||
{decOnly, &I, "=0", i(0)},
|
|
||||||
{decOnly, &I, "=-0", i(0)},
|
|
||||||
|
|
||||||
// Floats
|
|
||||||
{rndTrip, &F, "=", f(0)},
|
|
||||||
{rndTrip, &F, "=6.6", f(6.6)},
|
|
||||||
{rndTrip, &F, "=-6.6", f(-6.6)},
|
|
||||||
|
|
||||||
// Complexes
|
|
||||||
{rndTrip, &C, "=", c(complex(0, 0))},
|
|
||||||
{rndTrip, &C, "=42%2B6.6i", c(complex(42, 6.6))},
|
|
||||||
{rndTrip, &C, "=-42-6.6i", c(complex(-42, -6.6))},
|
|
||||||
|
|
||||||
// Runes
|
|
||||||
{rndTrip, &R, "=", r(0)},
|
|
||||||
{rndTrip, &R, "=97", r('a')},
|
|
||||||
{rndTrip, &R, "=8734", r('\u221E')},
|
|
||||||
|
|
||||||
// Strings
|
|
||||||
{rndTrip, &S, "=", s("")},
|
|
||||||
{rndTrip, &S, "=X+%26+Y+%26+Z", s("X & Y & Z")},
|
|
||||||
{rndTrip, &S, "=Hello%2C+there.", s("Hello, there.")},
|
|
||||||
{decOnly, &S, "=Hello, there.", s("Hello, there.")},
|
|
||||||
|
|
||||||
// Dates/Times
|
|
||||||
{rndTrip, &T, "=", t(time.Time{})},
|
|
||||||
{rndTrip, &T, "=2013-10-01T07%3A05%3A34.000000088Z", t(time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC))},
|
|
||||||
{decOnly, &T, "=2013-10-01T07:05:34.000000088Z", t(time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC))},
|
|
||||||
{rndTrip, &T, "=07%3A05%3A34.000000088Z", t(time.Date(0, 1, 1, 7, 5, 34, 88, time.UTC))},
|
|
||||||
{decOnly, &T, "=07:05:34.000000088Z", t(time.Date(0, 1, 1, 7, 5, 34, 88, time.UTC))},
|
|
||||||
{rndTrip, &T, "=2013-10-01", t(time.Date(2013, 10, 1, 0, 0, 0, 0, time.UTC))},
|
|
||||||
|
|
||||||
// URLs
|
|
||||||
{rndTrip, &U, "=", u(url.URL{})},
|
|
||||||
{rndTrip, &U, "=http%3A%2F%2Fexample.org%2Ffoo%23bar", u(url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"})},
|
|
||||||
{rndTrip, &U, "=git%3A%2F%2Fgithub.com%2Fajg%2Fform.git", u(url.URL{Scheme: "git", Host: "github.com", Path: "/ajg/form.git"})},
|
|
||||||
|
|
||||||
// Structs
|
|
||||||
{rndTrip, &Struct{Y: 786}, canonical,
|
|
||||||
&Struct{
|
|
||||||
true,
|
|
||||||
42,
|
|
||||||
6.6,
|
|
||||||
complex(42, 6.6),
|
|
||||||
'\u221E',
|
|
||||||
rune(0),
|
|
||||||
"Hello, there.",
|
|
||||||
time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC),
|
|
||||||
url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"},
|
|
||||||
Array{"x", "y", "z"},
|
|
||||||
Map{"Foo": 7, "Bar": 8, "Qux": 9},
|
|
||||||
786, // Y: This value should not change.
|
|
||||||
nil, // Ye: This value should not change.
|
|
||||||
Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}},
|
|
||||||
E{[]byte{0, 1, 2}, []byte{3, 4, 5}},
|
|
||||||
P{"P/D", "Q-B"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{decOnly, &Struct{Y: 786}, variation,
|
|
||||||
&Struct{
|
|
||||||
true,
|
|
||||||
42,
|
|
||||||
6.6,
|
|
||||||
complex(42, 6.6),
|
|
||||||
'\u221E',
|
|
||||||
rune(0),
|
|
||||||
"Hello, there.",
|
|
||||||
time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC),
|
|
||||||
url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"},
|
|
||||||
Array{"x", "y", "z"},
|
|
||||||
Map{"Foo": 7, "Bar": 8, "Qux": 9},
|
|
||||||
786, // Y: This value should not change.
|
|
||||||
nil, // Ye: This value should not change.
|
|
||||||
Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}},
|
|
||||||
E{[]byte{0, 1, 2}, []byte{3, 4, 5}},
|
|
||||||
P{"P/D", "Q-B"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Maps
|
|
||||||
{rndTrip, prepopulate(SXs{}), canonical,
|
|
||||||
SXs{"B": true,
|
|
||||||
"life": 42,
|
|
||||||
"F": 6.6,
|
|
||||||
"C": complex(42, 6.6),
|
|
||||||
"R": '\u221E',
|
|
||||||
// Re is omitted.
|
|
||||||
"S": "Hello, there.",
|
|
||||||
"T": time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC),
|
|
||||||
"U": url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"},
|
|
||||||
"A": Array{"x", "y", "z"},
|
|
||||||
"M": Map{"Foo": 7, "Bar": 8, "Qux": 9},
|
|
||||||
// Y is ignored.
|
|
||||||
// Ye is ignored.
|
|
||||||
"Zs": Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}},
|
|
||||||
"E": E{[]byte{0, 1, 2}, []byte{3, 4, 5}},
|
|
||||||
"P.D\\Q.B": P{"P/D", "Q-B"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{decOnly, prepopulate(SXs{}), variation,
|
|
||||||
SXs{"B": true,
|
|
||||||
"life": 42,
|
|
||||||
"F": 6.6,
|
|
||||||
"C": complex(42, 6.6),
|
|
||||||
"R": '\u221E',
|
|
||||||
// Re is omitted.
|
|
||||||
"S": "Hello, there.",
|
|
||||||
"T": time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC),
|
|
||||||
"U": url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"},
|
|
||||||
"A": Array{"x", "y", "z"},
|
|
||||||
"M": Map{"Foo": 7, "Bar": 8, "Qux": 9},
|
|
||||||
// Y is ignored.
|
|
||||||
// Ye is ignored.
|
|
||||||
"Zs": Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}},
|
|
||||||
"E": E{[]byte{0, 1, 2}, nil},
|
|
||||||
"Bytes2": string([]byte{3, 4, 5}),
|
|
||||||
"P.D\\Q.B": P{"P/D", "Q-B"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{rndTrip, SXs{}, canonical,
|
|
||||||
SXs{"B": "true",
|
|
||||||
"life": "42",
|
|
||||||
"F": "6.6",
|
|
||||||
"C": "42+6.6i",
|
|
||||||
"R": "8734",
|
|
||||||
// Re is omitted.
|
|
||||||
"S": "Hello, there.",
|
|
||||||
"T": "2013-10-01T07:05:34.000000088Z",
|
|
||||||
"U": "http://example.org/foo#bar",
|
|
||||||
"A": map[string]interface{}{"0": "x", "1": "y", "2": "z"},
|
|
||||||
"M": map[string]interface{}{"Foo": "7", "Bar": "8", "Qux": "9"},
|
|
||||||
// Y is ignored.
|
|
||||||
// Ye is ignored.
|
|
||||||
"Zs": map[string]interface{}{
|
|
||||||
"0": map[string]interface{}{
|
|
||||||
"Z": "2006-12-01",
|
|
||||||
"Q": "11_22",
|
|
||||||
"Qp": "33_44",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"E": map[string]interface{}{"Bytes1": string([]byte{0, 1, 2}), "Bytes2": string([]byte{3, 4, 5})},
|
|
||||||
"P.D\\Q.B": map[string]interface{}{"A": "P/D", "B": "Q-B"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{decOnly, SXs{}, variation,
|
|
||||||
SXs{"B": "true",
|
|
||||||
"life": "42",
|
|
||||||
"F": "6.6",
|
|
||||||
"C": "42+6.6i",
|
|
||||||
"R": "8734",
|
|
||||||
// Re is omitted.
|
|
||||||
"S": "Hello, there.",
|
|
||||||
"T": "2013-10-01T07:05:34.000000088Z",
|
|
||||||
"U": "http://example.org/foo#bar",
|
|
||||||
"A": map[string]interface{}{"0": "x", "1": "y", "2": "z"},
|
|
||||||
"M": map[string]interface{}{"Foo": "7", "Bar": "8", "Qux": "9"},
|
|
||||||
// Y is ignored.
|
|
||||||
// Ye is ignored.
|
|
||||||
"Zs": map[string]interface{}{
|
|
||||||
"0": map[string]interface{}{
|
|
||||||
"Z": "2006-12-01",
|
|
||||||
"Q": "11_22",
|
|
||||||
"Qp": "33_44",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"E": map[string]interface{}{"Bytes1": string([]byte{0, 1, 2})},
|
|
||||||
"Bytes2": string([]byte{3, 4, 5}),
|
|
||||||
"P.D\\Q.B": map[string]interface{}{"A": "P/D", "B": "Q-B"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
if c.d&dir != 0 {
|
|
||||||
cs = append(cs, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cs
|
|
||||||
}
|
|
||||||
|
|
||||||
type testCase struct {
|
|
||||||
d direction
|
|
||||||
a interface{}
|
|
||||||
s string
|
|
||||||
b interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func b(b bool) *bool { return &b }
|
|
||||||
func i(i int) *int { return &i }
|
|
||||||
func f(f float64) *float64 { return &f }
|
|
||||||
func c(c complex128) *complex128 { return &c }
|
|
||||||
func r(r rune) *rune { return &r }
|
|
||||||
func s(s string) *string { return &s }
|
|
||||||
func t(t time.Time) *time.Time { return &t }
|
|
||||||
func u(u url.URL) *url.URL { return &u }
|
|
||||||
|
|
||||||
func mustParseQuery(s string) url.Values {
|
|
||||||
vs, err := url.ParseQuery(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return vs
|
|
||||||
}
|
|
||||||
152
vendor/github.com/ajg/form/node.go
generated
vendored
152
vendor/github.com/ajg/form/node.go
generated
vendored
@ -1,152 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package form
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type node map[string]interface{}
|
|
||||||
|
|
||||||
func (n node) values(d, e rune) url.Values {
|
|
||||||
vs := url.Values{}
|
|
||||||
n.merge(d, e, "", &vs)
|
|
||||||
return vs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n node) merge(d, e rune, p string, vs *url.Values) {
|
|
||||||
for k, x := range n {
|
|
||||||
switch y := x.(type) {
|
|
||||||
case string:
|
|
||||||
vs.Add(p+escape(d, e, k), y)
|
|
||||||
case node:
|
|
||||||
y.merge(d, e, p+escape(d, e, k)+string(d), vs)
|
|
||||||
default:
|
|
||||||
panic("value is neither string nor node")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add tests for implicit indexing.
|
|
||||||
func parseValues(d, e rune, vs url.Values, canIndexFirstLevelOrdinally bool) node {
|
|
||||||
// NOTE: Because of the flattening of potentially multiple strings to one key, implicit indexing works:
|
|
||||||
// i. At the first level; e.g. Foo.Bar=A&Foo.Bar=B becomes 0.Foo.Bar=A&1.Foo.Bar=B
|
|
||||||
// ii. At the last level; e.g. Foo.Bar._=A&Foo.Bar._=B becomes Foo.Bar.0=A&Foo.Bar.1=B
|
|
||||||
// TODO: At in-between levels; e.g. Foo._.Bar=A&Foo._.Bar=B becomes Foo.0.Bar=A&Foo.1.Bar=B
|
|
||||||
// (This last one requires that there only be one placeholder in order for it to be unambiguous.)
|
|
||||||
|
|
||||||
m := map[string]string{}
|
|
||||||
for k, ss := range vs {
|
|
||||||
indexLastLevelOrdinally := strings.HasSuffix(k, string(d)+implicitKey)
|
|
||||||
|
|
||||||
for i, s := range ss {
|
|
||||||
if canIndexFirstLevelOrdinally {
|
|
||||||
k = strconv.Itoa(i) + string(d) + k
|
|
||||||
} else if indexLastLevelOrdinally {
|
|
||||||
k = strings.TrimSuffix(k, implicitKey) + strconv.Itoa(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
m[k] = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n := node{}
|
|
||||||
for k, s := range m {
|
|
||||||
n = n.split(d, e, k, s)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitPath(d, e rune, path string) (k, rest string) {
|
|
||||||
esc := false
|
|
||||||
for i, r := range path {
|
|
||||||
switch {
|
|
||||||
case !esc && r == e:
|
|
||||||
esc = true
|
|
||||||
case !esc && r == d:
|
|
||||||
return unescape(d, e, path[:i]), path[i+1:]
|
|
||||||
default:
|
|
||||||
esc = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return unescape(d, e, path), ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n node) split(d, e rune, path, s string) node {
|
|
||||||
k, rest := splitPath(d, e, path)
|
|
||||||
if rest == "" {
|
|
||||||
return add(n, k, s)
|
|
||||||
}
|
|
||||||
if _, ok := n[k]; !ok {
|
|
||||||
n[k] = node{}
|
|
||||||
}
|
|
||||||
|
|
||||||
c := getNode(n[k])
|
|
||||||
n[k] = c.split(d, e, rest, s)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func add(n node, k, s string) node {
|
|
||||||
if n == nil {
|
|
||||||
return node{k: s}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := n[k]; ok {
|
|
||||||
panic("key " + k + " already set")
|
|
||||||
}
|
|
||||||
|
|
||||||
n[k] = s
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEmpty(x interface{}) bool {
|
|
||||||
switch y := x.(type) {
|
|
||||||
case string:
|
|
||||||
return y == ""
|
|
||||||
case node:
|
|
||||||
if s, ok := y[""].(string); ok {
|
|
||||||
return s == ""
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
panic("value is neither string nor node")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNode(x interface{}) node {
|
|
||||||
switch y := x.(type) {
|
|
||||||
case string:
|
|
||||||
return node{"": y}
|
|
||||||
case node:
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
panic("value is neither string nor node")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getString(x interface{}) string {
|
|
||||||
switch y := x.(type) {
|
|
||||||
case string:
|
|
||||||
return y
|
|
||||||
case node:
|
|
||||||
if s, ok := y[""].(string); ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
panic("value is neither string nor node")
|
|
||||||
}
|
|
||||||
|
|
||||||
func escape(d, e rune, s string) string {
|
|
||||||
s = strings.Replace(s, string(e), string(e)+string(e), -1) // Escape the escape (\ => \\)
|
|
||||||
s = strings.Replace(s, string(d), string(e)+string(d), -1) // Escape the delimiter (. => \.)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func unescape(d, e rune, s string) string {
|
|
||||||
s = strings.Replace(s, string(e)+string(d), string(d), -1) // Unescape the delimiter (\. => .)
|
|
||||||
s = strings.Replace(s, string(e)+string(e), string(e), -1) // Unescape the escape (\\ => \)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
87
vendor/github.com/ajg/form/node_test.go
generated
vendored
87
vendor/github.com/ajg/form/node_test.go
generated
vendored
@ -1,87 +0,0 @@
|
|||||||
// Copyright 2014 Alvaro J. Genial. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package form
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type foo int
|
|
||||||
type bar interface {
|
|
||||||
void()
|
|
||||||
}
|
|
||||||
type qux struct{}
|
|
||||||
type zee []bar
|
|
||||||
|
|
||||||
func TestCanIndexOrdinally(t *testing.T) {
|
|
||||||
for _, c := range []struct {
|
|
||||||
x interface{}
|
|
||||||
b bool
|
|
||||||
}{
|
|
||||||
{int(0), false},
|
|
||||||
{foo(0), false},
|
|
||||||
{qux{}, false},
|
|
||||||
{(*int)(nil), false},
|
|
||||||
{(*foo)(nil), false},
|
|
||||||
{(*bar)(nil), false},
|
|
||||||
{(*qux)(nil), false},
|
|
||||||
{[]qux{}, true},
|
|
||||||
{[5]qux{}, true},
|
|
||||||
{&[]foo{}, true},
|
|
||||||
{&[5]foo{}, true},
|
|
||||||
{zee{}, true},
|
|
||||||
{&zee{}, true},
|
|
||||||
{map[int]foo{}, false},
|
|
||||||
{map[string]interface{}{}, false},
|
|
||||||
{map[interface{}]bar{}, false},
|
|
||||||
{(chan<- int)(nil), false},
|
|
||||||
{(chan bar)(nil), false},
|
|
||||||
{(<-chan foo)(nil), false},
|
|
||||||
} {
|
|
||||||
v := reflect.ValueOf(c.x)
|
|
||||||
if b := canIndexOrdinally(v); b != c.b {
|
|
||||||
t.Errorf("canIndexOrdinally(%#v)\n want (%#v)\n have (%#v)", v, c.b, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var escapingTestCases = []struct {
|
|
||||||
a, b string
|
|
||||||
d, e rune
|
|
||||||
}{
|
|
||||||
{"Foo", "Foo", defaultDelimiter, defaultEscape},
|
|
||||||
{"Foo", "Foo", '/', '^'},
|
|
||||||
{"Foo.Bar.Qux", "Foo\\.Bar\\.Qux", defaultDelimiter, defaultEscape},
|
|
||||||
{"Foo.Bar.Qux", "Foo.Bar.Qux", '/', '^'},
|
|
||||||
{"Foo/Bar/Qux", "Foo/Bar/Qux", defaultDelimiter, defaultEscape},
|
|
||||||
{"Foo/Bar/Qux", "Foo^/Bar^/Qux", '/', '^'},
|
|
||||||
{"0", "0", defaultDelimiter, defaultEscape},
|
|
||||||
{"0", "0", '/', '^'},
|
|
||||||
{"0.1.2", "0\\.1\\.2", defaultDelimiter, defaultEscape},
|
|
||||||
{"0.1.2", "0.1.2", '/', '^'},
|
|
||||||
{"0/1/2", "0/1/2", defaultDelimiter, defaultEscape},
|
|
||||||
{"0/1/2", "0^/1^/2", '/', '^'},
|
|
||||||
{"A\\B", "A\\\\B", defaultDelimiter, defaultEscape},
|
|
||||||
{"A\\B", "A\\B", '/', '^'},
|
|
||||||
{"A^B", "A^B", defaultDelimiter, defaultEscape},
|
|
||||||
{"A^B", "A^^B", '/', '^'},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEscape(t *testing.T) {
|
|
||||||
for _, c := range escapingTestCases {
|
|
||||||
if b := escape(c.d, c.e, c.a); b != c.b {
|
|
||||||
t.Errorf("escape(%q, %q, %q)\n want (%#v)\n have (%#v)", c.d, c.e, c.a, c.b, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnescape(t *testing.T) {
|
|
||||||
for _, c := range escapingTestCases {
|
|
||||||
if a := unescape(c.d, c.e, c.b); a != c.a {
|
|
||||||
t.Errorf("unescape(%q, %q, %q)\n want (%#v)\n have (%#v)", c.d, c.e, c.b, c.a, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
vendor/github.com/ajg/form/pre-commit.sh
generated
vendored
18
vendor/github.com/ajg/form/pre-commit.sh
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
#!/bin/bash -eu
|
|
||||||
|
|
||||||
# TODO: Only colorize messages given a suitable terminal.
|
|
||||||
# FIXME: Handle case in which no stash entry is created due to no changes.
|
|
||||||
|
|
||||||
printf "\e[30m=== PRE-COMMIT STARTING ===\e[m\n"
|
|
||||||
git stash save --quiet --keep-index --include-untracked
|
|
||||||
|
|
||||||
if go build -v ./... && go test -v -cover ./... && go vet ./... && golint . && travis-lint; then
|
|
||||||
result=$?
|
|
||||||
printf "\e[32m=== PRE-COMMIT SUCCEEDED ===\e[m\n"
|
|
||||||
else
|
|
||||||
result=$?
|
|
||||||
printf "\e[31m=== PRE-COMMIT FAILED ===\e[m\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
git stash pop --quiet
|
|
||||||
exit $result
|
|
||||||
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
28
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
28
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
language: go
|
|
||||||
go_import_path: github.com/davecgh/go-spew
|
|
||||||
go:
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- tip
|
|
||||||
sudo: false
|
|
||||||
install:
|
|
||||||
- go get -v github.com/alecthomas/gometalinter
|
|
||||||
- gometalinter --install
|
|
||||||
script:
|
|
||||||
- export PATH=$PATH:$HOME/gopath/bin
|
|
||||||
- export GORACE="halt_on_error=1"
|
|
||||||
- test -z "$(gometalinter --disable-all
|
|
||||||
--enable=gofmt
|
|
||||||
--enable=golint
|
|
||||||
--enable=vet
|
|
||||||
--enable=gosimple
|
|
||||||
--enable=unconvert
|
|
||||||
--deadline=4m ./spew | tee /dev/stderr)"
|
|
||||||
- go test -v -race -tags safe ./spew
|
|
||||||
- go test -v -race -tags testcgo ./spew -covermode=atomic -coverprofile=profile.cov
|
|
||||||
after_success:
|
|
||||||
- go get -v github.com/mattn/goveralls
|
|
||||||
- goveralls -coverprofile=profile.cov -service=travis-ci
|
|
||||||
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
ISC License
|
|
||||||
|
|
||||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
201
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
201
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
@ -1,201 +0,0 @@
|
|||||||
go-spew
|
|
||||||
=======
|
|
||||||
|
|
||||||
[](https://travis-ci.org/davecgh/go-spew)
|
|
||||||
[](http://copyfree.org)
|
|
||||||
[](https://coveralls.io/r/davecgh/go-spew?branch=master)
|
|
||||||
|
|
||||||
Go-spew implements a deep pretty printer for Go data structures to aid in
|
|
||||||
debugging. A comprehensive suite of tests with 100% test coverage is provided
|
|
||||||
to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
|
|
||||||
report. Go-spew is licensed under the liberal ISC license, so it may be used in
|
|
||||||
open source or commercial projects.
|
|
||||||
|
|
||||||
If you're interested in reading about how this package came to life and some
|
|
||||||
of the challenges involved in providing a deep pretty printer, there is a blog
|
|
||||||
post about it
|
|
||||||
[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
[](http://godoc.org/github.com/davecgh/go-spew/spew)
|
|
||||||
|
|
||||||
Full `go doc` style documentation for the project can be viewed online without
|
|
||||||
installing this package by using the excellent GoDoc site here:
|
|
||||||
http://godoc.org/github.com/davecgh/go-spew/spew
|
|
||||||
|
|
||||||
You can also view the documentation locally once the package is installed with
|
|
||||||
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
|
|
||||||
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get -u github.com/davecgh/go-spew/spew
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
Add this import line to the file you're working in:
|
|
||||||
|
|
||||||
```Go
|
|
||||||
import "github.com/davecgh/go-spew/spew"
|
|
||||||
```
|
|
||||||
|
|
||||||
To dump a variable with full newlines, indentation, type, and pointer
|
|
||||||
information use Dump, Fdump, or Sdump:
|
|
||||||
|
|
||||||
```Go
|
|
||||||
spew.Dump(myVar1, myVar2, ...)
|
|
||||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
|
||||||
str := spew.Sdump(myVar1, myVar2, ...)
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
|
||||||
printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
|
|
||||||
compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
|
|
||||||
and pointer addresses):
|
|
||||||
|
|
||||||
```Go
|
|
||||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
||||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
||||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
||||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debugging a Web Application Example
|
|
||||||
|
|
||||||
Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
|
|
||||||
|
|
||||||
```Go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
|
||||||
|
|
||||||
func handler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "text/html")
|
|
||||||
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
|
|
||||||
fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
http.HandleFunc("/", handler)
|
|
||||||
http.ListenAndServe(":8080", nil)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sample Dump Output
|
|
||||||
|
|
||||||
```
|
|
||||||
(main.Foo) {
|
|
||||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
|
||||||
flag: (main.Flag) flagTwo,
|
|
||||||
data: (uintptr) <nil>
|
|
||||||
}),
|
|
||||||
ExportedField: (map[interface {}]interface {}) {
|
|
||||||
(string) "one": (bool) true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
([]uint8) {
|
|
||||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
|
||||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
|
||||||
00000020 31 32 |12|
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sample Formatter Output
|
|
||||||
|
|
||||||
Double pointer to a uint8:
|
|
||||||
```
|
|
||||||
%v: <**>5
|
|
||||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
|
||||||
%#v: (**uint8)5
|
|
||||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
|
||||||
```
|
|
||||||
|
|
||||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
|
||||||
```
|
|
||||||
%v: <*>{1 <*><shown>}
|
|
||||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
|
||||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
|
||||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
Configuration of spew is handled by fields in the ConfigState type. For
|
|
||||||
convenience, all of the top-level functions use a global state available via the
|
|
||||||
spew.Config global.
|
|
||||||
|
|
||||||
It is also possible to create a ConfigState instance that provides methods
|
|
||||||
equivalent to the top-level functions. This allows concurrent configuration
|
|
||||||
options. See the ConfigState documentation for more details.
|
|
||||||
|
|
||||||
```
|
|
||||||
* Indent
|
|
||||||
String to use for each indentation level for Dump functions.
|
|
||||||
It is a single space by default. A popular alternative is "\t".
|
|
||||||
|
|
||||||
* MaxDepth
|
|
||||||
Maximum number of levels to descend into nested data structures.
|
|
||||||
There is no limit by default.
|
|
||||||
|
|
||||||
* DisableMethods
|
|
||||||
Disables invocation of error and Stringer interface methods.
|
|
||||||
Method invocation is enabled by default.
|
|
||||||
|
|
||||||
* DisablePointerMethods
|
|
||||||
Disables invocation of error and Stringer interface methods on types
|
|
||||||
which only accept pointer receivers from non-pointer variables. This option
|
|
||||||
relies on access to the unsafe package, so it will not have any effect when
|
|
||||||
running in environments without access to the unsafe package such as Google
|
|
||||||
App Engine or with the "safe" build tag specified.
|
|
||||||
Pointer method invocation is enabled by default.
|
|
||||||
|
|
||||||
* DisablePointerAddresses
|
|
||||||
DisablePointerAddresses specifies whether to disable the printing of
|
|
||||||
pointer addresses. This is useful when diffing data structures in tests.
|
|
||||||
|
|
||||||
* DisableCapacities
|
|
||||||
DisableCapacities specifies whether to disable the printing of capacities
|
|
||||||
for arrays, slices, maps and channels. This is useful when diffing data
|
|
||||||
structures in tests.
|
|
||||||
|
|
||||||
* ContinueOnMethod
|
|
||||||
Enables recursion into types after invoking error and Stringer interface
|
|
||||||
methods. Recursion after method invocation is disabled by default.
|
|
||||||
|
|
||||||
* SortKeys
|
|
||||||
Specifies map keys should be sorted before being printed. Use
|
|
||||||
this to have a more deterministic, diffable output. Note that
|
|
||||||
only native types (bool, int, uint, floats, uintptr and string)
|
|
||||||
and types which implement error or Stringer interfaces are supported,
|
|
||||||
with other types sorted according to the reflect.Value.String() output
|
|
||||||
which guarantees display stability. Natural map order is used by
|
|
||||||
default.
|
|
||||||
|
|
||||||
* SpewKeys
|
|
||||||
SpewKeys specifies that, as a last resort attempt, map keys should be
|
|
||||||
spewed to strings and sorted by those strings. This is only considered
|
|
||||||
if SortKeys is true.
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Unsafe Package Dependency
|
|
||||||
|
|
||||||
This package relies on the unsafe package to perform some of the more advanced
|
|
||||||
features, however it also supports a "limited" mode which allows it to work in
|
|
||||||
environments where the unsafe package is not available. By default, it will
|
|
||||||
operate in this mode on Google App Engine and when compiled with GopherJS. The
|
|
||||||
"safe" build tag may also be specified to force the package to build without
|
|
||||||
using the unsafe package.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
|
|
||||||
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# This script uses gocov to generate a test coverage report.
|
|
||||||
# The gocov tool my be obtained with the following command:
|
|
||||||
# go get github.com/axw/gocov/gocov
|
|
||||||
#
|
|
||||||
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
|
|
||||||
|
|
||||||
# Check for gocov.
|
|
||||||
if ! type gocov >/dev/null 2>&1; then
|
|
||||||
echo >&2 "This script requires the gocov tool."
|
|
||||||
echo >&2 "You may obtain it with the following command:"
|
|
||||||
echo >&2 "go get github.com/axw/gocov/gocov"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Only run the cgo tests if gcc is installed.
|
|
||||||
if type gcc >/dev/null 2>&1; then
|
|
||||||
(cd spew && gocov test -tags testcgo | gocov report)
|
|
||||||
else
|
|
||||||
(cd spew && gocov test | gocov report)
|
|
||||||
fi
|
|
||||||
145
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
145
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
@ -1,145 +0,0 @@
|
|||||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|
||||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
|
||||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
|
||||||
// tag is deprecated and thus should not be used.
|
|
||||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
|
||||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
|
||||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
|
||||||
// not access to the unsafe package is available.
|
|
||||||
UnsafeDisabled = false
|
|
||||||
|
|
||||||
// ptrSize is the size of a pointer on the current arch.
|
|
||||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
|
||||||
)
|
|
||||||
|
|
||||||
type flag uintptr
|
|
||||||
|
|
||||||
var (
|
|
||||||
// flagRO indicates whether the value field of a reflect.Value
|
|
||||||
// is read-only.
|
|
||||||
flagRO flag
|
|
||||||
|
|
||||||
// flagAddr indicates whether the address of the reflect.Value's
|
|
||||||
// value may be taken.
|
|
||||||
flagAddr flag
|
|
||||||
)
|
|
||||||
|
|
||||||
// flagKindMask holds the bits that make up the kind
|
|
||||||
// part of the flags field. In all the supported versions,
|
|
||||||
// it is in the lower 5 bits.
|
|
||||||
const flagKindMask = flag(0x1f)
|
|
||||||
|
|
||||||
// Different versions of Go have used different
|
|
||||||
// bit layouts for the flags type. This table
|
|
||||||
// records the known combinations.
|
|
||||||
var okFlags = []struct {
|
|
||||||
ro, addr flag
|
|
||||||
}{{
|
|
||||||
// From Go 1.4 to 1.5
|
|
||||||
ro: 1 << 5,
|
|
||||||
addr: 1 << 7,
|
|
||||||
}, {
|
|
||||||
// Up to Go tip.
|
|
||||||
ro: 1<<5 | 1<<6,
|
|
||||||
addr: 1 << 8,
|
|
||||||
}}
|
|
||||||
|
|
||||||
var flagValOffset = func() uintptr {
|
|
||||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
|
||||||
if !ok {
|
|
||||||
panic("reflect.Value has no flag field")
|
|
||||||
}
|
|
||||||
return field.Offset
|
|
||||||
}()
|
|
||||||
|
|
||||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
|
||||||
func flagField(v *reflect.Value) *flag {
|
|
||||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
|
||||||
// the typical safety restrictions preventing access to unaddressable and
|
|
||||||
// unexported data. It works by digging the raw pointer to the underlying
|
|
||||||
// value out of the protected value and generating a new unprotected (unsafe)
|
|
||||||
// reflect.Value to it.
|
|
||||||
//
|
|
||||||
// This allows us to check for implementations of the Stringer and error
|
|
||||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
|
||||||
// inaccessible values such as unexported struct fields.
|
|
||||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
|
||||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
flagFieldPtr := flagField(&v)
|
|
||||||
*flagFieldPtr &^= flagRO
|
|
||||||
*flagFieldPtr |= flagAddr
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity checks against future reflect package changes
|
|
||||||
// to the type or semantics of the Value.flag field.
|
|
||||||
func init() {
|
|
||||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
|
||||||
if !ok {
|
|
||||||
panic("reflect.Value has no flag field")
|
|
||||||
}
|
|
||||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
|
||||||
panic("reflect.Value flag field has changed kind")
|
|
||||||
}
|
|
||||||
type t0 int
|
|
||||||
var t struct {
|
|
||||||
A t0
|
|
||||||
// t0 will have flagEmbedRO set.
|
|
||||||
t0
|
|
||||||
// a will have flagStickyRO set
|
|
||||||
a t0
|
|
||||||
}
|
|
||||||
vA := reflect.ValueOf(t).FieldByName("A")
|
|
||||||
va := reflect.ValueOf(t).FieldByName("a")
|
|
||||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
|
||||||
|
|
||||||
// Infer flagRO from the difference between the flags
|
|
||||||
// for the (otherwise identical) fields in t.
|
|
||||||
flagPublic := *flagField(&vA)
|
|
||||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
|
||||||
flagRO = flagPublic ^ flagWithRO
|
|
||||||
|
|
||||||
// Infer flagAddr from the difference between a value
|
|
||||||
// taken from a pointer and not.
|
|
||||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
|
||||||
flagNoPtr := *flagField(&vA)
|
|
||||||
flagPtr := *flagField(&vPtrA)
|
|
||||||
flagAddr = flagNoPtr ^ flagPtr
|
|
||||||
|
|
||||||
// Check that the inferred flags tally with one of the known versions.
|
|
||||||
for _, f := range okFlags {
|
|
||||||
if flagRO == f.ro && flagAddr == f.addr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("reflect.Value read-only flag has changed semantics")
|
|
||||||
}
|
|
||||||
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
@ -1,38 +0,0 @@
|
|||||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|
||||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
|
||||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
|
||||||
// tag is deprecated and thus should not be used.
|
|
||||||
// +build js appengine safe disableunsafe !go1.4
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import "reflect"
|
|
||||||
|
|
||||||
const (
|
|
||||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
|
||||||
// not access to the unsafe package is available.
|
|
||||||
UnsafeDisabled = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
|
||||||
// that bypasses the typical safety restrictions preventing access to
|
|
||||||
// unaddressable and unexported data. However, doing this relies on access to
|
|
||||||
// the unsafe package. This is a stub version which simply returns the passed
|
|
||||||
// reflect.Value when the unsafe package is not available.
|
|
||||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
@ -1,341 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
|
||||||
// the technique used in the fmt package.
|
|
||||||
var (
|
|
||||||
panicBytes = []byte("(PANIC=")
|
|
||||||
plusBytes = []byte("+")
|
|
||||||
iBytes = []byte("i")
|
|
||||||
trueBytes = []byte("true")
|
|
||||||
falseBytes = []byte("false")
|
|
||||||
interfaceBytes = []byte("(interface {})")
|
|
||||||
commaNewlineBytes = []byte(",\n")
|
|
||||||
newlineBytes = []byte("\n")
|
|
||||||
openBraceBytes = []byte("{")
|
|
||||||
openBraceNewlineBytes = []byte("{\n")
|
|
||||||
closeBraceBytes = []byte("}")
|
|
||||||
asteriskBytes = []byte("*")
|
|
||||||
colonBytes = []byte(":")
|
|
||||||
colonSpaceBytes = []byte(": ")
|
|
||||||
openParenBytes = []byte("(")
|
|
||||||
closeParenBytes = []byte(")")
|
|
||||||
spaceBytes = []byte(" ")
|
|
||||||
pointerChainBytes = []byte("->")
|
|
||||||
nilAngleBytes = []byte("<nil>")
|
|
||||||
maxNewlineBytes = []byte("<max depth reached>\n")
|
|
||||||
maxShortBytes = []byte("<max>")
|
|
||||||
circularBytes = []byte("<already shown>")
|
|
||||||
circularShortBytes = []byte("<shown>")
|
|
||||||
invalidAngleBytes = []byte("<invalid>")
|
|
||||||
openBracketBytes = []byte("[")
|
|
||||||
closeBracketBytes = []byte("]")
|
|
||||||
percentBytes = []byte("%")
|
|
||||||
precisionBytes = []byte(".")
|
|
||||||
openAngleBytes = []byte("<")
|
|
||||||
closeAngleBytes = []byte(">")
|
|
||||||
openMapBytes = []byte("map[")
|
|
||||||
closeMapBytes = []byte("]")
|
|
||||||
lenEqualsBytes = []byte("len=")
|
|
||||||
capEqualsBytes = []byte("cap=")
|
|
||||||
)
|
|
||||||
|
|
||||||
// hexDigits is used to map a decimal value to a hex digit.
|
|
||||||
var hexDigits = "0123456789abcdef"
|
|
||||||
|
|
||||||
// catchPanic handles any panics that might occur during the handleMethods
|
|
||||||
// calls.
|
|
||||||
func catchPanic(w io.Writer, v reflect.Value) {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
w.Write(panicBytes)
|
|
||||||
fmt.Fprintf(w, "%v", err)
|
|
||||||
w.Write(closeParenBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleMethods attempts to call the Error and String methods on the underlying
|
|
||||||
// type the passed reflect.Value represents and outputes the result to Writer w.
|
|
||||||
//
|
|
||||||
// It handles panics in any called methods by catching and displaying the error
|
|
||||||
// as the formatted value.
|
|
||||||
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
|
||||||
// We need an interface to check if the type implements the error or
|
|
||||||
// Stringer interface. However, the reflect package won't give us an
|
|
||||||
// interface on certain things like unexported struct fields in order
|
|
||||||
// to enforce visibility rules. We use unsafe, when it's available,
|
|
||||||
// to bypass these restrictions since this package does not mutate the
|
|
||||||
// values.
|
|
||||||
if !v.CanInterface() {
|
|
||||||
if UnsafeDisabled {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
v = unsafeReflectValue(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Choose whether or not to do error and Stringer interface lookups against
|
|
||||||
// the base type or a pointer to the base type depending on settings.
|
|
||||||
// Technically calling one of these methods with a pointer receiver can
|
|
||||||
// mutate the value, however, types which choose to satisify an error or
|
|
||||||
// Stringer interface with a pointer receiver should not be mutating their
|
|
||||||
// state inside these interface methods.
|
|
||||||
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
|
||||||
v = unsafeReflectValue(v)
|
|
||||||
}
|
|
||||||
if v.CanAddr() {
|
|
||||||
v = v.Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is it an error or Stringer?
|
|
||||||
switch iface := v.Interface().(type) {
|
|
||||||
case error:
|
|
||||||
defer catchPanic(w, v)
|
|
||||||
if cs.ContinueOnMethod {
|
|
||||||
w.Write(openParenBytes)
|
|
||||||
w.Write([]byte(iface.Error()))
|
|
||||||
w.Write(closeParenBytes)
|
|
||||||
w.Write(spaceBytes)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write([]byte(iface.Error()))
|
|
||||||
return true
|
|
||||||
|
|
||||||
case fmt.Stringer:
|
|
||||||
defer catchPanic(w, v)
|
|
||||||
if cs.ContinueOnMethod {
|
|
||||||
w.Write(openParenBytes)
|
|
||||||
w.Write([]byte(iface.String()))
|
|
||||||
w.Write(closeParenBytes)
|
|
||||||
w.Write(spaceBytes)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
w.Write([]byte(iface.String()))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// printBool outputs a boolean value as true or false to Writer w.
|
|
||||||
func printBool(w io.Writer, val bool) {
|
|
||||||
if val {
|
|
||||||
w.Write(trueBytes)
|
|
||||||
} else {
|
|
||||||
w.Write(falseBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// printInt outputs a signed integer value to Writer w.
|
|
||||||
func printInt(w io.Writer, val int64, base int) {
|
|
||||||
w.Write([]byte(strconv.FormatInt(val, base)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// printUint outputs an unsigned integer value to Writer w.
|
|
||||||
func printUint(w io.Writer, val uint64, base int) {
|
|
||||||
w.Write([]byte(strconv.FormatUint(val, base)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// printFloat outputs a floating point value using the specified precision,
|
|
||||||
// which is expected to be 32 or 64bit, to Writer w.
|
|
||||||
func printFloat(w io.Writer, val float64, precision int) {
|
|
||||||
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// printComplex outputs a complex value using the specified float precision
|
|
||||||
// for the real and imaginary parts to Writer w.
|
|
||||||
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
|
||||||
r := real(c)
|
|
||||||
w.Write(openParenBytes)
|
|
||||||
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
|
||||||
i := imag(c)
|
|
||||||
if i >= 0 {
|
|
||||||
w.Write(plusBytes)
|
|
||||||
}
|
|
||||||
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
|
||||||
w.Write(iBytes)
|
|
||||||
w.Write(closeParenBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
|
||||||
// prefix to Writer w.
|
|
||||||
func printHexPtr(w io.Writer, p uintptr) {
|
|
||||||
// Null pointer.
|
|
||||||
num := uint64(p)
|
|
||||||
if num == 0 {
|
|
||||||
w.Write(nilAngleBytes)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
|
||||||
buf := make([]byte, 18)
|
|
||||||
|
|
||||||
// It's simpler to construct the hex string right to left.
|
|
||||||
base := uint64(16)
|
|
||||||
i := len(buf) - 1
|
|
||||||
for num >= base {
|
|
||||||
buf[i] = hexDigits[num%base]
|
|
||||||
num /= base
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
buf[i] = hexDigits[num]
|
|
||||||
|
|
||||||
// Add '0x' prefix.
|
|
||||||
i--
|
|
||||||
buf[i] = 'x'
|
|
||||||
i--
|
|
||||||
buf[i] = '0'
|
|
||||||
|
|
||||||
// Strip unused leading bytes.
|
|
||||||
buf = buf[i:]
|
|
||||||
w.Write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
|
||||||
// elements to be sorted.
|
|
||||||
type valuesSorter struct {
|
|
||||||
values []reflect.Value
|
|
||||||
strings []string // either nil or same len and values
|
|
||||||
cs *ConfigState
|
|
||||||
}
|
|
||||||
|
|
||||||
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
|
||||||
// surrogate keys on which the data should be sorted. It uses flags in
|
|
||||||
// ConfigState to decide if and how to populate those surrogate keys.
|
|
||||||
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
|
||||||
vs := &valuesSorter{values: values, cs: cs}
|
|
||||||
if canSortSimply(vs.values[0].Kind()) {
|
|
||||||
return vs
|
|
||||||
}
|
|
||||||
if !cs.DisableMethods {
|
|
||||||
vs.strings = make([]string, len(values))
|
|
||||||
for i := range vs.values {
|
|
||||||
b := bytes.Buffer{}
|
|
||||||
if !handleMethods(cs, &b, vs.values[i]) {
|
|
||||||
vs.strings = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
vs.strings[i] = b.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if vs.strings == nil && cs.SpewKeys {
|
|
||||||
vs.strings = make([]string, len(values))
|
|
||||||
for i := range vs.values {
|
|
||||||
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vs
|
|
||||||
}
|
|
||||||
|
|
||||||
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
|
||||||
// directly, or whether it should be considered for sorting by surrogate keys
|
|
||||||
// (if the ConfigState allows it).
|
|
||||||
func canSortSimply(kind reflect.Kind) bool {
|
|
||||||
// This switch parallels valueSortLess, except for the default case.
|
|
||||||
switch kind {
|
|
||||||
case reflect.Bool:
|
|
||||||
return true
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
return true
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
||||||
return true
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return true
|
|
||||||
case reflect.String:
|
|
||||||
return true
|
|
||||||
case reflect.Uintptr:
|
|
||||||
return true
|
|
||||||
case reflect.Array:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of values in the slice. It is part of the
|
|
||||||
// sort.Interface implementation.
|
|
||||||
func (s *valuesSorter) Len() int {
|
|
||||||
return len(s.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap swaps the values at the passed indices. It is part of the
|
|
||||||
// sort.Interface implementation.
|
|
||||||
func (s *valuesSorter) Swap(i, j int) {
|
|
||||||
s.values[i], s.values[j] = s.values[j], s.values[i]
|
|
||||||
if s.strings != nil {
|
|
||||||
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// valueSortLess returns whether the first value should sort before the second
|
|
||||||
// value. It is used by valueSorter.Less as part of the sort.Interface
|
|
||||||
// implementation.
|
|
||||||
func valueSortLess(a, b reflect.Value) bool {
|
|
||||||
switch a.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return !a.Bool() && b.Bool()
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
return a.Int() < b.Int()
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
||||||
return a.Uint() < b.Uint()
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return a.Float() < b.Float()
|
|
||||||
case reflect.String:
|
|
||||||
return a.String() < b.String()
|
|
||||||
case reflect.Uintptr:
|
|
||||||
return a.Uint() < b.Uint()
|
|
||||||
case reflect.Array:
|
|
||||||
// Compare the contents of both arrays.
|
|
||||||
l := a.Len()
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
av := a.Index(i)
|
|
||||||
bv := b.Index(i)
|
|
||||||
if av.Interface() == bv.Interface() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return valueSortLess(av, bv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a.String() < b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Less returns whether the value at index i should sort before the
|
|
||||||
// value at index j. It is part of the sort.Interface implementation.
|
|
||||||
func (s *valuesSorter) Less(i, j int) bool {
|
|
||||||
if s.strings == nil {
|
|
||||||
return valueSortLess(s.values[i], s.values[j])
|
|
||||||
}
|
|
||||||
return s.strings[i] < s.strings[j]
|
|
||||||
}
|
|
||||||
|
|
||||||
// sortValues is a sort function that handles both native types and any type that
|
|
||||||
// can be converted to error or Stringer. Other inputs are sorted according to
|
|
||||||
// their Value.String() value to ensure display stability.
|
|
||||||
func sortValues(values []reflect.Value, cs *ConfigState) {
|
|
||||||
if len(values) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sort.Sort(newValuesSorter(values, cs))
|
|
||||||
}
|
|
||||||
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
@ -1,298 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
|
||||||
|
|
||||||
// custom type to test Stinger interface on non-pointer receiver.
|
|
||||||
type stringer string
|
|
||||||
|
|
||||||
// String implements the Stringer interface for testing invocation of custom
|
|
||||||
// stringers on types with non-pointer receivers.
|
|
||||||
func (s stringer) String() string {
|
|
||||||
return "stringer " + string(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom type to test Stinger interface on pointer receiver.
|
|
||||||
type pstringer string
|
|
||||||
|
|
||||||
// String implements the Stringer interface for testing invocation of custom
|
|
||||||
// stringers on types with only pointer receivers.
|
|
||||||
func (s *pstringer) String() string {
|
|
||||||
return "stringer " + string(*s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// xref1 and xref2 are cross referencing structs for testing circular reference
|
|
||||||
// detection.
|
|
||||||
type xref1 struct {
|
|
||||||
ps2 *xref2
|
|
||||||
}
|
|
||||||
type xref2 struct {
|
|
||||||
ps1 *xref1
|
|
||||||
}
|
|
||||||
|
|
||||||
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
|
|
||||||
// reference for testing detection.
|
|
||||||
type indirCir1 struct {
|
|
||||||
ps2 *indirCir2
|
|
||||||
}
|
|
||||||
type indirCir2 struct {
|
|
||||||
ps3 *indirCir3
|
|
||||||
}
|
|
||||||
type indirCir3 struct {
|
|
||||||
ps1 *indirCir1
|
|
||||||
}
|
|
||||||
|
|
||||||
// embed is used to test embedded structures.
|
|
||||||
type embed struct {
|
|
||||||
a string
|
|
||||||
}
|
|
||||||
|
|
||||||
// embedwrap is used to test embedded structures.
|
|
||||||
type embedwrap struct {
|
|
||||||
*embed
|
|
||||||
e *embed
|
|
||||||
}
|
|
||||||
|
|
||||||
// panicer is used to intentionally cause a panic for testing spew properly
|
|
||||||
// handles them
|
|
||||||
type panicer int
|
|
||||||
|
|
||||||
func (p panicer) String() string {
|
|
||||||
panic("test panic")
|
|
||||||
}
|
|
||||||
|
|
||||||
// customError is used to test custom error interface invocation.
|
|
||||||
type customError int
|
|
||||||
|
|
||||||
func (e customError) Error() string {
|
|
||||||
return fmt.Sprintf("error: %d", int(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringizeWants converts a slice of wanted test output into a format suitable
|
|
||||||
// for a test error message.
|
|
||||||
func stringizeWants(wants []string) string {
|
|
||||||
s := ""
|
|
||||||
for i, want := range wants {
|
|
||||||
if i > 0 {
|
|
||||||
s += fmt.Sprintf("want%d: %s", i+1, want)
|
|
||||||
} else {
|
|
||||||
s += "want: " + want
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// testFailed returns whether or not a test failed by checking if the result
|
|
||||||
// of the test is in the slice of wanted strings.
|
|
||||||
func testFailed(result string, wants []string) bool {
|
|
||||||
for _, want := range wants {
|
|
||||||
if result == want {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type sortableStruct struct {
|
|
||||||
x int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss sortableStruct) String() string {
|
|
||||||
return fmt.Sprintf("ss.%d", ss.x)
|
|
||||||
}
|
|
||||||
|
|
||||||
type unsortableStruct struct {
|
|
||||||
x int
|
|
||||||
}
|
|
||||||
|
|
||||||
type sortTestCase struct {
|
|
||||||
input []reflect.Value
|
|
||||||
expected []reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
|
|
||||||
getInterfaces := func(values []reflect.Value) []interface{} {
|
|
||||||
interfaces := []interface{}{}
|
|
||||||
for _, v := range values {
|
|
||||||
interfaces = append(interfaces, v.Interface())
|
|
||||||
}
|
|
||||||
return interfaces
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
spew.SortValues(test.input, cs)
|
|
||||||
// reflect.DeepEqual cannot really make sense of reflect.Value,
|
|
||||||
// probably because of all the pointer tricks. For instance,
|
|
||||||
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
|
|
||||||
// instead.
|
|
||||||
input := getInterfaces(test.input)
|
|
||||||
expected := getInterfaces(test.expected)
|
|
||||||
if !reflect.DeepEqual(input, expected) {
|
|
||||||
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSortValues ensures the sort functionality for relect.Value based sorting
|
|
||||||
// works as intended.
|
|
||||||
func TestSortValues(t *testing.T) {
|
|
||||||
v := reflect.ValueOf
|
|
||||||
|
|
||||||
a := v("a")
|
|
||||||
b := v("b")
|
|
||||||
c := v("c")
|
|
||||||
embedA := v(embed{"a"})
|
|
||||||
embedB := v(embed{"b"})
|
|
||||||
embedC := v(embed{"c"})
|
|
||||||
tests := []sortTestCase{
|
|
||||||
// No values.
|
|
||||||
{
|
|
||||||
[]reflect.Value{},
|
|
||||||
[]reflect.Value{},
|
|
||||||
},
|
|
||||||
// Bools.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(false), v(true), v(false)},
|
|
||||||
[]reflect.Value{v(false), v(false), v(true)},
|
|
||||||
},
|
|
||||||
// Ints.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(2), v(1), v(3)},
|
|
||||||
[]reflect.Value{v(1), v(2), v(3)},
|
|
||||||
},
|
|
||||||
// Uints.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
|
|
||||||
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
|
|
||||||
},
|
|
||||||
// Floats.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
|
|
||||||
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
|
|
||||||
},
|
|
||||||
// Strings.
|
|
||||||
{
|
|
||||||
[]reflect.Value{b, a, c},
|
|
||||||
[]reflect.Value{a, b, c},
|
|
||||||
},
|
|
||||||
// Array
|
|
||||||
{
|
|
||||||
[]reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
|
|
||||||
[]reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
|
|
||||||
},
|
|
||||||
// Uintptrs.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
|
|
||||||
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
|
|
||||||
},
|
|
||||||
// SortableStructs.
|
|
||||||
{
|
|
||||||
// Note: not sorted - DisableMethods is set.
|
|
||||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
|
||||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
|
||||||
},
|
|
||||||
// UnsortableStructs.
|
|
||||||
{
|
|
||||||
// Note: not sorted - SpewKeys is false.
|
|
||||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
|
||||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
|
||||||
},
|
|
||||||
// Invalid.
|
|
||||||
{
|
|
||||||
[]reflect.Value{embedB, embedA, embedC},
|
|
||||||
[]reflect.Value{embedB, embedA, embedC},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
|
|
||||||
helpTestSortValues(tests, &cs, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
|
|
||||||
// based sorting works as intended when using string methods.
|
|
||||||
func TestSortValuesWithMethods(t *testing.T) {
|
|
||||||
v := reflect.ValueOf
|
|
||||||
|
|
||||||
a := v("a")
|
|
||||||
b := v("b")
|
|
||||||
c := v("c")
|
|
||||||
tests := []sortTestCase{
|
|
||||||
// Ints.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(2), v(1), v(3)},
|
|
||||||
[]reflect.Value{v(1), v(2), v(3)},
|
|
||||||
},
|
|
||||||
// Strings.
|
|
||||||
{
|
|
||||||
[]reflect.Value{b, a, c},
|
|
||||||
[]reflect.Value{a, b, c},
|
|
||||||
},
|
|
||||||
// SortableStructs.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
|
||||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
|
||||||
},
|
|
||||||
// UnsortableStructs.
|
|
||||||
{
|
|
||||||
// Note: not sorted - SpewKeys is false.
|
|
||||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
|
||||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
|
|
||||||
helpTestSortValues(tests, &cs, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
|
|
||||||
// based sorting works as intended when using spew to stringify keys.
|
|
||||||
func TestSortValuesWithSpew(t *testing.T) {
|
|
||||||
v := reflect.ValueOf
|
|
||||||
|
|
||||||
a := v("a")
|
|
||||||
b := v("b")
|
|
||||||
c := v("c")
|
|
||||||
tests := []sortTestCase{
|
|
||||||
// Ints.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(2), v(1), v(3)},
|
|
||||||
[]reflect.Value{v(1), v(2), v(3)},
|
|
||||||
},
|
|
||||||
// Strings.
|
|
||||||
{
|
|
||||||
[]reflect.Value{b, a, c},
|
|
||||||
[]reflect.Value{a, b, c},
|
|
||||||
},
|
|
||||||
// SortableStructs.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
|
||||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
|
||||||
},
|
|
||||||
// UnsortableStructs.
|
|
||||||
{
|
|
||||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
|
||||||
[]reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
|
|
||||||
helpTestSortValues(tests, &cs, t)
|
|
||||||
}
|
|
||||||
306
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
306
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConfigState houses the configuration options used by spew to format and
|
|
||||||
// display values. There is a global instance, Config, that is used to control
|
|
||||||
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
|
||||||
// provides methods equivalent to the top-level functions.
|
|
||||||
//
|
|
||||||
// The zero value for ConfigState provides no indentation. You would typically
|
|
||||||
// want to set it to a space or a tab.
|
|
||||||
//
|
|
||||||
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
|
||||||
// with default settings. See the documentation of NewDefaultConfig for default
|
|
||||||
// values.
|
|
||||||
type ConfigState struct {
|
|
||||||
// Indent specifies the string to use for each indentation level. The
|
|
||||||
// global config instance that all top-level functions use set this to a
|
|
||||||
// single space by default. If you would like more indentation, you might
|
|
||||||
// set this to a tab with "\t" or perhaps two spaces with " ".
|
|
||||||
Indent string
|
|
||||||
|
|
||||||
// MaxDepth controls the maximum number of levels to descend into nested
|
|
||||||
// data structures. The default, 0, means there is no limit.
|
|
||||||
//
|
|
||||||
// NOTE: Circular data structures are properly detected, so it is not
|
|
||||||
// necessary to set this value unless you specifically want to limit deeply
|
|
||||||
// nested data structures.
|
|
||||||
MaxDepth int
|
|
||||||
|
|
||||||
// DisableMethods specifies whether or not error and Stringer interfaces are
|
|
||||||
// invoked for types that implement them.
|
|
||||||
DisableMethods bool
|
|
||||||
|
|
||||||
// DisablePointerMethods specifies whether or not to check for and invoke
|
|
||||||
// error and Stringer interfaces on types which only accept a pointer
|
|
||||||
// receiver when the current type is not a pointer.
|
|
||||||
//
|
|
||||||
// NOTE: This might be an unsafe action since calling one of these methods
|
|
||||||
// with a pointer receiver could technically mutate the value, however,
|
|
||||||
// in practice, types which choose to satisify an error or Stringer
|
|
||||||
// interface with a pointer receiver should not be mutating their state
|
|
||||||
// inside these interface methods. As a result, this option relies on
|
|
||||||
// access to the unsafe package, so it will not have any effect when
|
|
||||||
// running in environments without access to the unsafe package such as
|
|
||||||
// Google App Engine or with the "safe" build tag specified.
|
|
||||||
DisablePointerMethods bool
|
|
||||||
|
|
||||||
// DisablePointerAddresses specifies whether to disable the printing of
|
|
||||||
// pointer addresses. This is useful when diffing data structures in tests.
|
|
||||||
DisablePointerAddresses bool
|
|
||||||
|
|
||||||
// DisableCapacities specifies whether to disable the printing of capacities
|
|
||||||
// for arrays, slices, maps and channels. This is useful when diffing
|
|
||||||
// data structures in tests.
|
|
||||||
DisableCapacities bool
|
|
||||||
|
|
||||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
|
||||||
// a custom error or Stringer interface is invoked. The default, false,
|
|
||||||
// means it will print the results of invoking the custom error or Stringer
|
|
||||||
// interface and return immediately instead of continuing to recurse into
|
|
||||||
// the internals of the data type.
|
|
||||||
//
|
|
||||||
// NOTE: This flag does not have any effect if method invocation is disabled
|
|
||||||
// via the DisableMethods or DisablePointerMethods options.
|
|
||||||
ContinueOnMethod bool
|
|
||||||
|
|
||||||
// SortKeys specifies map keys should be sorted before being printed. Use
|
|
||||||
// this to have a more deterministic, diffable output. Note that only
|
|
||||||
// native types (bool, int, uint, floats, uintptr and string) and types
|
|
||||||
// that support the error or Stringer interfaces (if methods are
|
|
||||||
// enabled) are supported, with other types sorted according to the
|
|
||||||
// reflect.Value.String() output which guarantees display stability.
|
|
||||||
SortKeys bool
|
|
||||||
|
|
||||||
// SpewKeys specifies that, as a last resort attempt, map keys should
|
|
||||||
// be spewed to strings and sorted by those strings. This is only
|
|
||||||
// considered if SortKeys is true.
|
|
||||||
SpewKeys bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config is the active configuration of the top-level functions.
|
|
||||||
// The configuration can be changed by modifying the contents of spew.Config.
|
|
||||||
var Config = ConfigState{Indent: " "}
|
|
||||||
|
|
||||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the formatted string as a value that satisfies error. See NewFormatter
|
|
||||||
// for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
|
||||||
return fmt.Errorf(format, c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Fprint(w, c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Fprintf(w, format, c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Fprintln(w, c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Print(c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Printf(format, c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Println(c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the resulting string. See NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Sprint(a ...interface{}) string {
|
|
||||||
return fmt.Sprint(c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
|
||||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
|
||||||
// the resulting string. See NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
|
||||||
return fmt.Sprintf(format, c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
|
||||||
// were passed with a Formatter interface returned by c.NewFormatter. It
|
|
||||||
// returns the resulting string. See NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
|
||||||
func (c *ConfigState) Sprintln(a ...interface{}) string {
|
|
||||||
return fmt.Sprintln(c.convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
|
||||||
interface. As a result, it integrates cleanly with standard fmt package
|
|
||||||
printing functions. The formatter is useful for inline printing of smaller data
|
|
||||||
types similar to the standard %v format specifier.
|
|
||||||
|
|
||||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
|
||||||
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
|
|
||||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
|
||||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
|
||||||
the width and precision arguments (however they will still work on the format
|
|
||||||
specifiers not handled by the custom formatter).
|
|
||||||
|
|
||||||
Typically this function shouldn't be called directly. It is much easier to make
|
|
||||||
use of the custom formatter by calling one of the convenience functions such as
|
|
||||||
c.Printf, c.Println, or c.Printf.
|
|
||||||
*/
|
|
||||||
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
|
||||||
return newFormatter(c, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
|
||||||
// exactly the same as Dump.
|
|
||||||
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
|
||||||
fdump(c, w, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Dump displays the passed parameters to standard out with newlines, customizable
|
|
||||||
indentation, and additional debug information such as complete types and all
|
|
||||||
pointer addresses used to indirect to the final value. It provides the
|
|
||||||
following features over the built-in printing facilities provided by the fmt
|
|
||||||
package:
|
|
||||||
|
|
||||||
* Pointers are dereferenced and followed
|
|
||||||
* Circular data structures are detected and handled properly
|
|
||||||
* Custom Stringer/error interfaces are optionally invoked, including
|
|
||||||
on unexported types
|
|
||||||
* Custom types which only implement the Stringer/error interfaces via
|
|
||||||
a pointer receiver are optionally invoked when passing non-pointer
|
|
||||||
variables
|
|
||||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
|
||||||
includes offsets, byte values in hex, and ASCII output
|
|
||||||
|
|
||||||
The configuration options are controlled by modifying the public members
|
|
||||||
of c. See ConfigState for options documentation.
|
|
||||||
|
|
||||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
|
||||||
get the formatted result as a string.
|
|
||||||
*/
|
|
||||||
func (c *ConfigState) Dump(a ...interface{}) {
|
|
||||||
fdump(c, os.Stdout, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
|
||||||
// as Dump.
|
|
||||||
func (c *ConfigState) Sdump(a ...interface{}) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fdump(c, &buf, a...)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
|
||||||
// length with each argument converted to a spew Formatter interface using
|
|
||||||
// the ConfigState associated with s.
|
|
||||||
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
|
||||||
formatters = make([]interface{}, len(args))
|
|
||||||
for index, arg := range args {
|
|
||||||
formatters[index] = newFormatter(c, arg)
|
|
||||||
}
|
|
||||||
return formatters
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDefaultConfig returns a ConfigState with the following default settings.
|
|
||||||
//
|
|
||||||
// Indent: " "
|
|
||||||
// MaxDepth: 0
|
|
||||||
// DisableMethods: false
|
|
||||||
// DisablePointerMethods: false
|
|
||||||
// ContinueOnMethod: false
|
|
||||||
// SortKeys: false
|
|
||||||
func NewDefaultConfig() *ConfigState {
|
|
||||||
return &ConfigState{Indent: " "}
|
|
||||||
}
|
|
||||||
211
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
211
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
@ -1,211 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package spew implements a deep pretty printer for Go data structures to aid in
|
|
||||||
debugging.
|
|
||||||
|
|
||||||
A quick overview of the additional features spew provides over the built-in
|
|
||||||
printing facilities for Go data types are as follows:
|
|
||||||
|
|
||||||
* Pointers are dereferenced and followed
|
|
||||||
* Circular data structures are detected and handled properly
|
|
||||||
* Custom Stringer/error interfaces are optionally invoked, including
|
|
||||||
on unexported types
|
|
||||||
* Custom types which only implement the Stringer/error interfaces via
|
|
||||||
a pointer receiver are optionally invoked when passing non-pointer
|
|
||||||
variables
|
|
||||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
|
||||||
includes offsets, byte values in hex, and ASCII output (only when using
|
|
||||||
Dump style)
|
|
||||||
|
|
||||||
There are two different approaches spew allows for dumping Go data structures:
|
|
||||||
|
|
||||||
* Dump style which prints with newlines, customizable indentation,
|
|
||||||
and additional debug information such as types and all pointer addresses
|
|
||||||
used to indirect to the final value
|
|
||||||
* A custom Formatter interface that integrates cleanly with the standard fmt
|
|
||||||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
|
||||||
similar to the default %v while providing the additional functionality
|
|
||||||
outlined above and passing unsupported format verbs such as %x and %q
|
|
||||||
along to fmt
|
|
||||||
|
|
||||||
Quick Start
|
|
||||||
|
|
||||||
This section demonstrates how to quickly get started with spew. See the
|
|
||||||
sections below for further details on formatting and configuration options.
|
|
||||||
|
|
||||||
To dump a variable with full newlines, indentation, type, and pointer
|
|
||||||
information use Dump, Fdump, or Sdump:
|
|
||||||
spew.Dump(myVar1, myVar2, ...)
|
|
||||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
|
||||||
str := spew.Sdump(myVar1, myVar2, ...)
|
|
||||||
|
|
||||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
|
||||||
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
|
||||||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
|
||||||
%#+v (adds types and pointer addresses):
|
|
||||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
||||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
||||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
||||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
||||||
|
|
||||||
Configuration Options
|
|
||||||
|
|
||||||
Configuration of spew is handled by fields in the ConfigState type. For
|
|
||||||
convenience, all of the top-level functions use a global state available
|
|
||||||
via the spew.Config global.
|
|
||||||
|
|
||||||
It is also possible to create a ConfigState instance that provides methods
|
|
||||||
equivalent to the top-level functions. This allows concurrent configuration
|
|
||||||
options. See the ConfigState documentation for more details.
|
|
||||||
|
|
||||||
The following configuration options are available:
|
|
||||||
* Indent
|
|
||||||
String to use for each indentation level for Dump functions.
|
|
||||||
It is a single space by default. A popular alternative is "\t".
|
|
||||||
|
|
||||||
* MaxDepth
|
|
||||||
Maximum number of levels to descend into nested data structures.
|
|
||||||
There is no limit by default.
|
|
||||||
|
|
||||||
* DisableMethods
|
|
||||||
Disables invocation of error and Stringer interface methods.
|
|
||||||
Method invocation is enabled by default.
|
|
||||||
|
|
||||||
* DisablePointerMethods
|
|
||||||
Disables invocation of error and Stringer interface methods on types
|
|
||||||
which only accept pointer receivers from non-pointer variables.
|
|
||||||
Pointer method invocation is enabled by default.
|
|
||||||
|
|
||||||
* DisablePointerAddresses
|
|
||||||
DisablePointerAddresses specifies whether to disable the printing of
|
|
||||||
pointer addresses. This is useful when diffing data structures in tests.
|
|
||||||
|
|
||||||
* DisableCapacities
|
|
||||||
DisableCapacities specifies whether to disable the printing of
|
|
||||||
capacities for arrays, slices, maps and channels. This is useful when
|
|
||||||
diffing data structures in tests.
|
|
||||||
|
|
||||||
* ContinueOnMethod
|
|
||||||
Enables recursion into types after invoking error and Stringer interface
|
|
||||||
methods. Recursion after method invocation is disabled by default.
|
|
||||||
|
|
||||||
* SortKeys
|
|
||||||
Specifies map keys should be sorted before being printed. Use
|
|
||||||
this to have a more deterministic, diffable output. Note that
|
|
||||||
only native types (bool, int, uint, floats, uintptr and string)
|
|
||||||
and types which implement error or Stringer interfaces are
|
|
||||||
supported with other types sorted according to the
|
|
||||||
reflect.Value.String() output which guarantees display
|
|
||||||
stability. Natural map order is used by default.
|
|
||||||
|
|
||||||
* SpewKeys
|
|
||||||
Specifies that, as a last resort attempt, map keys should be
|
|
||||||
spewed to strings and sorted by those strings. This is only
|
|
||||||
considered if SortKeys is true.
|
|
||||||
|
|
||||||
Dump Usage
|
|
||||||
|
|
||||||
Simply call spew.Dump with a list of variables you want to dump:
|
|
||||||
|
|
||||||
spew.Dump(myVar1, myVar2, ...)
|
|
||||||
|
|
||||||
You may also call spew.Fdump if you would prefer to output to an arbitrary
|
|
||||||
io.Writer. For example, to dump to standard error:
|
|
||||||
|
|
||||||
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
|
|
||||||
|
|
||||||
A third option is to call spew.Sdump to get the formatted output as a string:
|
|
||||||
|
|
||||||
str := spew.Sdump(myVar1, myVar2, ...)
|
|
||||||
|
|
||||||
Sample Dump Output
|
|
||||||
|
|
||||||
See the Dump example for details on the setup of the types and variables being
|
|
||||||
shown here.
|
|
||||||
|
|
||||||
(main.Foo) {
|
|
||||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
|
||||||
flag: (main.Flag) flagTwo,
|
|
||||||
data: (uintptr) <nil>
|
|
||||||
}),
|
|
||||||
ExportedField: (map[interface {}]interface {}) (len=1) {
|
|
||||||
(string) (len=3) "one": (bool) true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
|
||||||
command as shown.
|
|
||||||
([]uint8) (len=32 cap=32) {
|
|
||||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
|
||||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
|
||||||
00000020 31 32 |12|
|
|
||||||
}
|
|
||||||
|
|
||||||
Custom Formatter
|
|
||||||
|
|
||||||
Spew provides a custom formatter that implements the fmt.Formatter interface
|
|
||||||
so that it integrates cleanly with standard fmt package printing functions. The
|
|
||||||
formatter is useful for inline printing of smaller data types similar to the
|
|
||||||
standard %v format specifier.
|
|
||||||
|
|
||||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
|
||||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
|
||||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
|
||||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
|
||||||
the width and precision arguments (however they will still work on the format
|
|
||||||
specifiers not handled by the custom formatter).
|
|
||||||
|
|
||||||
Custom Formatter Usage
|
|
||||||
|
|
||||||
The simplest way to make use of the spew custom formatter is to call one of the
|
|
||||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
|
||||||
functions have syntax you are most likely already familiar with:
|
|
||||||
|
|
||||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
||||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
||||||
spew.Println(myVar, myVar2)
|
|
||||||
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
|
||||||
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
|
||||||
|
|
||||||
See the Index for the full list convenience functions.
|
|
||||||
|
|
||||||
Sample Formatter Output
|
|
||||||
|
|
||||||
Double pointer to a uint8:
|
|
||||||
%v: <**>5
|
|
||||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
|
||||||
%#v: (**uint8)5
|
|
||||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
|
||||||
|
|
||||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
|
||||||
%v: <*>{1 <*><shown>}
|
|
||||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
|
||||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
|
||||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
|
||||||
|
|
||||||
See the Printf example for details on the setup of variables being shown
|
|
||||||
here.
|
|
||||||
|
|
||||||
Errors
|
|
||||||
|
|
||||||
Since it is possible for custom Stringer/error interfaces to panic, spew
|
|
||||||
detects them and handles them internally by printing the panic information
|
|
||||||
inline with the output. Since spew is intended to provide deep pretty printing
|
|
||||||
capabilities on structures, it intentionally does not return any errors.
|
|
||||||
*/
|
|
||||||
package spew
|
|
||||||
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
@ -1,509 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// uint8Type is a reflect.Type representing a uint8. It is used to
|
|
||||||
// convert cgo types to uint8 slices for hexdumping.
|
|
||||||
uint8Type = reflect.TypeOf(uint8(0))
|
|
||||||
|
|
||||||
// cCharRE is a regular expression that matches a cgo char.
|
|
||||||
// It is used to detect character arrays to hexdump them.
|
|
||||||
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
|
||||||
|
|
||||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
|
||||||
// char. It is used to detect unsigned character arrays to hexdump
|
|
||||||
// them.
|
|
||||||
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
|
||||||
|
|
||||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
|
||||||
// It is used to detect uint8_t arrays to hexdump them.
|
|
||||||
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// dumpState contains information about the state of a dump operation.
|
|
||||||
type dumpState struct {
|
|
||||||
w io.Writer
|
|
||||||
depth int
|
|
||||||
pointers map[uintptr]int
|
|
||||||
ignoreNextType bool
|
|
||||||
ignoreNextIndent bool
|
|
||||||
cs *ConfigState
|
|
||||||
}
|
|
||||||
|
|
||||||
// indent performs indentation according to the depth level and cs.Indent
|
|
||||||
// option.
|
|
||||||
func (d *dumpState) indent() {
|
|
||||||
if d.ignoreNextIndent {
|
|
||||||
d.ignoreNextIndent = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackValue returns values inside of non-nil interfaces when possible.
|
|
||||||
// This is useful for data types like structs, arrays, slices, and maps which
|
|
||||||
// can contain varying types packed inside an interface.
|
|
||||||
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
|
||||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
|
||||||
func (d *dumpState) dumpPtr(v reflect.Value) {
|
|
||||||
// Remove pointers at or below the current depth from map used to detect
|
|
||||||
// circular refs.
|
|
||||||
for k, depth := range d.pointers {
|
|
||||||
if depth >= d.depth {
|
|
||||||
delete(d.pointers, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep list of all dereferenced pointers to show later.
|
|
||||||
pointerChain := make([]uintptr, 0)
|
|
||||||
|
|
||||||
// Figure out how many levels of indirection there are by dereferencing
|
|
||||||
// pointers and unpacking interfaces down the chain while detecting circular
|
|
||||||
// references.
|
|
||||||
nilFound := false
|
|
||||||
cycleFound := false
|
|
||||||
indirects := 0
|
|
||||||
ve := v
|
|
||||||
for ve.Kind() == reflect.Ptr {
|
|
||||||
if ve.IsNil() {
|
|
||||||
nilFound = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
indirects++
|
|
||||||
addr := ve.Pointer()
|
|
||||||
pointerChain = append(pointerChain, addr)
|
|
||||||
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
|
||||||
cycleFound = true
|
|
||||||
indirects--
|
|
||||||
break
|
|
||||||
}
|
|
||||||
d.pointers[addr] = d.depth
|
|
||||||
|
|
||||||
ve = ve.Elem()
|
|
||||||
if ve.Kind() == reflect.Interface {
|
|
||||||
if ve.IsNil() {
|
|
||||||
nilFound = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ve = ve.Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display type information.
|
|
||||||
d.w.Write(openParenBytes)
|
|
||||||
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
|
||||||
d.w.Write([]byte(ve.Type().String()))
|
|
||||||
d.w.Write(closeParenBytes)
|
|
||||||
|
|
||||||
// Display pointer information.
|
|
||||||
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
|
||||||
d.w.Write(openParenBytes)
|
|
||||||
for i, addr := range pointerChain {
|
|
||||||
if i > 0 {
|
|
||||||
d.w.Write(pointerChainBytes)
|
|
||||||
}
|
|
||||||
printHexPtr(d.w, addr)
|
|
||||||
}
|
|
||||||
d.w.Write(closeParenBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display dereferenced value.
|
|
||||||
d.w.Write(openParenBytes)
|
|
||||||
switch {
|
|
||||||
case nilFound:
|
|
||||||
d.w.Write(nilAngleBytes)
|
|
||||||
|
|
||||||
case cycleFound:
|
|
||||||
d.w.Write(circularBytes)
|
|
||||||
|
|
||||||
default:
|
|
||||||
d.ignoreNextType = true
|
|
||||||
d.dump(ve)
|
|
||||||
}
|
|
||||||
d.w.Write(closeParenBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
|
||||||
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
|
||||||
func (d *dumpState) dumpSlice(v reflect.Value) {
|
|
||||||
// Determine whether this type should be hex dumped or not. Also,
|
|
||||||
// for types which should be hexdumped, try to use the underlying data
|
|
||||||
// first, then fall back to trying to convert them to a uint8 slice.
|
|
||||||
var buf []uint8
|
|
||||||
doConvert := false
|
|
||||||
doHexDump := false
|
|
||||||
numEntries := v.Len()
|
|
||||||
if numEntries > 0 {
|
|
||||||
vt := v.Index(0).Type()
|
|
||||||
vts := vt.String()
|
|
||||||
switch {
|
|
||||||
// C types that need to be converted.
|
|
||||||
case cCharRE.MatchString(vts):
|
|
||||||
fallthrough
|
|
||||||
case cUnsignedCharRE.MatchString(vts):
|
|
||||||
fallthrough
|
|
||||||
case cUint8tCharRE.MatchString(vts):
|
|
||||||
doConvert = true
|
|
||||||
|
|
||||||
// Try to use existing uint8 slices and fall back to converting
|
|
||||||
// and copying if that fails.
|
|
||||||
case vt.Kind() == reflect.Uint8:
|
|
||||||
// We need an addressable interface to convert the type
|
|
||||||
// to a byte slice. However, the reflect package won't
|
|
||||||
// give us an interface on certain things like
|
|
||||||
// unexported struct fields in order to enforce
|
|
||||||
// visibility rules. We use unsafe, when available, to
|
|
||||||
// bypass these restrictions since this package does not
|
|
||||||
// mutate the values.
|
|
||||||
vs := v
|
|
||||||
if !vs.CanInterface() || !vs.CanAddr() {
|
|
||||||
vs = unsafeReflectValue(vs)
|
|
||||||
}
|
|
||||||
if !UnsafeDisabled {
|
|
||||||
vs = vs.Slice(0, numEntries)
|
|
||||||
|
|
||||||
// Use the existing uint8 slice if it can be
|
|
||||||
// type asserted.
|
|
||||||
iface := vs.Interface()
|
|
||||||
if slice, ok := iface.([]uint8); ok {
|
|
||||||
buf = slice
|
|
||||||
doHexDump = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The underlying data needs to be converted if it can't
|
|
||||||
// be type asserted to a uint8 slice.
|
|
||||||
doConvert = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy and convert the underlying type if needed.
|
|
||||||
if doConvert && vt.ConvertibleTo(uint8Type) {
|
|
||||||
// Convert and copy each element into a uint8 byte
|
|
||||||
// slice.
|
|
||||||
buf = make([]uint8, numEntries)
|
|
||||||
for i := 0; i < numEntries; i++ {
|
|
||||||
vv := v.Index(i)
|
|
||||||
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
|
||||||
}
|
|
||||||
doHexDump = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hexdump the entire slice as needed.
|
|
||||||
if doHexDump {
|
|
||||||
indent := strings.Repeat(d.cs.Indent, d.depth)
|
|
||||||
str := indent + hex.Dump(buf)
|
|
||||||
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
|
||||||
str = strings.TrimRight(str, d.cs.Indent)
|
|
||||||
d.w.Write([]byte(str))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively call dump for each item.
|
|
||||||
for i := 0; i < numEntries; i++ {
|
|
||||||
d.dump(d.unpackValue(v.Index(i)))
|
|
||||||
if i < (numEntries - 1) {
|
|
||||||
d.w.Write(commaNewlineBytes)
|
|
||||||
} else {
|
|
||||||
d.w.Write(newlineBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
|
||||||
// value to figure out what kind of object we are dealing with and formats it
|
|
||||||
// appropriately. It is a recursive function, however circular data structures
|
|
||||||
// are detected and handled properly.
|
|
||||||
func (d *dumpState) dump(v reflect.Value) {
|
|
||||||
// Handle invalid reflect values immediately.
|
|
||||||
kind := v.Kind()
|
|
||||||
if kind == reflect.Invalid {
|
|
||||||
d.w.Write(invalidAngleBytes)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle pointers specially.
|
|
||||||
if kind == reflect.Ptr {
|
|
||||||
d.indent()
|
|
||||||
d.dumpPtr(v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print type information unless already handled elsewhere.
|
|
||||||
if !d.ignoreNextType {
|
|
||||||
d.indent()
|
|
||||||
d.w.Write(openParenBytes)
|
|
||||||
d.w.Write([]byte(v.Type().String()))
|
|
||||||
d.w.Write(closeParenBytes)
|
|
||||||
d.w.Write(spaceBytes)
|
|
||||||
}
|
|
||||||
d.ignoreNextType = false
|
|
||||||
|
|
||||||
// Display length and capacity if the built-in len and cap functions
|
|
||||||
// work with the value's kind and the len/cap itself is non-zero.
|
|
||||||
valueLen, valueCap := 0, 0
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice, reflect.Chan:
|
|
||||||
valueLen, valueCap = v.Len(), v.Cap()
|
|
||||||
case reflect.Map, reflect.String:
|
|
||||||
valueLen = v.Len()
|
|
||||||
}
|
|
||||||
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
|
||||||
d.w.Write(openParenBytes)
|
|
||||||
if valueLen != 0 {
|
|
||||||
d.w.Write(lenEqualsBytes)
|
|
||||||
printInt(d.w, int64(valueLen), 10)
|
|
||||||
}
|
|
||||||
if !d.cs.DisableCapacities && valueCap != 0 {
|
|
||||||
if valueLen != 0 {
|
|
||||||
d.w.Write(spaceBytes)
|
|
||||||
}
|
|
||||||
d.w.Write(capEqualsBytes)
|
|
||||||
printInt(d.w, int64(valueCap), 10)
|
|
||||||
}
|
|
||||||
d.w.Write(closeParenBytes)
|
|
||||||
d.w.Write(spaceBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call Stringer/error interfaces if they exist and the handle methods flag
|
|
||||||
// is enabled
|
|
||||||
if !d.cs.DisableMethods {
|
|
||||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
|
||||||
if handled := handleMethods(d.cs, d.w, v); handled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case reflect.Invalid:
|
|
||||||
// Do nothing. We should never get here since invalid has already
|
|
||||||
// been handled above.
|
|
||||||
|
|
||||||
case reflect.Bool:
|
|
||||||
printBool(d.w, v.Bool())
|
|
||||||
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
printInt(d.w, v.Int(), 10)
|
|
||||||
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
||||||
printUint(d.w, v.Uint(), 10)
|
|
||||||
|
|
||||||
case reflect.Float32:
|
|
||||||
printFloat(d.w, v.Float(), 32)
|
|
||||||
|
|
||||||
case reflect.Float64:
|
|
||||||
printFloat(d.w, v.Float(), 64)
|
|
||||||
|
|
||||||
case reflect.Complex64:
|
|
||||||
printComplex(d.w, v.Complex(), 32)
|
|
||||||
|
|
||||||
case reflect.Complex128:
|
|
||||||
printComplex(d.w, v.Complex(), 64)
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
if v.IsNil() {
|
|
||||||
d.w.Write(nilAngleBytes)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case reflect.Array:
|
|
||||||
d.w.Write(openBraceNewlineBytes)
|
|
||||||
d.depth++
|
|
||||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
||||||
d.indent()
|
|
||||||
d.w.Write(maxNewlineBytes)
|
|
||||||
} else {
|
|
||||||
d.dumpSlice(v)
|
|
||||||
}
|
|
||||||
d.depth--
|
|
||||||
d.indent()
|
|
||||||
d.w.Write(closeBraceBytes)
|
|
||||||
|
|
||||||
case reflect.String:
|
|
||||||
d.w.Write([]byte(strconv.Quote(v.String())))
|
|
||||||
|
|
||||||
case reflect.Interface:
|
|
||||||
// The only time we should get here is for nil interfaces due to
|
|
||||||
// unpackValue calls.
|
|
||||||
if v.IsNil() {
|
|
||||||
d.w.Write(nilAngleBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Ptr:
|
|
||||||
// Do nothing. We should never get here since pointers have already
|
|
||||||
// been handled above.
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
// nil maps should be indicated as different than empty maps
|
|
||||||
if v.IsNil() {
|
|
||||||
d.w.Write(nilAngleBytes)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
d.w.Write(openBraceNewlineBytes)
|
|
||||||
d.depth++
|
|
||||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
||||||
d.indent()
|
|
||||||
d.w.Write(maxNewlineBytes)
|
|
||||||
} else {
|
|
||||||
numEntries := v.Len()
|
|
||||||
keys := v.MapKeys()
|
|
||||||
if d.cs.SortKeys {
|
|
||||||
sortValues(keys, d.cs)
|
|
||||||
}
|
|
||||||
for i, key := range keys {
|
|
||||||
d.dump(d.unpackValue(key))
|
|
||||||
d.w.Write(colonSpaceBytes)
|
|
||||||
d.ignoreNextIndent = true
|
|
||||||
d.dump(d.unpackValue(v.MapIndex(key)))
|
|
||||||
if i < (numEntries - 1) {
|
|
||||||
d.w.Write(commaNewlineBytes)
|
|
||||||
} else {
|
|
||||||
d.w.Write(newlineBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.depth--
|
|
||||||
d.indent()
|
|
||||||
d.w.Write(closeBraceBytes)
|
|
||||||
|
|
||||||
case reflect.Struct:
|
|
||||||
d.w.Write(openBraceNewlineBytes)
|
|
||||||
d.depth++
|
|
||||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
|
||||||
d.indent()
|
|
||||||
d.w.Write(maxNewlineBytes)
|
|
||||||
} else {
|
|
||||||
vt := v.Type()
|
|
||||||
numFields := v.NumField()
|
|
||||||
for i := 0; i < numFields; i++ {
|
|
||||||
d.indent()
|
|
||||||
vtf := vt.Field(i)
|
|
||||||
d.w.Write([]byte(vtf.Name))
|
|
||||||
d.w.Write(colonSpaceBytes)
|
|
||||||
d.ignoreNextIndent = true
|
|
||||||
d.dump(d.unpackValue(v.Field(i)))
|
|
||||||
if i < (numFields - 1) {
|
|
||||||
d.w.Write(commaNewlineBytes)
|
|
||||||
} else {
|
|
||||||
d.w.Write(newlineBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.depth--
|
|
||||||
d.indent()
|
|
||||||
d.w.Write(closeBraceBytes)
|
|
||||||
|
|
||||||
case reflect.Uintptr:
|
|
||||||
printHexPtr(d.w, uintptr(v.Uint()))
|
|
||||||
|
|
||||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
|
||||||
printHexPtr(d.w, v.Pointer())
|
|
||||||
|
|
||||||
// There were not any other types at the time this code was written, but
|
|
||||||
// fall back to letting the default fmt package handle it in case any new
|
|
||||||
// types are added.
|
|
||||||
default:
|
|
||||||
if v.CanInterface() {
|
|
||||||
fmt.Fprintf(d.w, "%v", v.Interface())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(d.w, "%v", v.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fdump is a helper function to consolidate the logic from the various public
|
|
||||||
// methods which take varying writers and config states.
|
|
||||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
|
||||||
for _, arg := range a {
|
|
||||||
if arg == nil {
|
|
||||||
w.Write(interfaceBytes)
|
|
||||||
w.Write(spaceBytes)
|
|
||||||
w.Write(nilAngleBytes)
|
|
||||||
w.Write(newlineBytes)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
d := dumpState{w: w, cs: cs}
|
|
||||||
d.pointers = make(map[uintptr]int)
|
|
||||||
d.dump(reflect.ValueOf(arg))
|
|
||||||
d.w.Write(newlineBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
|
||||||
// exactly the same as Dump.
|
|
||||||
func Fdump(w io.Writer, a ...interface{}) {
|
|
||||||
fdump(&Config, w, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
|
||||||
// as Dump.
|
|
||||||
func Sdump(a ...interface{}) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fdump(&Config, &buf, a...)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Dump displays the passed parameters to standard out with newlines, customizable
|
|
||||||
indentation, and additional debug information such as complete types and all
|
|
||||||
pointer addresses used to indirect to the final value. It provides the
|
|
||||||
following features over the built-in printing facilities provided by the fmt
|
|
||||||
package:
|
|
||||||
|
|
||||||
* Pointers are dereferenced and followed
|
|
||||||
* Circular data structures are detected and handled properly
|
|
||||||
* Custom Stringer/error interfaces are optionally invoked, including
|
|
||||||
on unexported types
|
|
||||||
* Custom types which only implement the Stringer/error interfaces via
|
|
||||||
a pointer receiver are optionally invoked when passing non-pointer
|
|
||||||
variables
|
|
||||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
|
||||||
includes offsets, byte values in hex, and ASCII output
|
|
||||||
|
|
||||||
The configuration options are controlled by an exported package global,
|
|
||||||
spew.Config. See ConfigState for options documentation.
|
|
||||||
|
|
||||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
|
||||||
get the formatted result as a string.
|
|
||||||
*/
|
|
||||||
func Dump(a ...interface{}) {
|
|
||||||
fdump(&Config, os.Stdout, a...)
|
|
||||||
}
|
|
||||||
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
101
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
101
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
@ -1,101 +0,0 @@
|
|||||||
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|
||||||
// when both cgo is supported and "-tags testcgo" is added to the go test
|
|
||||||
// command line. This means the cgo tests are only added (and hence run) when
|
|
||||||
// specifially requested. This configuration is used because spew itself
|
|
||||||
// does not require cgo to run even though it does handle certain cgo types
|
|
||||||
// specially. Rather than forcing all clients to require cgo and an external
|
|
||||||
// C compiler just to run the tests, this scheme makes them optional.
|
|
||||||
// +build cgo,testcgo
|
|
||||||
|
|
||||||
package spew_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func addCgoDumpTests() {
|
|
||||||
// C char pointer.
|
|
||||||
v := testdata.GetCgoCharPointer()
|
|
||||||
nv := testdata.GetCgoNullCharPointer()
|
|
||||||
pv := &v
|
|
||||||
vcAddr := fmt.Sprintf("%p", v)
|
|
||||||
vAddr := fmt.Sprintf("%p", pv)
|
|
||||||
pvAddr := fmt.Sprintf("%p", &pv)
|
|
||||||
vt := "*testdata._Ctype_char"
|
|
||||||
vs := "116"
|
|
||||||
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
|
|
||||||
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
|
|
||||||
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
|
|
||||||
addDumpTest(nv, "("+vt+")(<nil>)\n")
|
|
||||||
|
|
||||||
// C char array.
|
|
||||||
v2, v2l, v2c := testdata.GetCgoCharArray()
|
|
||||||
v2Len := fmt.Sprintf("%d", v2l)
|
|
||||||
v2Cap := fmt.Sprintf("%d", v2c)
|
|
||||||
v2t := "[6]testdata._Ctype_char"
|
|
||||||
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
|
|
||||||
"{\n 00000000 74 65 73 74 32 00 " +
|
|
||||||
" |test2.|\n}"
|
|
||||||
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
|
|
||||||
|
|
||||||
// C unsigned char array.
|
|
||||||
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
|
|
||||||
v3Len := fmt.Sprintf("%d", v3l)
|
|
||||||
v3Cap := fmt.Sprintf("%d", v3c)
|
|
||||||
v3t := "[6]testdata._Ctype_unsignedchar"
|
|
||||||
v3t2 := "[6]testdata._Ctype_uchar"
|
|
||||||
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
|
|
||||||
"{\n 00000000 74 65 73 74 33 00 " +
|
|
||||||
" |test3.|\n}"
|
|
||||||
addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n")
|
|
||||||
|
|
||||||
// C signed char array.
|
|
||||||
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
|
|
||||||
v4Len := fmt.Sprintf("%d", v4l)
|
|
||||||
v4Cap := fmt.Sprintf("%d", v4c)
|
|
||||||
v4t := "[6]testdata._Ctype_schar"
|
|
||||||
v4t2 := "testdata._Ctype_schar"
|
|
||||||
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
|
|
||||||
"{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
|
|
||||||
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
|
|
||||||
") 0\n}"
|
|
||||||
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
|
|
||||||
|
|
||||||
// C uint8_t array.
|
|
||||||
v5, v5l, v5c := testdata.GetCgoUint8tArray()
|
|
||||||
v5Len := fmt.Sprintf("%d", v5l)
|
|
||||||
v5Cap := fmt.Sprintf("%d", v5c)
|
|
||||||
v5t := "[6]testdata._Ctype_uint8_t"
|
|
||||||
v5t2 := "[6]testdata._Ctype_uchar"
|
|
||||||
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
|
|
||||||
"{\n 00000000 74 65 73 74 35 00 " +
|
|
||||||
" |test5.|\n}"
|
|
||||||
addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n")
|
|
||||||
|
|
||||||
// C typedefed unsigned char array.
|
|
||||||
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
|
|
||||||
v6Len := fmt.Sprintf("%d", v6l)
|
|
||||||
v6Cap := fmt.Sprintf("%d", v6c)
|
|
||||||
v6t := "[6]testdata._Ctype_custom_uchar_t"
|
|
||||||
v6t2 := "[6]testdata._Ctype_uchar"
|
|
||||||
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
|
|
||||||
"{\n 00000000 74 65 73 74 36 00 " +
|
|
||||||
" |test6.|\n}"
|
|
||||||
addDumpTest(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n")
|
|
||||||
}
|
|
||||||
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
@ -1,26 +0,0 @@
|
|||||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|
||||||
// when either cgo is not supported or "-tags testcgo" is not added to the go
|
|
||||||
// test command line. This file intentionally does not setup any cgo tests in
|
|
||||||
// this scenario.
|
|
||||||
// +build !cgo !testcgo
|
|
||||||
|
|
||||||
package spew_test
|
|
||||||
|
|
||||||
func addCgoDumpTests() {
|
|
||||||
// Don't add any tests for cgo since this file is only compiled when
|
|
||||||
// there should not be any cgo tests.
|
|
||||||
}
|
|
||||||
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
@ -1,226 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Flag int
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagOne Flag = iota
|
|
||||||
flagTwo
|
|
||||||
)
|
|
||||||
|
|
||||||
var flagStrings = map[Flag]string{
|
|
||||||
flagOne: "flagOne",
|
|
||||||
flagTwo: "flagTwo",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Flag) String() string {
|
|
||||||
if s, ok := flagStrings[f]; ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bar struct {
|
|
||||||
data uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
type Foo struct {
|
|
||||||
unexportedField Bar
|
|
||||||
ExportedField map[interface{}]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to use Dump to dump variables to stdout.
|
|
||||||
func ExampleDump() {
|
|
||||||
// The following package level declarations are assumed for this example:
|
|
||||||
/*
|
|
||||||
type Flag int
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagOne Flag = iota
|
|
||||||
flagTwo
|
|
||||||
)
|
|
||||||
|
|
||||||
var flagStrings = map[Flag]string{
|
|
||||||
flagOne: "flagOne",
|
|
||||||
flagTwo: "flagTwo",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Flag) String() string {
|
|
||||||
if s, ok := flagStrings[f]; ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bar struct {
|
|
||||||
data uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
type Foo struct {
|
|
||||||
unexportedField Bar
|
|
||||||
ExportedField map[interface{}]interface{}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Setup some sample data structures for the example.
|
|
||||||
bar := Bar{uintptr(0)}
|
|
||||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
|
||||||
f := Flag(5)
|
|
||||||
b := []byte{
|
|
||||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
|
||||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
|
||||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
|
||||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
|
||||||
0x31, 0x32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump!
|
|
||||||
spew.Dump(s1, f, b)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// (spew_test.Foo) {
|
|
||||||
// unexportedField: (spew_test.Bar) {
|
|
||||||
// data: (uintptr) <nil>
|
|
||||||
// },
|
|
||||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
|
||||||
// (string) (len=3) "one": (bool) true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// (spew_test.Flag) Unknown flag (5)
|
|
||||||
// ([]uint8) (len=34 cap=34) {
|
|
||||||
// 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
|
||||||
// 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
|
||||||
// 00000020 31 32 |12|
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to use Printf to display a variable with a
|
|
||||||
// format string and inline formatting.
|
|
||||||
func ExamplePrintf() {
|
|
||||||
// Create a double pointer to a uint 8.
|
|
||||||
ui8 := uint8(5)
|
|
||||||
pui8 := &ui8
|
|
||||||
ppui8 := &pui8
|
|
||||||
|
|
||||||
// Create a circular data type.
|
|
||||||
type circular struct {
|
|
||||||
ui8 uint8
|
|
||||||
c *circular
|
|
||||||
}
|
|
||||||
c := circular{ui8: 1}
|
|
||||||
c.c = &c
|
|
||||||
|
|
||||||
// Print!
|
|
||||||
spew.Printf("ppui8: %v\n", ppui8)
|
|
||||||
spew.Printf("circular: %v\n", c)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// ppui8: <**>5
|
|
||||||
// circular: {1 <*>{1 <*><shown>}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to use a ConfigState.
|
|
||||||
func ExampleConfigState() {
|
|
||||||
// Modify the indent level of the ConfigState only. The global
|
|
||||||
// configuration is not modified.
|
|
||||||
scs := spew.ConfigState{Indent: "\t"}
|
|
||||||
|
|
||||||
// Output using the ConfigState instance.
|
|
||||||
v := map[string]int{"one": 1}
|
|
||||||
scs.Printf("v: %v\n", v)
|
|
||||||
scs.Dump(v)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// v: map[one:1]
|
|
||||||
// (map[string]int) (len=1) {
|
|
||||||
// (string) (len=3) "one": (int) 1
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to use ConfigState.Dump to dump variables to
|
|
||||||
// stdout
|
|
||||||
func ExampleConfigState_Dump() {
|
|
||||||
// See the top-level Dump example for details on the types used in this
|
|
||||||
// example.
|
|
||||||
|
|
||||||
// Create two ConfigState instances with different indentation.
|
|
||||||
scs := spew.ConfigState{Indent: "\t"}
|
|
||||||
scs2 := spew.ConfigState{Indent: " "}
|
|
||||||
|
|
||||||
// Setup some sample data structures for the example.
|
|
||||||
bar := Bar{uintptr(0)}
|
|
||||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
|
||||||
|
|
||||||
// Dump using the ConfigState instances.
|
|
||||||
scs.Dump(s1)
|
|
||||||
scs2.Dump(s1)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// (spew_test.Foo) {
|
|
||||||
// unexportedField: (spew_test.Bar) {
|
|
||||||
// data: (uintptr) <nil>
|
|
||||||
// },
|
|
||||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
|
||||||
// (string) (len=3) "one": (bool) true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// (spew_test.Foo) {
|
|
||||||
// unexportedField: (spew_test.Bar) {
|
|
||||||
// data: (uintptr) <nil>
|
|
||||||
// },
|
|
||||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
|
||||||
// (string) (len=3) "one": (bool) true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to use ConfigState.Printf to display a variable
|
|
||||||
// with a format string and inline formatting.
|
|
||||||
func ExampleConfigState_Printf() {
|
|
||||||
// See the top-level Dump example for details on the types used in this
|
|
||||||
// example.
|
|
||||||
|
|
||||||
// Create two ConfigState instances and modify the method handling of the
|
|
||||||
// first ConfigState only.
|
|
||||||
scs := spew.NewDefaultConfig()
|
|
||||||
scs2 := spew.NewDefaultConfig()
|
|
||||||
scs.DisableMethods = true
|
|
||||||
|
|
||||||
// Alternatively
|
|
||||||
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
|
|
||||||
// scs2 := spew.ConfigState{Indent: " "}
|
|
||||||
|
|
||||||
// This is of type Flag which implements a Stringer and has raw value 1.
|
|
||||||
f := flagTwo
|
|
||||||
|
|
||||||
// Dump using the ConfigState instances.
|
|
||||||
scs.Printf("f: %v\n", f)
|
|
||||||
scs2.Printf("f: %v\n", f)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// f: 1
|
|
||||||
// f: flagTwo
|
|
||||||
}
|
|
||||||
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
@ -1,419 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// supportedFlags is a list of all the character flags supported by fmt package.
|
|
||||||
const supportedFlags = "0-+# "
|
|
||||||
|
|
||||||
// formatState implements the fmt.Formatter interface and contains information
|
|
||||||
// about the state of a formatting operation. The NewFormatter function can
|
|
||||||
// be used to get a new Formatter which can be used directly as arguments
|
|
||||||
// in standard fmt package printing calls.
|
|
||||||
type formatState struct {
|
|
||||||
value interface{}
|
|
||||||
fs fmt.State
|
|
||||||
depth int
|
|
||||||
pointers map[uintptr]int
|
|
||||||
ignoreNextType bool
|
|
||||||
cs *ConfigState
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildDefaultFormat recreates the original format string without precision
|
|
||||||
// and width information to pass in to fmt.Sprintf in the case of an
|
|
||||||
// unrecognized type. Unless new types are added to the language, this
|
|
||||||
// function won't ever be called.
|
|
||||||
func (f *formatState) buildDefaultFormat() (format string) {
|
|
||||||
buf := bytes.NewBuffer(percentBytes)
|
|
||||||
|
|
||||||
for _, flag := range supportedFlags {
|
|
||||||
if f.fs.Flag(int(flag)) {
|
|
||||||
buf.WriteRune(flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune('v')
|
|
||||||
|
|
||||||
format = buf.String()
|
|
||||||
return format
|
|
||||||
}
|
|
||||||
|
|
||||||
// constructOrigFormat recreates the original format string including precision
|
|
||||||
// and width information to pass along to the standard fmt package. This allows
|
|
||||||
// automatic deferral of all format strings this package doesn't support.
|
|
||||||
func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
|
||||||
buf := bytes.NewBuffer(percentBytes)
|
|
||||||
|
|
||||||
for _, flag := range supportedFlags {
|
|
||||||
if f.fs.Flag(int(flag)) {
|
|
||||||
buf.WriteRune(flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if width, ok := f.fs.Width(); ok {
|
|
||||||
buf.WriteString(strconv.Itoa(width))
|
|
||||||
}
|
|
||||||
|
|
||||||
if precision, ok := f.fs.Precision(); ok {
|
|
||||||
buf.Write(precisionBytes)
|
|
||||||
buf.WriteString(strconv.Itoa(precision))
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteRune(verb)
|
|
||||||
|
|
||||||
format = buf.String()
|
|
||||||
return format
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackValue returns values inside of non-nil interfaces when possible and
|
|
||||||
// ensures that types for values which have been unpacked from an interface
|
|
||||||
// are displayed when the show types flag is also set.
|
|
||||||
// This is useful for data types like structs, arrays, slices, and maps which
|
|
||||||
// can contain varying types packed inside an interface.
|
|
||||||
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
|
||||||
if v.Kind() == reflect.Interface {
|
|
||||||
f.ignoreNextType = false
|
|
||||||
if !v.IsNil() {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
|
||||||
func (f *formatState) formatPtr(v reflect.Value) {
|
|
||||||
// Display nil if top level pointer is nil.
|
|
||||||
showTypes := f.fs.Flag('#')
|
|
||||||
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
|
||||||
f.fs.Write(nilAngleBytes)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove pointers at or below the current depth from map used to detect
|
|
||||||
// circular refs.
|
|
||||||
for k, depth := range f.pointers {
|
|
||||||
if depth >= f.depth {
|
|
||||||
delete(f.pointers, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep list of all dereferenced pointers to possibly show later.
|
|
||||||
pointerChain := make([]uintptr, 0)
|
|
||||||
|
|
||||||
// Figure out how many levels of indirection there are by derferencing
|
|
||||||
// pointers and unpacking interfaces down the chain while detecting circular
|
|
||||||
// references.
|
|
||||||
nilFound := false
|
|
||||||
cycleFound := false
|
|
||||||
indirects := 0
|
|
||||||
ve := v
|
|
||||||
for ve.Kind() == reflect.Ptr {
|
|
||||||
if ve.IsNil() {
|
|
||||||
nilFound = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
indirects++
|
|
||||||
addr := ve.Pointer()
|
|
||||||
pointerChain = append(pointerChain, addr)
|
|
||||||
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
|
||||||
cycleFound = true
|
|
||||||
indirects--
|
|
||||||
break
|
|
||||||
}
|
|
||||||
f.pointers[addr] = f.depth
|
|
||||||
|
|
||||||
ve = ve.Elem()
|
|
||||||
if ve.Kind() == reflect.Interface {
|
|
||||||
if ve.IsNil() {
|
|
||||||
nilFound = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ve = ve.Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display type or indirection level depending on flags.
|
|
||||||
if showTypes && !f.ignoreNextType {
|
|
||||||
f.fs.Write(openParenBytes)
|
|
||||||
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
|
||||||
f.fs.Write([]byte(ve.Type().String()))
|
|
||||||
f.fs.Write(closeParenBytes)
|
|
||||||
} else {
|
|
||||||
if nilFound || cycleFound {
|
|
||||||
indirects += strings.Count(ve.Type().String(), "*")
|
|
||||||
}
|
|
||||||
f.fs.Write(openAngleBytes)
|
|
||||||
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
|
||||||
f.fs.Write(closeAngleBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display pointer information depending on flags.
|
|
||||||
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
|
||||||
f.fs.Write(openParenBytes)
|
|
||||||
for i, addr := range pointerChain {
|
|
||||||
if i > 0 {
|
|
||||||
f.fs.Write(pointerChainBytes)
|
|
||||||
}
|
|
||||||
printHexPtr(f.fs, addr)
|
|
||||||
}
|
|
||||||
f.fs.Write(closeParenBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display dereferenced value.
|
|
||||||
switch {
|
|
||||||
case nilFound:
|
|
||||||
f.fs.Write(nilAngleBytes)
|
|
||||||
|
|
||||||
case cycleFound:
|
|
||||||
f.fs.Write(circularShortBytes)
|
|
||||||
|
|
||||||
default:
|
|
||||||
f.ignoreNextType = true
|
|
||||||
f.format(ve)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// format is the main workhorse for providing the Formatter interface. It
|
|
||||||
// uses the passed reflect value to figure out what kind of object we are
|
|
||||||
// dealing with and formats it appropriately. It is a recursive function,
|
|
||||||
// however circular data structures are detected and handled properly.
|
|
||||||
func (f *formatState) format(v reflect.Value) {
|
|
||||||
// Handle invalid reflect values immediately.
|
|
||||||
kind := v.Kind()
|
|
||||||
if kind == reflect.Invalid {
|
|
||||||
f.fs.Write(invalidAngleBytes)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle pointers specially.
|
|
||||||
if kind == reflect.Ptr {
|
|
||||||
f.formatPtr(v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print type information unless already handled elsewhere.
|
|
||||||
if !f.ignoreNextType && f.fs.Flag('#') {
|
|
||||||
f.fs.Write(openParenBytes)
|
|
||||||
f.fs.Write([]byte(v.Type().String()))
|
|
||||||
f.fs.Write(closeParenBytes)
|
|
||||||
}
|
|
||||||
f.ignoreNextType = false
|
|
||||||
|
|
||||||
// Call Stringer/error interfaces if they exist and the handle methods
|
|
||||||
// flag is enabled.
|
|
||||||
if !f.cs.DisableMethods {
|
|
||||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
|
||||||
if handled := handleMethods(f.cs, f.fs, v); handled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case reflect.Invalid:
|
|
||||||
// Do nothing. We should never get here since invalid has already
|
|
||||||
// been handled above.
|
|
||||||
|
|
||||||
case reflect.Bool:
|
|
||||||
printBool(f.fs, v.Bool())
|
|
||||||
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
||||||
printInt(f.fs, v.Int(), 10)
|
|
||||||
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
||||||
printUint(f.fs, v.Uint(), 10)
|
|
||||||
|
|
||||||
case reflect.Float32:
|
|
||||||
printFloat(f.fs, v.Float(), 32)
|
|
||||||
|
|
||||||
case reflect.Float64:
|
|
||||||
printFloat(f.fs, v.Float(), 64)
|
|
||||||
|
|
||||||
case reflect.Complex64:
|
|
||||||
printComplex(f.fs, v.Complex(), 32)
|
|
||||||
|
|
||||||
case reflect.Complex128:
|
|
||||||
printComplex(f.fs, v.Complex(), 64)
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
if v.IsNil() {
|
|
||||||
f.fs.Write(nilAngleBytes)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
|
|
||||||
case reflect.Array:
|
|
||||||
f.fs.Write(openBracketBytes)
|
|
||||||
f.depth++
|
|
||||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
|
||||||
f.fs.Write(maxShortBytes)
|
|
||||||
} else {
|
|
||||||
numEntries := v.Len()
|
|
||||||
for i := 0; i < numEntries; i++ {
|
|
||||||
if i > 0 {
|
|
||||||
f.fs.Write(spaceBytes)
|
|
||||||
}
|
|
||||||
f.ignoreNextType = true
|
|
||||||
f.format(f.unpackValue(v.Index(i)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.depth--
|
|
||||||
f.fs.Write(closeBracketBytes)
|
|
||||||
|
|
||||||
case reflect.String:
|
|
||||||
f.fs.Write([]byte(v.String()))
|
|
||||||
|
|
||||||
case reflect.Interface:
|
|
||||||
// The only time we should get here is for nil interfaces due to
|
|
||||||
// unpackValue calls.
|
|
||||||
if v.IsNil() {
|
|
||||||
f.fs.Write(nilAngleBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Ptr:
|
|
||||||
// Do nothing. We should never get here since pointers have already
|
|
||||||
// been handled above.
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
// nil maps should be indicated as different than empty maps
|
|
||||||
if v.IsNil() {
|
|
||||||
f.fs.Write(nilAngleBytes)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
f.fs.Write(openMapBytes)
|
|
||||||
f.depth++
|
|
||||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
|
||||||
f.fs.Write(maxShortBytes)
|
|
||||||
} else {
|
|
||||||
keys := v.MapKeys()
|
|
||||||
if f.cs.SortKeys {
|
|
||||||
sortValues(keys, f.cs)
|
|
||||||
}
|
|
||||||
for i, key := range keys {
|
|
||||||
if i > 0 {
|
|
||||||
f.fs.Write(spaceBytes)
|
|
||||||
}
|
|
||||||
f.ignoreNextType = true
|
|
||||||
f.format(f.unpackValue(key))
|
|
||||||
f.fs.Write(colonBytes)
|
|
||||||
f.ignoreNextType = true
|
|
||||||
f.format(f.unpackValue(v.MapIndex(key)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.depth--
|
|
||||||
f.fs.Write(closeMapBytes)
|
|
||||||
|
|
||||||
case reflect.Struct:
|
|
||||||
numFields := v.NumField()
|
|
||||||
f.fs.Write(openBraceBytes)
|
|
||||||
f.depth++
|
|
||||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
|
||||||
f.fs.Write(maxShortBytes)
|
|
||||||
} else {
|
|
||||||
vt := v.Type()
|
|
||||||
for i := 0; i < numFields; i++ {
|
|
||||||
if i > 0 {
|
|
||||||
f.fs.Write(spaceBytes)
|
|
||||||
}
|
|
||||||
vtf := vt.Field(i)
|
|
||||||
if f.fs.Flag('+') || f.fs.Flag('#') {
|
|
||||||
f.fs.Write([]byte(vtf.Name))
|
|
||||||
f.fs.Write(colonBytes)
|
|
||||||
}
|
|
||||||
f.format(f.unpackValue(v.Field(i)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.depth--
|
|
||||||
f.fs.Write(closeBraceBytes)
|
|
||||||
|
|
||||||
case reflect.Uintptr:
|
|
||||||
printHexPtr(f.fs, uintptr(v.Uint()))
|
|
||||||
|
|
||||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
|
||||||
printHexPtr(f.fs, v.Pointer())
|
|
||||||
|
|
||||||
// There were not any other types at the time this code was written, but
|
|
||||||
// fall back to letting the default fmt package handle it if any get added.
|
|
||||||
default:
|
|
||||||
format := f.buildDefaultFormat()
|
|
||||||
if v.CanInterface() {
|
|
||||||
fmt.Fprintf(f.fs, format, v.Interface())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(f.fs, format, v.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
|
||||||
// details.
|
|
||||||
func (f *formatState) Format(fs fmt.State, verb rune) {
|
|
||||||
f.fs = fs
|
|
||||||
|
|
||||||
// Use standard formatting for verbs that are not v.
|
|
||||||
if verb != 'v' {
|
|
||||||
format := f.constructOrigFormat(verb)
|
|
||||||
fmt.Fprintf(fs, format, f.value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.value == nil {
|
|
||||||
if fs.Flag('#') {
|
|
||||||
fs.Write(interfaceBytes)
|
|
||||||
}
|
|
||||||
fs.Write(nilAngleBytes)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f.format(reflect.ValueOf(f.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
// newFormatter is a helper function to consolidate the logic from the various
|
|
||||||
// public methods which take varying config states.
|
|
||||||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
|
||||||
fs := &formatState{value: v, cs: cs}
|
|
||||||
fs.pointers = make(map[uintptr]int)
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
|
||||||
interface. As a result, it integrates cleanly with standard fmt package
|
|
||||||
printing functions. The formatter is useful for inline printing of smaller data
|
|
||||||
types similar to the standard %v format specifier.
|
|
||||||
|
|
||||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
|
||||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
|
||||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
|
||||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
|
||||||
the width and precision arguments (however they will still work on the format
|
|
||||||
specifiers not handled by the custom formatter).
|
|
||||||
|
|
||||||
Typically this function shouldn't be called directly. It is much easier to make
|
|
||||||
use of the custom formatter by calling one of the convenience functions such as
|
|
||||||
Printf, Println, or Fprintf.
|
|
||||||
*/
|
|
||||||
func NewFormatter(v interface{}) fmt.Formatter {
|
|
||||||
return newFormatter(&Config, v)
|
|
||||||
}
|
|
||||||
1558
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
1558
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
84
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
84
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This test file is part of the spew package rather than than the spew_test
|
|
||||||
package because it needs access to internals to properly test certain cases
|
|
||||||
which are not possible via the public interface since they should never happen.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// dummyFmtState implements a fake fmt.State to use for testing invalid
|
|
||||||
// reflect.Value handling. This is necessary because the fmt package catches
|
|
||||||
// invalid values before invoking the formatter on them.
|
|
||||||
type dummyFmtState struct {
|
|
||||||
bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dfs *dummyFmtState) Flag(f int) bool {
|
|
||||||
return f == int('+')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dfs *dummyFmtState) Precision() (int, bool) {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dfs *dummyFmtState) Width() (int, bool) {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestInvalidReflectValue ensures the dump and formatter code handles an
|
|
||||||
// invalid reflect value properly. This needs access to internal state since it
|
|
||||||
// should never happen in real code and therefore can't be tested via the public
|
|
||||||
// API.
|
|
||||||
func TestInvalidReflectValue(t *testing.T) {
|
|
||||||
i := 1
|
|
||||||
|
|
||||||
// Dump invalid reflect value.
|
|
||||||
v := new(reflect.Value)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
d := dumpState{w: buf, cs: &Config}
|
|
||||||
d.dump(*v)
|
|
||||||
s := buf.String()
|
|
||||||
want := "<invalid>"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
|
|
||||||
// Formatter invalid reflect value.
|
|
||||||
buf2 := new(dummyFmtState)
|
|
||||||
f := formatState{value: *v, cs: &Config, fs: buf2}
|
|
||||||
f.format(*v)
|
|
||||||
s = buf2.String()
|
|
||||||
want = "<invalid>"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortValues makes the internal sortValues function available to the test
|
|
||||||
// package.
|
|
||||||
func SortValues(values []reflect.Value, cs *ConfigState) {
|
|
||||||
sortValues(values, cs)
|
|
||||||
}
|
|
||||||
101
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
101
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
@ -1,101 +0,0 @@
|
|||||||
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
|
|
||||||
// Permission to use, copy, modify, and distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|
||||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
|
||||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
|
||||||
// tag is deprecated and thus should not be used.
|
|
||||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
|
||||||
|
|
||||||
/*
|
|
||||||
This test file is part of the spew package rather than than the spew_test
|
|
||||||
package because it needs access to internals to properly test certain cases
|
|
||||||
which are not possible via the public interface since they should never happen.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
|
|
||||||
// the maximum kind value which does not exist. This is needed to test the
|
|
||||||
// fallback code which punts to the standard fmt library for new types that
|
|
||||||
// might get added to the language.
|
|
||||||
func changeKind(v *reflect.Value, readOnly bool) {
|
|
||||||
flags := flagField(v)
|
|
||||||
if readOnly {
|
|
||||||
*flags |= flagRO
|
|
||||||
} else {
|
|
||||||
*flags &^= flagRO
|
|
||||||
}
|
|
||||||
*flags |= flagKindMask
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestAddedReflectValue tests functionaly of the dump and formatter code which
|
|
||||||
// falls back to the standard fmt library for new types that might get added to
|
|
||||||
// the language.
|
|
||||||
func TestAddedReflectValue(t *testing.T) {
|
|
||||||
i := 1
|
|
||||||
|
|
||||||
// Dump using a reflect.Value that is exported.
|
|
||||||
v := reflect.ValueOf(int8(5))
|
|
||||||
changeKind(&v, false)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
d := dumpState{w: buf, cs: &Config}
|
|
||||||
d.dump(v)
|
|
||||||
s := buf.String()
|
|
||||||
want := "(int8) 5"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
|
|
||||||
// Dump using a reflect.Value that is not exported.
|
|
||||||
changeKind(&v, true)
|
|
||||||
buf.Reset()
|
|
||||||
d.dump(v)
|
|
||||||
s = buf.String()
|
|
||||||
want = "(int8) <int8 Value>"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
|
|
||||||
// Formatter using a reflect.Value that is exported.
|
|
||||||
changeKind(&v, false)
|
|
||||||
buf2 := new(dummyFmtState)
|
|
||||||
f := formatState{value: v, cs: &Config, fs: buf2}
|
|
||||||
f.format(v)
|
|
||||||
s = buf2.String()
|
|
||||||
want = "5"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
|
|
||||||
// Formatter using a reflect.Value that is not exported.
|
|
||||||
changeKind(&v, true)
|
|
||||||
buf2.Reset()
|
|
||||||
f = formatState{value: v, cs: &Config, fs: buf2}
|
|
||||||
f.format(v)
|
|
||||||
s = buf2.String()
|
|
||||||
want = "<int8 Value>"
|
|
||||||
if s != want {
|
|
||||||
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the formatted string as a value that satisfies error. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Errorf(format string, a ...interface{}) (err error) {
|
|
||||||
return fmt.Errorf(format, convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Fprint(w, convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Fprintf(w, format, convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Fprintln(w, convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Print(a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Print(convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Printf(format string, a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Printf(format, convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the number of bytes written and any write error encountered. See
|
|
||||||
// NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Println(a ...interface{}) (n int, err error) {
|
|
||||||
return fmt.Println(convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the resulting string. See NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Sprint(a ...interface{}) string {
|
|
||||||
return fmt.Sprint(convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
|
||||||
// passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the resulting string. See NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Sprintf(format string, a ...interface{}) string {
|
|
||||||
return fmt.Sprintf(format, convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
|
||||||
// were passed with a default Formatter interface returned by NewFormatter. It
|
|
||||||
// returns the resulting string. See NewFormatter for formatting details.
|
|
||||||
//
|
|
||||||
// This function is shorthand for the following syntax:
|
|
||||||
//
|
|
||||||
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
|
||||||
func Sprintln(a ...interface{}) string {
|
|
||||||
return fmt.Sprintln(convertArgs(a)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
|
||||||
// length with each argument converted to a default spew Formatter interface.
|
|
||||||
func convertArgs(args []interface{}) (formatters []interface{}) {
|
|
||||||
formatters = make([]interface{}, len(args))
|
|
||||||
for index, arg := range args {
|
|
||||||
formatters[index] = NewFormatter(arg)
|
|
||||||
}
|
|
||||||
return formatters
|
|
||||||
}
|
|
||||||
320
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
320
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
@ -1,320 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package spew_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
|
||||||
|
|
||||||
// spewFunc is used to identify which public function of the spew package or
|
|
||||||
// ConfigState a test applies to.
|
|
||||||
type spewFunc int
|
|
||||||
|
|
||||||
const (
|
|
||||||
fCSFdump spewFunc = iota
|
|
||||||
fCSFprint
|
|
||||||
fCSFprintf
|
|
||||||
fCSFprintln
|
|
||||||
fCSPrint
|
|
||||||
fCSPrintln
|
|
||||||
fCSSdump
|
|
||||||
fCSSprint
|
|
||||||
fCSSprintf
|
|
||||||
fCSSprintln
|
|
||||||
fCSErrorf
|
|
||||||
fCSNewFormatter
|
|
||||||
fErrorf
|
|
||||||
fFprint
|
|
||||||
fFprintln
|
|
||||||
fPrint
|
|
||||||
fPrintln
|
|
||||||
fSdump
|
|
||||||
fSprint
|
|
||||||
fSprintf
|
|
||||||
fSprintln
|
|
||||||
)
|
|
||||||
|
|
||||||
// Map of spewFunc values to names for pretty printing.
|
|
||||||
var spewFuncStrings = map[spewFunc]string{
|
|
||||||
fCSFdump: "ConfigState.Fdump",
|
|
||||||
fCSFprint: "ConfigState.Fprint",
|
|
||||||
fCSFprintf: "ConfigState.Fprintf",
|
|
||||||
fCSFprintln: "ConfigState.Fprintln",
|
|
||||||
fCSSdump: "ConfigState.Sdump",
|
|
||||||
fCSPrint: "ConfigState.Print",
|
|
||||||
fCSPrintln: "ConfigState.Println",
|
|
||||||
fCSSprint: "ConfigState.Sprint",
|
|
||||||
fCSSprintf: "ConfigState.Sprintf",
|
|
||||||
fCSSprintln: "ConfigState.Sprintln",
|
|
||||||
fCSErrorf: "ConfigState.Errorf",
|
|
||||||
fCSNewFormatter: "ConfigState.NewFormatter",
|
|
||||||
fErrorf: "spew.Errorf",
|
|
||||||
fFprint: "spew.Fprint",
|
|
||||||
fFprintln: "spew.Fprintln",
|
|
||||||
fPrint: "spew.Print",
|
|
||||||
fPrintln: "spew.Println",
|
|
||||||
fSdump: "spew.Sdump",
|
|
||||||
fSprint: "spew.Sprint",
|
|
||||||
fSprintf: "spew.Sprintf",
|
|
||||||
fSprintln: "spew.Sprintln",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f spewFunc) String() string {
|
|
||||||
if s, ok := spewFuncStrings[f]; ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Unknown spewFunc (%d)", int(f))
|
|
||||||
}
|
|
||||||
|
|
||||||
// spewTest is used to describe a test to be performed against the public
|
|
||||||
// functions of the spew package or ConfigState.
|
|
||||||
type spewTest struct {
|
|
||||||
cs *spew.ConfigState
|
|
||||||
f spewFunc
|
|
||||||
format string
|
|
||||||
in interface{}
|
|
||||||
want string
|
|
||||||
}
|
|
||||||
|
|
||||||
// spewTests houses the tests to be performed against the public functions of
|
|
||||||
// the spew package and ConfigState.
|
|
||||||
//
|
|
||||||
// These tests are only intended to ensure the public functions are exercised
|
|
||||||
// and are intentionally not exhaustive of types. The exhaustive type
|
|
||||||
// tests are handled in the dump and format tests.
|
|
||||||
var spewTests []spewTest
|
|
||||||
|
|
||||||
// redirStdout is a helper function to return the standard output from f as a
|
|
||||||
// byte slice.
|
|
||||||
func redirStdout(f func()) ([]byte, error) {
|
|
||||||
tempFile, err := ioutil.TempFile("", "ss-test")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fileName := tempFile.Name()
|
|
||||||
defer os.Remove(fileName) // Ignore error
|
|
||||||
|
|
||||||
origStdout := os.Stdout
|
|
||||||
os.Stdout = tempFile
|
|
||||||
f()
|
|
||||||
os.Stdout = origStdout
|
|
||||||
tempFile.Close()
|
|
||||||
|
|
||||||
return ioutil.ReadFile(fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSpewTests() {
|
|
||||||
// Config states with various settings.
|
|
||||||
scsDefault := spew.NewDefaultConfig()
|
|
||||||
scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true}
|
|
||||||
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
|
|
||||||
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
|
|
||||||
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
|
|
||||||
scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true}
|
|
||||||
scsNoCap := &spew.ConfigState{DisableCapacities: true}
|
|
||||||
|
|
||||||
// Variables for tests on types which implement Stringer interface with and
|
|
||||||
// without a pointer receiver.
|
|
||||||
ts := stringer("test")
|
|
||||||
tps := pstringer("test")
|
|
||||||
|
|
||||||
type ptrTester struct {
|
|
||||||
s *struct{}
|
|
||||||
}
|
|
||||||
tptr := &ptrTester{s: &struct{}{}}
|
|
||||||
|
|
||||||
// depthTester is used to test max depth handling for structs, array, slices
|
|
||||||
// and maps.
|
|
||||||
type depthTester struct {
|
|
||||||
ic indirCir1
|
|
||||||
arr [1]string
|
|
||||||
slice []string
|
|
||||||
m map[string]int
|
|
||||||
}
|
|
||||||
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
|
|
||||||
map[string]int{"one": 1}}
|
|
||||||
|
|
||||||
// Variable for tests on types which implement error interface.
|
|
||||||
te := customError(10)
|
|
||||||
|
|
||||||
spewTests = []spewTest{
|
|
||||||
{scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
|
|
||||||
{scsDefault, fCSFprint, "", int16(32767), "32767"},
|
|
||||||
{scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
|
|
||||||
{scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
|
|
||||||
{scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
|
|
||||||
{scsDefault, fCSPrintln, "", uint8(255), "255\n"},
|
|
||||||
{scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
|
|
||||||
{scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
|
|
||||||
{scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
|
|
||||||
{scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
|
|
||||||
{scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
|
|
||||||
{scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
|
|
||||||
{scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
|
|
||||||
{scsDefault, fFprint, "", float32(3.14), "3.14"},
|
|
||||||
{scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
|
|
||||||
{scsDefault, fPrint, "", true, "true"},
|
|
||||||
{scsDefault, fPrintln, "", false, "false\n"},
|
|
||||||
{scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
|
|
||||||
{scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
|
|
||||||
{scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
|
|
||||||
{scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
|
|
||||||
{scsNoMethods, fCSFprint, "", ts, "test"},
|
|
||||||
{scsNoMethods, fCSFprint, "", &ts, "<*>test"},
|
|
||||||
{scsNoMethods, fCSFprint, "", tps, "test"},
|
|
||||||
{scsNoMethods, fCSFprint, "", &tps, "<*>test"},
|
|
||||||
{scsNoPmethods, fCSFprint, "", ts, "stringer test"},
|
|
||||||
{scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
|
|
||||||
{scsNoPmethods, fCSFprint, "", tps, "test"},
|
|
||||||
{scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
|
|
||||||
{scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
|
|
||||||
{scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
|
|
||||||
" ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
|
|
||||||
" arr: ([1]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
|
||||||
" slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
|
||||||
" m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"},
|
|
||||||
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
|
|
||||||
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
|
|
||||||
"(len=4) (stringer test) \"test\"\n"},
|
|
||||||
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
|
|
||||||
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
|
|
||||||
"(error: 10) 10\n"},
|
|
||||||
{scsNoPtrAddr, fCSFprint, "", tptr, "<*>{<*>{}}"},
|
|
||||||
{scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"},
|
|
||||||
{scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"},
|
|
||||||
{scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\n"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSpew executes all of the tests described by spewTests.
|
|
||||||
func TestSpew(t *testing.T) {
|
|
||||||
initSpewTests()
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(spewTests))
|
|
||||||
for i, test := range spewTests {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
switch test.f {
|
|
||||||
case fCSFdump:
|
|
||||||
test.cs.Fdump(buf, test.in)
|
|
||||||
|
|
||||||
case fCSFprint:
|
|
||||||
test.cs.Fprint(buf, test.in)
|
|
||||||
|
|
||||||
case fCSFprintf:
|
|
||||||
test.cs.Fprintf(buf, test.format, test.in)
|
|
||||||
|
|
||||||
case fCSFprintln:
|
|
||||||
test.cs.Fprintln(buf, test.in)
|
|
||||||
|
|
||||||
case fCSPrint:
|
|
||||||
b, err := redirStdout(func() { test.cs.Print(test.in) })
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v #%d %v", test.f, i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf.Write(b)
|
|
||||||
|
|
||||||
case fCSPrintln:
|
|
||||||
b, err := redirStdout(func() { test.cs.Println(test.in) })
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v #%d %v", test.f, i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf.Write(b)
|
|
||||||
|
|
||||||
case fCSSdump:
|
|
||||||
str := test.cs.Sdump(test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
case fCSSprint:
|
|
||||||
str := test.cs.Sprint(test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
case fCSSprintf:
|
|
||||||
str := test.cs.Sprintf(test.format, test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
case fCSSprintln:
|
|
||||||
str := test.cs.Sprintln(test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
case fCSErrorf:
|
|
||||||
err := test.cs.Errorf(test.format, test.in)
|
|
||||||
buf.WriteString(err.Error())
|
|
||||||
|
|
||||||
case fCSNewFormatter:
|
|
||||||
fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in))
|
|
||||||
|
|
||||||
case fErrorf:
|
|
||||||
err := spew.Errorf(test.format, test.in)
|
|
||||||
buf.WriteString(err.Error())
|
|
||||||
|
|
||||||
case fFprint:
|
|
||||||
spew.Fprint(buf, test.in)
|
|
||||||
|
|
||||||
case fFprintln:
|
|
||||||
spew.Fprintln(buf, test.in)
|
|
||||||
|
|
||||||
case fPrint:
|
|
||||||
b, err := redirStdout(func() { spew.Print(test.in) })
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v #%d %v", test.f, i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf.Write(b)
|
|
||||||
|
|
||||||
case fPrintln:
|
|
||||||
b, err := redirStdout(func() { spew.Println(test.in) })
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v #%d %v", test.f, i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buf.Write(b)
|
|
||||||
|
|
||||||
case fSdump:
|
|
||||||
str := spew.Sdump(test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
case fSprint:
|
|
||||||
str := spew.Sprint(test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
case fSprintf:
|
|
||||||
str := spew.Sprintf(test.format, test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
case fSprintln:
|
|
||||||
str := spew.Sprintln(test.in)
|
|
||||||
buf.WriteString(str)
|
|
||||||
|
|
||||||
default:
|
|
||||||
t.Errorf("%v #%d unrecognized function", test.f, i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
if test.want != s {
|
|
||||||
t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
82
vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
generated
vendored
82
vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
generated
vendored
@ -1,82 +0,0 @@
|
|||||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
|
||||||
//
|
|
||||||
// Permission to use, copy, modify, and distribute this software for any
|
|
||||||
// purpose with or without fee is hereby granted, provided that the above
|
|
||||||
// copyright notice and this permission notice appear in all copies.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
|
||||||
// when both cgo is supported and "-tags testcgo" is added to the go test
|
|
||||||
// command line. This code should really only be in the dumpcgo_test.go file,
|
|
||||||
// but unfortunately Go will not allow cgo in test files, so this is a
|
|
||||||
// workaround to allow cgo types to be tested. This configuration is used
|
|
||||||
// because spew itself does not require cgo to run even though it does handle
|
|
||||||
// certain cgo types specially. Rather than forcing all clients to require cgo
|
|
||||||
// and an external C compiler just to run the tests, this scheme makes them
|
|
||||||
// optional.
|
|
||||||
// +build cgo,testcgo
|
|
||||||
|
|
||||||
package testdata
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdint.h>
|
|
||||||
typedef unsigned char custom_uchar_t;
|
|
||||||
|
|
||||||
char *ncp = 0;
|
|
||||||
char *cp = "test";
|
|
||||||
char ca[6] = {'t', 'e', 's', 't', '2', '\0'};
|
|
||||||
unsigned char uca[6] = {'t', 'e', 's', 't', '3', '\0'};
|
|
||||||
signed char sca[6] = {'t', 'e', 's', 't', '4', '\0'};
|
|
||||||
uint8_t ui8ta[6] = {'t', 'e', 's', 't', '5', '\0'};
|
|
||||||
custom_uchar_t tuca[6] = {'t', 'e', 's', 't', '6', '\0'};
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// GetCgoNullCharPointer returns a null char pointer via cgo. This is only
|
|
||||||
// used for tests.
|
|
||||||
func GetCgoNullCharPointer() interface{} {
|
|
||||||
return C.ncp
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCgoCharPointer returns a char pointer via cgo. This is only used for
|
|
||||||
// tests.
|
|
||||||
func GetCgoCharPointer() interface{} {
|
|
||||||
return C.cp
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCgoCharArray returns a char array via cgo and the array's len and cap.
|
|
||||||
// This is only used for tests.
|
|
||||||
func GetCgoCharArray() (interface{}, int, int) {
|
|
||||||
return C.ca, len(C.ca), cap(C.ca)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCgoUnsignedCharArray returns an unsigned char array via cgo and the
|
|
||||||
// array's len and cap. This is only used for tests.
|
|
||||||
func GetCgoUnsignedCharArray() (interface{}, int, int) {
|
|
||||||
return C.uca, len(C.uca), cap(C.uca)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCgoSignedCharArray returns a signed char array via cgo and the array's len
|
|
||||||
// and cap. This is only used for tests.
|
|
||||||
func GetCgoSignedCharArray() (interface{}, int, int) {
|
|
||||||
return C.sca, len(C.sca), cap(C.sca)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCgoUint8tArray returns a uint8_t array via cgo and the array's len and
|
|
||||||
// cap. This is only used for tests.
|
|
||||||
func GetCgoUint8tArray() (interface{}, int, int) {
|
|
||||||
return C.ui8ta, len(C.ui8ta), cap(C.ui8ta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via
|
|
||||||
// cgo and the array's len and cap. This is only used for tests.
|
|
||||||
func GetCgoTypdefedUnsignedCharArray() (interface{}, int, int) {
|
|
||||||
return C.tuca, len(C.tuca), cap(C.tuca)
|
|
||||||
}
|
|
||||||
61
vendor/github.com/davecgh/go-spew/test_coverage.txt
generated
vendored
61
vendor/github.com/davecgh/go-spew/test_coverage.txt
generated
vendored
@ -1,61 +0,0 @@
|
|||||||
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
|
|
||||||
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505)
|
|
||||||
|
|
||||||
22
vendor/github.com/erikstmartin/go-testdb/.gitignore
generated
vendored
22
vendor/github.com/erikstmartin/go-testdb/.gitignore
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
23
vendor/github.com/erikstmartin/go-testdb/LICENSE
generated
vendored
23
vendor/github.com/erikstmartin/go-testdb/LICENSE
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
Copyright (c) 2013, Erik St. Martin
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
205
vendor/github.com/erikstmartin/go-testdb/README.md
generated
vendored
205
vendor/github.com/erikstmartin/go-testdb/README.md
generated
vendored
@ -1,205 +0,0 @@
|
|||||||
go-testdb
|
|
||||||
=========
|
|
||||||
|
|
||||||
Framework for stubbing responses from go's driver.Driver interface.
|
|
||||||
|
|
||||||
This can be used to sit in place of your sql.Db so that you can stub responses for sql calls, and remove database dependencies for your test suite.
|
|
||||||
|
|
||||||
This project is in its infancy, but has worked well for all the use cases i've had so far, and continues to evolve as new scenarios are uncovered. Please feel free to send pull-requests, or submit feature requests if you have scenarios that are not accounted for yet.
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
The only thing needed for setup is to include the go-testdb package, then you can create your db connection specifying "testdb" as your driver.
|
|
||||||
<pre>
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
_"github.com/erikstmartin/go-testdb"
|
|
||||||
)
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Stubbing connection failure
|
|
||||||
You're able to set your own function to execute when the sql library calls sql.Open
|
|
||||||
<pre>
|
|
||||||
testdb.SetOpenFunc(func(dsn string) (driver.Conn, error) {
|
|
||||||
return c, errors.New("failed to connect")
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Stubbing queries
|
|
||||||
You're able to stub responses to known queries, unknown queries will trigger log errors so that you can see that queries were executed that were not stubbed.
|
|
||||||
|
|
||||||
Differences in whitespace, and case are ignored.
|
|
||||||
|
|
||||||
For convenience a method has been created for you to take a CSV string and turn it into a database result object (RowsFromCSVString).
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select id, name, age from users"
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
result := `
|
|
||||||
1,tim,20,2012-10-01 01:00:01
|
|
||||||
2,joe,25,2012-10-02 02:00:02
|
|
||||||
3,bob,30,2012-10-03 03:00:03
|
|
||||||
`
|
|
||||||
testdb.StubQuery(sql, testdb.RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
If for some reason you need to specify another rune to split the columns, you can do it passing the rune that you want to use as `Comma` character as third argument to RowsFromCSVString
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select id, name, age, data from users"
|
|
||||||
columns := []string{"id", "name", "age", "data", "created"}
|
|
||||||
result := `
|
|
||||||
1|tim|20|part_1,part_2,part_3|2014-10-16 15:01:00
|
|
||||||
2|joe|25|part_4,part_5,part_6|2014-10-17 15:01:01
|
|
||||||
3|bob|30|part_7,part_8,part_9|2014-10-18 15:01:02
|
|
||||||
`
|
|
||||||
testdb.StunQuery(sql, RowsFromCSVString(columns, result, '|'))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Stubbing Query function
|
|
||||||
Some times you need more control over Query being run, maybe you need to assert whether or not a particular query is run.
|
|
||||||
|
|
||||||
You can return either a driver.Rows for response (your own or utilize RowsFromCSV) or an error to be returned
|
|
||||||
<pre>
|
|
||||||
testdb.SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
rows := `
|
|
||||||
1,tim,20,2012-10-01 01:00:01
|
|
||||||
2,joe,25,2012-10-02 02:00:02
|
|
||||||
3,bob,30,2012-10-03 03:00:03`
|
|
||||||
|
|
||||||
// inspect query to ensure it matches a pattern, or anything else you want to do first
|
|
||||||
return RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
res, err := db.Query("SELECT foo FROM bar")
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Stubbing Parameterized Query
|
|
||||||
Sometimes you need control over the results of a parameterized query.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
testdb.SetQueryWithArgsFunc(func(query string, args []driver.Value) (result driver.Rows, err error) {
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
|
|
||||||
rows := ""
|
|
||||||
if args[0] == "joe" {
|
|
||||||
rows = "2,joe,25,2012-10-02 02:00:02"
|
|
||||||
}
|
|
||||||
return testdb.RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
res, _ := db.Query("SELECT foo FROM bar WHERE name = $1", "joe")
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Stubbing errors returned from queries
|
|
||||||
In case you need to stub errors returned from queries to ensure your code handles them properly
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from error"
|
|
||||||
testdb.StubQueryError(sql, errors.New("test error"))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Stubbing Parameterized Exec query
|
|
||||||
Sometimes you need control over the handling of a parameterized query that does not return any rows.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
type testResult struct{
|
|
||||||
lastId int64
|
|
||||||
affectedRows int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r testResult) LastInsertId() (int64, error){
|
|
||||||
return r.lastId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r testResult) RowsAffected() (int64, error) {
|
|
||||||
return r.affectedRows, nil
|
|
||||||
}
|
|
||||||
testdb.SetExecWithArgsFunc(func(query string, args []driver.Value) (result driver.Result, err error) {
|
|
||||||
if args[0] == "joe" {
|
|
||||||
return testResult{1, 1}, nil
|
|
||||||
}
|
|
||||||
return testResult{1, 0}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
res, _ := db.Exec("UPDATE bar SET name = 'foo' WHERE name = ?", "joe")
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Stubbing Prepared Statements
|
|
||||||
You can use the same methods as `SetQueryFunc`, `SetQueryWithArgsFunc` for Prepared Statements
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
testdb.SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
rows := `
|
|
||||||
1,tim,20,2012-10-01 01:00:01
|
|
||||||
2,joe,25,2012-10-02 02:00:02
|
|
||||||
3,bob,30,2012-10-03 03:00:03`
|
|
||||||
|
|
||||||
// inspect query to ensure it matches a pattern, or anything else you want to do first
|
|
||||||
return RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
stmt, _ := db.Prepare("SELECT foo FROM bar")
|
|
||||||
res, err := stmt.Query("SELECT foo FROM bar")
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Reset
|
|
||||||
At any point in your test, or as a defer you can remove all stubbed queries, errors, custom set Query or Open functions by calling the reset method.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
func TestMyDatabase(t *testing.T){
|
|
||||||
defer testdb.Reset()
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
#### TODO
|
|
||||||
Feel free to contribute and send pull requests
|
|
||||||
- Transactions
|
|
||||||
|
|
||||||
#### License
|
|
||||||
Copyright (c) 2013, Erik St. Martin
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
115
vendor/github.com/erikstmartin/go-testdb/conn.go
generated
vendored
115
vendor/github.com/erikstmartin/go-testdb/conn.go
generated
vendored
@ -1,115 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type conn struct {
|
|
||||||
queries map[string]query
|
|
||||||
queryFunc func(query string, args []driver.Value) (driver.Rows, error)
|
|
||||||
execFunc func(query string, args []driver.Value) (driver.Result, error)
|
|
||||||
beginFunc func() (driver.Tx, error)
|
|
||||||
commitFunc func() error
|
|
||||||
rollbackFunc func() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConn() *conn {
|
|
||||||
return &conn{
|
|
||||||
queries: make(map[string]query),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Prepare(query string) (driver.Stmt, error) {
|
|
||||||
s := new(stmt)
|
|
||||||
|
|
||||||
if c.queryFunc != nil {
|
|
||||||
s.queryFunc = func(args []driver.Value) (driver.Rows, error) {
|
|
||||||
return c.queryFunc(query, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.execFunc != nil {
|
|
||||||
s.execFunc = func(args []driver.Value) (driver.Result, error) {
|
|
||||||
return c.execFunc(query, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if q, ok := d.conn.queries[getQueryHash(query)]; ok {
|
|
||||||
if s.queryFunc == nil && q.rows != nil {
|
|
||||||
s.queryFunc = func(args []driver.Value) (driver.Rows, error) {
|
|
||||||
if q.rows != nil {
|
|
||||||
if rows, ok := q.rows.(*rows); ok {
|
|
||||||
return rows.clone(), nil
|
|
||||||
}
|
|
||||||
return q.rows, nil
|
|
||||||
}
|
|
||||||
return nil, q.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.execFunc == nil && q.result != nil {
|
|
||||||
s.execFunc = func(args []driver.Value) (driver.Result, error) {
|
|
||||||
if q.result != nil {
|
|
||||||
return q.result, nil
|
|
||||||
}
|
|
||||||
return nil, q.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.queryFunc == nil && s.execFunc == nil {
|
|
||||||
return new(stmt), errors.New("Query not stubbed: " + query)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*conn) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Begin() (driver.Tx, error) {
|
|
||||||
if c.beginFunc != nil {
|
|
||||||
return c.beginFunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
t := &Tx{}
|
|
||||||
if c.commitFunc != nil {
|
|
||||||
t.SetCommitFunc(c.commitFunc)
|
|
||||||
}
|
|
||||||
if c.rollbackFunc != nil {
|
|
||||||
t.SetRollbackFunc(c.rollbackFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
|
||||||
if c.queryFunc != nil {
|
|
||||||
return c.queryFunc(query, args)
|
|
||||||
}
|
|
||||||
if q, ok := d.conn.queries[getQueryHash(query)]; ok {
|
|
||||||
if rows, ok := q.rows.(*rows); ok {
|
|
||||||
return rows.clone(), q.err
|
|
||||||
}
|
|
||||||
return q.rows, q.err
|
|
||||||
}
|
|
||||||
return nil, errors.New("Query not stubbed: " + query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
|
||||||
if c.execFunc != nil {
|
|
||||||
return c.execFunc(query, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
if q, ok := d.conn.queries[getQueryHash(query)]; ok {
|
|
||||||
if q.result != nil {
|
|
||||||
return q.result, nil
|
|
||||||
} else if q.err != nil {
|
|
||||||
return nil, q.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("Exec call not stubbed: " + query)
|
|
||||||
}
|
|
||||||
297
vendor/github.com/erikstmartin/go-testdb/examples_test.go
generated
vendored
297
vendor/github.com/erikstmartin/go-testdb/examples_test.go
generated
vendored
@ -1,297 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type user struct {
|
|
||||||
id int64
|
|
||||||
name string
|
|
||||||
age int64
|
|
||||||
created string
|
|
||||||
data string
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetOpenFunc() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetOpenFunc(func(dsn string) (driver.Conn, error) {
|
|
||||||
// Conn() will return the same internal driver.Conn being used by the driver
|
|
||||||
return Conn(), errors.New("test error")
|
|
||||||
})
|
|
||||||
|
|
||||||
// err only returns from this if it's an unknown driver, we are stubbing opening a connection
|
|
||||||
db, _ := sql.Open("testdb", "foo")
|
|
||||||
_, err := db.Driver().Open("foo")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Stubbed error returned as expected: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Stubbed error returned as expected: test error
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleRowsFromCSVString() {
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
result := `
|
|
||||||
1,tim,20,2012-10-01 01:00:01
|
|
||||||
2,joe,25,2012-10-02 02:00:02
|
|
||||||
3,bob,30,2012-10-03 03:00:03
|
|
||||||
`
|
|
||||||
rows := RowsFromCSVString(columns, result)
|
|
||||||
|
|
||||||
fmt.Println(rows.Columns())
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// [id name age created]
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleStubQuery() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select id, name, age from users"
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
result := `
|
|
||||||
1,tim,20,2012-10-01 01:00:01
|
|
||||||
2,joe,25,2012-10-02 02:00:02
|
|
||||||
3,bob,30,2012-10-03 03:00:03
|
|
||||||
`
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
res, _ := db.Query(sql)
|
|
||||||
|
|
||||||
for res.Next() {
|
|
||||||
var u = new(user)
|
|
||||||
res.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
fmt.Println(u.name + " - " + strconv.FormatInt(u.age, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// tim - 20
|
|
||||||
// joe - 25
|
|
||||||
// bob - 30
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleStubQuery_queryRow() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select id, name, age from users"
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
result := `
|
|
||||||
1,tim,20,2012-10-01 01:00:01
|
|
||||||
`
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
row := db.QueryRow(sql)
|
|
||||||
|
|
||||||
u := new(user)
|
|
||||||
row.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
fmt.Println(u.name + " - " + strconv.FormatInt(u.age, 10))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// tim - 20
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleStubQueryError() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from error"
|
|
||||||
|
|
||||||
StubQueryError(sql, errors.New("test error"))
|
|
||||||
|
|
||||||
_, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error returned: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Error returned: test error
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetQueryFunc() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
rows := "1,tim,20,2012-10-01 01:00:01\n2,joe,25,2012-10-02 02:00:02\n3,bob,30,2012-10-03 03:00:03"
|
|
||||||
|
|
||||||
SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
return RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
res, _ := db.Query("SELECT foo FROM bar")
|
|
||||||
|
|
||||||
for res.Next() {
|
|
||||||
var u = new(user)
|
|
||||||
res.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
fmt.Println(u.name + " - " + strconv.FormatInt(u.age, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// tim - 20
|
|
||||||
// joe - 25
|
|
||||||
// bob - 30
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetQueryFunc_queryRow() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
rows := "1,tim,20,2012-10-01 01:00:01"
|
|
||||||
|
|
||||||
SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
return RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
row := db.QueryRow("SELECT foo FROM bar")
|
|
||||||
|
|
||||||
var u = new(user)
|
|
||||||
row.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
fmt.Println(u.name + " - " + strconv.FormatInt(u.age, 10))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// tim - 20
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetQueryWithArgsFunc() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetQueryWithArgsFunc(func(query string, args []driver.Value) (result driver.Rows, err error) {
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
|
|
||||||
rows := ""
|
|
||||||
if args[0] == "joe" {
|
|
||||||
rows = "2,joe,25,2012-10-02 02:00:02"
|
|
||||||
}
|
|
||||||
return RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
res, _ := db.Query("SELECT foo FROM bar WHERE name = $1", "joe")
|
|
||||||
|
|
||||||
for res.Next() {
|
|
||||||
var u = new(user)
|
|
||||||
res.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
fmt.Println(u.name + " - " + strconv.FormatInt(u.age, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// joe - 25
|
|
||||||
}
|
|
||||||
|
|
||||||
type testResult struct {
|
|
||||||
lastId int64
|
|
||||||
affectedRows int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r testResult) LastInsertId() (int64, error) {
|
|
||||||
return r.lastId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r testResult) RowsAffected() (int64, error) {
|
|
||||||
return r.affectedRows, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetExecWithArgsFunc() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetExecWithArgsFunc(func(query string, args []driver.Value) (result driver.Result, err error) {
|
|
||||||
if args[0] == "joe" {
|
|
||||||
return testResult{1, 1}, nil
|
|
||||||
}
|
|
||||||
return testResult{1, 0}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
res, _ := db.Exec("UPDATE bar SET name = 'foo' WHERE name = ?", "joe")
|
|
||||||
|
|
||||||
rowsAffected, _ := res.RowsAffected()
|
|
||||||
fmt.Println("RowsAffected =", rowsAffected)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// RowsAffected = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetBeginFunc() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
commitCalled := false
|
|
||||||
rollbackCalled := false
|
|
||||||
SetBeginFunc(func() (txn driver.Tx, err error) {
|
|
||||||
t := &Tx{}
|
|
||||||
t.SetCommitFunc(func() error {
|
|
||||||
commitCalled = true
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
t.SetRollbackFunc(func() error {
|
|
||||||
rollbackCalled = true
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return t, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
tx, _ := db.Begin()
|
|
||||||
tx.Commit()
|
|
||||||
|
|
||||||
fmt.Println("CommitCalled =", commitCalled)
|
|
||||||
fmt.Println("RollbackCalled =", rollbackCalled)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// CommitCalled = true
|
|
||||||
// RollbackCalled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetCommitFunc() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetCommitFunc(func() error {
|
|
||||||
return errors.New("commit failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
tx, _ := db.Begin()
|
|
||||||
|
|
||||||
fmt.Println("CommitResult =", tx.Commit())
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// CommitResult = commit failed
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleSetRollbackFunc() {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetRollbackFunc(func() error {
|
|
||||||
return errors.New("rollback failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
tx, _ := db.Begin()
|
|
||||||
|
|
||||||
fmt.Println("RollbackResult =", tx.Rollback())
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// RollbackResult = rollback failed
|
|
||||||
}
|
|
||||||
25
vendor/github.com/erikstmartin/go-testdb/result.go
generated
vendored
25
vendor/github.com/erikstmartin/go-testdb/result.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
type Result struct {
|
|
||||||
lastInsertId int64
|
|
||||||
lastInsertIdError error
|
|
||||||
rowsAffected int64
|
|
||||||
rowsAffectedError error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewResult(lastId int64, lastIdError error, rowsAffected int64, rowsAffectedError error) (res *Result) {
|
|
||||||
return &Result{
|
|
||||||
lastInsertId: lastId,
|
|
||||||
lastInsertIdError: lastIdError,
|
|
||||||
rowsAffected: rowsAffected,
|
|
||||||
rowsAffectedError: rowsAffectedError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (res *Result) LastInsertId() (int64, error) {
|
|
||||||
return res.lastInsertId, res.lastInsertIdError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (res *Result) RowsAffected() (int64, error) {
|
|
||||||
return res.rowsAffected, res.rowsAffectedError
|
|
||||||
}
|
|
||||||
48
vendor/github.com/erikstmartin/go-testdb/rows.go
generated
vendored
48
vendor/github.com/erikstmartin/go-testdb/rows.go
generated
vendored
@ -1,48 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type rows struct {
|
|
||||||
closed bool
|
|
||||||
columns []string
|
|
||||||
rows [][]driver.Value
|
|
||||||
pos int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *rows) clone() *rows {
|
|
||||||
if rs == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &rows{closed: false, columns: rs.columns, rows: rs.rows, pos: 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *rows) Next(dest []driver.Value) error {
|
|
||||||
rs.pos++
|
|
||||||
if rs.pos > len(rs.rows) {
|
|
||||||
rs.closed = true
|
|
||||||
|
|
||||||
return io.EOF // per interface spec
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, col := range rs.rows[rs.pos-1] {
|
|
||||||
dest[i] = col
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *rows) Err() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *rows) Columns() []string {
|
|
||||||
return rs.columns
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *rows) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
27
vendor/github.com/erikstmartin/go-testdb/stmt.go
generated
vendored
27
vendor/github.com/erikstmartin/go-testdb/stmt.go
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stmt struct {
|
|
||||||
queryFunc func(args []driver.Value) (driver.Rows, error)
|
|
||||||
execFunc func(args []driver.Value) (driver.Result, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) NumInput() int {
|
|
||||||
// This prevents the sql package from validating the number of inputs
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
|
|
||||||
return s.execFunc(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
|
|
||||||
return s.queryFunc(args)
|
|
||||||
}
|
|
||||||
216
vendor/github.com/erikstmartin/go-testdb/testdb.go
generated
vendored
216
vendor/github.com/erikstmartin/go-testdb/testdb.go
generated
vendored
@ -1,216 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/csv"
|
|
||||||
"io"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var d *testDriver
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
d = newDriver()
|
|
||||||
sql.Register("testdb", d)
|
|
||||||
}
|
|
||||||
|
|
||||||
type testDriver struct {
|
|
||||||
openFunc func(dsn string) (driver.Conn, error)
|
|
||||||
conn *conn
|
|
||||||
enableTimeParsing bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type query struct {
|
|
||||||
rows driver.Rows
|
|
||||||
result *Result
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDriver() *testDriver {
|
|
||||||
return &testDriver{
|
|
||||||
conn: newConn(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnableTimeParsing(flag bool) {
|
|
||||||
d.enableTimeParsing = flag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *testDriver) Open(dsn string) (driver.Conn, error) {
|
|
||||||
if d.openFunc != nil {
|
|
||||||
conn, err := d.openFunc(dsn)
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.conn == nil {
|
|
||||||
d.conn = newConn()
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var whitespaceRegexp = regexp.MustCompile("\\s")
|
|
||||||
|
|
||||||
func getQueryHash(query string) string {
|
|
||||||
// Remove whitespace and lowercase to make stubbing less brittle
|
|
||||||
query = strings.ToLower(whitespaceRegexp.ReplaceAllString(query, ""))
|
|
||||||
|
|
||||||
h := sha1.New()
|
|
||||||
io.WriteString(h, query)
|
|
||||||
|
|
||||||
return string(h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when db.Query() is called. As with StubQuery() you can use the RowsFromCSVString() method to easily generate the driver.Rows, or you can return your own.
|
|
||||||
func SetQueryFunc(f func(query string) (result driver.Rows, err error)) {
|
|
||||||
SetQueryWithArgsFunc(func(query string, args []driver.Value) (result driver.Rows, err error) {
|
|
||||||
return f(query)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when db.Query() is called. As with StubQuery() you can use the RowsFromCSVString() method to easily generate the driver.Rows, or you can return your own.
|
|
||||||
func SetQueryWithArgsFunc(f func(query string, args []driver.Value) (result driver.Rows, err error)) {
|
|
||||||
d.conn.queryFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubs the global driver.Conn to return the supplied driver.Rows when db.Query() is called, query stubbing is case insensitive, and whitespace is also ignored.
|
|
||||||
func StubQuery(q string, rows driver.Rows) {
|
|
||||||
d.conn.queries[getQueryHash(q)] = query{
|
|
||||||
rows: rows,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubs the global driver.Conn to return the supplied error when db.Query() is called, query stubbing is case insensitive, and whitespace is also ignored.
|
|
||||||
func StubQueryError(q string, err error) {
|
|
||||||
d.conn.queries[getQueryHash(q)] = query{
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when db.Open() is called. You can either hand back a valid connection, or an error. Conn() can be used to grab the global Conn object containing stubbed queries.
|
|
||||||
func SetOpenFunc(f func(dsn string) (driver.Conn, error)) {
|
|
||||||
d.openFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when db.Exec is called. You can return an error or a Result object with the LastInsertId and RowsAffected
|
|
||||||
func SetExecFunc(f func(query string) (driver.Result, error)) {
|
|
||||||
SetExecWithArgsFunc(func(query string, args []driver.Value) (driver.Result, error) {
|
|
||||||
return f(query)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when db.Exec is called. You can return an error or a Result object with the LastInsertId and RowsAffected
|
|
||||||
func SetExecWithArgsFunc(f func(query string, args []driver.Value) (driver.Result, error)) {
|
|
||||||
d.conn.execFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubs the global driver.Conn to return the supplied Result when db.Exec is called, query stubbing is case insensitive, and whitespace is also ignored.
|
|
||||||
func StubExec(q string, r *Result) {
|
|
||||||
d.conn.queries[getQueryHash(q)] = query{
|
|
||||||
result: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubs the global driver.Conn to return the supplied error when db.Exec() is called, query stubbing is case insensitive, and whitespace is also ignored.
|
|
||||||
func StubExecError(q string, err error) {
|
|
||||||
StubQueryError(q, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when db.Begin() is called. You can either hand back a valid transaction, or an error. Conn() can be used to grab the global Conn object containing stubbed queries.
|
|
||||||
func SetBeginFunc(f func() (driver.Tx, error)) {
|
|
||||||
d.conn.beginFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubs the global driver.Conn to return the supplied tx and error when db.Begin() is called.
|
|
||||||
func StubBegin(tx driver.Tx, err error) {
|
|
||||||
SetBeginFunc(func() (driver.Tx, error) {
|
|
||||||
return tx, err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when tx.Commit() is called on the default transcation. Conn() can be used to grab the global Conn object containing stubbed queries.
|
|
||||||
func SetCommitFunc(f func() error) {
|
|
||||||
d.conn.commitFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubs the default transaction to return the supplied error when tx.Commit() is called.
|
|
||||||
func StubCommitError(err error) {
|
|
||||||
SetCommitFunc(func() error {
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set your own function to be executed when tx.Rollback() is called on the default transcation. Conn() can be used to grab the global Conn object containing stubbed queries.
|
|
||||||
func SetRollbackFunc(f func() error) {
|
|
||||||
d.conn.rollbackFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stubs the default transaction to return the supplied error when tx.Rollback() is called.
|
|
||||||
func StubRollbackError(err error) {
|
|
||||||
SetRollbackFunc(func() error {
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clears all stubbed queries, and replaced functions.
|
|
||||||
func Reset() {
|
|
||||||
d.conn = newConn()
|
|
||||||
d.openFunc = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a pointer to the global conn object associated with this driver.
|
|
||||||
func Conn() driver.Conn {
|
|
||||||
return d.conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func RowsFromCSVString(columns []string, s string, c ...rune) driver.Rows {
|
|
||||||
r := strings.NewReader(strings.TrimSpace(s))
|
|
||||||
csvReader := csv.NewReader(r)
|
|
||||||
if len(c) > 0 {
|
|
||||||
csvReader.Comma = c[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
rows := [][]driver.Value{}
|
|
||||||
for {
|
|
||||||
r, err := csvReader.Read()
|
|
||||||
|
|
||||||
if err != nil || r == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
row := make([]driver.Value, len(columns))
|
|
||||||
|
|
||||||
for i, v := range r {
|
|
||||||
v := strings.TrimSpace(v)
|
|
||||||
|
|
||||||
// If enableTimeParsing is on, check to see if this is a
|
|
||||||
// time in RFC33339 format
|
|
||||||
if d.enableTimeParsing {
|
|
||||||
if time, err := time.Parse(time.RFC3339, v); err == nil {
|
|
||||||
row[i] = time
|
|
||||||
} else {
|
|
||||||
row[i] = v
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
row[i] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
|
||||||
|
|
||||||
return RowsFromSlice(columns, rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RowsFromSlice(columns []string, data [][]driver.Value) driver.Rows {
|
|
||||||
return &rows{
|
|
||||||
closed: false,
|
|
||||||
columns: columns,
|
|
||||||
rows: data,
|
|
||||||
pos: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
727
vendor/github.com/erikstmartin/go-testdb/testdb_test.go
generated
vendored
727
vendor/github.com/erikstmartin/go-testdb/testdb_test.go
generated
vendored
@ -1,727 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"errors"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSetOpenFunc(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetOpenFunc(func(dsn string) (driver.Conn, error) {
|
|
||||||
return Conn(), errors.New("test error")
|
|
||||||
})
|
|
||||||
|
|
||||||
// err only returns from this if it's an unknown driver, we are stubbing opening a connection
|
|
||||||
db, _ := sql.Open("testdb", "foo")
|
|
||||||
conn, err := db.Driver().Open("foo")
|
|
||||||
|
|
||||||
if db == nil {
|
|
||||||
t.Fatal("driver.Open not properly set: db was nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn == nil {
|
|
||||||
t.Fatal("driver.Open not properly set: didn't connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err.Error() != "test error" {
|
|
||||||
t.Fatal("driver.Open not properly set: err was not returned properly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQuery(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from foo"
|
|
||||||
columns := []string{"count"}
|
|
||||||
result := `
|
|
||||||
5
|
|
||||||
`
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed query should not return error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Next() {
|
|
||||||
var count int64
|
|
||||||
err = res.Scan(&count)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if count != 5 {
|
|
||||||
t.Fatal("failed to return count")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryAdditionalWhitespace(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sqlWhitespace := "select count(*) from foo"
|
|
||||||
sql := "select count(*) from foo"
|
|
||||||
columns := []string{"count"}
|
|
||||||
result := `
|
|
||||||
5
|
|
||||||
`
|
|
||||||
StubQuery(sqlWhitespace, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed query should not return error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Next() {
|
|
||||||
var count int64
|
|
||||||
err = res.Scan(&count)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if count != 5 {
|
|
||||||
t.Fatal("failed to return count")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryChangeCase(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sqlCase := "SELECT COUNT(*) FROM foo"
|
|
||||||
sql := "select count(*) from foo"
|
|
||||||
columns := []string{"count"}
|
|
||||||
result := `
|
|
||||||
5
|
|
||||||
`
|
|
||||||
StubQuery(sqlCase, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed query should not return error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Next() {
|
|
||||||
var count int64
|
|
||||||
err = res.Scan(&count)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if count != 5 {
|
|
||||||
t.Fatal("failed to return count")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnknownQuery(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from foobar"
|
|
||||||
_, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Unknown queries should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from error"
|
|
||||||
|
|
||||||
StubQueryError(sql, errors.New("test error"))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("failed to return error from stubbed query")
|
|
||||||
}
|
|
||||||
|
|
||||||
if res != nil {
|
|
||||||
t.Fatal("result should be nil on error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryRowError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from error"
|
|
||||||
|
|
||||||
StubQueryError(sql, errors.New("test error"))
|
|
||||||
|
|
||||||
row := db.QueryRow(sql)
|
|
||||||
var count int64
|
|
||||||
err := row.Scan(&count)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("error not returned")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryMultipleResult(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select id, name, age from users"
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
result := `
|
|
||||||
1,tim,20,2012-10-01 01:00:01
|
|
||||||
2,joe,25,2012-10-02 02:00:02
|
|
||||||
3,bob,30,2012-10-03 03:00:03
|
|
||||||
`
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed query should not return error")
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
for res.Next() {
|
|
||||||
var u = user{}
|
|
||||||
err = res.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.id == 0 || u.name == "" || u.age == 0 || u.created == "" {
|
|
||||||
t.Fatal("failed to populate object with result")
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i != 3 {
|
|
||||||
t.Fatal("failed to return proper number of results")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryMultipleResultWithCustomComma(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select id, name, age from users"
|
|
||||||
columns := []string{"id", "name", "age", "data", "created"}
|
|
||||||
result := `
|
|
||||||
1|tim|20|part_1,part_2,part_3|2014-10-16 15:01:00
|
|
||||||
2|joe|25|part_4,part_5,part_6|2014-10-17 15:01:01
|
|
||||||
3|bob|30|part_7,part_8,part_9|2014-10-18 15:01:02
|
|
||||||
`
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result, '|'))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed query should not return error")
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
for res.Next() {
|
|
||||||
var u = user{}
|
|
||||||
err = res.Scan(&u.id, &u.name, &u.age, &u.data, &u.created)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.id == 0 || u.name == "" || u.age == 0 || u.data == "" || u.created == "" {
|
|
||||||
t.Fatal("failed to populate object with result")
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i != 3 {
|
|
||||||
t.Fatal("failed to return proper number of results")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryMultipleResultNewline(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select id, name, age from users"
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
result := "1,tim,20,2012-10-01 01:00:01\n2,joe,25,2012-10-02 02:00:02\n3,bob,30,2012-10-03 03:00:03"
|
|
||||||
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
res, err := db.Query(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed query should not return error")
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
for res.Next() {
|
|
||||||
var u = user{}
|
|
||||||
err = res.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.id == 0 || u.name == "" || u.age == 0 || u.created == "" {
|
|
||||||
t.Fatal("failed to populate object with result")
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i != 3 {
|
|
||||||
t.Fatal("failed to return proper number of results")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetQueryFunc(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
rows := "1,tim,20,2012-10-01 01:00:01\n2,joe,25,2012-10-02 02:00:02\n3,bob,30,2012-10-03 03:00:03"
|
|
||||||
|
|
||||||
SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
return RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if Conn().(*conn).queryFunc == nil {
|
|
||||||
t.Fatal("query function not stubbed")
|
|
||||||
}
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
res, err := db.Query("SELECT foo FROM bar")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
for res.Next() {
|
|
||||||
var u = user{}
|
|
||||||
err = res.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.id == 0 || u.name == "" || u.age == 0 || u.created == "" {
|
|
||||||
t.Fatal("failed to populate object with result")
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i != 3 {
|
|
||||||
t.Fatal("failed to return proper number of results")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetQueryFuncError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
return nil, errors.New("stubbed error")
|
|
||||||
})
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
_, err := db.Query("SELECT foo FROM bar")
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("failed to return error from QueryFunc")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReset(t *testing.T) {
|
|
||||||
sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from error"
|
|
||||||
StubQueryError(sql, errors.New("test error"))
|
|
||||||
|
|
||||||
Reset()
|
|
||||||
|
|
||||||
if len(d.conn.queries) > 0 {
|
|
||||||
t.Fatal("failed to reset connection")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryRow(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from foo"
|
|
||||||
columns := []string{"count"}
|
|
||||||
result := `
|
|
||||||
5
|
|
||||||
`
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
row := db.QueryRow(sql)
|
|
||||||
|
|
||||||
if row == nil {
|
|
||||||
t.Fatal("stub query should have returned row")
|
|
||||||
}
|
|
||||||
|
|
||||||
var count int64
|
|
||||||
err := row.Scan(&count)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if count != 5 {
|
|
||||||
t.Fatal("failed to return count")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubQueryRowReuse(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "select count(*) from foo"
|
|
||||||
columns := []string{"count"}
|
|
||||||
result := `
|
|
||||||
5
|
|
||||||
`
|
|
||||||
StubQuery(sql, RowsFromCSVString(columns, result))
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
rows, _ := db.Query(sql)
|
|
||||||
for rows.Next() {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i != 1 {
|
|
||||||
t.Fatal("stub query should have returned one row")
|
|
||||||
}
|
|
||||||
|
|
||||||
j := i
|
|
||||||
moreRows, _ := db.Query(sql)
|
|
||||||
for moreRows.Next() {
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == j {
|
|
||||||
t.Fatal("stub query did not return another set of rows")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetQueryFuncRow(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
columns := []string{"id", "name", "age", "created"}
|
|
||||||
rows := "1,tim,20,2012-10-01 01:00:01"
|
|
||||||
|
|
||||||
SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
return RowsFromCSVString(columns, rows), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if Conn().(*conn).queryFunc == nil {
|
|
||||||
t.Fatal("query function not stubbed")
|
|
||||||
}
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
row := db.QueryRow("SELECT foo FROM bar")
|
|
||||||
|
|
||||||
var u = user{}
|
|
||||||
err := row.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.id == 0 || u.name == "" || u.age == 0 || u.created == "" {
|
|
||||||
t.Fatal("failed to populate object with result")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetQueryFuncRowError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
SetQueryFunc(func(query string) (result driver.Rows, err error) {
|
|
||||||
return nil, errors.New("Stubbed error")
|
|
||||||
})
|
|
||||||
|
|
||||||
if Conn().(*conn).queryFunc == nil {
|
|
||||||
t.Fatal("query function not stubbed")
|
|
||||||
}
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
row := db.QueryRow("SELECT foo FROM bar")
|
|
||||||
|
|
||||||
var u = user{}
|
|
||||||
err := row.Scan(&u.id, &u.name, &u.age, &u.created)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Did not return error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubExec(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
sql := "INSERT INTO foo SET (foo) VALUES (bar)"
|
|
||||||
StubExec(sql, NewResult(5, errors.New("last insert error"), 3, errors.New("rows affected error")))
|
|
||||||
|
|
||||||
res, err := db.Exec(sql)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed exec call returned unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
var insertId int64
|
|
||||||
insertId, err = res.LastInsertId()
|
|
||||||
if insertId != 5 || err.Error() != "last insert error" {
|
|
||||||
t.Fatal("stubbed exec did not return expected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
var affected int64
|
|
||||||
affected, err = res.RowsAffected()
|
|
||||||
|
|
||||||
if affected != 3 || err.Error() != "rows affected error" {
|
|
||||||
t.Fatal("stubbed exec did not return expected result")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubExecError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
query := "INSERT INTO foo SET (foo) VALUES (bar)"
|
|
||||||
StubExecError(query, errors.New("request failed"))
|
|
||||||
|
|
||||||
res, err := db.Exec(query)
|
|
||||||
|
|
||||||
if reflect.Indirect(reflect.ValueOf(res)).CanAddr() {
|
|
||||||
t.Fatal("stubbed exec returned unexpected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "request failed" {
|
|
||||||
t.Fatal("stubbed exec call did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubExecFunc(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
query := "INSERT INTO foo SET (foo) VALUES (bar)"
|
|
||||||
result := NewResult(5, errors.New("last insert error"), 3, errors.New("rows affected error"))
|
|
||||||
|
|
||||||
SetExecFunc(func(query string) (driver.Result, error) {
|
|
||||||
return result, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
res, err := db.Exec(query)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("stubbed exec returned unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
var insertId int64
|
|
||||||
insertId, err = res.LastInsertId()
|
|
||||||
if insertId != 5 || err.Error() != "last insert error" {
|
|
||||||
t.Fatal("stubbed exec did not return expected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
var affected int64
|
|
||||||
affected, err = res.RowsAffected()
|
|
||||||
|
|
||||||
if affected != 3 || err.Error() != "rows affected error" {
|
|
||||||
t.Fatal("stubbed exec did not return expected result")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubExecFuncError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
query := "INSERT INTO foo SET (foo) VALUES (bar)"
|
|
||||||
|
|
||||||
SetExecFunc(func(query string) (driver.Result, error) {
|
|
||||||
return nil, errors.New("request failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
res, err := db.Exec(query)
|
|
||||||
|
|
||||||
if res != nil {
|
|
||||||
t.Fatal("stubbed exec unexpected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "request failed" {
|
|
||||||
t.Fatal("stubbed exec did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetBeginFunc(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
SetBeginFunc(func() (driver.Tx, error) {
|
|
||||||
return nil, errors.New("begin failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
res, err := db.Begin()
|
|
||||||
|
|
||||||
if res != nil {
|
|
||||||
t.Fatal("stubbed begin unexpected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "begin failed" {
|
|
||||||
t.Fatal("stubbed begin did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubBegin(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
StubBegin(nil, errors.New("begin failed"))
|
|
||||||
res, err := db.Begin()
|
|
||||||
|
|
||||||
if res != nil {
|
|
||||||
t.Fatal("stubbed begin unexpected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "begin failed" {
|
|
||||||
t.Fatal("stubbed begin did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetCommitFunc(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
SetCommitFunc(func() error {
|
|
||||||
return errors.New("commit failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
tx, err := db.Begin()
|
|
||||||
|
|
||||||
if tx == nil {
|
|
||||||
t.Fatal("begin expected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("begin returned unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Commit()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "commit failed" {
|
|
||||||
t.Fatal("stubbed commit did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubCommitError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
StubCommitError(errors.New("commit failed"))
|
|
||||||
|
|
||||||
tx, err := db.Begin()
|
|
||||||
|
|
||||||
if tx == nil {
|
|
||||||
t.Fatal("begin expected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("begin returned unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Commit()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "commit failed" {
|
|
||||||
t.Fatal("stubbed commit did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetRollbackFunc(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
SetRollbackFunc(func() error {
|
|
||||||
return errors.New("rollback failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
tx, err := db.Begin()
|
|
||||||
|
|
||||||
if tx == nil {
|
|
||||||
t.Fatal("begin expected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("begin returned unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Rollback()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "rollback failed" {
|
|
||||||
t.Fatal("stubbed rollback did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStubRollbackError(t *testing.T) {
|
|
||||||
defer Reset()
|
|
||||||
|
|
||||||
db, _ := sql.Open("testdb", "")
|
|
||||||
|
|
||||||
StubRollbackError(errors.New("rollback failed"))
|
|
||||||
|
|
||||||
tx, err := db.Begin()
|
|
||||||
|
|
||||||
if tx == nil {
|
|
||||||
t.Fatal("begin expected result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("begin returned unexpected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Rollback()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "rollback failed" {
|
|
||||||
t.Fatal("stubbed rollback did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
vendor/github.com/erikstmartin/go-testdb/tx.go
generated
vendored
40
vendor/github.com/erikstmartin/go-testdb/tx.go
generated
vendored
@ -1,40 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
type Tx struct {
|
|
||||||
commitFunc func() error
|
|
||||||
rollbackFunc func() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tx) Commit() error {
|
|
||||||
if t.commitFunc != nil {
|
|
||||||
return t.commitFunc()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tx) Rollback() error {
|
|
||||||
if t.rollbackFunc != nil {
|
|
||||||
return t.rollbackFunc()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tx) SetCommitFunc(f func() error) {
|
|
||||||
t.commitFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tx) StubCommitError(err error) {
|
|
||||||
t.SetCommitFunc(func() error {
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tx) SetRollbackFunc(f func() error) {
|
|
||||||
t.rollbackFunc = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tx) StubRollbackError(err error) {
|
|
||||||
t.SetRollbackFunc(func() error {
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
58
vendor/github.com/erikstmartin/go-testdb/tx_test.go
generated
vendored
58
vendor/github.com/erikstmartin/go-testdb/tx_test.go
generated
vendored
@ -1,58 +0,0 @@
|
|||||||
package testdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTxSetCommitFunc(t *testing.T) {
|
|
||||||
tx := &Tx{}
|
|
||||||
|
|
||||||
tx.SetCommitFunc(func() error {
|
|
||||||
return errors.New("commit failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
err := tx.Commit()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "commit failed" {
|
|
||||||
t.Fatal("stubbed commit did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTxStubCommitError(t *testing.T) {
|
|
||||||
tx := &Tx{}
|
|
||||||
|
|
||||||
tx.StubCommitError(errors.New("commit failed"))
|
|
||||||
|
|
||||||
err := tx.Commit()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "commit failed" {
|
|
||||||
t.Fatal("stubbed commit did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTxSetRollbackFunc(t *testing.T) {
|
|
||||||
tx := &Tx{}
|
|
||||||
|
|
||||||
tx.SetRollbackFunc(func() error {
|
|
||||||
return errors.New("rollback failed")
|
|
||||||
})
|
|
||||||
|
|
||||||
err := tx.Rollback()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "rollback failed" {
|
|
||||||
t.Fatal("stubbed rollback did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTxStubRollbackError(t *testing.T) {
|
|
||||||
tx := &Tx{}
|
|
||||||
|
|
||||||
tx.StubRollbackError(errors.New("rollback failed"))
|
|
||||||
|
|
||||||
err := tx.Rollback()
|
|
||||||
|
|
||||||
if err == nil || err.Error() != "rollback failed" {
|
|
||||||
t.Fatal("stubbed rollback did not return expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
vendor/github.com/fatih/structs/.gitignore
generated
vendored
23
vendor/github.com/fatih/structs/.gitignore
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
*.test
|
|
||||||
13
vendor/github.com/fatih/structs/.travis.yml
generated
vendored
13
vendor/github.com/fatih/structs/.travis.yml
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- tip
|
|
||||||
sudo: false
|
|
||||||
before_install:
|
|
||||||
- go get github.com/axw/gocov/gocov
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
|
||||||
script:
|
|
||||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
|
||||||
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
21
vendor/github.com/fatih/structs/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Fatih Arslan
|
|
||||||
|
|
||||||
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.
|
|
||||||
163
vendor/github.com/fatih/structs/README.md
generated
vendored
163
vendor/github.com/fatih/structs/README.md
generated
vendored
@ -1,163 +0,0 @@
|
|||||||
# Structs [](http://godoc.org/github.com/fatih/structs) [](https://travis-ci.org/fatih/structs) [](https://coveralls.io/r/fatih/structs)
|
|
||||||
|
|
||||||
Structs contains various utilities to work with Go (Golang) structs. It was
|
|
||||||
initially used by me to convert a struct into a `map[string]interface{}`. With
|
|
||||||
time I've added other utilities for structs. It's basically a high level
|
|
||||||
package based on primitives from the reflect package. Feel free to add new
|
|
||||||
functions or improve the existing code.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/fatih/structs
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage and Examples
|
|
||||||
|
|
||||||
Just like the standard lib `strings`, `bytes` and co packages, `structs` has
|
|
||||||
many global functions to manipulate or organize your struct data. Lets define
|
|
||||||
and declare a struct:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Server struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
ID int
|
|
||||||
Enabled bool
|
|
||||||
users []string // not exported
|
|
||||||
http.Server // embedded
|
|
||||||
}
|
|
||||||
|
|
||||||
server := &Server{
|
|
||||||
Name: "gopher",
|
|
||||||
ID: 123456,
|
|
||||||
Enabled: true,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Convert a struct to a map[string]interface{}
|
|
||||||
// => {"Name":"gopher", "ID":123456, "Enabled":true}
|
|
||||||
m := structs.Map(server)
|
|
||||||
|
|
||||||
// Convert the values of a struct to a []interface{}
|
|
||||||
// => ["gopher", 123456, true]
|
|
||||||
v := structs.Values(server)
|
|
||||||
|
|
||||||
// Convert the names of a struct to a []string
|
|
||||||
// (see "Names methods" for more info about fields)
|
|
||||||
n := structs.Names(server)
|
|
||||||
|
|
||||||
// Convert the values of a struct to a []*Field
|
|
||||||
// (see "Field methods" for more info about fields)
|
|
||||||
f := structs.Fields(server)
|
|
||||||
|
|
||||||
// Return the struct name => "Server"
|
|
||||||
n := structs.Name(server)
|
|
||||||
|
|
||||||
// Check if any field of a struct is initialized or not.
|
|
||||||
h := structs.HasZero(server)
|
|
||||||
|
|
||||||
// Check if all fields of a struct is initialized or not.
|
|
||||||
z := structs.IsZero(server)
|
|
||||||
|
|
||||||
// Check if server is a struct or a pointer to struct
|
|
||||||
i := structs.IsStruct(server)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Struct methods
|
|
||||||
|
|
||||||
The structs functions can be also used as independent methods by creating a new
|
|
||||||
`*structs.Struct`. This is handy if you want to have more control over the
|
|
||||||
structs (such as retrieving a single Field).
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create a new struct type:
|
|
||||||
s := structs.New(server)
|
|
||||||
|
|
||||||
m := s.Map() // Get a map[string]interface{}
|
|
||||||
v := s.Values() // Get a []interface{}
|
|
||||||
f := s.Fields() // Get a []*Field
|
|
||||||
n := s.Names() // Get a []string
|
|
||||||
f := s.Field(name) // Get a *Field based on the given field name
|
|
||||||
f, ok := s.FieldOk(name) // Get a *Field based on the given field name
|
|
||||||
n := s.Name() // Get the struct name
|
|
||||||
h := s.HasZero() // Check if any field is uninitialized
|
|
||||||
z := s.IsZero() // Check if all fields are uninitialized
|
|
||||||
```
|
|
||||||
|
|
||||||
### Field methods
|
|
||||||
|
|
||||||
We can easily examine a single Field for more detail. Below you can see how we
|
|
||||||
get and interact with various field methods:
|
|
||||||
|
|
||||||
|
|
||||||
```go
|
|
||||||
s := structs.New(server)
|
|
||||||
|
|
||||||
// Get the Field struct for the "Name" field
|
|
||||||
name := s.Field("Name")
|
|
||||||
|
|
||||||
// Get the underlying value, value => "gopher"
|
|
||||||
value := name.Value().(string)
|
|
||||||
|
|
||||||
// Set the field's value
|
|
||||||
name.Set("another gopher")
|
|
||||||
|
|
||||||
// Get the field's kind, kind => "string"
|
|
||||||
name.Kind()
|
|
||||||
|
|
||||||
// Check if the field is exported or not
|
|
||||||
if name.IsExported() {
|
|
||||||
fmt.Println("Name field is exported")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the value is a zero value, such as "" for string, 0 for int
|
|
||||||
if !name.IsZero() {
|
|
||||||
fmt.Println("Name is initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the field is an anonymous (embedded) field
|
|
||||||
if !name.IsEmbedded() {
|
|
||||||
fmt.Println("Name is not an embedded field")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the Field's tag value for tag name "json", tag value => "name,omitempty"
|
|
||||||
tagValue := name.Tag("json")
|
|
||||||
```
|
|
||||||
|
|
||||||
Nested structs are supported too:
|
|
||||||
|
|
||||||
```go
|
|
||||||
addrField := s.Field("Server").Field("Addr")
|
|
||||||
|
|
||||||
// Get the value for addr
|
|
||||||
a := addrField.Value().(string)
|
|
||||||
|
|
||||||
// Or get all fields
|
|
||||||
httpServer := s.Field("Server").Fields()
|
|
||||||
```
|
|
||||||
|
|
||||||
We can also get a slice of Fields from the Struct type to iterate over all
|
|
||||||
fields. This is handy if you wish to examine all fields:
|
|
||||||
|
|
||||||
```go
|
|
||||||
s := structs.New(server)
|
|
||||||
|
|
||||||
for _, f := range s.Fields() {
|
|
||||||
fmt.Printf("field name: %+v\n", f.Name())
|
|
||||||
|
|
||||||
if f.IsExported() {
|
|
||||||
fmt.Printf("value : %+v\n", f.Value())
|
|
||||||
fmt.Printf("is zero : %+v\n", f.IsZero())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
* [Fatih Arslan](https://github.com/fatih)
|
|
||||||
* [Cihangir Savas](https://github.com/cihangir)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License (MIT) - see LICENSE.md for more details
|
|
||||||
141
vendor/github.com/fatih/structs/field.go
generated
vendored
141
vendor/github.com/fatih/structs/field.go
generated
vendored
@ -1,141 +0,0 @@
|
|||||||
package structs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errNotExported = errors.New("field is not exported")
|
|
||||||
errNotSettable = errors.New("field is not settable")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Field represents a single struct field that encapsulates high level
|
|
||||||
// functions around the field.
|
|
||||||
type Field struct {
|
|
||||||
value reflect.Value
|
|
||||||
field reflect.StructField
|
|
||||||
defaultTag string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tag returns the value associated with key in the tag string. If there is no
|
|
||||||
// such key in the tag, Tag returns the empty string.
|
|
||||||
func (f *Field) Tag(key string) string {
|
|
||||||
return f.field.Tag.Get(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the underlying value of the field. It panics if the field
|
|
||||||
// is not exported.
|
|
||||||
func (f *Field) Value() interface{} {
|
|
||||||
return f.value.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
|
||||||
func (f *Field) IsEmbedded() bool {
|
|
||||||
return f.field.Anonymous
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsExported returns true if the given field is exported.
|
|
||||||
func (f *Field) IsExported() bool {
|
|
||||||
return f.field.PkgPath == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsZero returns true if the given field is not initialized (has a zero value).
|
|
||||||
// It panics if the field is not exported.
|
|
||||||
func (f *Field) IsZero() bool {
|
|
||||||
zero := reflect.Zero(f.value.Type()).Interface()
|
|
||||||
current := f.Value()
|
|
||||||
|
|
||||||
return reflect.DeepEqual(current, zero)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the given field
|
|
||||||
func (f *Field) Name() string {
|
|
||||||
return f.field.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kind returns the fields kind, such as "string", "map", "bool", etc ..
|
|
||||||
func (f *Field) Kind() reflect.Kind {
|
|
||||||
return f.value.Kind()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the field to given value v. It returns an error if the field is not
|
|
||||||
// settable (not addressable or not exported) or if the given value's type
|
|
||||||
// doesn't match the fields type.
|
|
||||||
func (f *Field) Set(val interface{}) error {
|
|
||||||
// we can't set unexported fields, so be sure this field is exported
|
|
||||||
if !f.IsExported() {
|
|
||||||
return errNotExported
|
|
||||||
}
|
|
||||||
|
|
||||||
// do we get here? not sure...
|
|
||||||
if !f.value.CanSet() {
|
|
||||||
return errNotSettable
|
|
||||||
}
|
|
||||||
|
|
||||||
given := reflect.ValueOf(val)
|
|
||||||
|
|
||||||
if f.value.Kind() != given.Kind() {
|
|
||||||
return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
f.value.Set(given)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero sets the field to its zero value. It returns an error if the field is not
|
|
||||||
// settable (not addressable or not exported).
|
|
||||||
func (f *Field) Zero() error {
|
|
||||||
zero := reflect.Zero(f.value.Type()).Interface()
|
|
||||||
return f.Set(zero)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fields returns a slice of Fields. This is particular handy to get the fields
|
|
||||||
// of a nested struct . A struct tag with the content of "-" ignores the
|
|
||||||
// checking of that particular field. Example:
|
|
||||||
//
|
|
||||||
// // Field is ignored by this package.
|
|
||||||
// Field *http.Request `structs:"-"`
|
|
||||||
//
|
|
||||||
// It panics if field is not exported or if field's kind is not struct
|
|
||||||
func (f *Field) Fields() []*Field {
|
|
||||||
return getFields(f.value, f.defaultTag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field returns the field from a nested struct. It panics if the nested struct
|
|
||||||
// is not exported or if the field was not found.
|
|
||||||
func (f *Field) Field(name string) *Field {
|
|
||||||
field, ok := f.FieldOk(name)
|
|
||||||
if !ok {
|
|
||||||
panic("field not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
|
|
||||||
// FieldOk returns the field from a nested struct. The boolean returns whether
|
|
||||||
// the field was found (true) or not (false).
|
|
||||||
func (f *Field) FieldOk(name string) (*Field, bool) {
|
|
||||||
value := &f.value
|
|
||||||
// value must be settable so we need to make sure it holds the address of the
|
|
||||||
// variable and not a copy, so we can pass the pointer to strctVal instead of a
|
|
||||||
// copy (which is not assigned to any variable, hence not settable).
|
|
||||||
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
|
|
||||||
if f.value.Kind() != reflect.Ptr {
|
|
||||||
a := f.value.Addr()
|
|
||||||
value = &a
|
|
||||||
}
|
|
||||||
v := strctVal(value.Interface())
|
|
||||||
t := v.Type()
|
|
||||||
|
|
||||||
field, ok := t.FieldByName(name)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Field{
|
|
||||||
field: field,
|
|
||||||
value: v.FieldByName(name),
|
|
||||||
}, true
|
|
||||||
}
|
|
||||||
397
vendor/github.com/fatih/structs/field_test.go
generated
vendored
397
vendor/github.com/fatih/structs/field_test.go
generated
vendored
@ -1,397 +0,0 @@
|
|||||||
package structs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A test struct that defines all cases
|
|
||||||
type Foo struct {
|
|
||||||
A string
|
|
||||||
B int `structs:"y"`
|
|
||||||
C bool `json:"c"`
|
|
||||||
d string // not exported
|
|
||||||
E *Baz
|
|
||||||
x string `xml:"x"` // not exported, with tag
|
|
||||||
Y []string
|
|
||||||
Z map[string]interface{}
|
|
||||||
*Bar // embedded
|
|
||||||
}
|
|
||||||
|
|
||||||
type Baz struct {
|
|
||||||
A string
|
|
||||||
B int
|
|
||||||
}
|
|
||||||
|
|
||||||
type Bar struct {
|
|
||||||
E string
|
|
||||||
F int
|
|
||||||
g []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStruct() *Struct {
|
|
||||||
b := &Bar{
|
|
||||||
E: "example",
|
|
||||||
F: 2,
|
|
||||||
g: []string{"zeynep", "fatih"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// B and x is not initialized for testing
|
|
||||||
f := &Foo{
|
|
||||||
A: "gopher",
|
|
||||||
C: true,
|
|
||||||
d: "small",
|
|
||||||
E: nil,
|
|
||||||
Y: []string{"example"},
|
|
||||||
Z: nil,
|
|
||||||
}
|
|
||||||
f.Bar = b
|
|
||||||
|
|
||||||
return New(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Set(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
f := s.Field("A")
|
|
||||||
err := f.Set("fatih")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Value().(string) != "fatih" {
|
|
||||||
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("Y")
|
|
||||||
err = f.Set([]string{"override", "with", "this"})
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sliceLen := len(f.Value().([]string))
|
|
||||||
if sliceLen != 3 {
|
|
||||||
t.Errorf("Setted values slice length is wrong: %d, want: %d", sliceLen, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("C")
|
|
||||||
err = f.Set(false)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Value().(bool) {
|
|
||||||
t.Errorf("Setted value is wrong: %t want: %t", f.Value().(bool), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's pass a different type
|
|
||||||
f = s.Field("A")
|
|
||||||
err = f.Set(123) // Field A is of type string, but we are going to pass an integer
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Setting a field's value with a different type than the field's type should return an error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// old value should be still there :)
|
|
||||||
if f.Value().(string) != "fatih" {
|
|
||||||
t.Errorf("Setted value is wrong: %s want: %s", f.Value().(string), "fatih")
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's access an unexported field, which should give an error
|
|
||||||
f = s.Field("d")
|
|
||||||
err = f.Set("large")
|
|
||||||
if err != errNotExported {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's set a pointer to struct
|
|
||||||
b := &Bar{
|
|
||||||
E: "gopher",
|
|
||||||
F: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("Bar")
|
|
||||||
err = f.Set(b)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
baz := &Baz{
|
|
||||||
A: "helloWorld",
|
|
||||||
B: 42,
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("E")
|
|
||||||
err = f.Set(baz)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ba := s.Field("E").Value().(*Baz)
|
|
||||||
|
|
||||||
if ba.A != "helloWorld" {
|
|
||||||
t.Errorf("could not set baz. Got: %s Want: helloWorld", ba.A)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_NotSettable(t *testing.T) {
|
|
||||||
a := map[int]Baz{
|
|
||||||
4: {
|
|
||||||
A: "value",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
s := New(a[4])
|
|
||||||
|
|
||||||
if err := s.Field("A").Set("newValue"); err != errNotSettable {
|
|
||||||
t.Errorf("Trying to set non-settable field should error with %q. Got %q instead.", errNotSettable, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Zero(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
f := s.Field("A")
|
|
||||||
err := f.Zero()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Value().(string) != "" {
|
|
||||||
t.Errorf("Zeroed value is wrong: %s want: %s", f.Value().(string), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("Y")
|
|
||||||
err = f.Zero()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sliceLen := len(f.Value().([]string))
|
|
||||||
if sliceLen != 0 {
|
|
||||||
t.Errorf("Zeroed values slice length is wrong: %d, want: %d", sliceLen, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("C")
|
|
||||||
err = f.Zero()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Value().(bool) {
|
|
||||||
t.Errorf("Zeroed value is wrong: %t want: %t", f.Value().(bool), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's access an unexported field, which should give an error
|
|
||||||
f = s.Field("d")
|
|
||||||
err = f.Zero()
|
|
||||||
if err != errNotExported {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("Bar")
|
|
||||||
err = f.Zero()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("E")
|
|
||||||
err = f.Zero()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
v := s.Field("E").value
|
|
||||||
if !v.IsNil() {
|
|
||||||
t.Errorf("could not set baz. Got: %s Want: <nil>", v.Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
err := recover()
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Retrieveing a non existing field from the struct should panic")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
_ = s.Field("no-field")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Kind(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
f := s.Field("A")
|
|
||||||
if f.Kind() != reflect.String {
|
|
||||||
t.Errorf("Field A has wrong kind: %s want: %s", f.Kind(), reflect.String)
|
|
||||||
}
|
|
||||||
|
|
||||||
f = s.Field("B")
|
|
||||||
if f.Kind() != reflect.Int {
|
|
||||||
t.Errorf("Field B has wrong kind: %s want: %s", f.Kind(), reflect.Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unexported
|
|
||||||
f = s.Field("d")
|
|
||||||
if f.Kind() != reflect.String {
|
|
||||||
t.Errorf("Field d has wrong kind: %s want: %s", f.Kind(), reflect.String)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Tag(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
v := s.Field("B").Tag("json")
|
|
||||||
if v != "" {
|
|
||||||
t.Errorf("Field's tag value of a non existing tag should return empty, got: %s", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
v = s.Field("C").Tag("json")
|
|
||||||
if v != "c" {
|
|
||||||
t.Errorf("Field's tag value of the existing field C should return 'c', got: %s", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
v = s.Field("d").Tag("json")
|
|
||||||
if v != "" {
|
|
||||||
t.Errorf("Field's tag value of a non exported field should return empty, got: %s", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
v = s.Field("x").Tag("xml")
|
|
||||||
if v != "x" {
|
|
||||||
t.Errorf("Field's tag value of a non exported field with a tag should return 'x', got: %s", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
v = s.Field("A").Tag("json")
|
|
||||||
if v != "" {
|
|
||||||
t.Errorf("Field's tag value of a existing field without a tag should return empty, got: %s", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Value(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
v := s.Field("A").Value()
|
|
||||||
val, ok := v.(string)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("Field's value of a A should be string")
|
|
||||||
}
|
|
||||||
|
|
||||||
if val != "gopher" {
|
|
||||||
t.Errorf("Field's value of a existing tag should return 'gopher', got: %s", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
err := recover()
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Value of a non exported field from the field should panic")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// should panic
|
|
||||||
_ = s.Field("d").Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_IsEmbedded(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
if !s.Field("Bar").IsEmbedded() {
|
|
||||||
t.Errorf("Fields 'Bar' field is an embedded field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Field("d").IsEmbedded() {
|
|
||||||
t.Errorf("Fields 'd' field is not an embedded field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_IsExported(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
if !s.Field("Bar").IsExported() {
|
|
||||||
t.Errorf("Fields 'Bar' field is an exported field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.Field("A").IsExported() {
|
|
||||||
t.Errorf("Fields 'A' field is an exported field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Field("d").IsExported() {
|
|
||||||
t.Errorf("Fields 'd' field is not an exported field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_IsZero(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
if s.Field("A").IsZero() {
|
|
||||||
t.Errorf("Fields 'A' field is an initialized field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.Field("B").IsZero() {
|
|
||||||
t.Errorf("Fields 'B' field is not an initialized field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Name(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
if s.Field("A").Name() != "A" {
|
|
||||||
t.Errorf("Fields 'A' field should have the name 'A'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Field(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
e := s.Field("Bar").Field("E")
|
|
||||||
|
|
||||||
val, ok := e.Value().(string)
|
|
||||||
if !ok {
|
|
||||||
t.Error("The value of the field 'e' inside 'Bar' struct should be string")
|
|
||||||
}
|
|
||||||
|
|
||||||
if val != "example" {
|
|
||||||
t.Errorf("The value of 'e' should be 'example, got: %s", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
err := recover()
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Field of a non existing nested struct should panic")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
_ = s.Field("Bar").Field("e")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_Fields(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
fields := s.Field("Bar").Fields()
|
|
||||||
|
|
||||||
if len(fields) != 3 {
|
|
||||||
t.Errorf("We expect 3 fields in embedded struct, was: %d", len(fields))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestField_FieldOk(t *testing.T) {
|
|
||||||
s := newStruct()
|
|
||||||
|
|
||||||
b, ok := s.FieldOk("Bar")
|
|
||||||
if !ok {
|
|
||||||
t.Error("The field 'Bar' should exists.")
|
|
||||||
}
|
|
||||||
|
|
||||||
e, ok := b.FieldOk("E")
|
|
||||||
if !ok {
|
|
||||||
t.Error("The field 'E' should exists.")
|
|
||||||
}
|
|
||||||
|
|
||||||
val, ok := e.Value().(string)
|
|
||||||
if !ok {
|
|
||||||
t.Error("The value of the field 'e' inside 'Bar' struct should be string")
|
|
||||||
}
|
|
||||||
|
|
||||||
if val != "example" {
|
|
||||||
t.Errorf("The value of 'e' should be 'example, got: %s", val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
584
vendor/github.com/fatih/structs/structs.go
generated
vendored
@ -1,584 +0,0 @@
|
|||||||
// Package structs contains various utilities functions to work with structs.
|
|
||||||
package structs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultTagName is the default tag name for struct fields which provides
|
|
||||||
// a more granular to tweak certain structs. Lookup the necessary functions
|
|
||||||
// for more info.
|
|
||||||
DefaultTagName = "structs" // struct's field default tag name
|
|
||||||
)
|
|
||||||
|
|
||||||
// Struct encapsulates a struct type to provide several high level functions
|
|
||||||
// around the struct.
|
|
||||||
type Struct struct {
|
|
||||||
raw interface{}
|
|
||||||
value reflect.Value
|
|
||||||
TagName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new *Struct with the struct s. It panics if the s's kind is
|
|
||||||
// not struct.
|
|
||||||
func New(s interface{}) *Struct {
|
|
||||||
return &Struct{
|
|
||||||
raw: s,
|
|
||||||
value: strctVal(s),
|
|
||||||
TagName: DefaultTagName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map converts the given struct to a map[string]interface{}, where the keys
|
|
||||||
// of the map are the field names and the values of the map the associated
|
|
||||||
// values of the fields. The default key string is the struct field name but
|
|
||||||
// can be changed in the struct field's tag value. The "structs" key in the
|
|
||||||
// struct's field tag value is the key name. Example:
|
|
||||||
//
|
|
||||||
// // Field appears in map as key "myName".
|
|
||||||
// Name string `structs:"myName"`
|
|
||||||
//
|
|
||||||
// A tag value with the content of "-" ignores that particular field. Example:
|
|
||||||
//
|
|
||||||
// // Field is ignored by this package.
|
|
||||||
// Field bool `structs:"-"`
|
|
||||||
//
|
|
||||||
// A tag value with the content of "string" uses the stringer to get the value. Example:
|
|
||||||
//
|
|
||||||
// // The value will be output of Animal's String() func.
|
|
||||||
// // Map will panic if Animal does not implement String().
|
|
||||||
// Field *Animal `structs:"field,string"`
|
|
||||||
//
|
|
||||||
// A tag value with the option of "flatten" used in a struct field is to flatten its fields
|
|
||||||
// in the output map. Example:
|
|
||||||
//
|
|
||||||
// // The FieldStruct's fields will be flattened into the output map.
|
|
||||||
// FieldStruct time.Time `structs:",flatten"`
|
|
||||||
//
|
|
||||||
// A tag value with the option of "omitnested" stops iterating further if the type
|
|
||||||
// is a struct. Example:
|
|
||||||
//
|
|
||||||
// // Field is not processed further by this package.
|
|
||||||
// Field time.Time `structs:"myName,omitnested"`
|
|
||||||
// Field *http.Request `structs:",omitnested"`
|
|
||||||
//
|
|
||||||
// A tag value with the option of "omitempty" ignores that particular field if
|
|
||||||
// the field value is empty. Example:
|
|
||||||
//
|
|
||||||
// // Field appears in map as key "myName", but the field is
|
|
||||||
// // skipped if empty.
|
|
||||||
// Field string `structs:"myName,omitempty"`
|
|
||||||
//
|
|
||||||
// // Field appears in map as key "Field" (the default), but
|
|
||||||
// // the field is skipped if empty.
|
|
||||||
// Field string `structs:",omitempty"`
|
|
||||||
//
|
|
||||||
// Note that only exported fields of a struct can be accessed, non exported
|
|
||||||
// fields will be neglected.
|
|
||||||
func (s *Struct) Map() map[string]interface{} {
|
|
||||||
out := make(map[string]interface{})
|
|
||||||
s.FillMap(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
|
||||||
// given map.
|
|
||||||
func (s *Struct) FillMap(out map[string]interface{}) {
|
|
||||||
if out == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := s.structFields()
|
|
||||||
|
|
||||||
for _, field := range fields {
|
|
||||||
name := field.Name
|
|
||||||
val := s.value.FieldByName(name)
|
|
||||||
isSubStruct := false
|
|
||||||
var finalVal interface{}
|
|
||||||
|
|
||||||
tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
||||||
if tagName != "" {
|
|
||||||
name = tagName
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the value is a zero value and the field is marked as omitempty do
|
|
||||||
// not include
|
|
||||||
if tagOpts.Has("omitempty") {
|
|
||||||
zero := reflect.Zero(val.Type()).Interface()
|
|
||||||
current := val.Interface()
|
|
||||||
|
|
||||||
if reflect.DeepEqual(current, zero) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tagOpts.Has("omitnested") {
|
|
||||||
finalVal = s.nested(val)
|
|
||||||
|
|
||||||
v := reflect.ValueOf(val.Interface())
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Map, reflect.Struct:
|
|
||||||
isSubStruct = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
finalVal = val.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
if tagOpts.Has("string") {
|
|
||||||
s, ok := val.Interface().(fmt.Stringer)
|
|
||||||
if ok {
|
|
||||||
out[name] = s.String()
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if isSubStruct && (tagOpts.Has("flatten")) {
|
|
||||||
for k := range finalVal.(map[string]interface{}) {
|
|
||||||
out[k] = finalVal.(map[string]interface{})[k]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out[name] = finalVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Values converts the given s struct's field values to a []interface{}. A
|
|
||||||
// struct tag with the content of "-" ignores the that particular field.
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// // Field is ignored by this package.
|
|
||||||
// Field int `structs:"-"`
|
|
||||||
//
|
|
||||||
// A value with the option of "omitnested" stops iterating further if the type
|
|
||||||
// is a struct. Example:
|
|
||||||
//
|
|
||||||
// // Fields is not processed further by this package.
|
|
||||||
// Field time.Time `structs:",omitnested"`
|
|
||||||
// Field *http.Request `structs:",omitnested"`
|
|
||||||
//
|
|
||||||
// A tag value with the option of "omitempty" ignores that particular field and
|
|
||||||
// is not added to the values if the field value is empty. Example:
|
|
||||||
//
|
|
||||||
// // Field is skipped if empty
|
|
||||||
// Field string `structs:",omitempty"`
|
|
||||||
//
|
|
||||||
// Note that only exported fields of a struct can be accessed, non exported
|
|
||||||
// fields will be neglected.
|
|
||||||
func (s *Struct) Values() []interface{} {
|
|
||||||
fields := s.structFields()
|
|
||||||
|
|
||||||
var t []interface{}
|
|
||||||
|
|
||||||
for _, field := range fields {
|
|
||||||
val := s.value.FieldByName(field.Name)
|
|
||||||
|
|
||||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
||||||
|
|
||||||
// if the value is a zero value and the field is marked as omitempty do
|
|
||||||
// not include
|
|
||||||
if tagOpts.Has("omitempty") {
|
|
||||||
zero := reflect.Zero(val.Type()).Interface()
|
|
||||||
current := val.Interface()
|
|
||||||
|
|
||||||
if reflect.DeepEqual(current, zero) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if tagOpts.Has("string") {
|
|
||||||
s, ok := val.Interface().(fmt.Stringer)
|
|
||||||
if ok {
|
|
||||||
t = append(t, s.String())
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
||||||
// look out for embedded structs, and convert them to a
|
|
||||||
// []interface{} to be added to the final values slice
|
|
||||||
t = append(t, Values(val.Interface())...)
|
|
||||||
} else {
|
|
||||||
t = append(t, val.Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fields returns a slice of Fields. A struct tag with the content of "-"
|
|
||||||
// ignores the checking of that particular field. Example:
|
|
||||||
//
|
|
||||||
// // Field is ignored by this package.
|
|
||||||
// Field bool `structs:"-"`
|
|
||||||
//
|
|
||||||
// It panics if s's kind is not struct.
|
|
||||||
func (s *Struct) Fields() []*Field {
|
|
||||||
return getFields(s.value, s.TagName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names returns a slice of field names. A struct tag with the content of "-"
|
|
||||||
// ignores the checking of that particular field. Example:
|
|
||||||
//
|
|
||||||
// // Field is ignored by this package.
|
|
||||||
// Field bool `structs:"-"`
|
|
||||||
//
|
|
||||||
// It panics if s's kind is not struct.
|
|
||||||
func (s *Struct) Names() []string {
|
|
||||||
fields := getFields(s.value, s.TagName)
|
|
||||||
|
|
||||||
names := make([]string, len(fields))
|
|
||||||
|
|
||||||
for i, field := range fields {
|
|
||||||
names[i] = field.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFields(v reflect.Value, tagName string) []*Field {
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
t := v.Type()
|
|
||||||
|
|
||||||
var fields []*Field
|
|
||||||
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
|
|
||||||
if tag := field.Tag.Get(tagName); tag == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
f := &Field{
|
|
||||||
field: field,
|
|
||||||
value: v.FieldByName(field.Name),
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = append(fields, f)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field returns a new Field struct that provides several high level functions
|
|
||||||
// around a single struct field entity. It panics if the field is not found.
|
|
||||||
func (s *Struct) Field(name string) *Field {
|
|
||||||
f, ok := s.FieldOk(name)
|
|
||||||
if !ok {
|
|
||||||
panic("field not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// FieldOk returns a new Field struct that provides several high level functions
|
|
||||||
// around a single struct field entity. The boolean returns true if the field
|
|
||||||
// was found.
|
|
||||||
func (s *Struct) FieldOk(name string) (*Field, bool) {
|
|
||||||
t := s.value.Type()
|
|
||||||
|
|
||||||
field, ok := t.FieldByName(name)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Field{
|
|
||||||
field: field,
|
|
||||||
value: s.value.FieldByName(name),
|
|
||||||
defaultTag: s.TagName,
|
|
||||||
}, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsZero returns true if all fields in a struct is a zero value (not
|
|
||||||
// initialized) A struct tag with the content of "-" ignores the checking of
|
|
||||||
// that particular field. Example:
|
|
||||||
//
|
|
||||||
// // Field is ignored by this package.
|
|
||||||
// Field bool `structs:"-"`
|
|
||||||
//
|
|
||||||
// A value with the option of "omitnested" stops iterating further if the type
|
|
||||||
// is a struct. Example:
|
|
||||||
//
|
|
||||||
// // Field is not processed further by this package.
|
|
||||||
// Field time.Time `structs:"myName,omitnested"`
|
|
||||||
// Field *http.Request `structs:",omitnested"`
|
|
||||||
//
|
|
||||||
// Note that only exported fields of a struct can be accessed, non exported
|
|
||||||
// fields will be neglected. It panics if s's kind is not struct.
|
|
||||||
func (s *Struct) IsZero() bool {
|
|
||||||
fields := s.structFields()
|
|
||||||
|
|
||||||
for _, field := range fields {
|
|
||||||
val := s.value.FieldByName(field.Name)
|
|
||||||
|
|
||||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
||||||
|
|
||||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
||||||
ok := IsZero(val.Interface())
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// zero value of the given field, such as "" for string, 0 for int
|
|
||||||
zero := reflect.Zero(val.Type()).Interface()
|
|
||||||
|
|
||||||
// current value of the given field
|
|
||||||
current := val.Interface()
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(current, zero) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasZero returns true if a field in a struct is not initialized (zero value).
|
|
||||||
// A struct tag with the content of "-" ignores the checking of that particular
|
|
||||||
// field. Example:
|
|
||||||
//
|
|
||||||
// // Field is ignored by this package.
|
|
||||||
// Field bool `structs:"-"`
|
|
||||||
//
|
|
||||||
// A value with the option of "omitnested" stops iterating further if the type
|
|
||||||
// is a struct. Example:
|
|
||||||
//
|
|
||||||
// // Field is not processed further by this package.
|
|
||||||
// Field time.Time `structs:"myName,omitnested"`
|
|
||||||
// Field *http.Request `structs:",omitnested"`
|
|
||||||
//
|
|
||||||
// Note that only exported fields of a struct can be accessed, non exported
|
|
||||||
// fields will be neglected. It panics if s's kind is not struct.
|
|
||||||
func (s *Struct) HasZero() bool {
|
|
||||||
fields := s.structFields()
|
|
||||||
|
|
||||||
for _, field := range fields {
|
|
||||||
val := s.value.FieldByName(field.Name)
|
|
||||||
|
|
||||||
_, tagOpts := parseTag(field.Tag.Get(s.TagName))
|
|
||||||
|
|
||||||
if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
|
|
||||||
ok := HasZero(val.Interface())
|
|
||||||
if ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// zero value of the given field, such as "" for string, 0 for int
|
|
||||||
zero := reflect.Zero(val.Type()).Interface()
|
|
||||||
|
|
||||||
// current value of the given field
|
|
||||||
current := val.Interface()
|
|
||||||
|
|
||||||
if reflect.DeepEqual(current, zero) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the structs's type name within its package. For more info refer
|
|
||||||
// to Name() function.
|
|
||||||
func (s *Struct) Name() string {
|
|
||||||
return s.value.Type().Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// structFields returns the exported struct fields for a given s struct. This
|
|
||||||
// is a convenient helper method to avoid duplicate code in some of the
|
|
||||||
// functions.
|
|
||||||
func (s *Struct) structFields() []reflect.StructField {
|
|
||||||
t := s.value.Type()
|
|
||||||
|
|
||||||
var f []reflect.StructField
|
|
||||||
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
// we can't access the value of unexported fields
|
|
||||||
if field.PkgPath != "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't check if it's omitted
|
|
||||||
if tag := field.Tag.Get(s.TagName); tag == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
f = append(f, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func strctVal(s interface{}) reflect.Value {
|
|
||||||
v := reflect.ValueOf(s)
|
|
||||||
|
|
||||||
// if pointer get the underlying element≤
|
|
||||||
for v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Kind() != reflect.Struct {
|
|
||||||
panic("not struct")
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map converts the given struct to a map[string]interface{}. For more info
|
|
||||||
// refer to Struct types Map() method. It panics if s's kind is not struct.
|
|
||||||
func Map(s interface{}) map[string]interface{} {
|
|
||||||
return New(s).Map()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillMap is the same as Map. Instead of returning the output, it fills the
|
|
||||||
// given map.
|
|
||||||
func FillMap(s interface{}, out map[string]interface{}) {
|
|
||||||
New(s).FillMap(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Values converts the given struct to a []interface{}. For more info refer to
|
|
||||||
// Struct types Values() method. It panics if s's kind is not struct.
|
|
||||||
func Values(s interface{}) []interface{} {
|
|
||||||
return New(s).Values()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fields returns a slice of *Field. For more info refer to Struct types
|
|
||||||
// Fields() method. It panics if s's kind is not struct.
|
|
||||||
func Fields(s interface{}) []*Field {
|
|
||||||
return New(s).Fields()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names returns a slice of field names. For more info refer to Struct types
|
|
||||||
// Names() method. It panics if s's kind is not struct.
|
|
||||||
func Names(s interface{}) []string {
|
|
||||||
return New(s).Names()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsZero returns true if all fields is equal to a zero value. For more info
|
|
||||||
// refer to Struct types IsZero() method. It panics if s's kind is not struct.
|
|
||||||
func IsZero(s interface{}) bool {
|
|
||||||
return New(s).IsZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasZero returns true if any field is equal to a zero value. For more info
|
|
||||||
// refer to Struct types HasZero() method. It panics if s's kind is not struct.
|
|
||||||
func HasZero(s interface{}) bool {
|
|
||||||
return New(s).HasZero()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsStruct returns true if the given variable is a struct or a pointer to
|
|
||||||
// struct.
|
|
||||||
func IsStruct(s interface{}) bool {
|
|
||||||
v := reflect.ValueOf(s)
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
// uninitialized zero value of a struct
|
|
||||||
if v.Kind() == reflect.Invalid {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Kind() == reflect.Struct
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the structs's type name within its package. It returns an
|
|
||||||
// empty string for unnamed types. It panics if s's kind is not struct.
|
|
||||||
func Name(s interface{}) string {
|
|
||||||
return New(s).Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// nested retrieves recursively all types for the given value and returns the
|
|
||||||
// nested value.
|
|
||||||
func (s *Struct) nested(val reflect.Value) interface{} {
|
|
||||||
var finalVal interface{}
|
|
||||||
|
|
||||||
v := reflect.ValueOf(val.Interface())
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
n := New(val.Interface())
|
|
||||||
n.TagName = s.TagName
|
|
||||||
m := n.Map()
|
|
||||||
|
|
||||||
// do not add the converted value if there are no exported fields, ie:
|
|
||||||
// time.Time
|
|
||||||
if len(m) == 0 {
|
|
||||||
finalVal = val.Interface()
|
|
||||||
} else {
|
|
||||||
finalVal = m
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
// get the element type of the map
|
|
||||||
mapElem := val.Type()
|
|
||||||
switch val.Type().Kind() {
|
|
||||||
case reflect.Ptr, reflect.Array, reflect.Map,
|
|
||||||
reflect.Slice, reflect.Chan:
|
|
||||||
mapElem = val.Type().Elem()
|
|
||||||
if mapElem.Kind() == reflect.Ptr {
|
|
||||||
mapElem = mapElem.Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// only iterate over struct types, ie: map[string]StructType,
|
|
||||||
// map[string][]StructType,
|
|
||||||
if mapElem.Kind() == reflect.Struct ||
|
|
||||||
(mapElem.Kind() == reflect.Slice &&
|
|
||||||
mapElem.Elem().Kind() == reflect.Struct) {
|
|
||||||
m := make(map[string]interface{}, val.Len())
|
|
||||||
for _, k := range val.MapKeys() {
|
|
||||||
m[k.String()] = s.nested(val.MapIndex(k))
|
|
||||||
}
|
|
||||||
finalVal = m
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(arslan): should this be optional?
|
|
||||||
finalVal = val.Interface()
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
if val.Type().Kind() == reflect.Interface {
|
|
||||||
finalVal = val.Interface()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(arslan): should this be optional?
|
|
||||||
// do not iterate of non struct types, just pass the value. Ie: []int,
|
|
||||||
// []string, co... We only iterate further if it's a struct.
|
|
||||||
// i.e []foo or []*foo
|
|
||||||
if val.Type().Elem().Kind() != reflect.Struct &&
|
|
||||||
!(val.Type().Elem().Kind() == reflect.Ptr &&
|
|
||||||
val.Type().Elem().Elem().Kind() == reflect.Struct) {
|
|
||||||
finalVal = val.Interface()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
slices := make([]interface{}, val.Len())
|
|
||||||
for x := 0; x < val.Len(); x++ {
|
|
||||||
slices[x] = s.nested(val.Index(x))
|
|
||||||
}
|
|
||||||
finalVal = slices
|
|
||||||
default:
|
|
||||||
finalVal = val.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalVal
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user