move project to go modules
- remove vendor - pin cockroach version in tests - remove golang.org/x/tools/cmd/cover (not maintained and moved to standart library)
This commit is contained in:
parent
a3f0de7916
commit
21e8493838
208
Gopkg.lock
generated
208
Gopkg.lock
generated
@ -1,208 +0,0 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761"
|
|
||||||
name = "github.com/BurntSushi/toml"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
|
|
||||||
version = "v0.3.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:038001d3925d508051d48a4e5e3e097015fa627ec9e7fc0c57e6f6ca7dc33c5e"
|
|
||||||
name = "github.com/ScaleFT/sshkeys"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "82451a80368171b074c7129d43b47fc2773f6e9f"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
|
|
||||||
name = "github.com/davecgh/go-spew"
|
|
||||||
packages = ["spew"]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
|
||||||
version = "v1.1.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:75a25fddc76a45f923f04b28f07827f49aaf6d51db4a56ec89a7cfb53e1601ab"
|
|
||||||
name = "github.com/dchest/bcrypt_pbkdf"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "83f37f9c154a678179d11e218bff73ebe5717f99"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:36fe9527deed01d2a317617e59304eb2c4ce9f8a24115bcc5c2e37b3aee5bae4"
|
|
||||||
name = "github.com/gin-contrib/sse"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:d5083934eb25e45d17f72ffa86cae3814f4a9d6c073c4f16b64147169b245606"
|
|
||||||
name = "github.com/gin-gonic/gin"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"binding",
|
|
||||||
"json",
|
|
||||||
"render",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "b869fe1415e4b9eb52f247441830d502aece2d4d"
|
|
||||||
version = "v1.3.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:6ba96a683441984156b05568b9d31dbc846d3336d21ac220fcc819a367dc1f65"
|
|
||||||
name = "github.com/golang/protobuf"
|
|
||||||
packages = ["proto"]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "5a0f697c9ed9d68fef0116532c6e05cfeae00e55"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:a2cff208d4759f6ba1b1cd228587b0a1869f95f22542ec9cd17fff64430113c7"
|
|
||||||
name = "github.com/jessevdk/go-flags"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "c6ca198ec95c841fdb89fc0de7496fed11ab854e"
|
|
||||||
version = "v1.4.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:7654989089e5bd5b6734ec3be8b695e87d3f1f8d95620b343fd7d3995a5b60d7"
|
|
||||||
name = "github.com/jmoiron/sqlx"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"reflectx",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "0dae4fefe7c0e190f7b5a78dac28a1c82cc8d849"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:be97e109f627d3ba8edfef50c9c74f0d0c17cbe3a2e924a8985e4804a894f282"
|
|
||||||
name = "github.com/json-iterator/go"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "36b14963da70d11297d313183d7e6388c8510e1e"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:bdd53b87de8185da386bae179c84d4848854c6870bacacf6a154fe63e2e750f7"
|
|
||||||
name = "github.com/lib/pq"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"oid",
|
|
||||||
"scram",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "bc6a3c0594130b1e34005880bc600b6d3f49fa7f"
|
|
||||||
version = "v1.1.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:fa610f9fe6a93f4a75e64c83673dfff9bf1a34bbb21e6102021b6bc7850834a3"
|
|
||||||
name = "github.com/mattn/go-isatty"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "57fdcb988a5c543893cc61bce354a6e24ab70022"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af"
|
|
||||||
name = "github.com/mitchellh/go-homedir"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
|
||||||
name = "github.com/pmezard/go-difflib"
|
|
||||||
packages = ["difflib"]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83"
|
|
||||||
name = "github.com/stretchr/testify"
|
|
||||||
packages = ["assert"]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
|
||||||
version = "v1.2.2"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:5589bfcc5b74ddf0a0cf48a9159f3965ec036434d520aadde91bdf95a95c40a0"
|
|
||||||
name = "github.com/tuvistavie/securerandom"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "15512123a948d62f6361bd84818e11f2ad84059a"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:c268acaa4a4d94a467980e5e91452eb61c460145765293dc0aed48e5e9919cc6"
|
|
||||||
name = "github.com/ugorji/go"
|
|
||||||
packages = ["codec"]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "c88ee250d0221a57af388746f5cf03768c21d6e2"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:2c0831757b96a0e62b2b0b851f139f50190c1e3efa6c6a7eac6f48e1a3d8f576"
|
|
||||||
name = "golang.org/x/crypto"
|
|
||||||
packages = [
|
|
||||||
"blowfish",
|
|
||||||
"curve25519",
|
|
||||||
"ed25519",
|
|
||||||
"ed25519/internal/edwards25519",
|
|
||||||
"internal/chacha20",
|
|
||||||
"internal/subtle",
|
|
||||||
"poly1305",
|
|
||||||
"ssh",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "8986dd9e96cf0a6f74da406c005ba3df38527c04"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:0cd3b4a6aec2641ff2bf7e35d93427787c60e5d94998460aab8f54921a1bc2db"
|
|
||||||
name = "golang.org/x/sys"
|
|
||||||
packages = [
|
|
||||||
"cpu",
|
|
||||||
"unix",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "fa5fdf94c78965f1aa8423f0cc50b8b8d728b05a"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:1b4724d3c8125f6044925f02b485b74bfec9905cbf579d95aafd1a6c8f8447d3"
|
|
||||||
name = "gopkg.in/go-playground/validator.v8"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "5f57d2222ad794d0dffb07e664ea05e2ee07d60c"
|
|
||||||
version = "v8.18.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:cacb98d52c60c337c2ce95a7af83ba0313a93ce5e73fa9e99a96aff70776b9d3"
|
|
||||||
name = "gopkg.in/yaml.v2"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "a5b47d31c556af34a302ce5d659e6fea44d90de0"
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
input-imports = [
|
|
||||||
"github.com/BurntSushi/toml",
|
|
||||||
"github.com/ScaleFT/sshkeys",
|
|
||||||
"github.com/gin-gonic/gin",
|
|
||||||
"github.com/jessevdk/go-flags",
|
|
||||||
"github.com/jmoiron/sqlx",
|
|
||||||
"github.com/lib/pq",
|
|
||||||
"github.com/mitchellh/go-homedir",
|
|
||||||
"github.com/stretchr/testify/assert",
|
|
||||||
"github.com/tuvistavie/securerandom",
|
|
||||||
"golang.org/x/crypto/ssh",
|
|
||||||
]
|
|
||||||
solver-name = "gps-cdcl"
|
|
||||||
solver-version = 1
|
|
74
Gopkg.toml
74
Gopkg.toml
@ -1,74 +0,0 @@
|
|||||||
# Gopkg.toml example
|
|
||||||
#
|
|
||||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
|
||||||
# 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"
|
|
||||||
#
|
|
||||||
# [prune]
|
|
||||||
# non-go = false
|
|
||||||
# go-tests = true
|
|
||||||
# unused-packages = true
|
|
||||||
|
|
||||||
[metadata.heroku]
|
|
||||||
root-package = "github.com/sosedoff/pgweb"
|
|
||||||
go-version = "1.13"
|
|
||||||
install = [ "./..." ]
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/BurntSushi/toml"
|
|
||||||
version = "0.3.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/gin-gonic/gin"
|
|
||||||
version = "1.3.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/jessevdk/go-flags"
|
|
||||||
version = "1.4.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/jmoiron/sqlx"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/lib/pq"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/mitchellh/go-homedir"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/stretchr/testify"
|
|
||||||
version = "1.2.2"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/tuvistavie/securerandom"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "golang.org/x/crypto"
|
|
||||||
|
|
||||||
[prune]
|
|
||||||
go-tests = true
|
|
||||||
unused-packages = true
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/ScaleFT/sshkeys"
|
|
3
Makefile
3
Makefile
@ -70,11 +70,8 @@ bootstrap:
|
|||||||
gox -build-toolchain
|
gox -build-toolchain
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
go get -u github.com/golang/dep/cmd/dep
|
|
||||||
go get -u golang.org/x/tools/cmd/cover
|
|
||||||
go get -u github.com/mitchellh/gox
|
go get -u github.com/mitchellh/gox
|
||||||
go get -u github.com/go-bindata/go-bindata/...
|
go get -u github.com/go-bindata/go-bindata/...
|
||||||
dep ensure
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -f ./pgweb
|
@rm -f ./pgweb
|
||||||
|
@ -14,8 +14,6 @@ install:
|
|||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
- cd %APPVEYOR_BUILD_FOLDER%
|
||||||
- go env
|
- go env
|
||||||
- go version
|
- go version
|
||||||
- go get github.com/golang/dep/cmd/dep
|
|
||||||
- dep ensure
|
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- go build
|
- go build
|
||||||
|
32
go.mod
Normal file
32
go.mod
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
module github.com/sosedoff/pgweb
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v0.3.1
|
||||||
|
github.com/ScaleFT/sshkeys v0.0.0-20181112160850-82451a803681
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
|
||||||
|
github.com/gin-gonic/gin v1.3.0
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||||
|
github.com/golang/protobuf v0.0.0-20170601230230-5a0f697c9ed9 // indirect
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/jessevdk/go-flags v1.4.0
|
||||||
|
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0
|
||||||
|
github.com/json-iterator/go v0.0.0-20170829155851-36b14963da70 // indirect
|
||||||
|
github.com/lib/pq v1.1.1
|
||||||
|
github.com/mattn/go-isatty v0.0.2-0.20170307163044-57fdcb988a5c // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.6 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.2.2
|
||||||
|
github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948
|
||||||
|
github.com/ugorji/go v0.0.0-20170215201144-c88ee250d022 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
|
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20160928153709-a5b47d31c556 // indirect
|
||||||
|
)
|
64
go.sum
Normal file
64
go.sum
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/ScaleFT/sshkeys v0.0.0-20181112160850-82451a803681 h1:JS2rl38kZmHgWa0xINSaSYH0Whtvem64/4+Ef0+Y5pE=
|
||||||
|
github.com/ScaleFT/sshkeys v0.0.0-20181112160850-82451a803681/go.mod h1:WfDateMPQ/55dPbZRp5Zxrux5WiEaHsjk9puUhz0KgY=
|
||||||
|
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/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=
|
||||||
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
|
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
|
||||||
|
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/golang/protobuf v0.0.0-20170601230230-5a0f697c9ed9 h1:6w6GCsh1LARYT2JCCS9B+cHIzp/zNoKCrEQrReZZ2p8=
|
||||||
|
github.com/golang/protobuf v0.0.0-20170601230230-5a0f697c9ed9/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE=
|
||||||
|
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
|
||||||
|
github.com/json-iterator/go v0.0.0-20170829155851-36b14963da70 h1:Mq6w++zHWe5wASWvrvVqTerOkfiXIUojnL85OhqK2/I=
|
||||||
|
github.com/json-iterator/go v0.0.0-20170829155851-36b14963da70/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||||
|
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/mattn/go-isatty v0.0.2-0.20170307163044-57fdcb988a5c h1:vNDTotKSxm/15mLGhBXjdU6q6Ncrx0HlVEd8ToAsGTw=
|
||||||
|
github.com/mattn/go-isatty v0.0.2-0.20170307163044-57fdcb988a5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
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/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948 h1:yL0l/u242MzDP6D0B5vGC+wxm5WRY+alQZy+dJk3bFI=
|
||||||
|
github.com/tuvistavie/securerandom v0.0.0-20140719024926-15512123a948/go.mod h1:a06d/M1pxWi51qiSrfGMHaEydtuXT06nha8N2aNQuXk=
|
||||||
|
github.com/ugorji/go v0.0.0-20170215201144-c88ee250d022 h1:wIYK3i9zY6ZBcWw4GFvoPVwtb45iEm8KyOVmDhSLvsE=
|
||||||
|
github.com/ugorji/go v0.0.0-20170215201144-c88ee250d022/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss=
|
||||||
|
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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 h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||||
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.1 h1:F8SLY5Vqesjs1nI1EL4qmF1PQZ1sitsmq0rPYXLyfGU=
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.1/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20160928153709-a5b47d31c556 h1:hKXbLW5oaJoQgs8KrzTLdF4PoHi+0oQPgea9TNtvE3E=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20160928153709-a5b47d31c556/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
@ -17,8 +17,8 @@ docker run \
|
|||||||
-d \
|
-d \
|
||||||
-t \
|
-t \
|
||||||
-p 26258:26257 \
|
-p 26258:26257 \
|
||||||
cockroachdb/cockroach \
|
cockroachdb/cockroach:v20.2.5 \
|
||||||
start --insecure
|
start-single-node --insecure
|
||||||
|
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
||||||
|
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
|
|
||||||
```
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](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}`.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
||||||
}
|
|
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
|
|
||||||
}
|
|
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
|
|
||||||
}
|
|
1
vendor/github.com/ScaleFT/sshkeys/.gitignore
generated
vendored
1
vendor/github.com/ScaleFT/sshkeys/.gitignore
generated
vendored
@ -1 +0,0 @@
|
|||||||
/vendor
|
|
17
vendor/github.com/ScaleFT/sshkeys/.travis.yml
generated
vendored
17
vendor/github.com/ScaleFT/sshkeys/.travis.yml
generated
vendored
@ -1,17 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
go_import_path: github.com/ScaleFT/sshkeys
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -u github.com/stretchr/testify/require github.com/dchest/bcrypt_pbkdf golang.org/x/crypto/ed25519 golang.org/x/crypto/ssh
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v ./...
|
|
74
vendor/github.com/ScaleFT/sshkeys/CODE_OF_CONDUCT.md
generated
vendored
74
vendor/github.com/ScaleFT/sshkeys/CODE_OF_CONDUCT.md
generated
vendored
@ -1,74 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to making participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
|
||||||
nationality, personal appearance, race, religion, or sexual identity and
|
|
||||||
orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community. Examples of
|
|
||||||
representing a project or community include using an official project e-mail
|
|
||||||
address, posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event. Representation of a project may be
|
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at [opensource@scaleft.com](mailto:opensource@scaleft.com). All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at [http://contributor-covenant.org/version/1/4][version]
|
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
42
vendor/github.com/ScaleFT/sshkeys/CONTRIBUTING.md
generated
vendored
42
vendor/github.com/ScaleFT/sshkeys/CONTRIBUTING.md
generated
vendored
@ -1,42 +0,0 @@
|
|||||||
# How to Contribute
|
|
||||||
|
|
||||||
ScaleFT's projects are [Apache 2.0 licensed](LICENSE) and accept contributions
|
|
||||||
via GitHub pull requests. This document outlines some of the conventions on
|
|
||||||
development workflow, contact points, community conduct and other resources
|
|
||||||
to make it easier to get your contribution accepted.
|
|
||||||
|
|
||||||
# Code of Conduct
|
|
||||||
|
|
||||||
This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md).
|
|
||||||
By participating, you are expected to uphold this code.
|
|
||||||
|
|
||||||
Please report unacceptable behavior to [opensource@scaleft.com](mailto:opensource@scaleft.com).
|
|
||||||
|
|
||||||
# Reporting Security Issues
|
|
||||||
|
|
||||||
ScaleFT takes security seriously. If you discover a security issue,
|
|
||||||
please bring it to our attention right away!
|
|
||||||
|
|
||||||
Please DO NOT file a public issue or pull request,
|
|
||||||
[instead send your report privately to the ScaleFT Security Team](https://www.scaleft.com/company/security/),
|
|
||||||
reachable at [security@scaleft.com](mailto:security@scaleft.com).
|
|
||||||
|
|
||||||
Security reports are greatly appreciated and we will publicly thank you for them.
|
|
||||||
|
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
- Fork the repository on GitHub
|
|
||||||
- Read the [README](README.md) for build and test instructions
|
|
||||||
- Play with the project, submit bugs, submit patches!
|
|
||||||
|
|
||||||
# Contribution Flow
|
|
||||||
|
|
||||||
This is a rough outline of what a contributor's workflow looks like:
|
|
||||||
|
|
||||||
- Create a topic branch from where you want to base your work (usually master).
|
|
||||||
- Make commits of logical units, rebasing later is ok too!
|
|
||||||
- Push your changes to a topic branch in your fork of the repository.
|
|
||||||
- Make sure the tests pass, and add any new tests as appropriate.
|
|
||||||
- Submit a pull request to the original repository.
|
|
||||||
|
|
||||||
Thanks for your contributions!
|
|
202
vendor/github.com/ScaleFT/sshkeys/LICENSE
generated
vendored
202
vendor/github.com/ScaleFT/sshkeys/LICENSE
generated
vendored
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
10
vendor/github.com/ScaleFT/sshkeys/NOTICE
generated
vendored
10
vendor/github.com/ScaleFT/sshkeys/NOTICE
generated
vendored
@ -1,10 +0,0 @@
|
|||||||
sshkeys
|
|
||||||
Copyright 2017 ScaleFT, Inc
|
|
||||||
|
|
||||||
This product includes software developed at ScaleFT, Inc.
|
|
||||||
(https://www.scaleft.com/).
|
|
||||||
|
|
||||||
Portions of this software are derived from
|
|
||||||
https://github.com/golang/crypto/blob/master/ssh/keys.go
|
|
||||||
|
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
14
vendor/github.com/ScaleFT/sshkeys/README.md
generated
vendored
14
vendor/github.com/ScaleFT/sshkeys/README.md
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
# sshkeys
|
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/ScaleFT/sshkeys?status.svg)](https://godoc.org/github.com/ScaleFT/sshkeys)
|
|
||||||
[![Build Status](https://travis-ci.org/ScaleFT/sshkeys.svg?branch=master)](https://travis-ci.org/ScaleFT/sshkeys)
|
|
||||||
|
|
||||||
`sshkeys` provides utilities for parsing and marshalling cryptographic keys used for SSH, in both cleartext and encrypted formats.
|
|
||||||
|
|
||||||
[ssh.ParseRawPrivateKey](https://godoc.org/golang.org/x/crypto/ssh#ParseRawPrivateKey) only supports parsing a subset of the formats `sshkeys` supports, does not support parsing encrypted private keys, and does not support marshalling.
|
|
||||||
|
|
||||||
## Supported Formats
|
|
||||||
|
|
||||||
* OpenSSH's [PROTOCOL.key](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key) for RSA and ED25519 keys.
|
|
||||||
* OpenSSH version >= 7.6 using aes256-ctr encryption
|
|
||||||
* "Classic" PEM containing RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
|
|
275
vendor/github.com/ScaleFT/sshkeys/marshal.go
generated
vendored
275
vendor/github.com/ScaleFT/sshkeys/marshal.go
generated
vendored
@ -1,275 +0,0 @@
|
|||||||
package sshkeys
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/dsa"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/asn1"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
mrand "math/rand"
|
|
||||||
|
|
||||||
"github.com/dchest/bcrypt_pbkdf"
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Format of private key to use when Marshaling.
|
|
||||||
type Format int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// FormatOpenSSHv1 encodes a private key using OpenSSH's PROTOCOL.key format: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
|
||||||
FormatOpenSSHv1 Format = iota
|
|
||||||
// FormatClassicPEM encodes private keys in PEM, with a key-specific encoding, as used by OpenSSH.
|
|
||||||
FormatClassicPEM
|
|
||||||
)
|
|
||||||
|
|
||||||
// MarshalOptions provides the Marshal function format and encryption options.
|
|
||||||
type MarshalOptions struct {
|
|
||||||
// Passphrase to encrypt private key with, if nil, the key will not be encrypted.
|
|
||||||
Passphrase []byte
|
|
||||||
// Format to encode the private key in.
|
|
||||||
Format Format
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal converts a private key into an optionally encrypted format.
|
|
||||||
func Marshal(pk interface{}, opts *MarshalOptions) ([]byte, error) {
|
|
||||||
switch opts.Format {
|
|
||||||
case FormatOpenSSHv1:
|
|
||||||
return marshalOpenssh(pk, opts)
|
|
||||||
case FormatClassicPEM:
|
|
||||||
return marshalPem(pk, opts)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("sshkeys: invalid format %d", opts.Format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalPem(pk interface{}, opts *MarshalOptions) ([]byte, error) {
|
|
||||||
var err error
|
|
||||||
var plain []byte
|
|
||||||
var pemType string
|
|
||||||
|
|
||||||
switch key := pk.(type) {
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
pemType = "RSA PRIVATE KEY"
|
|
||||||
plain = x509.MarshalPKCS1PrivateKey(key)
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
pemType = "EC PRIVATE KEY"
|
|
||||||
plain, err = x509.MarshalECPrivateKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case *dsa.PrivateKey:
|
|
||||||
pemType = "DSA PRIVATE KEY"
|
|
||||||
plain, err = marshalDSAPrivateKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case *ed25519.PrivateKey:
|
|
||||||
return nil, fmt.Errorf("sshkeys: ed25519 keys must be marshaled with FormatOpenSSHv1")
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("sshkeys: unsupported key type %T", pk)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts.Passphrase) > 0 {
|
|
||||||
block, err := x509.EncryptPEMBlock(rand.Reader, pemType, plain, opts.Passphrase, x509.PEMCipherAES128)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pem.EncodeToMemory(block), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: pemType,
|
|
||||||
Bytes: plain,
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type dsaOpenssl struct {
|
|
||||||
Version int
|
|
||||||
P *big.Int
|
|
||||||
Q *big.Int
|
|
||||||
G *big.Int
|
|
||||||
Pub *big.Int
|
|
||||||
Priv *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/golang/crypto/blob/master/ssh/keys.go#L793-L804
|
|
||||||
func marshalDSAPrivateKey(pk *dsa.PrivateKey) ([]byte, error) {
|
|
||||||
k := dsaOpenssl{
|
|
||||||
Version: 0,
|
|
||||||
P: pk.P,
|
|
||||||
Q: pk.Q,
|
|
||||||
G: pk.G,
|
|
||||||
Pub: pk.Y,
|
|
||||||
Priv: pk.X,
|
|
||||||
}
|
|
||||||
|
|
||||||
return asn1.Marshal(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
const opensshv1Magic = "openssh-key-v1"
|
|
||||||
|
|
||||||
type opensshHeader struct {
|
|
||||||
CipherName string
|
|
||||||
KdfName string
|
|
||||||
KdfOpts string
|
|
||||||
NumKeys uint32
|
|
||||||
PubKey string
|
|
||||||
PrivKeyBlock string
|
|
||||||
}
|
|
||||||
|
|
||||||
type opensshKey struct {
|
|
||||||
Check1 uint32
|
|
||||||
Check2 uint32
|
|
||||||
Keytype string
|
|
||||||
Rest []byte `ssh:"rest"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type opensshRsa struct {
|
|
||||||
N *big.Int
|
|
||||||
E *big.Int
|
|
||||||
D *big.Int
|
|
||||||
Iqmp *big.Int
|
|
||||||
P *big.Int
|
|
||||||
Q *big.Int
|
|
||||||
Comment string
|
|
||||||
Pad []byte `ssh:"rest"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type opensshED25519 struct {
|
|
||||||
Pub []byte
|
|
||||||
Priv []byte
|
|
||||||
Comment string
|
|
||||||
Pad []byte `ssh:"rest"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func padBytes(data []byte, blocksize int) []byte {
|
|
||||||
if blocksize != 0 {
|
|
||||||
var i byte
|
|
||||||
for i = byte(1); len(data)%blocksize != 0; i++ {
|
|
||||||
data = append(data, i&0xFF)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalOpenssh(pk interface{}, opts *MarshalOptions) ([]byte, error) {
|
|
||||||
var blocksize int
|
|
||||||
var keylen int
|
|
||||||
|
|
||||||
out := opensshHeader{
|
|
||||||
CipherName: "none",
|
|
||||||
KdfName: "none",
|
|
||||||
KdfOpts: "",
|
|
||||||
NumKeys: 1,
|
|
||||||
PubKey: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts.Passphrase) > 0 {
|
|
||||||
out.CipherName = "aes256-cbc"
|
|
||||||
out.KdfName = "bcrypt"
|
|
||||||
keylen = keySizeAES256
|
|
||||||
blocksize = aes.BlockSize
|
|
||||||
}
|
|
||||||
|
|
||||||
check := mrand.Uint32()
|
|
||||||
pk1 := opensshKey{
|
|
||||||
Check1: check,
|
|
||||||
Check2: check,
|
|
||||||
}
|
|
||||||
|
|
||||||
switch key := pk.(type) {
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
k := &opensshRsa{
|
|
||||||
N: key.N,
|
|
||||||
E: big.NewInt(int64(key.E)),
|
|
||||||
D: key.D,
|
|
||||||
Iqmp: key.Precomputed.Qinv,
|
|
||||||
P: key.Primes[0],
|
|
||||||
Q: key.Primes[1],
|
|
||||||
Comment: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
data := ssh.Marshal(k)
|
|
||||||
pk1.Keytype = ssh.KeyAlgoRSA
|
|
||||||
pk1.Rest = data
|
|
||||||
publicKey, err := ssh.NewPublicKey(&key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out.PubKey = string(publicKey.Marshal())
|
|
||||||
|
|
||||||
case ed25519.PrivateKey:
|
|
||||||
k := opensshED25519{
|
|
||||||
Pub: key.Public().(ed25519.PublicKey),
|
|
||||||
Priv: key,
|
|
||||||
}
|
|
||||||
data := ssh.Marshal(k)
|
|
||||||
pk1.Keytype = ssh.KeyAlgoED25519
|
|
||||||
pk1.Rest = data
|
|
||||||
|
|
||||||
publicKey, err := ssh.NewPublicKey(key.Public())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out.PubKey = string(publicKey.Marshal())
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("sshkeys: unsupported key type %T", pk)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opts.Passphrase) > 0 {
|
|
||||||
rounds := 16
|
|
||||||
ivlen := blocksize
|
|
||||||
salt := make([]byte, blocksize)
|
|
||||||
_, err := rand.Read(salt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
kdfdata, err := bcrypt_pbkdf.Key(opts.Passphrase, salt, rounds, keylen+ivlen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
iv := kdfdata[keylen : ivlen+keylen]
|
|
||||||
aeskey := kdfdata[0:keylen]
|
|
||||||
|
|
||||||
block, err := aes.NewCipher(aeskey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pkblock := padBytes(ssh.Marshal(pk1), blocksize)
|
|
||||||
|
|
||||||
cbc := cipher.NewCBCEncrypter(block, iv)
|
|
||||||
cbc.CryptBlocks(pkblock, pkblock)
|
|
||||||
|
|
||||||
out.PrivKeyBlock = string(pkblock)
|
|
||||||
|
|
||||||
var opts struct {
|
|
||||||
Salt []byte
|
|
||||||
Rounds uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
opts.Salt = salt
|
|
||||||
opts.Rounds = uint32(rounds)
|
|
||||||
|
|
||||||
out.KdfOpts = string(ssh.Marshal(&opts))
|
|
||||||
} else {
|
|
||||||
out.PrivKeyBlock = string(ssh.Marshal(pk1))
|
|
||||||
}
|
|
||||||
|
|
||||||
outBytes := []byte(opensshv1Magic)
|
|
||||||
outBytes = append(outBytes, 0)
|
|
||||||
outBytes = append(outBytes, ssh.Marshal(out)...)
|
|
||||||
block := &pem.Block{
|
|
||||||
Type: "OPENSSH PRIVATE KEY",
|
|
||||||
Bytes: outBytes,
|
|
||||||
}
|
|
||||||
return pem.EncodeToMemory(block), nil
|
|
||||||
}
|
|
244
vendor/github.com/ScaleFT/sshkeys/parse.go
generated
vendored
244
vendor/github.com/ScaleFT/sshkeys/parse.go
generated
vendored
@ -1,244 +0,0 @@
|
|||||||
// Portions of this file are based on https://github.com/golang/crypto/blob/master/ssh/keys.go
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sshkeys
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/dchest/bcrypt_pbkdf"
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrIncorrectPassword is returned when the supplied passphrase was not correct for an encrypted private key.
|
|
||||||
var ErrIncorrectPassword = errors.New("sshkeys: Invalid Passphrase")
|
|
||||||
|
|
||||||
const keySizeAES256 = 32
|
|
||||||
|
|
||||||
// ParseEncryptedPrivateKey returns a Signer from an encrypted private key. It supports
|
|
||||||
// the same keys as ParseEncryptedRawPrivateKey.
|
|
||||||
func ParseEncryptedPrivateKey(data []byte, passphrase []byte) (ssh.Signer, error) {
|
|
||||||
key, err := ParseEncryptedRawPrivateKey(data, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ssh.NewSignerFromKey(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseEncryptedRawPrivateKey returns a private key from an encrypted private key. It
|
|
||||||
// supports RSA (PKCS#1 or OpenSSH), DSA (OpenSSL), and ECDSA private keys.
|
|
||||||
//
|
|
||||||
// ErrIncorrectPassword will be returned if the supplied passphrase is wrong,
|
|
||||||
// but some formats like RSA in PKCS#1 detecting a wrong passphrase is difficult,
|
|
||||||
// and other parse errors may be returned.
|
|
||||||
func ParseEncryptedRawPrivateKey(data []byte, passphrase []byte) (interface{}, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
block, _ := pem.Decode(data)
|
|
||||||
if block == nil {
|
|
||||||
return nil, errors.New("no PEM block found")
|
|
||||||
}
|
|
||||||
|
|
||||||
if x509.IsEncryptedPEMBlock(block) {
|
|
||||||
data, err = x509.DecryptPEMBlock(block, passphrase)
|
|
||||||
if err == x509.IncorrectPasswordError {
|
|
||||||
return nil, ErrIncorrectPassword
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data = block.Bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
switch block.Type {
|
|
||||||
case "RSA PRIVATE KEY":
|
|
||||||
pk, err := x509.ParsePKCS1PrivateKey(data)
|
|
||||||
if err != nil {
|
|
||||||
// The Algos for PEM Encryption do not include strong message authentication,
|
|
||||||
// so sometimes DecryptPEMBlock works, but ParsePKCS1PrivateKey fails with an asn1 error.
|
|
||||||
// We are just catching the most common prefix here...
|
|
||||||
if strings.HasPrefix(err.Error(), "asn1: structure error") {
|
|
||||||
return nil, ErrIncorrectPassword
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pk, nil
|
|
||||||
case "EC PRIVATE KEY":
|
|
||||||
return x509.ParseECPrivateKey(data)
|
|
||||||
case "DSA PRIVATE KEY":
|
|
||||||
return ssh.ParseDSAPrivateKey(data)
|
|
||||||
case "OPENSSH PRIVATE KEY":
|
|
||||||
return parseOpenSSHPrivateKey(data, passphrase)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("sshkeys: unsupported key type %q", block.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseOpenSSHPrivateKey(data []byte, passphrase []byte) (interface{}, error) {
|
|
||||||
magic := append([]byte(opensshv1Magic), 0)
|
|
||||||
if !bytes.Equal(magic, data[0:len(magic)]) {
|
|
||||||
return nil, errors.New("sshkeys: invalid openssh private key format")
|
|
||||||
}
|
|
||||||
remaining := data[len(magic):]
|
|
||||||
|
|
||||||
w := opensshHeader{}
|
|
||||||
|
|
||||||
if err := ssh.Unmarshal(remaining, &w); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.NumKeys != 1 {
|
|
||||||
return nil, fmt.Errorf("sshkeys: NumKeys must be 1: %d", w.NumKeys)
|
|
||||||
}
|
|
||||||
|
|
||||||
var privateKeyBytes []byte
|
|
||||||
var encrypted bool
|
|
||||||
|
|
||||||
switch {
|
|
||||||
// OpenSSH supports bcrypt KDF w/ AES256-CBC or AES256-CTR mode
|
|
||||||
case w.KdfName == "bcrypt" && w.CipherName == "aes256-cbc":
|
|
||||||
iv, block, err := extractBcryptIvBlock(passphrase, w)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cbc := cipher.NewCBCDecrypter(block, iv)
|
|
||||||
privateKeyBytes = []byte(w.PrivKeyBlock)
|
|
||||||
cbc.CryptBlocks(privateKeyBytes, privateKeyBytes)
|
|
||||||
|
|
||||||
encrypted = true
|
|
||||||
|
|
||||||
case w.KdfName == "bcrypt" && w.CipherName == "aes256-ctr":
|
|
||||||
iv, block, err := extractBcryptIvBlock(passphrase, w)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stream := cipher.NewCTR(block, iv)
|
|
||||||
privateKeyBytes = []byte(w.PrivKeyBlock)
|
|
||||||
stream.XORKeyStream(privateKeyBytes, privateKeyBytes)
|
|
||||||
|
|
||||||
encrypted = true
|
|
||||||
|
|
||||||
case w.KdfName == "none" && w.CipherName == "none":
|
|
||||||
privateKeyBytes = []byte(w.PrivKeyBlock)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("sshkeys: unknown Cipher/KDF: %s:%s", w.CipherName, w.KdfName)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk1 := opensshKey{}
|
|
||||||
|
|
||||||
if err := ssh.Unmarshal(privateKeyBytes, &pk1); err != nil {
|
|
||||||
if encrypted {
|
|
||||||
return nil, ErrIncorrectPassword
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if pk1.Check1 != pk1.Check2 {
|
|
||||||
return nil, ErrIncorrectPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
// we only handle ed25519 and rsa keys currently
|
|
||||||
switch pk1.Keytype {
|
|
||||||
case ssh.KeyAlgoRSA:
|
|
||||||
// https://github.com/openssh/openssh-portable/blob/V_7_4_P1/sshkey.c#L2760-L2773
|
|
||||||
key := opensshRsa{}
|
|
||||||
|
|
||||||
err := ssh.Unmarshal(pk1.Rest, &key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, b := range key.Pad {
|
|
||||||
if int(b) != i+1 {
|
|
||||||
return nil, errors.New("sshkeys: padding not as expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := &rsa.PrivateKey{
|
|
||||||
PublicKey: rsa.PublicKey{
|
|
||||||
N: key.N,
|
|
||||||
E: int(key.E.Int64()),
|
|
||||||
},
|
|
||||||
D: key.D,
|
|
||||||
Primes: []*big.Int{key.P, key.Q},
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pk.Validate()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pk.Precompute()
|
|
||||||
|
|
||||||
return pk, nil
|
|
||||||
case ssh.KeyAlgoED25519:
|
|
||||||
key := opensshED25519{}
|
|
||||||
|
|
||||||
err := ssh.Unmarshal(pk1.Rest, &key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(key.Priv) != ed25519.PrivateKeySize {
|
|
||||||
return nil, errors.New("sshkeys: private key unexpected length")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, b := range key.Pad {
|
|
||||||
if int(b) != i+1 {
|
|
||||||
return nil, errors.New("sshkeys: padding not as expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
|
|
||||||
copy(pk, key.Priv)
|
|
||||||
return pk, nil
|
|
||||||
default:
|
|
||||||
return nil, errors.New("sshkeys: unhandled key type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractBcryptIvBlock(passphrase []byte, w opensshHeader) ([]byte, cipher.Block, error) {
|
|
||||||
cipherKeylen := keySizeAES256
|
|
||||||
cipherIvLen := aes.BlockSize
|
|
||||||
|
|
||||||
var opts struct {
|
|
||||||
Salt []byte
|
|
||||||
Rounds uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ssh.Unmarshal([]byte(w.KdfOpts), &opts); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
kdfdata, err := bcrypt_pbkdf.Key(passphrase, opts.Salt, int(opts.Rounds), cipherKeylen+cipherIvLen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
iv := kdfdata[cipherKeylen : cipherIvLen+cipherKeylen]
|
|
||||||
aeskey := kdfdata[0:cipherKeylen]
|
|
||||||
block, err := aes.NewCipher(aeskey)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return iv, block, nil
|
|
||||||
}
|
|
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.
|
|
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))
|
|
||||||
}
|
|
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...)
|
|
||||||
}
|
|
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)
|
|
||||||
}
|
|
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
|
|
||||||
}
|
|
27
vendor/github.com/dchest/bcrypt_pbkdf/LICENSE
generated
vendored
27
vendor/github.com/dchest/bcrypt_pbkdf/LICENSE
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2014 Dmitry Chestnykh <dmitry@codingrobots.com>
|
|
||||||
Copyright (c) 2010 The Go Authors
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials
|
|
||||||
provided with the distribution.
|
|
||||||
|
|
||||||
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.
|
|
21
vendor/github.com/dchest/bcrypt_pbkdf/README
generated
vendored
21
vendor/github.com/dchest/bcrypt_pbkdf/README
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
Go implementation of bcrypt_pbkdf(3) from OpenBSD
|
|
||||||
(a variant of PBKDF2 with bcrypt-based PRF).
|
|
||||||
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
|
|
||||||
func Key(password, salt []byte, rounds, keyLen int) ([]byte, error)
|
|
||||||
|
|
||||||
|
|
||||||
Key derives a key from the password, salt and rounds count, returning a
|
|
||||||
[]byte of length keyLen that can be used as cryptographic key.
|
|
||||||
|
|
||||||
Remember to get a good random salt of at least 16 bytes. Using a higher
|
|
||||||
rounds count will increase the cost of an exhaustive search but will also
|
|
||||||
make derivation proportionally slower.
|
|
||||||
|
|
||||||
|
|
||||||
REFERENCES
|
|
||||||
|
|
||||||
* http://www.tedunangst.com/flak/post/bcrypt-pbkdf
|
|
||||||
* http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libutil/bcrypt_pbkdf.c
|
|
97
vendor/github.com/dchest/bcrypt_pbkdf/bcrypt_pbkdf.go
generated
vendored
97
vendor/github.com/dchest/bcrypt_pbkdf/bcrypt_pbkdf.go
generated
vendored
@ -1,97 +0,0 @@
|
|||||||
// Copyright 2014 Dmitry Chestnykh. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package bcrypt_pbkdf implements password-based key derivation function based
|
|
||||||
// on bcrypt compatible with bcrypt_pbkdf(3) from OpenBSD.
|
|
||||||
package bcrypt_pbkdf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha512"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
// NOTE! Requires blowfish package version from Aug 1, 2014 or later.
|
|
||||||
// Will produce incorrect results if the package is older.
|
|
||||||
// See commit message for details: http://goo.gl/wx6g8O
|
|
||||||
"golang.org/x/crypto/blowfish"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Key derives a key from the password, salt and rounds count, returning a
|
|
||||||
// []byte of length keyLen that can be used as cryptographic key.
|
|
||||||
//
|
|
||||||
// Remember to get a good random salt of at least 16 bytes. Using a higher
|
|
||||||
// rounds count will increase the cost of an exhaustive search but will also
|
|
||||||
// make derivation proportionally slower.
|
|
||||||
func Key(password, salt []byte, rounds, keyLen int) ([]byte, error) {
|
|
||||||
if rounds < 1 {
|
|
||||||
return nil, errors.New("bcrypt_pbkdf: number of rounds is too small")
|
|
||||||
}
|
|
||||||
if len(password) == 0 {
|
|
||||||
return nil, errors.New("bcrypt_pbkdf: empty password")
|
|
||||||
}
|
|
||||||
if len(salt) == 0 || len(salt) > 1<<20 {
|
|
||||||
return nil, errors.New("bcrypt_pbkdf: bad salt length")
|
|
||||||
}
|
|
||||||
if keyLen > 1024 {
|
|
||||||
return nil, errors.New("bcrypt_pbkdf: keyLen is too large")
|
|
||||||
}
|
|
||||||
var shapass, shasalt [sha512.Size]byte
|
|
||||||
var out, tmp [32]byte
|
|
||||||
var cnt [4]byte
|
|
||||||
|
|
||||||
numBlocks := (keyLen + len(out) - 1) / len(out)
|
|
||||||
key := make([]byte, numBlocks*len(out))
|
|
||||||
|
|
||||||
h := sha512.New()
|
|
||||||
h.Write(password)
|
|
||||||
h.Sum(shapass[:0])
|
|
||||||
|
|
||||||
for block := 1; block <= numBlocks; block++ {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(salt)
|
|
||||||
cnt[0] = byte(block >> 24)
|
|
||||||
cnt[1] = byte(block >> 16)
|
|
||||||
cnt[2] = byte(block >> 8)
|
|
||||||
cnt[3] = byte(block)
|
|
||||||
h.Write(cnt[:])
|
|
||||||
bcryptHash(tmp[:], shapass[:], h.Sum(shasalt[:0]))
|
|
||||||
copy(out[:], tmp[:])
|
|
||||||
|
|
||||||
for i := 2; i <= rounds; i++ {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(tmp[:])
|
|
||||||
bcryptHash(tmp[:], shapass[:], h.Sum(shasalt[:0]))
|
|
||||||
for j := 0; j < len(out); j++ {
|
|
||||||
out[j] ^= tmp[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v := range out {
|
|
||||||
key[i*numBlocks+(block-1)] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return key[:keyLen], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var magic = []byte("OxychromaticBlowfishSwatDynamite")
|
|
||||||
|
|
||||||
func bcryptHash(out, shapass, shasalt []byte) {
|
|
||||||
c, err := blowfish.NewSaltedCipher(shapass, shasalt)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for i := 0; i < 64; i++ {
|
|
||||||
blowfish.ExpandKey(shasalt, c)
|
|
||||||
blowfish.ExpandKey(shapass, c)
|
|
||||||
}
|
|
||||||
copy(out[:], magic)
|
|
||||||
for i := 0; i < 32; i += 8 {
|
|
||||||
for j := 0; j < 64; j++ {
|
|
||||||
c.Encrypt(out[i:i+8], out[i:i+8])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Swap bytes due to different endianness.
|
|
||||||
for i := 0; i < 32; i += 4 {
|
|
||||||
out[i+3], out[i+2], out[i+1], out[i] = out[i], out[i+1], out[i+2], out[i+3]
|
|
||||||
}
|
|
||||||
}
|
|
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
go:
|
|
||||||
- 1.6.4
|
|
||||||
- 1.7.4
|
|
||||||
- tip
|
|
||||||
|
|
||||||
git:
|
|
||||||
depth: 3
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v -covermode=count -coverprofile=coverage.out
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
21
vendor/github.com/gin-contrib/sse/LICENSE
generated
vendored
21
vendor/github.com/gin-contrib/sse/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Manuel Martínez-Almeida
|
|
||||||
|
|
||||||
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.
|
|
58
vendor/github.com/gin-contrib/sse/README.md
generated
vendored
58
vendor/github.com/gin-contrib/sse/README.md
generated
vendored
@ -1,58 +0,0 @@
|
|||||||
# Server-Sent Events
|
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/gin-contrib/sse?status.svg)](https://godoc.org/github.com/gin-contrib/sse)
|
|
||||||
[![Build Status](https://travis-ci.org/gin-contrib/sse.svg)](https://travis-ci.org/gin-contrib/sse)
|
|
||||||
[![codecov](https://codecov.io/gh/gin-contrib/sse/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-contrib/sse)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/gin-contrib/sse)](https://goreportcard.com/report/github.com/gin-contrib/sse)
|
|
||||||
|
|
||||||
Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/).
|
|
||||||
|
|
||||||
- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
|
|
||||||
- [Browser support](http://caniuse.com/#feat=eventsource)
|
|
||||||
|
|
||||||
## Sample code
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/gin-contrib/sse"
|
|
||||||
|
|
||||||
func httpHandler(w http.ResponseWriter, req *http.Request) {
|
|
||||||
// data can be a primitive like a string, an integer or a float
|
|
||||||
sse.Encode(w, sse.Event{
|
|
||||||
Event: "message",
|
|
||||||
Data: "some data\nmore data",
|
|
||||||
})
|
|
||||||
|
|
||||||
// also a complex type, like a map, a struct or a slice
|
|
||||||
sse.Encode(w, sse.Event{
|
|
||||||
Id: "124",
|
|
||||||
Event: "message",
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"user": "manu",
|
|
||||||
"date": time.Now().Unix(),
|
|
||||||
"content": "hi!",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
```
|
|
||||||
event: message
|
|
||||||
data: some data\\nmore data
|
|
||||||
|
|
||||||
id: 124
|
|
||||||
event: message
|
|
||||||
data: {"content":"hi!","date":1431540810,"user":"manu"}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Content-Type
|
|
||||||
|
|
||||||
```go
|
|
||||||
fmt.Println(sse.ContentType)
|
|
||||||
```
|
|
||||||
```
|
|
||||||
text/event-stream
|
|
||||||
```
|
|
||||||
|
|
||||||
## Decoding support
|
|
||||||
|
|
||||||
There is a client-side implementation of SSE coming soon.
|
|
116
vendor/github.com/gin-contrib/sse/sse-decoder.go
generated
vendored
116
vendor/github.com/gin-contrib/sse/sse-decoder.go
generated
vendored
@ -1,116 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
type decoder struct {
|
|
||||||
events []Event
|
|
||||||
}
|
|
||||||
|
|
||||||
func Decode(r io.Reader) ([]Event, error) {
|
|
||||||
var dec decoder
|
|
||||||
return dec.decode(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) dispatchEvent(event Event, data string) {
|
|
||||||
dataLength := len(data)
|
|
||||||
if dataLength > 0 {
|
|
||||||
//If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
|
|
||||||
data = data[:dataLength-1]
|
|
||||||
dataLength--
|
|
||||||
}
|
|
||||||
if dataLength == 0 && event.Event == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if event.Event == "" {
|
|
||||||
event.Event = "message"
|
|
||||||
}
|
|
||||||
event.Data = data
|
|
||||||
d.events = append(d.events, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) decode(r io.Reader) ([]Event, error) {
|
|
||||||
buf, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentEvent Event
|
|
||||||
var dataBuffer *bytes.Buffer = new(bytes.Buffer)
|
|
||||||
// TODO (and unit tests)
|
|
||||||
// Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
|
|
||||||
// a single U+000A LINE FEED (LF) character,
|
|
||||||
// or a single U+000D CARRIAGE RETURN (CR) character.
|
|
||||||
lines := bytes.Split(buf, []byte{'\n'})
|
|
||||||
for _, line := range lines {
|
|
||||||
if len(line) == 0 {
|
|
||||||
// If the line is empty (a blank line). Dispatch the event.
|
|
||||||
d.dispatchEvent(currentEvent, dataBuffer.String())
|
|
||||||
|
|
||||||
// reset current event and data buffer
|
|
||||||
currentEvent = Event{}
|
|
||||||
dataBuffer.Reset()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if line[0] == byte(':') {
|
|
||||||
// If the line starts with a U+003A COLON character (:), ignore the line.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var field, value []byte
|
|
||||||
colonIndex := bytes.IndexRune(line, ':')
|
|
||||||
if colonIndex != -1 {
|
|
||||||
// If the line contains a U+003A COLON character character (:)
|
|
||||||
// Collect the characters on the line before the first U+003A COLON character (:),
|
|
||||||
// and let field be that string.
|
|
||||||
field = line[:colonIndex]
|
|
||||||
// Collect the characters on the line after the first U+003A COLON character (:),
|
|
||||||
// and let value be that string.
|
|
||||||
value = line[colonIndex+1:]
|
|
||||||
// If value starts with a single U+0020 SPACE character, remove it from value.
|
|
||||||
if len(value) > 0 && value[0] == ' ' {
|
|
||||||
value = value[1:]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
|
|
||||||
// Use the whole line as the field name, and the empty string as the field value.
|
|
||||||
field = line
|
|
||||||
value = []byte{}
|
|
||||||
}
|
|
||||||
// The steps to process the field given a field name and a field value depend on the field name,
|
|
||||||
// as given in the following list. Field names must be compared literally,
|
|
||||||
// with no case folding performed.
|
|
||||||
switch string(field) {
|
|
||||||
case "event":
|
|
||||||
// Set the event name buffer to field value.
|
|
||||||
currentEvent.Event = string(value)
|
|
||||||
case "id":
|
|
||||||
// Set the event stream's last event ID to the field value.
|
|
||||||
currentEvent.Id = string(value)
|
|
||||||
case "retry":
|
|
||||||
// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
|
|
||||||
// then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
|
|
||||||
// Otherwise, ignore the field.
|
|
||||||
currentEvent.Id = string(value)
|
|
||||||
case "data":
|
|
||||||
// Append the field value to the data buffer,
|
|
||||||
dataBuffer.Write(value)
|
|
||||||
// then append a single U+000A LINE FEED (LF) character to the data buffer.
|
|
||||||
dataBuffer.WriteString("\n")
|
|
||||||
default:
|
|
||||||
//Otherwise. The field is ignored.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Once the end of the file is reached, the user agent must dispatch the event one final time.
|
|
||||||
d.dispatchEvent(currentEvent, dataBuffer.String())
|
|
||||||
|
|
||||||
return d.events, nil
|
|
||||||
}
|
|
110
vendor/github.com/gin-contrib/sse/sse-encoder.go
generated
vendored
110
vendor/github.com/gin-contrib/sse/sse-encoder.go
generated
vendored
@ -1,110 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server-Sent Events
|
|
||||||
// W3C Working Draft 29 October 2009
|
|
||||||
// http://www.w3.org/TR/2009/WD-eventsource-20091029/
|
|
||||||
|
|
||||||
const ContentType = "text/event-stream"
|
|
||||||
|
|
||||||
var contentType = []string{ContentType}
|
|
||||||
var noCache = []string{"no-cache"}
|
|
||||||
|
|
||||||
var fieldReplacer = strings.NewReplacer(
|
|
||||||
"\n", "\\n",
|
|
||||||
"\r", "\\r")
|
|
||||||
|
|
||||||
var dataReplacer = strings.NewReplacer(
|
|
||||||
"\n", "\ndata:",
|
|
||||||
"\r", "\\r")
|
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
Event string
|
|
||||||
Id string
|
|
||||||
Retry uint
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Encode(writer io.Writer, event Event) error {
|
|
||||||
w := checkWriter(writer)
|
|
||||||
writeId(w, event.Id)
|
|
||||||
writeEvent(w, event.Event)
|
|
||||||
writeRetry(w, event.Retry)
|
|
||||||
return writeData(w, event.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeId(w stringWriter, id string) {
|
|
||||||
if len(id) > 0 {
|
|
||||||
w.WriteString("id:")
|
|
||||||
fieldReplacer.WriteString(w, id)
|
|
||||||
w.WriteString("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeEvent(w stringWriter, event string) {
|
|
||||||
if len(event) > 0 {
|
|
||||||
w.WriteString("event:")
|
|
||||||
fieldReplacer.WriteString(w, event)
|
|
||||||
w.WriteString("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeRetry(w stringWriter, retry uint) {
|
|
||||||
if retry > 0 {
|
|
||||||
w.WriteString("retry:")
|
|
||||||
w.WriteString(strconv.FormatUint(uint64(retry), 10))
|
|
||||||
w.WriteString("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeData(w stringWriter, data interface{}) error {
|
|
||||||
w.WriteString("data:")
|
|
||||||
switch kindOfData(data) {
|
|
||||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
|
||||||
err := json.NewEncoder(w).Encode(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.WriteString("\n")
|
|
||||||
default:
|
|
||||||
dataReplacer.WriteString(w, fmt.Sprint(data))
|
|
||||||
w.WriteString("\n\n")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Event) Render(w http.ResponseWriter) error {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
return Encode(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Event) WriteContentType(w http.ResponseWriter) {
|
|
||||||
header := w.Header()
|
|
||||||
header["Content-Type"] = contentType
|
|
||||||
|
|
||||||
if _, exist := header["Cache-Control"]; !exist {
|
|
||||||
header["Cache-Control"] = noCache
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func kindOfData(data interface{}) reflect.Kind {
|
|
||||||
value := reflect.ValueOf(data)
|
|
||||||
valueType := value.Kind()
|
|
||||||
if valueType == reflect.Ptr {
|
|
||||||
valueType = value.Elem().Kind()
|
|
||||||
}
|
|
||||||
return valueType
|
|
||||||
}
|
|
24
vendor/github.com/gin-contrib/sse/writer.go
generated
vendored
24
vendor/github.com/gin-contrib/sse/writer.go
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
package sse
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
type stringWriter interface {
|
|
||||||
io.Writer
|
|
||||||
WriteString(string) (int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type stringWrapper struct {
|
|
||||||
io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w stringWrapper) WriteString(str string) (int, error) {
|
|
||||||
return w.Writer.Write([]byte(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkWriter(writer io.Writer) stringWriter {
|
|
||||||
if w, ok := writer.(stringWriter); ok {
|
|
||||||
return w
|
|
||||||
} else {
|
|
||||||
return stringWrapper{writer}
|
|
||||||
}
|
|
||||||
}
|
|
5
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
5
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
vendor/*
|
|
||||||
!vendor/vendor.json
|
|
||||||
coverage.out
|
|
||||||
count.out
|
|
||||||
test
|
|
35
vendor/github.com/gin-gonic/gin/.travis.yml
generated
vendored
35
vendor/github.com/gin-gonic/gin/.travis.yml
generated
vendored
@ -1,35 +0,0 @@
|
|||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
go:
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- master
|
|
||||||
|
|
||||||
git:
|
|
||||||
depth: 10
|
|
||||||
|
|
||||||
install:
|
|
||||||
- make install
|
|
||||||
|
|
||||||
go_import_path: github.com/gin-gonic/gin
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make vet
|
|
||||||
- make fmt-check
|
|
||||||
- make embedmd
|
|
||||||
- make misspell-check
|
|
||||||
- make test
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
webhooks:
|
|
||||||
urls:
|
|
||||||
- https://webhooks.gitter.im/e/7f95bf605c4d356372f4
|
|
||||||
on_success: change # options: [always|never|change] default: always
|
|
||||||
on_failure: always # options: [always|never|change] default: always
|
|
||||||
on_start: false # default: false
|
|
231
vendor/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
231
vendor/github.com/gin-gonic/gin/AUTHORS.md
generated
vendored
@ -1,231 +0,0 @@
|
|||||||
List of all the awesome people working to make Gin the best Web Framework in Go.
|
|
||||||
|
|
||||||
## gin 1.x series authors
|
|
||||||
|
|
||||||
**Gin Core Team:** Bo-Yi Wu (@appleboy), 田欧 (@thinkerou), Javier Provecho (@javierprovecho)
|
|
||||||
|
|
||||||
## gin 0.x series authors
|
|
||||||
|
|
||||||
**Maintainers:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
|
|
||||||
|
|
||||||
People and companies, who have contributed, in alphabetical order.
|
|
||||||
|
|
||||||
**@858806258 (杰哥)**
|
|
||||||
- Fix typo in example
|
|
||||||
|
|
||||||
|
|
||||||
**@achedeuzot (Klemen Sever)**
|
|
||||||
- Fix newline debug printing
|
|
||||||
|
|
||||||
|
|
||||||
**@adammck (Adam Mckaig)**
|
|
||||||
- Add MIT license
|
|
||||||
|
|
||||||
|
|
||||||
**@AlexanderChen1989 (Alexander)**
|
|
||||||
- Typos in README
|
|
||||||
|
|
||||||
|
|
||||||
**@alexanderdidenko (Aleksandr Didenko)**
|
|
||||||
- Add support multipart/form-data
|
|
||||||
|
|
||||||
|
|
||||||
**@alexandernyquist (Alexander Nyquist)**
|
|
||||||
- Using template.Must to fix multiple return issue
|
|
||||||
- ★ Added support for OPTIONS verb
|
|
||||||
- ★ Setting response headers before calling WriteHeader
|
|
||||||
- Improved documentation for model binding
|
|
||||||
- ★ Added Content.Redirect()
|
|
||||||
- ★ Added tons of Unit tests
|
|
||||||
|
|
||||||
|
|
||||||
**@austinheap (Austin Heap)**
|
|
||||||
- Added travis CI integration
|
|
||||||
|
|
||||||
|
|
||||||
**@andredublin (Andre Dublin)**
|
|
||||||
- Fix typo in comment
|
|
||||||
|
|
||||||
|
|
||||||
**@bredov (Ludwig Valda Vasquez)**
|
|
||||||
- Fix html templating in debug mode
|
|
||||||
|
|
||||||
|
|
||||||
**@bluele (Jun Kimura)**
|
|
||||||
- Fixes code examples in README
|
|
||||||
|
|
||||||
|
|
||||||
**@chad-russell**
|
|
||||||
- ★ Support for serializing gin.H into XML
|
|
||||||
|
|
||||||
|
|
||||||
**@dickeyxxx (Jeff Dickey)**
|
|
||||||
- Typos in README
|
|
||||||
- Add example about serving static files
|
|
||||||
|
|
||||||
|
|
||||||
**@donileo (Adonis)**
|
|
||||||
- Add NoMethod handler
|
|
||||||
|
|
||||||
|
|
||||||
**@dutchcoders (DutchCoders)**
|
|
||||||
- ★ Fix security bug that allows client to spoof ip
|
|
||||||
- Fix typo. r.HTMLTemplates -> SetHTMLTemplate
|
|
||||||
|
|
||||||
|
|
||||||
**@el3ctro- (Joshua Loper)**
|
|
||||||
- Fix typo in example
|
|
||||||
|
|
||||||
|
|
||||||
**@ethankan (Ethan Kan)**
|
|
||||||
- Unsigned integers in binding
|
|
||||||
|
|
||||||
|
|
||||||
**(Evgeny Persienko)**
|
|
||||||
- Validate sub structures
|
|
||||||
|
|
||||||
|
|
||||||
**@frankbille (Frank Bille)**
|
|
||||||
- Add support for HTTP Realm Auth
|
|
||||||
|
|
||||||
|
|
||||||
**@fmd (Fareed Dudhia)**
|
|
||||||
- Fix typo. SetHTTPTemplate -> SetHTMLTemplate
|
|
||||||
|
|
||||||
|
|
||||||
**@ironiridis (Christopher Harrington)**
|
|
||||||
- Remove old reference
|
|
||||||
|
|
||||||
|
|
||||||
**@jammie-stackhouse (Jamie Stackhouse)**
|
|
||||||
- Add more shortcuts for router methods
|
|
||||||
|
|
||||||
|
|
||||||
**@jasonrhansen**
|
|
||||||
- Fix spelling and grammar errors in documentation
|
|
||||||
|
|
||||||
|
|
||||||
**@JasonSoft (Jason Lee)**
|
|
||||||
- Fix typo in comment
|
|
||||||
|
|
||||||
|
|
||||||
**@joiggama (Ignacio Galindo)**
|
|
||||||
- Add utf-8 charset header on renders
|
|
||||||
|
|
||||||
|
|
||||||
**@julienschmidt (Julien Schmidt)**
|
|
||||||
- gofmt the code examples
|
|
||||||
|
|
||||||
|
|
||||||
**@kelcecil (Kel Cecil)**
|
|
||||||
- Fix readme typo
|
|
||||||
|
|
||||||
|
|
||||||
**@kyledinh (Kyle Dinh)**
|
|
||||||
- Adds RunTLS()
|
|
||||||
|
|
||||||
|
|
||||||
**@LinusU (Linus Unnebäck)**
|
|
||||||
- Small fixes in README
|
|
||||||
|
|
||||||
|
|
||||||
**@loongmxbt (Saint Asky)**
|
|
||||||
- Fix typo in example
|
|
||||||
|
|
||||||
|
|
||||||
**@lucas-clemente (Lucas Clemente)**
|
|
||||||
- ★ work around path.Join removing trailing slashes from routes
|
|
||||||
|
|
||||||
|
|
||||||
**@mattn (Yasuhiro Matsumoto)**
|
|
||||||
- Improve color logger
|
|
||||||
|
|
||||||
|
|
||||||
**@mdigger (Dmitry Sedykh)**
|
|
||||||
- Fixes Form binding when content-type is x-www-form-urlencoded
|
|
||||||
- No repeat call c.Writer.Status() in gin.Logger
|
|
||||||
- Fixes Content-Type for json render
|
|
||||||
|
|
||||||
|
|
||||||
**@mirzac (Mirza Ceric)**
|
|
||||||
- Fix debug printing
|
|
||||||
|
|
||||||
|
|
||||||
**@mopemope (Yutaka Matsubara)**
|
|
||||||
- ★ Adds Godep support (Dependencies Manager)
|
|
||||||
- Fix variadic parameter in the flexible render API
|
|
||||||
- Fix Corrupted plain render
|
|
||||||
- Add Pluggable View Renderer Example
|
|
||||||
|
|
||||||
|
|
||||||
**@msemenistyi (Mykyta Semenistyi)**
|
|
||||||
- update Readme.md. Add code to String method
|
|
||||||
|
|
||||||
|
|
||||||
**@msoedov (Sasha Myasoedov)**
|
|
||||||
- ★ Adds tons of unit tests.
|
|
||||||
|
|
||||||
|
|
||||||
**@ngerakines (Nick Gerakines)**
|
|
||||||
- ★ Improves API, c.GET() doesn't panic
|
|
||||||
- Adds MustGet() method
|
|
||||||
|
|
||||||
|
|
||||||
**@r8k (Rajiv Kilaparti)**
|
|
||||||
- Fix Port usage in README.
|
|
||||||
|
|
||||||
|
|
||||||
**@rayrod2030 (Ray Rodriguez)**
|
|
||||||
- Fix typo in example
|
|
||||||
|
|
||||||
|
|
||||||
**@rns**
|
|
||||||
- Fix typo in example
|
|
||||||
|
|
||||||
|
|
||||||
**@RobAWilkinson (Robert Wilkinson)**
|
|
||||||
- Add example of forms and params
|
|
||||||
|
|
||||||
|
|
||||||
**@rogierlommers (Rogier Lommers)**
|
|
||||||
- Add updated static serve example
|
|
||||||
|
|
||||||
|
|
||||||
**@se77en (Damon Zhao)**
|
|
||||||
- Improve color logging
|
|
||||||
|
|
||||||
|
|
||||||
**@silasb (Silas Baronda)**
|
|
||||||
- Fixing quotes in README
|
|
||||||
|
|
||||||
|
|
||||||
**@SkuliOskarsson (Skuli Oskarsson)**
|
|
||||||
- Fixes some texts in README II
|
|
||||||
|
|
||||||
|
|
||||||
**@slimmy (Jimmy Pettersson)**
|
|
||||||
- Added messages for required bindings
|
|
||||||
|
|
||||||
|
|
||||||
**@smira (Andrey Smirnov)**
|
|
||||||
- Add support for ignored/unexported fields in binding
|
|
||||||
|
|
||||||
|
|
||||||
**@superalsrk (SRK.Lyu)**
|
|
||||||
- Update httprouter godeps
|
|
||||||
|
|
||||||
|
|
||||||
**@tebeka (Miki Tebeka)**
|
|
||||||
- Use net/http constants instead of numeric values
|
|
||||||
|
|
||||||
|
|
||||||
**@techjanitor**
|
|
||||||
- Update context.go reserved IPs
|
|
||||||
|
|
||||||
|
|
||||||
**@yosssi (Keiji Yoshida)**
|
|
||||||
- Fix link in README
|
|
||||||
|
|
||||||
|
|
||||||
**@yuyabee**
|
|
||||||
- Fixed README
|
|
604
vendor/github.com/gin-gonic/gin/BENCHMARKS.md
generated
vendored
604
vendor/github.com/gin-gonic/gin/BENCHMARKS.md
generated
vendored
@ -1,604 +0,0 @@
|
|||||||
|
|
||||||
## Benchmark System
|
|
||||||
|
|
||||||
**VM HOST:** DigitalOcean
|
|
||||||
**Machine:** 4 CPU, 8 GB RAM. Ubuntu 16.04.2 x64
|
|
||||||
**Date:** July 19th, 2017
|
|
||||||
**Go Version:** 1.8.3 linux/amd64
|
|
||||||
**Source:** [Go HTTP Router Benchmark](https://github.com/julienschmidt/go-http-routing-benchmark)
|
|
||||||
|
|
||||||
## Static Routes: 157
|
|
||||||
|
|
||||||
```
|
|
||||||
Gin: 30512 Bytes
|
|
||||||
|
|
||||||
HttpServeMux: 17344 Bytes
|
|
||||||
Ace: 30080 Bytes
|
|
||||||
Bear: 30472 Bytes
|
|
||||||
Beego: 96408 Bytes
|
|
||||||
Bone: 37904 Bytes
|
|
||||||
Denco: 10464 Bytes
|
|
||||||
Echo: 73680 Bytes
|
|
||||||
GocraftWeb: 55720 Bytes
|
|
||||||
Goji: 27200 Bytes
|
|
||||||
Gojiv2: 104464 Bytes
|
|
||||||
GoJsonRest: 136472 Bytes
|
|
||||||
GoRestful: 914904 Bytes
|
|
||||||
GorillaMux: 675568 Bytes
|
|
||||||
HttpRouter: 21128 Bytes
|
|
||||||
HttpTreeMux: 73448 Bytes
|
|
||||||
Kocha: 115072 Bytes
|
|
||||||
LARS: 30120 Bytes
|
|
||||||
Macaron: 37984 Bytes
|
|
||||||
Martini: 310832 Bytes
|
|
||||||
Pat: 20464 Bytes
|
|
||||||
Possum: 91328 Bytes
|
|
||||||
R2router: 23712 Bytes
|
|
||||||
Rivet: 23880 Bytes
|
|
||||||
Tango: 28008 Bytes
|
|
||||||
TigerTonic: 80368 Bytes
|
|
||||||
Traffic: 626480 Bytes
|
|
||||||
Vulcan: 369064 Bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
## GithubAPI Routes: 203
|
|
||||||
|
|
||||||
```
|
|
||||||
Gin: 52672 Bytes
|
|
||||||
|
|
||||||
Ace: 48992 Bytes
|
|
||||||
Bear: 161592 Bytes
|
|
||||||
Beego: 147992 Bytes
|
|
||||||
Bone: 97728 Bytes
|
|
||||||
Denco: 36440 Bytes
|
|
||||||
Echo: 95672 Bytes
|
|
||||||
GocraftWeb: 95640 Bytes
|
|
||||||
Goji: 86088 Bytes
|
|
||||||
Gojiv2: 144392 Bytes
|
|
||||||
GoJsonRest: 134648 Bytes
|
|
||||||
GoRestful: 1410760 Bytes
|
|
||||||
GorillaMux: 1509488 Bytes
|
|
||||||
HttpRouter: 37464 Bytes
|
|
||||||
HttpTreeMux: 78800 Bytes
|
|
||||||
Kocha: 785408 Bytes
|
|
||||||
LARS: 49032 Bytes
|
|
||||||
Macaron: 132712 Bytes
|
|
||||||
Martini: 564352 Bytes
|
|
||||||
Pat: 21200 Bytes
|
|
||||||
Possum: 83888 Bytes
|
|
||||||
R2router: 47104 Bytes
|
|
||||||
Rivet: 42840 Bytes
|
|
||||||
Tango: 54584 Bytes
|
|
||||||
TigerTonic: 96384 Bytes
|
|
||||||
Traffic: 1061920 Bytes
|
|
||||||
Vulcan: 465296 Bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
## GPlusAPI Routes: 13
|
|
||||||
|
|
||||||
```
|
|
||||||
Gin: 3968 Bytes
|
|
||||||
|
|
||||||
Ace: 3600 Bytes
|
|
||||||
Bear: 7112 Bytes
|
|
||||||
Beego: 10048 Bytes
|
|
||||||
Bone: 6480 Bytes
|
|
||||||
Denco: 3256 Bytes
|
|
||||||
Echo: 9000 Bytes
|
|
||||||
GocraftWeb: 7496 Bytes
|
|
||||||
Goji: 2912 Bytes
|
|
||||||
Gojiv2: 7376 Bytes
|
|
||||||
GoJsonRest: 11544 Bytes
|
|
||||||
GoRestful: 88776 Bytes
|
|
||||||
GorillaMux: 71488 Bytes
|
|
||||||
HttpRouter: 2712 Bytes
|
|
||||||
HttpTreeMux: 7440 Bytes
|
|
||||||
Kocha: 128880 Bytes
|
|
||||||
LARS: 3640 Bytes
|
|
||||||
Macaron: 8656 Bytes
|
|
||||||
Martini: 23936 Bytes
|
|
||||||
Pat: 1856 Bytes
|
|
||||||
Possum: 7248 Bytes
|
|
||||||
R2router: 3928 Bytes
|
|
||||||
Rivet: 3064 Bytes
|
|
||||||
Tango: 4912 Bytes
|
|
||||||
TigerTonic: 9408 Bytes
|
|
||||||
Traffic: 49472 Bytes
|
|
||||||
Vulcan: 25496 Bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
## ParseAPI Routes: 26
|
|
||||||
|
|
||||||
```
|
|
||||||
Gin: 6928 Bytes
|
|
||||||
|
|
||||||
Ace: 6592 Bytes
|
|
||||||
Bear: 12320 Bytes
|
|
||||||
Beego: 18960 Bytes
|
|
||||||
Bone: 11024 Bytes
|
|
||||||
Denco: 4184 Bytes
|
|
||||||
Echo: 11168 Bytes
|
|
||||||
GocraftWeb: 12800 Bytes
|
|
||||||
Goji: 5232 Bytes
|
|
||||||
Gojiv2: 14464 Bytes
|
|
||||||
GoJsonRest: 14216 Bytes
|
|
||||||
GoRestful: 127368 Bytes
|
|
||||||
GorillaMux: 123016 Bytes
|
|
||||||
HttpRouter: 4976 Bytes
|
|
||||||
HttpTreeMux: 7848 Bytes
|
|
||||||
Kocha: 181712 Bytes
|
|
||||||
LARS: 6632 Bytes
|
|
||||||
Macaron: 13648 Bytes
|
|
||||||
Martini: 45952 Bytes
|
|
||||||
Pat: 2560 Bytes
|
|
||||||
Possum: 9200 Bytes
|
|
||||||
R2router: 7056 Bytes
|
|
||||||
Rivet: 5680 Bytes
|
|
||||||
Tango: 8664 Bytes
|
|
||||||
TigerTonic: 9840 Bytes
|
|
||||||
Traffic: 93480 Bytes
|
|
||||||
Vulcan: 44504 Bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Static Routes
|
|
||||||
|
|
||||||
```
|
|
||||||
BenchmarkGin_StaticAll 50000 34506 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
BenchmarkAce_StaticAll 30000 49657 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkHttpServeMux_StaticAll 2000 1183737 ns/op 96 B/op 8 allocs/op
|
|
||||||
BenchmarkBeego_StaticAll 5000 412621 ns/op 57776 B/op 628 allocs/op
|
|
||||||
BenchmarkBear_StaticAll 10000 149242 ns/op 20336 B/op 461 allocs/op
|
|
||||||
BenchmarkBone_StaticAll 10000 118583 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkDenco_StaticAll 100000 13247 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkEcho_StaticAll 20000 79914 ns/op 5024 B/op 157 allocs/op
|
|
||||||
BenchmarkGocraftWeb_StaticAll 10000 211823 ns/op 46440 B/op 785 allocs/op
|
|
||||||
BenchmarkGoji_StaticAll 10000 109390 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGojiv2_StaticAll 3000 415533 ns/op 145696 B/op 1099 allocs/op
|
|
||||||
BenchmarkGoJsonRest_StaticAll 5000 364403 ns/op 51653 B/op 1727 allocs/op
|
|
||||||
BenchmarkGoRestful_StaticAll 500 2578579 ns/op 314936 B/op 3144 allocs/op
|
|
||||||
BenchmarkGorillaMux_StaticAll 500 2704856 ns/op 115648 B/op 1578 allocs/op
|
|
||||||
BenchmarkHttpRouter_StaticAll 100000 18541 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_StaticAll 100000 22332 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkKocha_StaticAll 50000 31176 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkLARS_StaticAll 50000 40840 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_StaticAll 5000 517656 ns/op 120576 B/op 1413 allocs/op
|
|
||||||
BenchmarkMartini_StaticAll 300 4462289 ns/op 125442 B/op 1717 allocs/op
|
|
||||||
BenchmarkPat_StaticAll 500 2157275 ns/op 533904 B/op 11123 allocs/op
|
|
||||||
BenchmarkPossum_StaticAll 10000 254701 ns/op 65312 B/op 471 allocs/op
|
|
||||||
BenchmarkR2router_StaticAll 10000 133956 ns/op 22608 B/op 628 allocs/op
|
|
||||||
BenchmarkRivet_StaticAll 30000 46812 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTango_StaticAll 5000 390613 ns/op 39225 B/op 1256 allocs/op
|
|
||||||
BenchmarkTigerTonic_StaticAll 20000 88060 ns/op 7504 B/op 157 allocs/op
|
|
||||||
BenchmarkTraffic_StaticAll 500 2910236 ns/op 729736 B/op 14287 allocs/op
|
|
||||||
BenchmarkVulcan_StaticAll 5000 277366 ns/op 15386 B/op 471 allocs/op
|
|
||||||
```
|
|
||||||
|
|
||||||
## Micro Benchmarks
|
|
||||||
|
|
||||||
```
|
|
||||||
BenchmarkGin_Param 20000000 113 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
BenchmarkAce_Param 5000000 375 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_Param 1000000 1709 ns/op 456 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_Param 1000000 2484 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_Param 1000000 2391 ns/op 688 B/op 5 allocs/op
|
|
||||||
BenchmarkDenco_Param 10000000 240 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_Param 5000000 366 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGocraftWeb_Param 1000000 2343 ns/op 648 B/op 8 allocs/op
|
|
||||||
BenchmarkGoji_Param 1000000 1197 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_Param 1000000 2771 ns/op 944 B/op 8 allocs/op
|
|
||||||
BenchmarkGoJsonRest_Param 1000000 2993 ns/op 649 B/op 13 allocs/op
|
|
||||||
BenchmarkGoRestful_Param 200000 8860 ns/op 2296 B/op 21 allocs/op
|
|
||||||
BenchmarkGorillaMux_Param 500000 4461 ns/op 1056 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpRouter_Param 10000000 175 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_Param 1000000 1167 ns/op 352 B/op 3 allocs/op
|
|
||||||
BenchmarkKocha_Param 3000000 429 ns/op 56 B/op 3 allocs/op
|
|
||||||
BenchmarkLARS_Param 10000000 134 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_Param 500000 4635 ns/op 1056 B/op 10 allocs/op
|
|
||||||
BenchmarkMartini_Param 200000 9933 ns/op 1072 B/op 10 allocs/op
|
|
||||||
BenchmarkPat_Param 1000000 2929 ns/op 648 B/op 12 allocs/op
|
|
||||||
BenchmarkPossum_Param 1000000 2503 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_Param 1000000 1507 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_Param 5000000 297 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_Param 1000000 1862 ns/op 248 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_Param 500000 5660 ns/op 992 B/op 17 allocs/op
|
|
||||||
BenchmarkTraffic_Param 200000 8408 ns/op 1960 B/op 21 allocs/op
|
|
||||||
BenchmarkVulcan_Param 2000000 963 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_Param5 2000000 740 ns/op 160 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_Param5 1000000 2777 ns/op 501 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_Param5 1000000 3740 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_Param5 1000000 2950 ns/op 736 B/op 5 allocs/op
|
|
||||||
BenchmarkDenco_Param5 2000000 644 ns/op 160 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_Param5 3000000 558 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGin_Param5 10000000 198 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_Param5 500000 3870 ns/op 920 B/op 11 allocs/op
|
|
||||||
BenchmarkGoji_Param5 1000000 1746 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_Param5 1000000 3214 ns/op 1008 B/op 8 allocs/op
|
|
||||||
BenchmarkGoJsonRest_Param5 500000 5509 ns/op 1097 B/op 16 allocs/op
|
|
||||||
BenchmarkGoRestful_Param5 200000 11232 ns/op 2392 B/op 21 allocs/op
|
|
||||||
BenchmarkGorillaMux_Param5 300000 7777 ns/op 1184 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpRouter_Param5 3000000 631 ns/op 160 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_Param5 1000000 2800 ns/op 576 B/op 6 allocs/op
|
|
||||||
BenchmarkKocha_Param5 1000000 2053 ns/op 440 B/op 10 allocs/op
|
|
||||||
BenchmarkLARS_Param5 10000000 232 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_Param5 500000 5888 ns/op 1056 B/op 10 allocs/op
|
|
||||||
BenchmarkMartini_Param5 200000 12807 ns/op 1232 B/op 11 allocs/op
|
|
||||||
BenchmarkPat_Param5 300000 7320 ns/op 964 B/op 32 allocs/op
|
|
||||||
BenchmarkPossum_Param5 1000000 2495 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_Param5 1000000 1844 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_Param5 2000000 935 ns/op 240 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_Param5 1000000 2327 ns/op 360 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_Param5 100000 18514 ns/op 2551 B/op 43 allocs/op
|
|
||||||
BenchmarkTraffic_Param5 200000 11997 ns/op 2248 B/op 25 allocs/op
|
|
||||||
BenchmarkVulcan_Param5 1000000 1333 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_Param20 1000000 2031 ns/op 640 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_Param20 200000 7285 ns/op 1664 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_Param20 300000 6224 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_Param20 200000 8023 ns/op 1903 B/op 5 allocs/op
|
|
||||||
BenchmarkDenco_Param20 1000000 2262 ns/op 640 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_Param20 1000000 1387 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGin_Param20 3000000 503 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_Param20 100000 14408 ns/op 3795 B/op 15 allocs/op
|
|
||||||
BenchmarkGoji_Param20 500000 5272 ns/op 1247 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_Param20 1000000 4163 ns/op 1248 B/op 8 allocs/op
|
|
||||||
BenchmarkGoJsonRest_Param20 100000 17866 ns/op 4485 B/op 20 allocs/op
|
|
||||||
BenchmarkGoRestful_Param20 100000 21022 ns/op 4724 B/op 23 allocs/op
|
|
||||||
BenchmarkGorillaMux_Param20 100000 17055 ns/op 3547 B/op 13 allocs/op
|
|
||||||
BenchmarkHttpRouter_Param20 1000000 1748 ns/op 640 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_Param20 200000 12246 ns/op 3196 B/op 10 allocs/op
|
|
||||||
BenchmarkKocha_Param20 300000 6861 ns/op 1808 B/op 27 allocs/op
|
|
||||||
BenchmarkLARS_Param20 3000000 526 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_Param20 100000 13069 ns/op 2906 B/op 12 allocs/op
|
|
||||||
BenchmarkMartini_Param20 100000 23602 ns/op 3597 B/op 13 allocs/op
|
|
||||||
BenchmarkPat_Param20 50000 32143 ns/op 4688 B/op 111 allocs/op
|
|
||||||
BenchmarkPossum_Param20 1000000 2396 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_Param20 200000 8907 ns/op 2283 B/op 7 allocs/op
|
|
||||||
BenchmarkRivet_Param20 1000000 3280 ns/op 1024 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_Param20 500000 4640 ns/op 856 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_Param20 20000 67581 ns/op 10532 B/op 138 allocs/op
|
|
||||||
BenchmarkTraffic_Param20 50000 40313 ns/op 7941 B/op 45 allocs/op
|
|
||||||
BenchmarkVulcan_Param20 1000000 2264 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_ParamWrite 3000000 532 ns/op 40 B/op 2 allocs/op
|
|
||||||
BenchmarkBear_ParamWrite 1000000 1778 ns/op 456 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_ParamWrite 1000000 2596 ns/op 376 B/op 5 allocs/op
|
|
||||||
BenchmarkBone_ParamWrite 1000000 2519 ns/op 688 B/op 5 allocs/op
|
|
||||||
BenchmarkDenco_ParamWrite 5000000 411 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_ParamWrite 2000000 718 ns/op 40 B/op 2 allocs/op
|
|
||||||
BenchmarkGin_ParamWrite 5000000 283 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_ParamWrite 1000000 2561 ns/op 656 B/op 9 allocs/op
|
|
||||||
BenchmarkGoji_ParamWrite 1000000 1378 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_ParamWrite 1000000 3128 ns/op 976 B/op 10 allocs/op
|
|
||||||
BenchmarkGoJsonRest_ParamWrite 500000 4446 ns/op 1128 B/op 18 allocs/op
|
|
||||||
BenchmarkGoRestful_ParamWrite 200000 10291 ns/op 2304 B/op 22 allocs/op
|
|
||||||
BenchmarkGorillaMux_ParamWrite 500000 5153 ns/op 1064 B/op 12 allocs/op
|
|
||||||
BenchmarkHttpRouter_ParamWrite 5000000 263 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_ParamWrite 1000000 1351 ns/op 352 B/op 3 allocs/op
|
|
||||||
BenchmarkKocha_ParamWrite 3000000 538 ns/op 56 B/op 3 allocs/op
|
|
||||||
BenchmarkLARS_ParamWrite 5000000 316 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_ParamWrite 500000 5756 ns/op 1160 B/op 14 allocs/op
|
|
||||||
BenchmarkMartini_ParamWrite 200000 13097 ns/op 1176 B/op 14 allocs/op
|
|
||||||
BenchmarkPat_ParamWrite 500000 4954 ns/op 1072 B/op 17 allocs/op
|
|
||||||
BenchmarkPossum_ParamWrite 1000000 2499 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_ParamWrite 1000000 1531 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_ParamWrite 3000000 570 ns/op 112 B/op 2 allocs/op
|
|
||||||
BenchmarkTango_ParamWrite 2000000 957 ns/op 136 B/op 4 allocs/op
|
|
||||||
BenchmarkTigerTonic_ParamWrite 200000 7025 ns/op 1424 B/op 23 allocs/op
|
|
||||||
BenchmarkTraffic_ParamWrite 200000 10112 ns/op 2384 B/op 25 allocs/op
|
|
||||||
BenchmarkVulcan_ParamWrite 1000000 1006 ns/op 98 B/op 3 allocs/op
|
|
||||||
```
|
|
||||||
|
|
||||||
## GitHub
|
|
||||||
|
|
||||||
```
|
|
||||||
BenchmarkGin_GithubStatic 10000000 156 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
BenchmarkAce_GithubStatic 5000000 294 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBear_GithubStatic 2000000 893 ns/op 120 B/op 3 allocs/op
|
|
||||||
BenchmarkBeego_GithubStatic 1000000 2491 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_GithubStatic 50000 25300 ns/op 2880 B/op 60 allocs/op
|
|
||||||
BenchmarkDenco_GithubStatic 20000000 76.0 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkEcho_GithubStatic 2000000 516 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGocraftWeb_GithubStatic 1000000 1448 ns/op 296 B/op 5 allocs/op
|
|
||||||
BenchmarkGoji_GithubStatic 3000000 496 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGojiv2_GithubStatic 1000000 2941 ns/op 928 B/op 7 allocs/op
|
|
||||||
BenchmarkGoRestful_GithubStatic 100000 27256 ns/op 3224 B/op 22 allocs/op
|
|
||||||
BenchmarkGoJsonRest_GithubStatic 1000000 2196 ns/op 329 B/op 11 allocs/op
|
|
||||||
BenchmarkGorillaMux_GithubStatic 50000 31617 ns/op 736 B/op 10 allocs/op
|
|
||||||
BenchmarkHttpRouter_GithubStatic 20000000 88.4 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_GithubStatic 10000000 134 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkKocha_GithubStatic 20000000 113 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkLARS_GithubStatic 10000000 195 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_GithubStatic 500000 3740 ns/op 768 B/op 9 allocs/op
|
|
||||||
BenchmarkMartini_GithubStatic 50000 27673 ns/op 768 B/op 9 allocs/op
|
|
||||||
BenchmarkPat_GithubStatic 100000 19470 ns/op 3648 B/op 76 allocs/op
|
|
||||||
BenchmarkPossum_GithubStatic 1000000 1729 ns/op 416 B/op 3 allocs/op
|
|
||||||
BenchmarkR2router_GithubStatic 2000000 879 ns/op 144 B/op 4 allocs/op
|
|
||||||
BenchmarkRivet_GithubStatic 10000000 231 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTango_GithubStatic 1000000 2325 ns/op 248 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_GithubStatic 3000000 610 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkTraffic_GithubStatic 20000 62973 ns/op 18904 B/op 148 allocs/op
|
|
||||||
BenchmarkVulcan_GithubStatic 1000000 1447 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_GithubParam 2000000 686 ns/op 96 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_GithubParam 1000000 2155 ns/op 496 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_GithubParam 1000000 2713 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_GithubParam 100000 15088 ns/op 1760 B/op 18 allocs/op
|
|
||||||
BenchmarkDenco_GithubParam 2000000 629 ns/op 128 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_GithubParam 2000000 653 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGin_GithubParam 5000000 255 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_GithubParam 1000000 3145 ns/op 712 B/op 9 allocs/op
|
|
||||||
BenchmarkGoji_GithubParam 1000000 1916 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_GithubParam 1000000 3975 ns/op 1024 B/op 10 allocs/op
|
|
||||||
BenchmarkGoJsonRest_GithubParam 300000 4134 ns/op 713 B/op 14 allocs/op
|
|
||||||
BenchmarkGoRestful_GithubParam 50000 30782 ns/op 2360 B/op 21 allocs/op
|
|
||||||
BenchmarkGorillaMux_GithubParam 100000 17148 ns/op 1088 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpRouter_GithubParam 3000000 523 ns/op 96 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_GithubParam 1000000 1671 ns/op 384 B/op 4 allocs/op
|
|
||||||
BenchmarkKocha_GithubParam 1000000 1021 ns/op 128 B/op 5 allocs/op
|
|
||||||
BenchmarkLARS_GithubParam 5000000 283 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_GithubParam 500000 4270 ns/op 1056 B/op 10 allocs/op
|
|
||||||
BenchmarkMartini_GithubParam 100000 21728 ns/op 1152 B/op 11 allocs/op
|
|
||||||
BenchmarkPat_GithubParam 200000 11208 ns/op 2464 B/op 48 allocs/op
|
|
||||||
BenchmarkPossum_GithubParam 1000000 2334 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_GithubParam 1000000 1487 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_GithubParam 2000000 782 ns/op 96 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_GithubParam 1000000 2653 ns/op 344 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_GithubParam 300000 14073 ns/op 1440 B/op 24 allocs/op
|
|
||||||
BenchmarkTraffic_GithubParam 50000 29164 ns/op 5992 B/op 52 allocs/op
|
|
||||||
BenchmarkVulcan_GithubParam 1000000 2529 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_GithubAll 10000 134059 ns/op 13792 B/op 167 allocs/op
|
|
||||||
BenchmarkBear_GithubAll 5000 534445 ns/op 86448 B/op 943 allocs/op
|
|
||||||
BenchmarkBeego_GithubAll 3000 592444 ns/op 74705 B/op 812 allocs/op
|
|
||||||
BenchmarkBone_GithubAll 200 6957308 ns/op 698784 B/op 8453 allocs/op
|
|
||||||
BenchmarkDenco_GithubAll 10000 158819 ns/op 20224 B/op 167 allocs/op
|
|
||||||
BenchmarkEcho_GithubAll 10000 154700 ns/op 6496 B/op 203 allocs/op
|
|
||||||
BenchmarkGin_GithubAll 30000 48375 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_GithubAll 3000 570806 ns/op 131656 B/op 1686 allocs/op
|
|
||||||
BenchmarkGoji_GithubAll 2000 818034 ns/op 56112 B/op 334 allocs/op
|
|
||||||
BenchmarkGojiv2_GithubAll 2000 1213973 ns/op 274768 B/op 3712 allocs/op
|
|
||||||
BenchmarkGoJsonRest_GithubAll 2000 785796 ns/op 134371 B/op 2737 allocs/op
|
|
||||||
BenchmarkGoRestful_GithubAll 300 5238188 ns/op 689672 B/op 4519 allocs/op
|
|
||||||
BenchmarkGorillaMux_GithubAll 100 10257726 ns/op 211840 B/op 2272 allocs/op
|
|
||||||
BenchmarkHttpRouter_GithubAll 20000 105414 ns/op 13792 B/op 167 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_GithubAll 10000 319934 ns/op 65856 B/op 671 allocs/op
|
|
||||||
BenchmarkKocha_GithubAll 10000 209442 ns/op 23304 B/op 843 allocs/op
|
|
||||||
BenchmarkLARS_GithubAll 20000 62565 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_GithubAll 2000 1161270 ns/op 204194 B/op 2000 allocs/op
|
|
||||||
BenchmarkMartini_GithubAll 200 9991713 ns/op 226549 B/op 2325 allocs/op
|
|
||||||
BenchmarkPat_GithubAll 200 5590793 ns/op 1499568 B/op 27435 allocs/op
|
|
||||||
BenchmarkPossum_GithubAll 10000 319768 ns/op 84448 B/op 609 allocs/op
|
|
||||||
BenchmarkR2router_GithubAll 10000 305134 ns/op 77328 B/op 979 allocs/op
|
|
||||||
BenchmarkRivet_GithubAll 10000 132134 ns/op 16272 B/op 167 allocs/op
|
|
||||||
BenchmarkTango_GithubAll 3000 552754 ns/op 63826 B/op 1618 allocs/op
|
|
||||||
BenchmarkTigerTonic_GithubAll 1000 1439483 ns/op 239104 B/op 5374 allocs/op
|
|
||||||
BenchmarkTraffic_GithubAll 100 11383067 ns/op 2659329 B/op 21848 allocs/op
|
|
||||||
BenchmarkVulcan_GithubAll 5000 394253 ns/op 19894 B/op 609 allocs/op
|
|
||||||
```
|
|
||||||
|
|
||||||
## Google+
|
|
||||||
|
|
||||||
```
|
|
||||||
BenchmarkGin_GPlusStatic 10000000 183 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
BenchmarkAce_GPlusStatic 5000000 276 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBear_GPlusStatic 2000000 652 ns/op 104 B/op 3 allocs/op
|
|
||||||
BenchmarkBeego_GPlusStatic 1000000 2239 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_GPlusStatic 5000000 380 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkDenco_GPlusStatic 30000000 45.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkEcho_GPlusStatic 5000000 338 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGocraftWeb_GPlusStatic 1000000 1158 ns/op 280 B/op 5 allocs/op
|
|
||||||
BenchmarkGoji_GPlusStatic 5000000 331 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGojiv2_GPlusStatic 1000000 2106 ns/op 928 B/op 7 allocs/op
|
|
||||||
BenchmarkGoJsonRest_GPlusStatic 1000000 1626 ns/op 329 B/op 11 allocs/op
|
|
||||||
BenchmarkGoRestful_GPlusStatic 300000 7598 ns/op 1976 B/op 20 allocs/op
|
|
||||||
BenchmarkGorillaMux_GPlusStatic 1000000 2629 ns/op 736 B/op 10 allocs/op
|
|
||||||
BenchmarkHttpRouter_GPlusStatic 30000000 52.5 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_GPlusStatic 20000000 85.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkKocha_GPlusStatic 20000000 89.2 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkLARS_GPlusStatic 10000000 162 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_GPlusStatic 500000 3479 ns/op 768 B/op 9 allocs/op
|
|
||||||
BenchmarkMartini_GPlusStatic 200000 9092 ns/op 768 B/op 9 allocs/op
|
|
||||||
BenchmarkPat_GPlusStatic 3000000 493 ns/op 96 B/op 2 allocs/op
|
|
||||||
BenchmarkPossum_GPlusStatic 1000000 1467 ns/op 416 B/op 3 allocs/op
|
|
||||||
BenchmarkR2router_GPlusStatic 2000000 788 ns/op 144 B/op 4 allocs/op
|
|
||||||
BenchmarkRivet_GPlusStatic 20000000 114 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTango_GPlusStatic 1000000 1534 ns/op 200 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_GPlusStatic 5000000 282 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkTraffic_GPlusStatic 500000 3798 ns/op 1192 B/op 15 allocs/op
|
|
||||||
BenchmarkVulcan_GPlusStatic 2000000 1125 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_GPlusParam 3000000 528 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_GPlusParam 1000000 1570 ns/op 480 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_GPlusParam 1000000 2369 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_GPlusParam 1000000 2028 ns/op 688 B/op 5 allocs/op
|
|
||||||
BenchmarkDenco_GPlusParam 5000000 385 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_GPlusParam 3000000 441 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGin_GPlusParam 10000000 174 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_GPlusParam 1000000 2033 ns/op 648 B/op 8 allocs/op
|
|
||||||
BenchmarkGoji_GPlusParam 1000000 1399 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_GPlusParam 1000000 2641 ns/op 944 B/op 8 allocs/op
|
|
||||||
BenchmarkGoJsonRest_GPlusParam 1000000 2824 ns/op 649 B/op 13 allocs/op
|
|
||||||
BenchmarkGoRestful_GPlusParam 200000 8875 ns/op 2296 B/op 21 allocs/op
|
|
||||||
BenchmarkGorillaMux_GPlusParam 200000 6291 ns/op 1056 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpRouter_GPlusParam 5000000 316 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_GPlusParam 1000000 1129 ns/op 352 B/op 3 allocs/op
|
|
||||||
BenchmarkKocha_GPlusParam 3000000 538 ns/op 56 B/op 3 allocs/op
|
|
||||||
BenchmarkLARS_GPlusParam 10000000 198 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_GPlusParam 500000 3554 ns/op 1056 B/op 10 allocs/op
|
|
||||||
BenchmarkMartini_GPlusParam 200000 9831 ns/op 1072 B/op 10 allocs/op
|
|
||||||
BenchmarkPat_GPlusParam 1000000 2706 ns/op 688 B/op 12 allocs/op
|
|
||||||
BenchmarkPossum_GPlusParam 1000000 2297 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_GPlusParam 1000000 1318 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_GPlusParam 5000000 399 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_GPlusParam 1000000 2070 ns/op 264 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_GPlusParam 500000 4853 ns/op 1056 B/op 17 allocs/op
|
|
||||||
BenchmarkTraffic_GPlusParam 200000 8278 ns/op 1976 B/op 21 allocs/op
|
|
||||||
BenchmarkVulcan_GPlusParam 1000000 1243 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_GPlus2Params 3000000 549 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_GPlus2Params 1000000 2112 ns/op 496 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_GPlus2Params 500000 2750 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_GPlus2Params 300000 7032 ns/op 1040 B/op 9 allocs/op
|
|
||||||
BenchmarkDenco_GPlus2Params 3000000 502 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_GPlus2Params 3000000 641 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGin_GPlus2Params 5000000 250 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_GPlus2Params 1000000 2681 ns/op 712 B/op 9 allocs/op
|
|
||||||
BenchmarkGoji_GPlus2Params 1000000 1926 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_GPlus2Params 500000 3996 ns/op 1024 B/op 11 allocs/op
|
|
||||||
BenchmarkGoJsonRest_GPlus2Params 500000 3886 ns/op 713 B/op 14 allocs/op
|
|
||||||
BenchmarkGoRestful_GPlus2Params 200000 10376 ns/op 2360 B/op 21 allocs/op
|
|
||||||
BenchmarkGorillaMux_GPlus2Params 100000 14162 ns/op 1088 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpRouter_GPlus2Params 5000000 336 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_GPlus2Params 1000000 1523 ns/op 384 B/op 4 allocs/op
|
|
||||||
BenchmarkKocha_GPlus2Params 2000000 970 ns/op 128 B/op 5 allocs/op
|
|
||||||
BenchmarkLARS_GPlus2Params 5000000 238 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_GPlus2Params 500000 4016 ns/op 1056 B/op 10 allocs/op
|
|
||||||
BenchmarkMartini_GPlus2Params 100000 21253 ns/op 1200 B/op 13 allocs/op
|
|
||||||
BenchmarkPat_GPlus2Params 200000 8632 ns/op 2256 B/op 34 allocs/op
|
|
||||||
BenchmarkPossum_GPlus2Params 1000000 2171 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_GPlus2Params 1000000 1340 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_GPlus2Params 3000000 557 ns/op 96 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_GPlus2Params 1000000 2186 ns/op 344 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_GPlus2Params 200000 9060 ns/op 1488 B/op 24 allocs/op
|
|
||||||
BenchmarkTraffic_GPlus2Params 100000 20324 ns/op 3272 B/op 31 allocs/op
|
|
||||||
BenchmarkVulcan_GPlus2Params 1000000 2039 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_GPlusAll 300000 6603 ns/op 640 B/op 11 allocs/op
|
|
||||||
BenchmarkBear_GPlusAll 100000 22363 ns/op 5488 B/op 61 allocs/op
|
|
||||||
BenchmarkBeego_GPlusAll 50000 38757 ns/op 4784 B/op 52 allocs/op
|
|
||||||
BenchmarkBone_GPlusAll 20000 54916 ns/op 10336 B/op 98 allocs/op
|
|
||||||
BenchmarkDenco_GPlusAll 300000 4959 ns/op 672 B/op 11 allocs/op
|
|
||||||
BenchmarkEcho_GPlusAll 200000 6558 ns/op 416 B/op 13 allocs/op
|
|
||||||
BenchmarkGin_GPlusAll 500000 2757 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_GPlusAll 50000 34615 ns/op 8040 B/op 103 allocs/op
|
|
||||||
BenchmarkGoji_GPlusAll 100000 16002 ns/op 3696 B/op 22 allocs/op
|
|
||||||
BenchmarkGojiv2_GPlusAll 50000 35060 ns/op 12624 B/op 115 allocs/op
|
|
||||||
BenchmarkGoJsonRest_GPlusAll 50000 41479 ns/op 8117 B/op 170 allocs/op
|
|
||||||
BenchmarkGoRestful_GPlusAll 10000 131653 ns/op 32024 B/op 275 allocs/op
|
|
||||||
BenchmarkGorillaMux_GPlusAll 10000 101380 ns/op 13296 B/op 142 allocs/op
|
|
||||||
BenchmarkHttpRouter_GPlusAll 500000 3711 ns/op 640 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_GPlusAll 100000 14438 ns/op 4032 B/op 38 allocs/op
|
|
||||||
BenchmarkKocha_GPlusAll 200000 8039 ns/op 976 B/op 43 allocs/op
|
|
||||||
BenchmarkLARS_GPlusAll 500000 2630 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_GPlusAll 30000 51123 ns/op 13152 B/op 128 allocs/op
|
|
||||||
BenchmarkMartini_GPlusAll 10000 176157 ns/op 14016 B/op 145 allocs/op
|
|
||||||
BenchmarkPat_GPlusAll 20000 69911 ns/op 16576 B/op 298 allocs/op
|
|
||||||
BenchmarkPossum_GPlusAll 100000 20716 ns/op 5408 B/op 39 allocs/op
|
|
||||||
BenchmarkR2router_GPlusAll 100000 17463 ns/op 5040 B/op 63 allocs/op
|
|
||||||
BenchmarkRivet_GPlusAll 300000 5142 ns/op 768 B/op 11 allocs/op
|
|
||||||
BenchmarkTango_GPlusAll 50000 27321 ns/op 3656 B/op 104 allocs/op
|
|
||||||
BenchmarkTigerTonic_GPlusAll 20000 77597 ns/op 14512 B/op 288 allocs/op
|
|
||||||
BenchmarkTraffic_GPlusAll 10000 151406 ns/op 37360 B/op 392 allocs/op
|
|
||||||
BenchmarkVulcan_GPlusAll 100000 18555 ns/op 1274 B/op 39 allocs/op
|
|
||||||
```
|
|
||||||
|
|
||||||
## Parse.com
|
|
||||||
|
|
||||||
```
|
|
||||||
BenchmarkGin_ParseStatic 10000000 133 ns/op 0 B/op 0 allocs/op
|
|
||||||
|
|
||||||
BenchmarkAce_ParseStatic 5000000 241 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBear_ParseStatic 2000000 728 ns/op 120 B/op 3 allocs/op
|
|
||||||
BenchmarkBeego_ParseStatic 1000000 2623 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_ParseStatic 1000000 1285 ns/op 144 B/op 3 allocs/op
|
|
||||||
BenchmarkDenco_ParseStatic 30000000 57.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkEcho_ParseStatic 5000000 342 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGocraftWeb_ParseStatic 1000000 1478 ns/op 296 B/op 5 allocs/op
|
|
||||||
BenchmarkGoji_ParseStatic 3000000 415 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGojiv2_ParseStatic 1000000 2087 ns/op 928 B/op 7 allocs/op
|
|
||||||
BenchmarkGoJsonRest_ParseStatic 1000000 1712 ns/op 329 B/op 11 allocs/op
|
|
||||||
BenchmarkGoRestful_ParseStatic 200000 11072 ns/op 3224 B/op 22 allocs/op
|
|
||||||
BenchmarkGorillaMux_ParseStatic 500000 4129 ns/op 752 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpRouter_ParseStatic 30000000 52.4 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_ParseStatic 20000000 109 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkKocha_ParseStatic 20000000 81.8 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkLARS_ParseStatic 10000000 150 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_ParseStatic 1000000 3288 ns/op 768 B/op 9 allocs/op
|
|
||||||
BenchmarkMartini_ParseStatic 200000 9110 ns/op 768 B/op 9 allocs/op
|
|
||||||
BenchmarkPat_ParseStatic 1000000 1135 ns/op 240 B/op 5 allocs/op
|
|
||||||
BenchmarkPossum_ParseStatic 1000000 1557 ns/op 416 B/op 3 allocs/op
|
|
||||||
BenchmarkR2router_ParseStatic 2000000 730 ns/op 144 B/op 4 allocs/op
|
|
||||||
BenchmarkRivet_ParseStatic 10000000 121 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTango_ParseStatic 1000000 1688 ns/op 248 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_ParseStatic 3000000 427 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkTraffic_ParseStatic 500000 5962 ns/op 1816 B/op 20 allocs/op
|
|
||||||
BenchmarkVulcan_ParseStatic 2000000 969 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_ParseParam 3000000 497 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_ParseParam 1000000 1473 ns/op 467 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_ParseParam 1000000 2384 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_ParseParam 1000000 2513 ns/op 768 B/op 6 allocs/op
|
|
||||||
BenchmarkDenco_ParseParam 5000000 364 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_ParseParam 5000000 418 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGin_ParseParam 10000000 163 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_ParseParam 1000000 2361 ns/op 664 B/op 8 allocs/op
|
|
||||||
BenchmarkGoji_ParseParam 1000000 1590 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_ParseParam 1000000 2851 ns/op 976 B/op 9 allocs/op
|
|
||||||
BenchmarkGoJsonRest_ParseParam 1000000 2965 ns/op 649 B/op 13 allocs/op
|
|
||||||
BenchmarkGoRestful_ParseParam 200000 12207 ns/op 3544 B/op 23 allocs/op
|
|
||||||
BenchmarkGorillaMux_ParseParam 500000 5187 ns/op 1088 B/op 12 allocs/op
|
|
||||||
BenchmarkHttpRouter_ParseParam 5000000 275 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_ParseParam 1000000 1108 ns/op 352 B/op 3 allocs/op
|
|
||||||
BenchmarkKocha_ParseParam 3000000 495 ns/op 56 B/op 3 allocs/op
|
|
||||||
BenchmarkLARS_ParseParam 10000000 192 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_ParseParam 500000 4103 ns/op 1056 B/op 10 allocs/op
|
|
||||||
BenchmarkMartini_ParseParam 200000 9878 ns/op 1072 B/op 10 allocs/op
|
|
||||||
BenchmarkPat_ParseParam 500000 3657 ns/op 1120 B/op 17 allocs/op
|
|
||||||
BenchmarkPossum_ParseParam 1000000 2084 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_ParseParam 1000000 1251 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_ParseParam 5000000 335 ns/op 48 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_ParseParam 1000000 1854 ns/op 280 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_ParseParam 500000 4582 ns/op 1008 B/op 17 allocs/op
|
|
||||||
BenchmarkTraffic_ParseParam 200000 8125 ns/op 2248 B/op 23 allocs/op
|
|
||||||
BenchmarkVulcan_ParseParam 1000000 1148 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_Parse2Params 3000000 539 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkBear_Parse2Params 1000000 1778 ns/op 496 B/op 5 allocs/op
|
|
||||||
BenchmarkBeego_Parse2Params 1000000 2519 ns/op 368 B/op 4 allocs/op
|
|
||||||
BenchmarkBone_Parse2Params 1000000 2596 ns/op 720 B/op 5 allocs/op
|
|
||||||
BenchmarkDenco_Parse2Params 3000000 492 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkEcho_Parse2Params 3000000 484 ns/op 32 B/op 1 allocs/op
|
|
||||||
BenchmarkGin_Parse2Params 10000000 193 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_Parse2Params 1000000 2575 ns/op 712 B/op 9 allocs/op
|
|
||||||
BenchmarkGoji_Parse2Params 1000000 1373 ns/op 336 B/op 2 allocs/op
|
|
||||||
BenchmarkGojiv2_Parse2Params 500000 2416 ns/op 960 B/op 8 allocs/op
|
|
||||||
BenchmarkGoJsonRest_Parse2Params 300000 3452 ns/op 713 B/op 14 allocs/op
|
|
||||||
BenchmarkGoRestful_Parse2Params 100000 17719 ns/op 6008 B/op 25 allocs/op
|
|
||||||
BenchmarkGorillaMux_Parse2Params 300000 5102 ns/op 1088 B/op 11 allocs/op
|
|
||||||
BenchmarkHttpRouter_Parse2Params 5000000 303 ns/op 64 B/op 1 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_Parse2Params 1000000 1372 ns/op 384 B/op 4 allocs/op
|
|
||||||
BenchmarkKocha_Parse2Params 2000000 874 ns/op 128 B/op 5 allocs/op
|
|
||||||
BenchmarkLARS_Parse2Params 10000000 192 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_Parse2Params 500000 3871 ns/op 1056 B/op 10 allocs/op
|
|
||||||
BenchmarkMartini_Parse2Params 200000 9954 ns/op 1152 B/op 11 allocs/op
|
|
||||||
BenchmarkPat_Parse2Params 500000 4194 ns/op 832 B/op 17 allocs/op
|
|
||||||
BenchmarkPossum_Parse2Params 1000000 2121 ns/op 560 B/op 6 allocs/op
|
|
||||||
BenchmarkR2router_Parse2Params 1000000 1415 ns/op 432 B/op 5 allocs/op
|
|
||||||
BenchmarkRivet_Parse2Params 3000000 457 ns/op 96 B/op 1 allocs/op
|
|
||||||
BenchmarkTango_Parse2Params 1000000 1914 ns/op 312 B/op 8 allocs/op
|
|
||||||
BenchmarkTigerTonic_Parse2Params 300000 6895 ns/op 1408 B/op 24 allocs/op
|
|
||||||
BenchmarkTraffic_Parse2Params 200000 8317 ns/op 2040 B/op 22 allocs/op
|
|
||||||
BenchmarkVulcan_Parse2Params 1000000 1274 ns/op 98 B/op 3 allocs/op
|
|
||||||
BenchmarkAce_ParseAll 200000 10401 ns/op 640 B/op 16 allocs/op
|
|
||||||
BenchmarkBear_ParseAll 50000 37743 ns/op 8928 B/op 110 allocs/op
|
|
||||||
BenchmarkBeego_ParseAll 20000 63193 ns/op 9568 B/op 104 allocs/op
|
|
||||||
BenchmarkBone_ParseAll 20000 61767 ns/op 14160 B/op 131 allocs/op
|
|
||||||
BenchmarkDenco_ParseAll 300000 7036 ns/op 928 B/op 16 allocs/op
|
|
||||||
BenchmarkEcho_ParseAll 200000 11824 ns/op 832 B/op 26 allocs/op
|
|
||||||
BenchmarkGin_ParseAll 300000 4199 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGocraftWeb_ParseAll 30000 51758 ns/op 13728 B/op 181 allocs/op
|
|
||||||
BenchmarkGoji_ParseAll 50000 29614 ns/op 5376 B/op 32 allocs/op
|
|
||||||
BenchmarkGojiv2_ParseAll 20000 68676 ns/op 24464 B/op 199 allocs/op
|
|
||||||
BenchmarkGoJsonRest_ParseAll 20000 76135 ns/op 13866 B/op 321 allocs/op
|
|
||||||
BenchmarkGoRestful_ParseAll 5000 389487 ns/op 110928 B/op 600 allocs/op
|
|
||||||
BenchmarkGorillaMux_ParseAll 10000 221250 ns/op 24864 B/op 292 allocs/op
|
|
||||||
BenchmarkHttpRouter_ParseAll 200000 6444 ns/op 640 B/op 16 allocs/op
|
|
||||||
BenchmarkHttpTreeMux_ParseAll 50000 30702 ns/op 5728 B/op 51 allocs/op
|
|
||||||
BenchmarkKocha_ParseAll 200000 13712 ns/op 1112 B/op 54 allocs/op
|
|
||||||
BenchmarkLARS_ParseAll 300000 6925 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMacaron_ParseAll 20000 96278 ns/op 24576 B/op 250 allocs/op
|
|
||||||
BenchmarkMartini_ParseAll 5000 271352 ns/op 25072 B/op 253 allocs/op
|
|
||||||
BenchmarkPat_ParseAll 20000 74941 ns/op 17264 B/op 343 allocs/op
|
|
||||||
BenchmarkPossum_ParseAll 50000 39947 ns/op 10816 B/op 78 allocs/op
|
|
||||||
BenchmarkR2router_ParseAll 50000 42479 ns/op 8352 B/op 120 allocs/op
|
|
||||||
BenchmarkRivet_ParseAll 200000 7726 ns/op 912 B/op 16 allocs/op
|
|
||||||
BenchmarkTango_ParseAll 30000 50014 ns/op 7168 B/op 208 allocs/op
|
|
||||||
BenchmarkTigerTonic_ParseAll 10000 106550 ns/op 19728 B/op 379 allocs/op
|
|
||||||
BenchmarkTraffic_ParseAll 10000 216037 ns/op 57776 B/op 642 allocs/op
|
|
||||||
BenchmarkVulcan_ParseAll 50000 34379 ns/op 2548 B/op 78 allocs/op
|
|
||||||
```
|
|
213
vendor/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
213
vendor/github.com/gin-gonic/gin/CHANGELOG.md
generated
vendored
@ -1,213 +0,0 @@
|
|||||||
# CHANGELOG
|
|
||||||
|
|
||||||
### Gin 1.3.0
|
|
||||||
|
|
||||||
- [NEW] Add [`func (*Context) QueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383)
|
|
||||||
- [NEW] Add [`func (*Context) AsciiJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.AsciiJSON), see [#1358](https://github.com/gin-gonic/gin/pull/1358)
|
|
||||||
- [NEW] Add `Pusher()` in [`type ResponseWriter`](https://godoc.org/github.com/gin-gonic/gin#ResponseWriter) for supporting http2 push, see [#1273](https://github.com/gin-gonic/gin/pull/1273)
|
|
||||||
- [NEW] Add [`func (*Context) DataFromReader`](https://godoc.org/github.com/gin-gonic/gin#Context.DataFromReader) for serving dynamic data, see [#1304](https://github.com/gin-gonic/gin/pull/1304)
|
|
||||||
- [NEW] Add [`func (*Context) ShouldBindBodyWith`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindBodyWith) allowing to call binding multiple times, see [#1341](https://github.com/gin-gonic/gin/pull/1341)
|
|
||||||
- [NEW] Support pointers in form binding, see [#1336](https://github.com/gin-gonic/gin/pull/1336)
|
|
||||||
- [NEW] Add [`func (*Context) JSONP`](https://godoc.org/github.com/gin-gonic/gin#Context.JSONP), see [#1333](https://github.com/gin-gonic/gin/pull/1333)
|
|
||||||
- [NEW] Support default value in form binding, see [#1138](https://github.com/gin-gonic/gin/pull/1138)
|
|
||||||
- [NEW] Expose validator engine in [`type StructValidator`](https://godoc.org/github.com/gin-gonic/gin/binding#StructValidator), see [#1277](https://github.com/gin-gonic/gin/pull/1277)
|
|
||||||
- [NEW] Add [`func (*Context) ShouldBind`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBind), [`func (*Context) ShouldBindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindQuery) and [`func (*Context) ShouldBindJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindJSON), see [#1047](https://github.com/gin-gonic/gin/pull/1047)
|
|
||||||
- [NEW] Add support for `time.Time` location in form binding, see [#1117](https://github.com/gin-gonic/gin/pull/1117)
|
|
||||||
- [NEW] Add [`func (*Context) BindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.BindQuery), see [#1029](https://github.com/gin-gonic/gin/pull/1029)
|
|
||||||
- [NEW] Make [jsonite](https://github.com/json-iterator/go) optional with build tags, see [#1026](https://github.com/gin-gonic/gin/pull/1026)
|
|
||||||
- [NEW] Show query string in logger, see [#999](https://github.com/gin-gonic/gin/pull/999)
|
|
||||||
- [NEW] Add [`func (*Context) SecureJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.SecureJSON), see [#987](https://github.com/gin-gonic/gin/pull/987) and [#993](https://github.com/gin-gonic/gin/pull/993)
|
|
||||||
- [DEPRECATE] `func (*Context) GetCookie` for [`func (*Context) Cookie`](https://godoc.org/github.com/gin-gonic/gin#Context.Cookie)
|
|
||||||
- [FIX] Don't display color tags if [`func DisableConsoleColor`](https://godoc.org/github.com/gin-gonic/gin#DisableConsoleColor) called, see [#1072](https://github.com/gin-gonic/gin/pull/1072)
|
|
||||||
- [FIX] Gin Mode `""` when calling [`func Mode`](https://godoc.org/github.com/gin-gonic/gin#Mode) now returns `const DebugMode`, see [#1250](https://github.com/gin-gonic/gin/pull/1250)
|
|
||||||
- [FIX] `Flush()` now doesn't overwrite `responseWriter` status code, see [#1460](https://github.com/gin-gonic/gin/pull/1460)
|
|
||||||
|
|
||||||
### Gin 1.2.0
|
|
||||||
|
|
||||||
- [NEW] Switch from godeps to govendor
|
|
||||||
- [NEW] Add support for Let's Encrypt via gin-gonic/autotls
|
|
||||||
- [NEW] Improve README examples and add extra at examples folder
|
|
||||||
- [NEW] Improved support with App Engine
|
|
||||||
- [NEW] Add custom template delimiters, see #860
|
|
||||||
- [NEW] Add Template Func Maps, see #962
|
|
||||||
- [NEW] Add \*context.Handler(), see #928
|
|
||||||
- [NEW] Add \*context.GetRawData()
|
|
||||||
- [NEW] Add \*context.GetHeader() (request)
|
|
||||||
- [NEW] Add \*context.AbortWithStatusJSON() (JSON content type)
|
|
||||||
- [NEW] Add \*context.Keys type cast helpers
|
|
||||||
- [NEW] Add \*context.ShouldBindWith()
|
|
||||||
- [NEW] Add \*context.MustBindWith()
|
|
||||||
- [NEW] Add \*engine.SetFuncMap()
|
|
||||||
- [DEPRECATE] On next release: \*context.BindWith(), see #855
|
|
||||||
- [FIX] Refactor render
|
|
||||||
- [FIX] Reworked tests
|
|
||||||
- [FIX] logger now supports cygwin
|
|
||||||
- [FIX] Use X-Forwarded-For before X-Real-Ip
|
|
||||||
- [FIX] time.Time binding (#904)
|
|
||||||
|
|
||||||
### Gin 1.1.4
|
|
||||||
|
|
||||||
- [NEW] Support google appengine for IsTerminal func
|
|
||||||
|
|
||||||
### Gin 1.1.3
|
|
||||||
|
|
||||||
- [FIX] Reverted Logger: skip ANSI color commands
|
|
||||||
|
|
||||||
### Gin 1.1
|
|
||||||
|
|
||||||
- [NEW] Implement QueryArray and PostArray methods
|
|
||||||
- [NEW] Refactor GetQuery and GetPostForm
|
|
||||||
- [NEW] Add contribution guide
|
|
||||||
- [FIX] Corrected typos in README
|
|
||||||
- [FIX] Removed additional Iota
|
|
||||||
- [FIX] Changed imports to gopkg instead of github in README (#733)
|
|
||||||
- [FIX] Logger: skip ANSI color commands if output is not a tty
|
|
||||||
|
|
||||||
### Gin 1.0rc2 (...)
|
|
||||||
|
|
||||||
- [PERFORMANCE] Fast path for writing Content-Type.
|
|
||||||
- [PERFORMANCE] Much faster 404 routing
|
|
||||||
- [PERFORMANCE] Allocation optimizations
|
|
||||||
- [PERFORMANCE] Faster root tree lookup
|
|
||||||
- [PERFORMANCE] Zero overhead, String() and JSON() rendering.
|
|
||||||
- [PERFORMANCE] Faster ClientIP parsing
|
|
||||||
- [PERFORMANCE] Much faster SSE implementation
|
|
||||||
- [NEW] Benchmarks suite
|
|
||||||
- [NEW] Bind validation can be disabled and replaced with custom validators.
|
|
||||||
- [NEW] More flexible HTML render
|
|
||||||
- [NEW] Multipart and PostForm bindings
|
|
||||||
- [NEW] Adds method to return all the registered routes
|
|
||||||
- [NEW] Context.HandlerName() returns the main handler's name
|
|
||||||
- [NEW] Adds Error.IsType() helper
|
|
||||||
- [FIX] Binding multipart form
|
|
||||||
- [FIX] Integration tests
|
|
||||||
- [FIX] Crash when binding non struct object in Context.
|
|
||||||
- [FIX] RunTLS() implementation
|
|
||||||
- [FIX] Logger() unit tests
|
|
||||||
- [FIX] Adds SetHTMLTemplate() warning
|
|
||||||
- [FIX] Context.IsAborted()
|
|
||||||
- [FIX] More unit tests
|
|
||||||
- [FIX] JSON, XML, HTML renders accept custom content-types
|
|
||||||
- [FIX] gin.AbortIndex is unexported
|
|
||||||
- [FIX] Better approach to avoid directory listing in StaticFS()
|
|
||||||
- [FIX] Context.ClientIP() always returns the IP with trimmed spaces.
|
|
||||||
- [FIX] Better warning when running in debug mode.
|
|
||||||
- [FIX] Google App Engine integration. debugPrint does not use os.Stdout
|
|
||||||
- [FIX] Fixes integer overflow in error type
|
|
||||||
- [FIX] Error implements the json.Marshaller interface
|
|
||||||
- [FIX] MIT license in every file
|
|
||||||
|
|
||||||
|
|
||||||
### Gin 1.0rc1 (May 22, 2015)
|
|
||||||
|
|
||||||
- [PERFORMANCE] Zero allocation router
|
|
||||||
- [PERFORMANCE] Faster JSON, XML and text rendering
|
|
||||||
- [PERFORMANCE] Custom hand optimized HttpRouter for Gin
|
|
||||||
- [PERFORMANCE] Misc code optimizations. Inlining, tail call optimizations
|
|
||||||
- [NEW] Built-in support for golang.org/x/net/context
|
|
||||||
- [NEW] Any(path, handler). Create a route that matches any path
|
|
||||||
- [NEW] Refactored rendering pipeline (faster and static typeded)
|
|
||||||
- [NEW] Refactored errors API
|
|
||||||
- [NEW] IndentedJSON() prints pretty JSON
|
|
||||||
- [NEW] Added gin.DefaultWriter
|
|
||||||
- [NEW] UNIX socket support
|
|
||||||
- [NEW] RouterGroup.BasePath is exposed
|
|
||||||
- [NEW] JSON validation using go-validate-yourself (very powerful options)
|
|
||||||
- [NEW] Completed suite of unit tests
|
|
||||||
- [NEW] HTTP streaming with c.Stream()
|
|
||||||
- [NEW] StaticFile() creates a router for serving just one file.
|
|
||||||
- [NEW] StaticFS() has an option to disable directory listing.
|
|
||||||
- [NEW] StaticFS() for serving static files through virtual filesystems
|
|
||||||
- [NEW] Server-Sent Events native support
|
|
||||||
- [NEW] WrapF() and WrapH() helpers for wrapping http.HandlerFunc and http.Handler
|
|
||||||
- [NEW] Added LoggerWithWriter() middleware
|
|
||||||
- [NEW] Added RecoveryWithWriter() middleware
|
|
||||||
- [NEW] Added DefaultPostFormValue()
|
|
||||||
- [NEW] Added DefaultFormValue()
|
|
||||||
- [NEW] Added DefaultParamValue()
|
|
||||||
- [FIX] BasicAuth() when using custom realm
|
|
||||||
- [FIX] Bug when serving static files in nested routing group
|
|
||||||
- [FIX] Redirect using built-in http.Redirect()
|
|
||||||
- [FIX] Logger when printing the requested path
|
|
||||||
- [FIX] Documentation typos
|
|
||||||
- [FIX] Context.Engine renamed to Context.engine
|
|
||||||
- [FIX] Better debugging messages
|
|
||||||
- [FIX] ErrorLogger
|
|
||||||
- [FIX] Debug HTTP render
|
|
||||||
- [FIX] Refactored binding and render modules
|
|
||||||
- [FIX] Refactored Context initialization
|
|
||||||
- [FIX] Refactored BasicAuth()
|
|
||||||
- [FIX] NoMethod/NoRoute handlers
|
|
||||||
- [FIX] Hijacking http
|
|
||||||
- [FIX] Better support for Google App Engine (using log instead of fmt)
|
|
||||||
|
|
||||||
|
|
||||||
### Gin 0.6 (Mar 9, 2015)
|
|
||||||
|
|
||||||
- [NEW] Support multipart/form-data
|
|
||||||
- [NEW] NoMethod handler
|
|
||||||
- [NEW] Validate sub structures
|
|
||||||
- [NEW] Support for HTTP Realm Auth
|
|
||||||
- [FIX] Unsigned integers in binding
|
|
||||||
- [FIX] Improve color logger
|
|
||||||
|
|
||||||
|
|
||||||
### Gin 0.5 (Feb 7, 2015)
|
|
||||||
|
|
||||||
- [NEW] Content Negotiation
|
|
||||||
- [FIX] Solved security bug that allow a client to spoof ip
|
|
||||||
- [FIX] Fix unexported/ignored fields in binding
|
|
||||||
|
|
||||||
|
|
||||||
### Gin 0.4 (Aug 21, 2014)
|
|
||||||
|
|
||||||
- [NEW] Development mode
|
|
||||||
- [NEW] Unit tests
|
|
||||||
- [NEW] Add Content.Redirect()
|
|
||||||
- [FIX] Deferring WriteHeader()
|
|
||||||
- [FIX] Improved documentation for model binding
|
|
||||||
|
|
||||||
|
|
||||||
### Gin 0.3 (Jul 18, 2014)
|
|
||||||
|
|
||||||
- [PERFORMANCE] Normal log and error log are printed in the same call.
|
|
||||||
- [PERFORMANCE] Improve performance of NoRouter()
|
|
||||||
- [PERFORMANCE] Improve context's memory locality, reduce CPU cache faults.
|
|
||||||
- [NEW] Flexible rendering API
|
|
||||||
- [NEW] Add Context.File()
|
|
||||||
- [NEW] Add shorcut RunTLS() for http.ListenAndServeTLS
|
|
||||||
- [FIX] Rename NotFound404() to NoRoute()
|
|
||||||
- [FIX] Errors in context are purged
|
|
||||||
- [FIX] Adds HEAD method in Static file serving
|
|
||||||
- [FIX] Refactors Static() file serving
|
|
||||||
- [FIX] Using keyed initialization to fix app-engine integration
|
|
||||||
- [FIX] Can't unmarshal JSON array, #63
|
|
||||||
- [FIX] Renaming Context.Req to Context.Request
|
|
||||||
- [FIX] Check application/x-www-form-urlencoded when parsing form
|
|
||||||
|
|
||||||
|
|
||||||
### Gin 0.2b (Jul 08, 2014)
|
|
||||||
- [PERFORMANCE] Using sync.Pool to allocatio/gc overhead
|
|
||||||
- [NEW] Travis CI integration
|
|
||||||
- [NEW] Completely new logger
|
|
||||||
- [NEW] New API for serving static files. gin.Static()
|
|
||||||
- [NEW] gin.H() can be serialized into XML
|
|
||||||
- [NEW] Typed errors. Errors can be typed. Internet/external/custom.
|
|
||||||
- [NEW] Support for Godeps
|
|
||||||
- [NEW] Travis/Godocs badges in README
|
|
||||||
- [NEW] New Bind() and BindWith() methods for parsing request body.
|
|
||||||
- [NEW] Add Content.Copy()
|
|
||||||
- [NEW] Add context.LastError()
|
|
||||||
- [NEW] Add shorcut for OPTIONS HTTP method
|
|
||||||
- [FIX] Tons of README fixes
|
|
||||||
- [FIX] Header is written before body
|
|
||||||
- [FIX] BasicAuth() and changes API a little bit
|
|
||||||
- [FIX] Recovery() middleware only prints panics
|
|
||||||
- [FIX] Context.Get() does not panic anymore. Use MustGet() instead.
|
|
||||||
- [FIX] Multiple http.WriteHeader() in NotFound handlers
|
|
||||||
- [FIX] Engine.Run() panics if http server can't be setted up
|
|
||||||
- [FIX] Crash when route path doesn't start with '/'
|
|
||||||
- [FIX] Do not update header when status code is negative
|
|
||||||
- [FIX] Setting response headers before calling WriteHeader in context.String()
|
|
||||||
- [FIX] Add MIT license
|
|
||||||
- [FIX] Changes behaviour of ErrorLogger() and Logger()
|
|
46
vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md
generated
vendored
46
vendor/github.com/gin-gonic/gin/CODE_OF_CONDUCT.md
generated
vendored
@ -1,46 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at teamgingonic@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
13
vendor/github.com/gin-gonic/gin/CONTRIBUTING.md
generated
vendored
13
vendor/github.com/gin-gonic/gin/CONTRIBUTING.md
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
## Contributing
|
|
||||||
|
|
||||||
- With issues:
|
|
||||||
- Use the search tool before opening a new issue.
|
|
||||||
- Please provide source code and commit sha if you found a bug.
|
|
||||||
- Review existing issues and provide feedback or react to them.
|
|
||||||
|
|
||||||
- With pull requests:
|
|
||||||
- Open your pull request against `master`
|
|
||||||
- Your pull request should have no more than two commits, if not you should squash them.
|
|
||||||
- It should pass all tests in the available continuous integrations systems such as TravisCI.
|
|
||||||
- You should add/modify tests to cover your proposed code changes.
|
|
||||||
- If your pull request contains a new feature, please document it on the README.
|
|
21
vendor/github.com/gin-gonic/gin/LICENSE
generated
vendored
21
vendor/github.com/gin-gonic/gin/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Manuel Martínez-Almeida
|
|
||||||
|
|
||||||
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.
|
|
62
vendor/github.com/gin-gonic/gin/Makefile
generated
vendored
62
vendor/github.com/gin-gonic/gin/Makefile
generated
vendored
@ -1,62 +0,0 @@
|
|||||||
GOFMT ?= gofmt "-s"
|
|
||||||
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
|
||||||
VETPACKAGES ?= $(shell go list ./... | grep -v /vendor/ | grep -v /examples/)
|
|
||||||
GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*")
|
|
||||||
|
|
||||||
all: install
|
|
||||||
|
|
||||||
install: deps
|
|
||||||
govendor sync
|
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
test:
|
|
||||||
sh coverage.sh
|
|
||||||
|
|
||||||
.PHONY: fmt
|
|
||||||
fmt:
|
|
||||||
$(GOFMT) -w $(GOFILES)
|
|
||||||
|
|
||||||
.PHONY: fmt-check
|
|
||||||
fmt-check:
|
|
||||||
# get all go files and run go fmt on them
|
|
||||||
@diff=$$($(GOFMT) -d $(GOFILES)); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
|
||||||
echo "Please run 'make fmt' and commit the result:"; \
|
|
||||||
echo "$${diff}"; \
|
|
||||||
exit 1; \
|
|
||||||
fi;
|
|
||||||
|
|
||||||
vet:
|
|
||||||
go vet $(VETPACKAGES)
|
|
||||||
|
|
||||||
deps:
|
|
||||||
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
go get -u github.com/kardianos/govendor; \
|
|
||||||
fi
|
|
||||||
@hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
go get -u github.com/campoy/embedmd; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
embedmd:
|
|
||||||
embedmd -d *.md
|
|
||||||
|
|
||||||
.PHONY: lint
|
|
||||||
lint:
|
|
||||||
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
go get -u github.com/golang/lint/golint; \
|
|
||||||
fi
|
|
||||||
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
|
||||||
|
|
||||||
.PHONY: misspell-check
|
|
||||||
misspell-check:
|
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
go get -u github.com/client9/misspell/cmd/misspell; \
|
|
||||||
fi
|
|
||||||
misspell -error $(GOFILES)
|
|
||||||
|
|
||||||
.PHONY: misspell
|
|
||||||
misspell:
|
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
go get -u github.com/client9/misspell/cmd/misspell; \
|
|
||||||
fi
|
|
||||||
misspell -w $(GOFILES)
|
|
1820
vendor/github.com/gin-gonic/gin/README.md
generated
vendored
1820
vendor/github.com/gin-gonic/gin/README.md
generated
vendored
File diff suppressed because it is too large
Load Diff
96
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
96
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
@ -1,96 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/subtle"
|
|
||||||
"encoding/base64"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AuthUserKey is the cookie name for user credential in basic auth.
|
|
||||||
const AuthUserKey = "user"
|
|
||||||
|
|
||||||
// Accounts defines a key/value for user/pass list of authorized logins.
|
|
||||||
type Accounts map[string]string
|
|
||||||
|
|
||||||
type authPair struct {
|
|
||||||
value string
|
|
||||||
user string
|
|
||||||
}
|
|
||||||
|
|
||||||
type authPairs []authPair
|
|
||||||
|
|
||||||
func (a authPairs) searchCredential(authValue string) (string, bool) {
|
|
||||||
if authValue == "" {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
for _, pair := range a {
|
|
||||||
if pair.value == authValue {
|
|
||||||
return pair.user, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where
|
|
||||||
// the key is the user name and the value is the password, as well as the name of the Realm.
|
|
||||||
// If the realm is empty, "Authorization Required" will be used by default.
|
|
||||||
// (see http://tools.ietf.org/html/rfc2617#section-1.2)
|
|
||||||
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
|
|
||||||
if realm == "" {
|
|
||||||
realm = "Authorization Required"
|
|
||||||
}
|
|
||||||
realm = "Basic realm=" + strconv.Quote(realm)
|
|
||||||
pairs := processAccounts(accounts)
|
|
||||||
return func(c *Context) {
|
|
||||||
// Search user in the slice of allowed credentials
|
|
||||||
user, found := pairs.searchCredential(c.requestHeader("Authorization"))
|
|
||||||
if !found {
|
|
||||||
// Credentials doesn't match, we return 401 and abort handlers chain.
|
|
||||||
c.Header("WWW-Authenticate", realm)
|
|
||||||
c.AbortWithStatus(http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
|
|
||||||
// c.MustGet(gin.AuthUserKey).
|
|
||||||
c.Set(AuthUserKey, user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where
|
|
||||||
// the key is the user name and the value is the password.
|
|
||||||
func BasicAuth(accounts Accounts) HandlerFunc {
|
|
||||||
return BasicAuthForRealm(accounts, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func processAccounts(accounts Accounts) authPairs {
|
|
||||||
assert1(len(accounts) > 0, "Empty list of authorized credentials")
|
|
||||||
pairs := make(authPairs, 0, len(accounts))
|
|
||||||
for user, password := range accounts {
|
|
||||||
assert1(user != "", "User can not be empty")
|
|
||||||
value := authorizationHeader(user, password)
|
|
||||||
pairs = append(pairs, authPair{
|
|
||||||
value: value,
|
|
||||||
user: user,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return pairs
|
|
||||||
}
|
|
||||||
|
|
||||||
func authorizationHeader(user, password string) string {
|
|
||||||
base := user + ":" + password
|
|
||||||
return "Basic " + base64.StdEncoding.EncodeToString([]byte(base))
|
|
||||||
}
|
|
||||||
|
|
||||||
func secureCompare(given, actual string) bool {
|
|
||||||
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
|
||||||
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
|
||||||
}
|
|
||||||
// Securely compare actual to itself to keep constant time, but always return false.
|
|
||||||
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
|
||||||
}
|
|
99
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
99
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
@ -1,99 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
// Content-Type MIME of the most common data formats.
|
|
||||||
const (
|
|
||||||
MIMEJSON = "application/json"
|
|
||||||
MIMEHTML = "text/html"
|
|
||||||
MIMEXML = "application/xml"
|
|
||||||
MIMEXML2 = "text/xml"
|
|
||||||
MIMEPlain = "text/plain"
|
|
||||||
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
|
||||||
MIMEMultipartPOSTForm = "multipart/form-data"
|
|
||||||
MIMEPROTOBUF = "application/x-protobuf"
|
|
||||||
MIMEMSGPACK = "application/x-msgpack"
|
|
||||||
MIMEMSGPACK2 = "application/msgpack"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Binding describes the interface which needs to be implemented for binding the
|
|
||||||
// data present in the request such as JSON request body, query parameters or
|
|
||||||
// the form POST.
|
|
||||||
type Binding interface {
|
|
||||||
Name() string
|
|
||||||
Bind(*http.Request, interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
|
|
||||||
// but it reads the body from supplied bytes instead of req.Body.
|
|
||||||
type BindingBody interface {
|
|
||||||
Binding
|
|
||||||
BindBody([]byte, interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructValidator is the minimal interface which needs to be implemented in
|
|
||||||
// order for it to be used as the validator engine for ensuring the correctness
|
|
||||||
// of the reqest. Gin provides a default implementation for this using
|
|
||||||
// https://github.com/go-playground/validator/tree/v8.18.2.
|
|
||||||
type StructValidator interface {
|
|
||||||
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
|
|
||||||
// If the received type is not a struct, any validation should be skipped and nil must be returned.
|
|
||||||
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
|
||||||
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
|
||||||
// Otherwise nil must be returned.
|
|
||||||
ValidateStruct(interface{}) error
|
|
||||||
|
|
||||||
// Engine returns the underlying validator engine which powers the
|
|
||||||
// StructValidator implementation.
|
|
||||||
Engine() interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validator is the default validator which implements the StructValidator
|
|
||||||
// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
|
|
||||||
// under the hood.
|
|
||||||
var Validator StructValidator = &defaultValidator{}
|
|
||||||
|
|
||||||
// These implement the Binding interface and can be used to bind the data
|
|
||||||
// present in the request to struct instances.
|
|
||||||
var (
|
|
||||||
JSON = jsonBinding{}
|
|
||||||
XML = xmlBinding{}
|
|
||||||
Form = formBinding{}
|
|
||||||
Query = queryBinding{}
|
|
||||||
FormPost = formPostBinding{}
|
|
||||||
FormMultipart = formMultipartBinding{}
|
|
||||||
ProtoBuf = protobufBinding{}
|
|
||||||
MsgPack = msgpackBinding{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default returns the appropriate Binding instance based on the HTTP method
|
|
||||||
// and the content type.
|
|
||||||
func Default(method, contentType string) Binding {
|
|
||||||
if method == "GET" {
|
|
||||||
return Form
|
|
||||||
}
|
|
||||||
|
|
||||||
switch contentType {
|
|
||||||
case MIMEJSON:
|
|
||||||
return JSON
|
|
||||||
case MIMEXML, MIMEXML2:
|
|
||||||
return XML
|
|
||||||
case MIMEPROTOBUF:
|
|
||||||
return ProtoBuf
|
|
||||||
case MIMEMSGPACK, MIMEMSGPACK2:
|
|
||||||
return MsgPack
|
|
||||||
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
|
||||||
return Form
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validate(obj interface{}) error {
|
|
||||||
if Validator == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return Validator.ValidateStruct(obj)
|
|
||||||
}
|
|
51
vendor/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
51
vendor/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
@ -1,51 +0,0 @@
|
|||||||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"gopkg.in/go-playground/validator.v8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type defaultValidator struct {
|
|
||||||
once sync.Once
|
|
||||||
validate *validator.Validate
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ StructValidator = &defaultValidator{}
|
|
||||||
|
|
||||||
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
|
||||||
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
|
||||||
value := reflect.ValueOf(obj)
|
|
||||||
valueType := value.Kind()
|
|
||||||
if valueType == reflect.Ptr {
|
|
||||||
valueType = value.Elem().Kind()
|
|
||||||
}
|
|
||||||
if valueType == reflect.Struct {
|
|
||||||
v.lazyinit()
|
|
||||||
if err := v.validate.Struct(obj); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Engine returns the underlying validator engine which powers the default
|
|
||||||
// Validator instance. This is useful if you want to register custom validations
|
|
||||||
// or struct level validations. See validator GoDoc for more info -
|
|
||||||
// https://godoc.org/gopkg.in/go-playground/validator.v8
|
|
||||||
func (v *defaultValidator) Engine() interface{} {
|
|
||||||
v.lazyinit()
|
|
||||||
return v.validate
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *defaultValidator) lazyinit() {
|
|
||||||
v.once.Do(func() {
|
|
||||||
config := &validator.Config{TagName: "binding"}
|
|
||||||
v.validate = validator.New(config)
|
|
||||||
})
|
|
||||||
}
|
|
56
vendor/github.com/gin-gonic/gin/binding/form.go
generated
vendored
56
vendor/github.com/gin-gonic/gin/binding/form.go
generated
vendored
@ -1,56 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
const defaultMemory = 32 * 1024 * 1024
|
|
||||||
|
|
||||||
type formBinding struct{}
|
|
||||||
type formPostBinding struct{}
|
|
||||||
type formMultipartBinding struct{}
|
|
||||||
|
|
||||||
func (formBinding) Name() string {
|
|
||||||
return "form"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (formBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
if err := req.ParseForm(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.ParseMultipartForm(defaultMemory)
|
|
||||||
if err := mapForm(obj, req.Form); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (formPostBinding) Name() string {
|
|
||||||
return "form-urlencoded"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
if err := req.ParseForm(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := mapForm(obj, req.PostForm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (formMultipartBinding) Name() string {
|
|
||||||
return "multipart/form-data"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
if err := req.ParseMultipartForm(defaultMemory); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := mapForm(obj, req.MultipartForm.Value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
|
209
vendor/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
209
vendor/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
@ -1,209 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func mapForm(ptr interface{}, form map[string][]string) error {
|
|
||||||
typ := reflect.TypeOf(ptr).Elem()
|
|
||||||
val := reflect.ValueOf(ptr).Elem()
|
|
||||||
for i := 0; i < typ.NumField(); i++ {
|
|
||||||
typeField := typ.Field(i)
|
|
||||||
structField := val.Field(i)
|
|
||||||
if !structField.CanSet() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
structFieldKind := structField.Kind()
|
|
||||||
inputFieldName := typeField.Tag.Get("form")
|
|
||||||
inputFieldNameList := strings.Split(inputFieldName, ",")
|
|
||||||
inputFieldName = inputFieldNameList[0]
|
|
||||||
var defaultValue string
|
|
||||||
if len(inputFieldNameList) > 1 {
|
|
||||||
defaultList := strings.SplitN(inputFieldNameList[1], "=", 2)
|
|
||||||
if defaultList[0] == "default" {
|
|
||||||
defaultValue = defaultList[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if inputFieldName == "" {
|
|
||||||
inputFieldName = typeField.Name
|
|
||||||
|
|
||||||
// if "form" tag is nil, we inspect if the field is a struct or struct pointer.
|
|
||||||
// this would not make sense for JSON parsing but it does for a form
|
|
||||||
// since data is flatten
|
|
||||||
if structFieldKind == reflect.Ptr {
|
|
||||||
if !structField.Elem().IsValid() {
|
|
||||||
structField.Set(reflect.New(structField.Type().Elem()))
|
|
||||||
}
|
|
||||||
structField = structField.Elem()
|
|
||||||
structFieldKind = structField.Kind()
|
|
||||||
}
|
|
||||||
if structFieldKind == reflect.Struct {
|
|
||||||
err := mapForm(structField.Addr().Interface(), form)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inputValue, exists := form[inputFieldName]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
if defaultValue == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inputValue = make([]string, 1)
|
|
||||||
inputValue[0] = defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
numElems := len(inputValue)
|
|
||||||
if structFieldKind == reflect.Slice && numElems > 0 {
|
|
||||||
sliceOf := structField.Type().Elem().Kind()
|
|
||||||
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
|
||||||
for i := 0; i < numElems; i++ {
|
|
||||||
if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val.Field(i).Set(slice)
|
|
||||||
} else {
|
|
||||||
if _, isTime := structField.Interface().(time.Time); isTime {
|
|
||||||
if err := setTimeField(inputValue[0], typeField, structField); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
|
||||||
switch valueKind {
|
|
||||||
case reflect.Int:
|
|
||||||
return setIntField(val, 0, structField)
|
|
||||||
case reflect.Int8:
|
|
||||||
return setIntField(val, 8, structField)
|
|
||||||
case reflect.Int16:
|
|
||||||
return setIntField(val, 16, structField)
|
|
||||||
case reflect.Int32:
|
|
||||||
return setIntField(val, 32, structField)
|
|
||||||
case reflect.Int64:
|
|
||||||
return setIntField(val, 64, structField)
|
|
||||||
case reflect.Uint:
|
|
||||||
return setUintField(val, 0, structField)
|
|
||||||
case reflect.Uint8:
|
|
||||||
return setUintField(val, 8, structField)
|
|
||||||
case reflect.Uint16:
|
|
||||||
return setUintField(val, 16, structField)
|
|
||||||
case reflect.Uint32:
|
|
||||||
return setUintField(val, 32, structField)
|
|
||||||
case reflect.Uint64:
|
|
||||||
return setUintField(val, 64, structField)
|
|
||||||
case reflect.Bool:
|
|
||||||
return setBoolField(val, structField)
|
|
||||||
case reflect.Float32:
|
|
||||||
return setFloatField(val, 32, structField)
|
|
||||||
case reflect.Float64:
|
|
||||||
return setFloatField(val, 64, structField)
|
|
||||||
case reflect.String:
|
|
||||||
structField.SetString(val)
|
|
||||||
case reflect.Ptr:
|
|
||||||
if !structField.Elem().IsValid() {
|
|
||||||
structField.Set(reflect.New(structField.Type().Elem()))
|
|
||||||
}
|
|
||||||
structFieldElem := structField.Elem()
|
|
||||||
return setWithProperType(structFieldElem.Kind(), val, structFieldElem)
|
|
||||||
default:
|
|
||||||
return errors.New("Unknown type")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setIntField(val string, bitSize int, field reflect.Value) error {
|
|
||||||
if val == "" {
|
|
||||||
val = "0"
|
|
||||||
}
|
|
||||||
intVal, err := strconv.ParseInt(val, 10, bitSize)
|
|
||||||
if err == nil {
|
|
||||||
field.SetInt(intVal)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUintField(val string, bitSize int, field reflect.Value) error {
|
|
||||||
if val == "" {
|
|
||||||
val = "0"
|
|
||||||
}
|
|
||||||
uintVal, err := strconv.ParseUint(val, 10, bitSize)
|
|
||||||
if err == nil {
|
|
||||||
field.SetUint(uintVal)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func setBoolField(val string, field reflect.Value) error {
|
|
||||||
if val == "" {
|
|
||||||
val = "false"
|
|
||||||
}
|
|
||||||
boolVal, err := strconv.ParseBool(val)
|
|
||||||
if err == nil {
|
|
||||||
field.SetBool(boolVal)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFloatField(val string, bitSize int, field reflect.Value) error {
|
|
||||||
if val == "" {
|
|
||||||
val = "0.0"
|
|
||||||
}
|
|
||||||
floatVal, err := strconv.ParseFloat(val, bitSize)
|
|
||||||
if err == nil {
|
|
||||||
field.SetFloat(floatVal)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
|
|
||||||
timeFormat := structField.Tag.Get("time_format")
|
|
||||||
if timeFormat == "" {
|
|
||||||
return errors.New("Blank time format")
|
|
||||||
}
|
|
||||||
|
|
||||||
if val == "" {
|
|
||||||
value.Set(reflect.ValueOf(time.Time{}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
l := time.Local
|
|
||||||
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
|
|
||||||
l = time.UTC
|
|
||||||
}
|
|
||||||
|
|
||||||
if locTag := structField.Tag.Get("time_location"); locTag != "" {
|
|
||||||
loc, err := time.LoadLocation(locTag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
l = loc
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := time.ParseInLocation(timeFormat, val, l)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
value.Set(reflect.ValueOf(t))
|
|
||||||
return nil
|
|
||||||
}
|
|
43
vendor/github.com/gin-gonic/gin/binding/json.go
generated
vendored
43
vendor/github.com/gin-gonic/gin/binding/json.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
|
|
||||||
// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
|
|
||||||
// interface{} as a Number instead of as a float64.
|
|
||||||
var EnableDecoderUseNumber = false
|
|
||||||
|
|
||||||
type jsonBinding struct{}
|
|
||||||
|
|
||||||
func (jsonBinding) Name() string {
|
|
||||||
return "json"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
return decodeJSON(req.Body, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jsonBinding) BindBody(body []byte, obj interface{}) error {
|
|
||||||
return decodeJSON(bytes.NewReader(body), obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeJSON(r io.Reader, obj interface{}) error {
|
|
||||||
decoder := json.NewDecoder(r)
|
|
||||||
if EnableDecoderUseNumber {
|
|
||||||
decoder.UseNumber()
|
|
||||||
}
|
|
||||||
if err := decoder.Decode(obj); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
|
35
vendor/github.com/gin-gonic/gin/binding/msgpack.go
generated
vendored
35
vendor/github.com/gin-gonic/gin/binding/msgpack.go
generated
vendored
@ -1,35 +0,0 @@
|
|||||||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/ugorji/go/codec"
|
|
||||||
)
|
|
||||||
|
|
||||||
type msgpackBinding struct{}
|
|
||||||
|
|
||||||
func (msgpackBinding) Name() string {
|
|
||||||
return "msgpack"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
return decodeMsgPack(req.Body, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
|
|
||||||
return decodeMsgPack(bytes.NewReader(body), obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeMsgPack(r io.Reader, obj interface{}) error {
|
|
||||||
cdc := new(codec.MsgpackHandle)
|
|
||||||
if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
|
36
vendor/github.com/gin-gonic/gin/binding/protobuf.go
generated
vendored
36
vendor/github.com/gin-gonic/gin/binding/protobuf.go
generated
vendored
@ -1,36 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
type protobufBinding struct{}
|
|
||||||
|
|
||||||
func (protobufBinding) Name() string {
|
|
||||||
return "protobuf"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
buf, err := ioutil.ReadAll(req.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return b.BindBody(buf, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (protobufBinding) BindBody(body []byte, obj interface{}) error {
|
|
||||||
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Here it's same to return validate(obj), but util now we cann't add
|
|
||||||
// `binding:""` to the struct which automatically generate by gen-proto
|
|
||||||
return nil
|
|
||||||
// return validate(obj)
|
|
||||||
}
|
|
21
vendor/github.com/gin-gonic/gin/binding/query.go
generated
vendored
21
vendor/github.com/gin-gonic/gin/binding/query.go
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type queryBinding struct{}
|
|
||||||
|
|
||||||
func (queryBinding) Name() string {
|
|
||||||
return "query"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (queryBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
values := req.URL.Query()
|
|
||||||
if err := mapForm(obj, values); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
|
33
vendor/github.com/gin-gonic/gin/binding/xml.go
generated
vendored
33
vendor/github.com/gin-gonic/gin/binding/xml.go
generated
vendored
@ -1,33 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type xmlBinding struct{}
|
|
||||||
|
|
||||||
func (xmlBinding) Name() string {
|
|
||||||
return "xml"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
|
||||||
return decodeXML(req.Body, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (xmlBinding) BindBody(body []byte, obj interface{}) error {
|
|
||||||
return decodeXML(bytes.NewReader(body), obj)
|
|
||||||
}
|
|
||||||
func decodeXML(r io.Reader, obj interface{}) error {
|
|
||||||
decoder := xml.NewDecoder(r)
|
|
||||||
if err := decoder.Decode(obj); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
|
5
vendor/github.com/gin-gonic/gin/codecov.yml
generated
vendored
5
vendor/github.com/gin-gonic/gin/codecov.yml
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
coverage:
|
|
||||||
notify:
|
|
||||||
gitter:
|
|
||||||
default:
|
|
||||||
url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165
|
|
933
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
933
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
@ -1,933 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"mime/multipart"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/sse"
|
|
||||||
"github.com/gin-gonic/gin/binding"
|
|
||||||
"github.com/gin-gonic/gin/render"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Content-Type MIME of the most common data formats.
|
|
||||||
const (
|
|
||||||
MIMEJSON = binding.MIMEJSON
|
|
||||||
MIMEHTML = binding.MIMEHTML
|
|
||||||
MIMEXML = binding.MIMEXML
|
|
||||||
MIMEXML2 = binding.MIMEXML2
|
|
||||||
MIMEPlain = binding.MIMEPlain
|
|
||||||
MIMEPOSTForm = binding.MIMEPOSTForm
|
|
||||||
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
|
|
||||||
BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
|
|
||||||
)
|
|
||||||
|
|
||||||
const abortIndex int8 = math.MaxInt8 / 2
|
|
||||||
|
|
||||||
// Context is the most important part of gin. It allows us to pass variables between middleware,
|
|
||||||
// manage the flow, validate the JSON of a request and render a JSON response for example.
|
|
||||||
type Context struct {
|
|
||||||
writermem responseWriter
|
|
||||||
Request *http.Request
|
|
||||||
Writer ResponseWriter
|
|
||||||
|
|
||||||
Params Params
|
|
||||||
handlers HandlersChain
|
|
||||||
index int8
|
|
||||||
|
|
||||||
engine *Engine
|
|
||||||
|
|
||||||
// Keys is a key/value pair exclusively for the context of each request.
|
|
||||||
Keys map[string]interface{}
|
|
||||||
|
|
||||||
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
|
|
||||||
Errors errorMsgs
|
|
||||||
|
|
||||||
// Accepted defines a list of manually accepted formats for content negotiation.
|
|
||||||
Accepted []string
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/********** CONTEXT CREATION ********/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
func (c *Context) reset() {
|
|
||||||
c.Writer = &c.writermem
|
|
||||||
c.Params = c.Params[0:0]
|
|
||||||
c.handlers = nil
|
|
||||||
c.index = -1
|
|
||||||
c.Keys = nil
|
|
||||||
c.Errors = c.Errors[0:0]
|
|
||||||
c.Accepted = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a copy of the current context that can be safely used outside the request's scope.
|
|
||||||
// This has to be used when the context has to be passed to a goroutine.
|
|
||||||
func (c *Context) Copy() *Context {
|
|
||||||
var cp = *c
|
|
||||||
cp.writermem.ResponseWriter = nil
|
|
||||||
cp.Writer = &cp.writermem
|
|
||||||
cp.index = abortIndex
|
|
||||||
cp.handlers = nil
|
|
||||||
return &cp
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()",
|
|
||||||
// this function will return "main.handleGetUsers".
|
|
||||||
func (c *Context) HandlerName() string {
|
|
||||||
return nameOfFunction(c.handlers.Last())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler returns the main handler.
|
|
||||||
func (c *Context) Handler() HandlerFunc {
|
|
||||||
return c.handlers.Last()
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/*********** FLOW CONTROL ***********/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
// Next should be used only inside middleware.
|
|
||||||
// It executes the pending handlers in the chain inside the calling handler.
|
|
||||||
// See example in GitHub.
|
|
||||||
func (c *Context) Next() {
|
|
||||||
c.index++
|
|
||||||
for s := int8(len(c.handlers)); c.index < s; c.index++ {
|
|
||||||
c.handlers[c.index](c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAborted returns true if the current context was aborted.
|
|
||||||
func (c *Context) IsAborted() bool {
|
|
||||||
return c.index >= abortIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
|
|
||||||
// Let's say you have an authorization middleware that validates that the current request is authorized.
|
|
||||||
// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
|
|
||||||
// for this request are not called.
|
|
||||||
func (c *Context) Abort() {
|
|
||||||
c.index = abortIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// AbortWithStatus calls `Abort()` and writes the headers with the specified status code.
|
|
||||||
// For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401).
|
|
||||||
func (c *Context) AbortWithStatus(code int) {
|
|
||||||
c.Status(code)
|
|
||||||
c.Writer.WriteHeaderNow()
|
|
||||||
c.Abort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
|
|
||||||
// This method stops the chain, writes the status code and return a JSON body.
|
|
||||||
// It also sets the Content-Type as "application/json".
|
|
||||||
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
|
|
||||||
c.Abort()
|
|
||||||
c.JSON(code, jsonObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AbortWithError calls `AbortWithStatus()` and `Error()` internally.
|
|
||||||
// This method stops the chain, writes the status code and pushes the specified error to `c.Errors`.
|
|
||||||
// See Context.Error() for more details.
|
|
||||||
func (c *Context) AbortWithError(code int, err error) *Error {
|
|
||||||
c.AbortWithStatus(code)
|
|
||||||
return c.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/********* ERROR MANAGEMENT *********/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
// Error attaches an error to the current context. The error is pushed to a list of errors.
|
|
||||||
// It's a good idea to call Error for each error that occurred during the resolution of a request.
|
|
||||||
// A middleware can be used to collect all the errors and push them to a database together,
|
|
||||||
// print a log, or append it in the HTTP response.
|
|
||||||
// Error will panic if err is nil.
|
|
||||||
func (c *Context) Error(err error) *Error {
|
|
||||||
if err == nil {
|
|
||||||
panic("err is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedError, ok := err.(*Error)
|
|
||||||
if !ok {
|
|
||||||
parsedError = &Error{
|
|
||||||
Err: err,
|
|
||||||
Type: ErrorTypePrivate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Errors = append(c.Errors, parsedError)
|
|
||||||
return parsedError
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/******** METADATA MANAGEMENT********/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
// Set is used to store a new key/value pair exclusively for this context.
|
|
||||||
// It also lazy initializes c.Keys if it was not used previously.
|
|
||||||
func (c *Context) Set(key string, value interface{}) {
|
|
||||||
if c.Keys == nil {
|
|
||||||
c.Keys = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
c.Keys[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the value for the given key, ie: (value, true).
|
|
||||||
// If the value does not exists it returns (nil, false)
|
|
||||||
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
|
||||||
value, exists = c.Keys[key]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustGet returns the value for the given key if it exists, otherwise it panics.
|
|
||||||
func (c *Context) MustGet(key string) interface{} {
|
|
||||||
if value, exists := c.Get(key); exists {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
panic("Key \"" + key + "\" does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetString returns the value associated with the key as a string.
|
|
||||||
func (c *Context) GetString(key string) (s string) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
s, _ = val.(string)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBool returns the value associated with the key as a boolean.
|
|
||||||
func (c *Context) GetBool(key string) (b bool) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
b, _ = val.(bool)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInt returns the value associated with the key as an integer.
|
|
||||||
func (c *Context) GetInt(key string) (i int) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
i, _ = val.(int)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInt64 returns the value associated with the key as an integer.
|
|
||||||
func (c *Context) GetInt64(key string) (i64 int64) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
i64, _ = val.(int64)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFloat64 returns the value associated with the key as a float64.
|
|
||||||
func (c *Context) GetFloat64(key string) (f64 float64) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
f64, _ = val.(float64)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTime returns the value associated with the key as time.
|
|
||||||
func (c *Context) GetTime(key string) (t time.Time) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
t, _ = val.(time.Time)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDuration returns the value associated with the key as a duration.
|
|
||||||
func (c *Context) GetDuration(key string) (d time.Duration) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
d, _ = val.(time.Duration)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStringSlice returns the value associated with the key as a slice of strings.
|
|
||||||
func (c *Context) GetStringSlice(key string) (ss []string) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
ss, _ = val.([]string)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
|
||||||
func (c *Context) GetStringMap(key string) (sm map[string]interface{}) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
sm, _ = val.(map[string]interface{})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStringMapString returns the value associated with the key as a map of strings.
|
|
||||||
func (c *Context) GetStringMapString(key string) (sms map[string]string) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
sms, _ = val.(map[string]string)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
|
|
||||||
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {
|
|
||||||
if val, ok := c.Get(key); ok && val != nil {
|
|
||||||
smss, _ = val.(map[string][]string)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/************ INPUT DATA ************/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
// Param returns the value of the URL param.
|
|
||||||
// It is a shortcut for c.Params.ByName(key)
|
|
||||||
// router.GET("/user/:id", func(c *gin.Context) {
|
|
||||||
// // a GET request to /user/john
|
|
||||||
// id := c.Param("id") // id == "john"
|
|
||||||
// })
|
|
||||||
func (c *Context) Param(key string) string {
|
|
||||||
return c.Params.ByName(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query returns the keyed url query value if it exists,
|
|
||||||
// otherwise it returns an empty string `("")`.
|
|
||||||
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
|
||||||
// GET /path?id=1234&name=Manu&value=
|
|
||||||
// c.Query("id") == "1234"
|
|
||||||
// c.Query("name") == "Manu"
|
|
||||||
// c.Query("value") == ""
|
|
||||||
// c.Query("wtf") == ""
|
|
||||||
func (c *Context) Query(key string) string {
|
|
||||||
value, _ := c.GetQuery(key)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultQuery returns the keyed url query value if it exists,
|
|
||||||
// otherwise it returns the specified defaultValue string.
|
|
||||||
// See: Query() and GetQuery() for further information.
|
|
||||||
// GET /?name=Manu&lastname=
|
|
||||||
// c.DefaultQuery("name", "unknown") == "Manu"
|
|
||||||
// c.DefaultQuery("id", "none") == "none"
|
|
||||||
// c.DefaultQuery("lastname", "none") == ""
|
|
||||||
func (c *Context) DefaultQuery(key, defaultValue string) string {
|
|
||||||
if value, ok := c.GetQuery(key); ok {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQuery is like Query(), it returns the keyed url query value
|
|
||||||
// if it exists `(value, true)` (even when the value is an empty string),
|
|
||||||
// otherwise it returns `("", false)`.
|
|
||||||
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
|
||||||
// GET /?name=Manu&lastname=
|
|
||||||
// ("Manu", true) == c.GetQuery("name")
|
|
||||||
// ("", false) == c.GetQuery("id")
|
|
||||||
// ("", true) == c.GetQuery("lastname")
|
|
||||||
func (c *Context) GetQuery(key string) (string, bool) {
|
|
||||||
if values, ok := c.GetQueryArray(key); ok {
|
|
||||||
return values[0], ok
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryArray returns a slice of strings for a given query key.
|
|
||||||
// The length of the slice depends on the number of params with the given key.
|
|
||||||
func (c *Context) QueryArray(key string) []string {
|
|
||||||
values, _ := c.GetQueryArray(key)
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQueryArray returns a slice of strings for a given query key, plus
|
|
||||||
// a boolean value whether at least one value exists for the given key.
|
|
||||||
func (c *Context) GetQueryArray(key string) ([]string, bool) {
|
|
||||||
if values, ok := c.Request.URL.Query()[key]; ok && len(values) > 0 {
|
|
||||||
return values, true
|
|
||||||
}
|
|
||||||
return []string{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryMap returns a map for a given query key.
|
|
||||||
func (c *Context) QueryMap(key string) map[string]string {
|
|
||||||
dicts, _ := c.GetQueryMap(key)
|
|
||||||
return dicts
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQueryMap returns a map for a given query key, plus a boolean value
|
|
||||||
// whether at least one value exists for the given key.
|
|
||||||
func (c *Context) GetQueryMap(key string) (map[string]string, bool) {
|
|
||||||
return c.get(c.Request.URL.Query(), key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostForm returns the specified key from a POST urlencoded form or multipart form
|
|
||||||
// when it exists, otherwise it returns an empty string `("")`.
|
|
||||||
func (c *Context) PostForm(key string) string {
|
|
||||||
value, _ := c.GetPostForm(key)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
|
|
||||||
// when it exists, otherwise it returns the specified defaultValue string.
|
|
||||||
// See: PostForm() and GetPostForm() for further information.
|
|
||||||
func (c *Context) DefaultPostForm(key, defaultValue string) string {
|
|
||||||
if value, ok := c.GetPostForm(key); ok {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded
|
|
||||||
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
|
|
||||||
// otherwise it returns ("", false).
|
|
||||||
// For example, during a PATCH request to update the user's email:
|
|
||||||
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
|
|
||||||
// email= --> ("", true) := GetPostForm("email") // set email to ""
|
|
||||||
// --> ("", false) := GetPostForm("email") // do nothing with email
|
|
||||||
func (c *Context) GetPostForm(key string) (string, bool) {
|
|
||||||
if values, ok := c.GetPostFormArray(key); ok {
|
|
||||||
return values[0], ok
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostFormArray returns a slice of strings for a given form key.
|
|
||||||
// The length of the slice depends on the number of params with the given key.
|
|
||||||
func (c *Context) PostFormArray(key string) []string {
|
|
||||||
values, _ := c.GetPostFormArray(key)
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPostFormArray returns a slice of strings for a given form key, plus
|
|
||||||
// a boolean value whether at least one value exists for the given key.
|
|
||||||
func (c *Context) GetPostFormArray(key string) ([]string, bool) {
|
|
||||||
req := c.Request
|
|
||||||
req.ParseForm()
|
|
||||||
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
|
|
||||||
if values := req.PostForm[key]; len(values) > 0 {
|
|
||||||
return values, true
|
|
||||||
}
|
|
||||||
if req.MultipartForm != nil && req.MultipartForm.File != nil {
|
|
||||||
if values := req.MultipartForm.Value[key]; len(values) > 0 {
|
|
||||||
return values, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return []string{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostFormMap returns a map for a given form key.
|
|
||||||
func (c *Context) PostFormMap(key string) map[string]string {
|
|
||||||
dicts, _ := c.GetPostFormMap(key)
|
|
||||||
return dicts
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPostFormMap returns a map for a given form key, plus a boolean value
|
|
||||||
// whether at least one value exists for the given key.
|
|
||||||
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
|
|
||||||
req := c.Request
|
|
||||||
req.ParseForm()
|
|
||||||
req.ParseMultipartForm(c.engine.MaxMultipartMemory)
|
|
||||||
dicts, exist := c.get(req.PostForm, key)
|
|
||||||
|
|
||||||
if !exist && req.MultipartForm != nil && req.MultipartForm.File != nil {
|
|
||||||
dicts, exist = c.get(req.MultipartForm.Value, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dicts, exist
|
|
||||||
}
|
|
||||||
|
|
||||||
// get is an internal method and returns a map which satisfy conditions.
|
|
||||||
func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
|
|
||||||
dicts := make(map[string]string)
|
|
||||||
exist := false
|
|
||||||
for k, v := range m {
|
|
||||||
if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
|
|
||||||
if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
|
|
||||||
exist = true
|
|
||||||
dicts[k[i+1:][:j]] = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dicts, exist
|
|
||||||
}
|
|
||||||
|
|
||||||
// FormFile returns the first file for the provided form key.
|
|
||||||
func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
|
|
||||||
_, fh, err := c.Request.FormFile(name)
|
|
||||||
return fh, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultipartForm is the parsed multipart form, including file uploads.
|
|
||||||
func (c *Context) MultipartForm() (*multipart.Form, error) {
|
|
||||||
err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory)
|
|
||||||
return c.Request.MultipartForm, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveUploadedFile uploads the form file to specific dst.
|
|
||||||
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
|
|
||||||
src, err := file.Open()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer src.Close()
|
|
||||||
|
|
||||||
out, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
io.Copy(out, src)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind checks the Content-Type to select a binding engine automatically,
|
|
||||||
// Depending the "Content-Type" header different bindings are used:
|
|
||||||
// "application/json" --> JSON binding
|
|
||||||
// "application/xml" --> XML binding
|
|
||||||
// otherwise --> returns an error.
|
|
||||||
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
|
|
||||||
// It decodes the json payload into the struct specified as a pointer.
|
|
||||||
// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
|
|
||||||
func (c *Context) Bind(obj interface{}) error {
|
|
||||||
b := binding.Default(c.Request.Method, c.ContentType())
|
|
||||||
return c.MustBindWith(obj, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
|
|
||||||
func (c *Context) BindJSON(obj interface{}) error {
|
|
||||||
return c.MustBindWith(obj, binding.JSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
|
|
||||||
func (c *Context) BindQuery(obj interface{}) error {
|
|
||||||
return c.MustBindWith(obj, binding.Query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
|
||||||
// It will abort the request with HTTP 400 if any error ocurrs.
|
|
||||||
// See the binding package.
|
|
||||||
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
|
||||||
if err = c.ShouldBindWith(obj, b); err != nil {
|
|
||||||
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBind checks the Content-Type to select a binding engine automatically,
|
|
||||||
// Depending the "Content-Type" header different bindings are used:
|
|
||||||
// "application/json" --> JSON binding
|
|
||||||
// "application/xml" --> XML binding
|
|
||||||
// otherwise --> returns an error
|
|
||||||
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
|
|
||||||
// It decodes the json payload into the struct specified as a pointer.
|
|
||||||
// Like c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid.
|
|
||||||
func (c *Context) ShouldBind(obj interface{}) error {
|
|
||||||
b := binding.Default(c.Request.Method, c.ContentType())
|
|
||||||
return c.ShouldBindWith(obj, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
|
|
||||||
func (c *Context) ShouldBindJSON(obj interface{}) error {
|
|
||||||
return c.ShouldBindWith(obj, binding.JSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
|
|
||||||
func (c *Context) ShouldBindQuery(obj interface{}) error {
|
|
||||||
return c.ShouldBindWith(obj, binding.Query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
|
|
||||||
// See the binding package.
|
|
||||||
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
|
||||||
return b.Bind(c.Request, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request
|
|
||||||
// body into the context, and reuse when it is called again.
|
|
||||||
//
|
|
||||||
// NOTE: This method reads the body before binding. So you should use
|
|
||||||
// ShouldBindWith for better performance if you need to call only once.
|
|
||||||
func (c *Context) ShouldBindBodyWith(
|
|
||||||
obj interface{}, bb binding.BindingBody,
|
|
||||||
) (err error) {
|
|
||||||
var body []byte
|
|
||||||
if cb, ok := c.Get(BodyBytesKey); ok {
|
|
||||||
if cbb, ok := cb.([]byte); ok {
|
|
||||||
body = cbb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if body == nil {
|
|
||||||
body, err = ioutil.ReadAll(c.Request.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Set(BodyBytesKey, body)
|
|
||||||
}
|
|
||||||
return bb.BindBody(body, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientIP implements a best effort algorithm to return the real client IP, it parses
|
|
||||||
// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
|
|
||||||
// Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
|
|
||||||
func (c *Context) ClientIP() string {
|
|
||||||
if c.engine.ForwardedByClientIP {
|
|
||||||
clientIP := c.requestHeader("X-Forwarded-For")
|
|
||||||
clientIP = strings.TrimSpace(strings.Split(clientIP, ",")[0])
|
|
||||||
if clientIP == "" {
|
|
||||||
clientIP = strings.TrimSpace(c.requestHeader("X-Real-Ip"))
|
|
||||||
}
|
|
||||||
if clientIP != "" {
|
|
||||||
return clientIP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.engine.AppEngine {
|
|
||||||
if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" {
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)); err == nil {
|
|
||||||
return ip
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContentType returns the Content-Type header of the request.
|
|
||||||
func (c *Context) ContentType() string {
|
|
||||||
return filterFlags(c.requestHeader("Content-Type"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWebsocket returns true if the request headers indicate that a websocket
|
|
||||||
// handshake is being initiated by the client.
|
|
||||||
func (c *Context) IsWebsocket() bool {
|
|
||||||
if strings.Contains(strings.ToLower(c.requestHeader("Connection")), "upgrade") &&
|
|
||||||
strings.ToLower(c.requestHeader("Upgrade")) == "websocket" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) requestHeader(key string) string {
|
|
||||||
return c.Request.Header.Get(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/******** RESPONSE RENDERING ********/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function.
|
|
||||||
func bodyAllowedForStatus(status int) bool {
|
|
||||||
switch {
|
|
||||||
case status >= 100 && status <= 199:
|
|
||||||
return false
|
|
||||||
case status == http.StatusNoContent:
|
|
||||||
return false
|
|
||||||
case status == http.StatusNotModified:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status sets the HTTP response code.
|
|
||||||
func (c *Context) Status(code int) {
|
|
||||||
c.writermem.WriteHeader(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
|
|
||||||
// It writes a header in the response.
|
|
||||||
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
|
|
||||||
func (c *Context) Header(key, value string) {
|
|
||||||
if value == "" {
|
|
||||||
c.Writer.Header().Del(key)
|
|
||||||
} else {
|
|
||||||
c.Writer.Header().Set(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHeader returns value from request headers.
|
|
||||||
func (c *Context) GetHeader(key string) string {
|
|
||||||
return c.requestHeader(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRawData return stream data.
|
|
||||||
func (c *Context) GetRawData() ([]byte, error) {
|
|
||||||
return ioutil.ReadAll(c.Request.Body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
|
|
||||||
// The provided cookie must have a valid Name. Invalid cookies may be
|
|
||||||
// silently dropped.
|
|
||||||
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
|
|
||||||
if path == "" {
|
|
||||||
path = "/"
|
|
||||||
}
|
|
||||||
http.SetCookie(c.Writer, &http.Cookie{
|
|
||||||
Name: name,
|
|
||||||
Value: url.QueryEscape(value),
|
|
||||||
MaxAge: maxAge,
|
|
||||||
Path: path,
|
|
||||||
Domain: domain,
|
|
||||||
Secure: secure,
|
|
||||||
HttpOnly: httpOnly,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cookie returns the named cookie provided in the request or
|
|
||||||
// ErrNoCookie if not found. And return the named cookie is unescaped.
|
|
||||||
// If multiple cookies match the given name, only one cookie will
|
|
||||||
// be returned.
|
|
||||||
func (c *Context) Cookie(name string) (string, error) {
|
|
||||||
cookie, err := c.Request.Cookie(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
val, _ := url.QueryUnescape(cookie.Value)
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) Render(code int, r render.Render) {
|
|
||||||
c.Status(code)
|
|
||||||
|
|
||||||
if !bodyAllowedForStatus(code) {
|
|
||||||
r.WriteContentType(c.Writer)
|
|
||||||
c.Writer.WriteHeaderNow()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := r.Render(c.Writer); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTML renders the HTTP template specified by its file name.
|
|
||||||
// It also updates the HTTP code and sets the Content-Type as "text/html".
|
|
||||||
// See http://golang.org/doc/articles/wiki/
|
|
||||||
func (c *Context) HTML(code int, name string, obj interface{}) {
|
|
||||||
instance := c.engine.HTMLRender.Instance(name, obj)
|
|
||||||
c.Render(code, instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
|
|
||||||
// It also sets the Content-Type as "application/json".
|
|
||||||
// WARNING: we recommend to use this only for development purposes since printing pretty JSON is
|
|
||||||
// more CPU and bandwidth consuming. Use Context.JSON() instead.
|
|
||||||
func (c *Context) IndentedJSON(code int, obj interface{}) {
|
|
||||||
c.Render(code, render.IndentedJSON{Data: obj})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecureJSON serializes the given struct as Secure JSON into the response body.
|
|
||||||
// Default prepends "while(1)," to response body if the given struct is array values.
|
|
||||||
// It also sets the Content-Type as "application/json".
|
|
||||||
func (c *Context) SecureJSON(code int, obj interface{}) {
|
|
||||||
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJsonPrefix, Data: obj})
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONP serializes the given struct as JSON into the response body.
|
|
||||||
// It add padding to response body to request data from a server residing in a different domain than the client.
|
|
||||||
// It also sets the Content-Type as "application/javascript".
|
|
||||||
func (c *Context) JSONP(code int, obj interface{}) {
|
|
||||||
callback := c.DefaultQuery("callback", "")
|
|
||||||
if callback == "" {
|
|
||||||
c.Render(code, render.JSON{Data: obj})
|
|
||||||
} else {
|
|
||||||
c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON serializes the given struct as JSON into the response body.
|
|
||||||
// It also sets the Content-Type as "application/json".
|
|
||||||
func (c *Context) JSON(code int, obj interface{}) {
|
|
||||||
c.Render(code, render.JSON{Data: obj})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
|
|
||||||
// It also sets the Content-Type as "application/json".
|
|
||||||
func (c *Context) AsciiJSON(code int, obj interface{}) {
|
|
||||||
c.Render(code, render.AsciiJSON{Data: obj})
|
|
||||||
}
|
|
||||||
|
|
||||||
// XML serializes the given struct as XML into the response body.
|
|
||||||
// It also sets the Content-Type as "application/xml".
|
|
||||||
func (c *Context) XML(code int, obj interface{}) {
|
|
||||||
c.Render(code, render.XML{Data: obj})
|
|
||||||
}
|
|
||||||
|
|
||||||
// YAML serializes the given struct as YAML into the response body.
|
|
||||||
func (c *Context) YAML(code int, obj interface{}) {
|
|
||||||
c.Render(code, render.YAML{Data: obj})
|
|
||||||
}
|
|
||||||
|
|
||||||
// String writes the given string into the response body.
|
|
||||||
func (c *Context) String(code int, format string, values ...interface{}) {
|
|
||||||
c.Render(code, render.String{Format: format, Data: values})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect returns a HTTP redirect to the specific location.
|
|
||||||
func (c *Context) Redirect(code int, location string) {
|
|
||||||
c.Render(-1, render.Redirect{
|
|
||||||
Code: code,
|
|
||||||
Location: location,
|
|
||||||
Request: c.Request,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data writes some data into the body stream and updates the HTTP code.
|
|
||||||
func (c *Context) Data(code int, contentType string, data []byte) {
|
|
||||||
c.Render(code, render.Data{
|
|
||||||
ContentType: contentType,
|
|
||||||
Data: data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataFromReader writes the specified reader into the body stream and updates the HTTP code.
|
|
||||||
func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) {
|
|
||||||
c.Render(code, render.Reader{
|
|
||||||
Headers: extraHeaders,
|
|
||||||
ContentType: contentType,
|
|
||||||
ContentLength: contentLength,
|
|
||||||
Reader: reader,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// File writes the specified file into the body stream in a efficient way.
|
|
||||||
func (c *Context) File(filepath string) {
|
|
||||||
http.ServeFile(c.Writer, c.Request, filepath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SSEvent writes a Server-Sent Event into the body stream.
|
|
||||||
func (c *Context) SSEvent(name string, message interface{}) {
|
|
||||||
c.Render(-1, sse.Event{
|
|
||||||
Event: name,
|
|
||||||
Data: message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) Stream(step func(w io.Writer) bool) {
|
|
||||||
w := c.Writer
|
|
||||||
clientGone := w.CloseNotify()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-clientGone:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
keepOpen := step(w)
|
|
||||||
w.Flush()
|
|
||||||
if !keepOpen {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/******** CONTENT NEGOTIATION *******/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
type Negotiate struct {
|
|
||||||
Offered []string
|
|
||||||
HTMLName string
|
|
||||||
HTMLData interface{}
|
|
||||||
JSONData interface{}
|
|
||||||
XMLData interface{}
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) Negotiate(code int, config Negotiate) {
|
|
||||||
switch c.NegotiateFormat(config.Offered...) {
|
|
||||||
case binding.MIMEJSON:
|
|
||||||
data := chooseData(config.JSONData, config.Data)
|
|
||||||
c.JSON(code, data)
|
|
||||||
|
|
||||||
case binding.MIMEHTML:
|
|
||||||
data := chooseData(config.HTMLData, config.Data)
|
|
||||||
c.HTML(code, config.HTMLName, data)
|
|
||||||
|
|
||||||
case binding.MIMEXML:
|
|
||||||
data := chooseData(config.XMLData, config.Data)
|
|
||||||
c.XML(code, data)
|
|
||||||
|
|
||||||
default:
|
|
||||||
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) NegotiateFormat(offered ...string) string {
|
|
||||||
assert1(len(offered) > 0, "you must provide at least one offer")
|
|
||||||
|
|
||||||
if c.Accepted == nil {
|
|
||||||
c.Accepted = parseAccept(c.requestHeader("Accept"))
|
|
||||||
}
|
|
||||||
if len(c.Accepted) == 0 {
|
|
||||||
return offered[0]
|
|
||||||
}
|
|
||||||
for _, accepted := range c.Accepted {
|
|
||||||
for _, offert := range offered {
|
|
||||||
if accepted == offert {
|
|
||||||
return offert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) SetAccepted(formats ...string) {
|
|
||||||
c.Accepted = formats
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************/
|
|
||||||
/***** GOLANG.ORG/X/NET/CONTEXT *****/
|
|
||||||
/************************************/
|
|
||||||
|
|
||||||
// Deadline returns the time when work done on behalf of this context
|
|
||||||
// should be canceled. Deadline returns ok==false when no deadline is
|
|
||||||
// set. Successive calls to Deadline return the same results.
|
|
||||||
func (c *Context) Deadline() (deadline time.Time, ok bool) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done returns a channel that's closed when work done on behalf of this
|
|
||||||
// context should be canceled. Done may return nil if this context can
|
|
||||||
// never be canceled. Successive calls to Done return the same value.
|
|
||||||
func (c *Context) Done() <-chan struct{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Err returns a non-nil error value after Done is closed,
|
|
||||||
// successive calls to Err return the same error.
|
|
||||||
// If Done is not yet closed, Err returns nil.
|
|
||||||
// If Done is closed, Err returns a non-nil error explaining why:
|
|
||||||
// Canceled if the context was canceled
|
|
||||||
// or DeadlineExceeded if the context's deadline passed.
|
|
||||||
func (c *Context) Err() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the value associated with this context for key, or nil
|
|
||||||
// if no value is associated with key. Successive calls to Value with
|
|
||||||
// the same key returns the same result.
|
|
||||||
func (c *Context) Value(key interface{}) interface{} {
|
|
||||||
if key == 0 {
|
|
||||||
return c.Request
|
|
||||||
}
|
|
||||||
if keyAsString, ok := key.(string); ok {
|
|
||||||
val, _ := c.Get(keyAsString)
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
11
vendor/github.com/gin-gonic/gin/context_appengine.go
generated
vendored
11
vendor/github.com/gin-gonic/gin/context_appengine.go
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
// +build appengine
|
|
||||||
|
|
||||||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
defaultAppEngine = true
|
|
||||||
}
|
|
13
vendor/github.com/gin-gonic/gin/coverage.sh
generated
vendored
13
vendor/github.com/gin-gonic/gin/coverage.sh
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "mode: count" > coverage.out
|
|
||||||
|
|
||||||
for d in $(go list ./... | grep -E 'gin$|binding$|render$' | grep -v 'examples'); do
|
|
||||||
go test -v -covermode=count -coverprofile=profile.out $d
|
|
||||||
if [ -f profile.out ]; then
|
|
||||||
cat profile.out | grep -v "mode:" >> coverage.out
|
|
||||||
rm profile.out
|
|
||||||
fi
|
|
||||||
done
|
|
80
vendor/github.com/gin-gonic/gin/debug.go
generated
vendored
80
vendor/github.com/gin-gonic/gin/debug.go
generated
vendored
@ -1,80 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"html/template"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDebugging returns true if the framework is running in debug mode.
|
|
||||||
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
|
||||||
func IsDebugging() bool {
|
|
||||||
return ginMode == debugCode
|
|
||||||
}
|
|
||||||
|
|
||||||
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
|
||||||
if IsDebugging() {
|
|
||||||
nuHandlers := len(handlers)
|
|
||||||
handlerName := nameOfFunction(handlers.Last())
|
|
||||||
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func debugPrintLoadTemplate(tmpl *template.Template) {
|
|
||||||
if IsDebugging() {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for _, tmpl := range tmpl.Templates() {
|
|
||||||
buf.WriteString("\t- ")
|
|
||||||
buf.WriteString(tmpl.Name())
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
debugPrint("Loaded HTML Templates (%d): \n%s\n", len(tmpl.Templates()), buf.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func debugPrint(format string, values ...interface{}) {
|
|
||||||
if IsDebugging() {
|
|
||||||
log.Printf("[GIN-debug] "+format, values...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func debugPrintWARNINGDefault() {
|
|
||||||
debugPrint(`[WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.
|
|
||||||
|
|
||||||
`)
|
|
||||||
debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
|
|
||||||
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func debugPrintWARNINGNew() {
|
|
||||||
debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production.
|
|
||||||
- using env: export GIN_MODE=release
|
|
||||||
- using code: gin.SetMode(gin.ReleaseMode)
|
|
||||||
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func debugPrintWARNINGSetHTMLTemplate() {
|
|
||||||
debugPrint(`[WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called
|
|
||||||
at initialization. ie. before any route is registered or the router is listening in a socket:
|
|
||||||
|
|
||||||
router := gin.Default()
|
|
||||||
router.SetHTMLTemplate(template) // << good place
|
|
||||||
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func debugPrintError(err error) {
|
|
||||||
if err != nil {
|
|
||||||
debugPrint("[ERROR] %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
21
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
21
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/binding"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BindWith binds the passed struct pointer using the specified binding engine.
|
|
||||||
// See the binding package.
|
|
||||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
|
||||||
log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to
|
|
||||||
be deprecated, please check issue #662 and either use MustBindWith() if you
|
|
||||||
want HTTP 400 to be automatically returned if any error occur, or use
|
|
||||||
ShouldBindWith() if you need to manage the error.`)
|
|
||||||
return c.MustBindWith(obj, b)
|
|
||||||
}
|
|
6
vendor/github.com/gin-gonic/gin/doc.go
generated
vendored
6
vendor/github.com/gin-gonic/gin/doc.go
generated
vendored
@ -1,6 +0,0 @@
|
|||||||
/*
|
|
||||||
Package gin implements a HTTP web framework called gin.
|
|
||||||
|
|
||||||
See https://gin-gonic.github.io/gin/ for more information about gin.
|
|
||||||
*/
|
|
||||||
package gin // import "github.com/gin-gonic/gin"
|
|
157
vendor/github.com/gin-gonic/gin/errors.go
generated
vendored
157
vendor/github.com/gin-gonic/gin/errors.go
generated
vendored
@ -1,157 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ErrorType uint64
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails
|
|
||||||
ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() fails
|
|
||||||
ErrorTypePrivate ErrorType = 1 << 0
|
|
||||||
ErrorTypePublic ErrorType = 1 << 1
|
|
||||||
|
|
||||||
ErrorTypeAny ErrorType = 1<<64 - 1
|
|
||||||
ErrorTypeNu = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
Err error
|
|
||||||
Type ErrorType
|
|
||||||
Meta interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type errorMsgs []*Error
|
|
||||||
|
|
||||||
var _ error = &Error{}
|
|
||||||
|
|
||||||
func (msg *Error) SetType(flags ErrorType) *Error {
|
|
||||||
msg.Type = flags
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *Error) SetMeta(data interface{}) *Error {
|
|
||||||
msg.Meta = data
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *Error) JSON() interface{} {
|
|
||||||
json := H{}
|
|
||||||
if msg.Meta != nil {
|
|
||||||
value := reflect.ValueOf(msg.Meta)
|
|
||||||
switch value.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
return msg.Meta
|
|
||||||
case reflect.Map:
|
|
||||||
for _, key := range value.MapKeys() {
|
|
||||||
json[key.String()] = value.MapIndex(key).Interface()
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
json["meta"] = msg.Meta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := json["error"]; !ok {
|
|
||||||
json["error"] = msg.Error()
|
|
||||||
}
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaller interface.
|
|
||||||
func (msg *Error) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(msg.JSON())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error implements the error interface
|
|
||||||
func (msg Error) Error() string {
|
|
||||||
return msg.Err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *Error) IsType(flags ErrorType) bool {
|
|
||||||
return (msg.Type & flags) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByType returns a readonly copy filtered the byte.
|
|
||||||
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic.
|
|
||||||
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
|
||||||
if len(a) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if typ == ErrorTypeAny {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
var result errorMsgs
|
|
||||||
for _, msg := range a {
|
|
||||||
if msg.IsType(typ) {
|
|
||||||
result = append(result, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Last returns the last error in the slice. It returns nil if the array is empty.
|
|
||||||
// Shortcut for errors[len(errors)-1].
|
|
||||||
func (a errorMsgs) Last() *Error {
|
|
||||||
if length := len(a); length > 0 {
|
|
||||||
return a[length-1]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors returns an array will all the error messages.
|
|
||||||
// Example:
|
|
||||||
// c.Error(errors.New("first"))
|
|
||||||
// c.Error(errors.New("second"))
|
|
||||||
// c.Error(errors.New("third"))
|
|
||||||
// c.Errors.Errors() // == []string{"first", "second", "third"}
|
|
||||||
func (a errorMsgs) Errors() []string {
|
|
||||||
if len(a) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
errorStrings := make([]string, len(a))
|
|
||||||
for i, err := range a {
|
|
||||||
errorStrings[i] = err.Error()
|
|
||||||
}
|
|
||||||
return errorStrings
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a errorMsgs) JSON() interface{} {
|
|
||||||
switch len(a) {
|
|
||||||
case 0:
|
|
||||||
return nil
|
|
||||||
case 1:
|
|
||||||
return a.Last().JSON()
|
|
||||||
default:
|
|
||||||
json := make([]interface{}, len(a))
|
|
||||||
for i, err := range a {
|
|
||||||
json[i] = err.JSON()
|
|
||||||
}
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a errorMsgs) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(a.JSON())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a errorMsgs) String() string {
|
|
||||||
if len(a) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
for i, msg := range a {
|
|
||||||
fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err)
|
|
||||||
if msg.Meta != nil {
|
|
||||||
fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buffer.String()
|
|
||||||
}
|
|
45
vendor/github.com/gin-gonic/gin/fs.go
generated
vendored
45
vendor/github.com/gin-gonic/gin/fs.go
generated
vendored
@ -1,45 +0,0 @@
|
|||||||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type onlyfilesFS struct {
|
|
||||||
fs http.FileSystem
|
|
||||||
}
|
|
||||||
|
|
||||||
type neuteredReaddirFile struct {
|
|
||||||
http.File
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally
|
|
||||||
// in router.Static().
|
|
||||||
// if listDirectory == true, then it works the same as http.Dir() otherwise it returns
|
|
||||||
// a filesystem that prevents http.FileServer() to list the directory files.
|
|
||||||
func Dir(root string, listDirectory bool) http.FileSystem {
|
|
||||||
fs := http.Dir(root)
|
|
||||||
if listDirectory {
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
return &onlyfilesFS{fs}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open conforms to http.Filesystem.
|
|
||||||
func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
|
||||||
f, err := fs.fs.Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return neuteredReaddirFile{f}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Readdir overrides the http.File default implementation.
|
|
||||||
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
|
||||||
// this disables directory listing
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
443
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
443
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
@ -1,443 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/render"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Version is Framework's version.
|
|
||||||
Version = "v1.3.0"
|
|
||||||
defaultMultipartMemory = 32 << 20 // 32 MB
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
default404Body = []byte("404 page not found")
|
|
||||||
default405Body = []byte("405 method not allowed")
|
|
||||||
defaultAppEngine bool
|
|
||||||
)
|
|
||||||
|
|
||||||
type HandlerFunc func(*Context)
|
|
||||||
type HandlersChain []HandlerFunc
|
|
||||||
|
|
||||||
// Last returns the last handler in the chain. ie. the last handler is the main own.
|
|
||||||
func (c HandlersChain) Last() HandlerFunc {
|
|
||||||
if length := len(c); length > 0 {
|
|
||||||
return c[length-1]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouteInfo struct {
|
|
||||||
Method string
|
|
||||||
Path string
|
|
||||||
Handler string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RoutesInfo []RouteInfo
|
|
||||||
|
|
||||||
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
|
|
||||||
// Create an instance of Engine, by using New() or Default()
|
|
||||||
type Engine struct {
|
|
||||||
RouterGroup
|
|
||||||
|
|
||||||
// Enables automatic redirection if the current route can't be matched but a
|
|
||||||
// handler for the path with (without) the trailing slash exists.
|
|
||||||
// For example if /foo/ is requested but a route only exists for /foo, the
|
|
||||||
// client is redirected to /foo with http status code 301 for GET requests
|
|
||||||
// and 307 for all other request methods.
|
|
||||||
RedirectTrailingSlash bool
|
|
||||||
|
|
||||||
// If enabled, the router tries to fix the current request path, if no
|
|
||||||
// handle is registered for it.
|
|
||||||
// First superfluous path elements like ../ or // are removed.
|
|
||||||
// Afterwards the router does a case-insensitive lookup of the cleaned path.
|
|
||||||
// If a handle can be found for this route, the router makes a redirection
|
|
||||||
// to the corrected path with status code 301 for GET requests and 307 for
|
|
||||||
// all other request methods.
|
|
||||||
// For example /FOO and /..//Foo could be redirected to /foo.
|
|
||||||
// RedirectTrailingSlash is independent of this option.
|
|
||||||
RedirectFixedPath bool
|
|
||||||
|
|
||||||
// If enabled, the router checks if another method is allowed for the
|
|
||||||
// current route, if the current request can not be routed.
|
|
||||||
// If this is the case, the request is answered with 'Method Not Allowed'
|
|
||||||
// and HTTP status code 405.
|
|
||||||
// If no other Method is allowed, the request is delegated to the NotFound
|
|
||||||
// handler.
|
|
||||||
HandleMethodNotAllowed bool
|
|
||||||
ForwardedByClientIP bool
|
|
||||||
|
|
||||||
// #726 #755 If enabled, it will thrust some headers starting with
|
|
||||||
// 'X-AppEngine...' for better integration with that PaaS.
|
|
||||||
AppEngine bool
|
|
||||||
|
|
||||||
// If enabled, the url.RawPath will be used to find parameters.
|
|
||||||
UseRawPath bool
|
|
||||||
|
|
||||||
// If true, the path value will be unescaped.
|
|
||||||
// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
|
|
||||||
// as url.Path gonna be used, which is already unescaped.
|
|
||||||
UnescapePathValues bool
|
|
||||||
|
|
||||||
// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
|
|
||||||
// method call.
|
|
||||||
MaxMultipartMemory int64
|
|
||||||
|
|
||||||
delims render.Delims
|
|
||||||
secureJsonPrefix string
|
|
||||||
HTMLRender render.HTMLRender
|
|
||||||
FuncMap template.FuncMap
|
|
||||||
allNoRoute HandlersChain
|
|
||||||
allNoMethod HandlersChain
|
|
||||||
noRoute HandlersChain
|
|
||||||
noMethod HandlersChain
|
|
||||||
pool sync.Pool
|
|
||||||
trees methodTrees
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ IRouter = &Engine{}
|
|
||||||
|
|
||||||
// New returns a new blank Engine instance without any middleware attached.
|
|
||||||
// By default the configuration is:
|
|
||||||
// - RedirectTrailingSlash: true
|
|
||||||
// - RedirectFixedPath: false
|
|
||||||
// - HandleMethodNotAllowed: false
|
|
||||||
// - ForwardedByClientIP: true
|
|
||||||
// - UseRawPath: false
|
|
||||||
// - UnescapePathValues: true
|
|
||||||
func New() *Engine {
|
|
||||||
debugPrintWARNINGNew()
|
|
||||||
engine := &Engine{
|
|
||||||
RouterGroup: RouterGroup{
|
|
||||||
Handlers: nil,
|
|
||||||
basePath: "/",
|
|
||||||
root: true,
|
|
||||||
},
|
|
||||||
FuncMap: template.FuncMap{},
|
|
||||||
RedirectTrailingSlash: true,
|
|
||||||
RedirectFixedPath: false,
|
|
||||||
HandleMethodNotAllowed: false,
|
|
||||||
ForwardedByClientIP: true,
|
|
||||||
AppEngine: defaultAppEngine,
|
|
||||||
UseRawPath: false,
|
|
||||||
UnescapePathValues: true,
|
|
||||||
MaxMultipartMemory: defaultMultipartMemory,
|
|
||||||
trees: make(methodTrees, 0, 9),
|
|
||||||
delims: render.Delims{Left: "{{", Right: "}}"},
|
|
||||||
secureJsonPrefix: "while(1);",
|
|
||||||
}
|
|
||||||
engine.RouterGroup.engine = engine
|
|
||||||
engine.pool.New = func() interface{} {
|
|
||||||
return engine.allocateContext()
|
|
||||||
}
|
|
||||||
return engine
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
|
||||||
func Default() *Engine {
|
|
||||||
debugPrintWARNINGDefault()
|
|
||||||
engine := New()
|
|
||||||
engine.Use(Logger(), Recovery())
|
|
||||||
return engine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (engine *Engine) allocateContext() *Context {
|
|
||||||
return &Context{engine: engine}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (engine *Engine) Delims(left, right string) *Engine {
|
|
||||||
engine.delims = render.Delims{Left: left, Right: right}
|
|
||||||
return engine
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecureJsonPrefix sets the secureJsonPrefix used in Context.SecureJSON.
|
|
||||||
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
|
|
||||||
engine.secureJsonPrefix = prefix
|
|
||||||
return engine
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadHTMLGlob loads HTML files identified by glob pattern
|
|
||||||
// and associates the result with HTML renderer.
|
|
||||||
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
|
||||||
left := engine.delims.Left
|
|
||||||
right := engine.delims.Right
|
|
||||||
templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
|
|
||||||
|
|
||||||
if IsDebugging() {
|
|
||||||
debugPrintLoadTemplate(templ)
|
|
||||||
engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.SetHTMLTemplate(templ)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadHTMLFiles loads a slice of HTML files
|
|
||||||
// and associates the result with HTML renderer.
|
|
||||||
func (engine *Engine) LoadHTMLFiles(files ...string) {
|
|
||||||
if IsDebugging() {
|
|
||||||
engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
|
|
||||||
engine.SetHTMLTemplate(templ)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHTMLTemplate associate a template with HTML renderer.
|
|
||||||
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
|
||||||
if len(engine.trees) > 0 {
|
|
||||||
debugPrintWARNINGSetHTMLTemplate()
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFuncMap sets the FuncMap used for template.FuncMap.
|
|
||||||
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
|
|
||||||
engine.FuncMap = funcMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoRoute adds handlers for NoRoute. It return a 404 code by default.
|
|
||||||
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
|
|
||||||
engine.noRoute = handlers
|
|
||||||
engine.rebuild404Handlers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoMethod sets the handlers called when... TODO.
|
|
||||||
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
|
||||||
engine.noMethod = handlers
|
|
||||||
engine.rebuild405Handlers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use attachs a global middleware to the router. ie. the middleware attached though Use() will be
|
|
||||||
// included in the handlers chain for every single request. Even 404, 405, static files...
|
|
||||||
// For example, this is the right place for a logger or error management middleware.
|
|
||||||
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
|
|
||||||
engine.RouterGroup.Use(middleware...)
|
|
||||||
engine.rebuild404Handlers()
|
|
||||||
engine.rebuild405Handlers()
|
|
||||||
return engine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (engine *Engine) rebuild404Handlers() {
|
|
||||||
engine.allNoRoute = engine.combineHandlers(engine.noRoute)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (engine *Engine) rebuild405Handlers() {
|
|
||||||
engine.allNoMethod = engine.combineHandlers(engine.noMethod)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
|
|
||||||
assert1(path[0] == '/', "path must begin with '/'")
|
|
||||||
assert1(method != "", "HTTP method can not be empty")
|
|
||||||
assert1(len(handlers) > 0, "there must be at least one handler")
|
|
||||||
|
|
||||||
debugPrintRoute(method, path, handlers)
|
|
||||||
root := engine.trees.get(method)
|
|
||||||
if root == nil {
|
|
||||||
root = new(node)
|
|
||||||
engine.trees = append(engine.trees, methodTree{method: method, root: root})
|
|
||||||
}
|
|
||||||
root.addRoute(path, handlers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routes returns a slice of registered routes, including some useful information, such as:
|
|
||||||
// the http method, path and the handler name.
|
|
||||||
func (engine *Engine) Routes() (routes RoutesInfo) {
|
|
||||||
for _, tree := range engine.trees {
|
|
||||||
routes = iterate("", tree.method, routes, tree.root)
|
|
||||||
}
|
|
||||||
return routes
|
|
||||||
}
|
|
||||||
|
|
||||||
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
|
||||||
path += root.path
|
|
||||||
if len(root.handlers) > 0 {
|
|
||||||
routes = append(routes, RouteInfo{
|
|
||||||
Method: method,
|
|
||||||
Path: path,
|
|
||||||
Handler: nameOfFunction(root.handlers.Last()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, child := range root.children {
|
|
||||||
routes = iterate(path, method, routes, child)
|
|
||||||
}
|
|
||||||
return routes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
|
|
||||||
// It is a shortcut for http.ListenAndServe(addr, router)
|
|
||||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
|
||||||
func (engine *Engine) Run(addr ...string) (err error) {
|
|
||||||
defer func() { debugPrintError(err) }()
|
|
||||||
|
|
||||||
address := resolveAddress(addr)
|
|
||||||
debugPrint("Listening and serving HTTP on %s\n", address)
|
|
||||||
err = http.ListenAndServe(address, engine)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
|
|
||||||
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
|
||||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
|
||||||
func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
|
|
||||||
debugPrint("Listening and serving HTTPS on %s\n", addr)
|
|
||||||
defer func() { debugPrintError(err) }()
|
|
||||||
|
|
||||||
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
|
|
||||||
// through the specified unix socket (ie. a file).
|
|
||||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
|
||||||
func (engine *Engine) RunUnix(file string) (err error) {
|
|
||||||
debugPrint("Listening and serving HTTP on unix:/%s", file)
|
|
||||||
defer func() { debugPrintError(err) }()
|
|
||||||
|
|
||||||
os.Remove(file)
|
|
||||||
listener, err := net.Listen("unix", file)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
err = http.Serve(listener, engine)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP conforms to the http.Handler interface.
|
|
||||||
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
||||||
c := engine.pool.Get().(*Context)
|
|
||||||
c.writermem.reset(w)
|
|
||||||
c.Request = req
|
|
||||||
c.reset()
|
|
||||||
|
|
||||||
engine.handleHTTPRequest(c)
|
|
||||||
|
|
||||||
engine.pool.Put(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleContext re-enter a context that has been rewritten.
|
|
||||||
// This can be done by setting c.Request.URL.Path to your new target.
|
|
||||||
// Disclaimer: You can loop yourself to death with this, use wisely.
|
|
||||||
func (engine *Engine) HandleContext(c *Context) {
|
|
||||||
c.reset()
|
|
||||||
engine.handleHTTPRequest(c)
|
|
||||||
engine.pool.Put(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (engine *Engine) handleHTTPRequest(c *Context) {
|
|
||||||
httpMethod := c.Request.Method
|
|
||||||
path := c.Request.URL.Path
|
|
||||||
unescape := false
|
|
||||||
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
|
|
||||||
path = c.Request.URL.RawPath
|
|
||||||
unescape = engine.UnescapePathValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find root of the tree for the given HTTP method
|
|
||||||
t := engine.trees
|
|
||||||
for i, tl := 0, len(t); i < tl; i++ {
|
|
||||||
if t[i].method != httpMethod {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
root := t[i].root
|
|
||||||
// Find route in tree
|
|
||||||
handlers, params, tsr := root.getValue(path, c.Params, unescape)
|
|
||||||
if handlers != nil {
|
|
||||||
c.handlers = handlers
|
|
||||||
c.Params = params
|
|
||||||
c.Next()
|
|
||||||
c.writermem.WriteHeaderNow()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if httpMethod != "CONNECT" && path != "/" {
|
|
||||||
if tsr && engine.RedirectTrailingSlash {
|
|
||||||
redirectTrailingSlash(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if engine.HandleMethodNotAllowed {
|
|
||||||
for _, tree := range engine.trees {
|
|
||||||
if tree.method == httpMethod {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
|
|
||||||
c.handlers = engine.allNoMethod
|
|
||||||
serveError(c, http.StatusMethodNotAllowed, default405Body)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.handlers = engine.allNoRoute
|
|
||||||
serveError(c, http.StatusNotFound, default404Body)
|
|
||||||
}
|
|
||||||
|
|
||||||
var mimePlain = []string{MIMEPlain}
|
|
||||||
|
|
||||||
func serveError(c *Context, code int, defaultMessage []byte) {
|
|
||||||
c.writermem.status = code
|
|
||||||
c.Next()
|
|
||||||
if c.writermem.Written() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.writermem.Status() == code {
|
|
||||||
c.writermem.Header()["Content-Type"] = mimePlain
|
|
||||||
c.Writer.Write(defaultMessage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.writermem.WriteHeaderNow()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectTrailingSlash(c *Context) {
|
|
||||||
req := c.Request
|
|
||||||
path := req.URL.Path
|
|
||||||
code := http.StatusMovedPermanently // Permanent redirect, request with GET method
|
|
||||||
if req.Method != "GET" {
|
|
||||||
code = http.StatusTemporaryRedirect
|
|
||||||
}
|
|
||||||
|
|
||||||
req.URL.Path = path + "/"
|
|
||||||
if length := len(path); length > 1 && path[length-1] == '/' {
|
|
||||||
req.URL.Path = path[:length-1]
|
|
||||||
}
|
|
||||||
debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
|
|
||||||
http.Redirect(c.Writer, req, req.URL.String(), code)
|
|
||||||
c.writermem.WriteHeaderNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
|
|
||||||
req := c.Request
|
|
||||||
path := req.URL.Path
|
|
||||||
|
|
||||||
if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(path), trailingSlash); ok {
|
|
||||||
code := http.StatusMovedPermanently // Permanent redirect, request with GET method
|
|
||||||
if req.Method != "GET" {
|
|
||||||
code = http.StatusTemporaryRedirect
|
|
||||||
}
|
|
||||||
req.URL.Path = string(fixedPath)
|
|
||||||
debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
|
|
||||||
http.Redirect(c.Writer, req, req.URL.String(), code)
|
|
||||||
c.writermem.WriteHeaderNow()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
15
vendor/github.com/gin-gonic/gin/json/json.go
generated
vendored
15
vendor/github.com/gin-gonic/gin/json/json.go
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !jsoniter
|
|
||||||
|
|
||||||
package json
|
|
||||||
|
|
||||||
import "encoding/json"
|
|
||||||
|
|
||||||
var (
|
|
||||||
Marshal = json.Marshal
|
|
||||||
MarshalIndent = json.MarshalIndent
|
|
||||||
NewDecoder = json.NewDecoder
|
|
||||||
)
|
|
16
vendor/github.com/gin-gonic/gin/json/jsoniter.go
generated
vendored
16
vendor/github.com/gin-gonic/gin/json/jsoniter.go
generated
vendored
@ -1,16 +0,0 @@
|
|||||||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build jsoniter
|
|
||||||
|
|
||||||
package json
|
|
||||||
|
|
||||||
import "github.com/json-iterator/go"
|
|
||||||
|
|
||||||
var (
|
|
||||||
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
Marshal = json.Marshal
|
|
||||||
MarshalIndent = json.MarshalIndent
|
|
||||||
NewDecoder = json.NewDecoder
|
|
||||||
)
|
|
152
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
152
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
@ -1,152 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mattn/go-isatty"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
|
|
||||||
white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
|
|
||||||
yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
|
|
||||||
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
|
|
||||||
blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
|
|
||||||
magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
|
|
||||||
cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
|
|
||||||
reset = string([]byte{27, 91, 48, 109})
|
|
||||||
disableColor = false
|
|
||||||
)
|
|
||||||
|
|
||||||
// DisableConsoleColor disables color output in the console.
|
|
||||||
func DisableConsoleColor() {
|
|
||||||
disableColor = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorLogger returns a handlerfunc for any error type.
|
|
||||||
func ErrorLogger() HandlerFunc {
|
|
||||||
return ErrorLoggerT(ErrorTypeAny)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorLoggerT returns a handlerfunc for a given error type.
|
|
||||||
func ErrorLoggerT(typ ErrorType) HandlerFunc {
|
|
||||||
return func(c *Context) {
|
|
||||||
c.Next()
|
|
||||||
errors := c.Errors.ByType(typ)
|
|
||||||
if len(errors) > 0 {
|
|
||||||
c.JSON(-1, errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter.
|
|
||||||
// By default gin.DefaultWriter = os.Stdout.
|
|
||||||
func Logger() HandlerFunc {
|
|
||||||
return LoggerWithWriter(DefaultWriter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoggerWithWriter instance a Logger middleware with the specified writter buffer.
|
|
||||||
// Example: os.Stdout, a file opened in write mode, a socket...
|
|
||||||
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
|
||||||
isTerm := true
|
|
||||||
|
|
||||||
if w, ok := out.(*os.File); !ok ||
|
|
||||||
(os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) ||
|
|
||||||
disableColor {
|
|
||||||
isTerm = false
|
|
||||||
}
|
|
||||||
|
|
||||||
var skip map[string]struct{}
|
|
||||||
|
|
||||||
if length := len(notlogged); length > 0 {
|
|
||||||
skip = make(map[string]struct{}, length)
|
|
||||||
|
|
||||||
for _, path := range notlogged {
|
|
||||||
skip[path] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(c *Context) {
|
|
||||||
// Start timer
|
|
||||||
start := time.Now()
|
|
||||||
path := c.Request.URL.Path
|
|
||||||
raw := c.Request.URL.RawQuery
|
|
||||||
|
|
||||||
// Process request
|
|
||||||
c.Next()
|
|
||||||
|
|
||||||
// Log only when path is not being skipped
|
|
||||||
if _, ok := skip[path]; !ok {
|
|
||||||
// Stop timer
|
|
||||||
end := time.Now()
|
|
||||||
latency := end.Sub(start)
|
|
||||||
|
|
||||||
clientIP := c.ClientIP()
|
|
||||||
method := c.Request.Method
|
|
||||||
statusCode := c.Writer.Status()
|
|
||||||
var statusColor, methodColor, resetColor string
|
|
||||||
if isTerm {
|
|
||||||
statusColor = colorForStatus(statusCode)
|
|
||||||
methodColor = colorForMethod(method)
|
|
||||||
resetColor = reset
|
|
||||||
}
|
|
||||||
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
|
||||||
|
|
||||||
if raw != "" {
|
|
||||||
path = path + "?" + raw
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",
|
|
||||||
end.Format("2006/01/02 - 15:04:05"),
|
|
||||||
statusColor, statusCode, resetColor,
|
|
||||||
latency,
|
|
||||||
clientIP,
|
|
||||||
methodColor, method, resetColor,
|
|
||||||
path,
|
|
||||||
comment,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func colorForStatus(code int) string {
|
|
||||||
switch {
|
|
||||||
case code >= http.StatusOK && code < http.StatusMultipleChoices:
|
|
||||||
return green
|
|
||||||
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
|
|
||||||
return white
|
|
||||||
case code >= http.StatusBadRequest && code < http.StatusInternalServerError:
|
|
||||||
return yellow
|
|
||||||
default:
|
|
||||||
return red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func colorForMethod(method string) string {
|
|
||||||
switch method {
|
|
||||||
case "GET":
|
|
||||||
return blue
|
|
||||||
case "POST":
|
|
||||||
return cyan
|
|
||||||
case "PUT":
|
|
||||||
return yellow
|
|
||||||
case "DELETE":
|
|
||||||
return red
|
|
||||||
case "PATCH":
|
|
||||||
return green
|
|
||||||
case "HEAD":
|
|
||||||
return magenta
|
|
||||||
case "OPTIONS":
|
|
||||||
return white
|
|
||||||
default:
|
|
||||||
return reset
|
|
||||||
}
|
|
||||||
}
|
|
72
vendor/github.com/gin-gonic/gin/mode.go
generated
vendored
72
vendor/github.com/gin-gonic/gin/mode.go
generated
vendored
@ -1,72 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/binding"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ENV_GIN_MODE = "GIN_MODE"
|
|
||||||
|
|
||||||
const (
|
|
||||||
DebugMode = "debug"
|
|
||||||
ReleaseMode = "release"
|
|
||||||
TestMode = "test"
|
|
||||||
)
|
|
||||||
const (
|
|
||||||
debugCode = iota
|
|
||||||
releaseCode
|
|
||||||
testCode
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultWriter is the default io.Writer used the Gin for debug output and
|
|
||||||
// middleware output like Logger() or Recovery().
|
|
||||||
// Note that both Logger and Recovery provides custom ways to configure their
|
|
||||||
// output io.Writer.
|
|
||||||
// To support coloring in Windows use:
|
|
||||||
// import "github.com/mattn/go-colorable"
|
|
||||||
// gin.DefaultWriter = colorable.NewColorableStdout()
|
|
||||||
var DefaultWriter io.Writer = os.Stdout
|
|
||||||
var DefaultErrorWriter io.Writer = os.Stderr
|
|
||||||
|
|
||||||
var ginMode = debugCode
|
|
||||||
var modeName = DebugMode
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
mode := os.Getenv(ENV_GIN_MODE)
|
|
||||||
SetMode(mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetMode(value string) {
|
|
||||||
switch value {
|
|
||||||
case DebugMode, "":
|
|
||||||
ginMode = debugCode
|
|
||||||
case ReleaseMode:
|
|
||||||
ginMode = releaseCode
|
|
||||||
case TestMode:
|
|
||||||
ginMode = testCode
|
|
||||||
default:
|
|
||||||
panic("gin mode unknown: " + value)
|
|
||||||
}
|
|
||||||
if value == "" {
|
|
||||||
value = DebugMode
|
|
||||||
}
|
|
||||||
modeName = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func DisableBindValidation() {
|
|
||||||
binding.Validator = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func EnableJsonDecoderUseNumber() {
|
|
||||||
binding.EnableDecoderUseNumber = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mode() string {
|
|
||||||
return modeName
|
|
||||||
}
|
|
123
vendor/github.com/gin-gonic/gin/path.go
generated
vendored
123
vendor/github.com/gin-gonic/gin/path.go
generated
vendored
@ -1,123 +0,0 @@
|
|||||||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
|
||||||
// Based on the path package, Copyright 2009 The Go Authors.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be found
|
|
||||||
// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
// cleanPath is the URL version of path.Clean, it returns a canonical URL path
|
|
||||||
// for p, eliminating . and .. elements.
|
|
||||||
//
|
|
||||||
// The following rules are applied iteratively until no further processing can
|
|
||||||
// be done:
|
|
||||||
// 1. Replace multiple slashes with a single slash.
|
|
||||||
// 2. Eliminate each . path name element (the current directory).
|
|
||||||
// 3. Eliminate each inner .. path name element (the parent directory)
|
|
||||||
// along with the non-.. element that precedes it.
|
|
||||||
// 4. Eliminate .. elements that begin a rooted path:
|
|
||||||
// that is, replace "/.." by "/" at the beginning of a path.
|
|
||||||
//
|
|
||||||
// If the result of this process is an empty string, "/" is returned.
|
|
||||||
func cleanPath(p string) string {
|
|
||||||
// Turn empty string into "/"
|
|
||||||
if p == "" {
|
|
||||||
return "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
n := len(p)
|
|
||||||
var buf []byte
|
|
||||||
|
|
||||||
// Invariants:
|
|
||||||
// reading from path; r is index of next byte to process.
|
|
||||||
// writing to buf; w is index of next byte to write.
|
|
||||||
|
|
||||||
// path must start with '/'
|
|
||||||
r := 1
|
|
||||||
w := 1
|
|
||||||
|
|
||||||
if p[0] != '/' {
|
|
||||||
r = 0
|
|
||||||
buf = make([]byte, n+1)
|
|
||||||
buf[0] = '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
trailing := n > 1 && p[n-1] == '/'
|
|
||||||
|
|
||||||
// A bit more clunky without a 'lazybuf' like the path package, but the loop
|
|
||||||
// gets completely inlined (bufApp). So in contrast to the path package this
|
|
||||||
// loop has no expensive function calls (except 1x make)
|
|
||||||
|
|
||||||
for r < n {
|
|
||||||
switch {
|
|
||||||
case p[r] == '/':
|
|
||||||
// empty path element, trailing slash is added after the end
|
|
||||||
r++
|
|
||||||
|
|
||||||
case p[r] == '.' && r+1 == n:
|
|
||||||
trailing = true
|
|
||||||
r++
|
|
||||||
|
|
||||||
case p[r] == '.' && p[r+1] == '/':
|
|
||||||
// . element
|
|
||||||
r += 2
|
|
||||||
|
|
||||||
case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'):
|
|
||||||
// .. element: remove to last /
|
|
||||||
r += 3
|
|
||||||
|
|
||||||
if w > 1 {
|
|
||||||
// can backtrack
|
|
||||||
w--
|
|
||||||
|
|
||||||
if buf == nil {
|
|
||||||
for w > 1 && p[w] != '/' {
|
|
||||||
w--
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for w > 1 && buf[w] != '/' {
|
|
||||||
w--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// real path element.
|
|
||||||
// add slash if needed
|
|
||||||
if w > 1 {
|
|
||||||
bufApp(&buf, p, w, '/')
|
|
||||||
w++
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy element
|
|
||||||
for r < n && p[r] != '/' {
|
|
||||||
bufApp(&buf, p, w, p[r])
|
|
||||||
w++
|
|
||||||
r++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-append trailing slash
|
|
||||||
if trailing && w > 1 {
|
|
||||||
bufApp(&buf, p, w, '/')
|
|
||||||
w++
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf == nil {
|
|
||||||
return p[:w]
|
|
||||||
}
|
|
||||||
return string(buf[:w])
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal helper to lazily create a buffer if necessary.
|
|
||||||
func bufApp(buf *[]byte, s string, w int, c byte) {
|
|
||||||
if *buf == nil {
|
|
||||||
if s[w] == c {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*buf = make([]byte, len(s))
|
|
||||||
copy(*buf, s[:w])
|
|
||||||
}
|
|
||||||
(*buf)[w] = c
|
|
||||||
}
|
|
116
vendor/github.com/gin-gonic/gin/recovery.go
generated
vendored
116
vendor/github.com/gin-gonic/gin/recovery.go
generated
vendored
@ -1,116 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
dunno = []byte("???")
|
|
||||||
centerDot = []byte("·")
|
|
||||||
dot = []byte(".")
|
|
||||||
slash = []byte("/")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
|
|
||||||
func Recovery() HandlerFunc {
|
|
||||||
return RecoveryWithWriter(DefaultErrorWriter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
|
|
||||||
func RecoveryWithWriter(out io.Writer) HandlerFunc {
|
|
||||||
var logger *log.Logger
|
|
||||||
if out != nil {
|
|
||||||
logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
|
|
||||||
}
|
|
||||||
return func(c *Context) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
if logger != nil {
|
|
||||||
stack := stack(3)
|
|
||||||
httprequest, _ := httputil.DumpRequest(c.Request, false)
|
|
||||||
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", timeFormat(time.Now()), string(httprequest), err, stack, reset)
|
|
||||||
}
|
|
||||||
c.AbortWithStatus(http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stack returns a nicely formatted stack frame, skipping skip frames.
|
|
||||||
func stack(skip int) []byte {
|
|
||||||
buf := new(bytes.Buffer) // the returned data
|
|
||||||
// As we loop, we open files and read them. These variables record the currently
|
|
||||||
// loaded file.
|
|
||||||
var lines [][]byte
|
|
||||||
var lastFile string
|
|
||||||
for i := skip; ; i++ { // Skip the expected number of frames
|
|
||||||
pc, file, line, ok := runtime.Caller(i)
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Print this much at least. If we can't find the source, it won't show.
|
|
||||||
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
|
|
||||||
if file != lastFile {
|
|
||||||
data, err := ioutil.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lines = bytes.Split(data, []byte{'\n'})
|
|
||||||
lastFile = file
|
|
||||||
}
|
|
||||||
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
|
|
||||||
}
|
|
||||||
return buf.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// source returns a space-trimmed slice of the n'th line.
|
|
||||||
func source(lines [][]byte, n int) []byte {
|
|
||||||
n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
|
|
||||||
if n < 0 || n >= len(lines) {
|
|
||||||
return dunno
|
|
||||||
}
|
|
||||||
return bytes.TrimSpace(lines[n])
|
|
||||||
}
|
|
||||||
|
|
||||||
// function returns, if possible, the name of the function containing the PC.
|
|
||||||
func function(pc uintptr) []byte {
|
|
||||||
fn := runtime.FuncForPC(pc)
|
|
||||||
if fn == nil {
|
|
||||||
return dunno
|
|
||||||
}
|
|
||||||
name := []byte(fn.Name())
|
|
||||||
// The name includes the path name to the package, which is unnecessary
|
|
||||||
// since the file name is already included. Plus, it has center dots.
|
|
||||||
// That is, we see
|
|
||||||
// runtime/debug.*T·ptrmethod
|
|
||||||
// and want
|
|
||||||
// *T.ptrmethod
|
|
||||||
// Also the package path might contains dot (e.g. code.google.com/...),
|
|
||||||
// so first eliminate the path prefix
|
|
||||||
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
|
|
||||||
name = name[lastslash+1:]
|
|
||||||
}
|
|
||||||
if period := bytes.Index(name, dot); period >= 0 {
|
|
||||||
name = name[period+1:]
|
|
||||||
}
|
|
||||||
name = bytes.Replace(name, centerDot, dot, -1)
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
func timeFormat(t time.Time) string {
|
|
||||||
var timeString = t.Format("2006/01/02 - 15:04:05")
|
|
||||||
return timeString
|
|
||||||
}
|
|
23
vendor/github.com/gin-gonic/gin/render/data.go
generated
vendored
23
vendor/github.com/gin-gonic/gin/render/data.go
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type Data struct {
|
|
||||||
ContentType string
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render (Data) writes data with custom ContentType.
|
|
||||||
func (r Data) Render(w http.ResponseWriter) (err error) {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
_, err = w.Write(r.Data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Data) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, []string{r.ContentType})
|
|
||||||
}
|
|
80
vendor/github.com/gin-gonic/gin/render/html.go
generated
vendored
80
vendor/github.com/gin-gonic/gin/render/html.go
generated
vendored
@ -1,80 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Delims struct {
|
|
||||||
Left string
|
|
||||||
Right string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTMLRender interface {
|
|
||||||
Instance(string, interface{}) Render
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTMLProduction struct {
|
|
||||||
Template *template.Template
|
|
||||||
Delims Delims
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTMLDebug struct {
|
|
||||||
Files []string
|
|
||||||
Glob string
|
|
||||||
Delims Delims
|
|
||||||
FuncMap template.FuncMap
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTML struct {
|
|
||||||
Template *template.Template
|
|
||||||
Name string
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var htmlContentType = []string{"text/html; charset=utf-8"}
|
|
||||||
|
|
||||||
func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
|
||||||
return HTML{
|
|
||||||
Template: r.Template,
|
|
||||||
Name: name,
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r HTMLDebug) Instance(name string, data interface{}) Render {
|
|
||||||
return HTML{
|
|
||||||
Template: r.loadTemplate(),
|
|
||||||
Name: name,
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (r HTMLDebug) loadTemplate() *template.Template {
|
|
||||||
if r.FuncMap == nil {
|
|
||||||
r.FuncMap = template.FuncMap{}
|
|
||||||
}
|
|
||||||
if len(r.Files) > 0 {
|
|
||||||
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...))
|
|
||||||
}
|
|
||||||
if r.Glob != "" {
|
|
||||||
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob))
|
|
||||||
}
|
|
||||||
panic("the HTML debug render was created without files or glob pattern")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r HTML) Render(w http.ResponseWriter) error {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
|
|
||||||
if r.Name == "" {
|
|
||||||
return r.Template.Execute(w, r.Data)
|
|
||||||
}
|
|
||||||
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r HTML) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, htmlContentType)
|
|
||||||
}
|
|
146
vendor/github.com/gin-gonic/gin/render/json.go
generated
vendored
146
vendor/github.com/gin-gonic/gin/render/json.go
generated
vendored
@ -1,146 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JSON struct {
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndentedJSON struct {
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SecureJSON struct {
|
|
||||||
Prefix string
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonpJSON struct {
|
|
||||||
Callback string
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type AsciiJSON struct {
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SecureJSONPrefix string
|
|
||||||
|
|
||||||
var jsonContentType = []string{"application/json; charset=utf-8"}
|
|
||||||
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
|
|
||||||
var jsonAsciiContentType = []string{"application/json"}
|
|
||||||
|
|
||||||
func (r JSON) Render(w http.ResponseWriter) (err error) {
|
|
||||||
if err = WriteJSON(w, r.Data); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r JSON) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, jsonContentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
|
|
||||||
writeContentType(w, jsonContentType)
|
|
||||||
jsonBytes, err := json.Marshal(obj)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.Write(jsonBytes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.Write(jsonBytes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, jsonContentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SecureJSON) Render(w http.ResponseWriter) error {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
jsonBytes, err := json.Marshal(r.Data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// if the jsonBytes is array values
|
|
||||||
if bytes.HasPrefix(jsonBytes, []byte("[")) && bytes.HasSuffix(jsonBytes, []byte("]")) {
|
|
||||||
w.Write([]byte(r.Prefix))
|
|
||||||
}
|
|
||||||
w.Write(jsonBytes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, jsonContentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
ret, err := json.Marshal(r.Data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Callback == "" {
|
|
||||||
w.Write(ret)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
callback := template.JSEscapeString(r.Callback)
|
|
||||||
w.Write([]byte(callback))
|
|
||||||
w.Write([]byte("("))
|
|
||||||
w.Write(ret)
|
|
||||||
w.Write([]byte(")"))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, jsonpContentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
ret, err := json.Marshal(r.Data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
for _, r := range string(ret) {
|
|
||||||
cvt := ""
|
|
||||||
if r < 128 {
|
|
||||||
cvt = string(r)
|
|
||||||
} else {
|
|
||||||
cvt = fmt.Sprintf("\\u%04x", int64(r))
|
|
||||||
}
|
|
||||||
buffer.WriteString(cvt)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(buffer.Bytes())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, jsonAsciiContentType)
|
|
||||||
}
|
|
31
vendor/github.com/gin-gonic/gin/render/msgpack.go
generated
vendored
31
vendor/github.com/gin-gonic/gin/render/msgpack.go
generated
vendored
@ -1,31 +0,0 @@
|
|||||||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/ugorji/go/codec"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MsgPack struct {
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
|
|
||||||
|
|
||||||
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, msgpackContentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r MsgPack) Render(w http.ResponseWriter) error {
|
|
||||||
return WriteMsgPack(w, r.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
|
|
||||||
writeContentType(w, msgpackContentType)
|
|
||||||
var h codec.Handle = new(codec.MsgpackHandle)
|
|
||||||
return codec.NewEncoder(w, h).Encode(obj)
|
|
||||||
}
|
|
36
vendor/github.com/gin-gonic/gin/render/reader.go
generated
vendored
36
vendor/github.com/gin-gonic/gin/render/reader.go
generated
vendored
@ -1,36 +0,0 @@
|
|||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Reader struct {
|
|
||||||
ContentType string
|
|
||||||
ContentLength int64
|
|
||||||
Reader io.Reader
|
|
||||||
Headers map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render (Reader) writes data with custom ContentType and headers.
|
|
||||||
func (r Reader) Render(w http.ResponseWriter) (err error) {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10)
|
|
||||||
r.writeHeaders(w, r.Headers)
|
|
||||||
_, err = io.Copy(w, r.Reader)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Reader) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, []string{r.ContentType})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
|
|
||||||
header := w.Header()
|
|
||||||
for k, v := range headers {
|
|
||||||
if val := header[k]; len(val) == 0 {
|
|
||||||
header[k] = []string{v}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
28
vendor/github.com/gin-gonic/gin/render/redirect.go
generated
vendored
28
vendor/github.com/gin-gonic/gin/render/redirect.go
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Redirect struct {
|
|
||||||
Code int
|
|
||||||
Request *http.Request
|
|
||||||
Location string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Redirect) Render(w http.ResponseWriter) error {
|
|
||||||
// todo(thinkerou): go1.6 not support StatusPermanentRedirect(308)
|
|
||||||
// when we upgrade go version we can use http.StatusPermanentRedirect
|
|
||||||
if (r.Code < 300 || r.Code > 308) && r.Code != 201 {
|
|
||||||
panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code))
|
|
||||||
}
|
|
||||||
http.Redirect(w, r.Request, r.Location, r.Code)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Redirect) WriteContentType(http.ResponseWriter) {}
|
|
37
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
37
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
@ -1,37 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type Render interface {
|
|
||||||
Render(http.ResponseWriter) error
|
|
||||||
WriteContentType(w http.ResponseWriter)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ Render = JSON{}
|
|
||||||
_ Render = IndentedJSON{}
|
|
||||||
_ Render = SecureJSON{}
|
|
||||||
_ Render = JsonpJSON{}
|
|
||||||
_ Render = XML{}
|
|
||||||
_ Render = String{}
|
|
||||||
_ Render = Redirect{}
|
|
||||||
_ Render = Data{}
|
|
||||||
_ Render = HTML{}
|
|
||||||
_ HTMLRender = HTMLDebug{}
|
|
||||||
_ HTMLRender = HTMLProduction{}
|
|
||||||
_ Render = YAML{}
|
|
||||||
_ Render = MsgPack{}
|
|
||||||
_ Render = Reader{}
|
|
||||||
_ Render = AsciiJSON{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func writeContentType(w http.ResponseWriter, value []string) {
|
|
||||||
header := w.Header()
|
|
||||||
if val := header["Content-Type"]; len(val) == 0 {
|
|
||||||
header["Content-Type"] = value
|
|
||||||
}
|
|
||||||
}
|
|
36
vendor/github.com/gin-gonic/gin/render/text.go
generated
vendored
36
vendor/github.com/gin-gonic/gin/render/text.go
generated
vendored
@ -1,36 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type String struct {
|
|
||||||
Format string
|
|
||||||
Data []interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var plainContentType = []string{"text/plain; charset=utf-8"}
|
|
||||||
|
|
||||||
func (r String) Render(w http.ResponseWriter) error {
|
|
||||||
WriteString(w, r.Format, r.Data)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r String) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, plainContentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
|
|
||||||
writeContentType(w, plainContentType)
|
|
||||||
if len(data) > 0 {
|
|
||||||
fmt.Fprintf(w, format, data...)
|
|
||||||
} else {
|
|
||||||
io.WriteString(w, format)
|
|
||||||
}
|
|
||||||
}
|
|
25
vendor/github.com/gin-gonic/gin/render/xml.go
generated
vendored
25
vendor/github.com/gin-gonic/gin/render/xml.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/xml"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type XML struct {
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var xmlContentType = []string{"application/xml; charset=utf-8"}
|
|
||||||
|
|
||||||
func (r XML) Render(w http.ResponseWriter) error {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
return xml.NewEncoder(w).Encode(r.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r XML) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, xmlContentType)
|
|
||||||
}
|
|
33
vendor/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
33
vendor/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
@ -1,33 +0,0 @@
|
|||||||
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type YAML struct {
|
|
||||||
Data interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
|
||||||
|
|
||||||
func (r YAML) Render(w http.ResponseWriter) error {
|
|
||||||
r.WriteContentType(w)
|
|
||||||
|
|
||||||
bytes, err := yaml.Marshal(r.Data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(bytes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r YAML) WriteContentType(w http.ResponseWriter) {
|
|
||||||
writeContentType(w, yamlContentType)
|
|
||||||
}
|
|
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