From dd5ccc2ec1326b695b16378a901f56705d155f09 Mon Sep 17 00:00:00 2001 From: Robin Yue Date: Wed, 22 Apr 2020 18:36:51 +0800 Subject: [PATCH 1/5] chore(vendor): switch to go mod and add vendor --- Gopkg.toml | 38 - go.mod | 8 + go.sum | 123 ++ vendor/github.com/caicloud/nirvana/.gitignore | 1 + .../github.com/caicloud/nirvana/.golangci.yml | 25 + .../caicloud/nirvana/.pre-commit-config.yaml | 6 + .../github.com/caicloud/nirvana/.travis.yml | 15 + .../caicloud/nirvana/CONTRIBUTING.md | 108 + vendor/github.com/caicloud/nirvana/LICENSE | 202 ++ vendor/github.com/caicloud/nirvana/Makefile | 37 + vendor/github.com/caicloud/nirvana/OWNERS | 7 + vendor/github.com/caicloud/nirvana/README.md | 50 + vendor/github.com/caicloud/nirvana/ROADMAP.md | 36 + vendor/github.com/caicloud/nirvana/banner.go | 66 + .../caicloud/nirvana/definition/definition.go | 245 +++ .../caicloud/nirvana/definition/helper.go | 260 +++ .../caicloud/nirvana/errors/errors.go | 197 ++ .../caicloud/nirvana/errors/external.go | 90 + .../caicloud/nirvana/errors/kinds.go | 100 + vendor/github.com/caicloud/nirvana/go.mod | 39 + vendor/github.com/caicloud/nirvana/go.sum | 173 ++ .../caicloud/nirvana/log/default.go | 135 ++ vendor/github.com/caicloud/nirvana/log/log.go | 104 + .../github.com/caicloud/nirvana/log/silent.go | 88 + vendor/github.com/caicloud/nirvana/log/std.go | 189 ++ vendor/github.com/caicloud/nirvana/nirvana.go | 388 ++++ .../nirvana/operators/validator/tags.go | 124 ++ .../nirvana/operators/validator/validator.go | 258 +++ .../caicloud/nirvana/plugins/reqlog/reqlog.go | 312 +++ .../caicloud/nirvana/service/builder.go | 344 ++++ .../caicloud/nirvana/service/content.go | 730 +++++++ .../caicloud/nirvana/service/context.go | 288 +++ .../caicloud/nirvana/service/executor.go | 515 +++++ .../caicloud/nirvana/service/filter.go | 180 ++ .../caicloud/nirvana/service/handler.go | 238 +++ .../caicloud/nirvana/service/method.go | 78 + .../caicloud/nirvana/service/modifier.go | 157 ++ .../caicloud/nirvana/service/router/basic.go | 269 +++ .../nirvana/service/router/executor.go | 56 + .../caicloud/nirvana/service/router/path.go | 65 + .../caicloud/nirvana/service/router/regexp.go | 176 ++ .../caicloud/nirvana/service/router/router.go | 344 ++++ .../caicloud/nirvana/service/router/string.go | 117 ++ .../caicloud/nirvana/service/router/utils.go | 88 + .../caicloud/nirvana/service/source.go | 556 ++++++ .../caicloud/nirvana/service/utils.go | 162 ++ .../caicloud/nirvana/utils/api/analyzer.go | 159 ++ .../caicloud/nirvana/utils/api/api.go | 148 ++ .../caicloud/nirvana/utils/api/comments.go | 212 ++ .../caicloud/nirvana/utils/api/definitions.go | 188 ++ .../caicloud/nirvana/utils/api/types.go | 309 +++ .../go-playground/locales/.gitignore | 24 + .../github.com/go-playground/locales/LICENSE | 21 + .../go-playground/locales/README.md | 172 ++ .../locales/currency/currency.go | 308 +++ .../github.com/go-playground/locales/logo.png | Bin 0 -> 37360 bytes .../github.com/go-playground/locales/rules.go | 293 +++ .../universal-translator/.gitignore | 24 + .../universal-translator/LICENSE | 21 + .../universal-translator/README.md | 90 + .../universal-translator/errors.go | 148 ++ .../universal-translator/import_export.go | 274 +++ .../universal-translator/logo.png | Bin 0 -> 16598 bytes .../universal-translator/translator.go | 420 ++++ .../universal_translator.go | 113 ++ vendor/github.com/spf13/pflag/.gitignore | 2 + vendor/github.com/spf13/pflag/.travis.yml | 22 + vendor/github.com/spf13/pflag/LICENSE | 28 + vendor/github.com/spf13/pflag/README.md | 296 +++ vendor/github.com/spf13/pflag/bool.go | 94 + vendor/github.com/spf13/pflag/bool_slice.go | 185 ++ vendor/github.com/spf13/pflag/bytes.go | 209 ++ vendor/github.com/spf13/pflag/count.go | 96 + vendor/github.com/spf13/pflag/duration.go | 86 + .../github.com/spf13/pflag/duration_slice.go | 166 ++ vendor/github.com/spf13/pflag/flag.go | 1239 ++++++++++++ vendor/github.com/spf13/pflag/float32.go | 88 + .../github.com/spf13/pflag/float32_slice.go | 174 ++ vendor/github.com/spf13/pflag/float64.go | 84 + .../github.com/spf13/pflag/float64_slice.go | 166 ++ vendor/github.com/spf13/pflag/go.mod | 3 + vendor/github.com/spf13/pflag/go.sum | 0 vendor/github.com/spf13/pflag/golangflag.go | 105 + vendor/github.com/spf13/pflag/int.go | 84 + vendor/github.com/spf13/pflag/int16.go | 88 + vendor/github.com/spf13/pflag/int32.go | 88 + vendor/github.com/spf13/pflag/int32_slice.go | 174 ++ vendor/github.com/spf13/pflag/int64.go | 84 + vendor/github.com/spf13/pflag/int64_slice.go | 166 ++ vendor/github.com/spf13/pflag/int8.go | 88 + vendor/github.com/spf13/pflag/int_slice.go | 158 ++ vendor/github.com/spf13/pflag/ip.go | 94 + vendor/github.com/spf13/pflag/ip_slice.go | 186 ++ vendor/github.com/spf13/pflag/ipmask.go | 122 ++ vendor/github.com/spf13/pflag/ipnet.go | 98 + vendor/github.com/spf13/pflag/string.go | 80 + vendor/github.com/spf13/pflag/string_array.go | 129 ++ vendor/github.com/spf13/pflag/string_slice.go | 163 ++ .../github.com/spf13/pflag/string_to_int.go | 149 ++ .../github.com/spf13/pflag/string_to_int64.go | 149 ++ .../spf13/pflag/string_to_string.go | 160 ++ vendor/github.com/spf13/pflag/uint.go | 88 + vendor/github.com/spf13/pflag/uint16.go | 88 + vendor/github.com/spf13/pflag/uint32.go | 88 + vendor/github.com/spf13/pflag/uint64.go | 88 + vendor/github.com/spf13/pflag/uint8.go | 88 + vendor/github.com/spf13/pflag/uint_slice.go | 168 ++ .../go-playground/validator.v9/.gitignore | 29 + .../go-playground/validator.v9/LICENSE | 22 + .../go-playground/validator.v9/Makefile | 16 + .../go-playground/validator.v9/README.md | 153 ++ .../go-playground/validator.v9/baked_in.go | 1741 +++++++++++++++++ .../go-playground/validator.v9/cache.go | 337 ++++ .../go-playground/validator.v9/doc.go | 942 +++++++++ .../go-playground/validator.v9/errors.go | 272 +++ .../go-playground/validator.v9/field_level.go | 69 + .../go-playground/validator.v9/logo.png | Bin 0 -> 13443 bytes .../go-playground/validator.v9/regexes.go | 81 + .../validator.v9/struct_level.go | 175 ++ .../validator.v9/translations.go | 11 + .../go-playground/validator.v9/util.go | 257 +++ .../go-playground/validator.v9/validator.go | 475 +++++ .../validator.v9/validator_instance.go | 586 ++++++ vendor/modules.txt | 19 + 124 files changed, 21769 insertions(+), 38 deletions(-) delete mode 100644 Gopkg.toml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 vendor/github.com/caicloud/nirvana/.gitignore create mode 100644 vendor/github.com/caicloud/nirvana/.golangci.yml create mode 100644 vendor/github.com/caicloud/nirvana/.pre-commit-config.yaml create mode 100644 vendor/github.com/caicloud/nirvana/.travis.yml create mode 100644 vendor/github.com/caicloud/nirvana/CONTRIBUTING.md create mode 100644 vendor/github.com/caicloud/nirvana/LICENSE create mode 100644 vendor/github.com/caicloud/nirvana/Makefile create mode 100644 vendor/github.com/caicloud/nirvana/OWNERS create mode 100644 vendor/github.com/caicloud/nirvana/README.md create mode 100644 vendor/github.com/caicloud/nirvana/ROADMAP.md create mode 100644 vendor/github.com/caicloud/nirvana/banner.go create mode 100644 vendor/github.com/caicloud/nirvana/definition/definition.go create mode 100644 vendor/github.com/caicloud/nirvana/definition/helper.go create mode 100644 vendor/github.com/caicloud/nirvana/errors/errors.go create mode 100644 vendor/github.com/caicloud/nirvana/errors/external.go create mode 100644 vendor/github.com/caicloud/nirvana/errors/kinds.go create mode 100644 vendor/github.com/caicloud/nirvana/go.mod create mode 100644 vendor/github.com/caicloud/nirvana/go.sum create mode 100644 vendor/github.com/caicloud/nirvana/log/default.go create mode 100644 vendor/github.com/caicloud/nirvana/log/log.go create mode 100644 vendor/github.com/caicloud/nirvana/log/silent.go create mode 100644 vendor/github.com/caicloud/nirvana/log/std.go create mode 100644 vendor/github.com/caicloud/nirvana/nirvana.go create mode 100644 vendor/github.com/caicloud/nirvana/operators/validator/tags.go create mode 100644 vendor/github.com/caicloud/nirvana/operators/validator/validator.go create mode 100644 vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go create mode 100644 vendor/github.com/caicloud/nirvana/service/builder.go create mode 100644 vendor/github.com/caicloud/nirvana/service/content.go create mode 100644 vendor/github.com/caicloud/nirvana/service/context.go create mode 100644 vendor/github.com/caicloud/nirvana/service/executor.go create mode 100644 vendor/github.com/caicloud/nirvana/service/filter.go create mode 100644 vendor/github.com/caicloud/nirvana/service/handler.go create mode 100644 vendor/github.com/caicloud/nirvana/service/method.go create mode 100644 vendor/github.com/caicloud/nirvana/service/modifier.go create mode 100644 vendor/github.com/caicloud/nirvana/service/router/basic.go create mode 100644 vendor/github.com/caicloud/nirvana/service/router/executor.go create mode 100644 vendor/github.com/caicloud/nirvana/service/router/path.go create mode 100644 vendor/github.com/caicloud/nirvana/service/router/regexp.go create mode 100644 vendor/github.com/caicloud/nirvana/service/router/router.go create mode 100644 vendor/github.com/caicloud/nirvana/service/router/string.go create mode 100644 vendor/github.com/caicloud/nirvana/service/router/utils.go create mode 100644 vendor/github.com/caicloud/nirvana/service/source.go create mode 100644 vendor/github.com/caicloud/nirvana/service/utils.go create mode 100644 vendor/github.com/caicloud/nirvana/utils/api/analyzer.go create mode 100644 vendor/github.com/caicloud/nirvana/utils/api/api.go create mode 100644 vendor/github.com/caicloud/nirvana/utils/api/comments.go create mode 100644 vendor/github.com/caicloud/nirvana/utils/api/definitions.go create mode 100644 vendor/github.com/caicloud/nirvana/utils/api/types.go create mode 100644 vendor/github.com/go-playground/locales/.gitignore create mode 100644 vendor/github.com/go-playground/locales/LICENSE create mode 100644 vendor/github.com/go-playground/locales/README.md create mode 100644 vendor/github.com/go-playground/locales/currency/currency.go create mode 100644 vendor/github.com/go-playground/locales/logo.png create mode 100644 vendor/github.com/go-playground/locales/rules.go create mode 100644 vendor/github.com/go-playground/universal-translator/.gitignore create mode 100644 vendor/github.com/go-playground/universal-translator/LICENSE create mode 100644 vendor/github.com/go-playground/universal-translator/README.md create mode 100644 vendor/github.com/go-playground/universal-translator/errors.go create mode 100644 vendor/github.com/go-playground/universal-translator/import_export.go create mode 100644 vendor/github.com/go-playground/universal-translator/logo.png create mode 100644 vendor/github.com/go-playground/universal-translator/translator.go create mode 100644 vendor/github.com/go-playground/universal-translator/universal_translator.go create mode 100644 vendor/github.com/spf13/pflag/.gitignore create mode 100644 vendor/github.com/spf13/pflag/.travis.yml create mode 100644 vendor/github.com/spf13/pflag/LICENSE create mode 100644 vendor/github.com/spf13/pflag/README.md create mode 100644 vendor/github.com/spf13/pflag/bool.go create mode 100644 vendor/github.com/spf13/pflag/bool_slice.go create mode 100644 vendor/github.com/spf13/pflag/bytes.go create mode 100644 vendor/github.com/spf13/pflag/count.go create mode 100644 vendor/github.com/spf13/pflag/duration.go create mode 100644 vendor/github.com/spf13/pflag/duration_slice.go create mode 100644 vendor/github.com/spf13/pflag/flag.go create mode 100644 vendor/github.com/spf13/pflag/float32.go create mode 100644 vendor/github.com/spf13/pflag/float32_slice.go create mode 100644 vendor/github.com/spf13/pflag/float64.go create mode 100644 vendor/github.com/spf13/pflag/float64_slice.go create mode 100644 vendor/github.com/spf13/pflag/go.mod create mode 100644 vendor/github.com/spf13/pflag/go.sum create mode 100644 vendor/github.com/spf13/pflag/golangflag.go create mode 100644 vendor/github.com/spf13/pflag/int.go create mode 100644 vendor/github.com/spf13/pflag/int16.go create mode 100644 vendor/github.com/spf13/pflag/int32.go create mode 100644 vendor/github.com/spf13/pflag/int32_slice.go create mode 100644 vendor/github.com/spf13/pflag/int64.go create mode 100644 vendor/github.com/spf13/pflag/int64_slice.go create mode 100644 vendor/github.com/spf13/pflag/int8.go create mode 100644 vendor/github.com/spf13/pflag/int_slice.go create mode 100644 vendor/github.com/spf13/pflag/ip.go create mode 100644 vendor/github.com/spf13/pflag/ip_slice.go create mode 100644 vendor/github.com/spf13/pflag/ipmask.go create mode 100644 vendor/github.com/spf13/pflag/ipnet.go create mode 100644 vendor/github.com/spf13/pflag/string.go create mode 100644 vendor/github.com/spf13/pflag/string_array.go create mode 100644 vendor/github.com/spf13/pflag/string_slice.go create mode 100644 vendor/github.com/spf13/pflag/string_to_int.go create mode 100644 vendor/github.com/spf13/pflag/string_to_int64.go create mode 100644 vendor/github.com/spf13/pflag/string_to_string.go create mode 100644 vendor/github.com/spf13/pflag/uint.go create mode 100644 vendor/github.com/spf13/pflag/uint16.go create mode 100644 vendor/github.com/spf13/pflag/uint32.go create mode 100644 vendor/github.com/spf13/pflag/uint64.go create mode 100644 vendor/github.com/spf13/pflag/uint8.go create mode 100644 vendor/github.com/spf13/pflag/uint_slice.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/.gitignore create mode 100644 vendor/gopkg.in/go-playground/validator.v9/LICENSE create mode 100644 vendor/gopkg.in/go-playground/validator.v9/Makefile create mode 100644 vendor/gopkg.in/go-playground/validator.v9/README.md create mode 100644 vendor/gopkg.in/go-playground/validator.v9/baked_in.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/cache.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/doc.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/errors.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/field_level.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/logo.png create mode 100644 vendor/gopkg.in/go-playground/validator.v9/regexes.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/struct_level.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/translations.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/util.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/validator.go create mode 100644 vendor/gopkg.in/go-playground/validator.v9/validator_instance.go create mode 100644 vendor/modules.txt diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index b320b89..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,38 +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 - -[[constraint]] - name = "github.com/caicloud/nirvana" - branch = "master" - -[[constraint]] - name = "github.com/spf13/pflag" - branch = "master" - -[prune] - non-go = true - go-tests = true - #unused-packages = true diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b26cf65 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/caicloud/nirvana-practice + +go 1.13 + +require ( + github.com/caicloud/nirvana v0.2.3 + github.com/spf13/pflag v1.0.5 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3383aab --- /dev/null +++ b/go.sum @@ -0,0 +1,123 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/caicloud/nirvana v0.2.3 h1:/uGk7DswiFyvqGNYre+B7LS8lv4Bw/vAuUptDxbE6Rs= +github.com/caicloud/nirvana v0.2.3/go.mod h1:VixZq2ALgoVR8VyuiM+dxuX6Izk3T/NwlSMa1aSrE7Q= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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.v9 v9.17.1 h1:+BgStg92rQ9TTsN/QTKUMyptg16ZZ+h88BlX4wCi+z4= +gopkg.in/go-playground/validator.v9 v9.17.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/caicloud/nirvana/.gitignore b/vendor/github.com/caicloud/nirvana/.gitignore new file mode 100644 index 0000000..9f11b75 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/vendor/github.com/caicloud/nirvana/.golangci.yml b/vendor/github.com/caicloud/nirvana/.golangci.yml new file mode 100644 index 0000000..8356963 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/.golangci.yml @@ -0,0 +1,25 @@ +run: + deadline: 5m + +linter-settings: + goconst: + min-len: 2 + min-occurrences: 2 + +linters: + enable: + - golint + - goconst + - gofmt + - goimports + - misspell + - unparam + - unconvert + +issues: + exclude-use-default: false + exclude-rules: + - path: _test.go + linters: + - errcheck + - unparam diff --git a/vendor/github.com/caicloud/nirvana/.pre-commit-config.yaml b/vendor/github.com/caicloud/nirvana/.pre-commit-config.yaml new file mode 100644 index 0000000..ec81caa --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +- repo: git://github.com/dnephin/pre-commit-golang + sha: HEAD + hooks: + - id: go-fmt + args: [-s] + exclude: ^vendor/ diff --git a/vendor/github.com/caicloud/nirvana/.travis.yml b/vendor/github.com/caicloud/nirvana/.travis.yml new file mode 100644 index 0000000..8a545dd --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/.travis.yml @@ -0,0 +1,15 @@ +language: go + +go: + - "1.13.x" + +before_install: + - go get github.com/mattn/goveralls + - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.23.6 + +script: + - hack/verify_boilerplate.py + - golangci-lint run + - GO_COVER_PROFILE=$(mktemp -u) + - go test ./... -race -covermode atomic -coverprofile $GO_COVER_PROFILE + - goveralls -service=travis-ci -coverprofile $GO_COVER_PROFILE diff --git a/vendor/github.com/caicloud/nirvana/CONTRIBUTING.md b/vendor/github.com/caicloud/nirvana/CONTRIBUTING.md new file mode 100644 index 0000000..9b4bbab --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/CONTRIBUTING.md @@ -0,0 +1,108 @@ +# Nirvana Contributor Guide + +Welcome to Nirvana! This document shows you how to contribute to the code base. Plaese file issues or pull requests if +you find anything missing or incorrect. + +### Fork && Clone + +First, you can fork this repository via the `Fork` button on the top of this page (If you like Nirvana, please star it :)). +Then you can clone Nirvana to your local path: + +``` +$ mkdir -p $GOPATH/src/github.com/caicloud +$ go get github.com/caicloud/nirvana + +# Avoid push to caicloud/nirvana +$ git config push.default nothing + +# Set user to match your github profile name: +$ user={your github profile name} + +$ git remove add $user git@github.com:$user/nirvana.git +$ git fetch $user +``` + +There are some environment requirements: + +- [Latest golang](https://golang.org/dl/) +- [Latest dep](https://github.com/golang/dep/) +- [Latest gometalinter](https://github.com/alecthomas/gometalinter) + +### Find something you are interested in + +Nirvana is under active development, there are quite a few incomplete features. Following is a list of items to checkout: + +- Docs + - English + - 中文 + - ... +- Loggers + - File Logger + - Remote Logger + - ... +- Operators + - More Powerful Vaditor + - ... +- Plugins +- OpenAPI Generation +- Clients Generation + - go + - graphql + - ... +- Test Cases +- Core + - API Definition + - HTTP Service + - Router + - Code Refactor + - ... +- Bugs + - File Issues + - Fix Issues + - Fix Typo && Comments + - ... + +We welcome any thoughts or contributions on these items! + +### File an issue to discuss your idea or design + +Before start writing code, we strongly recommend you to file an issue to discuss the motivation and proposals. +Here are some workflows for you: + +#### Write a new feature + +1. Search in [Nirvana Issues](https://github.com/caicloud/nirvana/issues) to check if there is similar issue. +2. If not, file an issue to describe the feature you want to work on. +3. Discuss with other contributors and adjust your design accordingly. +4. Finalize your design. +5. Implement the feature and push to your own repository. +6. Create a pull request to Nirvana. +7. Code review and change your PR if necessary. +8. Other contributors will merge your pull request. + +#### Take tasks from an issue + +1. Find an issue you are intersted in. +2. Take the tasks by discussing with issue author and code owners. +3. Implement the feature (or fix the bug), and push to your own repository. +4. Create a pull request to Nirvana. +7. Code review and change your PR if necessary. +8. Other contributors will merge your pull request. + +#### Fix typos, commments or submit docs + +1. Modify and push to your own repository. +2. Create a pull request to Nirvana. +7. Code review and change your PR if necessary. +8. Other contributors will merge your pull request. + +### Code review + +Nirvana use an [automatic tool](https://github.com/caicloud-bot) to manage the project. Its basic functionality +is assigning pull requests to reviewers and merging pull requests if it received `/lgtm` and `/approve`. + +Many directories of Nirvana have `OWNERS`, these files contains github account names of reviewers and approvers. +- Reviewers can review pull requests and reply `/lgtm` if they are satisfied with those pull requests. +- Approvers can give a final approval by `/approve` to indicate whether a change to a directory or subdirectory should be accepted. + +If a pull request receives both `/lgtm` and `/approve`, it will be merged in a short duration. diff --git a/vendor/github.com/caicloud/nirvana/LICENSE b/vendor/github.com/caicloud/nirvana/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/caicloud/nirvana/Makefile b/vendor/github.com/caicloud/nirvana/Makefile new file mode 100644 index 0000000..36814a5 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/Makefile @@ -0,0 +1,37 @@ +# Copyright 2018 Caicloud Authors +# +# 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. + +# This will force go to use the vendor files instead of using the `$GOPATH/pkg/mod`. (vendor mode) +# more info: https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away +export GOFLAGS := -mod=vendor + +.PHONY: test +test: + hack/verify_boilerplate.py + @go test -race -cover ./... + +.PHONY: license +license: + @go run ./hack/license/apache.go --go-header-file hack/boilerplate/boilerplate.go.txt + +.PHONY: format +format: + @find . ! -path "./vendor/*" -name "*.go" | xargs gofmt -s -w + +.PHONY: refine +refine: format license + +.PHONY: gitbook +gitbook: + @gitbook build ./manuals ./docs diff --git a/vendor/github.com/caicloud/nirvana/OWNERS b/vendor/github.com/caicloud/nirvana/OWNERS new file mode 100644 index 0000000..004a416 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/OWNERS @@ -0,0 +1,7 @@ +approvers: + - ddysher + - kdada + - iawia002 + - supereagle +reviewers: + - hezhizhen diff --git a/vendor/github.com/caicloud/nirvana/README.md b/vendor/github.com/caicloud/nirvana/README.md new file mode 100644 index 0000000..bc5ee8b --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/README.md @@ -0,0 +1,50 @@ +# Nirvana + + + +[![Build Status](https://travis-ci.org/caicloud/nirvana.svg?branch=master)](https://travis-ci.org/caicloud/nirvana) +[![Coverage Status](https://coveralls.io/repos/github/caicloud/nirvana/badge.svg?branch=master)](https://coveralls.io/github/caicloud/nirvana?branch=master) +[![GoDoc](http://godoc.org/github.com/caicloud/nirvana?status.svg)](http://godoc.org/github.com/caicloud/nirvana) +[![Go Report Card](https://goreportcard.com/badge/github.com/caicloud/nirvana)](https://goreportcard.com/report/github.com/caicloud/nirvana) +[![OpenTracing Badge](https://img.shields.io/badge/OpenTracing-enabled-blue.svg)](http://opentracing.io) + +Nirvana is a golang API framework designed for productivity and usability. It aims to be the building block for +all golang services at Caicloud. The high-level goals and features include: + +* consistent API behavior, structure and layout across all golang projects +* improve engineering productivity with openAPI and client generation, etc +* validation can be added by declaring validation method as part of API definition +* out-of-box instrumentation support, e.g. metrics, profiling, tracing, etc +* easy and standard configuration management, as well as standard cli interface + +Nirvana is also extensible and performant, with the goal to support fast developmenet velocity. + +## Getting Started + +Nirvana provides documentations in two languages to help you expore this framework. Note right now, only Chinese +docs are kept up-to-date. + +- [中文](https://caicloud.github.io/nirvana/zh-hans) +- [English (Expired)](https://caicloud.github.io/nirvana/en) + +In addition, [nirvana-practice](https://github.com/caicloud/nirvana-practice) provides a practical way for you +to get familiar with Nirvana. + +## Features + +- API Framework based on Descriptors +- Request Filter +- Middleware +- Validator +- Plugins +- API Doc Generation +- Client Generation + +## Contributing + +If you are interested in contributing to Nirvana, please checkout [CONTRIBUTING.md](./CONTRIBUTING.md). +We welcome any code or non-code contribution! + +## Licensing + +Nirvana is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE) for the full license text. diff --git a/vendor/github.com/caicloud/nirvana/ROADMAP.md b/vendor/github.com/caicloud/nirvana/ROADMAP.md new file mode 100644 index 0000000..35c2f7e --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/ROADMAP.md @@ -0,0 +1,36 @@ +# Road Map + +### Long Term +- SOA Governance + +### 0.3 +- Optimize the performance of service (Go Reflect) +- Improve OpenAPI and Client generation usability +- Refine profiling/metrics/tracing plugins +- Add more documents + +### 0.2 +- Update compiler to golang 1.10 ([#202](https://github.com/caicloud/nirvana/issues/202)) +- Plugins + - Healthcheck ([#51](https://github.com/caicloud/nirvana/issues/51)) + - Version ([#51](https://github.com/caicloud/nirvana/issues/51)) +- OpenAPI Generation ([#123](https://github.com/caicloud/nirvana/issues/123) [#132](https://github.com/caicloud/nirvana/issues/132) [#183](https://github.com/caicloud/nirvana/issues/183)) + - Prefab ([#5](https://github.com/caicloud/nirvana/issues/5)) + - Operator ([#5](https://github.com/caicloud/nirvana/issues/5)) +- Client Generation + - go ([#41](https://github.com/caicloud/nirvana/issues/41)) + - graphql +- Refine Error ([#72](https://github.com/caicloud/nirvana/issues/72) [#200](https://github.com/caicloud/nirvana/issues/200)) +- Nirvana cli [#203](https://github.com/caicloud/nirvana/issues/203) + - Project structure +- Test coverage 60% ([#67](https://github.com/caicloud/nirvana/issues/67)) +- Performance ([#148](https://github.com/caicloud/nirvana/issues/148)) +- Monitoring architechture examples + - Tracing (OpenTracing) + - Metrics (Prometheus) +- Docs ([#39](https://github.com/caicloud/nirvana/issues/39) [#154](https://github.com/caicloud/nirvana/issues/154)) + - Chinese + - English + +# 0.1 +- Basic Framework diff --git a/vendor/github.com/caicloud/nirvana/banner.go b/vendor/github.com/caicloud/nirvana/banner.go new file mode 100644 index 0000000..b957c3e --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/banner.go @@ -0,0 +1,66 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nirvana + +// Logo presents an ASCII art of nirvana logo. +const Logo = ` + '-:::::- + .:/oo+++oo+: + .:+++//+sys.' + .:. :+++//+sysss+ + /o++ :o++//osysos+. ''.......' + oo/o- -o+///ossoos+.'--/+oooooooooooooo/-. + +o//y '+o///+ssooos::+oooooooossooooooooooooo' + 's//o+ .s+///osoooohoooooosooo+:-...''''''''''.' + '// /+//y./s+//+ssooooosoooso/-.' '- + s+s/s///ysh+///ysoooooooos+-' yh' :: + 'h//yy///sd+///ssoooooooso:..' '+-dys'-h+ + 'y//ys///+s///+hoooooooso'h++o+'.-hydhyyshy' + y//++////////ssooooooos 'y+/sso+++syyyyyhyo '' + o+///////+s//yoooooooh:+oo+:/:::::::+++ooshss++o + 's///////yh//ysoooooodo/:::::::::::::::::::/sss+ + //:/s//////hyo/+hoooosh+:::::::::::::::::::::::/y. + s/+os////++hoh//ysoosh/::::::::::::+o///+o/:::::/h' + y////////hyhosy//yssh::::::::::::/o- ':s/::::so. + +o//////+yshyosy+/sd/::::::::::::y '+hyy::::o+:o. + 'y//////+hoodyooy+/+ssoo+/+oooo::s .mNsy::::s::dy +///. /+//////ssoosyooss+////+++//+y::s: ++:/ssh .ss +++/oo/. o+/. s////+yosyooooooossso++++//y/:::+o/-''-/o+s++syy-// + +o///o+/. oo/+o/- -soh+///yysyhoooooooooooosyoso:::::::/++/::/s....o: + .+o////+o/os+//+o/y++so///+ssssooooooooossh/-::/:::::::::::::o/oo// + .+o+////+shy+///oy+/oso////++ossssssso+os+++/::::::::::::::+oy+/: + '-+o+////+o+///////ossssoo+++++oooooo/:::::::::o/::::::::/h/::/ + s++sys+//yysssso+oysssm///+++////::::::::::::/s:::/+:::/y''' + :+o+++oo/+ssssoosyyhyod:::::::::::::::::::::+o:::+h++:/s' + '-+++/////+++shyyooooys::::::::::::::::::::s++++sooh+s' + './oysoo///osssssssho::::::::::::::::::::/:::o+/so' + ss+++//////////+oys/:::::::::::::::::::::::+o: + '.:/+++++++++//-.'.soo/o::::::::::::::::/oo:' + '''''' h/-./ss+++///////++oo/-' + oo++/.''o+/y////::-. + ://- +` + +// Banner shows an ASCII art of nirvana. +const Banner = ` + ____ _____ _ + |_ \|_ _| (_) + | \ | | __ _ .--. _ __ ,--. _ .--. ,--. + | |\ \| | [ | [ '/''\][ \ [ ]''_\ : [ '.-. | ''_\ : + _| |_\ |_ | | | | \ \/ / // | |, | | | | // | |, + |_____|\____|[___][___] \__/ \'-;__/[___||__]\'-;__/ +` diff --git a/vendor/github.com/caicloud/nirvana/definition/definition.go b/vendor/github.com/caicloud/nirvana/definition/definition.go new file mode 100644 index 0000000..132f272 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/definition/definition.go @@ -0,0 +1,245 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package definition + +import ( + "context" + "reflect" +) + +// Chain contains all subsequent actions. +type Chain interface { + // Continue continues to execute the next subsequent actions. + Continue(context.Context) error +} + +// Middleware describes the form of middlewares. If you want to +// carry on, call Chain.Continue() and pass the context. +type Middleware func(context.Context, Chain) error + +// Operator is used to operate an object and return an replacement object. +// +// For example: +// A converter: +// type ConverterForAnObject struct{} +// func (c *ConverterForAnObject) Kind() {return "converter"} +// func (c *ConverterForAnObject) In() reflect.Type {return definition.TypeOf(&ObjectV1{})} +// func (c *ConverterForAnObject) Out() reflect.Type {return definition.TypeOf(&ObjectV2{})} +// func (c *ConverterForAnObject) Operate(ctx context.Context, object interface{}) (interface{}, error) { +// objV2, err := convertObjectV1ToObjectV2(object.(*ObjectV1)) +// return objV2, err +// } +// +// A validator: +// type ValidatorForAnObject struct{} +// func (c *ValidatorForAnObject) Kind() {return "validator"} +// func (c *ValidatorForAnObject) In() reflect.Type {return definition.TypeOf(&Object{})} +// func (c *ValidatorForAnObject) Out() reflect.Type {return definition.TypeOf(&Object{})} +// func (c *ValidatorForAnObject) Operate(ctx context.Context, object interface{}) (interface{}, error) { +// if err := validate(object.(*Object)); err != nil { +// return nil, err +// } +// return object, nil +// } +type Operator interface { + // Kind indicates operator type. + Kind() string + // In returns the type of the only object parameter of operator. + // The type must be a concrete struct or built-in type. It should + // not be interface unless it's a file or stream reader. + In() reflect.Type + // Out returns the type of the only object result of operator. + // The type must be a concrete struct or built-in type. It should + // not be interface unless it's a file or stream reader. + Out() reflect.Type + // Operate operates an object and return one. + Operate(ctx context.Context, field string, object interface{}) (interface{}, error) +} + +// Method is an alternative of HTTP method. It's more clearer than HTTP method. +// A definition method binds a certain HTTP method and a success status code. +type Method string + +const ( + // List binds to http.MethodGet and code http.StatusOK(200). + List Method = "List" + // Get binds to http.MethodGet and code http.StatusOK(200). + Get Method = "Get" + // Create binds to http.MethodPost and code http.StatusCreated(201). + Create Method = "Create" + // Update binds to http.MethodPut and code http.StatusOK(200). + Update Method = "Update" + // Patch binds to http.MethodPatch and code http.StatusOK(200). + Patch Method = "Patch" + // Delete binds to http.MethodDelete and code http.StatusNoContent(204). + Delete Method = "Delete" + // AsyncCreate binds to http.MethodPost and code http.StatusAccepted(202). + AsyncCreate Method = "AsyncCreate" + // AsyncUpdate binds to http.MethodPut and code http.StatusAccepted(202). + AsyncUpdate Method = "AsyncUpdate" + // AsyncPatch binds to http.MethodPatch and code http.StatusAccepted(202). + AsyncPatch Method = "AsyncPatch" + // AsyncDelete binds to http.MethodDelete and code http.StatusAccepted(202). + AsyncDelete Method = "AsyncDelete" +) + +// Source indicates which place a value is from. +type Source string + +const ( + // Path means value is from URL path. + Path Source = "Path" + // Query means value is from URL query string. + Query Source = "Query" + // Header means value is from request header. + Header Source = "Header" + // Form means value is from request body and content type must be + // "application/x-www-form-urlencoded" and "multipart/form-data". + Form Source = "Form" + // File means value is from request body and content type must be + // "multipart/form-data". + File Source = "File" + // Body means value is from request body. + Body Source = "Body" + // Auto identifies a struct and generate field values by field tag. + // + // Tag name is "source". Its value format is "Source,Name". + // + // ex. + // type Example struct { + // Start int `source:"Query,start"` + // ContentType string `source:"Header,Content-Type"` + // } + Auto Source = "Auto" + // Prefab means value is from a prefab generator. + // A prefab combines data to generate value. + Prefab Source = "Prefab" +) + +// Destination indicates the target type to place function results. +type Destination string + +const ( + // Meta means result will be set into the header of response. + Meta Destination = "Meta" + // Data means result will be set into the body of response. + Data Destination = "Data" + // Error means the result is an error and should be treated specially. + // An error occurs indicates that there is no data to return. So the + // error should be treated as data and be writed back to client. + Error Destination = "Error" +) + +// Example is just an example. +type Example struct { + // Description describes the example. + Description string + // Instance is a custom data. + Instance interface{} +} + +// Parameter describes a function parameter. +type Parameter struct { + // Source is the parameter value generated from. + Source Source + // Name is the name to get value from a request. + // ex. a query name, a header key, etc. + Name string + // Default value is used when a request does not provide a value + // for the parameter. + Default interface{} + // Operators can modify and validate the target value. + // Parameter value is passed to the first operator, then + // previous operator's result is as next operator's parameter. + // The result of last operator will be passed to target function. + Operators []Operator + // Description describes the parameter. + Description string +} + +// Result describes how to handle a result from function results. +type Result struct { + // Destination is the target for the result. Different types make different behavior. + Destination Destination + // Operators can modify the result value. + // Result value is passed to the first operator, then + // previous operator's result is as next operator's parameter. + // The result of last operator will be passed to destination handler. + Operators []Operator + // Description describes the result. + Description string +} + +// Definition defines an API handler. +type Definition struct { + // Method is definition method. + Method Method + // Consumes indicates how many content types the handler can consume. + // It will override parent descriptor's consumes. + Consumes []string + // Produces indicates how many content types the handler can produce. + // It will override parent descriptor's produces. + Produces []string + // Tags indicates tags of the API handler. + // It will override parent descriptor's tags. + Tags []string + // ErrorProduces is used to generate data for error. If this field is empty, + // it means that this field equals to Produces. + // In some cases, succeessful data and error data should be generated in + // different ways. + ErrorProduces []string + // Function is a function handler. It must be func type. + Function interface{} + // Parameters describes function parameters. + Parameters []Parameter + // Results describes function retrun values. + Results []Result + // Summary is a one-line brief description of this definition. + Summary string + // Description describes the API handler. + Description string + // Examples contains many examples for the API handler. + Examples []Example +} + +// Descriptor describes a descriptor for API definitions. +type Descriptor struct { + // Path is the url path. It will inherit parent's path. + // + // If parent path is "/api/v1", current is "/some", + // It means current definitions handles "/api/v1/some". + Path string + // Consumes indicates content types that current definitions + // and child definitions can consume. + // It will override parent descriptor's consumes. + Consumes []string + // Produces indicates content types that current definitions + // and child definitions can produce. + // It will override parent descriptor's produces. + Produces []string + // Tags indicates tags of current definitions and child definitions. + // It will override parent descriptor's tags. + Tags []string + // Middlewares contains path middlewares. + Middlewares []Middleware + // Definitions contains definitions for current path. + Definitions []Definition + // Children is used to place sub-descriptors. + Children []Descriptor + // Description describes the usage of the path. + Description string +} diff --git a/vendor/github.com/caicloud/nirvana/definition/helper.go b/vendor/github.com/caicloud/nirvana/definition/helper.go new file mode 100644 index 0000000..af61e10 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/definition/helper.go @@ -0,0 +1,260 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package definition + +import ( + "context" + "fmt" + "reflect" +) + +// MIME types +const ( + // acceptTypeAll indicates a accept type from http request. + // It means client can receive any content. + // Request content type in header "Content-Type" must not set to "*/*". + // It only can exist in request header "Accept". + // In most time, it locate at the last element of "Accept". + // It's default value if client have not set "Accept" header. + MIMEAll = "*/*" + MIMENone = "" + MIMEText = "text/plain" + MIMEJSON = "application/json" + MIMEXML = "application/xml" + MIMEOctetStream = "application/octet-stream" + MIMEURLEncoded = "application/x-www-form-urlencoded" + MIMEFormData = "multipart/form-data" +) + +// DataErrorResults returns the most frequently-used results. +// Definition function should have two results. The first is +// any type for data, and the last is error. +func DataErrorResults(description string) []Result { + return []Result{ + { + Destination: Data, + Description: description, + }, + { + Destination: Error, + }, + } +} + +// ParameterFor creates a simple parameter. +func ParameterFor(source Source, name string, description string) Parameter { + return Parameter{ + Source: source, + Name: name, + Description: description, + } +} + +// PathParameterFor creates a path parameter +func PathParameterFor(name string, description string) Parameter { + return ParameterFor(Path, name, description) +} + +// QueryParameterFor creates a query parameter +func QueryParameterFor(name string, description string) Parameter { + return ParameterFor(Query, name, description) +} + +// HeaderParameterFor creates a header parameter +func HeaderParameterFor(name string, description string) Parameter { + return ParameterFor(Header, name, description) +} + +// FormParameterFor creates a form parameter +func FormParameterFor(name string, description string) Parameter { + return ParameterFor(Form, name, description) +} + +// FileParameterFor creates a file parameter +func FileParameterFor(name string, description string) Parameter { + return ParameterFor(File, name, description) +} + +// BodyParameterFor creates a body parameter +func BodyParameterFor(description string) Parameter { + return ParameterFor(Body, "", description) +} + +// PrefabParameterFor creates a prefab parameter +func PrefabParameterFor(name string, description string) Parameter { + return ParameterFor(Prefab, name, description) +} + +// AutoParameterFor creates an auto parameter +func AutoParameterFor(description string) Parameter { + return ParameterFor(Auto, "", description) +} + +// ResultFor creates a simple result. +func ResultFor(dest Destination, description string) Result { + return Result{ + Destination: dest, + Description: description, + } +} + +// MetaResultFor creates meta result. +func MetaResultFor(description string) Result { + return ResultFor(Meta, description) +} + +// DataResultFor creates data result. +func DataResultFor(description string) Result { + return ResultFor(Data, description) +} + +// ErrorResult creates error result. +func ErrorResult() Result { + return ResultFor(Error, "") +} + +// OperatorFunc creates operator by function. +// function must has signature: +// func f(context.Context, string, AnyType) (AnyType, error) +// The second parameter is a string that is used to identify field. +// AnyType can be any type in go. But struct type and +// built-in data type is recommended. +func OperatorFunc(kind string, f interface{}) Operator { + typ := reflect.TypeOf(f) + if typ.Kind() != reflect.Func { + panic(fmt.Sprintf("Parameter f in OperatorFunc must be a function, but got: %s", typ.Kind())) + } + if typ.NumIn() != 3 { + panic(fmt.Sprintf("Function must have 3 parameters, but got: %d", typ.NumIn())) + } + if typ.In(0) != reflect.TypeOf((*context.Context)(nil)).Elem() { + panic(fmt.Sprintf("The first parameter of function must be context.Context, but got: %v", typ.In(0))) + } + if typ.In(1) != reflect.TypeOf("") { + panic(fmt.Sprintf("The second parameter of function must be string, but got: %v", typ.In(0))) + } + if typ.NumOut() != 2 { + panic(fmt.Sprintf("Parameter f in OperatorFunc must have two results, but got: %d", typ.NumOut())) + } + if typ.Out(1).String() != "error" { + panic(fmt.Sprintf("The last result of parameter f in OperatorFunc must be error, but got: %v", typ.Out(1))) + } + return &operatorRef{ + kind: kind, + in: typ.In(2), + out: typ.Out(0), + value: reflect.ValueOf(f), + } +} + +// NewOperator creates operator by function. +// function must has signature: +// func f(context.Context, AnyType) (AnyType, error) +// AnyType can be any type in go. But struct type and +// built-in data type is recommended. +func NewOperator(kind string, in, out reflect.Type, f func(ctx context.Context, field string, object interface{}) (interface{}, error)) Operator { + return &operator{ + kind: kind, + in: in, + out: out, + f: f, + } +} + +type operator struct { + kind string + in reflect.Type + out reflect.Type + f func(ctx context.Context, field string, object interface{}) (interface{}, error) +} + +// Kind indicates operator type. +func (o *operator) Kind() string { + return o.kind +} + +// In returns the type of the only object parameter of operator. +func (o *operator) In() reflect.Type { + return o.in +} + +// Out returns the type of the only object result of operator. +func (o *operator) Out() reflect.Type { + return o.out +} + +// Operate operates an object and return one. +func (o *operator) Operate(ctx context.Context, field string, object interface{}) (interface{}, error) { + return o.f(ctx, field, object) +} + +type operatorRef struct { + kind string + in reflect.Type + out reflect.Type + value reflect.Value +} + +// Kind indicates operator type. +func (o *operatorRef) Kind() string { + return o.kind +} + +// In returns the type of the only object parameter of operator. +func (o *operatorRef) In() reflect.Type { + return o.in +} + +// Out returns the type of the only object result of operator. +func (o *operatorRef) Out() reflect.Type { + return o.out +} + +// Operate operates an object and return one. +func (o *operatorRef) Operate(ctx context.Context, field string, object interface{}) (interface{}, error) { + var objectValue reflect.Value + if object == nil { + objectValue = reflect.Zero(o.in) + } else { + objectValue = reflect.ValueOf(object) + } + + results := o.value.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(field), objectValue}) + v := results[1] + if v.IsNil() { + return results[0].Interface(), nil + } + return nil, results[1].Interface().(error) +} + +// SimpleDescriptor creates a simple descriptor for handler. +// The descriptor consumes all content types and produces all +// accept types. +func SimpleDescriptor(method Method, path string, f interface{}) Descriptor { + return Descriptor{ + Path: path, + Definitions: []Definition{ + { + Method: method, + Function: f, + Consumes: []string{MIMEAll}, + Produces: []string{MIMEAll}, + }, + }, + } + +} diff --git a/vendor/github.com/caicloud/nirvana/errors/errors.go b/vendor/github.com/caicloud/nirvana/errors/errors.go new file mode 100644 index 0000000..ceeb3f5 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/errors/errors.go @@ -0,0 +1,197 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errors + +import ( + "encoding/xml" + "fmt" +) + +// Reason is an enumeration of possible failure causes. Each Reason +// must map to a format which is a string containing ${formatArgu1}. +// +// Following format is recommended: +// MuduleName[:SubmoduleName]:ShortErrorDescription +// Examples: +// Reason "Nirvana:KindNotFound" may map to format "${kindName} was not found". +// Reason "Nirvana:SomeoneIsSleeping" may map to format "${name} is sleeping now" +type Reason string + +// Factory can create error from a fixed format. +type Factory interface { + // Error generates an error from v. + Error(v ...interface{}) error + // Derived checks if an error was derived from current factory. + Derived(e error) bool +} + +// dataMap is a wrapper for marshalling map into XML. Standard package can't +// generate XML for map. +type dataMap map[string]string + +// MarshalXML marshals data map into XML. +func (dm dataMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + tokens := []xml.Token{start} + for key, value := range dm { + t := xml.StartElement{ + Name: xml.Name{ + Local: key, + }, + } + tokens = append(tokens, t, xml.CharData(value), xml.EndElement{Name: t.Name}) + } + tokens = append(tokens, xml.EndElement{Name: start.Name}) + + for _, t := range tokens { + err := e.EncodeToken(t) + if err != nil { + return err + } + } + + return e.Flush() +} + +// message can be marshaled for transferring. +// +// Example: +// { +// "message": "name of something is to short", +// "reason": "SomeModule:NameTooShort", +// "data": { +// "name": "something" +// } +// } +type message struct { + // Reason is a unique key for an error in global environment. + Reason Reason `json:"reason,omitempty" xml:",omitempty"` + // Message contains the detailed description of an error. + Message string `json:"message"` + // Data is used for i18n. + Data dataMap `json:"data,omitempty" xml:",omitempty"` +} + +// Error returns error description. +func (m *message) Error() string { + return m.Message +} + +// err implements error interface. +type err struct { + message + factory *factory +} + +// Code returns status code of the error. +func (e *err) Code() int { + return e.factory.code +} + +// Message returns detailed message of the error. +func (e *err) Message() interface{} { + return &e.message +} + +// Error returns error description. +func (e *err) Error() string { + return e.message.Message +} + +// factory is an error factory. +type factory struct { + code int + reason Reason + format string +} + +// Code returns code of current factory. +func (f *factory) Code() int { + return f.code +} + +// Reason returns reason of current factory. +func (f *factory) Reason() Reason { + return f.reason +} + +// Error generates an error from v. +func (f *factory) Error(v ...interface{}) error { + msg := message{Reason: f.reason} + msg.Message, msg.Data = expand(f.format, v...) + return &err{ + message: msg, + factory: f, + } +} + +// Derived checks if an error was derived from current factory. +// If an error is not derived by current factory but implements +// ExternalError as well as code and reason are matched, +// this method also returns true. +func (f *factory) Derived(e error) bool { + origin, ok := e.(*err) + if ok { + return origin.factory == f + } + external, ok := e.(ExternalError) + return ok && external.Code() == f.code && + external.Reason() == string(f.reason) +} + +// expand expands a format string like "name ${name} is too short" to "name japari is too short" +// by replacing ${} with v... one by one. +// Note that if len(v) < count of ${}, it will panic. +func expand(format string, v ...interface{}) (msg string, data map[string]string) { + n := 0 + var m map[string]string + buf := make([]byte, 0, len(format)) + + for i := 0; i < len(format); { + if format[i] == '$' && (i+1) < len(format) && format[i+1] == '{' { + b := make([]byte, 0, len(format)-i) + if i+2 == len(format) { // check "...${" + panic("unexpected EOF while looking for matching }") + } + ii := i + 2 + for ii < len(format[i+2:])+i+2 { + if format[ii] != '}' { + b = append(b, format[ii]) + } else { + break + } + ii++ + if ii == len(format[i+2:])+i+2 { // check "...${..." + panic("unexpected EOF while looking for matching }") + } + } + i = ii + 1 + if n == len(v) { + panic("not enough args") + } + if m == nil { + m = map[string]string{} + } + m[string(b)] = fmt.Sprint(v[n]) + buf = append(buf, fmt.Sprint(v[n])...) + n++ + } else { + buf = append(buf, format[i]) + i++ + } + } + return string(buf), m +} diff --git a/vendor/github.com/caicloud/nirvana/errors/external.go b/vendor/github.com/caicloud/nirvana/errors/external.go new file mode 100644 index 0000000..b82fe93 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/errors/external.go @@ -0,0 +1,90 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errors + +import ( + "encoding/json" + "encoding/xml" +) + +// DataType is the type of raw data. +type DataType string + +const ( + // DataTypeJSON corresponds to content type "application/json". + DataTypeJSON DataType = "json" + // DataTypeXML corresponds to content type "application/xml". + DataTypeXML DataType = "xml" + // DataTypePlain indicates there is a plain error message. + DataTypePlain DataType = "" +) + +// ExternalError describes an error interface for client error. +type ExternalError interface { + error + // Code returns status code of the error. + Code() int + // Reason returns reason string of the error. + Reason() string + // Data returns data map of the error. + Data() map[string]string +} + +type externalError struct { + message + code int +} + +// Code returns status code of the error. +func (e *externalError) Code() int { + return e.code +} + +// Reason returns reason of the error. +func (e *externalError) Reason() string { + return string(e.message.Reason) +} + +// Data returns data map of the error. +func (e *externalError) Data() map[string]string { + return e.message.Data +} + +// Message returns detailed message of the error. +func (e *externalError) Message() interface{} { + return &e.message +} + +// Error returns error description. +func (e *externalError) Error() string { + return e.message.Message +} + +// ParseError parse error from raw data. +func ParseError(code int, dt DataType, data []byte) (ExternalError, error) { + e := &externalError{ + code: code, + } + switch dt { + case DataTypeJSON: + return e, json.Unmarshal(data, &e.message) + case DataTypeXML: + return e, xml.Unmarshal(data, &e.message) + } + e.message.Message = string(data) + return e, nil +} diff --git a/vendor/github.com/caicloud/nirvana/errors/kinds.go b/vendor/github.com/caicloud/nirvana/errors/kinds.go new file mode 100644 index 0000000..2f71fa7 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/errors/kinds.go @@ -0,0 +1,100 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package errors + +// Builder can build error factories and errros. +type Builder interface { + // Build builds a factory to generate errors with predefined format. + Build(reason Reason, format string) Factory + // Error immediately creates an error without reason. + Error(format string, v ...interface{}) error +} + +// kind maps to http code. +// And it can be used to make an error factory. +type kind int + +// newKind creates a new builder with code. +func newKind(code int) Builder { + return kind(code) +} + +// Build builds a factory to generate errors with predefined format. +func (t kind) Build(reason Reason, format string) Factory { + return &factory{code: int(t), reason: reason, format: format} +} + +// Error immediately creates an error without reason. +func (t kind) Error(format string, v ...interface{}) error { + msg, data := expand(format, v...) + return &err{ + message: message{ + Message: msg, + Data: data, + }, + factory: &factory{code: int(t)}, + } +} + +// NewFactory creates a factory to create errors. The usage of this function is +// not recommended. Prefab kinds are more preferable. +func NewFactory(code int, reason Reason, format string) Factory { + return newKind(code).Build(reason, format) +} + +// These factory builders is used to build error factory. +var ( + BadRequest = newKind(400) // RFC 7231, 6.5.1 + Unauthorized = newKind(401) // RFC 7235, 3.1 + PaymentRequired = newKind(402) // RFC 7231, 6.5.2 + Forbidden = newKind(403) // RFC 7231, 6.5.3 + NotFound = newKind(404) // RFC 7231, 6.5.4 + MethodNotAllowed = newKind(405) // RFC 7231, 6.5.5 + NotAcceptable = newKind(406) // RFC 7231, 6.5.6 + ProxyAuthRequired = newKind(407) // RFC 7235, 3.2 + RequestTimeout = newKind(408) // RFC 7231, 6.5.7 + Conflict = newKind(409) // RFC 7231, 6.5.8 + Gone = newKind(410) // RFC 7231, 6.5.9 + LengthRequired = newKind(411) // RFC 7231, 6.5.10 + PreconditionFailed = newKind(412) // RFC 7232, 4.2 + RequestEntityTooLarge = newKind(413) // RFC 7231, 6.5.11 + RequestURITooLong = newKind(414) // RFC 7231, 6.5.12 + UnsupportedMediaType = newKind(415) // RFC 7231, 6.5.13 + RequestedRangeNotSatisfiable = newKind(416) // RFC 7233, 4.4 + ExpectationFailed = newKind(417) // RFC 7231, 6.5.14 + Teapot = newKind(418) // RFC 7168, 2.3.3 + UnprocessableEntity = newKind(422) // RFC 4918, 11.2 + Locked = newKind(423) // RFC 4918, 11.3 + FailedDependency = newKind(424) // RFC 4918, 11.4 + UpgradeRequired = newKind(426) // RFC 7231, 6.5.15 + PreconditionRequired = newKind(428) // RFC 6585, 3 + TooManyRequests = newKind(429) // RFC 6585, 4 + RequestHeaderFieldsTooLarge = newKind(431) // RFC 6585, 5 + UnavailableForLegalReasons = newKind(451) // RFC 7725, 3 + + InternalServerError = newKind(500) // RFC 7231, 6.6.1 + NotImplemented = newKind(501) // RFC 7231, 6.6.2 + BadGateway = newKind(502) // RFC 7231, 6.6.3 + ServiceUnavailable = newKind(503) // RFC 7231, 6.6.4 + GatewayTimeout = newKind(504) // RFC 7231, 6.6.5 + HTTPVersionNotSupported = newKind(505) // RFC 7231, 6.6.6 + VariantAlsoNegotiates = newKind(506) // RFC 2295, 8.1 + InsufficientStorage = newKind(507) // RFC 4918, 11.5 + LoopDetected = newKind(508) // RFC 5842, 7.2 + NotExtended = newKind(510) // RFC 2774, 7 + NetworkAuthenticationRequired = newKind(511) // RFC 6585, 6 +) diff --git a/vendor/github.com/caicloud/nirvana/go.mod b/vendor/github.com/caicloud/nirvana/go.mod new file mode 100644 index 0000000..48d7811 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/go.mod @@ -0,0 +1,39 @@ +module github.com/caicloud/nirvana + +go 1.13 + +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/PuerkitoBio/purell v1.1.0 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect + github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa // indirect + github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d // indirect + github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee + github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 // indirect + github.com/go-playground/locales v0.12.1 // indirect + github.com/go-playground/universal-translator v0.16.0 // indirect + github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/magiconair/properties v1.8.0 // indirect + github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57 // indirect + github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675 // indirect + github.com/opentracing/opentracing-go v1.1.0 + github.com/pelletier/go-toml v1.1.0 // indirect + github.com/prometheus/client_golang v1.5.1 + github.com/spf13/afero v1.1.0 // indirect + github.com/spf13/cast v1.2.0 + github.com/spf13/cobra v0.0.3 + github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec // indirect + github.com/spf13/pflag v1.0.1 + github.com/spf13/viper v1.0.2 + github.com/stretchr/testify v1.5.1 // indirect + github.com/uber/jaeger-client-go v2.22.1+incompatible + github.com/uber/jaeger-lib v2.2.0+incompatible // indirect + go.uber.org/atomic v1.6.0 // indirect + golang.org/x/text v0.3.0 + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + gopkg.in/go-playground/validator.v9 v9.17.1 + gopkg.in/yaml.v2 v2.2.5 +) diff --git a/vendor/github.com/caicloud/nirvana/go.sum b/vendor/github.com/caicloud/nirvana/go.sum new file mode 100644 index 0000000..d0c008e --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/go.sum @@ -0,0 +1,173 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa h1:hr8WVDjg4JKtQptZpzyb196TmruCs7PIsdJz8KAOZp8= +github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d h1:k3UQ7Z8yFYq0BNkYykKIheY0HlZBl1Hku+pO9HE9FNU= +github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee h1:eo0HQoNFtbiEc7+1gRF9pgW6azx8a1cO2fXcqq1MuD0= +github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 h1:+vsw187FKvA2QUGAcE+vQSfyxqLbUXixPYRRMAzwu04= +github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= +github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +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/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57 h1:qhv1ir3dIyOFmFU+5KqG4dF3zSQTA4nn1DFhu2NQC44= +github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675 h1:/rdJjIiKG5rRdwG5yxHmSE/7ZREjpyC0kL7GxGT/qJw= +github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM= +github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.0 h1:bopulORc2JeYaxfHLvJa5NzxviA9PoWhpiiJkru7Ji4= +github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= +github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig= +github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso= +github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM= +github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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.v9 v9.17.1 h1:+BgStg92rQ9TTsN/QTKUMyptg16ZZ+h88BlX4wCi+z4= +gopkg.in/go-playground/validator.v9 v9.17.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/caicloud/nirvana/log/default.go b/vendor/github.com/caicloud/nirvana/log/default.go new file mode 100644 index 0000000..41bad8a --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/log/default.go @@ -0,0 +1,135 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package log + +// default logger. Don't use the logger directly. +var logger Logger = newStderrLogger(LevelDebug, 1) + +// DefaultLogger returns default logger. +func DefaultLogger() Logger { + return logger.Clone(-1) +} + +// SetDefaultLogger sets default logger. +func SetDefaultLogger(l Logger) { + if l == nil { + logger = &SilentLogger{} + } else { + logger = l.Clone(1) + } +} + +type defaultVerboser struct { + verboser Verboser +} + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (v *defaultVerboser) Info(a ...interface{}) { + v.verboser.Info(a...) +} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (v *defaultVerboser) Infof(format string, a ...interface{}) { + v.verboser.Infof(format, a...) +} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (v *defaultVerboser) Infoln(a ...interface{}) { + v.verboser.Infoln(a...) +} + +// V reports whether verbosity at the call site is at least the requested level. +// The returned value is a Verboser, which implements Info, Infof +// and Infoln. These methods will write to the Info log if called. +func V(v Level) Verboser { + return &defaultVerboser{logger.V(v)} +} + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Info(a ...interface{}) { + logger.Info(a...) +} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Infof(format string, a ...interface{}) { + logger.Infof(format, a...) +} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Infoln(a ...interface{}) { + logger.Infoln(a...) +} + +// Warning logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Warning(a ...interface{}) { + logger.Warning(a...) +} + +// Warningf logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Warningf(format string, a ...interface{}) { + logger.Warningf(format, a...) +} + +// Warningln logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Warningln(a ...interface{}) { + logger.Warningln(a...) +} + +// Error logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Error(a ...interface{}) { + logger.Error(a...) +} + +// Errorf logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Errorf(format string, a ...interface{}) { + logger.Errorf(format, a...) +} + +// Errorln logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Errorln(a ...interface{}) { + logger.Errorln(a...) +} + +// Fatal logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func Fatal(a ...interface{}) { + logger.Fatal(a...) +} + +// Fatalf logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func Fatalf(format string, a ...interface{}) { + logger.Fatalf(format, a...) +} + +// Fatalln logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func Fatalln(a ...interface{}) { + logger.Fatalln(a...) +} diff --git a/vendor/github.com/caicloud/nirvana/log/log.go b/vendor/github.com/caicloud/nirvana/log/log.go new file mode 100644 index 0000000..d9d3f40 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/log/log.go @@ -0,0 +1,104 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package log + +import "math" + +// Level is log level of verboser. We strongly recommend you to +// follow the rules: +// 1. Use LevelDebug for debug log. +// 2. Use 1 for important but not frequent log. +// 3. Use 2 for important and frequent log. +// 4. Use 3 for not important and not frequent log. +// 5. Use 4 for not important but frequent log. +// 6. Use [5, LevelDebug) only if you want to custom log levels. +type Level int32 + +const ( + // LevelDebug is for debug info. + LevelDebug Level = math.MaxInt32 +) + +// Severity has four classes to correspond with log situation. +type Severity string + +const ( + // SeverityInfo is for usual log. + SeverityInfo Severity = "INFO" + // SeverityWarning is for warning. + SeverityWarning Severity = "WARN" + // SeverityError is for error. + SeverityError Severity = "ERROR" + // SeverityFatal is for panic error. The severity means that + // a logger will output the error and exit immediately. + // It can't be recovered. + SeverityFatal Severity = "FATAL" +) + +// Verboser is an interface type that implements Info(f|ln) . +// See the documentation of V for more information. +type Verboser interface { + // Info logs to the INFO log. + // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. + Info(...interface{}) + // Infof logs to the INFO log. + // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. + Infof(string, ...interface{}) + // Infoln logs to the INFO log. + // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. + Infoln(...interface{}) +} + +// Logger provides a set of methods to output log. +type Logger interface { + Verboser + // V reports whether verbosity at the call site is at least the requested level. + // The returned value is a Verboser, which implements Info, Infof + // and Infoln. These methods will write to the Info log if called. + V(Level) Verboser + // Warning logs to the WARNING logs. + // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. + Warning(...interface{}) + // Warningf logs to the WARNING logs. + // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. + Warningf(string, ...interface{}) + // Warningln logs to the WARNING logs. + // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. + Warningln(...interface{}) + // Error logs to the ERROR logs. + // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. + Error(...interface{}) + // Errorf logs to the ERROR logs. + // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. + Errorf(string, ...interface{}) + // Errorln logs to the ERROR logs. + // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. + Errorln(...interface{}) + // Fatal logs to the FATAL logs, then calls os.Exit(1). + // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. + Fatal(...interface{}) + // Fatalf logs to the FATAL logs, then calls os.Exit(1). + // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. + Fatalf(string, ...interface{}) + // Fatalln logs to the FATAL logs, then calls os.Exit(1). + // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. + Fatalln(...interface{}) + // Clone clones current logger with new wrapper. + // A positive wrapper indicates how many wrappers outside the logger. + // A negative wrapper indicates how many wrappers should be stripped. + Clone(wrapper int) Logger +} diff --git a/vendor/github.com/caicloud/nirvana/log/silent.go b/vendor/github.com/caicloud/nirvana/log/silent.go new file mode 100644 index 0000000..3254a27 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/log/silent.go @@ -0,0 +1,88 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package log + +import "os" + +// SilentLogger logs nothing. +type SilentLogger struct{} + +var silentLogger Logger = &SilentLogger{} + +// V reports whether verbosity at the call site is at least the requested level. +// The returned value is a Verboser, which implements Info, Infof +func (l *SilentLogger) V(Level) Verboser { return l } + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (*SilentLogger) Info(...interface{}) {} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (*SilentLogger) Infof(string, ...interface{}) {} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (*SilentLogger) Infoln(...interface{}) {} + +// Warning logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func (*SilentLogger) Warning(...interface{}) {} + +// Warningf logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (*SilentLogger) Warningf(string, ...interface{}) {} + +// Warningln logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (*SilentLogger) Warningln(...interface{}) {} + +// Error logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func (*SilentLogger) Error(...interface{}) {} + +// Errorf logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (*SilentLogger) Errorf(string, ...interface{}) {} + +// Errorln logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (*SilentLogger) Errorln(...interface{}) {} + +// Fatal logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func (*SilentLogger) Fatal(v ...interface{}) { + os.Exit(1) +} + +// Fatalf logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (*SilentLogger) Fatalf(string, ...interface{}) { + os.Exit(1) +} + +// Fatalln logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (*SilentLogger) Fatalln(v ...interface{}) { + os.Exit(1) +} + +// Clone clones current logger with new wrapper. +// A positive wrapper indicates how many wrappers outside the logger. +func (l *SilentLogger) Clone(wrapper int) Logger { + return l +} diff --git a/vendor/github.com/caicloud/nirvana/log/std.go b/vendor/github.com/caicloud/nirvana/log/std.go new file mode 100644 index 0000000..321d238 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/log/std.go @@ -0,0 +1,189 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package log + +import ( + "fmt" + "io" + "os" + "path" + "runtime" + "time" +) + +// stdLogger writes logs to a stream. +type stdLogger struct { + level Level + stream io.Writer + wrapper int +} + +// NewStdLogger creates a stdandard logger for logging to stderr. +func NewStdLogger(level Level) Logger { + return newStderrLogger(level, 0) +} + +func newStderrLogger(level Level, wrapper int) *stdLogger { + return newStdLogger(level, os.Stderr, wrapper) +} + +func newStdLogger(level Level, stream io.Writer, wrapper int) *stdLogger { + return &stdLogger{ + level: level, + stream: stream, + wrapper: wrapper, + } +} + +var _ Logger = &stdLogger{} + +// V reports whether verbosity at the call site is at least the requested level. +// The returned value is a Verboser, which implements Info, Infof +// and Infoln. These methods will write to the Info log if called. +func (l *stdLogger) V(v Level) Verboser { + if v > l.level { + return silentLogger + } + return l +} + +// Info logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (l *stdLogger) Info(a ...interface{}) { + l.output(SeverityInfo, a...) +} + +// Infof logs to the INFO log. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (l *stdLogger) Infof(format string, a ...interface{}) { + l.outputf(SeverityInfo, format, a...) +} + +// Infoln logs to the INFO log. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (l *stdLogger) Infoln(a ...interface{}) { + l.outputln(SeverityInfo, a...) +} + +// Warning logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func (l *stdLogger) Warning(a ...interface{}) { + l.output(SeverityWarning, a...) +} + +// Warningf logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (l *stdLogger) Warningf(format string, a ...interface{}) { + l.outputf(SeverityWarning, format, a...) +} + +// Warningln logs to the WARNING logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (l *stdLogger) Warningln(a ...interface{}) { + l.outputln(SeverityWarning, a...) +} + +// Error logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func (l *stdLogger) Error(a ...interface{}) { + l.output(SeverityError, a...) +} + +// Errorf logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (l *stdLogger) Errorf(format string, a ...interface{}) { + l.outputf(SeverityError, format, a...) +} + +// Errorln logs to the ERROR logs. +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (l *stdLogger) Errorln(a ...interface{}) { + l.outputln(SeverityError, a...) +} + +// Fatal logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. +func (l *stdLogger) Fatal(a ...interface{}) { + l.output(SeverityFatal, a...) +} + +// Fatalf logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. +func (l *stdLogger) Fatalf(format string, a ...interface{}) { + l.outputf(SeverityFatal, format, a...) +} + +// Fatalln logs to the FATAL logs, then calls os.Exit(1). +// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. +func (l *stdLogger) Fatalln(a ...interface{}) { + l.outputln(SeverityFatal, a...) +} + +// Clone clones current logger with new wrapper. +// A positive wrapper indicates how many wrappers outside the logger. +func (l *stdLogger) Clone(wrapper int) Logger { + return &stdLogger{ + level: l.level, + stream: l.stream, + wrapper: l.wrapper + wrapper, + } +} + +func (l *stdLogger) output(severity Severity, a ...interface{}) { + l.write(prefix(severity, l.wrapper+2) + fmt.Sprint(a...)) + l.exitIfFatal(severity) +} + +func (l *stdLogger) outputf(severity Severity, format string, a ...interface{}) { + l.write(prefix(severity, l.wrapper+2) + fmt.Sprintf(format, a...)) + l.exitIfFatal(severity) +} + +func (l *stdLogger) outputln(severity Severity, a ...interface{}) { + l.write(prefix(severity, l.wrapper+2) + fmt.Sprintln(a...)) + l.exitIfFatal(severity) +} + +func (l *stdLogger) exitIfFatal(severity Severity) { + if severity == SeverityFatal { + os.Exit(1) + } +} + +func (l *stdLogger) write(data string) { + if data[len(data)-1] != '\n' { + data += "\n" + } + _, err := fmt.Fprint(l.stream, data) + // Ignore output error. + _ = err +} + +func prefix(severity Severity, depth int) string { + return fmt.Sprintf("%-5s %s %s | ", severity, time.Now().Format("0102-15:04:05.000-07"), file(1+depth)) +} + +func file(depth int) string { + _, file, number, ok := runtime.Caller(1 + depth) + if !ok { + file = "???" + number = 0 + } else { + file = path.Base(file) + } + return fmt.Sprintf("%s:%d", file, number) +} diff --git a/vendor/github.com/caicloud/nirvana/nirvana.go b/vendor/github.com/caicloud/nirvana/nirvana.go new file mode 100644 index 0000000..565ffb0 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/nirvana.go @@ -0,0 +1,388 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package nirvana + +import ( + "context" + "fmt" + "net/http" + "sync" + "sync/atomic" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/errors" + "github.com/caicloud/nirvana/log" + "github.com/caicloud/nirvana/service" + + // This blank import will make it in the dependencies of projects using Nirvana + // for API docs generation. + _ "github.com/caicloud/nirvana/utils/api" +) + +// Server is a complete API server. +// The server contains a router to handle all requests from clients. +type Server interface { + // Serve starts to listen and serve requests. + // The method won't return except an error occurs. + Serve() error + // Shutdown gracefully shuts down the server without interrupting any + // active connections. + Shutdown(ctx context.Context) error + // Builder create a service builder for current server. Don't use this method directly except + // there is a special server to hold http services. After server shutdown, clean resources via + // returned cleaner. + // This method always returns same builder until cleaner is called. Then it will + // returns new one. + Builder() (builder service.Builder, cleaner func() error, err error) +} + +// Config describes configuration of server. +type Config struct { + // tls cert file + certFile string + // tls ket file + keyFile string + // ip is the ip to listen. Empty means `0.0.0.0`. + ip string + // port is the port to listen. + port uint16 + // logger is used to output info inside framework. + logger log.Logger + // descriptors contains all APIs. + descriptors []definition.Descriptor + // filters is http filters. + filters []service.Filter + // modifiers is definition modifiers + modifiers service.DefinitionModifiers + // configSet contains all configurations of plugins. + configSet map[string]interface{} + // locked is for locking current config. If the field + // is not 0, any modification causes panic. + locked int32 +} + +// lock locks config. If succeed, it will return ture. +func (c *Config) lock() bool { + return atomic.CompareAndSwapInt32(&c.locked, 0, 1) +} + +// Locked checks if the config is locked. +func (c *Config) Locked() bool { + return atomic.LoadInt32(&c.locked) != 0 +} + +// IP returns listenning ip. +func (c *Config) IP() string { + return c.ip +} + +// Port returns listenning port. +func (c *Config) Port() uint16 { + return c.port +} + +// Logger returns logger. +func (c *Config) Logger() log.Logger { + return c.logger +} + +// Configurer is used to configure server config. +type Configurer func(c *Config) error + +var immutableConfig = errors.InternalServerError.Build("Nirvana:ImmutableConfig", "config has been locked and must not be modified") + +// Configure configs by configurers. It panics if an error occurs or config is locked. +func (c *Config) Configure(configurers ...Configurer) *Config { + if c.Locked() { + panic(immutableConfig.Error()) + } + for _, configurer := range configurers { + if err := configurer(c); err != nil { + panic(err) + } + } + return c +} + +// Config gets external config by name. This method is for plugins. +func (c *Config) Config(name string) interface{} { + return c.configSet[name] +} + +// Set sets external config by name. This method is for plugins. +// Set a nil config will delete it. +func (c *Config) Set(name string, config interface{}) { + if config == nil { + delete(c.configSet, name) + } else { + c.configSet[name] = config + } +} + +// forEach traverses all plugin configs. +func (c *Config) forEach(f func(name string, config interface{}) error) error { + for name, cfg := range c.configSet { + if err := f(name, cfg); err != nil { + return err + } + } + return nil +} + +// NewDefaultConfig creates default config. +// Default config contains: +// Filters: RedirectTrailingSlash, FillLeadingSlash, ParseRequestForm. +// Modifiers: FirstContextParameter, +// ConsumeAllIfConsumesIsEmpty, ProduceAllIfProducesIsEmpty, +// ConsumeNoneForHTTPGet, ConsumeNoneForHTTPDelete, +// ProduceNoneForHTTPDelete. +func NewDefaultConfig() *Config { + return NewConfig().Configure( + Logger(log.DefaultLogger()), + Filter( + service.RedirectTrailingSlash(), + service.FillLeadingSlash(), + service.ParseRequestForm(), + ), + Modifier( + service.FirstContextParameter(), + service.ConsumeAllIfConsumesIsEmpty(), + service.ProduceAllIfProducesIsEmpty(), + service.ConsumeNoneForHTTPGet(), + service.ConsumeNoneForHTTPDelete(), + service.ProduceNoneForHTTPDelete(), + ), + ) +} + +// NewConfig creates a pure config. If you don't know how to set filters and +// modifiers for specific scenario, please use NewDefaultConfig(). +func NewConfig() *Config { + return &Config{ + port: 8080, + logger: &log.SilentLogger{}, + configSet: make(map[string]interface{}), + } +} + +// server is nirvana server. +type server struct { + lock sync.Mutex + config *Config + server *http.Server + builder service.Builder + cleaner func() error +} + +// NewServer creates a nirvana server. After creation, don't modify +// config. Also don't create another server with current config. +func NewServer(c *Config) Server { + if c.Locked() || !c.lock() { + panic(immutableConfig.Error()) + } + return &server{ + config: c, + } +} + +var noConfigInstaller = errors.InternalServerError.Build("Nirvana:NoConfigInstaller", "no config installer for external config name ${name}") + +// Builder create a service builder for current server. Don't use this method directly except +// there is a special server to hold http services. After server shutdown, clean resources via +// returned cleaner. +// This method always returns same builder until cleaner is called. Then it will +// returns new one. +func (s *server) Builder() (builder service.Builder, cleaner func() error, err error) { + s.lock.Lock() + defer s.lock.Unlock() + if s.builder != nil { + return s.builder, s.cleaner, nil + } + builder = service.NewBuilder() + builder.SetLogger(s.config.logger) + builder.AddFilter(s.config.filters...) + builder.SetModifier(s.config.modifiers.Combine()) + if err := builder.AddDescriptor(s.config.descriptors...); err != nil { + return nil, nil, err + } + if err := s.config.forEach(func(name string, config interface{}) error { + installer := ConfigInstallerFor(name) + if installer == nil { + return noConfigInstaller.Error(name) + } + return installer.Install(builder, s.config) + }); err != nil { + return nil, nil, err + } + s.builder = builder + s.cleaner = func() (err error) { + // Clean builder and plugins. + s.lock.Lock() + defer func() { + if err == nil { + s.builder = nil + s.cleaner = nil + } + s.lock.Unlock() + }() + if s.builder == nil { + return nil + } + return s.config.forEach(func(name string, config interface{}) error { + installer := ConfigInstallerFor(name) + if installer == nil { + return noConfigInstaller.Error(name) + } + return installer.Uninstall(builder, s.config) + }) + } + return s.builder, s.cleaner, nil +} + +var builderInUse = errors.InternalServerError.Build("Nirvana:BuilderInUse", "service builder is in use, clean it before serving") + +// Serve starts to listen and serve requests. +// The method won't return except an error occurs. +func (s *server) Serve() (e error) { + s.lock.Lock() + if s.builder != nil || s.cleaner != nil { + return builderInUse.Error() + } + s.lock.Unlock() + builder, cleaner, err := s.Builder() + if err != nil { + return err + } + defer func() { + if err := cleaner(); err != nil { + s.config.logger.Error(err) + if e == nil { + e = err + } + } + }() + + service, err := builder.Build() + if err != nil { + return err + } + + s.server = &http.Server{ + Addr: fmt.Sprintf("%s:%d", s.config.ip, s.config.port), + Handler: service, + } + + if len(s.config.certFile) != 0 && len(s.config.keyFile) != 0 { + return s.server.ListenAndServeTLS(s.config.certFile, s.config.keyFile) + } + return s.server.ListenAndServe() +} + +// Shutdown gracefully shuts down the server without interrupting any +// active connections. +func (s *server) Shutdown(ctx context.Context) error { + if s.server != nil { + return s.server.Shutdown(ctx) + } + return nil +} + +// ConfigInstaller is used to install config to service builder. +type ConfigInstaller interface { + // Name is the external config name. + Name() string + // Install installs stuffs before server starting. + Install(builder service.Builder, config *Config) error + // Uninstall uninstalls stuffs after server terminating. + Uninstall(builder service.Builder, config *Config) error +} + +var installers = map[string]ConfigInstaller{} + +// ConfigInstallerFor gets installer by name. +func ConfigInstallerFor(name string) ConfigInstaller { + return installers[name] +} + +// RegisterConfigInstaller registers a config installer. +func RegisterConfigInstaller(ci ConfigInstaller) { + if ConfigInstallerFor(ci.Name()) != nil { + panic(fmt.Sprintf("Config installer %s has been installed.", ci.Name())) + } + installers[ci.Name()] = ci +} + +// IP returns a configurer to set ip into config. +func IP(ip string) Configurer { + return func(c *Config) error { + c.ip = ip + return nil + } +} + +// TLS returns a configurer to set certFile and keyFile in config. +func TLS(certFile, keyFile string) Configurer { + return func(c *Config) error { + c.certFile = certFile + c.keyFile = keyFile + return nil + } +} + +// Port returns a configurer to set port into config. +func Port(port uint16) Configurer { + return func(c *Config) error { + c.port = port + return nil + } +} + +// Logger returns a configurer to set logger into config. +func Logger(logger log.Logger) Configurer { + return func(c *Config) error { + if logger == nil { + c.logger = &log.SilentLogger{} + } else { + c.logger = logger + } + return nil + } +} + +// Descriptor returns a configurer to add descriptors into config. +func Descriptor(descriptors ...definition.Descriptor) Configurer { + return func(c *Config) error { + c.descriptors = append(c.descriptors, descriptors...) + return nil + } +} + +// Filter returns a configurer to add filters into config. +func Filter(filters ...service.Filter) Configurer { + return func(c *Config) error { + c.filters = append(c.filters, filters...) + return nil + } +} + +// Modifier returns a configurer to add definition modifiers into config. +func Modifier(modifiers ...service.DefinitionModifier) Configurer { + return func(c *Config) error { + c.modifiers = append(c.modifiers, modifiers...) + return nil + } +} diff --git a/vendor/github.com/caicloud/nirvana/operators/validator/tags.go b/vendor/github.com/caicloud/nirvana/operators/validator/tags.go new file mode 100644 index 0000000..81baf99 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/operators/validator/tags.go @@ -0,0 +1,124 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package validator + +// Tag is the validation tag can be used. +type Tag string + +// Tags for ref and doc gen. +const ( + TagIsColor Tag = "iscolor" + TagHasValue Tag = "required" + TagIsDefault Tag = "isdefault" + TagHasLengthOf Tag = "len" + TagHasMinOf Tag = "min" + TagHasMaxOf Tag = "max" + TagIsEq Tag = "eq" + TagIsNe Tag = "ne" + TagIsLt Tag = "lt" + TagIsLte Tag = "lte" + TagIsGt Tag = "gt" + TagIsGte Tag = "gte" + TagIsEqField Tag = "eqfield" + TagIsEqCrossStructField Tag = "eqcsfield" + TagIsNeCrossStructField Tag = "necsfield" + TagIsGtCrossStructField Tag = "gtcsfield" + TagIsGteCrossStructField Tag = "gtecsfield" + TagIsLtCrossStructField Tag = "ltcsfield" + TagIsLteCrossStructField Tag = "ltecsfield" + TagIsNeField Tag = "nefield" + TagIsGteField Tag = "gtefield" + TagIsGtField Tag = "gtfield" + TagIsLteField Tag = "ltefield" + TagIsLtField Tag = "ltfield" + TagIsAlpha Tag = "alpha" + TagIsAlphanum Tag = "alphanum" + TagIsAlphaUnicode Tag = "alphaunicode" + TagIsAlphanumUnicode Tag = "alphanumunicode" + TagIsNumeric Tag = "numeric" + TagIsNumber Tag = "number" + TagIsHexadecimal Tag = "hexadecimal" + TagIsHEXColor Tag = "hexcolor" + TagIsRGB Tag = "rgb" + TagIsRGBA Tag = "rgba" + TagIsHSL Tag = "hsl" + TagIsHSLA Tag = "hsla" + TagIsEmail Tag = "email" + TagIsURL Tag = "url" + TagIsURI Tag = "uri" + TagIsBase64 Tag = "base64" + TagContains Tag = "contains" + TagContainsAny Tag = "containsany" + TagContainsRune Tag = "containsrune" + TagExcludes Tag = "excludes" + TagExcludesAll Tag = "excludesall" + TagExcludesRune Tag = "excludesrune" + TagIsISBN Tag = "isbn" + TagIsISBN10 Tag = "isbn10" + TagIsISBN13 Tag = "isbn13" + TagIsUUID Tag = "uuid" + TagIsUUID3 Tag = "uuid3" + TagIsUUID4 Tag = "uuid4" + TagIsUUID5 Tag = "uuid5" + TagIsASCII Tag = "ascii" + TagIsPrintableASCII Tag = "printascii" + TagHasMultiByteCharacter Tag = "multibyte" + TagIsDataURI Tag = "datauri" + TagIsLatitude Tag = "latitude" + TagIsLongitude Tag = "longitude" + TagIsSSN Tag = "ssn" + TagIsIPv4 Tag = "ipv4" + TagIsIPv6 Tag = "ipv6" + TagIsIP Tag = "ip" + TagIsCIDRv4 Tag = "cidrv4" + TagIsCIDRv6 Tag = "cidrv6" + TagIsCIDR Tag = "cidr" + TagIsTCP4AddrResolvable Tag = "tcp4_addr" + TagIsTCP6AddrResolvable Tag = "tcp6_addr" + TagIsTCPAddrResolvable Tag = "tcp_addr" + TagIsUDP4AddrResolvable Tag = "udp4_addr" + TagIsUDP6AddrResolvable Tag = "udp6_addr" + TagIsUDPAddrResolvable Tag = "udp_addr" + TagIsIP4AddrResolvable Tag = "ip4_addr" + TagIsIP6AddrResolvable Tag = "ip6_addr" + TagIsIPAddrResolvable Tag = "ip_addr" + TagIsUnixAddrResolvable Tag = "unix_addr" + TagIsMAC Tag = "mac" + TagIsHostname Tag = "hostname" + TagIsFQDN Tag = "fqdn" + TagIsUnique Tag = "unique" +) + +// Special tags. +const ( + TagUTF8HexComma Tag = "0x2C" + TagUTF8Pipe Tag = "0x7C" + TagAndSeparator Tag = "," + TagOrSeparator Tag = "|" + TagKeySeparator Tag = "=" + TagStructOnly Tag = "structonly" + TagNoStructLevel Tag = "nostructlevel" + TagOmitempty Tag = "omitempty" + TagSkipValidationTag Tag = "-" + TagDiveTag Tag = "dive" + TagKeysTag Tag = "keys" + TagEndKeysTag Tag = "endkeys" + TagRequiredTag Tag = "required" + TagNamespaceSeparator Tag = "." + TagLeftBracket Tag = "[" + TagRightBracket Tag = "]" +) diff --git a/vendor/github.com/caicloud/nirvana/operators/validator/validator.go b/vendor/github.com/caicloud/nirvana/operators/validator/validator.go new file mode 100644 index 0000000..92fa306 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/operators/validator/validator.go @@ -0,0 +1,258 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package validator + +import ( + "context" + "fmt" + "reflect" + "strings" + + val "gopkg.in/go-playground/validator.v9" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/errors" +) + +var std = val.New() + +// OperatorKind means opeartor kind. All operators generated in this package +// are has kind `validator`. +const OperatorKind = "validator" + +// Category distinguishs validation type based on different Validator implementation. +type Category string + +const ( + // CategoryVar indicates that the validator can validate basic built-in types. + // Types: string, int*, uint*, bool. + CategoryVar Category = "Var" + // CategoryStruct indicates that the validator can validate struct. + CategoryStruct Category = "Struct" + // CategoryCustom indicates the validator is a custom validator. + CategoryCustom Category = "Custom" +) + +// Validator describes an interface for all validator. +type Validator interface { + definition.Operator + // Category indicates validator type. + Category() Category + // Tag returns tag. + Tag() string + // Description returns description of current validator. + Description() string +} + +type validator struct { + in reflect.Type + out reflect.Type + f func(ctx context.Context, field string, object interface{}) (interface{}, error) + category Category + tag string + description string +} + +// Kind indicates operator type. +func (o *validator) Kind() string { + return OperatorKind +} + +// In returns the type of the only object parameter of operator. +func (o *validator) In() reflect.Type { + return o.in +} + +// Out returns the type of the only object result of operator. +func (o *validator) Out() reflect.Type { + return o.out +} + +// Operate operates an object and return one. +func (o *validator) Operate(ctx context.Context, field string, object interface{}) (interface{}, error) { + return o.f(ctx, field, object) +} + +// Category indicates validator type. +func (o *validator) Category() Category { + return o.category +} + +// Tag returns tag. +func (o *validator) Tag() string { + return o.tag +} + +// Description returns description of current validator. +func (o *validator) Description() string { + return o.description +} + +// NewCustom calls f for validation, using description for doc gen. +// User should only do custom validation in f. +// Validations which can be done by other way should be done in another Operator. +// Exp: +// []definition.Operator{NewCustom(f,"custom validation description")} +// f should be func(ctx context.Context, object AnyType) error +func NewCustom(f interface{}, description string) Validator { + typ := reflect.TypeOf(f) + if typ.Kind() != reflect.Func { + panic(fmt.Sprintf("Parameter f in Custom Validator Func must be a function, but got: %s", typ.Kind())) + } + if typ.NumIn() != 2 { + panic(fmt.Sprintf("Custom Validator Func must have 2 parameters, but got: %d", typ.NumIn())) + } + if typ.In(0) != reflect.TypeOf((*context.Context)(nil)).Elem() { + panic(fmt.Sprintf("The first parameter of Custom Validator Func must be context.Context, but got: %v", typ.In(0))) + } + if typ.NumOut() != 1 { + panic(fmt.Sprintf("Parameter f in Custom Validator Func must have two results, but got: %d", typ.NumOut())) + } + if typ.Out(0).String() != "error" { + panic(fmt.Sprintf("The result of parameter f in Custom Validator Func must be error, but got: %v", typ.Out(1))) + } + op := definition.NewOperator(OperatorKind, typ.In(1), typ.In(1), func(ctx context.Context, _ string, object interface{}) (interface{}, error) { + v := reflect.ValueOf(f).Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(object)}) + if v[0].IsNil() { + return object, nil + } + return object, v[0].Interface().(error) + }) + return &validator{ + in: op.In(), + out: op.Out(), + f: op.Operate, + category: CategoryCustom, + description: description, + } +} + +// Struct returns an operator to validate a structs exposed fields, and automatically validates nested structs, unless otherwise specified +// and also allows passing of context.Context for contextual validation information. +func Struct(instance interface{}) Validator { + return &validator{ + in: reflect.TypeOf(instance), + out: reflect.TypeOf(instance), + f: func(ctx context.Context, field string, object interface{}) (interface{}, error) { + err := std.StructCtx(ctx, object) + return object, decorateStructErr(err) + }, + category: CategoryStruct, + } +} + +// String creates validator for string type. +func String(tag string) Validator { + return varFor(tag, "") +} + +// Int creates validator for int type. +func Int(tag string) Validator { + return varFor(tag, int(0)) +} + +// Int64 creates validator for int64 type. +func Int64(tag string) Validator { + return varFor(tag, int64(0)) +} + +// Int32 creates validator for int32 type. +func Int32(tag string) Validator { + return varFor(tag, int32(0)) +} + +// Int16 creates validator for int16 type. +func Int16(tag string) Validator { + return varFor(tag, int16(0)) +} + +// Int8 creates validator for int8 type. +func Int8(tag string) Validator { + return varFor(tag, int8(0)) +} + +// Byte creates validator for byte type. +func Byte(tag string) Validator { + return varFor(tag, byte(0)) +} + +// Uint creates validator for uint type. +func Uint(tag string) Validator { + return varFor(tag, uint(0)) +} + +// Uint64 creates validator for uint64 type. +func Uint64(tag string) Validator { + return varFor(tag, uint64(0)) +} + +// Uint32 creates validator for uint32 type. +func Uint32(tag string) Validator { + return varFor(tag, uint32(0)) +} + +// Uint16 creates validator for uint16 type. +func Uint16(tag string) Validator { + return varFor(tag, uint16(0)) +} + +// Uint8 creates validator for uint8 type. +func Uint8(tag string) Validator { + return varFor(tag, uint8(0)) +} + +// Bool creates validator for bool type. +func Bool(tag string) Validator { + return varFor(tag, bool(false)) +} + +// var returns an operator to validate a single variable using tag style validation and allows passing of contextual +// validation information via context.Context. +func varFor(tag string, instance interface{}) Validator { + return &validator{ + in: reflect.TypeOf(instance), + out: reflect.TypeOf(instance), + f: func(ctx context.Context, field string, object interface{}) (interface{}, error) { + err := std.VarCtx(ctx, object, tag) + return object, decorateErr(err, field, object, tag) + }, + category: CategoryVar, + tag: tag, + } +} + +func decorateErr(err error, field string, object interface{}, tag string) error { + if err == nil { + return nil + } + return errors.BadRequest.Error("value '${value}' on query param '${field}' cannot pass validator tag '${tag}'", object, field, tag) +} + +func decorateStructErr(err error) error { + if err == nil { + return nil + } + if err, ok := err.(val.ValidationErrors); ok { + es := make([]string, 0, len(err)) + for _, fe := range err { + es = append(es, fmt.Sprintf("value '%s' on struct field '%s' cannot pass validator tag '%s'", fe.Value(), fe.Field(), fe.Tag())) + } + return errors.BadRequest.Error("${err}", strings.Join(es, "; ")) + } + + return errors.BadRequest.Error("${err}", err) +} diff --git a/vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go b/vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go new file mode 100644 index 0000000..5461d49 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go @@ -0,0 +1,312 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package reqlog + +import ( + "context" + "time" + + "github.com/caicloud/nirvana" + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/log" + "github.com/caicloud/nirvana/service" +) + +func init() { + nirvana.RegisterConfigInstaller(&reqlogInstaller{}) +} + +// ExternalConfigName is the external config name of request logger. +const ExternalConfigName = "reqlog" + +// config is reqlog config. +type config struct { + doubleLog bool + sourceAddr bool + requestKey string + requestID bool + logger log.Logger +} + +type reqlogInstaller struct{} + +// Name is the external config name. +func (i *reqlogInstaller) Name() string { + return ExternalConfigName +} + +// Install installs stuffs before server starting. +func (i *reqlogInstaller) Install(builder service.Builder, cfg *nirvana.Config) error { + var err error + wrapper(cfg, func(c *config) { + begin, end := i.buildPrinters(c) + err = builder.AddDescriptor(definition.Descriptor{ + Path: "/", + Middlewares: []definition.Middleware{ + func(ctx context.Context, next definition.Chain) error { + start := time.Now() + httpCtx := service.HTTPContextFrom(ctx) + begin(httpCtx, nil) + + err := next.Continue(ctx) + end(httpCtx, map[string]interface{}{ + intervalDuration: time.Since(start), + responseError: err, + }) + return err + }, + }, + }) + }) + return err +} + +type printer func(ctx service.HTTPContext, data map[string]interface{}) + +const ( + intervalDuration = "intervalDuration" + responseError = "responseError" +) + +func (i *reqlogInstaller) buildPrinters(c *config) (begin printer, end printer) { + logger := c.logger + output := func(ctx service.HTTPContext, data map[string]interface{}, + components []func(ctx service.HTTPContext, data map[string]interface{}) interface{}) { + results := make([]interface{}, 0, len(components)) + for _, c := range components { + result := c(ctx, data) + if result == nil { + continue + } + results = append(results, result) + } + if logger != nil { + logger.Infoln(results...) + } else { + log.Infoln(results...) + } + } + method := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + return ctx.Request().Method + } + url := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + return ctx.Request().URL.String() + } + clientAddr := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + return ctx.Request().RemoteAddr + } + statusCode := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + return ctx.ResponseWriter().StatusCode() + } + contentLength := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + return ctx.ResponseWriter().ContentLength() + } + requestID := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + id := ctx.Request().Header.Get(c.requestKey) + if id == "" { + return nil + } + return id + } + interval := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + if data != nil { + if result, ok := data[intervalDuration]; ok { + return result + } + } + return nil + } + respErr := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { + if data != nil { + if result, ok := data[responseError]; ok { + return result + } + } + return nil + } + + beginning := []func(ctx service.HTTPContext, data map[string]interface{}) interface{}{} + if c.doubleLog { + beginning = append(beginning, method, url) + if c.requestID { + beginning = append(beginning, requestID) + } + if c.sourceAddr { + beginning = append(beginning, clientAddr) + } + } + + ending := []func(ctx service.HTTPContext, data map[string]interface{}) interface{}{ + method, + statusCode, + contentLength, + interval, + url, + } + if c.requestID { + ending = append(ending, requestID) + } + if c.sourceAddr { + ending = append(ending, clientAddr) + } + ending = append(ending, respErr) + return func(ctx service.HTTPContext, data map[string]interface{}) { + if c.doubleLog { + output(ctx, data, beginning) + } + }, + func(ctx service.HTTPContext, data map[string]interface{}) { + output(ctx, data, ending) + } +} + +// Uninstall uninstalls stuffs after server terminating. +func (i *reqlogInstaller) Uninstall(builder service.Builder, cfg *nirvana.Config) error { + return nil +} + +// Disable returns a configurer to disable reqlog. +func Disable() nirvana.Configurer { + return func(c *nirvana.Config) error { + c.Set(ExternalConfigName, nil) + return nil + } +} + +// Default Configurer does nothing but ensure default config was set. +func Default() nirvana.Configurer { + return func(c *nirvana.Config) error { + wrapper(c, func(c *config) { + c.logger = nil + }) + return nil + } +} + +// Logger Configurer sets logger. +func Logger(l log.Logger) nirvana.Configurer { + return func(c *nirvana.Config) error { + wrapper(c, func(c *config) { + c.logger = l + }) + return nil + } +} + +// DoubleLog returns a configurer to enable or +// disable double log. If it's enabled, every +// request outputs two entries. One for starting +// and another for ending. If it's disabled, only +// outputs ending entry. +// Defaults to false. +func DoubleLog(enable bool) nirvana.Configurer { + return func(c *nirvana.Config) error { + wrapper(c, func(c *config) { + c.doubleLog = enable + }) + return nil + } +} + +// SourceAddr returns a configurer to enable or +// disable showing source addr. +// Defaults to false. +func SourceAddr(enable bool) nirvana.Configurer { + return func(c *nirvana.Config) error { + wrapper(c, func(c *config) { + c.sourceAddr = enable + }) + return nil + } +} + +// RequestID returns a configurer to enable or +// disable showing request id. +// Defaults to false. +func RequestID(enable bool) nirvana.Configurer { + return func(c *nirvana.Config) error { + wrapper(c, func(c *config) { + c.requestID = enable + }) + return nil + } +} + +// RequestIDKey returns a configurer to set header key +// of request id. +// Defaults to X-Request-Id. +func RequestIDKey(key string) nirvana.Configurer { + if key == "" { + key = "X-Request-Id" + } + return func(c *nirvana.Config) error { + wrapper(c, func(c *config) { + c.requestKey = key + }) + return nil + } +} + +func wrapper(c *nirvana.Config, f func(c *config)) { + conf := c.Config(ExternalConfigName) + var cfg *config + if conf == nil { + // Default config. + cfg = &config{ + requestKey: "X-Request-Id", + } + } else { + // Panic if config type is wrong. + cfg = conf.(*config) + } + f(cfg) + c.Set(ExternalConfigName, cfg) +} + +// Option contains basic configurations of reqlog. +type Option struct { + DoubleLog bool `desc:"Output two entries for every request"` + SourceAddr bool `desc:"Output source addr for request log"` + RequestID bool `desc:"Output request id for request log"` + RequestIDKey string `desc:"Request header key for request id"` +} + +// NewDefaultOption creates default option. +func NewDefaultOption() *Option { + return &Option{ + DoubleLog: false, + SourceAddr: false, + RequestID: false, + RequestIDKey: "X-Request-Id", + } +} + +// Name returns plugin name. +func (p *Option) Name() string { + return ExternalConfigName +} + +// Configure configures nirvana config via current options. +func (p *Option) Configure(cfg *nirvana.Config) error { + cfg.Configure( + DoubleLog(p.DoubleLog), + SourceAddr(p.SourceAddr), + RequestID(p.RequestID), + RequestIDKey(p.RequestIDKey), + ) + return nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/builder.go b/vendor/github.com/caicloud/nirvana/service/builder.go new file mode 100644 index 0000000..a799d7e --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/builder.go @@ -0,0 +1,344 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "context" + "net/http" + "strings" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/log" + "github.com/caicloud/nirvana/service/router" +) + +// Builder builds service. +type Builder interface { + // Logger returns logger of builder. + Logger() log.Logger + // SetLogger sets logger to server. + SetLogger(logger log.Logger) + // Modifier returns modifier of builder. + Modifier() DefinitionModifier + // SetModifier sets definition modifier. + SetModifier(m DefinitionModifier) + // Filters returns all request filters. + Filters() []Filter + // AddFilters add filters to filter requests. + AddFilter(filters ...Filter) + // AddDescriptors adds descriptors to router. + AddDescriptor(descriptors ...definition.Descriptor) error + // Middlewares returns all router middlewares. + Middlewares() map[string][]definition.Middleware + // Definitions returns all definitions. If a modifier exists, it will be executed. + Definitions() map[string][]definition.Definition + // Build builds a service to handle request. + Build() (Service, error) +} + +type binding struct { + middlewares []definition.Middleware + definitions []definition.Definition +} + +type builder struct { + bindings map[string]*binding + modifier DefinitionModifier + filters []Filter + logger log.Logger +} + +// NewBuilder creates a service builder. +func NewBuilder() Builder { + return &builder{ + bindings: make(map[string]*binding), + logger: &log.SilentLogger{}, + } +} + +// Filters returns all request filters. +func (b *builder) Filters() []Filter { + result := make([]Filter, len(b.filters)) + copy(result, b.filters) + return result +} + +// AddFilters add filters to filter requests. +func (b *builder) AddFilter(filters ...Filter) { + b.filters = append(b.filters, filters...) +} + +// Logger returns logger of builder. +func (b *builder) Logger() log.Logger { + return b.logger +} + +// SetLogger sets logger to builder. +func (b *builder) SetLogger(logger log.Logger) { + if logger != nil { + b.logger = logger + } else { + b.logger = &log.SilentLogger{} + } +} + +// Modifier returns modifier of builder. +func (b *builder) Modifier() DefinitionModifier { + return b.modifier +} + +// SetModifier sets definition modifier. +func (b *builder) SetModifier(m DefinitionModifier) { + b.modifier = m +} + +// AddDescriptor adds descriptors to router. +func (b *builder) AddDescriptor(descriptors ...definition.Descriptor) error { + for _, descriptor := range descriptors { + b.addDescriptor("", nil, nil, nil, descriptor) + } + return nil +} + +func (b *builder) addDescriptor(prefix string, consumes []string, produces []string, tags []string, descriptor definition.Descriptor) { + path := strings.Join([]string{prefix, strings.Trim(descriptor.Path, "/")}, "/") + if descriptor.Consumes != nil { + consumes = descriptor.Consumes + } + if descriptor.Produces != nil { + produces = descriptor.Produces + } + if descriptor.Tags != nil { + tags = descriptor.Tags + } + if len(descriptor.Middlewares) > 0 || len(descriptor.Definitions) > 0 { + bd, ok := b.bindings[path] + if !ok { + bd = &binding{} + b.bindings[path] = bd + } + if len(descriptor.Middlewares) > 0 { + bd.middlewares = append(bd.middlewares, descriptor.Middlewares...) + } + if len(descriptor.Definitions) > 0 { + for _, d := range descriptor.Definitions { + bd.definitions = append(bd.definitions, *b.copyDefinition(&d, consumes, produces, tags)) + } + } + } + for _, child := range descriptor.Children { + b.addDescriptor(strings.TrimRight(path, "/"), consumes, produces, tags, child) + } +} + +// copyDefinition creates a copy from original definition. Those fields with type interface{} only have shallow copies. +func (b *builder) copyDefinition(d *definition.Definition, consumes []string, produces []string, tags []string) *definition.Definition { + newOne := &definition.Definition{ + Method: d.Method, + Summary: d.Summary, + Function: d.Function, + Description: d.Description, + } + if len(d.Consumes) > 0 { + consumes = d.Consumes + } + newOne.Consumes = make([]string, len(consumes)) + copy(newOne.Consumes, consumes) + + if len(d.Produces) > 0 { + produces = d.Produces + } + newOne.Produces = make([]string, len(produces)) + copy(newOne.Produces, produces) + + if len(d.Tags) > 0 { + tags = d.Tags + } + newOne.Tags = make([]string, len(tags)) + copy(newOne.Tags, tags) + + if len(d.ErrorProduces) > 0 { + produces = d.ErrorProduces + } + newOne.ErrorProduces = make([]string, len(produces)) + copy(newOne.ErrorProduces, produces) + + newOne.Parameters = make([]definition.Parameter, len(d.Parameters)) + for i, p := range d.Parameters { + newParameter := p + newParameter.Operators = make([]definition.Operator, len(p.Operators)) + copy(newParameter.Operators, p.Operators) + newOne.Parameters[i] = newParameter + } + newOne.Results = make([]definition.Result, len(d.Results)) + for i, r := range d.Results { + newResult := r + newResult.Operators = make([]definition.Operator, len(r.Operators)) + copy(newResult.Operators, r.Operators) + newOne.Results[i] = newResult + } + newOne.Examples = make([]definition.Example, len(d.Examples)) + copy(newOne.Examples, d.Examples) + return newOne +} + +// Middlewares returns all router middlewares. +func (b *builder) Middlewares() map[string][]definition.Middleware { + result := make(map[string][]definition.Middleware) + for path, bd := range b.bindings { + if len(bd.middlewares) > 0 { + middlewares := make([]definition.Middleware, len(bd.middlewares)) + copy(middlewares, bd.middlewares) + result[path] = middlewares + } + } + return result +} + +// Definitions returns all definitions. If a modifier exists, it will be executed. +// All results are copied from original definitions. Modifications can not affect +// original data. +func (b *builder) Definitions() map[string][]definition.Definition { + result := make(map[string][]definition.Definition) + for path, bd := range b.bindings { + if len(bd.definitions) > 0 { + definitions := make([]definition.Definition, len(bd.definitions)) + for i, d := range bd.definitions { + newCopy := b.copyDefinition(&d, nil, nil, nil) + if b.modifier != nil { + b.modifier(newCopy) + } + definitions[i] = *newCopy + } + result[path] = definitions + } + } + return result +} + +// Build builds a service to handle request. +func (b *builder) Build() (Service, error) { + if len(b.bindings) <= 0 { + return nil, noRouter.Error() + } + var root router.Router + for path, bd := range b.bindings { + b.logger.V(log.LevelDebug).Infof("Definitions: %d Middlewares: %d Path: %s", + len(bd.definitions), len(bd.middlewares), path) + top, leaf, err := router.Parse(path) + if err != nil { + b.logger.Errorf("Can't parse path: %s, %s", path, err.Error()) + return nil, err + } + if len(bd.definitions) > 0 { + // RedirectTrailingSlash would redirect "/somepath/" to "/somepath". Any definition under "/somepath/" + // will never be executed. + if len(path) > 1 && strings.HasSuffix(path, "/") { + b.logger.Warningf("If RedirectTrailingSlash filter is enabled, following %d definition(s) would not be executed", len(bd.definitions)) + } + inspector := newInspector(path, b.logger) + for _, d := range bd.definitions { + b.logger.V(log.LevelDebug).Infof(" Method: %s Consumes: %v Produces: %v", + d.Method, d.Consumes, d.Produces) + if b.modifier != nil { + b.modifier(&d) + } + if err := inspector.addDefinition(d); err != nil { + return nil, err + } + } + + leaf.SetInspector(inspector) + } + for _, m := range bd.middlewares { + m := m + leaf.AddMiddleware(func(ctx context.Context, chain router.RoutingChain) error { + return m(ctx, chain) + }) + } + if root == nil { + root = top + } else if root, err = root.Merge(top); err != nil { + return nil, err + } + } + s := &service{ + root: root, + filters: b.filters, + logger: b.logger, + producers: AllProducers(), + } + return s, nil +} + +// Service handles HTTP requests. +// +// Workflow: +// Service.ServeHTTP() +// ---------------------- +// ↓ ↑ +// |-----Filters------| ↑ +// ↓ ↑ +// |---Router Match---| ↑ +// ↓ ↑ +// |-------------Middlewares------------| +// ↓ ↑ +// |-------------Executor---------------| +// ↓ ↑ +// |-ParameterGenerators-|-DestinationHandlers-| +// ↓ ↑ +// |------------User Function-----------| +type Service interface { + http.Handler +} + +type service struct { + root router.Router + filters []Filter + logger log.Logger + producers []Producer +} + +func (s *service) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + for _, f := range s.filters { + if !f(resp, req) { + return + } + } + ctx := newHTTPContext(resp, req) + + executor, err := s.root.Match(ctx, &ctx.container, req.URL.EscapedPath()) + if err != nil { + if err := writeError(ctx, s.producers, err); err != nil { + s.logger.Error(err) + } + return + } + err = executor.Execute(ctx) + if err == nil && ctx.response.HeaderWritable() { + err = invalidService.Error() + } + if err != nil { + if ctx.response.HeaderWritable() { + if err := writeError(ctx, s.producers, err); err != nil { + s.logger.Error(err) + } + } else { + s.logger.Error(err) + } + } +} diff --git a/vendor/github.com/caicloud/nirvana/service/content.go b/vendor/github.com/caicloud/nirvana/service/content.go new file mode 100644 index 0000000..9c80e49 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/content.go @@ -0,0 +1,730 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "context" + "encoding/json" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "reflect" + "strconv" + + "github.com/caicloud/nirvana/definition" +) + +// Consumer handles specifically typed data from a reader and unmarshals it into an object. +type Consumer interface { + // ContentType returns a HTTP MIME type. + ContentType() string + // Consume unmarshals data from r into v. + Consume(r io.Reader, v interface{}) error +} + +// Producer marshals an object to specifically typed data and write it into a writer. +type Producer interface { + // ContentType returns a HTTP MIME type. + ContentType() string + // Produce marshals v to data and write to w. + Produce(w io.Writer, v interface{}) error +} + +var consumers = map[string]Consumer{ + definition.MIMENone: &NoneSerializer{}, + definition.MIMEText: NewSimpleSerializer(definition.MIMEText), + definition.MIMEJSON: &JSONSerializer{}, + definition.MIMEXML: &XMLSerializer{}, + definition.MIMEOctetStream: NewSimpleSerializer(definition.MIMEOctetStream), + definition.MIMEURLEncoded: &URLEncodedConsumer{}, + definition.MIMEFormData: &FormDataConsumer{}, +} + +var producers = map[string]Producer{ + definition.MIMENone: &NoneSerializer{}, + definition.MIMEText: NewSimpleSerializer(definition.MIMEText), + definition.MIMEJSON: &JSONSerializer{}, + definition.MIMEXML: &XMLSerializer{}, + definition.MIMEOctetStream: NewSimpleSerializer(definition.MIMEOctetStream), +} + +// AllConsumers returns all consumers. +func AllConsumers() []Consumer { + cs := make([]Consumer, 0, len(consumers)) + for _, c := range consumers { + cs = append(cs, c) + } + return cs +} + +// ConsumerFor gets a consumer for specified content type. +func ConsumerFor(contentType string) Consumer { + return consumers[contentType] +} + +// AllProducers returns all producers. +func AllProducers() []Producer { + ps := make([]Producer, 0, len(producers)) + // JSON always the first one in producers. + // The first one will be chosen when accept types + // are not recognized. + if p := producers[definition.MIMEJSON]; p != nil { + ps = append(ps, p) + } + for _, p := range producers { + if p.ContentType() == definition.MIMEJSON { + continue + } + ps = append(ps, p) + } + return ps +} + +// ProducerFor gets a producer for specified content type. +func ProducerFor(contentType string) Producer { + return producers[contentType] +} + +// RegisterConsumer register a consumer. A consumer must not handle "*/*". +func RegisterConsumer(c Consumer) error { + if c.ContentType() == definition.MIMEAll { + return invalidConsumer.Error(definition.MIMEAll) + } + consumers[c.ContentType()] = c + return nil +} + +// RegisterProducer register a producer. A producer must not handle "*/*". +func RegisterProducer(p Producer) error { + if p.ContentType() == definition.MIMEAll { + return invalidProducer.Error(definition.MIMEAll) + } + producers[p.ContentType()] = p + return nil +} + +// NoneSerializer implements Consumer and Producer for content types +// which can only receive data by io.Reader. +type NoneSerializer struct{} + +// ContentType returns none MIME type. +func (s *NoneSerializer) ContentType() string { + return definition.MIMENone +} + +// Consume does nothing. +func (s *NoneSerializer) Consume(r io.Reader, v interface{}) error { + return invalidTypeForConsumer.Error(s.ContentType(), reflect.TypeOf(v)) +} + +// Produce does nothing. +func (s *NoneSerializer) Produce(w io.Writer, v interface{}) error { + return invalidTypeForProducer.Error(s.ContentType(), reflect.TypeOf(v)) +} + +// RawSerializer implements a raw serializer. +type RawSerializer struct{} + +// CanConsumeData checks if raw serializer can consume type v with specified content type. +func (s *RawSerializer) CanConsumeData(contentType string, r io.Reader, v interface{}) bool { + switch v.(type) { + case *string, *[]byte: + return true + } + return false +} + +// ConsumeData reads data and converts it to string, []byte. +func (s *RawSerializer) ConsumeData(contentType string, r io.Reader, v interface{}) error { + switch target := v.(type) { + case *string: + data, err := ioutil.ReadAll(r) + if err != nil { + return err + } + *target = string(data) + return nil + case *[]byte: + data, err := ioutil.ReadAll(r) + if err != nil { + return err + } + *target = data + return nil + } + return invalidTypeForConsumer.Error(contentType, reflect.TypeOf(v)) +} + +// CanProduceData checks if raw serializer can produce data for specified content type from type v. +func (s *RawSerializer) CanProduceData(contentType string, w io.Writer, v interface{}) bool { + if _, ok := v.(io.Reader); ok { + return true + } + switch v.(type) { + case string, []byte: + return true + } + return false +} + +// ProduceData writes v to writer. v should be string, []byte, io.Reader. +func (s *RawSerializer) ProduceData(contentType string, w io.Writer, v interface{}) error { + if r, ok := v.(io.Reader); ok { + _, err := io.Copy(w, r) + return err + } + switch source := v.(type) { + case string: + _, err := io.WriteString(w, source) + return err + case []byte: + _, err := w.Write(source) + return err + } + return invalidTypeForProducer.Error(contentType, reflect.TypeOf(v)) +} + +// SimpleSerializer implements a simple serializer. +type SimpleSerializer struct { + RawSerializer + contentType string +} + +// NewSimpleSerializer creates a simple serializer. +func NewSimpleSerializer(contentType string) *SimpleSerializer { + return &SimpleSerializer{ + contentType: contentType, + } +} + +// ContentType returns plain text MIME type. +func (s *SimpleSerializer) ContentType() string { + return s.contentType +} + +// Consume reads data and converts it to string, []byte. +func (s *SimpleSerializer) Consume(r io.Reader, v interface{}) error { + return s.ConsumeData(s.ContentType(), r, v) +} + +// Produce writes v to writer. v should be string, []byte, io.Reader. +func (s *SimpleSerializer) Produce(w io.Writer, v interface{}) error { + if s.CanProduceData(s.ContentType(), w, v) { + return s.ProduceData(s.ContentType(), w, v) + } + if r, ok := v.(error); ok { + _, err := io.WriteString(w, r.Error()) + return err + } + if r, ok := v.(fmt.Stringer); ok { + _, err := io.WriteString(w, r.String()) + return err + } + return invalidTypeForProducer.Error(s.ContentType(), reflect.TypeOf(v)) +} + +// URLEncodedConsumer implements Consumer for content type "application/x-www-form-urlencoded" +type URLEncodedConsumer struct{ RawSerializer } + +// ContentType returns url encoded MIME type. +func (s *URLEncodedConsumer) ContentType() string { + return definition.MIMEURLEncoded +} + +// Consume reads data and converts it to string, []byte. +func (s *URLEncodedConsumer) Consume(r io.Reader, v interface{}) error { + return s.ConsumeData(s.ContentType(), r, v) +} + +// FormDataConsumer implements Consumer for content type "multipart/form-data" +type FormDataConsumer struct{ RawSerializer } + +// ContentType returns form data MIME type. +func (s *FormDataConsumer) ContentType() string { + return definition.MIMEFormData +} + +// Consume reads data and converts it to string, []byte. +func (s *FormDataConsumer) Consume(r io.Reader, v interface{}) error { + return s.ConsumeData(s.ContentType(), r, v) +} + +// JSONSerializer implements Consumer and Producer for content type "application/json". +type JSONSerializer struct{ RawSerializer } + +// ContentType returns json MIME type. +func (s *JSONSerializer) ContentType() string { + return definition.MIMEJSON +} + +// Consume unmarshals json from r into v. +func (s *JSONSerializer) Consume(r io.Reader, v interface{}) error { + if s.CanConsumeData(s.ContentType(), r, v) { + return s.ConsumeData(s.ContentType(), r, v) + } + err := json.NewDecoder(r).Decode(v) + if err == io.EOF { + return nil + } + return err +} + +// Produce marshals v to json and write to w. +func (s *JSONSerializer) Produce(w io.Writer, v interface{}) error { + if s.CanProduceData(s.ContentType(), w, v) { + return s.ProduceData(s.ContentType(), w, v) + } + return json.NewEncoder(w).Encode(v) +} + +// XMLSerializer implements Consumer and Producer for content type "application/xml". +type XMLSerializer struct{ RawSerializer } + +// ContentType returns xml MIME type. +func (s *XMLSerializer) ContentType() string { + return definition.MIMEXML +} + +// Consume unmarshals xml from r into v. +func (s *XMLSerializer) Consume(r io.Reader, v interface{}) error { + if s.CanConsumeData(s.ContentType(), r, v) { + return s.ConsumeData(s.ContentType(), r, v) + } + err := xml.NewDecoder(r).Decode(v) + if err == io.EOF { + return nil + } + return err +} + +// Produce marshals v to xml and write to w. +func (s *XMLSerializer) Produce(w io.Writer, v interface{}) error { + if s.CanProduceData(s.ContentType(), w, v) { + return s.ProduceData(s.ContentType(), w, v) + } + return xml.NewEncoder(w).Encode(v) +} + +// Prefab creates instances for internal type. These instances are not +// unmarshaled form http request data. +type Prefab interface { + // Name returns prefab name. + Name() string + // Type is instance type. + Type() reflect.Type + // Make makes an instance. + Make(ctx context.Context) (interface{}, error) +} + +var prefabs = map[string]Prefab{ + "context": &ContextPrefab{}, +} + +// PrefabFor gets a prefab by name. +func PrefabFor(name string) Prefab { + return prefabs[name] +} + +// RegisterPrefab registers a prefab. +func RegisterPrefab(prefab Prefab) error { + prefabs[prefab.Name()] = prefab + return nil +} + +// ContextPrefab returns context from parameter of Make(). +// It's usually used for generating the first parameter of api handler. +type ContextPrefab struct{} + +// Name returns prefab name. +func (p *ContextPrefab) Name() string { + return "context" +} + +// Type is type of context.Context. +func (p *ContextPrefab) Type() reflect.Type { + return reflect.TypeOf((*context.Context)(nil)).Elem() +} + +// Make returns context simply. +func (p *ContextPrefab) Make(ctx context.Context) (interface{}, error) { + return ctx, nil +} + +// Converter is used to convert []string to specific type. Data must have one +// element at least or it will panic. +type Converter func(ctx context.Context, data []string) (interface{}, error) + +var converters = map[reflect.Type]Converter{ + reflect.TypeOf(bool(false)): ConvertToBool, + reflect.TypeOf(int(0)): ConvertToInt, + reflect.TypeOf(int8(0)): ConvertToInt8, + reflect.TypeOf(int16(0)): ConvertToInt16, + reflect.TypeOf(int32(0)): ConvertToInt32, + reflect.TypeOf(int64(0)): ConvertToInt64, + reflect.TypeOf(uint(0)): ConvertToUint, + reflect.TypeOf(uint8(0)): ConvertToUint8, + reflect.TypeOf(uint16(0)): ConvertToUint16, + reflect.TypeOf(uint32(0)): ConvertToUint32, + reflect.TypeOf(uint64(0)): ConvertToUint64, + reflect.TypeOf(float32(0)): ConvertToFloat32, + reflect.TypeOf(float64(0)): ConvertToFloat64, + reflect.TypeOf(string("")): ConvertToString, + reflect.TypeOf(new(bool)): ConvertToBoolP, + reflect.TypeOf(new(int)): ConvertToIntP, + reflect.TypeOf(new(int8)): ConvertToInt8P, + reflect.TypeOf(new(int16)): ConvertToInt16P, + reflect.TypeOf(new(int32)): ConvertToInt32P, + reflect.TypeOf(new(int64)): ConvertToInt64P, + reflect.TypeOf(new(uint)): ConvertToUintP, + reflect.TypeOf(new(uint8)): ConvertToUint8P, + reflect.TypeOf(new(uint16)): ConvertToUint16P, + reflect.TypeOf(new(uint32)): ConvertToUint32P, + reflect.TypeOf(new(uint64)): ConvertToUint64P, + reflect.TypeOf(new(float32)): ConvertToFloat32P, + reflect.TypeOf(new(float64)): ConvertToFloat64P, + reflect.TypeOf(new(string)): ConvertToStringP, + reflect.TypeOf([]bool{}): ConvertToBoolSlice, + reflect.TypeOf([]int{}): ConvertToIntSlice, + reflect.TypeOf([]float64{}): ConvertToFloat64Slice, + reflect.TypeOf([]string{}): ConvertToStringSlice, +} + +// ConverterFor gets converter for specified type. +func ConverterFor(typ reflect.Type) Converter { + return converters[typ] +} + +// RegisterConverter registers a converter for specified type. New converter +// overrides old one. +func RegisterConverter(typ reflect.Type, converter Converter) { + converters[typ] = converter +} + +// ConvertToBool converts []string to bool. Only the first data is used. +func ConvertToBool(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseBool(origin) + if err != nil { + return nil, invalidConversion.Error(origin, "bool") + } + return target, nil +} + +// ConvertToBoolP converts []string to *bool. Only the first data is used. +func ConvertToBoolP(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToBool(ctx, data) + if err != nil { + return nil, err + } + value := ret.(bool) + return &value, nil +} + +// ConvertToInt converts []string to int. Only the first data is used. +func ConvertToInt(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseInt(origin, 10, 0) + if err != nil { + return nil, invalidConversion.Error(origin, "int") + } + return int(target), nil +} + +// ConvertToIntP converts []string to *int. Only the first data is used. +func ConvertToIntP(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToInt(ctx, data) + if err != nil { + return nil, err + } + value := ret.(int) + return &value, nil +} + +// ConvertToInt8 converts []string to int8. Only the first data is used. +func ConvertToInt8(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseInt(origin, 10, 8) + if err != nil { + return nil, invalidConversion.Error(origin, "int8") + } + return int8(target), nil +} + +// ConvertToInt8P converts []string to *int8. Only the first data is used. +func ConvertToInt8P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToInt8(ctx, data) + if err != nil { + return nil, err + } + value := ret.(int8) + return &value, nil +} + +// ConvertToInt16 converts []string to int16. Only the first data is used. +func ConvertToInt16(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseInt(origin, 10, 16) + if err != nil { + return nil, invalidConversion.Error(origin, "int16") + } + return int16(target), nil +} + +// ConvertToInt16P converts []string to *int16. Only the first data is used. +func ConvertToInt16P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToInt16(ctx, data) + if err != nil { + return nil, err + } + value := ret.(int16) + return &value, nil +} + +// ConvertToInt32 converts []string to int32. Only the first data is used. +func ConvertToInt32(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseInt(origin, 10, 32) + if err != nil { + return nil, invalidConversion.Error(origin, "int32") + } + return int32(target), nil +} + +// ConvertToInt32P converts []string to *int32. Only the first data is used. +func ConvertToInt32P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToInt32(ctx, data) + if err != nil { + return nil, err + } + value := ret.(int32) + return &value, nil +} + +// ConvertToInt64 converts []string to int64. Only the first data is used. +func ConvertToInt64(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseInt(origin, 10, 64) + if err != nil { + return nil, invalidConversion.Error(origin, "int64") + } + return target, nil +} + +// ConvertToInt64P converts []string to *int64. Only the first data is used. +func ConvertToInt64P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToInt64(ctx, data) + if err != nil { + return nil, err + } + value := ret.(int64) + return &value, nil +} + +// ConvertToUint converts []string to uint. Only the first data is used. +func ConvertToUint(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseUint(origin, 10, 0) + if err != nil { + return nil, invalidConversion.Error(origin, "uint") + } + return uint(target), nil +} + +// ConvertToUintP converts []string to *uint. Only the first data is used. +func ConvertToUintP(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToUint(ctx, data) + if err != nil { + return nil, err + } + value := ret.(uint) + return &value, nil +} + +// ConvertToUint8 converts []string to uint8. Only the first data is used. +func ConvertToUint8(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseUint(origin, 10, 8) + if err != nil { + return nil, invalidConversion.Error(origin, "uint8") + } + return uint8(target), nil +} + +// ConvertToUint8P converts []string to *uint8. Only the first data is used. +func ConvertToUint8P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToUint8(ctx, data) + if err != nil { + return nil, err + } + value := ret.(uint8) + return &value, nil +} + +// ConvertToUint16 converts []string to uint16. Only the first data is used. +func ConvertToUint16(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseUint(origin, 10, 16) + if err != nil { + return nil, invalidConversion.Error(origin, "uint16") + } + return uint16(target), nil +} + +// ConvertToUint16P converts []string to *uint16. Only the first data is used. +func ConvertToUint16P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToUint16(ctx, data) + if err != nil { + return nil, err + } + value := ret.(uint16) + return &value, nil +} + +// ConvertToUint32 converts []string to uint32. Only the first data is used. +func ConvertToUint32(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseUint(origin, 10, 32) + if err != nil { + return nil, invalidConversion.Error(origin, "uint32") + } + return uint32(target), nil +} + +// ConvertToUint32P converts []string to *uint32. Only the first data is used. +func ConvertToUint32P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToUint32(ctx, data) + if err != nil { + return nil, err + } + value := ret.(uint32) + return &value, nil +} + +// ConvertToUint64 converts []string to uint64. Only the first data is used. +func ConvertToUint64(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseUint(origin, 10, 64) + if err != nil { + return nil, invalidConversion.Error(origin, "uint64") + } + return target, nil +} + +// ConvertToUint64P converts []string to *uint64. Only the first data is used. +func ConvertToUint64P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToUint64(ctx, data) + if err != nil { + return nil, err + } + value := ret.(uint64) + return &value, nil +} + +// ConvertToFloat32 converts []string to float32. Only the first data is used. +func ConvertToFloat32(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseFloat(origin, 32) + if err != nil { + return nil, invalidConversion.Error(origin, "float32") + } + return float32(target), nil +} + +// ConvertToFloat32P converts []string to *float32. Only the first data is used. +func ConvertToFloat32P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToFloat32(ctx, data) + if err != nil { + return nil, err + } + value := ret.(float32) + return &value, nil +} + +// ConvertToFloat64 converts []string to float64. Only the first data is used. +func ConvertToFloat64(ctx context.Context, data []string) (interface{}, error) { + origin := data[0] + target, err := strconv.ParseFloat(origin, 64) + if err != nil { + return nil, invalidConversion.Error(origin, "float64") + } + return target, nil +} + +// ConvertToFloat64P converts []string to *float64. Only the first data is used. +func ConvertToFloat64P(ctx context.Context, data []string) (interface{}, error) { + ret, err := ConvertToFloat64(ctx, data) + if err != nil { + return nil, err + } + value := ret.(float64) + return &value, nil +} + +// ConvertToString return the first element in []string. +func ConvertToString(ctx context.Context, data []string) (interface{}, error) { + return data[0], nil +} + +// ConvertToStringP return the first element's pointer in []string. +func ConvertToStringP(ctx context.Context, data []string) (interface{}, error) { + return &data[0], nil +} + +// ConvertToBoolSlice converts all elements in data to bool, and return []bool +func ConvertToBoolSlice(ctx context.Context, data []string) (interface{}, error) { + ret := make([]bool, len(data)) + for i := range data { + r, err := ConvertToBool(ctx, data[i:i+1]) + if err != nil { + return nil, err + } + ret[i] = r.(bool) + } + return ret, nil +} + +// ConvertToIntSlice converts all elements in data to int, and return []int +func ConvertToIntSlice(ctx context.Context, data []string) (interface{}, error) { + ret := make([]int, len(data)) + for i := range data { + r, err := ConvertToInt(ctx, data[i:i+1]) + if err != nil { + return nil, err + } + ret[i] = r.(int) + } + return ret, nil +} + +// ConvertToFloat64Slice converts all elements in data to float64, and return []float64 +func ConvertToFloat64Slice(ctx context.Context, data []string) (interface{}, error) { + ret := make([]float64, len(data)) + for i := range data { + r, err := ConvertToFloat64(ctx, data[i:i+1]) + if err != nil { + return nil, err + } + ret[i] = r.(float64) + } + return ret, nil +} + +// ConvertToStringSlice return all strings in data. +func ConvertToStringSlice(ctx context.Context, data []string) (interface{}, error) { + return data, nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/context.go b/vendor/github.com/caicloud/nirvana/service/context.go new file mode 100644 index 0000000..f967494 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/context.go @@ -0,0 +1,288 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "bufio" + "context" + "io" + "mime/multipart" + "net" + "net/http" + "net/textproto" + "net/url" +) + +var ( + // contextKeyUnderlyingHTTPContext is a key for context. + // It's unique and point to httpCtx. + contextKeyUnderlyingHTTPContext interface{} = new(byte) +) + +// httpCtx contains a http.Request and a http.ResponseWriter for a request. +// It goes through the life cycle of a request. +type httpCtx struct { + context.Context + container container + response response + path string +} + +func newHTTPContext(resp http.ResponseWriter, request *http.Request) *httpCtx { + ctx := &httpCtx{} + ctx.Context = request.Context() + ctx.container.request = request + ctx.container.params = make([]param, 0, 5) + ctx.response.writer = resp + return ctx +} + +// Value returns itself when key is contextKeyUnderlyingHTTPContext. +func (c *httpCtx) Value(key interface{}) interface{} { + if key == contextKeyUnderlyingHTTPContext { + return c + } + return c.Context.Value(key) +} + +// ValueContainer contains values from a request. +type ValueContainer interface { + // Path returns path value by key. + Path(key string) (string, bool) + // Query returns value from query string. + Query(key string) ([]string, bool) + // Header returns value by header key. + Header(key string) ([]string, bool) + // Form returns value from request. It is valid when + // http "Content-Type" is "application/x-www-form-urlencoded" + // or "multipart/form-data". + Form(key string) ([]string, bool) + // File returns a file reader when "Content-Type" is "multipart/form-data". + File(key string) (multipart.File, bool) + // Body returns a reader to read data from request body. + // The reader only can read once. + Body() (reader io.ReadCloser, contentType string, ok bool) +} + +type param struct { + key string + value string +} + +// container implements ValueContainer and provides methods to get values. +type container struct { + request *http.Request + params []param + query url.Values +} + +// Set sets path parameter key-value pairs. +func (c *container) Set(key, value string) { + c.params = append(c.params, param{key, value}) +} + +// Get gets path value. +func (c *container) Get(key string) (string, bool) { + for i := len(c.params) - 1; i >= 0; i-- { + p := c.params[i] + if p.key == key { + return p.value, true + } + } + return "", false +} + +// Path returns path value by key. It's same as Get(). +func (c *container) Path(key string) (string, bool) { + return c.Get(key) +} + +// Query returns value from query string. +func (c *container) Query(key string) ([]string, bool) { + if c.query == nil { + c.query = c.request.URL.Query() + } + return c.removeEmpties(c.query[key]) +} + +// Header returns value by header key. +func (c *container) Header(key string) ([]string, bool) { + h := c.request.Header[textproto.CanonicalMIMEHeaderKey(key)] + return c.removeEmpties(h) +} + +// Form returns value from request. It is valid when +// http "Content-Type" is "application/x-www-form-urlencoded" +// or "multipart/form-data". +func (c *container) Form(key string) ([]string, bool) { + return c.removeEmpties(c.request.PostForm[key]) +} + +// removeEmpties removes empty strings. +func (c *container) removeEmpties(values []string) ([]string, bool) { + if len(values) <= 0 { + return values, false + } + results := make([]string, 0, len(values)) + for _, value := range values { + if value != "" { + results = append(results, value) + } + } + return results, len(results) > 0 +} + +// File returns a file reader when "Content-Type" is "multipart/form-data". +func (c *container) File(key string) (multipart.File, bool) { + file, _, err := c.request.FormFile(key) + return file, err == nil +} + +// Body returns a reader to read data from request body. +// The reader only can read once. +func (c *container) Body() (reader io.ReadCloser, contentType string, ok bool) { + contentType, err := ContentType(c.request) + return c.request.Body, contentType, err == nil +} + +// ResponseWriter extends http.ResponseWriter. +type ResponseWriter interface { + http.ResponseWriter + // HeaderWritable can check whether WriteHeader() has + // been called. If the method returns false, you should + // not recall WriteHeader(). + HeaderWritable() bool + // StatusCode returns status code. + StatusCode() int + // ContentLength returns the length of written content. + ContentLength() int +} + +type response struct { + writer http.ResponseWriter + statusCode int + contentLength int + hijacked bool +} + +// For http.HTTPResponseWriter and HTTPResponseInfo +func (c *response) Header() http.Header { + return c.writer.Header() +} + +// Write is a disguise of http.response.Write(). +func (c *response) Write(data []byte) (int, error) { + if c.statusCode <= 0 { + c.WriteHeader(200) + } + length, err := c.writer.Write(data) + c.contentLength += length + return length, err +} + +// WriteHeader is a disguise of http.response.WriteHeader(). +func (c *response) WriteHeader(code int) { + c.statusCode = code + c.writer.WriteHeader(code) +} + +// Flush is a disguise of http.response.Flush(). +func (c *response) Flush() { + c.writer.(http.Flusher).Flush() +} + +// CloseNotify is a disguise of http.response.CloseNotify(). +// +// Deprecated: use `http.Request.Context.Done()` as instead. +func (c *response) CloseNotify() <-chan bool { + return c.writer.(http.CloseNotifier).CloseNotify() +} + +// Hijack is a disguise of http.response.Hijack(). +func (c *response) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if h, ok := c.writer.(http.Hijacker); ok { + c.hijacked = true + return h.Hijack() + } + return nil, nil, noConnectionHijacker.Error() +} + +// StatusCode returns status code. +func (c *response) StatusCode() int { + if c.statusCode <= 0 { + return http.StatusOK + } + return c.statusCode +} + +// ContentLength returns the length of written content. +func (c *response) ContentLength() int { + return c.contentLength +} + +// HeaderWritable can check whether WriteHeader() has +// been called. If the method returns false, you should +// not recall WriteHeader(). +func (c *response) HeaderWritable() bool { + return !c.hijacked && c.statusCode <= 0 +} + +// HTTPContext describes an http context. +type HTTPContext interface { + Request() *http.Request + ResponseWriter() ResponseWriter + ValueContainer() ValueContainer + RoutePath() string + setRoutePath(path string) +} + +// HTTPContextFrom get http context from context. +func HTTPContextFrom(ctx context.Context) HTTPContext { + value := ctx.Value(contextKeyUnderlyingHTTPContext) + if value == nil { + return nil + } + if c, ok := value.(*httpCtx); ok { + return c + } + return nil +} + +// Request gets http.Request. +func (c *httpCtx) Request() *http.Request { + return c.container.request +} + +// ResponseWriter gets ResponseWriter. +func (c *httpCtx) ResponseWriter() ResponseWriter { + return &c.response +} + +// ValueContainer gets ValueContainer. +func (c *httpCtx) ValueContainer() ValueContainer { + return &c.container +} + +// RoutePath is the abstract path which matches request URL. +func (c *httpCtx) RoutePath() string { + return c.path +} + +// setRoutePath sets the abstract path which matches request URL. +func (c *httpCtx) setRoutePath(path string) { + c.path = path +} diff --git a/vendor/github.com/caicloud/nirvana/service/executor.go b/vendor/github.com/caicloud/nirvana/service/executor.go new file mode 100644 index 0000000..2506c28 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/executor.go @@ -0,0 +1,515 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "context" + "fmt" + "io" + "path" + "reflect" + "runtime" + "sort" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/log" + "github.com/caicloud/nirvana/service/router" +) + +type inspector struct { + path string + logger log.Logger + executors map[string][]*executor +} + +func newInspector(path string, logger log.Logger) *inspector { + return &inspector{ + path: path, + logger: logger, + executors: map[string][]*executor{}, + } +} + +func (i *inspector) addDefinition(d definition.Definition) error { + method := HTTPMethodFor(d.Method) + if method == "" { + return definitionNoMethod.Error(d.Method, i.path) + } + if len(d.Consumes) <= 0 { + return definitionNoConsumes.Error(d.Method, i.path) + } + if len(d.Produces) <= 0 { + return definitionNoProduces.Error(d.Method, i.path) + } + if len(d.ErrorProduces) <= 0 { + return definitionNoErrorProduces.Error(d.Method, i.path) + } + if d.Function == nil { + return definitionNoFunction.Error(d.Method, i.path) + } + value := reflect.ValueOf(d.Function) + if value.Kind() != reflect.Func { + return definitionInvalidFunctionType.Error(value.Type(), d.Method, i.path) + } + c := &executor{ + logger: i.logger, + method: method, + code: HTTPCodeFor(d.Method), + function: value, + } + consumeAll := false + consumes := map[string]bool{} + for _, ct := range d.Consumes { + if ct == definition.MIMEAll { + consumeAll = true + continue + } + if consumer := ConsumerFor(ct); consumer != nil { + c.consumers = append(c.consumers, consumer) + consumes[consumer.ContentType()] = true + } else { + return definitionNoConsumer.Error(ct, d.Method, i.path) + } + } + if consumeAll { + // Add remaining consumers to executor. + for _, consumer := range AllConsumers() { + if !consumes[consumer.ContentType()] { + c.consumers = append(c.consumers, consumer) + } + } + } + produceAll := false + produces := map[string]bool{} + for _, ct := range d.Produces { + if ct == definition.MIMEAll { + produceAll = true + continue + } + if producer := ProducerFor(ct); producer != nil { + c.producers = append(c.producers, producer) + produces[producer.ContentType()] = true + } else { + return definitionNoProducer.Error(ct, d.Method, i.path) + } + } + if produceAll { + // Add remaining producers to executor. + for _, producer := range AllProducers() { + if !produces[producer.ContentType()] { + c.producers = append(c.producers, producer) + } + } + } + errorProduceAll := false + errorProduces := map[string]bool{} + for _, ct := range d.ErrorProduces { + if ct == definition.MIMEAll { + errorProduceAll = true + continue + } + if producer := ProducerFor(ct); producer != nil { + c.errorProducers = append(c.errorProducers, producer) + errorProduces[producer.ContentType()] = true + } else { + return definitionNoProducer.Error(ct, d.Method, i.path) + } + } + if errorProduceAll { + // Add remaining producers to executor. + for _, producer := range AllProducers() { + if !errorProduces[producer.ContentType()] { + c.errorProducers = append(c.errorProducers, producer) + } + } + } + // Get func name and file position. + f := runtime.FuncForPC(value.Pointer()) + file, line := f.FileLine(value.Pointer()) + // Function name examples: + // 1. Common function: api.CreateSomething(create.go#30) + // 2. Anonymous function: api.glob..func1(create.go#30) + // Anonymous function names are generated by go. Don't explore their meaning. + funcName := fmt.Sprintf("%s(%s#%d)", path.Base(f.Name()), path.Base(file), line) + ps, err := i.generateParameters(funcName, value.Type(), d.Parameters) + if err != nil { + return err + } + c.parameters = ps + rs, err := i.generateResults(funcName, value.Type(), d.Results) + if err != nil { + return err + } + c.results = rs + if err := i.conflictCheck(c); err != nil { + return err + } + i.executors[method] = append(i.executors[method], c) + return nil +} + +func (i *inspector) conflictCheck(c *executor) error { + cs := i.executors[c.method] + if len(cs) <= 0 { + return nil + } + ctMap := map[string]bool{} + for _, extant := range cs { + result := extant.ctMap() + for k, vs := range result { + for _, v := range vs { + ctMap[k+":"+v] = true + } + } + } + cMap := c.ctMap() + for k, vs := range cMap { + for _, v := range vs { + if ctMap[k+":"+v] { + return definitionConflict.Error(k, v, c.method, i.path) + } + } + } + return nil +} + +func (i *inspector) generateParameters(funcName string, typ reflect.Type, ps []definition.Parameter) ([]parameter, error) { + if typ.NumIn() != len(ps) { + return nil, definitionUnmatchedParameters.Error(funcName, typ.NumIn(), len(ps), i.path) + } + parameters := make([]parameter, 0, len(ps)) + for index, p := range ps { + generator := ParameterGeneratorFor(p.Source) + if generator == nil { + return nil, noParameterGenerator.Error(p.Source) + } + + param := parameter{ + name: p.Name, + defaultValue: p.Default, + generator: generator, + operators: p.Operators, + } + if len(p.Operators) <= 0 { + param.targetType = typ.In(index) + } else { + param.targetType = p.Operators[0].In() + } + if err := generator.Validate(param.name, param.defaultValue, param.targetType); err != nil { + // Order from 0 is odd. So index+1. + i.logger.Errorf("Can't validate %s parameter of function %s: %s", order(index+1), funcName, err.Error()) + return nil, err + } + if len(param.operators) > 0 { + if err := i.validateOperators(param.targetType, typ.In(index), param.operators); err != nil { + i.logger.Errorf("Can't validate operators for %s parameter of function %s: %s", order(index+1), funcName, err.Error()) + return nil, err + } + } + parameters = append(parameters, param) + } + return parameters, nil +} + +func (i *inspector) generateResults(funcName string, typ reflect.Type, rs []definition.Result) ([]result, error) { + if typ.NumOut() != len(rs) { + return nil, definitionUnmatchedResults.Error(funcName, typ.NumOut(), len(rs), i.path) + } + results := make([]result, 0, len(rs)) + for index, r := range rs { + handler := DestinationHandlerFor(r.Destination) + if handler == nil { + return nil, noDestinationHandler.Error(r.Destination) + } + result := result{ + index: index, + handler: handler, + operators: r.Operators, + } + outType := typ.Out(index) + if len(result.operators) > 0 { + LastOperatorOutType := result.operators[len(result.operators)-1].Out() + if err := i.validateOperators(outType, LastOperatorOutType, result.operators); err != nil { + i.logger.Errorf("Can't validate operators for %s result of function %s: %s", order(index+1), funcName, err.Error()) + return nil, err + } + outType = LastOperatorOutType + } + if err := handler.Validate(outType); err != nil { + // Order from 0 is odd. So index+1. + i.logger.Errorf("Can't validate %s result of function %s: %s", order(index+1), funcName, err.Error()) + return nil, err + } + results = append(results, result) + } + sort.Sort(resultsSorter(results)) + return results, nil + +} + +// validateOperators checks if the chain is valid: +// in -> operators[0].In() +// operators[0].Out() -> operators[1].In() +// ... +// operators[N].Out() -> out +func (i *inspector) validateOperators(in, out reflect.Type, operators []definition.Operator) error { + if len(operators) <= 0 { + return nil + } + index := 0 + for ; index < len(operators); index++ { + operator := operators[index] + if !in.AssignableTo(operator.In()) { + // The out type of operator[index-1] is not compatible to operator[index]. + return invalidOperatorInType.Error(in, order(index+1)) + } + in = operator.Out() + } + typ := operators[index-1].Out() + if !typ.AssignableTo(out) { + // The last operator is not compatible to out type. + return invalidOperatorOutType.Error(order(index), out) + } + return nil +} + +type resultsSorter []result + +// Len is the number of elements in the collection. +func (s resultsSorter) Len() int { + return len(s) +} + +// Less reports whether the element with +// index i should sort before the element with index j. +func (s resultsSorter) Less(i, j int) bool { + return s[i].handler.Priority() < s[j].handler.Priority() +} + +// Swap swaps the elements with indexes i and j. +func (s resultsSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Inspect finds a valid executor to execute target context. +func (i *inspector) Inspect(ctx context.Context) (router.Executor, error) { + httpCtx := HTTPContextFrom(ctx) + req := httpCtx.Request() + if req == nil { + return nil, noContext.Error() + } + executors := []*executor{} + if cs, ok := i.executors[req.Method]; ok && len(cs) > 0 { + executors = append(executors, cs...) + } + if len(executors) <= 0 { + return nil, noExecutorForMethod.Error() + } + ct, err := ContentType(req) + if err != nil { + return nil, err + } + accepted := 0 + for i, c := range executors { + if c.acceptable(ct) { + if accepted != i { + executors[accepted] = c + } + accepted++ + } + } + if accepted <= 0 { + return nil, noExecutorForContentType.Error() + } + ats, err := AcceptTypes(req) + if err != nil { + return nil, err + } + executors = executors[:accepted] + var target *executor + for _, c := range executors { + if c.producible(ats) { + target = c + break + } + } + if target == nil { + for _, at := range ats { + if at == definition.MIMEAll { + target = executors[0] + } + } + } + if target == nil { + return nil, noExecutorToProduce.Error() + } + httpCtx.setRoutePath(i.path) + return target, nil +} + +type executor struct { + logger log.Logger + method string + code int + consumers []Consumer + producers []Producer + errorProducers []Producer + parameters []parameter + results []result + function reflect.Value +} + +type parameter struct { + name string + targetType reflect.Type + defaultValue interface{} + generator ParameterGenerator + operators []definition.Operator +} + +type result struct { + index int + handler DestinationHandler + operators []definition.Operator +} + +func (e *executor) ctMap() map[string][]string { + result := map[string][]string{} + for _, c := range e.consumers { + for _, p := range e.producers { + ct := c.ContentType() + result[ct] = append(result[ct], p.ContentType()) + } + } + return result +} + +func (e *executor) acceptable(ct string) bool { + for _, c := range e.consumers { + if c.ContentType() == ct { + return true + } + } + return false +} + +func (e *executor) check(producers []Producer, ats []string) bool { + for _, at := range ats { + for _, c := range producers { + if c.ContentType() == at { + return true + } + } + } + return false +} + +func (e *executor) producible(ats []string) bool { + return e.check(e.producers, ats) && e.check(e.errorProducers, ats) +} + +// Execute executes with context. +func (e *executor) Execute(ctx context.Context) (err error) { + c := HTTPContextFrom(ctx) + if c == nil { + return noContext.Error() + } + paramValues := make([]reflect.Value, 0, len(e.parameters)) + for _, p := range e.parameters { + result, err := p.generator.Generate(ctx, c.ValueContainer(), e.consumers, p.name, p.targetType) + if err != nil { + return writeError(ctx, e.errorProducers, err) + } + if result == nil { + if p.defaultValue != nil { + result = p.defaultValue + } else { + result = reflect.Zero(p.targetType).Interface() + } + } + for _, operator := range p.operators { + result, err = operator.Operate(ctx, p.name, result) + if err != nil { + return writeError(ctx, e.errorProducers, err) + } + } + if result == nil { + return writeError(ctx, e.errorProducers, requiredField.Error(p.name, p.generator.Source())) + } else if closer, ok := result.(io.Closer); ok { + defer func() { + if e := closer.Close(); e != nil && err == nil { + // Need to print error here. + err = e + } + }() + } + + paramValues = append(paramValues, reflect.ValueOf(result)) + } + resultValues := e.function.Call(paramValues) + for _, r := range e.results { + v := resultValues[r.index] + data := v.Interface() + for _, operator := range r.operators { + newData, err := operator.Operate(ctx, string(r.handler.Destination()), data) + if err != nil { + return err + } + data = newData + } + if data != nil { + if closer, ok := data.(io.Closer); ok { + defer func() { + if e := closer.Close(); e != nil && err == nil { + // Need to print error here. + err = e + } + }() + } + } + producers := e.producers + if r.handler.Destination() == definition.Error { + // Select correct producers to produce error. + producers = e.errorProducers + } + goon, err := r.handler.Handle(ctx, producers, e.code, data) + if err != nil { + return err + } + if !goon { + break + } + } + resp := c.ResponseWriter() + if resp.HeaderWritable() { + resp.WriteHeader(e.code) + } + return nil +} + +func order(i int) string { + switch i % 10 { + case 1: + return fmt.Sprintf("%dst", i) + case 2: + return fmt.Sprintf("%dnd", i) + case 3: + return fmt.Sprintf("%drd", i) + default: + return fmt.Sprintf("%dth", i) + } +} diff --git a/vendor/github.com/caicloud/nirvana/service/filter.go b/vendor/github.com/caicloud/nirvana/service/filter.go new file mode 100644 index 0000000..88b22e6 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/filter.go @@ -0,0 +1,180 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "fmt" + "mime" + "net/http" + "sort" + "strconv" + "strings" + + "github.com/caicloud/nirvana/definition" +) + +// Filter can filter request. It has the highest priority in a request +// lifecycle. It runs before router matching. +// If a filter return false, that means the request should be filtered. +// If a filter want to filter a request, it should handle the request +// by itself. +type Filter func(resp http.ResponseWriter, req *http.Request) bool + +// RedirectTrailingSlash returns a filter to redirect request. +// If a request has trailing slash like `some-url/`, the filter will +// redirect the request to `some-url`. +func RedirectTrailingSlash() Filter { + return func(resp http.ResponseWriter, req *http.Request) bool { + path := req.URL.Path + if len(path) > 1 && path[len(path)-1] == '/' { + req.URL.Path = strings.TrimRight(path, "/") + // Redirect to path without trailing slash. + http.Redirect(resp, req, req.URL.String(), http.StatusTemporaryRedirect) + return false + } + return true + } +} + +// FillLeadingSlash returns a pseudo filter to fill a leading slash when +// a request path does not have a leading slash. +// The filter won't filter anything. +func FillLeadingSlash() Filter { + return func(resp http.ResponseWriter, req *http.Request) bool { + path := req.URL.Path + if len(path) <= 0 || path[0] != '/' { + // Relative path may omit leading slash. + req.URL.Path = "/" + path + } + return true + } +} + +// ParseRequestForm returns a filter to parse request form when content +// type is "application/x-www-form-urlencoded" or "multipart/form-data". +// The filter won't filter anything unless some error occurs in parsing. +func ParseRequestForm() Filter { + return func(resp http.ResponseWriter, req *http.Request) bool { + ct, err := ContentType(req) + if err == nil { + switch ct { + case definition.MIMEURLEncoded: + err = req.ParseForm() + case definition.MIMEFormData: + err = req.ParseMultipartForm(32 << 20) + default: + req.Form = req.URL.Query() + } + } + if err != nil { + http.Error(resp, err.Error(), http.StatusBadRequest) + return false + } + return true + } +} + +// isGTZero returns a boolean result indicating if the content length is greater than 0. +func isGTZero(length string) bool { + if length == "" { + return false + } + i, err := strconv.Atoi(length) + if err != nil { + return false + } + return i > 0 +} + +// ContentType is a util to get content type from a request. +func ContentType(req *http.Request) (string, error) { + ct := req.Header.Get("Content-Type") + if ct == "" { + length := req.Header.Get("Content-Length") + transfer := req.Header.Get("Transfer-Encoding") + if isGTZero(length) || transfer != "" { + return definition.MIMEOctetStream, nil + } + return definition.MIMENone, nil + } + result, _, err := mime.ParseMediaType(ct) + if err != nil { + return "", invalidContentType.Error(ct) + } + return result, nil +} + +// AcceptTypes is a util to get accept types from a request. +// Accept types are sorted by q. +func AcceptTypes(req *http.Request) ([]string, error) { + ct := req.Header.Get("Accept") + if ct == "" { + return []string{definition.MIMEAll}, nil + } + return parseAcceptTypes(ct) +} + +type acceptType struct { + name string + preference float64 +} + +func parseAcceptTypes(v string) ([]string, error) { + var types []acceptType + strs := strings.Split(v, ",") + for _, str := range strs { + fields := strings.Split(str, ";") + factor := 1.0 + ctFields := make([]string, 0, len(fields)) + for _, field := range fields { + index := strings.IndexByte(field, '=') + key := "" + value := "" + if index >= 0 { + key = strings.TrimSpace(field[:index]) + value = strings.TrimSpace(field[index+1:]) + if key == "q" && len(value) > 0 { + q, err := strconv.ParseFloat(value, 32) + if err != nil { + return nil, err + } + factor = q + continue + } + } else { + key = strings.TrimSpace(field) + } + if value == "" { + ctFields = append(ctFields, key) + } else { + ctFields = append(ctFields, fmt.Sprintf("%s=%s", key, value)) + } + } + types = append(types, acceptType{ + name: strings.Join(ctFields, ";"), + preference: factor, + }) + } + sort.Slice(types, func(i, j int) bool { + return types[i].preference > types[j].preference + }) + var ret []string + for _, t := range types { + ret = append(ret, t.name) + } + return ret, nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/handler.go b/vendor/github.com/caicloud/nirvana/service/handler.go new file mode 100644 index 0000000..59f17d9 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/handler.go @@ -0,0 +1,238 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "context" + "net/http" + "reflect" + "strings" + + "github.com/caicloud/nirvana/definition" +) + +// Error is a common interface for error. +// If an error implements the interface, type handlers can +// use Code() to get a specified HTTP status code. +type Error interface { + // Code is a HTTP status code. + Code() int + // Message is an object which contains information of the error. + Message() interface{} +} + +const ( + // HighPriority for error type. + // If an error occurs, ignore meta and data. + HighPriority int = 100 + // MediumPriority for meta type. + MediumPriority int = 200 + // LowPriority for data type. + LowPriority int = 300 +) + +// DestinationHandler is used to handle the results from API handlers. +type DestinationHandler interface { + // Destination returns definition.Destination which the destination handler can handle. + Destination() definition.Destination + // Priority returns priority of the type handler. Type handler with higher priority will prior execute. + Priority() int + // Validate validates whether the type handler can handle the target type. + Validate(target reflect.Type) error + // Handle handles a value. If the handler has something wrong, it should return an error. + // The handler descides how to deal with value by producers and status code. + // The status code is a success status code. If everything is ok, the handler should use the status code. + // + // There are three cases for return values (goon means go on or continue): + // 1. go on is true, err is nil. + // It means that current type handler did nothing (or looks like did nothing) and next type handler + // should take the context. + // 2. go on is false, err is nil. + // It means that current type handler has finished the context and next type handler should not run. + // 3. err is not nil + // It means that current type handler handled the context but something wrong. All subsequent type + // handlers should not run. + Handle(ctx context.Context, producers []Producer, code int, value interface{}) (goon bool, err error) +} + +var handlers = map[definition.Destination]DestinationHandler{ + definition.Meta: &MetaDestinationHandler{}, + definition.Data: &DataDestinationHandler{}, + definition.Error: &ErrorDestinationHandler{}, +} + +// DestinationHandlerFor gets a type handler for specified type. +func DestinationHandlerFor(typ definition.Destination) DestinationHandler { + return handlers[typ] +} + +// RegisterDestinationHandler registers a type handler. +func RegisterDestinationHandler(handler DestinationHandler) error { + handlers[handler.Destination()] = handler + return nil +} + +// MetaDestinationHandler writes metadata to http.ResponseWriter.Header and value type should be map[string]string. +// If value type is not map, the handler will stop the handlers chain and return an error. +// If there is no error, it always expect that the next handler goes on. +type MetaDestinationHandler struct{} + +// Destination returns definition.Destination which the destination handler can handle. +func (h *MetaDestinationHandler) Destination() definition.Destination { return definition.Meta } + +// Priority returns priority of the type handler. +func (h *MetaDestinationHandler) Priority() int { return MediumPriority } + +// Validate validates whether the type handler can handle the target type. +func (h *MetaDestinationHandler) Validate(target reflect.Type) error { return nil } + +// Handle handles a value. If the handler has something wrong, it should return an error. +func (h *MetaDestinationHandler) Handle(ctx context.Context, producers []Producer, code int, value interface{}) (goon bool, err error) { + if value == nil { + return true, nil + } + if values, ok := value.(map[string]string); ok { + headers := HTTPContextFrom(ctx).ResponseWriter().Header() + for key, value := range values { + headers.Set(key, value) + } + return true, nil + } + return false, invalidMetaType.Error(reflect.TypeOf(value)) +} + +// DataDestinationHandler writes value to http.ResponseWriter. The type handler handle object value. +// If value is nil, the handler does nothing. +type DataDestinationHandler struct{} + +// Destination returns definition.Destination which the destination handler can handle. +func (h *DataDestinationHandler) Destination() definition.Destination { return definition.Data } + +// Priority returns priority of the type handler. +func (h *DataDestinationHandler) Priority() int { return LowPriority } + +// Validate validates whether the type handler can handle the target type. +func (h *DataDestinationHandler) Validate(target reflect.Type) error { return nil } + +// Handle handles a value. If the handler has something wrong, it should return an error. +func (h *DataDestinationHandler) Handle(ctx context.Context, producers []Producer, code int, value interface{}) (goon bool, err error) { + if value == nil { + return true, nil + } + err = WriteData(ctx, producers, code, value) + return err == nil, err +} + +// ErrorDestinationHandler writes error to http.ResponseWriter. +// If there is no error, the handler does nothing. +type ErrorDestinationHandler struct{} + +// Destination returns definition.Destination which the destination handler can handle. +func (h *ErrorDestinationHandler) Destination() definition.Destination { return definition.Error } + +// Priority returns priority of the type handler. +func (h *ErrorDestinationHandler) Priority() int { return HighPriority } + +// Validate validates whether the type handler can handle the target type. +func (h *ErrorDestinationHandler) Validate(target reflect.Type) error { return nil } + +// Handle handles a value. If the handler has something wrong, it should return an error. +func (h *ErrorDestinationHandler) Handle(ctx context.Context, producers []Producer, code int, value interface{}) (goon bool, err error) { + if value == nil { + return true, nil + } + return false, writeError(ctx, producers, value) +} + +func writeError(ctx context.Context, producers []Producer, err interface{}) error { + httpCtx := HTTPContextFrom(ctx) + ats, e := AcceptTypes(httpCtx.Request()) + if e != nil { + return e + } + if len(producers) <= 0 { + return noProducerToWrite.Error(ats) + } + code := http.StatusInternalServerError + var msg interface{} + switch e := err.(type) { + case Error: + code = e.Code() + msg = e.Message() + case error: + msg = e.Error() + default: + msg = err + } + + producer := chooseProducer(ats, producers) + if producer == nil { + // Choose the first producer + producer = producers[0] + } + resp := httpCtx.ResponseWriter() + if resp.HeaderWritable() { + // Error always has highest priority. So it can override "Content-Type". + resp.Header().Set("Content-Type", producer.ContentType()) + resp.WriteHeader(code) + } + return producer.Produce(resp, msg) +} + +// WriteData chooses right producer by "Accrpt" header and writes data to context. +// You should never call the function except you are writing a type handler. +func WriteData(ctx context.Context, producers []Producer, code int, data interface{}) error { + httpCtx := HTTPContextFrom(ctx) + ats, err := AcceptTypes(httpCtx.Request()) + if err != nil { + return err + } + if len(producers) <= 0 { + return noProducerToWrite.Error(ats) + } + producer := chooseProducer(ats, producers) + if producer == nil { + return noProducerToWrite.Error(ats) + } + resp := httpCtx.ResponseWriter() + if resp.HeaderWritable() { + // If "Content-Type" has been set, ignore producer's. + ctype := resp.Header().Get("Content-Type") + if strings.TrimSpace(ctype) == "" { + resp.Header().Set("Content-Type", producer.ContentType()) + } + resp.WriteHeader(code) + } + return producer.Produce(resp, data) +} + +func chooseProducer(acceptTypes []string, producers []Producer) Producer { + if len(acceptTypes) <= 0 || len(producers) <= 0 { + return nil + } + for _, v := range acceptTypes { + if v == definition.MIMEAll { + return producers[0] + } + for _, p := range producers { + if p.ContentType() == v { + return p + } + } + } + return nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/method.go b/vendor/github.com/caicloud/nirvana/service/method.go new file mode 100644 index 0000000..71c8e2c --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/method.go @@ -0,0 +1,78 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "net/http" + + "github.com/caicloud/nirvana/definition" +) + +// statusMapping contains a binding of HTTP method and success status code. +type statusMapping struct { + // HTTPMethod is HTTP method. + HTTPMethod string + // Code means a success status code. + Code int +} + +var mappings = map[definition.Method]statusMapping{ + definition.List: {http.MethodGet, http.StatusOK}, + definition.Get: {http.MethodGet, http.StatusOK}, + definition.Create: {http.MethodPost, http.StatusCreated}, + definition.Update: {http.MethodPut, http.StatusOK}, + definition.Patch: {http.MethodPatch, http.StatusOK}, + definition.Delete: {http.MethodDelete, http.StatusNoContent}, + definition.AsyncCreate: {http.MethodPost, http.StatusAccepted}, + definition.AsyncUpdate: {http.MethodPut, http.StatusAccepted}, + definition.AsyncPatch: {http.MethodPatch, http.StatusAccepted}, + definition.AsyncDelete: {http.MethodDelete, http.StatusAccepted}, +} + +// HTTPMethodFor gets a HTTP method for specified definition method. +func HTTPMethodFor(m definition.Method) string { + return mappings[m].HTTPMethod +} + +// HTTPCodeFor gets a success status code for specified definition method. +func HTTPCodeFor(m definition.Method) int { + return mappings[m].Code +} + +// RegisterMethod registers a HTTP method and a success status code for a definition method. +func RegisterMethod(method definition.Method, httpMethod string, code int) error { + validHTTPMethod := []string{http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, + http.MethodPatch, http.MethodDelete, http.MethodConnect, http.MethodOptions, http.MethodTrace} + found := false + for _, m := range validHTTPMethod { + if m == httpMethod { + found = true + break + } + } + if !found { + return invalidMethod.Error(httpMethod) + } + if code < 100 || code >= 600 { + return invalidStatusCode.Error() + } + mappings[method] = statusMapping{ + Code: code, + HTTPMethod: httpMethod, + } + return nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/modifier.go b/vendor/github.com/caicloud/nirvana/service/modifier.go new file mode 100644 index 0000000..6a0a643 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/modifier.go @@ -0,0 +1,157 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "net/http" + + "github.com/caicloud/nirvana/definition" +) + +// DefinitionModifier is used in Server. It's used to modify definition. +// If you want to add some common data into all definitions, you can write +// a customized modifier for it. +type DefinitionModifier func(d *definition.Definition) + +// DefinitionModifiers is a convenient type for []DefinitionModifier +type DefinitionModifiers []DefinitionModifier + +// Combine combines a list of modifiers to one. +func (m DefinitionModifiers) Combine() DefinitionModifier { + return func(d *definition.Definition) { + for _, f := range m { + f(d) + } + } +} + +// FirstContextParameter adds a context prefab parameter into all definitions. +// Then you don't need to manually write the parameter to every definitions. +func FirstContextParameter() DefinitionModifier { + return func(d *definition.Definition) { + if len(d.Parameters) > 0 { + p := d.Parameters[0] + if p.Source == definition.Prefab && p.Name == "context" { + return + } + } + ps := make([]definition.Parameter, len(d.Parameters)+1) + ps[0] = definition.Parameter{ + Name: "context", + Source: definition.Prefab, + } + copy(ps[1:], d.Parameters) + d.Parameters = ps + } +} + +// ConsumeAllIfConsumesIsEmpty adds definition.MIMEAll to consumes if consumes +// is empty. +func ConsumeAllIfConsumesIsEmpty() DefinitionModifier { + return func(d *definition.Definition) { + if len(d.Consumes) <= 0 { + d.Consumes = []string{definition.MIMEAll} + } + } +} + +// ProduceAllIfProducesIsEmpty adds definition.MIMEAll to consumes if consumes +// is empty. +func ProduceAllIfProducesIsEmpty() DefinitionModifier { + return func(d *definition.Definition) { + if len(d.Produces) <= 0 { + d.Produces = []string{definition.MIMEAll} + } + } +} + +// ConsumeNoneForHTTPGet adds definition.MIMENone to consumes for get definitions. +// Then you don't need to manually write the consume to every get definitions. +// The get is http get rather than definition.Get. +func ConsumeNoneForHTTPGet() DefinitionModifier { + return func(d *definition.Definition) { + if HTTPMethodFor(d.Method) == http.MethodGet { + found := false + for _, v := range d.Consumes { + if v == definition.MIMENone { + found = true + break + } + } + if !found { + d.Consumes = append(d.Consumes, definition.MIMENone) + } + } + } +} + +// ConsumeNoneForHTTPDelete adds definition.MIMENone to consumes for delete definitions. +// Then you don't need to manually write the consume to every delete definitions. +// The delete is http delete rather than definition.Delete. +func ConsumeNoneForHTTPDelete() DefinitionModifier { + return func(d *definition.Definition) { + if HTTPMethodFor(d.Method) == http.MethodDelete { + found := false + for _, v := range d.Consumes { + if v == definition.MIMENone { + found = true + break + } + } + if !found { + d.Consumes = append(d.Consumes, definition.MIMENone) + } + } + } +} + +// ProduceNoneForHTTPDelete adds definition.MIMENone to produces for delete definitions. +// Then you don't need to manually write the produce to every delete definitions. +// The delete is http delete rather than definition.Delete. +func ProduceNoneForHTTPDelete() DefinitionModifier { + return func(d *definition.Definition) { + if HTTPMethodFor(d.Method) == http.MethodDelete { + found := false + for _, v := range d.Produces { + if v == definition.MIMENone { + found = true + break + } + } + if !found { + d.Produces = append(d.Produces, definition.MIMENone) + } + } + } +} + +// LastErrorResult adds a error result into all definitions. +// Then you don't need to manually write the result to every definitions. +func LastErrorResult() DefinitionModifier { + return func(d *definition.Definition) { + length := len(d.Results) + if length > 0 { + r := d.Results[length-1] + if r.Destination == definition.Error { + return + } + } + d.Results = append(d.Results, definition.Result{ + Destination: definition.Error, + }) + } +} diff --git a/vendor/github.com/caicloud/nirvana/service/router/basic.go b/vendor/github.com/caicloud/nirvana/service/router/basic.go new file mode 100644 index 0000000..2341191 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/router/basic.go @@ -0,0 +1,269 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package router + +import ( + "context" + "reflect" + "sort" +) + +// handler contains middlewares and executor. +type handler struct { + middlewares []Middleware + inspector Inspector +} + +// AddMiddleware adds middleware to the router node. +// If the router matches a path, all middlewares in the router +// will be executed by the returned executor. +func (h *handler) AddMiddleware(ms ...Middleware) { + h.middlewares = append(h.middlewares, ms...) +} + +// Middlewares returns all middlewares of the router. +// Don't modify the returned values. +func (h *handler) Middlewares() []Middleware { + return h.middlewares +} + +// SetInspector sets inspector to the router node. +func (h *handler) SetInspector(inspector Inspector) { + h.inspector = inspector +} + +// Inspector gets inspector from the router node. +// Don't modify the returned values. +func (h *handler) Inspector() Inspector { + return h.inspector +} + +// Merge merges middlewares and executors. +func (h *handler) Merge(o *handler) error { + h.AddMiddleware(o.middlewares...) + if h.inspector != nil { + if o.inspector != nil { + return ConflictInspectors.Error() + } + } else { + h.inspector = o.inspector + } + return nil +} + +// pack packs middlewares with the executor. +func (h *handler) pack(e Executor) (Executor, error) { + if e == nil { + return nil, NoExecutor.Error() + } + if len(h.middlewares) <= 0 { + return e, nil + } + return newMiddlewareExecutor(h.middlewares, e), nil +} + +// unionExecutor packs middlewares and own executor. +func (h *handler) unionExecutor(ctx context.Context) (Executor, error) { + if h.inspector == nil { + return nil, NoInspector.Error() + } + e, err := h.inspector.Inspect(ctx) + if err != nil { + return nil, err + } + return h.pack(e) +} + +// charRouter is a router for characters +type charRouter struct { + char byte + router *stringNode +} + +// children contains all children routers. +type children struct { + stringRouters []charRouter + regexpRouters []Router + pathRouter Router +} + +// findStringRouter find a router by first char. +func (p *children) findStringRouter(char byte) Router { + length := len(p.stringRouters) + if length <= 3 { + // If the length is less than 3, use linear search. + for _, cr := range p.stringRouters { + if cr.char == char { + return cr.router + } + } + return nil + } + // Binary search. + index := sort.Search(len(p.stringRouters), func(i int) bool { + return char <= p.stringRouters[i].char + }) + if index >= length { + return nil + } + target := p.stringRouters[index] + if char != target.char { + return nil + } + return target.router +} + +// Match find an executor matched by path. +// The context contains information to inspect executor. +// The container can save key-value pair from the path. +// If the router is the leaf node to match the path, it will return +// the first executor which Inspect() returns true. +func (p *children) Match(ctx context.Context, c Container, path string) (Executor, error) { + if len(path) <= 0 { + return nil, RouterNotFound.Error() + } + + // Two routers may match same path: + // /path/{id} without inspector + // /path/{name} with inspector + // When match `/path/some`, the first router won't match it and + // returns NoInspector. The the second router can match the path. + // If the second router can't generate an executor, an error is + // returned by inspector. In this case, resultError should be the + // assigned with the error from second router. + // If there are multiple routers match a path, the error is from + // the last matched router. + resultError := RouterNotFound.Error() + + // Match string routers + if len(p.stringRouters) > 0 { + if router := p.findStringRouter(path[0]); router != nil { + if executor, err := router.Match(ctx, c, path); err == nil { + return executor, nil + } else if !RouterNotFound.Derived(err) && + !NoInspector.Derived(err) && + !NoExecutor.Derived(err) { + resultError = err + } + } + } + + // Match regexp routers + for _, regexp := range p.regexpRouters { + if executor, err := regexp.Match(ctx, c, path); err == nil { + return executor, nil + } else if !RouterNotFound.Derived(err) && + !NoInspector.Derived(err) && + !NoExecutor.Derived(err) { + resultError = err + } + } + + // Match path router + if p.pathRouter != nil { + if executor, err := p.pathRouter.Match(ctx, c, path); err == nil { + return executor, nil + } else if !RouterNotFound.Derived(err) && + !NoInspector.Derived(err) && + !NoExecutor.Derived(err) { + resultError = err + } + } + return nil, resultError +} + +// addRouter adds a router to current progeny. +func (p *children) addRouter(router Router) error { + switch router.Kind() { + case String: + target := router.Target() + if len(target) <= 0 { + return EmptyRouterTarget.Error(router.Kind()) + } + r, ok := router.(*stringNode) + if !ok { + return UnknownRouterType.Error(router.Kind(), reflect.TypeOf(router).String()) + } + c := target[0] + sr := p.findStringRouter(c) + if sr != nil { + _, err := sr.Merge(router) + return err + } + length := len(p.stringRouters) + index := 0 + if length > 0 { + index = sort.Search(length, func(i int) bool { + return c < p.stringRouters[i].char + }) + } + cr := charRouter{c, r} + if index >= length { + p.stringRouters = append(p.stringRouters, cr) + } else { + p.stringRouters = append(p.stringRouters[:index+1], p.stringRouters[index:]...) + p.stringRouters[index] = cr + } + case Regexp: + found := false + for _, r := range p.regexpRouters { + if r.Target() == router.Target() { + if _, err := r.Merge(router); err != nil { + return err + } + found = true + break + } + } + if !found { + p.regexpRouters = append(p.regexpRouters, router) + } + case Path: + if p.pathRouter != nil { + r, err := p.pathRouter.Merge(router) + if err != nil { + return err + } + p.pathRouter = r + } else { + p.pathRouter = router + } + default: + return UnknownRouterType.Error(router.Kind(), reflect.TypeOf(router).String()) + } + return nil +} + +// merge merges children routers. +func (p *children) merge(o *children) error { + for _, r := range o.stringRouters { + if err := p.addRouter(r.router); err != nil { + return err + } + } + for _, r := range o.regexpRouters { + if err := p.addRouter(r); err != nil { + return err + } + } + if o.pathRouter != nil { + if err := p.addRouter(o.pathRouter); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/router/executor.go b/vendor/github.com/caicloud/nirvana/service/router/executor.go new file mode 100644 index 0000000..10dc140 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/router/executor.go @@ -0,0 +1,56 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package router + +import "context" + +// middlewareExecutor is a combination of middlewares and executor. +type middlewareExecutor struct { + // Middlewares contains all middlewares for the executor. + middlewares []Middleware + // Index is used to record the count of executed middleware. + index int + // Executor executes after middlewares. + executor Executor +} + +// newMiddlewareExecutor creates a new executor with middlewares. +func newMiddlewareExecutor(ms []Middleware, e Executor) Executor { + return &middlewareExecutor{ms, 0, e} +} + +// Execute executes middlewares and executor. +func (me *middlewareExecutor) Execute(c context.Context) error { + me.index = 0 + defer func() { + me.index = 0 + }() + return me.Continue(c) +} + +// Continue continues to execute the next middleware or executor. +func (me *middlewareExecutor) Continue(c context.Context) error { + if me.index >= len(me.middlewares) { + if me.executor != nil { + return me.executor.Execute(c) + } + return nil + } + m := me.middlewares[me.index] + me.index++ + return m(c, me) +} diff --git a/vendor/github.com/caicloud/nirvana/service/router/path.go b/vendor/github.com/caicloud/nirvana/service/router/path.go new file mode 100644 index 0000000..d8911d4 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/router/path.go @@ -0,0 +1,65 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package router + +import ( + "context" + "reflect" +) + +// pathNode matches all rest path. +type pathNode struct { + handler + // key is the key for the rest path. + key string +} + +// Target returns the matching target of the node. +func (n *pathNode) Target() string { + return "" +} + +// Kind returns the kind of the router node. +func (n *pathNode) Kind() RouteKind { + return Path +} + +// Match find an executor matched by path. +// The context contains information to inspect executor. +// The container can save key-value pair from the path. +// If the router is the leaf node to match the path, it will return +// the first executor which Inspect() returns true. +func (n *pathNode) Match(ctx context.Context, c Container, path string) (Executor, error) { + c.Set(n.key, path) + return n.handler.unionExecutor(ctx) +} + +// Merge merges r to the current router. The type of r should be same +// as the current one. +func (n *pathNode) Merge(r Router) (Router, error) { + node, ok := r.(*pathNode) + if !ok { + return nil, UnknownRouterType.Error(r.Kind(), reflect.TypeOf(r).String()) + } + if n.key != node.key { + return nil, UnmatchedRouterKey.Error(n.key, node.key) + } + if err := n.handler.Merge(&node.handler); err != nil { + return nil, err + } + return n, nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/router/regexp.go b/vendor/github.com/caicloud/nirvana/service/router/regexp.go new file mode 100644 index 0000000..ef0eab2 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/router/regexp.go @@ -0,0 +1,176 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package router + +import ( + "context" + "reflect" + "regexp" + "strings" +) + +// index contains the key and it's index of the submatches. +type index struct { + // Key is the name for the value. + Key string + // Pos is the index of value in submatches. + Pos int +} + +// regexpNode contains information for matching a regexp segment. +type regexpNode struct { + handler + children + // indices contains all positions to get values from submatches. + indices []index + // exp is the regular expression. + exp string + // regexp is a regexp instance to match. + regexp *regexp.Regexp +} + +// Target returns the matching target of the node. +func (n *regexpNode) Target() string { + return n.exp +} + +// Kind returns the kind of the router node. +func (n *regexpNode) Kind() RouteKind { + return Regexp +} + +// Match find an executor matched by path. +// The context contains information to inspect executor. +// The container can save key-value pair from the path. +// If the router is the leaf node to match the path, it will return +// the first executor which Inspect() returns true. +func (n *regexpNode) Match(ctx context.Context, c Container, path string) (Executor, error) { + // Match self + index := strings.IndexByte(path, '/') + if index < 0 { + index = len(path) + } + segment := path[:index] + result := n.regexp.FindStringSubmatch(segment) + if result == nil { + return nil, RouterNotFound.Error() + } + // Match progeny + var executor Executor + var err error + if index < len(path) { + executor, err = n.children.Match(ctx, c, path[index:]) + if err == nil { + executor, err = n.handler.pack(executor) + } + } else { + executor, err = n.unionExecutor(ctx) + } + + if err != nil { + // Unmatched + return nil, err + } + + // Set values + for _, i := range n.indices { + c.Set(i.Key, result[i.Pos]) + } + return executor, nil +} + +// Merge merges r to the current router. The type of r should be same +// as the current one or it panics. +func (n *regexpNode) Merge(r Router) (Router, error) { + node, ok := r.(*regexpNode) + if !ok { + return nil, UnknownRouterType.Error(r.Kind(), reflect.TypeOf(r).String()) + } + if n.exp != node.exp { + return nil, UnmatchedRouterRegexp.Error(n.exp, node.exp) + } + if err := n.handler.Merge(&node.handler); err != nil { + return nil, err + } + if err := n.children.merge(&node.children); err != nil { + return nil, err + } + return n, nil +} + +// fullMatchRegexpNode is an optimizing of RegexpNode. +type fullMatchRegexpNode struct { + handler + children + // key is the name for the only value. + key string +} + +// Target returns the matching target of the node. +func (n *fullMatchRegexpNode) Target() string { + return (&expSegment{FullMatchTarget, n.key}).Target() +} + +// Kind returns the kind of the router node. +func (n *fullMatchRegexpNode) Kind() RouteKind { + return Regexp +} + +// Match find an executor matched by path. +// The context contains information to inspect executor. +// The container can save key-value pair from the path. +// If the router is the leaf node to match the path, it will return +// the first executor which Inspect() returns true. +func (n *fullMatchRegexpNode) Match(ctx context.Context, c Container, path string) (Executor, error) { + index := strings.IndexByte(path, '/') + var executor Executor + var err error + if index > 0 { + executor, err = n.children.Match(ctx, c, path[index:]) + if err == nil { + executor, err = n.handler.pack(executor) + } + } else { + index = len(path) + executor, err = n.unionExecutor(ctx) + } + if err != nil { + // Unmatched + return nil, err + } + c.Set(n.key, path[:index]) + return executor, nil +} + +// Merge merges r to the current router. The type of r should be same +// as the current one or it panics. +func (n *fullMatchRegexpNode) Merge(r Router) (Router, error) { + node, ok := r.(*fullMatchRegexpNode) + if !ok { + return nil, UnknownRouterType.Error(r.Kind(), reflect.TypeOf(r).String()) + } + if n.key != node.key { + return nil, UnmatchedRouterKey.Error(n.key, node.key) + } + if err := n.handler.Merge(&node.handler); err != nil { + return nil, err + } + if err := n.children.merge(&node.children); err != nil { + return nil, err + } + return n, nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/router/router.go b/vendor/github.com/caicloud/nirvana/service/router/router.go new file mode 100644 index 0000000..9bbec73 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/router/router.go @@ -0,0 +1,344 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package router + +import ( + "context" + "fmt" + "reflect" + "regexp" + "strings" +) + +// RoutingChain contains the call chain of middlewares and executor. +type RoutingChain interface { + // Continue continues to execute the next middleware or executor. + Continue(context.Context) error +} + +// Middleware describes the form of middlewares. If you want to +// carry on, call RoutingChain.Continue() and pass the context. +type Middleware func(context.Context, RoutingChain) error + +// Inspector can select an executor to execute. +type Inspector interface { + // Inspect finds a valid executor to execute target context. + // It returns an error if it can't find a valid executor. + Inspect(context.Context) (Executor, error) +} + +// Executor executs with a context. +type Executor interface { + // Execute executes with context. + Execute(context.Context) error +} + +// RouteKind is kind of routers. +type RouteKind string + +const ( + // String means the router has a fixed string. + String RouteKind = "String" + // Regexp means the router has a regular expression. + Regexp RouteKind = "Regexp" + // Path means the router matches the rest. Path router only can + // be placed at the leaf node. + Path RouteKind = "Path" +) + +// Container is a key-value container. It saves key-values from path. +type Container interface { + // Set sets key-value into the container. + Set(key, value string) + // Get gets a value by key from the container. + Get(key string) (string, bool) +} + +// Router describes the interface of a router node. +type Router interface { + // Target returns the matching target of the node. + // It can be a fixed string or a regular expression. + Target() string + // Kind returns the kind of the router node. + Kind() RouteKind + // Match find an executor matched by path. + // The context contains information to inspect executor. + // The container can save key-value pair from the path. + // If the router is the leaf node to match the path, it will return + // the first executor which Inspect() returns true. + Match(ctx context.Context, c Container, path string) (Executor, error) + // AddMiddleware adds middleware to the router node. + // If the router matches a path, all middlewares in the router + // will be executed by the returned executor. + AddMiddleware(ms ...Middleware) + // Middlewares returns all middlewares of the router. + // Don't modify the returned values. + Middlewares() []Middleware + // SetInspector sets inspector to the router node. + SetInspector(inspector Inspector) + // Inspector gets inspector from the router node. + // Don't modify the returned values. + Inspector() Inspector + // Merge merges r to the current router. The type of r should be same + // as the current one or it panics. + // + // For instance: + // Router A: /namespaces/ -> {namespace} + // Router B: /nameless/ -> {other} + // Result: + // /name -> spaces/ -> {namespace} + // |-> less/ -> {other} + Merge(r Router) (Router, error) +} + +const ( + // FullMatchTarget is a match for full regular expression. All regexp router without expression + // will use the expression. + FullMatchTarget = ".*" + // TailMatchTarget is a match expression for tail only. + TailMatchTarget = "*" +) + +// Parse parses a path to a router tree. It returns the root router and +// the leaf router. you can add middlewares and executor to the routers. +// A valid path should like: +// /segments/{segment}/resources/{resource} +// /segments/{segment:[a-z]{1,2}}.log/paths/{path:*} +func Parse(path string) (Router, Router, error) { + paths, err := Split(path) + if err != nil { + return nil, nil, err + } + if len(paths) <= 0 { + return nil, nil, InvalidPath.Error() + } + segments, err := reorganize(paths) + if err != nil { + return nil, nil, err + } + var root Router + var leaf Router + var parent Router + for i, seg := range segments { + router, err := segmentToRouter(seg) + if err != nil { + return nil, nil, err + } + if i == 0 { + root = router + } + if i == len(segments)-1 { + leaf = router + } + if parent != nil { + if c, ok := parent.(interface { + addRouter(router Router) error + }); ok { + if err := c.addRouter(router); err != nil { + return nil, nil, err + } + } else { + return nil, nil, InvalidParentRouter.Error(reflect.TypeOf(parent).String()) + } + } + parent = router + } + return root, leaf, nil +} + +// segmentToRouter converts segment to a router. +func segmentToRouter(seg *segment) (Router, error) { + switch seg.kind { + case String: + return &stringNode{ + prefix: seg.match, + }, nil + case Regexp: + if len(seg.keys) == 1 && seg.match == (&expSegment{FullMatchTarget, seg.keys[0]}).Target() { + return &fullMatchRegexpNode{ + key: seg.keys[0], + }, nil + } + + node := ®expNode{ + exp: seg.match, + } + r, err := regexp.Compile("^" + seg.match + "$") + if err != nil { + return nil, InvalidRegexp.Error(seg.match) + } + node.regexp = r + names := r.SubexpNames() + j := 0 + for i := 0; i < len(names) && j < len(seg.keys); i++ { + if names[i] == seg.keys[j] { + node.indices = append(node.indices, index{names[i], i}) + j++ + } + } + if j != len(seg.keys) { + return nil, UnmatchedSegmentKeys.Error(seg) + } + return node, nil + + case Path: + return &pathNode{ + key: seg.keys[0], + }, nil + } + return nil, UnknownSegment.Error(seg) +} + +// Split splits string segments and regexp segments. +// +// For instance: +// /segments/{segment:[a-z]{1,2}}.log/paths/{path:*} +// TO: +// /segments/ {segment:[a-z]{1,2}} .log/paths/ {path:*} +func Split(path string) ([]string, error) { + result := make([]string, 0, 5) + lastElementPos := 0 + braceCounter := 0 + for i, c := range path { + switch c { + case '{': + braceCounter++ + if braceCounter == 1 { + if i > lastElementPos { + result = append(result, path[lastElementPos:i]) + } + lastElementPos = i + } + case '}': + braceCounter-- + if braceCounter == 0 { + result = append(result, path[lastElementPos:i+1]) + lastElementPos = i + 1 + } + } + } + if braceCounter > 0 { + return nil, UnmatchedPathBrace.Error() + } + if lastElementPos < len(path) { + result = append(result, path[lastElementPos:]) + } + return result, nil +} + +// segment contains information to construct a router. +type segment struct { + // match is the target string. + match string + // keys contains keys from segments. + keys []string + // kind is the router kind which the segment can be converted to. + kind RouteKind +} + +// reorganize reorganizes the form of paths. +// +// For instance: +// /segments/ {segment:[a-z]{1,2}} .log/paths/ {path:*} +// To: +// {/segments/ {} String} {(?P[a-z]{1,2})\.log {segment} Regexp} {/paths/ {} String} { {path} Path} +func reorganize(paths []string) ([]*segment, error) { + segments := make([]*segment, 0, len(paths)) + var target *segment + for i := 0; i < len(paths); i++ { + p := paths[i] + if !strings.HasPrefix(p, "{") { + if target == nil { + // String segment + segments = append(segments, &segment{p, nil, String}) + } else { + // Regexp segment + slashPos := strings.Index(p, "/") + if slashPos < 0 { + // No slash + target.match += regexp.QuoteMeta(p) + } else { + target.match += regexp.QuoteMeta(p[:slashPos]) + segments = append(segments, target, &segment{p[slashPos:], nil, String}) + target = nil + } + } + } else { + // Regexp segment + seg, err := parseExpSegment(p) + if err != nil { + return nil, err + } + if seg.Tail() { + if i != len(paths)-1 { + return nil, InvalidPathKey.Error(seg.key) + } + if target != nil { + segments = append(segments, target) + target = nil + } + segments = append(segments, &segment{"", []string{seg.key}, Path}) + break + } + if target == nil { + target = &segment{"", []string{}, Regexp} + } + target.match += seg.Target() + target.keys = append(target.keys, seg.key) + } + } + if target != nil { + segments = append(segments, target) + } + return segments, nil +} + +// expSegment describes a regexp segment. +type expSegment struct { + // exp is the regular expression. + exp string + // key is the key for the expression. + key string +} + +// parseExpSegment parses a regexp segment to ExpSegment. +func parseExpSegment(exp string) (*expSegment, error) { + if !strings.HasPrefix(exp, "{") || !strings.HasSuffix(exp, "}") { + return nil, InvalidRegexp.Error(exp) + } + exp = exp[1 : len(exp)-1] + pos := strings.Index(exp, ":") + seg := &expSegment{} + if pos < 0 { + seg.exp = FullMatchTarget + seg.key = exp + } else { + seg.exp = exp[pos+1:] + seg.key = exp[:pos] + } + return seg, nil +} + +// Tail returns whether the segment contains a tail match target. +func (es *expSegment) Tail() bool { + return es.exp == TailMatchTarget +} + +// Target returns the whole regular expression for the segment. +func (es *expSegment) Target() string { + return fmt.Sprintf("(?P<%s>%s)", es.key, es.exp) +} diff --git a/vendor/github.com/caicloud/nirvana/service/router/string.go b/vendor/github.com/caicloud/nirvana/service/router/string.go new file mode 100644 index 0000000..9354d5e --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/router/string.go @@ -0,0 +1,117 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package router + +import ( + "context" + "reflect" + "strings" +) + +// stringNode describes a string router node. +type stringNode struct { + handler + children + // prefix is the fixed string to match path. + prefix string +} + +// Target returns the matching target of the node. +func (n *stringNode) Target() string { + return n.prefix +} + +// Kind returns the kind of the router node. +func (n *stringNode) Kind() RouteKind { + return String +} + +// Match find an executor matched by path. +// The context contains information to inspect executor. +// The container can save key-value pair from the path. +// If the router is the leaf node to match the path, it will return +// the first executor which Inspect() returns true. +func (n *stringNode) Match(ctx context.Context, c Container, path string) (Executor, error) { + if n.prefix != "" && !strings.HasPrefix(path, n.prefix) { + // No match + return nil, RouterNotFound.Error() + } + if len(n.prefix) < len(path) { + // Match prefix + executor, err := n.children.Match(ctx, c, path[len(n.prefix):]) + if err != nil { + return nil, err + } + return n.handler.pack(executor) + } + // Match self + return n.handler.unionExecutor(ctx) +} + +// Merge merges r to the current router. The type of r should be same +// as the current one or it panics. +func (n *stringNode) Merge(r Router) (Router, error) { + node, ok := r.(*stringNode) + if !ok { + return nil, UnknownRouterType.Error(r.Kind(), reflect.TypeOf(r).String()) + } + commonPrefix := 0 + for commonPrefix < len(n.prefix) && commonPrefix < len(node.prefix) { + if n.prefix[commonPrefix] != node.prefix[commonPrefix] { + break + } + commonPrefix++ + } + if commonPrefix <= 0 { + return nil, NoCommonPrefix.Error() + } + switch { + case commonPrefix == len(n.prefix) && commonPrefix == len(node.prefix): + if err := n.handler.Merge(&node.handler); err != nil { + return nil, err + } + if err := n.children.merge(&node.children); err != nil { + return nil, err + } + case commonPrefix == len(n.prefix): + node.prefix = node.prefix[commonPrefix:] + if err := n.addRouter(node); err != nil { + return nil, err + } + case commonPrefix == len(node.prefix): + copy := *n + copy.prefix = copy.prefix[commonPrefix:] + *n = *node + if err := n.addRouter(©); err != nil { + return nil, err + } + default: + copy := *n + copy.prefix = copy.prefix[commonPrefix:] + node.prefix = node.prefix[commonPrefix:] + n.handler = handler{} + n.children = children{} + n.prefix = n.prefix[:commonPrefix] + if err := n.addRouter(©); err != nil { + return nil, err + } + if err := n.addRouter(node); err != nil { + return nil, err + } + } + return n, nil +} diff --git a/vendor/github.com/caicloud/nirvana/service/router/utils.go b/vendor/github.com/caicloud/nirvana/service/router/utils.go new file mode 100644 index 0000000..41f9596 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/router/utils.go @@ -0,0 +1,88 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package router + +import "github.com/caicloud/nirvana/errors" + +// Router `Match` method only can return errors from these error factory: +// RouterNotFound, NoInspector, NoExecutor. + +// RouterNotFound means there no router matches path. +var RouterNotFound = errors.NotFound.Build("Nirvana:Router:RouterNotFound", + "can't find router") + +// NoInspector means there is no inspector in router. +var NoInspector = errors.NotFound.Build("Nirvana:Router:NoInspector", + "no inspector to generate executor") + +// NoExecutor means can't pack middlewares for nil executor. +var NoExecutor = errors.NotFound.Build("Nirvana:Router:NoExecutor", + "no executor to pack middlewares") + +// ConflictInspectors can build errors for failure of merging routers. +// If attempts to merge two router and they all have inspector, an error +// should be returned. A merged router can't have two inspectors. +var ConflictInspectors = errors.Conflict.Build("Nirvana:Router:ConflictInspectors", + "can't merge two routers that all have inspector") + +// EmptyRouterTarget means a router node has an invalid empty target. +var EmptyRouterTarget = errors.UnprocessableEntity.Build("Nirvana:Router:EmptyRouterTarget", + "router ${kind} has no target") + +// UnknownRouterType means a node type is unprocessable. +var UnknownRouterType = errors.UnprocessableEntity.Build("Nirvana:Router:UnknownRouterType", + "router ${kind} has unknown type ${type}") + +// UnmatchedRouterKey means a router's key is not matched with another. +var UnmatchedRouterKey = errors.UnprocessableEntity.Build("Nirvana:Router:UnmatchedRouterKey", + "router key ${keyA} is not matched with ${keyB}") + +// UnmatchedRouterRegexp means a router's regexp is not matched with another. +var UnmatchedRouterRegexp = errors.UnprocessableEntity.Build("Nirvana:Router:UnmatchedRouterRegexp", + "router regexp ${regexpA} is not matched with ${regexpA}") + +// NoCommonPrefix means two routers have no common prefix. +var NoCommonPrefix = errors.UnprocessableEntity.Build("Nirvana:Router:NoCommonPrefix", + "there is no common prefix for the two routers") + +// InvalidPath means router path is invalid. +var InvalidPath = errors.UnprocessableEntity.Build("Nirvana:Router:InvalidPath", + "invalid path") + +// InvalidParentRouter means router node has no method to add child routers. +var InvalidParentRouter = errors.UnprocessableEntity.Build("Nirvana:Router:InvalidParentRouter", + "router ${type} has no method to add children") + +// UnmatchedSegmentKeys means segment has unmatched keys. +var UnmatchedSegmentKeys = errors.UnprocessableEntity.Build("Nirvana:Router:UnmatchedSegmentKeys", + "segment ${value} has unmatched keys") + +// UnknownSegment means can't recognize segment. +var UnknownSegment = errors.UnprocessableEntity.Build("Nirvana:Router:UnknownSegment", + "unknown segment ${value}") + +// UnmatchedPathBrace means path has unmatched brace. +var UnmatchedPathBrace = errors.UnprocessableEntity.Build("Nirvana:Router:UnmatchedPathBrace", + "unmatched braces") + +// InvalidPathKey means path key must be the last element. +var InvalidPathKey = errors.UnprocessableEntity.Build("Nirvana:Router:InvalidPathKey", + "key ${key} should be last element in the path") + +// InvalidRegexp means regexp is not notmative. +var InvalidRegexp = errors.UnprocessableEntity.Build("Nirvana:Router:InvalidRegexp", + "regexp ${regexp} does not have normative format") diff --git a/vendor/github.com/caicloud/nirvana/service/source.go b/vendor/github.com/caicloud/nirvana/service/source.go new file mode 100644 index 0000000..d6f2976 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/source.go @@ -0,0 +1,556 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "context" + "fmt" + "io" + "mime/multipart" + "reflect" + "strings" + + "github.com/caicloud/nirvana/definition" +) + +// ParameterGenerator is used to generate object for a parameter. +type ParameterGenerator interface { + // Source returns the source generated by current generator. + Source() definition.Source + // Validate validates whether defaultValue and target type is valid. + Validate(name string, defaultValue interface{}, target reflect.Type) error + // Generate generates an object by data from value container. + Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, name string, target reflect.Type) (interface{}, error) +} + +var generators = map[definition.Source]ParameterGenerator{ + definition.Path: &PathParameterGenerator{}, + definition.Query: &QueryParameterGenerator{}, + definition.Header: &HeaderParameterGenerator{}, + definition.Form: &FormParameterGenerator{}, + definition.File: &FileParameterGenerator{}, + definition.Body: &BodyParameterGenerator{}, + definition.Auto: &AutoParameterGenerator{}, + definition.Prefab: &PrefabParameterGenerator{}, +} + +// ParameterGeneratorFor gets a parameter generator for specified source. +func ParameterGeneratorFor(source definition.Source) ParameterGenerator { + return generators[source] +} + +// RegisterParameterGenerator register a generator. +func RegisterParameterGenerator(generator ParameterGenerator) error { + generators[generator.Source()] = generator + return nil +} + +func assignable(defaultValue interface{}, target reflect.Type) error { + if defaultValue == nil { + return nil + } + value := reflect.ValueOf(defaultValue) + if !value.Type().AssignableTo(target) { + return unassignableType.Error(value.Type(), target) + } + return nil +} + +func convertible(target reflect.Type) error { + c := ConverterFor(target) + if c == nil { + return noConverter.Error(target) + } + return nil +} + +// PathParameterGenerator is used to generate object by value from path +type PathParameterGenerator struct{} + +// Source returns the source generated by current generator. +func (g *PathParameterGenerator) Source() definition.Source { return definition.Path } + +// Validate validates whether defaultValue and target type is valid. +func (g *PathParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + if name == "" { + return noName.Error(g.Source()) + } + if err := assignable(defaultValue, target); err != nil { + return err + } + if err := convertible(target); err != nil { + return err + } + return nil +} + +// Generate generates an object by data from value container. +func (g *PathParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, + name string, target reflect.Type) (interface{}, error) { + data, ok := vc.Path(name) + if !ok || len(data) <= 0 { + return nil, nil + } + if converter := ConverterFor(target); converter != nil { + return converter(ctx, []string{data}) + } + return nil, nil +} + +// QueryParameterGenerator is used to generate object by value from query string. +type QueryParameterGenerator struct{} + +// Source returns the source generated by current generator. +func (g *QueryParameterGenerator) Source() definition.Source { return definition.Query } + +// Validate validates whether defaultValue and target type is valid. +func (g *QueryParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + if name == "" { + return noName.Error(g.Source()) + } + if err := assignable(defaultValue, target); err != nil { + return err + } + if err := convertible(target); err != nil { + return err + } + return nil +} + +// Generate generates an object by data from value container. +func (g *QueryParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, + name string, target reflect.Type) (interface{}, error) { + data, ok := vc.Query(name) + if !ok || len(data) <= 0 { + return nil, nil + } + if converter := ConverterFor(target); converter != nil { + return converter(ctx, data) + } + return nil, nil +} + +// HeaderParameterGenerator is used to generate object by value from request header. +type HeaderParameterGenerator struct{} + +// Source returns the source generated by current generator. +func (g *HeaderParameterGenerator) Source() definition.Source { return definition.Header } + +// Validate validates whether defaultValue and target type is valid. +func (g *HeaderParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + if name == "" { + return noName.Error(g.Source()) + } + if err := assignable(defaultValue, target); err != nil { + return err + } + if err := convertible(target); err != nil { + return err + } + return nil +} + +// Generate generates an object by data from value container. +func (g *HeaderParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, + name string, target reflect.Type) (interface{}, error) { + data, ok := vc.Header(name) + if !ok || len(data) <= 0 { + return nil, nil + } + if converter := ConverterFor(target); converter != nil { + return converter(ctx, data) + } + return nil, nil +} + +// FormParameterGenerator is used to generate object by value from request form. +type FormParameterGenerator struct{} + +// Source returns the source generated by current generator. +func (g *FormParameterGenerator) Source() definition.Source { return definition.Form } + +// Validate validates whether defaultValue and target type is valid. +func (g *FormParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + if name == "" { + return noName.Error(g.Source()) + } + if err := assignable(defaultValue, target); err != nil { + return err + } + if err := convertible(target); err != nil { + return err + } + return nil +} + +// Generate generates an object by data from value container. +func (g *FormParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, + name string, target reflect.Type) (interface{}, error) { + data, ok := vc.Form(name) + if !ok || len(data) <= 0 { + return nil, nil + } + if converter := ConverterFor(target); converter != nil { + return converter(ctx, data) + } + return nil, nil +} + +type repeatableCloserForFile struct { + multipart.File + closed bool +} + +// Close closes the File. +func (c *repeatableCloserForFile) Close() error { + if c.closed { + return nil + } + err := c.File.Close() + if err != nil { + return err + } + c.closed = true + return nil +} + +// FileParameterGenerator is used to generate file reader by value from request form file. +type FileParameterGenerator struct { +} + +// Source returns the source generated by current generator. +func (g *FileParameterGenerator) Source() definition.Source { return definition.File } + +// Validate validates whether defaultValue and target type is valid. +func (g *FileParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + if name == "" { + return noName.Error(g.Source()) + } + err := assignable(defaultValue, target) + if err != nil { + return err + } + if !reflect.TypeOf((*multipart.File)(nil)).Elem().AssignableTo(target) { + return unassignableType.Error("multipart.File", target) + } + return nil +} + +// Generate generates an object by data from value container. +func (g *FileParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, + name string, target reflect.Type) (interface{}, error) { + file, ok := vc.File(name) + if !ok { + return nil, nil + } + return &repeatableCloserForFile{file, false}, nil +} + +// BodyParameterGenerator is used to generate object or body reader by value from request body. +type BodyParameterGenerator struct{} + +// Source returns the source generated by current generator. +func (g *BodyParameterGenerator) Source() definition.Source { return definition.Body } + +// Validate validates whether defaultValue and target type is valid. +func (g *BodyParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + err := assignable(defaultValue, target) + if err != nil { + return err + } + kind := target.Kind() + switch { + case kind == reflect.String: + case kind == reflect.Slice: + case kind == reflect.Struct: + case kind == reflect.Ptr && target.Elem().Kind() == reflect.Struct: + case kind == reflect.Interface && reflect.TypeOf((*io.ReadCloser)(nil)).Elem().AssignableTo(target): + default: + return invalidBodyType.Error(target) + } + return nil +} + +type repeatableCloserForBody struct { + io.ReadCloser + closed bool +} + +// Close closes the Body. +func (c *repeatableCloserForBody) Close() error { + if c.closed { + return nil + } + err := c.ReadCloser.Close() + if err != nil { + return err + } + c.closed = true + return nil +} + +// Generate generates an object by data from value container. +func (g *BodyParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, + name string, target reflect.Type) (interface{}, error) { + reader, contentType, ok := vc.Body() + if !ok { + return nil, nil + } + kind := target.Kind() + if kind == reflect.Interface && reflect.TypeOf((*io.ReadCloser)(nil)).Elem().AssignableTo(target) { + return &repeatableCloserForBody{reader, false}, nil + } + var consumer Consumer + for _, c := range consumers { + if c.ContentType() == contentType { + consumer = c + break + } + } + if consumer == nil { + return nil, nil + } + var value reflect.Value + // Create a pointer to target. + // Value is a pointer and it points to nil. + // consumer should fill it. + switch { + case kind == reflect.String || kind == reflect.Slice || kind == reflect.Struct: + value = reflect.New(target) + case kind == reflect.Ptr && target.Elem().Kind() == reflect.Struct: + value = reflect.New(target.Elem()) + default: + return nil, nil + } + if err := consumer.Consume(reader, value.Interface()); err != nil { + return nil, err + } + if kind == reflect.String || kind == reflect.Slice || kind == reflect.Struct { + return value.Elem().Interface(), nil + } + return value.Interface(), nil +} + +// PrefabParameterGenerator is used to generate object by prefabs. +type PrefabParameterGenerator struct{} + +// Source returns the source generated by current generator. +func (g *PrefabParameterGenerator) Source() definition.Source { return definition.Prefab } + +// Validate validates whether defaultValue and target type is valid. +func (g *PrefabParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + if name == "" { + return noName.Error(g.Source()) + } + err := assignable(defaultValue, target) + if err != nil { + return err + } + prefab := PrefabFor(name) + if prefab == nil { + return noPrefab.Error(name) + } + if !prefab.Type().AssignableTo(target) { + return unassignableType.Error(prefab.Type(), target) + } + return nil +} + +// Generate generates an object by data from value container. +func (g *PrefabParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, + name string, target reflect.Type) (interface{}, error) { + prefab := PrefabFor(name) + if prefab == nil { + return nil, noPrefab.Error(name) + } + return prefab.Make(ctx) +} + +// AutoParameterGenerator generates an object from a struct type. The fields in a struct can have tag. +// Tag name is "source". Its value format is "Source,Name". +// +// ex. +// type Example struct { +// Start int `source:"Query,start"` +// ContentType string `source:"Header,Content-Type"` +// } +type AutoParameterGenerator struct{} + +// Source returns the source generated by current generator. +func (g *AutoParameterGenerator) Source() definition.Source { return definition.Auto } + +// Validate validates whether defaultValue and target type is valid. +func (g *AutoParameterGenerator) Validate(name string, defaultValue interface{}, target reflect.Type) error { + err := assignable(defaultValue, target) + if err != nil { + return err + } + if target.Kind() != reflect.Struct && !(target.Kind() == reflect.Ptr && target.Elem().Kind() == reflect.Struct) { + return invalidAutoParameter.Error(target) + } + f := func(index []int, field reflect.StructField) error { + source, name, params, err := ParseAutoParameterTag(field.Tag.Get("source")) + if err != nil { + return err + } + generator := ParameterGeneratorFor(source) + if generator == nil { + return noParameterGenerator.Error(source) + } + + var value interface{} + defaultValue := params.Get(AutoParameterConfigKeyDefaultValue) + if defaultValue != "" { + if c := ConverterFor(field.Type); c != nil { + var err error + value, err = c(context.Background(), []string{defaultValue}) + if err != nil { + return err + } + } + } + + return generator.Validate(name, value, field.Type) + } + if target.Kind() == reflect.Struct { + err = g.enum([]int{}, target, f) + } else { + err = g.enum([]int{}, target.Elem(), f) + } + return err +} + +// Generate generates an object by data from value container. +func (g *AutoParameterGenerator) Generate(ctx context.Context, vc ValueContainer, consumers []Consumer, name string, target reflect.Type) (interface{}, error) { + var result reflect.Value + var value reflect.Value + if target.Kind() == reflect.Struct { + result = reflect.New(target).Elem() + value = result + } else { + result = reflect.New(target.Elem()) + value = result.Elem() + } + if err := g.generate(ctx, vc, consumers, value); err != nil { + return nil, err + } + return result.Interface(), nil +} + +func (g *AutoParameterGenerator) generate(ctx context.Context, vc ValueContainer, consumers []Consumer, value reflect.Value) error { + f := func(index []int, field reflect.StructField) error { + source, name, params, err := ParseAutoParameterTag(field.Tag.Get("source")) + if err != nil { + return err + } + generator := ParameterGeneratorFor(source) + if generator == nil { + return noParameterGenerator.Error(source) + } + ins, err := generator.Generate(ctx, vc, consumers, name, field.Type) + if err != nil { + return err + } + + defaultValue := params.Get(AutoParameterConfigKeyDefaultValue) + if ins == nil && defaultValue != "" { + if c := ConverterFor(field.Type); c != nil { + // After passing the validation phase, here will never return an error + ins, _ = c(ctx, []string{defaultValue}) // #nosec + } + } + + if ins != nil { + value.FieldByIndex(index).Set(reflect.ValueOf(ins)) + } + + return nil + } + return g.enum([]int{}, value.Type(), f) +} + +func (g *AutoParameterGenerator) enum(index []int, typ reflect.Type, f func(index []int, field reflect.StructField) error) error { + var err error + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + if field.Tag.Get("source") != "" { + err = f(append(index, i), field) + } else if field.Type.Kind() == reflect.Struct { + err = g.enum(append(index, i), field.Type, f) + } + if err != nil { + return err + } + } + return nil +} + +// AutoParameterConfig contains configs of AutoParameter. +type AutoParameterConfig map[AutoParameterConfigKey]string + +// AutoParameterConfigKey is the key of AutoParameterConfig. +type AutoParameterConfigKey string + +const ( + // AutoParameterConfigKeyDefaultValue is the key of default value. + AutoParameterConfigKeyDefaultValue AutoParameterConfigKey = "default" + // AutoParameterConfigKeyAnonymous is the prefix of anonymous configs. + AutoParameterConfigKeyAnonymous AutoParameterConfigKey = " anonymous" +) + +// Get gets value of a config key. +func (f AutoParameterConfig) Get(key AutoParameterConfigKey) string { + return f[key] +} + +// Set sets value for a config key. +func (f AutoParameterConfig) Set(key AutoParameterConfigKey, value string) { + f[key] = value +} + +// ParseAutoParameterTag parse the tag of AutoParameter. +func ParseAutoParameterTag(tag string) (source definition.Source, name string, apc AutoParameterConfig, err error) { + apc = make(AutoParameterConfig) + // This operation is problematic. + result := strings.Split(tag, ",") + + length := len(result) + if length <= 0 { + return "", "", nil, invalidFieldTag.Error(tag) + } + if length >= 1 { + source = definition.Source(strings.Title(strings.ToLower(strings.TrimSpace(result[0])))) + } + if length >= 2 { + name = strings.TrimSpace(result[1]) + } + if length >= 3 { + result := result[2:] + anonymous := 0 + for _, param := range result { + index := strings.Index(param, "=") + key := "" + if index > 0 { + key = strings.TrimSpace(param[:index]) + } else { + key = fmt.Sprint(AutoParameterConfigKeyAnonymous, anonymous) + } + apc.Set(AutoParameterConfigKey(key), strings.TrimSpace(param[index+1:])) + } + } + return +} diff --git a/vendor/github.com/caicloud/nirvana/service/utils.go b/vendor/github.com/caicloud/nirvana/service/utils.go new file mode 100644 index 0000000..d40d69a --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/service/utils.go @@ -0,0 +1,162 @@ +/* +Copyright 2017 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "context" + "io" + "mime" + "net/http" + "os" + "path/filepath" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/errors" +) + +// WrapHTTPHandler wraps an http handler to definition function. +func WrapHTTPHandler(h http.Handler) func(ctx context.Context) { + return func(ctx context.Context) { + httpCtx := HTTPContextFrom(ctx) + h.ServeHTTP(httpCtx.ResponseWriter(), httpCtx.Request()) + } +} + +// WrapHTTPHandlerFunc wraps an http handler func to definition function. +func WrapHTTPHandlerFunc(f http.HandlerFunc) func(ctx context.Context) { + return func(ctx context.Context) { + httpCtx := HTTPContextFrom(ctx) + f(httpCtx.ResponseWriter(), httpCtx.Request()) + } +} + +// FileNotFound is an error factory to show why can't find a file. +// This error may contains private information. Don't return this error to end users directly. +var FileNotFound = errors.NotFound.Build("Nirvana:Service:FileNotFound", "can't find file ${path} because ${reason}") + +// FileForbidden is an error factory to show why can't access a file. +// This error may contains private information. Don't return this error to end users directly. +var FileForbidden = errors.Forbidden.Build("Nirvana:Service:FileForbidden", "can't access file ${path} because ${reason}") + +// UnreadableFile is an error factory to show why can't read a file. +// This error may contains private information. Don't return this error to end users directly. +var UnreadableFile = errors.InternalServerError.Build("Nirvana:Service:UnreadableFile", "can't read file ${path} because ${reason}") + +// UnseekableFile is an error factory to show why can't seek a file. +// This error may contains private information. Don't return this error to end users directly. +var UnseekableFile = errors.InternalServerError.Build("Nirvana:Service:UnseekableFile", "can't seek file ${path} because ${reason}") + +// ReadFile reads file and returns mime type. +func ReadFile(path string) (string, io.ReadCloser, error) { + file, err := os.Open(path) + if err != nil { + switch { + case os.IsNotExist(err): + return "", nil, FileNotFound.Error(path, err) + case os.IsPermission(err): + return "", nil, FileForbidden.Error(path, err) + } + return "", nil, UnreadableFile.Error(path, err) + } + ctype := mime.TypeByExtension(filepath.Ext(path)) + if ctype == "" { + // Read a chunk to decide between utf-8 text and binary + var buf [512]byte + n, _ := io.ReadFull(file, buf[:]) + ctype = http.DetectContentType(buf[:n]) + _, err := file.Seek(0, io.SeekStart) + if err != nil { + return "", nil, UnseekableFile.Error(path, err) + } + } + if ctype == "" { + ctype = definition.MIMEOctetStream + } + return ctype, file, nil +} + +// MetaForContentType returns a meta for content type. +func MetaForContentType(ctype string) map[string]string { + return map[string]string{"Content-Type": ctype} +} + +// Internal error factories: +var noExecutorForMethod = errors.MethodNotAllowed.Build("Nirvana:Service:NoExecutorForMethod", "method not allowed") +var noExecutorForContentType = errors.UnsupportedMediaType.Build("Nirvana:Service:NoExecutorForContentType", "unsupported media type") +var noExecutorToProduce = errors.NotAcceptable.Build("Nirvana:Service:NoExecutorToProduce", "not acceptable") +var invalidContentType = errors.BadRequest.Build("Nirvana:Service:InvalidContentType", "invalid content type ${type}") +var invalidConversion = errors.BadRequest.Build("Nirvana:Service:InvalidConversion", "can't convert ${data} to ${type}") + +var noRouter = errors.InternalServerError.Build("Nirvana:Service:NoRouter", "no router to build service") +var invalidService = errors.InternalServerError.Build("Nirvana:Service:NoResponse", "no response") +var invalidConsumer = errors.InternalServerError.Build("Nirvana:Service:InvalidConsumer", "${type} is invalid for consumer") +var invalidProducer = errors.InternalServerError.Build("Nirvana:Service:InvalidProducer", "${type} is invalid for producer") +var noConnectionHijacker = errors.InternalServerError.Build("Nirvana:Service:NoConnectionHijacker", + "underlying http.ResponseWriter does not implement http.Hijacker") +var definitionNoMethod = errors.InternalServerError.Build("Nirvana:Service:DefinitionNoMethod", "no http method in [${method}]${path}") +var definitionNoConsumes = errors.InternalServerError.Build("Nirvana:Service:DefinitionNoConsumes", "no content type to consume in [${method}]${path}") +var definitionNoProduces = errors.InternalServerError.Build("Nirvana:Service:DefinitionNoProduces", "no content type to produce in [${method}]${path}") +var definitionNoErrorProduces = errors.InternalServerError.Build("Nirvana:Service:DefinitionNoErrorProduces", + "no content type to produce error in [${method}]${path}") +var definitionNoFunction = errors.InternalServerError.Build("Nirvana:Service:DefinitionNoFunction", "no function in [${method}]${path}") +var definitionInvalidFunctionType = errors.InternalServerError.Build("Nirvana:Service:DefinitionInvalidFunctionType", + "${type} is not function in [${method}]${path}") + +var definitionNoConsumer = errors.InternalServerError.Build("Nirvana:Service:DefinitionNoConsumer", + "no consumer for content type ${type} in [${method}]${path}") + +var definitionNoProducer = errors.InternalServerError.Build("Nirvana:Service:DefinitionNoProducer", + "no producer for content type ${type} in [${method}]${path}") + +var definitionConflict = errors.InternalServerError.Build("Nirvana:Service:DefinitionConflict", + "consumer-producer pair ${key}:${value} conflicts in [http.${method}]${path}") + +var definitionUnmatchedParameters = errors.InternalServerError.Build("Nirvana:Service:DefinitionUnmatchedParameters", + "function ${function} has ${count} parameters but want ${desired} in ${path}, "+ + "you can define it with descriptor->definition[]->parameters[]") + +var definitionUnmatchedResults = errors.InternalServerError.Build("Nirvana:Service:DefinitionUnmatchedResults", + "function ${function} has ${count} results but want ${desired} in ${path}, "+ + "you can define it with descriptor->definition[]->results[]") + +var noDestinationHandler = errors.InternalServerError.Build("Nirvana:Service:NoDestinationHandler", "no destination handler for destination ${destination}, "+ + "you can define it with descriptor->definition[]->results[]->destination") + +var noContext = errors.InternalServerError.Build("Nirvana:Service:NoContext", "can't find http context, "+ + "you should define `ctx context.Context` as the first parameter of your handler function") + +var requiredField = errors.InternalServerError.Build("Nirvana:Service:RequiredField", "required field ${field} in ${source} but got empty") +var invalidMetaType = errors.InternalServerError.Build("Nirvana:Service:InvalidMetaType", "can't recognize meta for type ${type}") +var noProducerToWrite = errors.NotAcceptable.Build("Nirvana:Service:NoProducerToWrite", "can't find producer for accept types ${types}") +var invalidMethod = errors.InternalServerError.Build("Nirvana:Service:InvalidMethod", "http method ${method} is invalid") +var invalidStatusCode = errors.InternalServerError.Build("Nirvana:Service:InvalidStatusCode", "http status code must be in [100,599]") +var unassignableType = errors.InternalServerError.Build("Nirvana:Service:UnassignableType", "type ${typeA} can't assign to ${typeB}") +var noConverter = errors.InternalServerError.Build("Nirvana:Service:UnassignableType", "no converter for type ${type}") +var invalidBodyType = errors.InternalServerError.Build("Nirvana:Service:InvalidBodyType", "${type} is not a valid type for body") +var noPrefab = errors.InternalServerError.Build("Nirvana:Service:NoPrefab", "no prefab named ${name}") +var invalidAutoParameter = errors.InternalServerError.Build("Nirvana:Service:InvalidAutoParameter", "${type} is not a struct or a pointer to struct") +var noParameterGenerator = errors.InternalServerError.Build("Nirvana:Service:NoParameterGenerator", "no parameter generator for source ${source}") +var invalidFieldTag = errors.InternalServerError.Build("Nirvana:Service:InvalidFieldTag", "filed tag ${tag} is invalid") +var noName = errors.InternalServerError.Build("Nirvana:Service:NoName", "${source} must have a name") +var invalidTypeForConsumer = errors.InternalServerError.Build("Nirvana:Service:InvalidTypeForConsumer", + "consumer ${content} can't consume data for type ${type}") +var invalidTypeForProducer = errors.InternalServerError.Build("Nirvana:Service:InvalidTypeForProducer", + "producer ${content} can't produce data for type ${type}") +var invalidOperatorInType = errors.InternalServerError.Build("Nirvana:Service:InvalidOperatorInType", + "the type ${type} is not compatible to the in type of the ${index} operator") +var invalidOperatorOutType = errors.InternalServerError.Build("Nirvana:Service:InvalidOperatorOutType", + "the out type of the ${index} operator is not compatible to the type ${type}") diff --git a/vendor/github.com/caicloud/nirvana/utils/api/analyzer.go b/vendor/github.com/caicloud/nirvana/utils/api/analyzer.go new file mode 100644 index 0000000..a587c93 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/utils/api/analyzer.go @@ -0,0 +1,159 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "go/types" + "path/filepath" + "strings" +) + +// Analyzer analyzes go packages. +type Analyzer struct { + root string + files map[string]*ast.File + packageFiles map[string][]*ast.File + packages map[string]*types.Package + fileset *token.FileSet +} + +// NewAnalyzer creates a code analyzer. +func NewAnalyzer(root string) *Analyzer { + return &Analyzer{ + root: root, + files: map[string]*ast.File{}, + packageFiles: map[string][]*ast.File{}, + packages: map[string]*types.Package{}, + fileset: token.NewFileSet(), + } +} + +// Import imports a package and all packages it depends on. +func (a *Analyzer) Import(path string) (*types.Package, error) { + pkg, err := build.Import(path, a.root, 0) + if err != nil { + return nil, err + } + if parsedPkg, ok := a.packages[pkg.ImportPath]; ok { + return parsedPkg, nil + } + cfg := &types.Config{ + IgnoreFuncBodies: true, + Importer: a, + Error: func(err error) { + }, + } + files := make([]*ast.File, len(pkg.GoFiles)) + for i, path := range pkg.GoFiles { + path = filepath.Join(pkg.Dir, path) + file, ok := a.files[path] + if !ok { + file, err = parser.ParseFile(a.fileset, path, nil, parser.ParseComments) + if err != nil { + return nil, err + } + } + a.files[path] = file + files[i] = file + } + a.packageFiles[pkg.ImportPath] = files + parsedPkg, err := cfg.Check(pkg.ImportPath, a.fileset, files, nil) + a.packages[path] = parsedPkg + if pkg.ImportPath != path { + a.packages[pkg.ImportPath] = parsedPkg + } + return parsedPkg, err +} + +// PackageComments returns comments above package keyword. +// Import package before calling this method. +func (a *Analyzer) PackageComments(path string) []*ast.CommentGroup { + files, ok := a.packageFiles[path] + if !ok { + return nil + } + results := []*ast.CommentGroup{} + for _, file := range files { + for _, cg := range file.Comments { + if cg.End() < file.Package { + results = append(results, cg) + } + } + } + return results +} + +// Packages returns packages under specified directory (including itself). +// Import package before calling this method. +func (a *Analyzer) Packages(parent string, vendor bool) []string { + results := []string{} + for path := range a.packages { + if !vendor && strings.Index(path, "/vendor/") > 0 { + continue + } + if strings.HasPrefix(path, parent) { + results = append(results, path) + } + } + return results +} + +// FindPackages returns packages which contain target. +// Import package before calling this method. +func (a *Analyzer) FindPackages(target string) []string { + results := []string{} + for path := range a.packages { + if strings.Contains(path, target) { + results = append(results, path) + } + } + return results +} + +// Comments returns immediate comments above pos. +// Import package before calling this method. +func (a *Analyzer) Comments(pos token.Pos) *ast.CommentGroup { + position := a.fileset.Position(pos) + file := a.files[position.Filename] + for _, cg := range file.Comments { + cgPos := a.fileset.Position(cg.End()) + if cgPos.Line == position.Line-1 { + return cg + } + } + return nil +} + +// ObjectOf returns declaration object of target. +func (a *Analyzer) ObjectOf(pkg, name string) (types.Object, error) { + p, err := a.Import(pkg) + // Ignore the error if p is not nil. + // We need to rewrite analyzer with go/parser rather than go/types. + if p == nil && err != nil { + return nil, err + } + obj := p.Scope().Lookup(name) + if obj == nil { + return nil, fmt.Errorf("can't find declearation of %s.%s", pkg, name) + } + return obj, nil +} diff --git a/vendor/github.com/caicloud/nirvana/utils/api/api.go b/vendor/github.com/caicloud/nirvana/utils/api/api.go new file mode 100644 index 0000000..25f0565 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/utils/api/api.go @@ -0,0 +1,148 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "fmt" + "reflect" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/service" +) + +// Definitions describes all APIs and its related object types. +type Definitions struct { + // Definitions holds mappings between path and API descriptions. + Definitions map[string][]Definition + // Types contains all types used by definitions. + Types map[TypeName]*Type +} + +// Subset returns a subset required by a definition filter. +func (d *Definitions) Subset(require func(path string, def *Definition) bool) *Definitions { + definitions := map[string][]Definition{} + for path, defs := range d.Definitions { + for _, def := range defs { + if require(path, &def) { + definitions[path] = append(definitions[path], def) + } + } + } + if len(definitions) > 0 { + result := &Definitions{definitions, map[TypeName]*Type{}} + d.complete(result) + return result + } + return nil +} + +// complete fills types for a new definitions. target definitions must be a subset of this definitions. +func (d *Definitions) complete(definitions *Definitions) { + for _, defs := range definitions.Definitions { + for _, def := range defs { + d.fillTypes(definitions.Types, def.Function) + for _, parameter := range def.Parameters { + d.fillTypes(definitions.Types, parameter.Type) + } + for _, result := range def.Results { + d.fillTypes(definitions.Types, result.Type) + } + } + } +} + +// fillTypes puts a type into a map. The type must be in this definitions. +func (d *Definitions) fillTypes(types map[TypeName]*Type, name TypeName) { + if types[name] != nil { + return + } + typ, ok := d.Types[name] + if !ok { + panic(fmt.Errorf("no type named %s", name)) + } + types[name] = typ + switch typ.Kind { + case reflect.Bool, 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.Interface: + // For basic data types, there are no related types need to handle. + // For interfaces, there are no concrete entities to handle. + case reflect.Array, reflect.Slice, reflect.Ptr: + d.fillTypes(types, typ.Elem) + case reflect.Map: + d.fillTypes(types, typ.Key) + d.fillTypes(types, typ.Elem) + case reflect.Struct: + for _, field := range typ.Fields { + d.fillTypes(types, field.Type) + } + case reflect.Func: + for _, param := range typ.In { + d.fillTypes(types, param.Type) + } + for _, result := range typ.Out { + d.fillTypes(types, result.Type) + } + default: + panic(fmt.Errorf("can't recognize type %s with kind %s", name, typ.Kind.String())) + } +} + +// Container contains informations to generate APIs. +type Container struct { + modifiers service.DefinitionModifiers + descriptors []definition.Descriptor + typeContainer *TypeContainer + analyzer *Analyzer +} + +// NewContainer creates API container. +func NewContainer(root string) *Container { + return &Container{ + typeContainer: NewTypeContainer(), + analyzer: NewAnalyzer(root), + } +} + +// AddModifier add definition modifiers to container. +func (ac *Container) AddModifier(modifiers ...service.DefinitionModifier) { + ac.modifiers = append(ac.modifiers, modifiers...) +} + +// AddDescriptor add descriptors to container. +func (ac *Container) AddDescriptor(descriptors ...definition.Descriptor) { + ac.descriptors = append(ac.descriptors, descriptors...) +} + +// Generate generates API definitions. +func (ac *Container) Generate() (*Definitions, error) { + builder := service.NewBuilder() + builder.SetModifier(ac.modifiers.Combine()) + if err := builder.AddDescriptor(ac.descriptors...); err != nil { + return nil, err + } + definitions := builder.Definitions() + result, err := NewPathDefinitions(ac.typeContainer, definitions) + if err != nil { + return nil, err + } + err = ac.typeContainer.Complete(ac.analyzer) + return &Definitions{ + Definitions: result, + Types: ac.typeContainer.Types(), + }, err +} diff --git a/vendor/github.com/caicloud/nirvana/utils/api/comments.go b/vendor/github.com/caicloud/nirvana/utils/api/comments.go new file mode 100644 index 0000000..8db1d7d --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/utils/api/comments.go @@ -0,0 +1,212 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "bytes" + "fmt" + "reflect" + "regexp" + "strconv" + "strings" +) + +const ( + // CommentsOptionDescriptors is the option name of descriptors. + CommentsOptionDescriptors = "descriptors" + // CommentsOptionModifiers is the option name of modifiers. + CommentsOptionModifiers = "modifiers" + // CommentsOptionAlias is the option name of alias. + CommentsOptionAlias = "alias" + // CommentsOptionOrigin is the option name of original name. + CommentsOptionOrigin = "origin" +) + +// Comments is parsed from go comments. +type Comments struct { + lines []string + options map[string][]string +} + +var optionsRegexp = regexp.MustCompile(`^[ \t]*\+nirvana:api[ \t]*=(.*)$`) +var options = []string{CommentsOptionDescriptors, CommentsOptionModifiers, CommentsOptionAlias} + +// ParseComments parses comments and extracts nirvana options. +func ParseComments(comments string) *Comments { + c := &Comments{ + options: map[string][]string{}, + } + comments = strings.TrimSpace(comments) + lines := strings.Split(comments, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if line != "" { + matches := optionsRegexp.FindAllStringSubmatch(line, -1) + for _, match := range matches { + if len(match) != 2 { + continue + } + tag := reflect.StructTag(match[1]) + for _, option := range options { + value := tag.Get(option) + if value != "" { + opt := c.options[option] + if opt == nil { + opt = []string{} + } + opt = append(opt, value) + c.options[option] = opt + } + } + } + if len(matches) > 0 { + continue + } + c.lines = append(c.lines, line) + } else if len(c.lines) > 0 && c.lines[len(c.lines)-1] != "" { + c.lines = append(c.lines, line) + } + } + if len(c.lines) > 0 && c.lines[len(c.lines)-1] == "" { + c.lines = c.lines[:len(c.lines)-1] + } + return c +} + +// BlockComments returns block style comments. +func (c *Comments) BlockComments() string { + buf := NewBuffer() + buf.Write("/*\n") + for _, line := range c.lines { + buf.Writef("%s\n", line) + } + if len(c.options) > 0 { + buf.Writeln() + } + for option, values := range c.options { + for _, value := range values { + buf.Writef("+nirvana:api=%s:%s\n", option, strconv.Quote(value)) + } + } + buf.Write("*/") + return buf.String() +} + +// LineComments returns line style comments. +func (c *Comments) LineComments() string { + if len(c.lines) <= 0 { + return "" + } + buf := NewBuffer() + for _, line := range c.lines { + buf.Writef("// %s\n", line) + } + if len(c.options) > 0 { + buf.Write("//\n") + } + for option, values := range c.options { + for _, value := range values { + buf.Writef("// +nirvana:api=%s:%s\n", option, strconv.Quote(value)) + } + } + return buf.String() +} + +// Rename replaces the first word of this comments. If its first word is +// not the same as origin, the method returns false. +func (c *Comments) Rename(origin, target string) bool { + if len(c.lines) > 0 { + line := c.lines[0] + if strings.HasPrefix(line, origin) { + line = target + line[len(origin):] + c.lines[0] = line + return true + } + } + return false +} + +// Options returns all options. +func (c *Comments) Options() map[string][]string { + return c.options +} + +// Option returns values of an option. +func (c *Comments) Option(name string) []string { + return c.options[name] +} + +// AddOption adds an option. +func (c *Comments) AddOption(name, value string) { + c.options[name] = append(c.options[name], value) +} + +// CleanOptions removes all options. +func (c *Comments) CleanOptions() { + c.options = map[string][]string{} +} + +// String returns comments. +func (c *Comments) String() string { + return strings.Join(c.lines, "\n") +} + +// Buffer provides a buffer to write data. The buffer will panic if +// an error occurs. +type Buffer struct { + buf *bytes.Buffer +} + +// NewBuffer creates a buffer. +func NewBuffer() *Buffer { + return &Buffer{bytes.NewBuffer(nil)} +} + +// Write writes data to this buffer. +func (b *Buffer) Write(a ...interface{}) { + _, err := fmt.Fprint(b.buf, a...) + if err != nil { + panic(err) + } +} + +// Writef writes data with format to this buffer. +func (b *Buffer) Writef(format string, a ...interface{}) { + _, err := fmt.Fprintf(b.buf, format, a...) + if err != nil { + panic(err) + } +} + +// Writeln writes data with a newline to this buffer. +func (b *Buffer) Writeln(a ...interface{}) { + _, err := fmt.Fprintln(b.buf, a...) + if err != nil { + panic(err) + } +} + +// Bytes returns a slice of length b.Len() holding the unread portion of the buffer. +func (b *Buffer) Bytes() []byte { + return b.buf.Bytes() +} + +// String returns the contents of the unread portion of the buffer +// as a string. If the Buffer is a nil pointer, it returns "". +func (b *Buffer) String() string { + return b.buf.String() +} diff --git a/vendor/github.com/caicloud/nirvana/utils/api/definitions.go b/vendor/github.com/caicloud/nirvana/utils/api/definitions.go new file mode 100644 index 0000000..aab9910 --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/utils/api/definitions.go @@ -0,0 +1,188 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "encoding/json" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/service" +) + +// Parameter describes a function parameter. +type Parameter struct { + // Source is the parameter value generated from. + Source definition.Source + // Name is the name to get value from a request. + Name string + // Description describes the parameter. + Description string + // Type is parameter object type. + Type TypeName + // Default is encoded default value. + Default []byte +} + +// Result describes a function result. +type Result struct { + // Destination is the target for the result. + Destination definition.Destination + // Description describes the result. + Description string + // Type is result object type. + Type TypeName +} + +// Example is just an example. +type Example struct { + // Description describes the example. + Description string + // Type is result object type. + Type TypeName + // Instance is encoded instance data. + Instance []byte +} + +// Definition is complete version of def.Definition. +type Definition struct { + // Method is definition method. + Method definition.Method + // HTTPMethod is http method. + HTTPMethod string + // HTTPCode is http success code. + HTTPCode int + // Summary is a brief of this definition. + Summary string + // Description describes the API handler. + Description string + // Tags indicates tags of the API handler. + // It will override parent descriptor's tags. + Tags []string + // Consumes indicates how many content types the handler can consume. + // It will override parent descriptor's consumes. + Consumes []string + // Produces indicates how many content types the handler can produce. + // It will override parent descriptor's produces. + Produces []string + // ErrorProduces is used to generate data for error. If this field is empty, + // it means that this field equals to Produces. + // In some cases, successful data and error data should be generated in + // different ways. + ErrorProduces []string + // Function is a function handler. It must be func type. + Function TypeName + // Parameters describes function parameters. + Parameters []Parameter + // Results describes function retrun values. + Results []Result + // Examples contains many examples for the API handler. + Examples []Example +} + +// NewDefinition creates openapi.Definition from definition.Definition. +func NewDefinition(tc *TypeContainer, d *definition.Definition) (*Definition, error) { + cd := &Definition{ + Method: d.Method, + HTTPMethod: service.HTTPMethodFor(d.Method), + HTTPCode: service.HTTPCodeFor(d.Method), + Summary: d.Summary, + Description: d.Description, + Tags: d.Tags, + Consumes: d.Consumes, + Produces: d.Produces, + ErrorProduces: d.ErrorProduces, + Function: tc.NameOfInstance(d.Function), + } + functionType := tc.Type(cd.Function) + for i, p := range d.Parameters { + param := Parameter{ + Source: p.Source, + Name: p.Name, + Description: p.Description, + Type: functionType.In[i].Type, + } + if p.Default != nil { + data, err := encode(p.Default) + if err != nil { + return nil, err + } + param.Default = data + } + if len(p.Operators) > 0 { + param.Type = tc.NameOf(p.Operators[0].In()) + } + cd.Parameters = append(cd.Parameters, param) + } + for i, r := range d.Results { + result := Result{ + Destination: r.Destination, + Description: r.Description, + Type: functionType.Out[i].Type, + } + if len(r.Operators) > 0 { + result.Type = tc.NameOf(r.Operators[len(r.Operators)-1].Out()) + } + cd.Results = append(cd.Results, result) + } + for _, e := range d.Examples { + example := Example{ + Description: e.Description, + } + if e.Instance != nil { + example.Type = tc.NameOfInstance(e.Instance) + data, err := encode(e.Instance) + if err != nil { + return nil, err + } + example.Instance = data + } + cd.Examples = append(cd.Examples, example) + } + return cd, nil +} + +// NewDefinitions creates a list of definitions. +func NewDefinitions(tc *TypeContainer, definitions []definition.Definition) ([]Definition, error) { + result := make([]Definition, len(definitions)) + for i, d := range definitions { + cd, err := NewDefinition(tc, &d) + if err != nil { + return nil, err + } + result[i] = *cd + } + return result, nil +} + +// NewPathDefinitions creates a list of definitions with path. +func NewPathDefinitions(tc *TypeContainer, definitions map[string][]definition.Definition) (map[string][]Definition, error) { + result := make(map[string][]Definition) + for path, defs := range definitions { + cds, err := NewDefinitions(tc, defs) + if err != nil { + return nil, err + } + result[path] = cds + + } + return result, nil +} + +// encode encodes instance to json format. +func encode(ins interface{}) ([]byte, error) { + return json.Marshal(ins) +} diff --git a/vendor/github.com/caicloud/nirvana/utils/api/types.go b/vendor/github.com/caicloud/nirvana/utils/api/types.go new file mode 100644 index 0000000..0e5d43c --- /dev/null +++ b/vendor/github.com/caicloud/nirvana/utils/api/types.go @@ -0,0 +1,309 @@ +/* +Copyright 2018 Caicloud Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package api + +import ( + "fmt" + "go/types" + "reflect" + "runtime" + "strings" + "sync" + "unsafe" +) + +// TypeName is unique name for go types. +type TypeName string + +// TypeNameInvalid indicates an invalid type name. +const TypeNameInvalid = "" + +// StructField describes a field of a struct. +type StructField struct { + // Name is the field name. + Name string + // Comments of the type. + Comments string + // PkgPath is the package path that qualifies a lower case (unexported) + // field name. It is empty for upper case (exported) field names. + PkgPath string + // Type is field type name. + Type TypeName + // Tag is field tag. + Tag reflect.StructTag + // Offset within struct, in bytes. + Offset uintptr + // Index sequence for Type.FieldByIndex. + Index []int + // Anonymous shows whether the field is an embedded field. + Anonymous bool +} + +// FuncField describes a field of function. +type FuncField struct { + // Name is the field name. + Name string + // Type is field type name. + Type TypeName +} + +// Type describes an go type. +type Type struct { + // Name is short type name. + Name string + // Comments of the type. + Comments string + // PkgPath is the package for this type. + PkgPath string + // Kind is type kind. + Kind reflect.Kind + // Key is map key type. Only used in map. + Key TypeName + // Elem is the element type of map, slice, array, pointer. + Elem TypeName + // Fields contains all struct fields of a struct. + Fields []StructField + // In presents fields of function input parameters. + In []FuncField + // Out presents fields of function output results. + Out []FuncField + // Conflict identifies the index of current type in a list of + // types which have same type names. In most cases, this field is 0. + Conflict int +} + +// RawTypeName returns raw type name without confliction. +func (t *Type) RawTypeName() TypeName { + if t.Name == "" { + return TypeNameInvalid + } + if t.PkgPath == "" { + return TypeName(t.Name) + } + return TypeName(t.PkgPath + "." + t.Name) +} + +// TypeName returns type unique name. +func (t *Type) TypeName() TypeName { + tn := t.RawTypeName() + if tn != TypeNameInvalid && t.Conflict > 0 { + tn = TypeName(fmt.Sprintf("%s [%d]", tn, t.Conflict)) + } + return tn +} + +// TypeContainer contains types. +type TypeContainer struct { + lock sync.RWMutex + types map[TypeName]*Type + real map[TypeName][]reflect.Type +} + +// NewTypeContainer creates a type container. +func NewTypeContainer() *TypeContainer { + return &TypeContainer{ + types: make(map[TypeName]*Type), + real: make(map[TypeName][]reflect.Type), + } +} + +// Type gets type via type name. +func (tc *TypeContainer) Type(name TypeName) *Type { + if name == TypeNameInvalid { + return nil + } + tc.lock.RLock() + defer tc.lock.RUnlock() + return tc.types[name] +} + +// Types returns all types +func (tc *TypeContainer) Types() map[TypeName]*Type { + return tc.types +} + +// setType sets type with unique name. +func (tc *TypeContainer) setType(t *Type, typ reflect.Type) { + tn := t.RawTypeName() + if tn == TypeNameInvalid { + return + } + tc.lock.Lock() + defer tc.lock.Unlock() + types := tc.real[tn] + index := len(types) + for i, originalType := range types { + if originalType == typ { + index = i + } + } + t.Conflict = index + tn = t.TypeName() + tc.types[tn] = t + tc.real[tn] = append(types, typ) +} + +// NameOf gets an unique name of a type. +func (tc *TypeContainer) NameOf(typ reflect.Type) TypeName { + t := &Type{ + Name: typ.Name(), + PkgPath: typ.PkgPath(), + Kind: typ.Kind(), + } + if t.Name == "" && t.Kind == reflect.Interface { + // Type interface{} has no name. Set it. + t.Name = "interface{}" + } + tn := t.TypeName() + if tn != TypeNameInvalid && tc.Type(tn) != nil { + return tn + } + switch t.Kind { + case reflect.Array, reflect.Slice: + t.Elem = tc.NameOf(typ.Elem()) + if t.Name == "" { + t.Name = fmt.Sprint("[]", t.Elem) + } + case reflect.Ptr: + t.Elem = tc.NameOf(typ.Elem()) + if t.Name == "" { + t.Name = fmt.Sprint("*", t.Elem) + } + case reflect.Map: + t.Key = tc.NameOf(typ.Key()) + t.Elem = tc.NameOf(typ.Elem()) + if t.Name == "" { + t.Name = fmt.Sprintf("map[%s]%s", t.Key, t.Elem) + } + case reflect.Chan: + t.Elem = tc.NameOf(typ.Elem()) + if t.Name == "" { + t.Name = fmt.Sprint("chan ", t.Elem) + } + case reflect.Struct: + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + field := StructField{ + Name: f.Name, + PkgPath: f.PkgPath, + Tag: f.Tag, + Offset: f.Offset, + Index: f.Index, + Anonymous: f.Anonymous, + Type: tc.NameOf(f.Type), + } + t.Fields = append(t.Fields, field) + } + case reflect.Func: + tc.fillFunctionSignature(t, typ) + if t.Name == "" { + t.Name = fmt.Sprintf("func(%s)", strings.Join(*(*[]string)(unsafe.Pointer(&t.In)), ", ")) + if len(t.Out) == 1 { + t.Name += fmt.Sprintf(" %s", t.Out[0]) + } else if len(t.Out) > 1 { + t.Name += fmt.Sprintf(" (%s)", strings.Join(*(*[]string)(unsafe.Pointer(&t.Out)), ", ")) + } + } + } + tc.setType(t, typ) + return t.TypeName() +} + +func (tc *TypeContainer) fillFunctionSignature(t *Type, typ reflect.Type) { + for i := 0; i < typ.NumIn(); i++ { + t.In = append(t.In, FuncField{Type: tc.NameOf(typ.In(i))}) + } + for i := 0; i < typ.NumOut(); i++ { + t.Out = append(t.Out, FuncField{Type: tc.NameOf(typ.Out(i))}) + } +} + +// NameOfInstance gets type name of an instance. +func (tc *TypeContainer) NameOfInstance(ins interface{}) TypeName { + typ := reflect.TypeOf(ins) + if typ.Kind() != reflect.Func { + return tc.NameOf(typ) + } + funcInfo := runtime.FuncForPC(reflect.ValueOf(ins).Pointer()) + t := &Type{ + Kind: typ.Kind(), + Name: funcInfo.Name(), + } + if index := strings.LastIndexByte(t.Name, '.'); index >= 0 { + t.PkgPath = t.Name[:index] + t.Name = t.Name[index+1:] + } + tc.fillFunctionSignature(t, typ) + tc.setType(t, typ) + return t.TypeName() +} + +// Complete fills comments for all types. +func (tc *TypeContainer) Complete(analyzer *Analyzer) error { + tc.lock.Lock() + defer tc.lock.Unlock() + errors := []error{} + for _, typ := range tc.types { + if typ.PkgPath == "" { + continue + } + obj, err := analyzer.ObjectOf(typ.PkgPath, typ.Name) + if err != nil { + errors = append(errors, err) + continue + } + if comments := analyzer.Comments(obj.Pos()); comments != nil { + typ.Comments = comments.Text() + } + switch typ.Kind { + case reflect.Struct: + o, ok := obj.Type().(*types.Named) + if !ok { + continue + } + st, ok := o.Underlying().(*types.Struct) + if !ok { + continue + } + for i := 0; i < st.NumFields(); i++ { + field := st.Field(i) + for j := 0; j < len(typ.Fields); j++ { + if typ.Fields[j].Name == field.Name() { + if comments := analyzer.Comments(field.Pos()); comments != nil { + typ.Fields[j].Comments = comments.Text() + } + break + } + } + } + case reflect.Func: + o, ok := obj.Type().(*types.Signature) + if !ok { + continue + } + for i := 0; i < o.Params().Len(); i++ { + param := o.Params().At(i) + typ.In[i].Name = param.Name() + } + for i := 0; i < o.Results().Len(); i++ { + result := o.Results().At(i) + typ.Out[i].Name = result.Name() + } + } + } + return fmt.Errorf("%v", errors) +} diff --git a/vendor/github.com/go-playground/locales/.gitignore b/vendor/github.com/go-playground/locales/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/go-playground/locales/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/go-playground/locales/LICENSE b/vendor/github.com/go-playground/locales/LICENSE new file mode 100644 index 0000000..75854ac --- /dev/null +++ b/vendor/github.com/go-playground/locales/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Go Playground + +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. \ No newline at end of file diff --git a/vendor/github.com/go-playground/locales/README.md b/vendor/github.com/go-playground/locales/README.md new file mode 100644 index 0000000..43329f8 --- /dev/null +++ b/vendor/github.com/go-playground/locales/README.md @@ -0,0 +1,172 @@ +## locales +![Project status](https://img.shields.io/badge/version-0.12.1-green.svg) +[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/locales/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/locales) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/locales)](https://goreportcard.com/report/github.com/go-playground/locales) +[![GoDoc](https://godoc.org/github.com/go-playground/locales?status.svg)](https://godoc.org/github.com/go-playground/locales) +![License](https://img.shields.io/dub/l/vibe-d.svg) +[![Gitter](https://badges.gitter.im/go-playground/locales.svg)](https://gitter.im/go-playground/locales?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Locales is a set of locales generated from the [Unicode CLDR Project](http://cldr.unicode.org/) which can be used independently or within +an i18n package; these were built for use with, but not exclusive to, [Universal Translator](https://github.com/go-playground/universal-translator). + +Features +-------- +- [x] Rules generated from the latest [CLDR](http://cldr.unicode.org/index/downloads) data, v31.0.1 +- [x] Contains Cardinal, Ordinal and Range Plural Rules +- [x] Contains Month, Weekday and Timezone translations built in +- [x] Contains Date & Time formatting functions +- [x] Contains Number, Currency, Accounting and Percent formatting functions +- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere ) + +Full Tests +-------------------- +I could sure use your help adding tests for every locale, it is a huge undertaking and I just don't have the free time to do it all at the moment; +any help would be **greatly appreciated!!!!** please see [issue](https://github.com/go-playground/locales/issues/1) for details. + +Installation +----------- + +Use go get + +```shell +go get github.com/go-playground/locales +``` + +NOTES +-------- +You'll notice most return types are []byte, this is because most of the time the results will be concatenated with a larger body +of text and can avoid some allocations if already appending to a byte array, otherwise just cast as string. + +Usage +------- +```go +package main + +import ( + "fmt" + "time" + + "github.com/go-playground/locales/currency" + "github.com/go-playground/locales/en_CA" +) + +func main() { + + loc, _ := time.LoadLocation("America/Toronto") + datetime := time.Date(2016, 02, 03, 9, 0, 1, 0, loc) + + l := en_CA.New() + + // Dates + fmt.Println(l.FmtDateFull(datetime)) + fmt.Println(l.FmtDateLong(datetime)) + fmt.Println(l.FmtDateMedium(datetime)) + fmt.Println(l.FmtDateShort(datetime)) + + // Times + fmt.Println(l.FmtTimeFull(datetime)) + fmt.Println(l.FmtTimeLong(datetime)) + fmt.Println(l.FmtTimeMedium(datetime)) + fmt.Println(l.FmtTimeShort(datetime)) + + // Months Wide + fmt.Println(l.MonthWide(time.January)) + fmt.Println(l.MonthWide(time.February)) + fmt.Println(l.MonthWide(time.March)) + // ... + + // Months Abbreviated + fmt.Println(l.MonthAbbreviated(time.January)) + fmt.Println(l.MonthAbbreviated(time.February)) + fmt.Println(l.MonthAbbreviated(time.March)) + // ... + + // Months Narrow + fmt.Println(l.MonthNarrow(time.January)) + fmt.Println(l.MonthNarrow(time.February)) + fmt.Println(l.MonthNarrow(time.March)) + // ... + + // Weekdays Wide + fmt.Println(l.WeekdayWide(time.Sunday)) + fmt.Println(l.WeekdayWide(time.Monday)) + fmt.Println(l.WeekdayWide(time.Tuesday)) + // ... + + // Weekdays Abbreviated + fmt.Println(l.WeekdayAbbreviated(time.Sunday)) + fmt.Println(l.WeekdayAbbreviated(time.Monday)) + fmt.Println(l.WeekdayAbbreviated(time.Tuesday)) + // ... + + // Weekdays Short + fmt.Println(l.WeekdayShort(time.Sunday)) + fmt.Println(l.WeekdayShort(time.Monday)) + fmt.Println(l.WeekdayShort(time.Tuesday)) + // ... + + // Weekdays Narrow + fmt.Println(l.WeekdayNarrow(time.Sunday)) + fmt.Println(l.WeekdayNarrow(time.Monday)) + fmt.Println(l.WeekdayNarrow(time.Tuesday)) + // ... + + var f64 float64 + + f64 = -10356.4523 + + // Number + fmt.Println(l.FmtNumber(f64, 2)) + + // Currency + fmt.Println(l.FmtCurrency(f64, 2, currency.CAD)) + fmt.Println(l.FmtCurrency(f64, 2, currency.USD)) + + // Accounting + fmt.Println(l.FmtAccounting(f64, 2, currency.CAD)) + fmt.Println(l.FmtAccounting(f64, 2, currency.USD)) + + f64 = 78.12 + + // Percent + fmt.Println(l.FmtPercent(f64, 0)) + + // Plural Rules for locale, so you know what rules you must cover + fmt.Println(l.PluralsCardinal()) + fmt.Println(l.PluralsOrdinal()) + + // Cardinal Plural Rules + fmt.Println(l.CardinalPluralRule(1, 0)) + fmt.Println(l.CardinalPluralRule(1.0, 0)) + fmt.Println(l.CardinalPluralRule(1.0, 1)) + fmt.Println(l.CardinalPluralRule(3, 0)) + + // Ordinal Plural Rules + fmt.Println(l.OrdinalPluralRule(21, 0)) // 21st + fmt.Println(l.OrdinalPluralRule(22, 0)) // 22nd + fmt.Println(l.OrdinalPluralRule(33, 0)) // 33rd + fmt.Println(l.OrdinalPluralRule(34, 0)) // 34th + + // Range Plural Rules + fmt.Println(l.RangePluralRule(1, 0, 1, 0)) // 1-1 + fmt.Println(l.RangePluralRule(1, 0, 2, 0)) // 1-2 + fmt.Println(l.RangePluralRule(5, 0, 8, 0)) // 5-8 +} +``` + +NOTES: +------- +These rules were generated from the [Unicode CLDR Project](http://cldr.unicode.org/), if you encounter any issues +I strongly encourage contributing to the CLDR project to get the locale information corrected and the next time +these locales are regenerated the fix will come with. + +I do however realize that time constraints are often important and so there are two options: + +1. Create your own locale, copy, paste and modify, and ensure it complies with the `Translator` interface. +2. Add an exception in the locale generation code directly and once regenerated, fix will be in place. + +Please to not make fixes inside the locale files, they WILL get overwritten when the locales are regenerated. + +License +------ +Distributed under MIT License, please see license file in code for more details. diff --git a/vendor/github.com/go-playground/locales/currency/currency.go b/vendor/github.com/go-playground/locales/currency/currency.go new file mode 100644 index 0000000..cdaba59 --- /dev/null +++ b/vendor/github.com/go-playground/locales/currency/currency.go @@ -0,0 +1,308 @@ +package currency + +// Type is the currency type associated with the locales currency enum +type Type int + +// locale currencies +const ( + ADP Type = iota + AED + AFA + AFN + ALK + ALL + AMD + ANG + AOA + AOK + AON + AOR + ARA + ARL + ARM + ARP + ARS + ATS + AUD + AWG + AZM + AZN + BAD + BAM + BAN + BBD + BDT + BEC + BEF + BEL + BGL + BGM + BGN + BGO + BHD + BIF + BMD + BND + BOB + BOL + BOP + BOV + BRB + BRC + BRE + BRL + BRN + BRR + BRZ + BSD + BTN + BUK + BWP + BYB + BYN + BYR + BZD + CAD + CDF + CHE + CHF + CHW + CLE + CLF + CLP + CNH + CNX + CNY + COP + COU + CRC + CSD + CSK + CUC + CUP + CVE + CYP + CZK + DDM + DEM + DJF + DKK + DOP + DZD + ECS + ECV + EEK + EGP + ERN + ESA + ESB + ESP + ETB + EUR + FIM + FJD + FKP + FRF + GBP + GEK + GEL + GHC + GHS + GIP + GMD + GNF + GNS + GQE + GRD + GTQ + GWE + GWP + GYD + HKD + HNL + HRD + HRK + HTG + HUF + IDR + IEP + ILP + ILR + ILS + INR + IQD + IRR + ISJ + ISK + ITL + JMD + JOD + JPY + KES + KGS + KHR + KMF + KPW + KRH + KRO + KRW + KWD + KYD + KZT + LAK + LBP + LKR + LRD + LSL + LTL + LTT + LUC + LUF + LUL + LVL + LVR + LYD + MAD + MAF + MCF + MDC + MDL + MGA + MGF + MKD + MKN + MLF + MMK + MNT + MOP + MRO + MTL + MTP + MUR + MVP + MVR + MWK + MXN + MXP + MXV + MYR + MZE + MZM + MZN + NAD + NGN + NIC + NIO + NLG + NOK + NPR + NZD + OMR + PAB + PEI + PEN + PES + PGK + PHP + PKR + PLN + PLZ + PTE + PYG + QAR + RHD + ROL + RON + RSD + RUB + RUR + RWF + SAR + SBD + SCR + SDD + SDG + SDP + SEK + SGD + SHP + SIT + SKK + SLL + SOS + SRD + SRG + SSP + STD + STN + SUR + SVC + SYP + SZL + THB + TJR + TJS + TMM + TMT + TND + TOP + TPE + TRL + TRY + TTD + TWD + TZS + UAH + UAK + UGS + UGX + USD + USN + USS + UYI + UYP + UYU + UZS + VEB + VEF + VND + VNN + VUV + WST + XAF + XAG + XAU + XBA + XBB + XBC + XBD + XCD + XDR + XEU + XFO + XFU + XOF + XPD + XPF + XPT + XRE + XSU + XTS + XUA + XXX + YDD + YER + YUD + YUM + YUN + YUR + ZAL + ZAR + ZMK + ZMW + ZRN + ZRZ + ZWD + ZWL + ZWR +) diff --git a/vendor/github.com/go-playground/locales/logo.png b/vendor/github.com/go-playground/locales/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3038276e6873076ecd542099e95b25037b1c0c34 GIT binary patch literal 37360 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 z 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/go-playground/locales/rules.go b/vendor/github.com/go-playground/locales/rules.go new file mode 100644 index 0000000..9202900 --- /dev/null +++ b/vendor/github.com/go-playground/locales/rules.go @@ -0,0 +1,293 @@ +package locales + +import ( + "strconv" + "time" + + "github.com/go-playground/locales/currency" +) + +// // ErrBadNumberValue is returned when the number passed for +// // plural rule determination cannot be parsed +// type ErrBadNumberValue struct { +// NumberValue string +// InnerError error +// } + +// // Error returns ErrBadNumberValue error string +// func (e *ErrBadNumberValue) Error() string { +// return fmt.Sprintf("Invalid Number Value '%s' %s", e.NumberValue, e.InnerError) +// } + +// var _ error = new(ErrBadNumberValue) + +// PluralRule denotes the type of plural rules +type PluralRule int + +// PluralRule's +const ( + PluralRuleUnknown PluralRule = iota + PluralRuleZero // zero + PluralRuleOne // one - singular + PluralRuleTwo // two - dual + PluralRuleFew // few - paucal + PluralRuleMany // many - also used for fractions if they have a separate class + PluralRuleOther // other - required—general plural form—also used if the language only has a single form +) + +const ( + pluralsString = "UnknownZeroOneTwoFewManyOther" +) + +// Translator encapsulates an instance of a locale +// NOTE: some values are returned as a []byte just in case the caller +// wishes to add more and can help avoid allocations; otherwise just cast as string +type Translator interface { + + // The following Functions are for overriding, debugging or developing + // with a Translator Locale + + // Locale returns the string value of the translator + Locale() string + + // returns an array of cardinal plural rules associated + // with this translator + PluralsCardinal() []PluralRule + + // returns an array of ordinal plural rules associated + // with this translator + PluralsOrdinal() []PluralRule + + // returns an array of range plural rules associated + // with this translator + PluralsRange() []PluralRule + + // returns the cardinal PluralRule given 'num' and digits/precision of 'v' for locale + CardinalPluralRule(num float64, v uint64) PluralRule + + // returns the ordinal PluralRule given 'num' and digits/precision of 'v' for locale + OrdinalPluralRule(num float64, v uint64) PluralRule + + // returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for locale + RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) PluralRule + + // returns the locales abbreviated month given the 'month' provided + MonthAbbreviated(month time.Month) string + + // returns the locales abbreviated months + MonthsAbbreviated() []string + + // returns the locales narrow month given the 'month' provided + MonthNarrow(month time.Month) string + + // returns the locales narrow months + MonthsNarrow() []string + + // returns the locales wide month given the 'month' provided + MonthWide(month time.Month) string + + // returns the locales wide months + MonthsWide() []string + + // returns the locales abbreviated weekday given the 'weekday' provided + WeekdayAbbreviated(weekday time.Weekday) string + + // returns the locales abbreviated weekdays + WeekdaysAbbreviated() []string + + // returns the locales narrow weekday given the 'weekday' provided + WeekdayNarrow(weekday time.Weekday) string + + // WeekdaysNarrowreturns the locales narrow weekdays + WeekdaysNarrow() []string + + // returns the locales short weekday given the 'weekday' provided + WeekdayShort(weekday time.Weekday) string + + // returns the locales short weekdays + WeekdaysShort() []string + + // returns the locales wide weekday given the 'weekday' provided + WeekdayWide(weekday time.Weekday) string + + // returns the locales wide weekdays + WeekdaysWide() []string + + // The following Functions are common Formatting functionsfor the Translator's Locale + + // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v' + FmtNumber(num float64, v uint64) string + + // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v' + // NOTE: 'num' passed into FmtPercent is assumed to be in percent already + FmtPercent(num float64, v uint64) string + + // returns the currency representation of 'num' with digits/precision of 'v' for locale + FmtCurrency(num float64, v uint64, currency currency.Type) string + + // returns the currency representation of 'num' with digits/precision of 'v' for locale + // in accounting notation. + FmtAccounting(num float64, v uint64, currency currency.Type) string + + // returns the short date representation of 't' for locale + FmtDateShort(t time.Time) string + + // returns the medium date representation of 't' for locale + FmtDateMedium(t time.Time) string + + // returns the long date representation of 't' for locale + FmtDateLong(t time.Time) string + + // returns the full date representation of 't' for locale + FmtDateFull(t time.Time) string + + // returns the short time representation of 't' for locale + FmtTimeShort(t time.Time) string + + // returns the medium time representation of 't' for locale + FmtTimeMedium(t time.Time) string + + // returns the long time representation of 't' for locale + FmtTimeLong(t time.Time) string + + // returns the full time representation of 't' for locale + FmtTimeFull(t time.Time) string +} + +// String returns the string value of PluralRule +func (p PluralRule) String() string { + + switch p { + case PluralRuleZero: + return pluralsString[7:11] + case PluralRuleOne: + return pluralsString[11:14] + case PluralRuleTwo: + return pluralsString[14:17] + case PluralRuleFew: + return pluralsString[17:20] + case PluralRuleMany: + return pluralsString[20:24] + case PluralRuleOther: + return pluralsString[24:] + default: + return pluralsString[:7] + } +} + +// +// Precision Notes: +// +// must specify a precision >= 0, and here is why https://play.golang.org/p/LyL90U0Vyh +// +// v := float64(3.141) +// i := float64(int64(v)) +// +// fmt.Println(v - i) +// +// or +// +// s := strconv.FormatFloat(v-i, 'f', -1, 64) +// fmt.Println(s) +// +// these will not print what you'd expect: 0.14100000000000001 +// and so this library requires a precision to be specified, or +// inaccurate plural rules could be applied. +// +// +// +// n - absolute value of the source number (integer and decimals). +// i - integer digits of n. +// v - number of visible fraction digits in n, with trailing zeros. +// w - number of visible fraction digits in n, without trailing zeros. +// f - visible fractional digits in n, with trailing zeros. +// t - visible fractional digits in n, without trailing zeros. +// +// +// Func(num float64, v uint64) // v = digits/precision and prevents -1 as a special case as this can lead to very unexpected behaviour, see precision note's above. +// +// n := math.Abs(num) +// i := int64(n) +// v := v +// +// +// w := strconv.FormatFloat(num-float64(i), 'f', int(v), 64) // then parse backwards on string until no more zero's.... +// f := strconv.FormatFloat(n, 'f', int(v), 64) // then turn everything after decimal into an int64 +// t := strconv.FormatFloat(n, 'f', int(v), 64) // then parse backwards on string until no more zero's.... +// +// +// +// General Inclusion Rules +// - v will always be available inherently +// - all require n +// - w requires i +// + +// W returns the number of visible fraction digits in N, without trailing zeros. +func W(n float64, v uint64) (w int64) { + + s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) + + // with either be '0' or '0.xxxx', so if 1 then w will be zero + // otherwise need to parse + if len(s) != 1 { + + s = s[2:] + end := len(s) + 1 + + for i := end; i >= 0; i-- { + if s[i] != '0' { + end = i + 1 + break + } + } + + w = int64(len(s[:end])) + } + + return +} + +// F returns the visible fractional digits in N, with trailing zeros. +func F(n float64, v uint64) (f int64) { + + s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) + + // with either be '0' or '0.xxxx', so if 1 then f will be zero + // otherwise need to parse + if len(s) != 1 { + + // ignoring error, because it can't fail as we generated + // the string internally from a real number + f, _ = strconv.ParseInt(s[2:], 10, 64) + } + + return +} + +// T returns the visible fractional digits in N, without trailing zeros. +func T(n float64, v uint64) (t int64) { + + s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) + + // with either be '0' or '0.xxxx', so if 1 then t will be zero + // otherwise need to parse + if len(s) != 1 { + + s = s[2:] + end := len(s) + 1 + + for i := end; i >= 0; i-- { + if s[i] != '0' { + end = i + 1 + break + } + } + + // ignoring error, because it can't fail as we generated + // the string internally from a real number + t, _ = strconv.ParseInt(s[:end], 10, 64) + } + + return +} diff --git a/vendor/github.com/go-playground/universal-translator/.gitignore b/vendor/github.com/go-playground/universal-translator/.gitignore new file mode 100644 index 0000000..2661785 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof \ No newline at end of file diff --git a/vendor/github.com/go-playground/universal-translator/LICENSE b/vendor/github.com/go-playground/universal-translator/LICENSE new file mode 100644 index 0000000..8d8aba1 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Go Playground + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/go-playground/universal-translator/README.md b/vendor/github.com/go-playground/universal-translator/README.md new file mode 100644 index 0000000..24aef15 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/README.md @@ -0,0 +1,90 @@ +## universal-translator + +![Project status](https://img.shields.io/badge/version-0.16.0-green.svg) +[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/universal-translator/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/universal-translator) +[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator) +[![GoDoc](https://godoc.org/github.com/go-playground/universal-translator?status.svg)](https://godoc.org/github.com/go-playground/universal-translator) +![License](https://img.shields.io/dub/l/vibe-d.svg) +[![Gitter](https://badges.gitter.im/go-playground/universal-translator.svg)](https://gitter.im/go-playground/universal-translator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +Universal Translator is an i18n Translator for Go/Golang using CLDR data + pluralization rules + +Why another i18n library? +-------------------------- +Because none of the plural rules seem to be correct out there, including the previous implementation of this package, +so I took it upon myself to create [locales](https://github.com/go-playground/locales) for everyone to use; this package +is a thin wrapper around [locales](https://github.com/go-playground/locales) in order to store and translate text for +use in your applications. + +Features +-------- +- [x] Rules generated from the [CLDR](http://cldr.unicode.org/index/downloads) data, v30.0.3 +- [x] Contains Cardinal, Ordinal and Range Plural Rules +- [x] Contains Month, Weekday and Timezone translations built in +- [x] Contains Date & Time formatting functions +- [x] Contains Number, Currency, Accounting and Percent formatting functions +- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere ) +- [x] Support loading translations from files +- [x] Exporting translations to file(s), mainly for getting them professionally translated +- [ ] Code Generation for translation files -> Go code.. i.e. after it has been professionally translated +- [ ] Tests for all languages, I need help with this, please see [here](https://github.com/go-playground/locales/issues/1) + +Installation +----------- + +Use go get + +```shell +go get github.com/go-playground/universal-translator +``` + +Usage & Documentation +------- + +Please see https://godoc.org/github.com/go-playground/universal-translator for usage docs + +##### Examples: + +- [Basic](https://github.com/go-playground/universal-translator/tree/master/examples/basic) +- [Full - no files](https://github.com/go-playground/universal-translator/tree/master/examples/full-no-files) +- [Full - with files](https://github.com/go-playground/universal-translator/tree/master/examples/full-with-files) + +File formatting +-------------- +All types, Plain substitution, Cardinal, Ordinal and Range translations can all be contained withing the same file(s); +they are only separated for easy viewing. + +##### Examples: + +- [Formats](https://github.com/go-playground/universal-translator/tree/master/examples/file-formats) + +##### Basic Makeup +NOTE: not all fields are needed for all translation types, see [examples](https://github.com/go-playground/universal-translator/tree/master/examples/file-formats) +```json +{ + "locale": "en", + "key": "days-left", + "trans": "You have {0} day left.", + "type": "Cardinal", + "rule": "One", + "override": false +} +``` +|Field|Description| +|---|---| +|locale|The locale for which the translation is for.| +|key|The translation key that will be used to store and lookup each translation; normally it is a string or integer.| +|trans|The actual translation text.| +|type|The type of translation Cardinal, Ordinal, Range or "" for a plain substitution(not required to be defined if plain used)| +|rule|The plural rule for which the translation is for eg. One, Two, Few, Many or Other.(not required to be defined if plain used)| +|override|If you wish to override an existing translation that has already been registered, set this to 'true'. 99% of the time there is no need to define it.| + +Help With Tests +--------------- +To anyone interesting in helping or contributing, I sure could use some help creating tests for each language. +Please see issue [here](https://github.com/go-playground/locales/issues/1) for details. + +License +------ +Distributed under MIT License, please see license file in code for more details. diff --git a/vendor/github.com/go-playground/universal-translator/errors.go b/vendor/github.com/go-playground/universal-translator/errors.go new file mode 100644 index 0000000..38b163b --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/errors.go @@ -0,0 +1,148 @@ +package ut + +import ( + "errors" + "fmt" + + "github.com/go-playground/locales" +) + +var ( + // ErrUnknowTranslation indicates the translation could not be found + ErrUnknowTranslation = errors.New("Unknown Translation") +) + +var _ error = new(ErrConflictingTranslation) +var _ error = new(ErrRangeTranslation) +var _ error = new(ErrOrdinalTranslation) +var _ error = new(ErrCardinalTranslation) +var _ error = new(ErrMissingPluralTranslation) +var _ error = new(ErrExistingTranslator) + +// ErrExistingTranslator is the error representing a conflicting translator +type ErrExistingTranslator struct { + locale string +} + +// Error returns ErrExistingTranslator's internal error text +func (e *ErrExistingTranslator) Error() string { + return fmt.Sprintf("error: conflicting translator for locale '%s'", e.locale) +} + +// ErrConflictingTranslation is the error representing a conflicting translation +type ErrConflictingTranslation struct { + locale string + key interface{} + rule locales.PluralRule + text string +} + +// Error returns ErrConflictingTranslation's internal error text +func (e *ErrConflictingTranslation) Error() string { + + if _, ok := e.key.(string); !ok { + return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale) + } + + return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale) +} + +// ErrRangeTranslation is the error representing a range translation error +type ErrRangeTranslation struct { + text string +} + +// Error returns ErrRangeTranslation's internal error text +func (e *ErrRangeTranslation) Error() string { + return e.text +} + +// ErrOrdinalTranslation is the error representing an ordinal translation error +type ErrOrdinalTranslation struct { + text string +} + +// Error returns ErrOrdinalTranslation's internal error text +func (e *ErrOrdinalTranslation) Error() string { + return e.text +} + +// ErrCardinalTranslation is the error representing a cardinal translation error +type ErrCardinalTranslation struct { + text string +} + +// Error returns ErrCardinalTranslation's internal error text +func (e *ErrCardinalTranslation) Error() string { + return e.text +} + +// ErrMissingPluralTranslation is the error signifying a missing translation given +// the locales plural rules. +type ErrMissingPluralTranslation struct { + locale string + key interface{} + rule locales.PluralRule + translationType string +} + +// Error returns ErrMissingPluralTranslation's internal error text +func (e *ErrMissingPluralTranslation) Error() string { + + if _, ok := e.key.(string); !ok { + return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v' and locale '%s'", e.translationType, e.rule, e.key, e.locale) + } + + return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s' and locale '%s'", e.translationType, e.rule, e.key, e.locale) +} + +// ErrMissingBracket is the error representing a missing bracket in a translation +// eg. This is a {0 <-- missing ending '}' +type ErrMissingBracket struct { + locale string + key interface{} + text string +} + +// Error returns ErrMissingBracket error message +func (e *ErrMissingBracket) Error() string { + return fmt.Sprintf("error: missing bracket '{}', in translation. locale: '%s' key: '%v' text: '%s'", e.locale, e.key, e.text) +} + +// ErrBadParamSyntax is the error representing a bad parameter definition in a translation +// eg. This is a {must-be-int} +type ErrBadParamSyntax struct { + locale string + param string + key interface{} + text string +} + +// Error returns ErrBadParamSyntax error message +func (e *ErrBadParamSyntax) Error() string { + return fmt.Sprintf("error: bad parameter syntax, missing parameter '%s' in translation. locale: '%s' key: '%v' text: '%s'", e.param, e.locale, e.key, e.text) +} + +// import/export errors + +// ErrMissingLocale is the error representing an expected locale that could +// not be found aka locale not registered with the UniversalTranslator Instance +type ErrMissingLocale struct { + locale string +} + +// Error returns ErrMissingLocale's internal error text +func (e *ErrMissingLocale) Error() string { + return fmt.Sprintf("error: locale '%s' not registered.", e.locale) +} + +// ErrBadPluralDefinition is the error representing an incorrect plural definition +// usually found within translations defined within files during the import process. +type ErrBadPluralDefinition struct { + tl translation +} + +// Error returns ErrBadPluralDefinition's internal error text +func (e *ErrBadPluralDefinition) Error() string { + return fmt.Sprintf("error: bad plural definition '%#v'", e.tl) +} diff --git a/vendor/github.com/go-playground/universal-translator/import_export.go b/vendor/github.com/go-playground/universal-translator/import_export.go new file mode 100644 index 0000000..7bd76f2 --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/import_export.go @@ -0,0 +1,274 @@ +package ut + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "io" + + "github.com/go-playground/locales" +) + +type translation struct { + Locale string `json:"locale"` + Key interface{} `json:"key"` // either string or integer + Translation string `json:"trans"` + PluralType string `json:"type,omitempty"` + PluralRule string `json:"rule,omitempty"` + OverrideExisting bool `json:"override,omitempty"` +} + +const ( + cardinalType = "Cardinal" + ordinalType = "Ordinal" + rangeType = "Range" +) + +// ImportExportFormat is the format of the file import or export +type ImportExportFormat uint8 + +// supported Export Formats +const ( + FormatJSON ImportExportFormat = iota +) + +// Export writes the translations out to a file on disk. +// +// NOTE: this currently only works with string or int translations keys. +func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error { + + _, err := os.Stat(dirname) + fmt.Println(dirname, err, os.IsNotExist(err)) + if err != nil { + + if !os.IsNotExist(err) { + return err + } + + if err = os.MkdirAll(dirname, 0744); err != nil { + return err + } + } + + // build up translations + var trans []translation + var b []byte + var ext string + + for _, locale := range t.translators { + + for k, v := range locale.(*translator).translations { + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k, + Translation: v.text, + }) + } + + for k, pluralTrans := range locale.(*translator).cardinalTanslations { + + for i, plural := range pluralTrans { + + // leave enough for all plural rules + // but not all are set for all languages. + if plural == nil { + continue + } + + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k.(string), + Translation: plural.text, + PluralType: cardinalType, + PluralRule: locales.PluralRule(i).String(), + }) + } + } + + for k, pluralTrans := range locale.(*translator).ordinalTanslations { + + for i, plural := range pluralTrans { + + // leave enough for all plural rules + // but not all are set for all languages. + if plural == nil { + continue + } + + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k.(string), + Translation: plural.text, + PluralType: ordinalType, + PluralRule: locales.PluralRule(i).String(), + }) + } + } + + for k, pluralTrans := range locale.(*translator).rangeTanslations { + + for i, plural := range pluralTrans { + + // leave enough for all plural rules + // but not all are set for all languages. + if plural == nil { + continue + } + + trans = append(trans, translation{ + Locale: locale.Locale(), + Key: k.(string), + Translation: plural.text, + PluralType: rangeType, + PluralRule: locales.PluralRule(i).String(), + }) + } + } + + switch format { + case FormatJSON: + b, err = json.MarshalIndent(trans, "", " ") + ext = ".json" + } + + if err != nil { + return err + } + + err = ioutil.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644) + if err != nil { + return err + } + + trans = trans[0:0] + } + + return nil +} + +// Import reads the translations out of a file or directory on disk. +// +// NOTE: this currently only works with string or int translations keys. +func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error { + + fi, err := os.Stat(dirnameOrFilename) + if err != nil { + return err + } + + processFn := func(filename string) error { + + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + + return t.ImportByReader(format, f) + } + + if !fi.IsDir() { + return processFn(dirnameOrFilename) + } + + // recursively go through directory + walker := func(path string, info os.FileInfo, err error) error { + + if info.IsDir() { + return nil + } + + switch format { + case FormatJSON: + // skip non JSON files + if filepath.Ext(info.Name()) != ".json" { + return nil + } + } + + return processFn(path) + } + + return filepath.Walk(dirnameOrFilename, walker) +} + +// ImportByReader imports the the translations found within the contents read from the supplied reader. +// +// NOTE: generally used when assets have been embedded into the binary and are already in memory. +func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error { + + b, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + + var trans []translation + + switch format { + case FormatJSON: + err = json.Unmarshal(b, &trans) + } + + if err != nil { + return err + } + + for _, tl := range trans { + + locale, found := t.FindTranslator(tl.Locale) + if !found { + return &ErrMissingLocale{locale: tl.Locale} + } + + pr := stringToPR(tl.PluralRule) + + if pr == locales.PluralRuleUnknown { + + err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting) + if err != nil { + return err + } + + continue + } + + switch tl.PluralType { + case cardinalType: + err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting) + case ordinalType: + err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting) + case rangeType: + err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting) + default: + return &ErrBadPluralDefinition{tl: tl} + } + + if err != nil { + return err + } + } + + return nil +} + +func stringToPR(s string) locales.PluralRule { + + switch s { + case "One": + return locales.PluralRuleOne + case "Two": + return locales.PluralRuleTwo + case "Few": + return locales.PluralRuleFew + case "Many": + return locales.PluralRuleMany + case "Other": + return locales.PluralRuleOther + default: + return locales.PluralRuleUnknown + } + +} diff --git a/vendor/github.com/go-playground/universal-translator/logo.png b/vendor/github.com/go-playground/universal-translator/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a37aa8c0cd0f6e1b98e0be3eb2531ebc6ac6717b GIT binary patch literal 16598 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 Z{{;$xH 0 && tarr[rule] != nil && !override { + return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} + } + + } else { + tarr = make([]*transText, 7, 7) + t.cardinalTanslations[key] = tarr + } + + trans := &transText{ + text: text, + indexes: make([]int, 2, 2), + } + + tarr[rule] = trans + + idx := strings.Index(text, paramZero) + if idx == -1 { + tarr[rule] = nil + return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} + } + + trans.indexes[0] = idx + trans.indexes[1] = idx + len(paramZero) + + return nil +} + +// AddOrdinal adds an ordinal plural translation for a particular language/locale +// {0} is the only replacement type accepted and only one variable is accepted as +// multiple cannot be used for a plural rule determination, unless it is a range; +// see AddRange below. +// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - 1st, 2nd, 3rd... +func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error { + + var verified bool + + // verify plural rule exists for locale + for _, pr := range t.PluralsOrdinal() { + if pr == rule { + verified = true + break + } + } + + if !verified { + return &ErrOrdinalTranslation{text: fmt.Sprintf("error: ordinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} + } + + tarr, ok := t.ordinalTanslations[key] + if ok { + // verify not adding a conflicting record + if len(tarr) > 0 && tarr[rule] != nil && !override { + return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} + } + + } else { + tarr = make([]*transText, 7, 7) + t.ordinalTanslations[key] = tarr + } + + trans := &transText{ + text: text, + indexes: make([]int, 2, 2), + } + + tarr[rule] = trans + + idx := strings.Index(text, paramZero) + if idx == -1 { + tarr[rule] = nil + return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} + } + + trans.indexes[0] = idx + trans.indexes[1] = idx + len(paramZero) + + return nil +} + +// AddRange adds a range plural translation for a particular language/locale +// {0} and {1} are the only replacement types accepted and only these are accepted. +// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left' +func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error { + + var verified bool + + // verify plural rule exists for locale + for _, pr := range t.PluralsRange() { + if pr == rule { + verified = true + break + } + } + + if !verified { + return &ErrRangeTranslation{text: fmt.Sprintf("error: range plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} + } + + tarr, ok := t.rangeTanslations[key] + if ok { + // verify not adding a conflicting record + if len(tarr) > 0 && tarr[rule] != nil && !override { + return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} + } + + } else { + tarr = make([]*transText, 7, 7) + t.rangeTanslations[key] = tarr + } + + trans := &transText{ + text: text, + indexes: make([]int, 4, 4), + } + + tarr[rule] = trans + + idx := strings.Index(text, paramZero) + if idx == -1 { + tarr[rule] = nil + return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation? locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} + } + + trans.indexes[0] = idx + trans.indexes[1] = idx + len(paramZero) + + idx = strings.Index(text, paramOne) + if idx == -1 { + tarr[rule] = nil + return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters. locale: '%s' key: '%v' text: '%s'", paramOne, t.Locale(), key, text)} + } + + trans.indexes[2] = idx + trans.indexes[3] = idx + len(paramOne) + + return nil +} + +// T creates the translation for the locale given the 'key' and params passed in +func (t *translator) T(key interface{}, params ...string) (string, error) { + + trans, ok := t.translations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + b := make([]byte, 0, 64) + + var start, end, count int + + for i := 0; i < len(trans.indexes); i++ { + end = trans.indexes[i] + b = append(b, trans.text[start:end]...) + b = append(b, params[count]...) + i++ + start = trans.indexes[i] + count++ + } + + b = append(b, trans.text[start:]...) + + return string(b), nil +} + +// C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in +func (t *translator) C(key interface{}, num float64, digits uint64, param string) (string, error) { + + tarr, ok := t.cardinalTanslations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + rule := t.CardinalPluralRule(num, digits) + + trans := tarr[rule] + + b := make([]byte, 0, 64) + b = append(b, trans.text[:trans.indexes[0]]...) + b = append(b, param...) + b = append(b, trans.text[trans.indexes[1]:]...) + + return string(b), nil +} + +// O creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in +func (t *translator) O(key interface{}, num float64, digits uint64, param string) (string, error) { + + tarr, ok := t.ordinalTanslations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + rule := t.OrdinalPluralRule(num, digits) + + trans := tarr[rule] + + b := make([]byte, 0, 64) + b = append(b, trans.text[:trans.indexes[0]]...) + b = append(b, param...) + b = append(b, trans.text[trans.indexes[1]:]...) + + return string(b), nil +} + +// R creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and 'digit2' arguments +// and 'param1' and 'param2' passed in +func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) { + + tarr, ok := t.rangeTanslations[key] + if !ok { + return unknownTranslation, ErrUnknowTranslation + } + + rule := t.RangePluralRule(num1, digits1, num2, digits2) + + trans := tarr[rule] + + b := make([]byte, 0, 64) + b = append(b, trans.text[:trans.indexes[0]]...) + b = append(b, param1...) + b = append(b, trans.text[trans.indexes[1]:trans.indexes[2]]...) + b = append(b, param2...) + b = append(b, trans.text[trans.indexes[3]:]...) + + return string(b), nil +} + +// VerifyTranslations checks to ensures that no plural rules have been +// missed within the translations. +func (t *translator) VerifyTranslations() error { + + for k, v := range t.cardinalTanslations { + + for _, rule := range t.PluralsCardinal() { + + if v[rule] == nil { + return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "plural", rule: rule, key: k} + } + } + } + + for k, v := range t.ordinalTanslations { + + for _, rule := range t.PluralsOrdinal() { + + if v[rule] == nil { + return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "ordinal", rule: rule, key: k} + } + } + } + + for k, v := range t.rangeTanslations { + + for _, rule := range t.PluralsRange() { + + if v[rule] == nil { + return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "range", rule: rule, key: k} + } + } + } + + return nil +} diff --git a/vendor/github.com/go-playground/universal-translator/universal_translator.go b/vendor/github.com/go-playground/universal-translator/universal_translator.go new file mode 100644 index 0000000..dbf707f --- /dev/null +++ b/vendor/github.com/go-playground/universal-translator/universal_translator.go @@ -0,0 +1,113 @@ +package ut + +import ( + "strings" + + "github.com/go-playground/locales" +) + +// UniversalTranslator holds all locale & translation data +type UniversalTranslator struct { + translators map[string]Translator + fallback Translator +} + +// New returns a new UniversalTranslator instance set with +// the fallback locale and locales it should support +func New(fallback locales.Translator, supportedLocales ...locales.Translator) *UniversalTranslator { + + t := &UniversalTranslator{ + translators: make(map[string]Translator), + } + + for _, v := range supportedLocales { + + trans := newTranslator(v) + t.translators[strings.ToLower(trans.Locale())] = trans + + if fallback.Locale() == v.Locale() { + t.fallback = trans + } + } + + if t.fallback == nil && fallback != nil { + t.fallback = newTranslator(fallback) + } + + return t +} + +// FindTranslator trys to find a Translator based on an array of locales +// and returns the first one it can find, otherwise returns the +// fallback translator. +func (t *UniversalTranslator) FindTranslator(locales ...string) (trans Translator, found bool) { + + for _, locale := range locales { + + if trans, found = t.translators[strings.ToLower(locale)]; found { + return + } + } + + return t.fallback, false +} + +// GetTranslator returns the specified translator for the given locale, +// or fallback if not found +func (t *UniversalTranslator) GetTranslator(locale string) (trans Translator, found bool) { + + if trans, found = t.translators[strings.ToLower(locale)]; found { + return + } + + return t.fallback, false +} + +// GetFallback returns the fallback locale +func (t *UniversalTranslator) GetFallback() Translator { + return t.fallback +} + +// AddTranslator adds the supplied translator, if it already exists the override param +// will be checked and if false an error will be returned, otherwise the translator will be +// overridden; if the fallback matches the supplied translator it will be overridden as well +// NOTE: this is normally only used when translator is embedded within a library +func (t *UniversalTranslator) AddTranslator(translator locales.Translator, override bool) error { + + lc := strings.ToLower(translator.Locale()) + _, ok := t.translators[lc] + if ok && !override { + return &ErrExistingTranslator{locale: translator.Locale()} + } + + trans := newTranslator(translator) + + if t.fallback.Locale() == translator.Locale() { + + // because it's optional to have a fallback, I don't impose that limitation + // don't know why you wouldn't but... + if !override { + return &ErrExistingTranslator{locale: translator.Locale()} + } + + t.fallback = trans + } + + t.translators[lc] = trans + + return nil +} + +// VerifyTranslations runs through all locales and identifies any issues +// eg. missing plural rules for a locale +func (t *UniversalTranslator) VerifyTranslations() (err error) { + + for _, trans := range t.translators { + err = trans.VerifyTranslations() + if err != nil { + return + } + } + + return +} diff --git a/vendor/github.com/spf13/pflag/.gitignore b/vendor/github.com/spf13/pflag/.gitignore new file mode 100644 index 0000000..c3da290 --- /dev/null +++ b/vendor/github.com/spf13/pflag/.gitignore @@ -0,0 +1,2 @@ +.idea/* + diff --git a/vendor/github.com/spf13/pflag/.travis.yml b/vendor/github.com/spf13/pflag/.travis.yml new file mode 100644 index 0000000..00d04cb --- /dev/null +++ b/vendor/github.com/spf13/pflag/.travis.yml @@ -0,0 +1,22 @@ +sudo: false + +language: go + +go: + - 1.9.x + - 1.10.x + - 1.11.x + - tip + +matrix: + allow_failures: + - go: tip + +install: + - go get golang.org/x/lint/golint + - export PATH=$GOPATH/bin:$PATH + - go install ./... + +script: + - verify/all.sh -v + - go test ./... diff --git a/vendor/github.com/spf13/pflag/LICENSE b/vendor/github.com/spf13/pflag/LICENSE new file mode 100644 index 0000000..63ed1cf --- /dev/null +++ b/vendor/github.com/spf13/pflag/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 Alex Ogier. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/spf13/pflag/README.md b/vendor/github.com/spf13/pflag/README.md new file mode 100644 index 0000000..7eacc5b --- /dev/null +++ b/vendor/github.com/spf13/pflag/README.md @@ -0,0 +1,296 @@ +[![Build Status](https://travis-ci.org/spf13/pflag.svg?branch=master)](https://travis-ci.org/spf13/pflag) +[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/pflag)](https://goreportcard.com/report/github.com/spf13/pflag) +[![GoDoc](https://godoc.org/github.com/spf13/pflag?status.svg)](https://godoc.org/github.com/spf13/pflag) + +## Description + +pflag is a drop-in replacement for Go's flag package, implementing +POSIX/GNU-style --flags. + +pflag is compatible with the [GNU extensions to the POSIX recommendations +for command-line options][1]. For a more precise description, see the +"Command-line flag syntax" section below. + +[1]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + +pflag is available under the same style of BSD license as the Go language, +which can be found in the LICENSE file. + +## Installation + +pflag is available using the standard `go get` command. + +Install by running: + + go get github.com/spf13/pflag + +Run tests by running: + + go test github.com/spf13/pflag + +## Usage + +pflag is a drop-in replacement of Go's native flag package. If you import +pflag under the name "flag" then all code should continue to function +with no changes. + +``` go +import flag "github.com/spf13/pflag" +``` + +There is one exception to this: if you directly instantiate the Flag struct +there is one more field "Shorthand" that you will need to set. +Most code never instantiates this struct directly, and instead uses +functions such as String(), BoolVar(), and Var(), and is therefore +unaffected. + +Define flags using flag.String(), Bool(), Int(), etc. + +This declares an integer flag, -flagname, stored in the pointer ip, with type *int. + +``` go +var ip *int = flag.Int("flagname", 1234, "help message for flagname") +``` + +If you like, you can bind the flag to a variable using the Var() functions. + +``` go +var flagvar int +func init() { + flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") +} +``` + +Or you can create custom flags that satisfy the Value interface (with +pointer receivers) and couple them to flag parsing by + +``` go +flag.Var(&flagVal, "name", "help message for flagname") +``` + +For such flags, the default value is just the initial value of the variable. + +After all flags are defined, call + +``` go +flag.Parse() +``` + +to parse the command line into the defined flags. + +Flags may then be used directly. If you're using the flags themselves, +they are all pointers; if you bind to variables, they're values. + +``` go +fmt.Println("ip has value ", *ip) +fmt.Println("flagvar has value ", flagvar) +``` + +There are helper functions available to get the value stored in a Flag if you have a FlagSet but find +it difficult to keep up with all of the pointers in your code. +If you have a pflag.FlagSet with a flag called 'flagname' of type int you +can use GetInt() to get the int value. But notice that 'flagname' must exist +and it must be an int. GetString("flagname") will fail. + +``` go +i, err := flagset.GetInt("flagname") +``` + +After parsing, the arguments after the flag are available as the +slice flag.Args() or individually as flag.Arg(i). +The arguments are indexed from 0 through flag.NArg()-1. + +The pflag package also defines some new functions that are not in flag, +that give one-letter shorthands for flags. You can use these by appending +'P' to the name of any function that defines a flag. + +``` go +var ip = flag.IntP("flagname", "f", 1234, "help message") +var flagvar bool +func init() { + flag.BoolVarP(&flagvar, "boolname", "b", true, "help message") +} +flag.VarP(&flagVal, "varname", "v", "help message") +``` + +Shorthand letters can be used with single dashes on the command line. +Boolean shorthand flags can be combined with other shorthand flags. + +The default set of command-line flags is controlled by +top-level functions. The FlagSet type allows one to define +independent sets of flags, such as to implement subcommands +in a command-line interface. The methods of FlagSet are +analogous to the top-level functions for the command-line +flag set. + +## Setting no option default values for flags + +After you create a flag it is possible to set the pflag.NoOptDefVal for +the given flag. Doing this changes the meaning of the flag slightly. If +a flag has a NoOptDefVal and the flag is set on the command line without +an option the flag will be set to the NoOptDefVal. For example given: + +``` go +var ip = flag.IntP("flagname", "f", 1234, "help message") +flag.Lookup("flagname").NoOptDefVal = "4321" +``` + +Would result in something like + +| Parsed Arguments | Resulting Value | +| ------------- | ------------- | +| --flagname=1357 | ip=1357 | +| --flagname | ip=4321 | +| [nothing] | ip=1234 | + +## Command line flag syntax + +``` +--flag // boolean flags, or flags with no option default values +--flag x // only on flags without a default value +--flag=x +``` + +Unlike the flag package, a single dash before an option means something +different than a double dash. Single dashes signify a series of shorthand +letters for flags. All but the last shorthand letter must be boolean flags +or a flag with a default value + +``` +// boolean or flags where the 'no option default value' is set +-f +-f=true +-abc +but +-b true is INVALID + +// non-boolean and flags without a 'no option default value' +-n 1234 +-n=1234 +-n1234 + +// mixed +-abcs "hello" +-absd="hello" +-abcs1234 +``` + +Flag parsing stops after the terminator "--". Unlike the flag package, +flags can be interspersed with arguments anywhere on the command line +before this terminator. + +Integer flags accept 1234, 0664, 0x1234 and may be negative. +Boolean flags (in their long form) accept 1, 0, t, f, true, false, +TRUE, FALSE, True, False. +Duration flags accept any input valid for time.ParseDuration. + +## Mutating or "Normalizing" Flag names + +It is possible to set a custom flag name 'normalization function.' It allows flag names to be mutated both when created in the code and when used on the command line to some 'normalized' form. The 'normalized' form is used for comparison. Two examples of using the custom normalization func follow. + +**Example #1**: You want -, _, and . in flags to compare the same. aka --my-flag == --my_flag == --my.flag + +``` go +func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { + from := []string{"-", "_"} + to := "." + for _, sep := range from { + name = strings.Replace(name, sep, to, -1) + } + return pflag.NormalizedName(name) +} + +myFlagSet.SetNormalizeFunc(wordSepNormalizeFunc) +``` + +**Example #2**: You want to alias two flags. aka --old-flag-name == --new-flag-name + +``` go +func aliasNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { + switch name { + case "old-flag-name": + name = "new-flag-name" + break + } + return pflag.NormalizedName(name) +} + +myFlagSet.SetNormalizeFunc(aliasNormalizeFunc) +``` + +## Deprecating a flag or its shorthand +It is possible to deprecate a flag, or just its shorthand. Deprecating a flag/shorthand hides it from help text and prints a usage message when the deprecated flag/shorthand is used. + +**Example #1**: You want to deprecate a flag named "badflag" as well as inform the users what flag they should use instead. +```go +// deprecate a flag by specifying its name and a usage message +flags.MarkDeprecated("badflag", "please use --good-flag instead") +``` +This hides "badflag" from help text, and prints `Flag --badflag has been deprecated, please use --good-flag instead` when "badflag" is used. + +**Example #2**: You want to keep a flag name "noshorthandflag" but deprecate its shortname "n". +```go +// deprecate a flag shorthand by specifying its flag name and a usage message +flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only") +``` +This hides the shortname "n" from help text, and prints `Flag shorthand -n has been deprecated, please use --noshorthandflag only` when the shorthand "n" is used. + +Note that usage message is essential here, and it should not be empty. + +## Hidden flags +It is possible to mark a flag as hidden, meaning it will still function as normal, however will not show up in usage/help text. + +**Example**: You have a flag named "secretFlag" that you need for internal use only and don't want it showing up in help text, or for its usage text to be available. +```go +// hide a flag by specifying its name +flags.MarkHidden("secretFlag") +``` + +## Disable sorting of flags +`pflag` allows you to disable sorting of flags for help and usage message. + +**Example**: +```go +flags.BoolP("verbose", "v", false, "verbose output") +flags.String("coolflag", "yeaah", "it's really cool flag") +flags.Int("usefulflag", 777, "sometimes it's very useful") +flags.SortFlags = false +flags.PrintDefaults() +``` +**Output**: +``` + -v, --verbose verbose output + --coolflag string it's really cool flag (default "yeaah") + --usefulflag int sometimes it's very useful (default 777) +``` + + +## Supporting Go flags when using pflag +In order to support flags defined using Go's `flag` package, they must be added to the `pflag` flagset. This is usually necessary +to support flags defined by third-party dependencies (e.g. `golang/glog`). + +**Example**: You want to add the Go flags to the `CommandLine` flagset +```go +import ( + goflag "flag" + flag "github.com/spf13/pflag" +) + +var ip *int = flag.Int("flagname", 1234, "help message for flagname") + +func main() { + flag.CommandLine.AddGoFlagSet(goflag.CommandLine) + flag.Parse() +} +``` + +## More info + +You can see the full reference documentation of the pflag package +[at godoc.org][3], or through go's standard documentation system by +running `godoc -http=:6060` and browsing to +[http://localhost:6060/pkg/github.com/spf13/pflag][2] after +installation. + +[2]: http://localhost:6060/pkg/github.com/spf13/pflag +[3]: http://godoc.org/github.com/spf13/pflag diff --git a/vendor/github.com/spf13/pflag/bool.go b/vendor/github.com/spf13/pflag/bool.go new file mode 100644 index 0000000..c4c5c0b --- /dev/null +++ b/vendor/github.com/spf13/pflag/bool.go @@ -0,0 +1,94 @@ +package pflag + +import "strconv" + +// optional interface to indicate boolean flags that can be +// supplied without "=value" text +type boolFlag interface { + Value + IsBoolFlag() bool +} + +// -- bool Value +type boolValue bool + +func newBoolValue(val bool, p *bool) *boolValue { + *p = val + return (*boolValue)(p) +} + +func (b *boolValue) Set(s string) error { + v, err := strconv.ParseBool(s) + *b = boolValue(v) + return err +} + +func (b *boolValue) Type() string { + return "bool" +} + +func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) } + +func (b *boolValue) IsBoolFlag() bool { return true } + +func boolConv(sval string) (interface{}, error) { + return strconv.ParseBool(sval) +} + +// GetBool return the bool value of a flag with the given name +func (f *FlagSet) GetBool(name string) (bool, error) { + val, err := f.getFlagType(name, "bool", boolConv) + if err != nil { + return false, err + } + return val.(bool), nil +} + +// BoolVar defines a bool flag with specified name, default value, and usage string. +// The argument p points to a bool variable in which to store the value of the flag. +func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { + f.BoolVarP(p, name, "", value, usage) +} + +// BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BoolVarP(p *bool, name, shorthand string, value bool, usage string) { + flag := f.VarPF(newBoolValue(value, p), name, shorthand, usage) + flag.NoOptDefVal = "true" +} + +// BoolVar defines a bool flag with specified name, default value, and usage string. +// The argument p points to a bool variable in which to store the value of the flag. +func BoolVar(p *bool, name string, value bool, usage string) { + BoolVarP(p, name, "", value, usage) +} + +// BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash. +func BoolVarP(p *bool, name, shorthand string, value bool, usage string) { + flag := CommandLine.VarPF(newBoolValue(value, p), name, shorthand, usage) + flag.NoOptDefVal = "true" +} + +// Bool defines a bool flag with specified name, default value, and usage string. +// The return value is the address of a bool variable that stores the value of the flag. +func (f *FlagSet) Bool(name string, value bool, usage string) *bool { + return f.BoolP(name, "", value, usage) +} + +// BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BoolP(name, shorthand string, value bool, usage string) *bool { + p := new(bool) + f.BoolVarP(p, name, shorthand, value, usage) + return p +} + +// Bool defines a bool flag with specified name, default value, and usage string. +// The return value is the address of a bool variable that stores the value of the flag. +func Bool(name string, value bool, usage string) *bool { + return BoolP(name, "", value, usage) +} + +// BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash. +func BoolP(name, shorthand string, value bool, usage string) *bool { + b := CommandLine.BoolP(name, shorthand, value, usage) + return b +} diff --git a/vendor/github.com/spf13/pflag/bool_slice.go b/vendor/github.com/spf13/pflag/bool_slice.go new file mode 100644 index 0000000..3731370 --- /dev/null +++ b/vendor/github.com/spf13/pflag/bool_slice.go @@ -0,0 +1,185 @@ +package pflag + +import ( + "io" + "strconv" + "strings" +) + +// -- boolSlice Value +type boolSliceValue struct { + value *[]bool + changed bool +} + +func newBoolSliceValue(val []bool, p *[]bool) *boolSliceValue { + bsv := new(boolSliceValue) + bsv.value = p + *bsv.value = val + return bsv +} + +// Set converts, and assigns, the comma-separated boolean argument string representation as the []bool value of this flag. +// If Set is called on a flag that already has a []bool assigned, the newly converted values will be appended. +func (s *boolSliceValue) Set(val string) error { + + // remove all quote characters + rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") + + // read flag arguments with CSV parser + boolStrSlice, err := readAsCSV(rmQuote.Replace(val)) + if err != nil && err != io.EOF { + return err + } + + // parse boolean values into slice + out := make([]bool, 0, len(boolStrSlice)) + for _, boolStr := range boolStrSlice { + b, err := strconv.ParseBool(strings.TrimSpace(boolStr)) + if err != nil { + return err + } + out = append(out, b) + } + + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + + s.changed = true + + return nil +} + +// Type returns a string that uniquely represents this flag's type. +func (s *boolSliceValue) Type() string { + return "boolSlice" +} + +// String defines a "native" format for this boolean slice flag value. +func (s *boolSliceValue) String() string { + + boolStrSlice := make([]string, len(*s.value)) + for i, b := range *s.value { + boolStrSlice[i] = strconv.FormatBool(b) + } + + out, _ := writeAsCSV(boolStrSlice) + + return "[" + out + "]" +} + +func (s *boolSliceValue) fromString(val string) (bool, error) { + return strconv.ParseBool(val) +} + +func (s *boolSliceValue) toString(val bool) string { + return strconv.FormatBool(val) +} + +func (s *boolSliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *boolSliceValue) Replace(val []string) error { + out := make([]bool, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *boolSliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func boolSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []bool{}, nil + } + ss := strings.Split(val, ",") + out := make([]bool, len(ss)) + for i, t := range ss { + var err error + out[i], err = strconv.ParseBool(t) + if err != nil { + return nil, err + } + } + return out, nil +} + +// GetBoolSlice returns the []bool value of a flag with the given name. +func (f *FlagSet) GetBoolSlice(name string) ([]bool, error) { + val, err := f.getFlagType(name, "boolSlice", boolSliceConv) + if err != nil { + return []bool{}, err + } + return val.([]bool), nil +} + +// BoolSliceVar defines a boolSlice flag with specified name, default value, and usage string. +// The argument p points to a []bool variable in which to store the value of the flag. +func (f *FlagSet) BoolSliceVar(p *[]bool, name string, value []bool, usage string) { + f.VarP(newBoolSliceValue(value, p), name, "", usage) +} + +// BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) { + f.VarP(newBoolSliceValue(value, p), name, shorthand, usage) +} + +// BoolSliceVar defines a []bool flag with specified name, default value, and usage string. +// The argument p points to a []bool variable in which to store the value of the flag. +func BoolSliceVar(p *[]bool, name string, value []bool, usage string) { + CommandLine.VarP(newBoolSliceValue(value, p), name, "", usage) +} + +// BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash. +func BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) { + CommandLine.VarP(newBoolSliceValue(value, p), name, shorthand, usage) +} + +// BoolSlice defines a []bool flag with specified name, default value, and usage string. +// The return value is the address of a []bool variable that stores the value of the flag. +func (f *FlagSet) BoolSlice(name string, value []bool, usage string) *[]bool { + p := []bool{} + f.BoolSliceVarP(&p, name, "", value, usage) + return &p +} + +// BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool { + p := []bool{} + f.BoolSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// BoolSlice defines a []bool flag with specified name, default value, and usage string. +// The return value is the address of a []bool variable that stores the value of the flag. +func BoolSlice(name string, value []bool, usage string) *[]bool { + return CommandLine.BoolSliceP(name, "", value, usage) +} + +// BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash. +func BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool { + return CommandLine.BoolSliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/bytes.go b/vendor/github.com/spf13/pflag/bytes.go new file mode 100644 index 0000000..67d5304 --- /dev/null +++ b/vendor/github.com/spf13/pflag/bytes.go @@ -0,0 +1,209 @@ +package pflag + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + "strings" +) + +// BytesHex adapts []byte for use as a flag. Value of flag is HEX encoded +type bytesHexValue []byte + +// String implements pflag.Value.String. +func (bytesHex bytesHexValue) String() string { + return fmt.Sprintf("%X", []byte(bytesHex)) +} + +// Set implements pflag.Value.Set. +func (bytesHex *bytesHexValue) Set(value string) error { + bin, err := hex.DecodeString(strings.TrimSpace(value)) + + if err != nil { + return err + } + + *bytesHex = bin + + return nil +} + +// Type implements pflag.Value.Type. +func (*bytesHexValue) Type() string { + return "bytesHex" +} + +func newBytesHexValue(val []byte, p *[]byte) *bytesHexValue { + *p = val + return (*bytesHexValue)(p) +} + +func bytesHexConv(sval string) (interface{}, error) { + + bin, err := hex.DecodeString(sval) + + if err == nil { + return bin, nil + } + + return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) +} + +// GetBytesHex return the []byte value of a flag with the given name +func (f *FlagSet) GetBytesHex(name string) ([]byte, error) { + val, err := f.getFlagType(name, "bytesHex", bytesHexConv) + + if err != nil { + return []byte{}, err + } + + return val.([]byte), nil +} + +// BytesHexVar defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func (f *FlagSet) BytesHexVar(p *[]byte, name string, value []byte, usage string) { + f.VarP(newBytesHexValue(value, p), name, "", usage) +} + +// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { + f.VarP(newBytesHexValue(value, p), name, shorthand, usage) +} + +// BytesHexVar defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func BytesHexVar(p *[]byte, name string, value []byte, usage string) { + CommandLine.VarP(newBytesHexValue(value, p), name, "", usage) +} + +// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. +func BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { + CommandLine.VarP(newBytesHexValue(value, p), name, shorthand, usage) +} + +// BytesHex defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func (f *FlagSet) BytesHex(name string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesHexVarP(p, name, "", value, usage) + return p +} + +// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesHexVarP(p, name, shorthand, value, usage) + return p +} + +// BytesHex defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func BytesHex(name string, value []byte, usage string) *[]byte { + return CommandLine.BytesHexP(name, "", value, usage) +} + +// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. +func BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { + return CommandLine.BytesHexP(name, shorthand, value, usage) +} + +// BytesBase64 adapts []byte for use as a flag. Value of flag is Base64 encoded +type bytesBase64Value []byte + +// String implements pflag.Value.String. +func (bytesBase64 bytesBase64Value) String() string { + return base64.StdEncoding.EncodeToString([]byte(bytesBase64)) +} + +// Set implements pflag.Value.Set. +func (bytesBase64 *bytesBase64Value) Set(value string) error { + bin, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value)) + + if err != nil { + return err + } + + *bytesBase64 = bin + + return nil +} + +// Type implements pflag.Value.Type. +func (*bytesBase64Value) Type() string { + return "bytesBase64" +} + +func newBytesBase64Value(val []byte, p *[]byte) *bytesBase64Value { + *p = val + return (*bytesBase64Value)(p) +} + +func bytesBase64ValueConv(sval string) (interface{}, error) { + + bin, err := base64.StdEncoding.DecodeString(sval) + if err == nil { + return bin, nil + } + + return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) +} + +// GetBytesBase64 return the []byte value of a flag with the given name +func (f *FlagSet) GetBytesBase64(name string) ([]byte, error) { + val, err := f.getFlagType(name, "bytesBase64", bytesBase64ValueConv) + + if err != nil { + return []byte{}, err + } + + return val.([]byte), nil +} + +// BytesBase64Var defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func (f *FlagSet) BytesBase64Var(p *[]byte, name string, value []byte, usage string) { + f.VarP(newBytesBase64Value(value, p), name, "", usage) +} + +// BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) { + f.VarP(newBytesBase64Value(value, p), name, shorthand, usage) +} + +// BytesBase64Var defines an []byte flag with specified name, default value, and usage string. +// The argument p points to an []byte variable in which to store the value of the flag. +func BytesBase64Var(p *[]byte, name string, value []byte, usage string) { + CommandLine.VarP(newBytesBase64Value(value, p), name, "", usage) +} + +// BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash. +func BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) { + CommandLine.VarP(newBytesBase64Value(value, p), name, shorthand, usage) +} + +// BytesBase64 defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func (f *FlagSet) BytesBase64(name string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesBase64VarP(p, name, "", value, usage) + return p +} + +// BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte { + p := new([]byte) + f.BytesBase64VarP(p, name, shorthand, value, usage) + return p +} + +// BytesBase64 defines an []byte flag with specified name, default value, and usage string. +// The return value is the address of an []byte variable that stores the value of the flag. +func BytesBase64(name string, value []byte, usage string) *[]byte { + return CommandLine.BytesBase64P(name, "", value, usage) +} + +// BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash. +func BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte { + return CommandLine.BytesBase64P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/count.go b/vendor/github.com/spf13/pflag/count.go new file mode 100644 index 0000000..a0b2679 --- /dev/null +++ b/vendor/github.com/spf13/pflag/count.go @@ -0,0 +1,96 @@ +package pflag + +import "strconv" + +// -- count Value +type countValue int + +func newCountValue(val int, p *int) *countValue { + *p = val + return (*countValue)(p) +} + +func (i *countValue) Set(s string) error { + // "+1" means that no specific value was passed, so increment + if s == "+1" { + *i = countValue(*i + 1) + return nil + } + v, err := strconv.ParseInt(s, 0, 0) + *i = countValue(v) + return err +} + +func (i *countValue) Type() string { + return "count" +} + +func (i *countValue) String() string { return strconv.Itoa(int(*i)) } + +func countConv(sval string) (interface{}, error) { + i, err := strconv.Atoi(sval) + if err != nil { + return nil, err + } + return i, nil +} + +// GetCount return the int value of a flag with the given name +func (f *FlagSet) GetCount(name string) (int, error) { + val, err := f.getFlagType(name, "count", countConv) + if err != nil { + return 0, err + } + return val.(int), nil +} + +// CountVar defines a count flag with specified name, default value, and usage string. +// The argument p points to an int variable in which to store the value of the flag. +// A count flag will add 1 to its value every time it is found on the command line +func (f *FlagSet) CountVar(p *int, name string, usage string) { + f.CountVarP(p, name, "", usage) +} + +// CountVarP is like CountVar only take a shorthand for the flag name. +func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) { + flag := f.VarPF(newCountValue(0, p), name, shorthand, usage) + flag.NoOptDefVal = "+1" +} + +// CountVar like CountVar only the flag is placed on the CommandLine instead of a given flag set +func CountVar(p *int, name string, usage string) { + CommandLine.CountVar(p, name, usage) +} + +// CountVarP is like CountVar only take a shorthand for the flag name. +func CountVarP(p *int, name, shorthand string, usage string) { + CommandLine.CountVarP(p, name, shorthand, usage) +} + +// Count defines a count flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +// A count flag will add 1 to its value every time it is found on the command line +func (f *FlagSet) Count(name string, usage string) *int { + p := new(int) + f.CountVarP(p, name, "", usage) + return p +} + +// CountP is like Count only takes a shorthand for the flag name. +func (f *FlagSet) CountP(name, shorthand string, usage string) *int { + p := new(int) + f.CountVarP(p, name, shorthand, usage) + return p +} + +// Count defines a count flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +// A count flag will add 1 to its value evey time it is found on the command line +func Count(name string, usage string) *int { + return CommandLine.CountP(name, "", usage) +} + +// CountP is like Count only takes a shorthand for the flag name. +func CountP(name, shorthand string, usage string) *int { + return CommandLine.CountP(name, shorthand, usage) +} diff --git a/vendor/github.com/spf13/pflag/duration.go b/vendor/github.com/spf13/pflag/duration.go new file mode 100644 index 0000000..e9debef --- /dev/null +++ b/vendor/github.com/spf13/pflag/duration.go @@ -0,0 +1,86 @@ +package pflag + +import ( + "time" +) + +// -- time.Duration Value +type durationValue time.Duration + +func newDurationValue(val time.Duration, p *time.Duration) *durationValue { + *p = val + return (*durationValue)(p) +} + +func (d *durationValue) Set(s string) error { + v, err := time.ParseDuration(s) + *d = durationValue(v) + return err +} + +func (d *durationValue) Type() string { + return "duration" +} + +func (d *durationValue) String() string { return (*time.Duration)(d).String() } + +func durationConv(sval string) (interface{}, error) { + return time.ParseDuration(sval) +} + +// GetDuration return the duration value of a flag with the given name +func (f *FlagSet) GetDuration(name string) (time.Duration, error) { + val, err := f.getFlagType(name, "duration", durationConv) + if err != nil { + return 0, err + } + return val.(time.Duration), nil +} + +// DurationVar defines a time.Duration flag with specified name, default value, and usage string. +// The argument p points to a time.Duration variable in which to store the value of the flag. +func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { + f.VarP(newDurationValue(value, p), name, "", usage) +} + +// DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) { + f.VarP(newDurationValue(value, p), name, shorthand, usage) +} + +// DurationVar defines a time.Duration flag with specified name, default value, and usage string. +// The argument p points to a time.Duration variable in which to store the value of the flag. +func DurationVar(p *time.Duration, name string, value time.Duration, usage string) { + CommandLine.VarP(newDurationValue(value, p), name, "", usage) +} + +// DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash. +func DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) { + CommandLine.VarP(newDurationValue(value, p), name, shorthand, usage) +} + +// Duration defines a time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a time.Duration variable that stores the value of the flag. +func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration { + p := new(time.Duration) + f.DurationVarP(p, name, "", value, usage) + return p +} + +// DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration { + p := new(time.Duration) + f.DurationVarP(p, name, shorthand, value, usage) + return p +} + +// Duration defines a time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a time.Duration variable that stores the value of the flag. +func Duration(name string, value time.Duration, usage string) *time.Duration { + return CommandLine.DurationP(name, "", value, usage) +} + +// DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash. +func DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration { + return CommandLine.DurationP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/duration_slice.go b/vendor/github.com/spf13/pflag/duration_slice.go new file mode 100644 index 0000000..badadda --- /dev/null +++ b/vendor/github.com/spf13/pflag/duration_slice.go @@ -0,0 +1,166 @@ +package pflag + +import ( + "fmt" + "strings" + "time" +) + +// -- durationSlice Value +type durationSliceValue struct { + value *[]time.Duration + changed bool +} + +func newDurationSliceValue(val []time.Duration, p *[]time.Duration) *durationSliceValue { + dsv := new(durationSliceValue) + dsv.value = p + *dsv.value = val + return dsv +} + +func (s *durationSliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]time.Duration, len(ss)) + for i, d := range ss { + var err error + out[i], err = time.ParseDuration(d) + if err != nil { + return err + } + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *durationSliceValue) Type() string { + return "durationSlice" +} + +func (s *durationSliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%s", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func (s *durationSliceValue) fromString(val string) (time.Duration, error) { + return time.ParseDuration(val) +} + +func (s *durationSliceValue) toString(val time.Duration) string { + return fmt.Sprintf("%s", val) +} + +func (s *durationSliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *durationSliceValue) Replace(val []string) error { + out := make([]time.Duration, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *durationSliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func durationSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []time.Duration{}, nil + } + ss := strings.Split(val, ",") + out := make([]time.Duration, len(ss)) + for i, d := range ss { + var err error + out[i], err = time.ParseDuration(d) + if err != nil { + return nil, err + } + + } + return out, nil +} + +// GetDurationSlice returns the []time.Duration value of a flag with the given name +func (f *FlagSet) GetDurationSlice(name string) ([]time.Duration, error) { + val, err := f.getFlagType(name, "durationSlice", durationSliceConv) + if err != nil { + return []time.Duration{}, err + } + return val.([]time.Duration), nil +} + +// DurationSliceVar defines a durationSlice flag with specified name, default value, and usage string. +// The argument p points to a []time.Duration variable in which to store the value of the flag. +func (f *FlagSet) DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { + f.VarP(newDurationSliceValue(value, p), name, "", usage) +} + +// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { + f.VarP(newDurationSliceValue(value, p), name, shorthand, usage) +} + +// DurationSliceVar defines a duration[] flag with specified name, default value, and usage string. +// The argument p points to a duration[] variable in which to store the value of the flag. +func DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { + CommandLine.VarP(newDurationSliceValue(value, p), name, "", usage) +} + +// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. +func DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { + CommandLine.VarP(newDurationSliceValue(value, p), name, shorthand, usage) +} + +// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a []time.Duration variable that stores the value of the flag. +func (f *FlagSet) DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { + p := []time.Duration{} + f.DurationSliceVarP(&p, name, "", value, usage) + return &p +} + +// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { + p := []time.Duration{} + f.DurationSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a []time.Duration variable that stores the value of the flag. +func DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { + return CommandLine.DurationSliceP(name, "", value, usage) +} + +// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. +func DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { + return CommandLine.DurationSliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/flag.go b/vendor/github.com/spf13/pflag/flag.go new file mode 100644 index 0000000..24a5036 --- /dev/null +++ b/vendor/github.com/spf13/pflag/flag.go @@ -0,0 +1,1239 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package pflag is a drop-in replacement for Go's flag package, implementing +POSIX/GNU-style --flags. + +pflag is compatible with the GNU extensions to the POSIX recommendations +for command-line options. See +http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + +Usage: + +pflag is a drop-in replacement of Go's native flag package. If you import +pflag under the name "flag" then all code should continue to function +with no changes. + + import flag "github.com/spf13/pflag" + +There is one exception to this: if you directly instantiate the Flag struct +there is one more field "Shorthand" that you will need to set. +Most code never instantiates this struct directly, and instead uses +functions such as String(), BoolVar(), and Var(), and is therefore +unaffected. + +Define flags using flag.String(), Bool(), Int(), etc. + +This declares an integer flag, -flagname, stored in the pointer ip, with type *int. + var ip = flag.Int("flagname", 1234, "help message for flagname") +If you like, you can bind the flag to a variable using the Var() functions. + var flagvar int + func init() { + flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") + } +Or you can create custom flags that satisfy the Value interface (with +pointer receivers) and couple them to flag parsing by + flag.Var(&flagVal, "name", "help message for flagname") +For such flags, the default value is just the initial value of the variable. + +After all flags are defined, call + flag.Parse() +to parse the command line into the defined flags. + +Flags may then be used directly. If you're using the flags themselves, +they are all pointers; if you bind to variables, they're values. + fmt.Println("ip has value ", *ip) + fmt.Println("flagvar has value ", flagvar) + +After parsing, the arguments after the flag are available as the +slice flag.Args() or individually as flag.Arg(i). +The arguments are indexed from 0 through flag.NArg()-1. + +The pflag package also defines some new functions that are not in flag, +that give one-letter shorthands for flags. You can use these by appending +'P' to the name of any function that defines a flag. + var ip = flag.IntP("flagname", "f", 1234, "help message") + var flagvar bool + func init() { + flag.BoolVarP(&flagvar, "boolname", "b", true, "help message") + } + flag.VarP(&flagval, "varname", "v", "help message") +Shorthand letters can be used with single dashes on the command line. +Boolean shorthand flags can be combined with other shorthand flags. + +Command line flag syntax: + --flag // boolean flags only + --flag=x + +Unlike the flag package, a single dash before an option means something +different than a double dash. Single dashes signify a series of shorthand +letters for flags. All but the last shorthand letter must be boolean flags. + // boolean flags + -f + -abc + // non-boolean flags + -n 1234 + -Ifile + // mixed + -abcs "hello" + -abcn1234 + +Flag parsing stops after the terminator "--". Unlike the flag package, +flags can be interspersed with arguments anywhere on the command line +before this terminator. + +Integer flags accept 1234, 0664, 0x1234 and may be negative. +Boolean flags (in their long form) accept 1, 0, t, f, true, false, +TRUE, FALSE, True, False. +Duration flags accept any input valid for time.ParseDuration. + +The default set of command-line flags is controlled by +top-level functions. The FlagSet type allows one to define +independent sets of flags, such as to implement subcommands +in a command-line interface. The methods of FlagSet are +analogous to the top-level functions for the command-line +flag set. +*/ +package pflag + +import ( + "bytes" + "errors" + goflag "flag" + "fmt" + "io" + "os" + "sort" + "strings" +) + +// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. +var ErrHelp = errors.New("pflag: help requested") + +// ErrorHandling defines how to handle flag parsing errors. +type ErrorHandling int + +const ( + // ContinueOnError will return an err from Parse() if an error is found + ContinueOnError ErrorHandling = iota + // ExitOnError will call os.Exit(2) if an error is found when parsing + ExitOnError + // PanicOnError will panic() if an error is found when parsing flags + PanicOnError +) + +// ParseErrorsWhitelist defines the parsing errors that can be ignored +type ParseErrorsWhitelist struct { + // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags + UnknownFlags bool +} + +// NormalizedName is a flag name that has been normalized according to rules +// for the FlagSet (e.g. making '-' and '_' equivalent). +type NormalizedName string + +// A FlagSet represents a set of defined flags. +type FlagSet struct { + // Usage is the function called when an error occurs while parsing flags. + // The field is a function (not a method) that may be changed to point to + // a custom error handler. + Usage func() + + // SortFlags is used to indicate, if user wants to have sorted flags in + // help/usage messages. + SortFlags bool + + // ParseErrorsWhitelist is used to configure a whitelist of errors + ParseErrorsWhitelist ParseErrorsWhitelist + + name string + parsed bool + actual map[NormalizedName]*Flag + orderedActual []*Flag + sortedActual []*Flag + formal map[NormalizedName]*Flag + orderedFormal []*Flag + sortedFormal []*Flag + shorthands map[byte]*Flag + args []string // arguments after flags + argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no -- + errorHandling ErrorHandling + output io.Writer // nil means stderr; use out() accessor + interspersed bool // allow interspersed option/non-option args + normalizeNameFunc func(f *FlagSet, name string) NormalizedName + + addedGoFlagSets []*goflag.FlagSet +} + +// A Flag represents the state of a flag. +type Flag struct { + Name string // name as it appears on command line + Shorthand string // one-letter abbreviated flag + Usage string // help message + Value Value // value as set + DefValue string // default value (as text); for usage message + Changed bool // If the user set the value (or if left to default) + NoOptDefVal string // default value (as text); if the flag is on the command line without any options + Deprecated string // If this flag is deprecated, this string is the new or now thing to use + Hidden bool // used by cobra.Command to allow flags to be hidden from help/usage text + ShorthandDeprecated string // If the shorthand of this flag is deprecated, this string is the new or now thing to use + Annotations map[string][]string // used by cobra.Command bash autocomple code +} + +// Value is the interface to the dynamic value stored in a flag. +// (The default value is represented as a string.) +type Value interface { + String() string + Set(string) error + Type() string +} + +// SliceValue is a secondary interface to all flags which hold a list +// of values. This allows full control over the value of list flags, +// and avoids complicated marshalling and unmarshalling to csv. +type SliceValue interface { + // Append adds the specified value to the end of the flag value list. + Append(string) error + // Replace will fully overwrite any data currently in the flag value list. + Replace([]string) error + // GetSlice returns the flag value list as an array of strings. + GetSlice() []string +} + +// sortFlags returns the flags as a slice in lexicographical sorted order. +func sortFlags(flags map[NormalizedName]*Flag) []*Flag { + list := make(sort.StringSlice, len(flags)) + i := 0 + for k := range flags { + list[i] = string(k) + i++ + } + list.Sort() + result := make([]*Flag, len(list)) + for i, name := range list { + result[i] = flags[NormalizedName(name)] + } + return result +} + +// SetNormalizeFunc allows you to add a function which can translate flag names. +// Flags added to the FlagSet will be translated and then when anything tries to +// look up the flag that will also be translated. So it would be possible to create +// a flag named "getURL" and have it translated to "geturl". A user could then pass +// "--getUrl" which may also be translated to "geturl" and everything will work. +func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) { + f.normalizeNameFunc = n + f.sortedFormal = f.sortedFormal[:0] + for fname, flag := range f.formal { + nname := f.normalizeFlagName(flag.Name) + if fname == nname { + continue + } + flag.Name = string(nname) + delete(f.formal, fname) + f.formal[nname] = flag + if _, set := f.actual[fname]; set { + delete(f.actual, fname) + f.actual[nname] = flag + } + } +} + +// GetNormalizeFunc returns the previously set NormalizeFunc of a function which +// does no translation, if not set previously. +func (f *FlagSet) GetNormalizeFunc() func(f *FlagSet, name string) NormalizedName { + if f.normalizeNameFunc != nil { + return f.normalizeNameFunc + } + return func(f *FlagSet, name string) NormalizedName { return NormalizedName(name) } +} + +func (f *FlagSet) normalizeFlagName(name string) NormalizedName { + n := f.GetNormalizeFunc() + return n(f, name) +} + +func (f *FlagSet) out() io.Writer { + if f.output == nil { + return os.Stderr + } + return f.output +} + +// SetOutput sets the destination for usage and error messages. +// If output is nil, os.Stderr is used. +func (f *FlagSet) SetOutput(output io.Writer) { + f.output = output +} + +// VisitAll visits the flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. +// It visits all flags, even those not set. +func (f *FlagSet) VisitAll(fn func(*Flag)) { + if len(f.formal) == 0 { + return + } + + var flags []*Flag + if f.SortFlags { + if len(f.formal) != len(f.sortedFormal) { + f.sortedFormal = sortFlags(f.formal) + } + flags = f.sortedFormal + } else { + flags = f.orderedFormal + } + + for _, flag := range flags { + fn(flag) + } +} + +// HasFlags returns a bool to indicate if the FlagSet has any flags defined. +func (f *FlagSet) HasFlags() bool { + return len(f.formal) > 0 +} + +// HasAvailableFlags returns a bool to indicate if the FlagSet has any flags +// that are not hidden. +func (f *FlagSet) HasAvailableFlags() bool { + for _, flag := range f.formal { + if !flag.Hidden { + return true + } + } + return false +} + +// VisitAll visits the command-line flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. +// It visits all flags, even those not set. +func VisitAll(fn func(*Flag)) { + CommandLine.VisitAll(fn) +} + +// Visit visits the flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. +// It visits only those flags that have been set. +func (f *FlagSet) Visit(fn func(*Flag)) { + if len(f.actual) == 0 { + return + } + + var flags []*Flag + if f.SortFlags { + if len(f.actual) != len(f.sortedActual) { + f.sortedActual = sortFlags(f.actual) + } + flags = f.sortedActual + } else { + flags = f.orderedActual + } + + for _, flag := range flags { + fn(flag) + } +} + +// Visit visits the command-line flags in lexicographical order or +// in primordial order if f.SortFlags is false, calling fn for each. +// It visits only those flags that have been set. +func Visit(fn func(*Flag)) { + CommandLine.Visit(fn) +} + +// Lookup returns the Flag structure of the named flag, returning nil if none exists. +func (f *FlagSet) Lookup(name string) *Flag { + return f.lookup(f.normalizeFlagName(name)) +} + +// ShorthandLookup returns the Flag structure of the short handed flag, +// returning nil if none exists. +// It panics, if len(name) > 1. +func (f *FlagSet) ShorthandLookup(name string) *Flag { + if name == "" { + return nil + } + if len(name) > 1 { + msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name) + fmt.Fprintf(f.out(), msg) + panic(msg) + } + c := name[0] + return f.shorthands[c] +} + +// lookup returns the Flag structure of the named flag, returning nil if none exists. +func (f *FlagSet) lookup(name NormalizedName) *Flag { + return f.formal[name] +} + +// func to return a given type for a given flag name +func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) { + flag := f.Lookup(name) + if flag == nil { + err := fmt.Errorf("flag accessed but not defined: %s", name) + return nil, err + } + + if flag.Value.Type() != ftype { + err := fmt.Errorf("trying to get %s value of flag of type %s", ftype, flag.Value.Type()) + return nil, err + } + + sval := flag.Value.String() + result, err := convFunc(sval) + if err != nil { + return nil, err + } + return result, nil +} + +// ArgsLenAtDash will return the length of f.Args at the moment when a -- was +// found during arg parsing. This allows your program to know which args were +// before the -- and which came after. +func (f *FlagSet) ArgsLenAtDash() int { + return f.argsLenAtDash +} + +// MarkDeprecated indicated that a flag is deprecated in your program. It will +// continue to function but will not show up in help or usage messages. Using +// this flag will also print the given usageMessage. +func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { + flag := f.Lookup(name) + if flag == nil { + return fmt.Errorf("flag %q does not exist", name) + } + if usageMessage == "" { + return fmt.Errorf("deprecated message for flag %q must be set", name) + } + flag.Deprecated = usageMessage + flag.Hidden = true + return nil +} + +// MarkShorthandDeprecated will mark the shorthand of a flag deprecated in your +// program. It will continue to function but will not show up in help or usage +// messages. Using this flag will also print the given usageMessage. +func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error { + flag := f.Lookup(name) + if flag == nil { + return fmt.Errorf("flag %q does not exist", name) + } + if usageMessage == "" { + return fmt.Errorf("deprecated message for flag %q must be set", name) + } + flag.ShorthandDeprecated = usageMessage + return nil +} + +// MarkHidden sets a flag to 'hidden' in your program. It will continue to +// function but will not show up in help or usage messages. +func (f *FlagSet) MarkHidden(name string) error { + flag := f.Lookup(name) + if flag == nil { + return fmt.Errorf("flag %q does not exist", name) + } + flag.Hidden = true + return nil +} + +// Lookup returns the Flag structure of the named command-line flag, +// returning nil if none exists. +func Lookup(name string) *Flag { + return CommandLine.Lookup(name) +} + +// ShorthandLookup returns the Flag structure of the short handed flag, +// returning nil if none exists. +func ShorthandLookup(name string) *Flag { + return CommandLine.ShorthandLookup(name) +} + +// Set sets the value of the named flag. +func (f *FlagSet) Set(name, value string) error { + normalName := f.normalizeFlagName(name) + flag, ok := f.formal[normalName] + if !ok { + return fmt.Errorf("no such flag -%v", name) + } + + err := flag.Value.Set(value) + if err != nil { + var flagName string + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { + flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name) + } else { + flagName = fmt.Sprintf("--%s", flag.Name) + } + return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err) + } + + if !flag.Changed { + if f.actual == nil { + f.actual = make(map[NormalizedName]*Flag) + } + f.actual[normalName] = flag + f.orderedActual = append(f.orderedActual, flag) + + flag.Changed = true + } + + if flag.Deprecated != "" { + fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + } + return nil +} + +// SetAnnotation allows one to set arbitrary annotations on a flag in the FlagSet. +// This is sometimes used by spf13/cobra programs which want to generate additional +// bash completion information. +func (f *FlagSet) SetAnnotation(name, key string, values []string) error { + normalName := f.normalizeFlagName(name) + flag, ok := f.formal[normalName] + if !ok { + return fmt.Errorf("no such flag -%v", name) + } + if flag.Annotations == nil { + flag.Annotations = map[string][]string{} + } + flag.Annotations[key] = values + return nil +} + +// Changed returns true if the flag was explicitly set during Parse() and false +// otherwise +func (f *FlagSet) Changed(name string) bool { + flag := f.Lookup(name) + // If a flag doesn't exist, it wasn't changed.... + if flag == nil { + return false + } + return flag.Changed +} + +// Set sets the value of the named command-line flag. +func Set(name, value string) error { + return CommandLine.Set(name, value) +} + +// PrintDefaults prints, to standard error unless configured +// otherwise, the default values of all defined flags in the set. +func (f *FlagSet) PrintDefaults() { + usages := f.FlagUsages() + fmt.Fprint(f.out(), usages) +} + +// defaultIsZeroValue returns true if the default value for this flag represents +// a zero value. +func (f *Flag) defaultIsZeroValue() bool { + switch f.Value.(type) { + case boolFlag: + return f.DefValue == "false" + case *durationValue: + // Beginning in Go 1.7, duration zero values are "0s" + return f.DefValue == "0" || f.DefValue == "0s" + case *intValue, *int8Value, *int32Value, *int64Value, *uintValue, *uint8Value, *uint16Value, *uint32Value, *uint64Value, *countValue, *float32Value, *float64Value: + return f.DefValue == "0" + case *stringValue: + return f.DefValue == "" + case *ipValue, *ipMaskValue, *ipNetValue: + return f.DefValue == "" + case *intSliceValue, *stringSliceValue, *stringArrayValue: + return f.DefValue == "[]" + default: + switch f.Value.String() { + case "false": + return true + case "": + return true + case "": + return true + case "0": + return true + } + return false + } +} + +// UnquoteUsage extracts a back-quoted name from the usage +// string for a flag and returns it and the un-quoted usage. +// Given "a `name` to show" it returns ("name", "a name to show"). +// If there are no back quotes, the name is an educated guess of the +// type of the flag's value, or the empty string if the flag is boolean. +func UnquoteUsage(flag *Flag) (name string, usage string) { + // Look for a back-quoted name, but avoid the strings package. + usage = flag.Usage + for i := 0; i < len(usage); i++ { + if usage[i] == '`' { + for j := i + 1; j < len(usage); j++ { + if usage[j] == '`' { + name = usage[i+1 : j] + usage = usage[:i] + name + usage[j+1:] + return name, usage + } + } + break // Only one back quote; use type name. + } + } + + name = flag.Value.Type() + switch name { + case "bool": + name = "" + case "float64": + name = "float" + case "int64": + name = "int" + case "uint64": + name = "uint" + case "stringSlice": + name = "strings" + case "intSlice": + name = "ints" + case "uintSlice": + name = "uints" + case "boolSlice": + name = "bools" + } + + return +} + +// Splits the string `s` on whitespace into an initial substring up to +// `i` runes in length and the remainder. Will go `slop` over `i` if +// that encompasses the entire string (which allows the caller to +// avoid short orphan words on the final line). +func wrapN(i, slop int, s string) (string, string) { + if i+slop > len(s) { + return s, "" + } + + w := strings.LastIndexAny(s[:i], " \t\n") + if w <= 0 { + return s, "" + } + nlPos := strings.LastIndex(s[:i], "\n") + if nlPos > 0 && nlPos < w { + return s[:nlPos], s[nlPos+1:] + } + return s[:w], s[w+1:] +} + +// Wraps the string `s` to a maximum width `w` with leading indent +// `i`. The first line is not indented (this is assumed to be done by +// caller). Pass `w` == 0 to do no wrapping +func wrap(i, w int, s string) string { + if w == 0 { + return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) + } + + // space between indent i and end of line width w into which + // we should wrap the text. + wrap := w - i + + var r, l string + + // Not enough space for sensible wrapping. Wrap as a block on + // the next line instead. + if wrap < 24 { + i = 16 + wrap = w - i + r += "\n" + strings.Repeat(" ", i) + } + // If still not enough space then don't even try to wrap. + if wrap < 24 { + return strings.Replace(s, "\n", r, -1) + } + + // Try to avoid short orphan words on the final line, by + // allowing wrapN to go a bit over if that would fit in the + // remainder of the line. + slop := 5 + wrap = wrap - slop + + // Handle first line, which is indented by the caller (or the + // special case above) + l, s = wrapN(wrap, slop, s) + r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) + + // Now wrap the rest + for s != "" { + var t string + + t, s = wrapN(wrap, slop, s) + r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) + } + + return r + +} + +// FlagUsagesWrapped returns a string containing the usage information +// for all flags in the FlagSet. Wrapped to `cols` columns (0 for no +// wrapping) +func (f *FlagSet) FlagUsagesWrapped(cols int) string { + buf := new(bytes.Buffer) + + lines := make([]string, 0, len(f.formal)) + + maxlen := 0 + f.VisitAll(func(flag *Flag) { + if flag.Hidden { + return + } + + line := "" + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { + line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name) + } else { + line = fmt.Sprintf(" --%s", flag.Name) + } + + varname, usage := UnquoteUsage(flag) + if varname != "" { + line += " " + varname + } + if flag.NoOptDefVal != "" { + switch flag.Value.Type() { + case "string": + line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) + case "bool": + if flag.NoOptDefVal != "true" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + case "count": + if flag.NoOptDefVal != "+1" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + default: + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + } + + // This special character will be replaced with spacing once the + // correct alignment is calculated + line += "\x00" + if len(line) > maxlen { + maxlen = len(line) + } + + line += usage + if !flag.defaultIsZeroValue() { + if flag.Value.Type() == "string" { + line += fmt.Sprintf(" (default %q)", flag.DefValue) + } else { + line += fmt.Sprintf(" (default %s)", flag.DefValue) + } + } + if len(flag.Deprecated) != 0 { + line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated) + } + + lines = append(lines, line) + }) + + for _, line := range lines { + sidx := strings.Index(line, "\x00") + spacing := strings.Repeat(" ", maxlen-sidx) + // maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in maxlen-sidx + fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:])) + } + + return buf.String() +} + +// FlagUsages returns a string containing the usage information for all flags in +// the FlagSet +func (f *FlagSet) FlagUsages() string { + return f.FlagUsagesWrapped(0) +} + +// PrintDefaults prints to standard error the default values of all defined command-line flags. +func PrintDefaults() { + CommandLine.PrintDefaults() +} + +// defaultUsage is the default function to print a usage message. +func defaultUsage(f *FlagSet) { + fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + f.PrintDefaults() +} + +// NOTE: Usage is not just defaultUsage(CommandLine) +// because it serves (via godoc flag Usage) as the example +// for how to write your own usage function. + +// Usage prints to standard error a usage message documenting all defined command-line flags. +// The function is a variable that may be changed to point to a custom function. +// By default it prints a simple header and calls PrintDefaults; for details about the +// format of the output and how to control it, see the documentation for PrintDefaults. +var Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + PrintDefaults() +} + +// NFlag returns the number of flags that have been set. +func (f *FlagSet) NFlag() int { return len(f.actual) } + +// NFlag returns the number of command-line flags that have been set. +func NFlag() int { return len(CommandLine.actual) } + +// Arg returns the i'th argument. Arg(0) is the first remaining argument +// after flags have been processed. +func (f *FlagSet) Arg(i int) string { + if i < 0 || i >= len(f.args) { + return "" + } + return f.args[i] +} + +// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument +// after flags have been processed. +func Arg(i int) string { + return CommandLine.Arg(i) +} + +// NArg is the number of arguments remaining after flags have been processed. +func (f *FlagSet) NArg() int { return len(f.args) } + +// NArg is the number of arguments remaining after flags have been processed. +func NArg() int { return len(CommandLine.args) } + +// Args returns the non-flag arguments. +func (f *FlagSet) Args() []string { return f.args } + +// Args returns the non-flag command-line arguments. +func Args() []string { return CommandLine.args } + +// Var defines a flag with the specified name and usage string. The type and +// value of the flag are represented by the first argument, of type Value, which +// typically holds a user-defined implementation of Value. For instance, the +// caller could create a flag that turns a comma-separated string into a slice +// of strings by giving the slice the methods of Value; in particular, Set would +// decompose the comma-separated string into the slice. +func (f *FlagSet) Var(value Value, name string, usage string) { + f.VarP(value, name, "", usage) +} + +// VarPF is like VarP, but returns the flag created +func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag { + // Remember the default value as a string; it won't change. + flag := &Flag{ + Name: name, + Shorthand: shorthand, + Usage: usage, + Value: value, + DefValue: value.String(), + } + f.AddFlag(flag) + return flag +} + +// VarP is like Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) VarP(value Value, name, shorthand, usage string) { + f.VarPF(value, name, shorthand, usage) +} + +// AddFlag will add the flag to the FlagSet +func (f *FlagSet) AddFlag(flag *Flag) { + normalizedFlagName := f.normalizeFlagName(flag.Name) + + _, alreadyThere := f.formal[normalizedFlagName] + if alreadyThere { + msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) + fmt.Fprintln(f.out(), msg) + panic(msg) // Happens only if flags are declared with identical names + } + if f.formal == nil { + f.formal = make(map[NormalizedName]*Flag) + } + + flag.Name = string(normalizedFlagName) + f.formal[normalizedFlagName] = flag + f.orderedFormal = append(f.orderedFormal, flag) + + if flag.Shorthand == "" { + return + } + if len(flag.Shorthand) > 1 { + msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand) + fmt.Fprintf(f.out(), msg) + panic(msg) + } + if f.shorthands == nil { + f.shorthands = make(map[byte]*Flag) + } + c := flag.Shorthand[0] + used, alreadyThere := f.shorthands[c] + if alreadyThere { + msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name) + fmt.Fprintf(f.out(), msg) + panic(msg) + } + f.shorthands[c] = flag +} + +// AddFlagSet adds one FlagSet to another. If a flag is already present in f +// the flag from newSet will be ignored. +func (f *FlagSet) AddFlagSet(newSet *FlagSet) { + if newSet == nil { + return + } + newSet.VisitAll(func(flag *Flag) { + if f.Lookup(flag.Name) == nil { + f.AddFlag(flag) + } + }) +} + +// Var defines a flag with the specified name and usage string. The type and +// value of the flag are represented by the first argument, of type Value, which +// typically holds a user-defined implementation of Value. For instance, the +// caller could create a flag that turns a comma-separated string into a slice +// of strings by giving the slice the methods of Value; in particular, Set would +// decompose the comma-separated string into the slice. +func Var(value Value, name string, usage string) { + CommandLine.VarP(value, name, "", usage) +} + +// VarP is like Var, but accepts a shorthand letter that can be used after a single dash. +func VarP(value Value, name, shorthand, usage string) { + CommandLine.VarP(value, name, shorthand, usage) +} + +// failf prints to standard error a formatted error and usage message and +// returns the error. +func (f *FlagSet) failf(format string, a ...interface{}) error { + err := fmt.Errorf(format, a...) + if f.errorHandling != ContinueOnError { + fmt.Fprintln(f.out(), err) + f.usage() + } + return err +} + +// usage calls the Usage method for the flag set, or the usage function if +// the flag set is CommandLine. +func (f *FlagSet) usage() { + if f == CommandLine { + Usage() + } else if f.Usage == nil { + defaultUsage(f) + } else { + f.Usage() + } +} + +//--unknown (args will be empty) +//--unknown --next-flag ... (args will be --next-flag ...) +//--unknown arg ... (args will be arg ...) +func stripUnknownFlagValue(args []string) []string { + if len(args) == 0 { + //--unknown + return args + } + + first := args[0] + if len(first) > 0 && first[0] == '-' { + //--unknown --next-flag ... + return args + } + + //--unknown arg ... (args will be arg ...) + if len(args) > 1 { + return args[1:] + } + return nil +} + +func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) { + a = args + name := s[2:] + if len(name) == 0 || name[0] == '-' || name[0] == '=' { + err = f.failf("bad flag syntax: %s", s) + return + } + + split := strings.SplitN(name, "=", 2) + name = split[0] + flag, exists := f.formal[f.normalizeFlagName(name)] + + if !exists { + switch { + case name == "help": + f.usage() + return a, ErrHelp + case f.ParseErrorsWhitelist.UnknownFlags: + // --unknown=unknownval arg ... + // we do not want to lose arg in this case + if len(split) >= 2 { + return a, nil + } + + return stripUnknownFlagValue(a), nil + default: + err = f.failf("unknown flag: --%s", name) + return + } + } + + var value string + if len(split) == 2 { + // '--flag=arg' + value = split[1] + } else if flag.NoOptDefVal != "" { + // '--flag' (arg was optional) + value = flag.NoOptDefVal + } else if len(a) > 0 { + // '--flag arg' + value = a[0] + a = a[1:] + } else { + // '--flag' (arg was required) + err = f.failf("flag needs an argument: %s", s) + return + } + + err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } + return +} + +func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) { + outArgs = args + + if strings.HasPrefix(shorthands, "test.") { + return + } + + outShorts = shorthands[1:] + c := shorthands[0] + + flag, exists := f.shorthands[c] + if !exists { + switch { + case c == 'h': + f.usage() + err = ErrHelp + return + case f.ParseErrorsWhitelist.UnknownFlags: + // '-f=arg arg ...' + // we do not want to lose arg in this case + if len(shorthands) > 2 && shorthands[1] == '=' { + outShorts = "" + return + } + + outArgs = stripUnknownFlagValue(outArgs) + return + default: + err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) + return + } + } + + var value string + if len(shorthands) > 2 && shorthands[1] == '=' { + // '-f=arg' + value = shorthands[2:] + outShorts = "" + } else if flag.NoOptDefVal != "" { + // '-f' (arg was optional) + value = flag.NoOptDefVal + } else if len(shorthands) > 1 { + // '-farg' + value = shorthands[1:] + outShorts = "" + } else if len(args) > 0 { + // '-f arg' + value = args[0] + outArgs = args[1:] + } else { + // '-f' (arg was required) + err = f.failf("flag needs an argument: %q in -%s", c, shorthands) + return + } + + if flag.ShorthandDeprecated != "" { + fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) + } + + err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } + return +} + +func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []string, err error) { + a = args + shorthands := s[1:] + + // "shorthands" can be a series of shorthand letters of flags (e.g. "-vvv"). + for len(shorthands) > 0 { + shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn) + if err != nil { + return + } + } + + return +} + +func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) { + for len(args) > 0 { + s := args[0] + args = args[1:] + if len(s) == 0 || s[0] != '-' || len(s) == 1 { + if !f.interspersed { + f.args = append(f.args, s) + f.args = append(f.args, args...) + return nil + } + f.args = append(f.args, s) + continue + } + + if s[1] == '-' { + if len(s) == 2 { // "--" terminates the flags + f.argsLenAtDash = len(f.args) + f.args = append(f.args, args...) + break + } + args, err = f.parseLongArg(s, args, fn) + } else { + args, err = f.parseShortArg(s, args, fn) + } + if err != nil { + return + } + } + return +} + +// Parse parses flag definitions from the argument list, which should not +// include the command name. Must be called after all flags in the FlagSet +// are defined and before flags are accessed by the program. +// The return value will be ErrHelp if -help was set but not defined. +func (f *FlagSet) Parse(arguments []string) error { + if f.addedGoFlagSets != nil { + for _, goFlagSet := range f.addedGoFlagSets { + goFlagSet.Parse(nil) + } + } + f.parsed = true + + if len(arguments) < 0 { + return nil + } + + f.args = make([]string, 0, len(arguments)) + + set := func(flag *Flag, value string) error { + return f.Set(flag.Name, value) + } + + err := f.parseArgs(arguments, set) + if err != nil { + switch f.errorHandling { + case ContinueOnError: + return err + case ExitOnError: + fmt.Println(err) + os.Exit(2) + case PanicOnError: + panic(err) + } + } + return nil +} + +type parseFunc func(flag *Flag, value string) error + +// ParseAll parses flag definitions from the argument list, which should not +// include the command name. The arguments for fn are flag and value. Must be +// called after all flags in the FlagSet are defined and before flags are +// accessed by the program. The return value will be ErrHelp if -help was set +// but not defined. +func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) error) error { + f.parsed = true + f.args = make([]string, 0, len(arguments)) + + err := f.parseArgs(arguments, fn) + if err != nil { + switch f.errorHandling { + case ContinueOnError: + return err + case ExitOnError: + os.Exit(2) + case PanicOnError: + panic(err) + } + } + return nil +} + +// Parsed reports whether f.Parse has been called. +func (f *FlagSet) Parsed() bool { + return f.parsed +} + +// Parse parses the command-line flags from os.Args[1:]. Must be called +// after all flags are defined and before flags are accessed by the program. +func Parse() { + // Ignore errors; CommandLine is set for ExitOnError. + CommandLine.Parse(os.Args[1:]) +} + +// ParseAll parses the command-line flags from os.Args[1:] and called fn for each. +// The arguments for fn are flag and value. Must be called after all flags are +// defined and before flags are accessed by the program. +func ParseAll(fn func(flag *Flag, value string) error) { + // Ignore errors; CommandLine is set for ExitOnError. + CommandLine.ParseAll(os.Args[1:], fn) +} + +// SetInterspersed sets whether to support interspersed option/non-option arguments. +func SetInterspersed(interspersed bool) { + CommandLine.SetInterspersed(interspersed) +} + +// Parsed returns true if the command-line flags have been parsed. +func Parsed() bool { + return CommandLine.Parsed() +} + +// CommandLine is the default set of command-line flags, parsed from os.Args. +var CommandLine = NewFlagSet(os.Args[0], ExitOnError) + +// NewFlagSet returns a new, empty flag set with the specified name, +// error handling property and SortFlags set to true. +func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { + f := &FlagSet{ + name: name, + errorHandling: errorHandling, + argsLenAtDash: -1, + interspersed: true, + SortFlags: true, + } + return f +} + +// SetInterspersed sets whether to support interspersed option/non-option arguments. +func (f *FlagSet) SetInterspersed(interspersed bool) { + f.interspersed = interspersed +} + +// Init sets the name and error handling property for a flag set. +// By default, the zero FlagSet uses an empty name and the +// ContinueOnError error handling policy. +func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { + f.name = name + f.errorHandling = errorHandling + f.argsLenAtDash = -1 +} diff --git a/vendor/github.com/spf13/pflag/float32.go b/vendor/github.com/spf13/pflag/float32.go new file mode 100644 index 0000000..a243f81 --- /dev/null +++ b/vendor/github.com/spf13/pflag/float32.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- float32 Value +type float32Value float32 + +func newFloat32Value(val float32, p *float32) *float32Value { + *p = val + return (*float32Value)(p) +} + +func (f *float32Value) Set(s string) error { + v, err := strconv.ParseFloat(s, 32) + *f = float32Value(v) + return err +} + +func (f *float32Value) Type() string { + return "float32" +} + +func (f *float32Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 32) } + +func float32Conv(sval string) (interface{}, error) { + v, err := strconv.ParseFloat(sval, 32) + if err != nil { + return 0, err + } + return float32(v), nil +} + +// GetFloat32 return the float32 value of a flag with the given name +func (f *FlagSet) GetFloat32(name string) (float32, error) { + val, err := f.getFlagType(name, "float32", float32Conv) + if err != nil { + return 0, err + } + return val.(float32), nil +} + +// Float32Var defines a float32 flag with specified name, default value, and usage string. +// The argument p points to a float32 variable in which to store the value of the flag. +func (f *FlagSet) Float32Var(p *float32, name string, value float32, usage string) { + f.VarP(newFloat32Value(value, p), name, "", usage) +} + +// Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float32VarP(p *float32, name, shorthand string, value float32, usage string) { + f.VarP(newFloat32Value(value, p), name, shorthand, usage) +} + +// Float32Var defines a float32 flag with specified name, default value, and usage string. +// The argument p points to a float32 variable in which to store the value of the flag. +func Float32Var(p *float32, name string, value float32, usage string) { + CommandLine.VarP(newFloat32Value(value, p), name, "", usage) +} + +// Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash. +func Float32VarP(p *float32, name, shorthand string, value float32, usage string) { + CommandLine.VarP(newFloat32Value(value, p), name, shorthand, usage) +} + +// Float32 defines a float32 flag with specified name, default value, and usage string. +// The return value is the address of a float32 variable that stores the value of the flag. +func (f *FlagSet) Float32(name string, value float32, usage string) *float32 { + p := new(float32) + f.Float32VarP(p, name, "", value, usage) + return p +} + +// Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float32P(name, shorthand string, value float32, usage string) *float32 { + p := new(float32) + f.Float32VarP(p, name, shorthand, value, usage) + return p +} + +// Float32 defines a float32 flag with specified name, default value, and usage string. +// The return value is the address of a float32 variable that stores the value of the flag. +func Float32(name string, value float32, usage string) *float32 { + return CommandLine.Float32P(name, "", value, usage) +} + +// Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash. +func Float32P(name, shorthand string, value float32, usage string) *float32 { + return CommandLine.Float32P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/float32_slice.go b/vendor/github.com/spf13/pflag/float32_slice.go new file mode 100644 index 0000000..caa3527 --- /dev/null +++ b/vendor/github.com/spf13/pflag/float32_slice.go @@ -0,0 +1,174 @@ +package pflag + +import ( + "fmt" + "strconv" + "strings" +) + +// -- float32Slice Value +type float32SliceValue struct { + value *[]float32 + changed bool +} + +func newFloat32SliceValue(val []float32, p *[]float32) *float32SliceValue { + isv := new(float32SliceValue) + isv.value = p + *isv.value = val + return isv +} + +func (s *float32SliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]float32, len(ss)) + for i, d := range ss { + var err error + var temp64 float64 + temp64, err = strconv.ParseFloat(d, 32) + if err != nil { + return err + } + out[i] = float32(temp64) + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *float32SliceValue) Type() string { + return "float32Slice" +} + +func (s *float32SliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%f", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func (s *float32SliceValue) fromString(val string) (float32, error) { + t64, err := strconv.ParseFloat(val, 32) + if err != nil { + return 0, err + } + return float32(t64), nil +} + +func (s *float32SliceValue) toString(val float32) string { + return fmt.Sprintf("%f", val) +} + +func (s *float32SliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *float32SliceValue) Replace(val []string) error { + out := make([]float32, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *float32SliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func float32SliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []float32{}, nil + } + ss := strings.Split(val, ",") + out := make([]float32, len(ss)) + for i, d := range ss { + var err error + var temp64 float64 + temp64, err = strconv.ParseFloat(d, 32) + if err != nil { + return nil, err + } + out[i] = float32(temp64) + + } + return out, nil +} + +// GetFloat32Slice return the []float32 value of a flag with the given name +func (f *FlagSet) GetFloat32Slice(name string) ([]float32, error) { + val, err := f.getFlagType(name, "float32Slice", float32SliceConv) + if err != nil { + return []float32{}, err + } + return val.([]float32), nil +} + +// Float32SliceVar defines a float32Slice flag with specified name, default value, and usage string. +// The argument p points to a []float32 variable in which to store the value of the flag. +func (f *FlagSet) Float32SliceVar(p *[]float32, name string, value []float32, usage string) { + f.VarP(newFloat32SliceValue(value, p), name, "", usage) +} + +// Float32SliceVarP is like Float32SliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float32SliceVarP(p *[]float32, name, shorthand string, value []float32, usage string) { + f.VarP(newFloat32SliceValue(value, p), name, shorthand, usage) +} + +// Float32SliceVar defines a float32[] flag with specified name, default value, and usage string. +// The argument p points to a float32[] variable in which to store the value of the flag. +func Float32SliceVar(p *[]float32, name string, value []float32, usage string) { + CommandLine.VarP(newFloat32SliceValue(value, p), name, "", usage) +} + +// Float32SliceVarP is like Float32SliceVar, but accepts a shorthand letter that can be used after a single dash. +func Float32SliceVarP(p *[]float32, name, shorthand string, value []float32, usage string) { + CommandLine.VarP(newFloat32SliceValue(value, p), name, shorthand, usage) +} + +// Float32Slice defines a []float32 flag with specified name, default value, and usage string. +// The return value is the address of a []float32 variable that stores the value of the flag. +func (f *FlagSet) Float32Slice(name string, value []float32, usage string) *[]float32 { + p := []float32{} + f.Float32SliceVarP(&p, name, "", value, usage) + return &p +} + +// Float32SliceP is like Float32Slice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float32SliceP(name, shorthand string, value []float32, usage string) *[]float32 { + p := []float32{} + f.Float32SliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// Float32Slice defines a []float32 flag with specified name, default value, and usage string. +// The return value is the address of a []float32 variable that stores the value of the flag. +func Float32Slice(name string, value []float32, usage string) *[]float32 { + return CommandLine.Float32SliceP(name, "", value, usage) +} + +// Float32SliceP is like Float32Slice, but accepts a shorthand letter that can be used after a single dash. +func Float32SliceP(name, shorthand string, value []float32, usage string) *[]float32 { + return CommandLine.Float32SliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/float64.go b/vendor/github.com/spf13/pflag/float64.go new file mode 100644 index 0000000..04b5492 --- /dev/null +++ b/vendor/github.com/spf13/pflag/float64.go @@ -0,0 +1,84 @@ +package pflag + +import "strconv" + +// -- float64 Value +type float64Value float64 + +func newFloat64Value(val float64, p *float64) *float64Value { + *p = val + return (*float64Value)(p) +} + +func (f *float64Value) Set(s string) error { + v, err := strconv.ParseFloat(s, 64) + *f = float64Value(v) + return err +} + +func (f *float64Value) Type() string { + return "float64" +} + +func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) } + +func float64Conv(sval string) (interface{}, error) { + return strconv.ParseFloat(sval, 64) +} + +// GetFloat64 return the float64 value of a flag with the given name +func (f *FlagSet) GetFloat64(name string) (float64, error) { + val, err := f.getFlagType(name, "float64", float64Conv) + if err != nil { + return 0, err + } + return val.(float64), nil +} + +// Float64Var defines a float64 flag with specified name, default value, and usage string. +// The argument p points to a float64 variable in which to store the value of the flag. +func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) { + f.VarP(newFloat64Value(value, p), name, "", usage) +} + +// Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float64VarP(p *float64, name, shorthand string, value float64, usage string) { + f.VarP(newFloat64Value(value, p), name, shorthand, usage) +} + +// Float64Var defines a float64 flag with specified name, default value, and usage string. +// The argument p points to a float64 variable in which to store the value of the flag. +func Float64Var(p *float64, name string, value float64, usage string) { + CommandLine.VarP(newFloat64Value(value, p), name, "", usage) +} + +// Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash. +func Float64VarP(p *float64, name, shorthand string, value float64, usage string) { + CommandLine.VarP(newFloat64Value(value, p), name, shorthand, usage) +} + +// Float64 defines a float64 flag with specified name, default value, and usage string. +// The return value is the address of a float64 variable that stores the value of the flag. +func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { + p := new(float64) + f.Float64VarP(p, name, "", value, usage) + return p +} + +// Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float64P(name, shorthand string, value float64, usage string) *float64 { + p := new(float64) + f.Float64VarP(p, name, shorthand, value, usage) + return p +} + +// Float64 defines a float64 flag with specified name, default value, and usage string. +// The return value is the address of a float64 variable that stores the value of the flag. +func Float64(name string, value float64, usage string) *float64 { + return CommandLine.Float64P(name, "", value, usage) +} + +// Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash. +func Float64P(name, shorthand string, value float64, usage string) *float64 { + return CommandLine.Float64P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/float64_slice.go b/vendor/github.com/spf13/pflag/float64_slice.go new file mode 100644 index 0000000..85bf307 --- /dev/null +++ b/vendor/github.com/spf13/pflag/float64_slice.go @@ -0,0 +1,166 @@ +package pflag + +import ( + "fmt" + "strconv" + "strings" +) + +// -- float64Slice Value +type float64SliceValue struct { + value *[]float64 + changed bool +} + +func newFloat64SliceValue(val []float64, p *[]float64) *float64SliceValue { + isv := new(float64SliceValue) + isv.value = p + *isv.value = val + return isv +} + +func (s *float64SliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]float64, len(ss)) + for i, d := range ss { + var err error + out[i], err = strconv.ParseFloat(d, 64) + if err != nil { + return err + } + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *float64SliceValue) Type() string { + return "float64Slice" +} + +func (s *float64SliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%f", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func (s *float64SliceValue) fromString(val string) (float64, error) { + return strconv.ParseFloat(val, 64) +} + +func (s *float64SliceValue) toString(val float64) string { + return fmt.Sprintf("%f", val) +} + +func (s *float64SliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *float64SliceValue) Replace(val []string) error { + out := make([]float64, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *float64SliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func float64SliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []float64{}, nil + } + ss := strings.Split(val, ",") + out := make([]float64, len(ss)) + for i, d := range ss { + var err error + out[i], err = strconv.ParseFloat(d, 64) + if err != nil { + return nil, err + } + + } + return out, nil +} + +// GetFloat64Slice return the []float64 value of a flag with the given name +func (f *FlagSet) GetFloat64Slice(name string) ([]float64, error) { + val, err := f.getFlagType(name, "float64Slice", float64SliceConv) + if err != nil { + return []float64{}, err + } + return val.([]float64), nil +} + +// Float64SliceVar defines a float64Slice flag with specified name, default value, and usage string. +// The argument p points to a []float64 variable in which to store the value of the flag. +func (f *FlagSet) Float64SliceVar(p *[]float64, name string, value []float64, usage string) { + f.VarP(newFloat64SliceValue(value, p), name, "", usage) +} + +// Float64SliceVarP is like Float64SliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float64SliceVarP(p *[]float64, name, shorthand string, value []float64, usage string) { + f.VarP(newFloat64SliceValue(value, p), name, shorthand, usage) +} + +// Float64SliceVar defines a float64[] flag with specified name, default value, and usage string. +// The argument p points to a float64[] variable in which to store the value of the flag. +func Float64SliceVar(p *[]float64, name string, value []float64, usage string) { + CommandLine.VarP(newFloat64SliceValue(value, p), name, "", usage) +} + +// Float64SliceVarP is like Float64SliceVar, but accepts a shorthand letter that can be used after a single dash. +func Float64SliceVarP(p *[]float64, name, shorthand string, value []float64, usage string) { + CommandLine.VarP(newFloat64SliceValue(value, p), name, shorthand, usage) +} + +// Float64Slice defines a []float64 flag with specified name, default value, and usage string. +// The return value is the address of a []float64 variable that stores the value of the flag. +func (f *FlagSet) Float64Slice(name string, value []float64, usage string) *[]float64 { + p := []float64{} + f.Float64SliceVarP(&p, name, "", value, usage) + return &p +} + +// Float64SliceP is like Float64Slice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Float64SliceP(name, shorthand string, value []float64, usage string) *[]float64 { + p := []float64{} + f.Float64SliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// Float64Slice defines a []float64 flag with specified name, default value, and usage string. +// The return value is the address of a []float64 variable that stores the value of the flag. +func Float64Slice(name string, value []float64, usage string) *[]float64 { + return CommandLine.Float64SliceP(name, "", value, usage) +} + +// Float64SliceP is like Float64Slice, but accepts a shorthand letter that can be used after a single dash. +func Float64SliceP(name, shorthand string, value []float64, usage string) *[]float64 { + return CommandLine.Float64SliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/go.mod b/vendor/github.com/spf13/pflag/go.mod new file mode 100644 index 0000000..b2287ee --- /dev/null +++ b/vendor/github.com/spf13/pflag/go.mod @@ -0,0 +1,3 @@ +module github.com/spf13/pflag + +go 1.12 diff --git a/vendor/github.com/spf13/pflag/go.sum b/vendor/github.com/spf13/pflag/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/vendor/github.com/spf13/pflag/golangflag.go b/vendor/github.com/spf13/pflag/golangflag.go new file mode 100644 index 0000000..d3dd72b --- /dev/null +++ b/vendor/github.com/spf13/pflag/golangflag.go @@ -0,0 +1,105 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pflag + +import ( + goflag "flag" + "reflect" + "strings" +) + +// flagValueWrapper implements pflag.Value around a flag.Value. The main +// difference here is the addition of the Type method that returns a string +// name of the type. As this is generally unknown, we approximate that with +// reflection. +type flagValueWrapper struct { + inner goflag.Value + flagType string +} + +// We are just copying the boolFlag interface out of goflag as that is what +// they use to decide if a flag should get "true" when no arg is given. +type goBoolFlag interface { + goflag.Value + IsBoolFlag() bool +} + +func wrapFlagValue(v goflag.Value) Value { + // If the flag.Value happens to also be a pflag.Value, just use it directly. + if pv, ok := v.(Value); ok { + return pv + } + + pv := &flagValueWrapper{ + inner: v, + } + + t := reflect.TypeOf(v) + if t.Kind() == reflect.Interface || t.Kind() == reflect.Ptr { + t = t.Elem() + } + + pv.flagType = strings.TrimSuffix(t.Name(), "Value") + return pv +} + +func (v *flagValueWrapper) String() string { + return v.inner.String() +} + +func (v *flagValueWrapper) Set(s string) error { + return v.inner.Set(s) +} + +func (v *flagValueWrapper) Type() string { + return v.flagType +} + +// PFlagFromGoFlag will return a *pflag.Flag given a *flag.Flag +// If the *flag.Flag.Name was a single character (ex: `v`) it will be accessiblei +// with both `-v` and `--v` in flags. If the golang flag was more than a single +// character (ex: `verbose`) it will only be accessible via `--verbose` +func PFlagFromGoFlag(goflag *goflag.Flag) *Flag { + // Remember the default value as a string; it won't change. + flag := &Flag{ + Name: goflag.Name, + Usage: goflag.Usage, + Value: wrapFlagValue(goflag.Value), + // Looks like golang flags don't set DefValue correctly :-( + //DefValue: goflag.DefValue, + DefValue: goflag.Value.String(), + } + // Ex: if the golang flag was -v, allow both -v and --v to work + if len(flag.Name) == 1 { + flag.Shorthand = flag.Name + } + if fv, ok := goflag.Value.(goBoolFlag); ok && fv.IsBoolFlag() { + flag.NoOptDefVal = "true" + } + return flag +} + +// AddGoFlag will add the given *flag.Flag to the pflag.FlagSet +func (f *FlagSet) AddGoFlag(goflag *goflag.Flag) { + if f.Lookup(goflag.Name) != nil { + return + } + newflag := PFlagFromGoFlag(goflag) + f.AddFlag(newflag) +} + +// AddGoFlagSet will add the given *flag.FlagSet to the pflag.FlagSet +func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) { + if newSet == nil { + return + } + newSet.VisitAll(func(goflag *goflag.Flag) { + f.AddGoFlag(goflag) + }) + if f.addedGoFlagSets == nil { + f.addedGoFlagSets = make([]*goflag.FlagSet, 0) + } + f.addedGoFlagSets = append(f.addedGoFlagSets, newSet) +} diff --git a/vendor/github.com/spf13/pflag/int.go b/vendor/github.com/spf13/pflag/int.go new file mode 100644 index 0000000..1474b89 --- /dev/null +++ b/vendor/github.com/spf13/pflag/int.go @@ -0,0 +1,84 @@ +package pflag + +import "strconv" + +// -- int Value +type intValue int + +func newIntValue(val int, p *int) *intValue { + *p = val + return (*intValue)(p) +} + +func (i *intValue) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 64) + *i = intValue(v) + return err +} + +func (i *intValue) Type() string { + return "int" +} + +func (i *intValue) String() string { return strconv.Itoa(int(*i)) } + +func intConv(sval string) (interface{}, error) { + return strconv.Atoi(sval) +} + +// GetInt return the int value of a flag with the given name +func (f *FlagSet) GetInt(name string) (int, error) { + val, err := f.getFlagType(name, "int", intConv) + if err != nil { + return 0, err + } + return val.(int), nil +} + +// IntVar defines an int flag with specified name, default value, and usage string. +// The argument p points to an int variable in which to store the value of the flag. +func (f *FlagSet) IntVar(p *int, name string, value int, usage string) { + f.VarP(newIntValue(value, p), name, "", usage) +} + +// IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IntVarP(p *int, name, shorthand string, value int, usage string) { + f.VarP(newIntValue(value, p), name, shorthand, usage) +} + +// IntVar defines an int flag with specified name, default value, and usage string. +// The argument p points to an int variable in which to store the value of the flag. +func IntVar(p *int, name string, value int, usage string) { + CommandLine.VarP(newIntValue(value, p), name, "", usage) +} + +// IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash. +func IntVarP(p *int, name, shorthand string, value int, usage string) { + CommandLine.VarP(newIntValue(value, p), name, shorthand, usage) +} + +// Int defines an int flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +func (f *FlagSet) Int(name string, value int, usage string) *int { + p := new(int) + f.IntVarP(p, name, "", value, usage) + return p +} + +// IntP is like Int, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IntP(name, shorthand string, value int, usage string) *int { + p := new(int) + f.IntVarP(p, name, shorthand, value, usage) + return p +} + +// Int defines an int flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +func Int(name string, value int, usage string) *int { + return CommandLine.IntP(name, "", value, usage) +} + +// IntP is like Int, but accepts a shorthand letter that can be used after a single dash. +func IntP(name, shorthand string, value int, usage string) *int { + return CommandLine.IntP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/int16.go b/vendor/github.com/spf13/pflag/int16.go new file mode 100644 index 0000000..f1a01d0 --- /dev/null +++ b/vendor/github.com/spf13/pflag/int16.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- int16 Value +type int16Value int16 + +func newInt16Value(val int16, p *int16) *int16Value { + *p = val + return (*int16Value)(p) +} + +func (i *int16Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 16) + *i = int16Value(v) + return err +} + +func (i *int16Value) Type() string { + return "int16" +} + +func (i *int16Value) String() string { return strconv.FormatInt(int64(*i), 10) } + +func int16Conv(sval string) (interface{}, error) { + v, err := strconv.ParseInt(sval, 0, 16) + if err != nil { + return 0, err + } + return int16(v), nil +} + +// GetInt16 returns the int16 value of a flag with the given name +func (f *FlagSet) GetInt16(name string) (int16, error) { + val, err := f.getFlagType(name, "int16", int16Conv) + if err != nil { + return 0, err + } + return val.(int16), nil +} + +// Int16Var defines an int16 flag with specified name, default value, and usage string. +// The argument p points to an int16 variable in which to store the value of the flag. +func (f *FlagSet) Int16Var(p *int16, name string, value int16, usage string) { + f.VarP(newInt16Value(value, p), name, "", usage) +} + +// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int16VarP(p *int16, name, shorthand string, value int16, usage string) { + f.VarP(newInt16Value(value, p), name, shorthand, usage) +} + +// Int16Var defines an int16 flag with specified name, default value, and usage string. +// The argument p points to an int16 variable in which to store the value of the flag. +func Int16Var(p *int16, name string, value int16, usage string) { + CommandLine.VarP(newInt16Value(value, p), name, "", usage) +} + +// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. +func Int16VarP(p *int16, name, shorthand string, value int16, usage string) { + CommandLine.VarP(newInt16Value(value, p), name, shorthand, usage) +} + +// Int16 defines an int16 flag with specified name, default value, and usage string. +// The return value is the address of an int16 variable that stores the value of the flag. +func (f *FlagSet) Int16(name string, value int16, usage string) *int16 { + p := new(int16) + f.Int16VarP(p, name, "", value, usage) + return p +} + +// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int16P(name, shorthand string, value int16, usage string) *int16 { + p := new(int16) + f.Int16VarP(p, name, shorthand, value, usage) + return p +} + +// Int16 defines an int16 flag with specified name, default value, and usage string. +// The return value is the address of an int16 variable that stores the value of the flag. +func Int16(name string, value int16, usage string) *int16 { + return CommandLine.Int16P(name, "", value, usage) +} + +// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. +func Int16P(name, shorthand string, value int16, usage string) *int16 { + return CommandLine.Int16P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/int32.go b/vendor/github.com/spf13/pflag/int32.go new file mode 100644 index 0000000..9b95944 --- /dev/null +++ b/vendor/github.com/spf13/pflag/int32.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- int32 Value +type int32Value int32 + +func newInt32Value(val int32, p *int32) *int32Value { + *p = val + return (*int32Value)(p) +} + +func (i *int32Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 32) + *i = int32Value(v) + return err +} + +func (i *int32Value) Type() string { + return "int32" +} + +func (i *int32Value) String() string { return strconv.FormatInt(int64(*i), 10) } + +func int32Conv(sval string) (interface{}, error) { + v, err := strconv.ParseInt(sval, 0, 32) + if err != nil { + return 0, err + } + return int32(v), nil +} + +// GetInt32 return the int32 value of a flag with the given name +func (f *FlagSet) GetInt32(name string) (int32, error) { + val, err := f.getFlagType(name, "int32", int32Conv) + if err != nil { + return 0, err + } + return val.(int32), nil +} + +// Int32Var defines an int32 flag with specified name, default value, and usage string. +// The argument p points to an int32 variable in which to store the value of the flag. +func (f *FlagSet) Int32Var(p *int32, name string, value int32, usage string) { + f.VarP(newInt32Value(value, p), name, "", usage) +} + +// Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int32VarP(p *int32, name, shorthand string, value int32, usage string) { + f.VarP(newInt32Value(value, p), name, shorthand, usage) +} + +// Int32Var defines an int32 flag with specified name, default value, and usage string. +// The argument p points to an int32 variable in which to store the value of the flag. +func Int32Var(p *int32, name string, value int32, usage string) { + CommandLine.VarP(newInt32Value(value, p), name, "", usage) +} + +// Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash. +func Int32VarP(p *int32, name, shorthand string, value int32, usage string) { + CommandLine.VarP(newInt32Value(value, p), name, shorthand, usage) +} + +// Int32 defines an int32 flag with specified name, default value, and usage string. +// The return value is the address of an int32 variable that stores the value of the flag. +func (f *FlagSet) Int32(name string, value int32, usage string) *int32 { + p := new(int32) + f.Int32VarP(p, name, "", value, usage) + return p +} + +// Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int32P(name, shorthand string, value int32, usage string) *int32 { + p := new(int32) + f.Int32VarP(p, name, shorthand, value, usage) + return p +} + +// Int32 defines an int32 flag with specified name, default value, and usage string. +// The return value is the address of an int32 variable that stores the value of the flag. +func Int32(name string, value int32, usage string) *int32 { + return CommandLine.Int32P(name, "", value, usage) +} + +// Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash. +func Int32P(name, shorthand string, value int32, usage string) *int32 { + return CommandLine.Int32P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/int32_slice.go b/vendor/github.com/spf13/pflag/int32_slice.go new file mode 100644 index 0000000..ff128ff --- /dev/null +++ b/vendor/github.com/spf13/pflag/int32_slice.go @@ -0,0 +1,174 @@ +package pflag + +import ( + "fmt" + "strconv" + "strings" +) + +// -- int32Slice Value +type int32SliceValue struct { + value *[]int32 + changed bool +} + +func newInt32SliceValue(val []int32, p *[]int32) *int32SliceValue { + isv := new(int32SliceValue) + isv.value = p + *isv.value = val + return isv +} + +func (s *int32SliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]int32, len(ss)) + for i, d := range ss { + var err error + var temp64 int64 + temp64, err = strconv.ParseInt(d, 0, 32) + if err != nil { + return err + } + out[i] = int32(temp64) + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *int32SliceValue) Type() string { + return "int32Slice" +} + +func (s *int32SliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%d", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func (s *int32SliceValue) fromString(val string) (int32, error) { + t64, err := strconv.ParseInt(val, 0, 32) + if err != nil { + return 0, err + } + return int32(t64), nil +} + +func (s *int32SliceValue) toString(val int32) string { + return fmt.Sprintf("%d", val) +} + +func (s *int32SliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *int32SliceValue) Replace(val []string) error { + out := make([]int32, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *int32SliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func int32SliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []int32{}, nil + } + ss := strings.Split(val, ",") + out := make([]int32, len(ss)) + for i, d := range ss { + var err error + var temp64 int64 + temp64, err = strconv.ParseInt(d, 0, 32) + if err != nil { + return nil, err + } + out[i] = int32(temp64) + + } + return out, nil +} + +// GetInt32Slice return the []int32 value of a flag with the given name +func (f *FlagSet) GetInt32Slice(name string) ([]int32, error) { + val, err := f.getFlagType(name, "int32Slice", int32SliceConv) + if err != nil { + return []int32{}, err + } + return val.([]int32), nil +} + +// Int32SliceVar defines a int32Slice flag with specified name, default value, and usage string. +// The argument p points to a []int32 variable in which to store the value of the flag. +func (f *FlagSet) Int32SliceVar(p *[]int32, name string, value []int32, usage string) { + f.VarP(newInt32SliceValue(value, p), name, "", usage) +} + +// Int32SliceVarP is like Int32SliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int32SliceVarP(p *[]int32, name, shorthand string, value []int32, usage string) { + f.VarP(newInt32SliceValue(value, p), name, shorthand, usage) +} + +// Int32SliceVar defines a int32[] flag with specified name, default value, and usage string. +// The argument p points to a int32[] variable in which to store the value of the flag. +func Int32SliceVar(p *[]int32, name string, value []int32, usage string) { + CommandLine.VarP(newInt32SliceValue(value, p), name, "", usage) +} + +// Int32SliceVarP is like Int32SliceVar, but accepts a shorthand letter that can be used after a single dash. +func Int32SliceVarP(p *[]int32, name, shorthand string, value []int32, usage string) { + CommandLine.VarP(newInt32SliceValue(value, p), name, shorthand, usage) +} + +// Int32Slice defines a []int32 flag with specified name, default value, and usage string. +// The return value is the address of a []int32 variable that stores the value of the flag. +func (f *FlagSet) Int32Slice(name string, value []int32, usage string) *[]int32 { + p := []int32{} + f.Int32SliceVarP(&p, name, "", value, usage) + return &p +} + +// Int32SliceP is like Int32Slice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int32SliceP(name, shorthand string, value []int32, usage string) *[]int32 { + p := []int32{} + f.Int32SliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// Int32Slice defines a []int32 flag with specified name, default value, and usage string. +// The return value is the address of a []int32 variable that stores the value of the flag. +func Int32Slice(name string, value []int32, usage string) *[]int32 { + return CommandLine.Int32SliceP(name, "", value, usage) +} + +// Int32SliceP is like Int32Slice, but accepts a shorthand letter that can be used after a single dash. +func Int32SliceP(name, shorthand string, value []int32, usage string) *[]int32 { + return CommandLine.Int32SliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/int64.go b/vendor/github.com/spf13/pflag/int64.go new file mode 100644 index 0000000..0026d78 --- /dev/null +++ b/vendor/github.com/spf13/pflag/int64.go @@ -0,0 +1,84 @@ +package pflag + +import "strconv" + +// -- int64 Value +type int64Value int64 + +func newInt64Value(val int64, p *int64) *int64Value { + *p = val + return (*int64Value)(p) +} + +func (i *int64Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 64) + *i = int64Value(v) + return err +} + +func (i *int64Value) Type() string { + return "int64" +} + +func (i *int64Value) String() string { return strconv.FormatInt(int64(*i), 10) } + +func int64Conv(sval string) (interface{}, error) { + return strconv.ParseInt(sval, 0, 64) +} + +// GetInt64 return the int64 value of a flag with the given name +func (f *FlagSet) GetInt64(name string) (int64, error) { + val, err := f.getFlagType(name, "int64", int64Conv) + if err != nil { + return 0, err + } + return val.(int64), nil +} + +// Int64Var defines an int64 flag with specified name, default value, and usage string. +// The argument p points to an int64 variable in which to store the value of the flag. +func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { + f.VarP(newInt64Value(value, p), name, "", usage) +} + +// Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int64VarP(p *int64, name, shorthand string, value int64, usage string) { + f.VarP(newInt64Value(value, p), name, shorthand, usage) +} + +// Int64Var defines an int64 flag with specified name, default value, and usage string. +// The argument p points to an int64 variable in which to store the value of the flag. +func Int64Var(p *int64, name string, value int64, usage string) { + CommandLine.VarP(newInt64Value(value, p), name, "", usage) +} + +// Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash. +func Int64VarP(p *int64, name, shorthand string, value int64, usage string) { + CommandLine.VarP(newInt64Value(value, p), name, shorthand, usage) +} + +// Int64 defines an int64 flag with specified name, default value, and usage string. +// The return value is the address of an int64 variable that stores the value of the flag. +func (f *FlagSet) Int64(name string, value int64, usage string) *int64 { + p := new(int64) + f.Int64VarP(p, name, "", value, usage) + return p +} + +// Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int64P(name, shorthand string, value int64, usage string) *int64 { + p := new(int64) + f.Int64VarP(p, name, shorthand, value, usage) + return p +} + +// Int64 defines an int64 flag with specified name, default value, and usage string. +// The return value is the address of an int64 variable that stores the value of the flag. +func Int64(name string, value int64, usage string) *int64 { + return CommandLine.Int64P(name, "", value, usage) +} + +// Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash. +func Int64P(name, shorthand string, value int64, usage string) *int64 { + return CommandLine.Int64P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/int64_slice.go b/vendor/github.com/spf13/pflag/int64_slice.go new file mode 100644 index 0000000..2546463 --- /dev/null +++ b/vendor/github.com/spf13/pflag/int64_slice.go @@ -0,0 +1,166 @@ +package pflag + +import ( + "fmt" + "strconv" + "strings" +) + +// -- int64Slice Value +type int64SliceValue struct { + value *[]int64 + changed bool +} + +func newInt64SliceValue(val []int64, p *[]int64) *int64SliceValue { + isv := new(int64SliceValue) + isv.value = p + *isv.value = val + return isv +} + +func (s *int64SliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]int64, len(ss)) + for i, d := range ss { + var err error + out[i], err = strconv.ParseInt(d, 0, 64) + if err != nil { + return err + } + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *int64SliceValue) Type() string { + return "int64Slice" +} + +func (s *int64SliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%d", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func (s *int64SliceValue) fromString(val string) (int64, error) { + return strconv.ParseInt(val, 0, 64) +} + +func (s *int64SliceValue) toString(val int64) string { + return fmt.Sprintf("%d", val) +} + +func (s *int64SliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *int64SliceValue) Replace(val []string) error { + out := make([]int64, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *int64SliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func int64SliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []int64{}, nil + } + ss := strings.Split(val, ",") + out := make([]int64, len(ss)) + for i, d := range ss { + var err error + out[i], err = strconv.ParseInt(d, 0, 64) + if err != nil { + return nil, err + } + + } + return out, nil +} + +// GetInt64Slice return the []int64 value of a flag with the given name +func (f *FlagSet) GetInt64Slice(name string) ([]int64, error) { + val, err := f.getFlagType(name, "int64Slice", int64SliceConv) + if err != nil { + return []int64{}, err + } + return val.([]int64), nil +} + +// Int64SliceVar defines a int64Slice flag with specified name, default value, and usage string. +// The argument p points to a []int64 variable in which to store the value of the flag. +func (f *FlagSet) Int64SliceVar(p *[]int64, name string, value []int64, usage string) { + f.VarP(newInt64SliceValue(value, p), name, "", usage) +} + +// Int64SliceVarP is like Int64SliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int64SliceVarP(p *[]int64, name, shorthand string, value []int64, usage string) { + f.VarP(newInt64SliceValue(value, p), name, shorthand, usage) +} + +// Int64SliceVar defines a int64[] flag with specified name, default value, and usage string. +// The argument p points to a int64[] variable in which to store the value of the flag. +func Int64SliceVar(p *[]int64, name string, value []int64, usage string) { + CommandLine.VarP(newInt64SliceValue(value, p), name, "", usage) +} + +// Int64SliceVarP is like Int64SliceVar, but accepts a shorthand letter that can be used after a single dash. +func Int64SliceVarP(p *[]int64, name, shorthand string, value []int64, usage string) { + CommandLine.VarP(newInt64SliceValue(value, p), name, shorthand, usage) +} + +// Int64Slice defines a []int64 flag with specified name, default value, and usage string. +// The return value is the address of a []int64 variable that stores the value of the flag. +func (f *FlagSet) Int64Slice(name string, value []int64, usage string) *[]int64 { + p := []int64{} + f.Int64SliceVarP(&p, name, "", value, usage) + return &p +} + +// Int64SliceP is like Int64Slice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int64SliceP(name, shorthand string, value []int64, usage string) *[]int64 { + p := []int64{} + f.Int64SliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// Int64Slice defines a []int64 flag with specified name, default value, and usage string. +// The return value is the address of a []int64 variable that stores the value of the flag. +func Int64Slice(name string, value []int64, usage string) *[]int64 { + return CommandLine.Int64SliceP(name, "", value, usage) +} + +// Int64SliceP is like Int64Slice, but accepts a shorthand letter that can be used after a single dash. +func Int64SliceP(name, shorthand string, value []int64, usage string) *[]int64 { + return CommandLine.Int64SliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/int8.go b/vendor/github.com/spf13/pflag/int8.go new file mode 100644 index 0000000..4da9222 --- /dev/null +++ b/vendor/github.com/spf13/pflag/int8.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- int8 Value +type int8Value int8 + +func newInt8Value(val int8, p *int8) *int8Value { + *p = val + return (*int8Value)(p) +} + +func (i *int8Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 8) + *i = int8Value(v) + return err +} + +func (i *int8Value) Type() string { + return "int8" +} + +func (i *int8Value) String() string { return strconv.FormatInt(int64(*i), 10) } + +func int8Conv(sval string) (interface{}, error) { + v, err := strconv.ParseInt(sval, 0, 8) + if err != nil { + return 0, err + } + return int8(v), nil +} + +// GetInt8 return the int8 value of a flag with the given name +func (f *FlagSet) GetInt8(name string) (int8, error) { + val, err := f.getFlagType(name, "int8", int8Conv) + if err != nil { + return 0, err + } + return val.(int8), nil +} + +// Int8Var defines an int8 flag with specified name, default value, and usage string. +// The argument p points to an int8 variable in which to store the value of the flag. +func (f *FlagSet) Int8Var(p *int8, name string, value int8, usage string) { + f.VarP(newInt8Value(value, p), name, "", usage) +} + +// Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int8VarP(p *int8, name, shorthand string, value int8, usage string) { + f.VarP(newInt8Value(value, p), name, shorthand, usage) +} + +// Int8Var defines an int8 flag with specified name, default value, and usage string. +// The argument p points to an int8 variable in which to store the value of the flag. +func Int8Var(p *int8, name string, value int8, usage string) { + CommandLine.VarP(newInt8Value(value, p), name, "", usage) +} + +// Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash. +func Int8VarP(p *int8, name, shorthand string, value int8, usage string) { + CommandLine.VarP(newInt8Value(value, p), name, shorthand, usage) +} + +// Int8 defines an int8 flag with specified name, default value, and usage string. +// The return value is the address of an int8 variable that stores the value of the flag. +func (f *FlagSet) Int8(name string, value int8, usage string) *int8 { + p := new(int8) + f.Int8VarP(p, name, "", value, usage) + return p +} + +// Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Int8P(name, shorthand string, value int8, usage string) *int8 { + p := new(int8) + f.Int8VarP(p, name, shorthand, value, usage) + return p +} + +// Int8 defines an int8 flag with specified name, default value, and usage string. +// The return value is the address of an int8 variable that stores the value of the flag. +func Int8(name string, value int8, usage string) *int8 { + return CommandLine.Int8P(name, "", value, usage) +} + +// Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash. +func Int8P(name, shorthand string, value int8, usage string) *int8 { + return CommandLine.Int8P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/int_slice.go b/vendor/github.com/spf13/pflag/int_slice.go new file mode 100644 index 0000000..e71c39d --- /dev/null +++ b/vendor/github.com/spf13/pflag/int_slice.go @@ -0,0 +1,158 @@ +package pflag + +import ( + "fmt" + "strconv" + "strings" +) + +// -- intSlice Value +type intSliceValue struct { + value *[]int + changed bool +} + +func newIntSliceValue(val []int, p *[]int) *intSliceValue { + isv := new(intSliceValue) + isv.value = p + *isv.value = val + return isv +} + +func (s *intSliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]int, len(ss)) + for i, d := range ss { + var err error + out[i], err = strconv.Atoi(d) + if err != nil { + return err + } + + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *intSliceValue) Type() string { + return "intSlice" +} + +func (s *intSliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%d", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func (s *intSliceValue) Append(val string) error { + i, err := strconv.Atoi(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *intSliceValue) Replace(val []string) error { + out := make([]int, len(val)) + for i, d := range val { + var err error + out[i], err = strconv.Atoi(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *intSliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = strconv.Itoa(d) + } + return out +} + +func intSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []int{}, nil + } + ss := strings.Split(val, ",") + out := make([]int, len(ss)) + for i, d := range ss { + var err error + out[i], err = strconv.Atoi(d) + if err != nil { + return nil, err + } + + } + return out, nil +} + +// GetIntSlice return the []int value of a flag with the given name +func (f *FlagSet) GetIntSlice(name string) ([]int, error) { + val, err := f.getFlagType(name, "intSlice", intSliceConv) + if err != nil { + return []int{}, err + } + return val.([]int), nil +} + +// IntSliceVar defines a intSlice flag with specified name, default value, and usage string. +// The argument p points to a []int variable in which to store the value of the flag. +func (f *FlagSet) IntSliceVar(p *[]int, name string, value []int, usage string) { + f.VarP(newIntSliceValue(value, p), name, "", usage) +} + +// IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) { + f.VarP(newIntSliceValue(value, p), name, shorthand, usage) +} + +// IntSliceVar defines a int[] flag with specified name, default value, and usage string. +// The argument p points to a int[] variable in which to store the value of the flag. +func IntSliceVar(p *[]int, name string, value []int, usage string) { + CommandLine.VarP(newIntSliceValue(value, p), name, "", usage) +} + +// IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash. +func IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) { + CommandLine.VarP(newIntSliceValue(value, p), name, shorthand, usage) +} + +// IntSlice defines a []int flag with specified name, default value, and usage string. +// The return value is the address of a []int variable that stores the value of the flag. +func (f *FlagSet) IntSlice(name string, value []int, usage string) *[]int { + p := []int{} + f.IntSliceVarP(&p, name, "", value, usage) + return &p +} + +// IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IntSliceP(name, shorthand string, value []int, usage string) *[]int { + p := []int{} + f.IntSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// IntSlice defines a []int flag with specified name, default value, and usage string. +// The return value is the address of a []int variable that stores the value of the flag. +func IntSlice(name string, value []int, usage string) *[]int { + return CommandLine.IntSliceP(name, "", value, usage) +} + +// IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash. +func IntSliceP(name, shorthand string, value []int, usage string) *[]int { + return CommandLine.IntSliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/ip.go b/vendor/github.com/spf13/pflag/ip.go new file mode 100644 index 0000000..3d414ba --- /dev/null +++ b/vendor/github.com/spf13/pflag/ip.go @@ -0,0 +1,94 @@ +package pflag + +import ( + "fmt" + "net" + "strings" +) + +// -- net.IP value +type ipValue net.IP + +func newIPValue(val net.IP, p *net.IP) *ipValue { + *p = val + return (*ipValue)(p) +} + +func (i *ipValue) String() string { return net.IP(*i).String() } +func (i *ipValue) Set(s string) error { + ip := net.ParseIP(strings.TrimSpace(s)) + if ip == nil { + return fmt.Errorf("failed to parse IP: %q", s) + } + *i = ipValue(ip) + return nil +} + +func (i *ipValue) Type() string { + return "ip" +} + +func ipConv(sval string) (interface{}, error) { + ip := net.ParseIP(sval) + if ip != nil { + return ip, nil + } + return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval) +} + +// GetIP return the net.IP value of a flag with the given name +func (f *FlagSet) GetIP(name string) (net.IP, error) { + val, err := f.getFlagType(name, "ip", ipConv) + if err != nil { + return nil, err + } + return val.(net.IP), nil +} + +// IPVar defines an net.IP flag with specified name, default value, and usage string. +// The argument p points to an net.IP variable in which to store the value of the flag. +func (f *FlagSet) IPVar(p *net.IP, name string, value net.IP, usage string) { + f.VarP(newIPValue(value, p), name, "", usage) +} + +// IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) { + f.VarP(newIPValue(value, p), name, shorthand, usage) +} + +// IPVar defines an net.IP flag with specified name, default value, and usage string. +// The argument p points to an net.IP variable in which to store the value of the flag. +func IPVar(p *net.IP, name string, value net.IP, usage string) { + CommandLine.VarP(newIPValue(value, p), name, "", usage) +} + +// IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash. +func IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) { + CommandLine.VarP(newIPValue(value, p), name, shorthand, usage) +} + +// IP defines an net.IP flag with specified name, default value, and usage string. +// The return value is the address of an net.IP variable that stores the value of the flag. +func (f *FlagSet) IP(name string, value net.IP, usage string) *net.IP { + p := new(net.IP) + f.IPVarP(p, name, "", value, usage) + return p +} + +// IPP is like IP, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPP(name, shorthand string, value net.IP, usage string) *net.IP { + p := new(net.IP) + f.IPVarP(p, name, shorthand, value, usage) + return p +} + +// IP defines an net.IP flag with specified name, default value, and usage string. +// The return value is the address of an net.IP variable that stores the value of the flag. +func IP(name string, value net.IP, usage string) *net.IP { + return CommandLine.IPP(name, "", value, usage) +} + +// IPP is like IP, but accepts a shorthand letter that can be used after a single dash. +func IPP(name, shorthand string, value net.IP, usage string) *net.IP { + return CommandLine.IPP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/ip_slice.go b/vendor/github.com/spf13/pflag/ip_slice.go new file mode 100644 index 0000000..775faae --- /dev/null +++ b/vendor/github.com/spf13/pflag/ip_slice.go @@ -0,0 +1,186 @@ +package pflag + +import ( + "fmt" + "io" + "net" + "strings" +) + +// -- ipSlice Value +type ipSliceValue struct { + value *[]net.IP + changed bool +} + +func newIPSliceValue(val []net.IP, p *[]net.IP) *ipSliceValue { + ipsv := new(ipSliceValue) + ipsv.value = p + *ipsv.value = val + return ipsv +} + +// Set converts, and assigns, the comma-separated IP argument string representation as the []net.IP value of this flag. +// If Set is called on a flag that already has a []net.IP assigned, the newly converted values will be appended. +func (s *ipSliceValue) Set(val string) error { + + // remove all quote characters + rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") + + // read flag arguments with CSV parser + ipStrSlice, err := readAsCSV(rmQuote.Replace(val)) + if err != nil && err != io.EOF { + return err + } + + // parse ip values into slice + out := make([]net.IP, 0, len(ipStrSlice)) + for _, ipStr := range ipStrSlice { + ip := net.ParseIP(strings.TrimSpace(ipStr)) + if ip == nil { + return fmt.Errorf("invalid string being converted to IP address: %s", ipStr) + } + out = append(out, ip) + } + + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + + s.changed = true + + return nil +} + +// Type returns a string that uniquely represents this flag's type. +func (s *ipSliceValue) Type() string { + return "ipSlice" +} + +// String defines a "native" format for this net.IP slice flag value. +func (s *ipSliceValue) String() string { + + ipStrSlice := make([]string, len(*s.value)) + for i, ip := range *s.value { + ipStrSlice[i] = ip.String() + } + + out, _ := writeAsCSV(ipStrSlice) + + return "[" + out + "]" +} + +func (s *ipSliceValue) fromString(val string) (net.IP, error) { + return net.ParseIP(strings.TrimSpace(val)), nil +} + +func (s *ipSliceValue) toString(val net.IP) string { + return val.String() +} + +func (s *ipSliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *ipSliceValue) Replace(val []string) error { + out := make([]net.IP, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *ipSliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func ipSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []net.IP{}, nil + } + ss := strings.Split(val, ",") + out := make([]net.IP, len(ss)) + for i, sval := range ss { + ip := net.ParseIP(strings.TrimSpace(sval)) + if ip == nil { + return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval) + } + out[i] = ip + } + return out, nil +} + +// GetIPSlice returns the []net.IP value of a flag with the given name +func (f *FlagSet) GetIPSlice(name string) ([]net.IP, error) { + val, err := f.getFlagType(name, "ipSlice", ipSliceConv) + if err != nil { + return []net.IP{}, err + } + return val.([]net.IP), nil +} + +// IPSliceVar defines a ipSlice flag with specified name, default value, and usage string. +// The argument p points to a []net.IP variable in which to store the value of the flag. +func (f *FlagSet) IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) { + f.VarP(newIPSliceValue(value, p), name, "", usage) +} + +// IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) { + f.VarP(newIPSliceValue(value, p), name, shorthand, usage) +} + +// IPSliceVar defines a []net.IP flag with specified name, default value, and usage string. +// The argument p points to a []net.IP variable in which to store the value of the flag. +func IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) { + CommandLine.VarP(newIPSliceValue(value, p), name, "", usage) +} + +// IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash. +func IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) { + CommandLine.VarP(newIPSliceValue(value, p), name, shorthand, usage) +} + +// IPSlice defines a []net.IP flag with specified name, default value, and usage string. +// The return value is the address of a []net.IP variable that stores the value of that flag. +func (f *FlagSet) IPSlice(name string, value []net.IP, usage string) *[]net.IP { + p := []net.IP{} + f.IPSliceVarP(&p, name, "", value, usage) + return &p +} + +// IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP { + p := []net.IP{} + f.IPSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// IPSlice defines a []net.IP flag with specified name, default value, and usage string. +// The return value is the address of a []net.IP variable that stores the value of the flag. +func IPSlice(name string, value []net.IP, usage string) *[]net.IP { + return CommandLine.IPSliceP(name, "", value, usage) +} + +// IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash. +func IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP { + return CommandLine.IPSliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/ipmask.go b/vendor/github.com/spf13/pflag/ipmask.go new file mode 100644 index 0000000..5bd44bd --- /dev/null +++ b/vendor/github.com/spf13/pflag/ipmask.go @@ -0,0 +1,122 @@ +package pflag + +import ( + "fmt" + "net" + "strconv" +) + +// -- net.IPMask value +type ipMaskValue net.IPMask + +func newIPMaskValue(val net.IPMask, p *net.IPMask) *ipMaskValue { + *p = val + return (*ipMaskValue)(p) +} + +func (i *ipMaskValue) String() string { return net.IPMask(*i).String() } +func (i *ipMaskValue) Set(s string) error { + ip := ParseIPv4Mask(s) + if ip == nil { + return fmt.Errorf("failed to parse IP mask: %q", s) + } + *i = ipMaskValue(ip) + return nil +} + +func (i *ipMaskValue) Type() string { + return "ipMask" +} + +// ParseIPv4Mask written in IP form (e.g. 255.255.255.0). +// This function should really belong to the net package. +func ParseIPv4Mask(s string) net.IPMask { + mask := net.ParseIP(s) + if mask == nil { + if len(s) != 8 { + return nil + } + // net.IPMask.String() actually outputs things like ffffff00 + // so write a horrible parser for that as well :-( + m := []int{} + for i := 0; i < 4; i++ { + b := "0x" + s[2*i:2*i+2] + d, err := strconv.ParseInt(b, 0, 0) + if err != nil { + return nil + } + m = append(m, int(d)) + } + s := fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3]) + mask = net.ParseIP(s) + if mask == nil { + return nil + } + } + return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15]) +} + +func parseIPv4Mask(sval string) (interface{}, error) { + mask := ParseIPv4Mask(sval) + if mask == nil { + return nil, fmt.Errorf("unable to parse %s as net.IPMask", sval) + } + return mask, nil +} + +// GetIPv4Mask return the net.IPv4Mask value of a flag with the given name +func (f *FlagSet) GetIPv4Mask(name string) (net.IPMask, error) { + val, err := f.getFlagType(name, "ipMask", parseIPv4Mask) + if err != nil { + return nil, err + } + return val.(net.IPMask), nil +} + +// IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string. +// The argument p points to an net.IPMask variable in which to store the value of the flag. +func (f *FlagSet) IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) { + f.VarP(newIPMaskValue(value, p), name, "", usage) +} + +// IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) { + f.VarP(newIPMaskValue(value, p), name, shorthand, usage) +} + +// IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string. +// The argument p points to an net.IPMask variable in which to store the value of the flag. +func IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) { + CommandLine.VarP(newIPMaskValue(value, p), name, "", usage) +} + +// IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash. +func IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) { + CommandLine.VarP(newIPMaskValue(value, p), name, shorthand, usage) +} + +// IPMask defines an net.IPMask flag with specified name, default value, and usage string. +// The return value is the address of an net.IPMask variable that stores the value of the flag. +func (f *FlagSet) IPMask(name string, value net.IPMask, usage string) *net.IPMask { + p := new(net.IPMask) + f.IPMaskVarP(p, name, "", value, usage) + return p +} + +// IPMaskP is like IPMask, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask { + p := new(net.IPMask) + f.IPMaskVarP(p, name, shorthand, value, usage) + return p +} + +// IPMask defines an net.IPMask flag with specified name, default value, and usage string. +// The return value is the address of an net.IPMask variable that stores the value of the flag. +func IPMask(name string, value net.IPMask, usage string) *net.IPMask { + return CommandLine.IPMaskP(name, "", value, usage) +} + +// IPMaskP is like IP, but accepts a shorthand letter that can be used after a single dash. +func IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask { + return CommandLine.IPMaskP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/ipnet.go b/vendor/github.com/spf13/pflag/ipnet.go new file mode 100644 index 0000000..e2c1b8b --- /dev/null +++ b/vendor/github.com/spf13/pflag/ipnet.go @@ -0,0 +1,98 @@ +package pflag + +import ( + "fmt" + "net" + "strings" +) + +// IPNet adapts net.IPNet for use as a flag. +type ipNetValue net.IPNet + +func (ipnet ipNetValue) String() string { + n := net.IPNet(ipnet) + return n.String() +} + +func (ipnet *ipNetValue) Set(value string) error { + _, n, err := net.ParseCIDR(strings.TrimSpace(value)) + if err != nil { + return err + } + *ipnet = ipNetValue(*n) + return nil +} + +func (*ipNetValue) Type() string { + return "ipNet" +} + +func newIPNetValue(val net.IPNet, p *net.IPNet) *ipNetValue { + *p = val + return (*ipNetValue)(p) +} + +func ipNetConv(sval string) (interface{}, error) { + _, n, err := net.ParseCIDR(strings.TrimSpace(sval)) + if err == nil { + return *n, nil + } + return nil, fmt.Errorf("invalid string being converted to IPNet: %s", sval) +} + +// GetIPNet return the net.IPNet value of a flag with the given name +func (f *FlagSet) GetIPNet(name string) (net.IPNet, error) { + val, err := f.getFlagType(name, "ipNet", ipNetConv) + if err != nil { + return net.IPNet{}, err + } + return val.(net.IPNet), nil +} + +// IPNetVar defines an net.IPNet flag with specified name, default value, and usage string. +// The argument p points to an net.IPNet variable in which to store the value of the flag. +func (f *FlagSet) IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) { + f.VarP(newIPNetValue(value, p), name, "", usage) +} + +// IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) { + f.VarP(newIPNetValue(value, p), name, shorthand, usage) +} + +// IPNetVar defines an net.IPNet flag with specified name, default value, and usage string. +// The argument p points to an net.IPNet variable in which to store the value of the flag. +func IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) { + CommandLine.VarP(newIPNetValue(value, p), name, "", usage) +} + +// IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash. +func IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) { + CommandLine.VarP(newIPNetValue(value, p), name, shorthand, usage) +} + +// IPNet defines an net.IPNet flag with specified name, default value, and usage string. +// The return value is the address of an net.IPNet variable that stores the value of the flag. +func (f *FlagSet) IPNet(name string, value net.IPNet, usage string) *net.IPNet { + p := new(net.IPNet) + f.IPNetVarP(p, name, "", value, usage) + return p +} + +// IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet { + p := new(net.IPNet) + f.IPNetVarP(p, name, shorthand, value, usage) + return p +} + +// IPNet defines an net.IPNet flag with specified name, default value, and usage string. +// The return value is the address of an net.IPNet variable that stores the value of the flag. +func IPNet(name string, value net.IPNet, usage string) *net.IPNet { + return CommandLine.IPNetP(name, "", value, usage) +} + +// IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash. +func IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet { + return CommandLine.IPNetP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string.go b/vendor/github.com/spf13/pflag/string.go new file mode 100644 index 0000000..04e0a26 --- /dev/null +++ b/vendor/github.com/spf13/pflag/string.go @@ -0,0 +1,80 @@ +package pflag + +// -- string Value +type stringValue string + +func newStringValue(val string, p *string) *stringValue { + *p = val + return (*stringValue)(p) +} + +func (s *stringValue) Set(val string) error { + *s = stringValue(val) + return nil +} +func (s *stringValue) Type() string { + return "string" +} + +func (s *stringValue) String() string { return string(*s) } + +func stringConv(sval string) (interface{}, error) { + return sval, nil +} + +// GetString return the string value of a flag with the given name +func (f *FlagSet) GetString(name string) (string, error) { + val, err := f.getFlagType(name, "string", stringConv) + if err != nil { + return "", err + } + return val.(string), nil +} + +// StringVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a string variable in which to store the value of the flag. +func (f *FlagSet) StringVar(p *string, name string, value string, usage string) { + f.VarP(newStringValue(value, p), name, "", usage) +} + +// StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringVarP(p *string, name, shorthand string, value string, usage string) { + f.VarP(newStringValue(value, p), name, shorthand, usage) +} + +// StringVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a string variable in which to store the value of the flag. +func StringVar(p *string, name string, value string, usage string) { + CommandLine.VarP(newStringValue(value, p), name, "", usage) +} + +// StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash. +func StringVarP(p *string, name, shorthand string, value string, usage string) { + CommandLine.VarP(newStringValue(value, p), name, shorthand, usage) +} + +// String defines a string flag with specified name, default value, and usage string. +// The return value is the address of a string variable that stores the value of the flag. +func (f *FlagSet) String(name string, value string, usage string) *string { + p := new(string) + f.StringVarP(p, name, "", value, usage) + return p +} + +// StringP is like String, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringP(name, shorthand string, value string, usage string) *string { + p := new(string) + f.StringVarP(p, name, shorthand, value, usage) + return p +} + +// String defines a string flag with specified name, default value, and usage string. +// The return value is the address of a string variable that stores the value of the flag. +func String(name string, value string, usage string) *string { + return CommandLine.StringP(name, "", value, usage) +} + +// StringP is like String, but accepts a shorthand letter that can be used after a single dash. +func StringP(name, shorthand string, value string, usage string) *string { + return CommandLine.StringP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string_array.go b/vendor/github.com/spf13/pflag/string_array.go new file mode 100644 index 0000000..4894af8 --- /dev/null +++ b/vendor/github.com/spf13/pflag/string_array.go @@ -0,0 +1,129 @@ +package pflag + +// -- stringArray Value +type stringArrayValue struct { + value *[]string + changed bool +} + +func newStringArrayValue(val []string, p *[]string) *stringArrayValue { + ssv := new(stringArrayValue) + ssv.value = p + *ssv.value = val + return ssv +} + +func (s *stringArrayValue) Set(val string) error { + if !s.changed { + *s.value = []string{val} + s.changed = true + } else { + *s.value = append(*s.value, val) + } + return nil +} + +func (s *stringArrayValue) Append(val string) error { + *s.value = append(*s.value, val) + return nil +} + +func (s *stringArrayValue) Replace(val []string) error { + out := make([]string, len(val)) + for i, d := range val { + var err error + out[i] = d + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *stringArrayValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = d + } + return out +} + +func (s *stringArrayValue) Type() string { + return "stringArray" +} + +func (s *stringArrayValue) String() string { + str, _ := writeAsCSV(*s.value) + return "[" + str + "]" +} + +func stringArrayConv(sval string) (interface{}, error) { + sval = sval[1 : len(sval)-1] + // An empty string would cause a array with one (empty) string + if len(sval) == 0 { + return []string{}, nil + } + return readAsCSV(sval) +} + +// GetStringArray return the []string value of a flag with the given name +func (f *FlagSet) GetStringArray(name string) ([]string, error) { + val, err := f.getFlagType(name, "stringArray", stringArrayConv) + if err != nil { + return []string{}, err + } + return val.([]string), nil +} + +// StringArrayVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a []string variable in which to store the values of the multiple flags. +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. +func (f *FlagSet) StringArrayVar(p *[]string, name string, value []string, usage string) { + f.VarP(newStringArrayValue(value, p), name, "", usage) +} + +// StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) { + f.VarP(newStringArrayValue(value, p), name, shorthand, usage) +} + +// StringArrayVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a []string variable in which to store the value of the flag. +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. +func StringArrayVar(p *[]string, name string, value []string, usage string) { + CommandLine.VarP(newStringArrayValue(value, p), name, "", usage) +} + +// StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash. +func StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) { + CommandLine.VarP(newStringArrayValue(value, p), name, shorthand, usage) +} + +// StringArray defines a string flag with specified name, default value, and usage string. +// The return value is the address of a []string variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. +func (f *FlagSet) StringArray(name string, value []string, usage string) *[]string { + p := []string{} + f.StringArrayVarP(&p, name, "", value, usage) + return &p +} + +// StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringArrayP(name, shorthand string, value []string, usage string) *[]string { + p := []string{} + f.StringArrayVarP(&p, name, shorthand, value, usage) + return &p +} + +// StringArray defines a string flag with specified name, default value, and usage string. +// The return value is the address of a []string variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma. Use a StringSlice for that. +func StringArray(name string, value []string, usage string) *[]string { + return CommandLine.StringArrayP(name, "", value, usage) +} + +// StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash. +func StringArrayP(name, shorthand string, value []string, usage string) *[]string { + return CommandLine.StringArrayP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string_slice.go b/vendor/github.com/spf13/pflag/string_slice.go new file mode 100644 index 0000000..3cb2e69 --- /dev/null +++ b/vendor/github.com/spf13/pflag/string_slice.go @@ -0,0 +1,163 @@ +package pflag + +import ( + "bytes" + "encoding/csv" + "strings" +) + +// -- stringSlice Value +type stringSliceValue struct { + value *[]string + changed bool +} + +func newStringSliceValue(val []string, p *[]string) *stringSliceValue { + ssv := new(stringSliceValue) + ssv.value = p + *ssv.value = val + return ssv +} + +func readAsCSV(val string) ([]string, error) { + if val == "" { + return []string{}, nil + } + stringReader := strings.NewReader(val) + csvReader := csv.NewReader(stringReader) + return csvReader.Read() +} + +func writeAsCSV(vals []string) (string, error) { + b := &bytes.Buffer{} + w := csv.NewWriter(b) + err := w.Write(vals) + if err != nil { + return "", err + } + w.Flush() + return strings.TrimSuffix(b.String(), "\n"), nil +} + +func (s *stringSliceValue) Set(val string) error { + v, err := readAsCSV(val) + if err != nil { + return err + } + if !s.changed { + *s.value = v + } else { + *s.value = append(*s.value, v...) + } + s.changed = true + return nil +} + +func (s *stringSliceValue) Type() string { + return "stringSlice" +} + +func (s *stringSliceValue) String() string { + str, _ := writeAsCSV(*s.value) + return "[" + str + "]" +} + +func (s *stringSliceValue) Append(val string) error { + *s.value = append(*s.value, val) + return nil +} + +func (s *stringSliceValue) Replace(val []string) error { + *s.value = val + return nil +} + +func (s *stringSliceValue) GetSlice() []string { + return *s.value +} + +func stringSliceConv(sval string) (interface{}, error) { + sval = sval[1 : len(sval)-1] + // An empty string would cause a slice with one (empty) string + if len(sval) == 0 { + return []string{}, nil + } + return readAsCSV(sval) +} + +// GetStringSlice return the []string value of a flag with the given name +func (f *FlagSet) GetStringSlice(name string) ([]string, error) { + val, err := f.getFlagType(name, "stringSlice", stringSliceConv) + if err != nil { + return []string{}, err + } + return val.([]string), nil +} + +// StringSliceVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a []string variable in which to store the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" --ss="v3" +// will result in +// []string{"v1", "v2", "v3"} +func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) { + f.VarP(newStringSliceValue(value, p), name, "", usage) +} + +// StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) { + f.VarP(newStringSliceValue(value, p), name, shorthand, usage) +} + +// StringSliceVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a []string variable in which to store the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" --ss="v3" +// will result in +// []string{"v1", "v2", "v3"} +func StringSliceVar(p *[]string, name string, value []string, usage string) { + CommandLine.VarP(newStringSliceValue(value, p), name, "", usage) +} + +// StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash. +func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) { + CommandLine.VarP(newStringSliceValue(value, p), name, shorthand, usage) +} + +// StringSlice defines a string flag with specified name, default value, and usage string. +// The return value is the address of a []string variable that stores the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" --ss="v3" +// will result in +// []string{"v1", "v2", "v3"} +func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string { + p := []string{} + f.StringSliceVarP(&p, name, "", value, usage) + return &p +} + +// StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage string) *[]string { + p := []string{} + f.StringSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// StringSlice defines a string flag with specified name, default value, and usage string. +// The return value is the address of a []string variable that stores the value of the flag. +// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. +// For example: +// --ss="v1,v2" --ss="v3" +// will result in +// []string{"v1", "v2", "v3"} +func StringSlice(name string, value []string, usage string) *[]string { + return CommandLine.StringSliceP(name, "", value, usage) +} + +// StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash. +func StringSliceP(name, shorthand string, value []string, usage string) *[]string { + return CommandLine.StringSliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string_to_int.go b/vendor/github.com/spf13/pflag/string_to_int.go new file mode 100644 index 0000000..5ceda39 --- /dev/null +++ b/vendor/github.com/spf13/pflag/string_to_int.go @@ -0,0 +1,149 @@ +package pflag + +import ( + "bytes" + "fmt" + "strconv" + "strings" +) + +// -- stringToInt Value +type stringToIntValue struct { + value *map[string]int + changed bool +} + +func newStringToIntValue(val map[string]int, p *map[string]int) *stringToIntValue { + ssv := new(stringToIntValue) + ssv.value = p + *ssv.value = val + return ssv +} + +// Format: a=1,b=2 +func (s *stringToIntValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make(map[string]int, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return fmt.Errorf("%s must be formatted as key=value", pair) + } + var err error + out[kv[0]], err = strconv.Atoi(kv[1]) + if err != nil { + return err + } + } + if !s.changed { + *s.value = out + } else { + for k, v := range out { + (*s.value)[k] = v + } + } + s.changed = true + return nil +} + +func (s *stringToIntValue) Type() string { + return "stringToInt" +} + +func (s *stringToIntValue) String() string { + var buf bytes.Buffer + i := 0 + for k, v := range *s.value { + if i > 0 { + buf.WriteRune(',') + } + buf.WriteString(k) + buf.WriteRune('=') + buf.WriteString(strconv.Itoa(v)) + i++ + } + return "[" + buf.String() + "]" +} + +func stringToIntConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // An empty string would cause an empty map + if len(val) == 0 { + return map[string]int{}, nil + } + ss := strings.Split(val, ",") + out := make(map[string]int, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return nil, fmt.Errorf("%s must be formatted as key=value", pair) + } + var err error + out[kv[0]], err = strconv.Atoi(kv[1]) + if err != nil { + return nil, err + } + } + return out, nil +} + +// GetStringToInt return the map[string]int value of a flag with the given name +func (f *FlagSet) GetStringToInt(name string) (map[string]int, error) { + val, err := f.getFlagType(name, "stringToInt", stringToIntConv) + if err != nil { + return map[string]int{}, err + } + return val.(map[string]int), nil +} + +// StringToIntVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a map[string]int variable in which to store the values of the multiple flags. +// The value of each argument will not try to be separated by comma +func (f *FlagSet) StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) { + f.VarP(newStringToIntValue(value, p), name, "", usage) +} + +// StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) { + f.VarP(newStringToIntValue(value, p), name, shorthand, usage) +} + +// StringToIntVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a map[string]int variable in which to store the value of the flag. +// The value of each argument will not try to be separated by comma +func StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) { + CommandLine.VarP(newStringToIntValue(value, p), name, "", usage) +} + +// StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash. +func StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) { + CommandLine.VarP(newStringToIntValue(value, p), name, shorthand, usage) +} + +// StringToInt defines a string flag with specified name, default value, and usage string. +// The return value is the address of a map[string]int variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma +func (f *FlagSet) StringToInt(name string, value map[string]int, usage string) *map[string]int { + p := map[string]int{} + f.StringToIntVarP(&p, name, "", value, usage) + return &p +} + +// StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int { + p := map[string]int{} + f.StringToIntVarP(&p, name, shorthand, value, usage) + return &p +} + +// StringToInt defines a string flag with specified name, default value, and usage string. +// The return value is the address of a map[string]int variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma +func StringToInt(name string, value map[string]int, usage string) *map[string]int { + return CommandLine.StringToIntP(name, "", value, usage) +} + +// StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash. +func StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int { + return CommandLine.StringToIntP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string_to_int64.go b/vendor/github.com/spf13/pflag/string_to_int64.go new file mode 100644 index 0000000..a807a04 --- /dev/null +++ b/vendor/github.com/spf13/pflag/string_to_int64.go @@ -0,0 +1,149 @@ +package pflag + +import ( + "bytes" + "fmt" + "strconv" + "strings" +) + +// -- stringToInt64 Value +type stringToInt64Value struct { + value *map[string]int64 + changed bool +} + +func newStringToInt64Value(val map[string]int64, p *map[string]int64) *stringToInt64Value { + ssv := new(stringToInt64Value) + ssv.value = p + *ssv.value = val + return ssv +} + +// Format: a=1,b=2 +func (s *stringToInt64Value) Set(val string) error { + ss := strings.Split(val, ",") + out := make(map[string]int64, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return fmt.Errorf("%s must be formatted as key=value", pair) + } + var err error + out[kv[0]], err = strconv.ParseInt(kv[1], 10, 64) + if err != nil { + return err + } + } + if !s.changed { + *s.value = out + } else { + for k, v := range out { + (*s.value)[k] = v + } + } + s.changed = true + return nil +} + +func (s *stringToInt64Value) Type() string { + return "stringToInt64" +} + +func (s *stringToInt64Value) String() string { + var buf bytes.Buffer + i := 0 + for k, v := range *s.value { + if i > 0 { + buf.WriteRune(',') + } + buf.WriteString(k) + buf.WriteRune('=') + buf.WriteString(strconv.FormatInt(v, 10)) + i++ + } + return "[" + buf.String() + "]" +} + +func stringToInt64Conv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // An empty string would cause an empty map + if len(val) == 0 { + return map[string]int64{}, nil + } + ss := strings.Split(val, ",") + out := make(map[string]int64, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return nil, fmt.Errorf("%s must be formatted as key=value", pair) + } + var err error + out[kv[0]], err = strconv.ParseInt(kv[1], 10, 64) + if err != nil { + return nil, err + } + } + return out, nil +} + +// GetStringToInt64 return the map[string]int64 value of a flag with the given name +func (f *FlagSet) GetStringToInt64(name string) (map[string]int64, error) { + val, err := f.getFlagType(name, "stringToInt64", stringToInt64Conv) + if err != nil { + return map[string]int64{}, err + } + return val.(map[string]int64), nil +} + +// StringToInt64Var defines a string flag with specified name, default value, and usage string. +// The argument p point64s to a map[string]int64 variable in which to store the values of the multiple flags. +// The value of each argument will not try to be separated by comma +func (f *FlagSet) StringToInt64Var(p *map[string]int64, name string, value map[string]int64, usage string) { + f.VarP(newStringToInt64Value(value, p), name, "", usage) +} + +// StringToInt64VarP is like StringToInt64Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringToInt64VarP(p *map[string]int64, name, shorthand string, value map[string]int64, usage string) { + f.VarP(newStringToInt64Value(value, p), name, shorthand, usage) +} + +// StringToInt64Var defines a string flag with specified name, default value, and usage string. +// The argument p point64s to a map[string]int64 variable in which to store the value of the flag. +// The value of each argument will not try to be separated by comma +func StringToInt64Var(p *map[string]int64, name string, value map[string]int64, usage string) { + CommandLine.VarP(newStringToInt64Value(value, p), name, "", usage) +} + +// StringToInt64VarP is like StringToInt64Var, but accepts a shorthand letter that can be used after a single dash. +func StringToInt64VarP(p *map[string]int64, name, shorthand string, value map[string]int64, usage string) { + CommandLine.VarP(newStringToInt64Value(value, p), name, shorthand, usage) +} + +// StringToInt64 defines a string flag with specified name, default value, and usage string. +// The return value is the address of a map[string]int64 variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma +func (f *FlagSet) StringToInt64(name string, value map[string]int64, usage string) *map[string]int64 { + p := map[string]int64{} + f.StringToInt64VarP(&p, name, "", value, usage) + return &p +} + +// StringToInt64P is like StringToInt64, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringToInt64P(name, shorthand string, value map[string]int64, usage string) *map[string]int64 { + p := map[string]int64{} + f.StringToInt64VarP(&p, name, shorthand, value, usage) + return &p +} + +// StringToInt64 defines a string flag with specified name, default value, and usage string. +// The return value is the address of a map[string]int64 variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma +func StringToInt64(name string, value map[string]int64, usage string) *map[string]int64 { + return CommandLine.StringToInt64P(name, "", value, usage) +} + +// StringToInt64P is like StringToInt64, but accepts a shorthand letter that can be used after a single dash. +func StringToInt64P(name, shorthand string, value map[string]int64, usage string) *map[string]int64 { + return CommandLine.StringToInt64P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string_to_string.go b/vendor/github.com/spf13/pflag/string_to_string.go new file mode 100644 index 0000000..890a01a --- /dev/null +++ b/vendor/github.com/spf13/pflag/string_to_string.go @@ -0,0 +1,160 @@ +package pflag + +import ( + "bytes" + "encoding/csv" + "fmt" + "strings" +) + +// -- stringToString Value +type stringToStringValue struct { + value *map[string]string + changed bool +} + +func newStringToStringValue(val map[string]string, p *map[string]string) *stringToStringValue { + ssv := new(stringToStringValue) + ssv.value = p + *ssv.value = val + return ssv +} + +// Format: a=1,b=2 +func (s *stringToStringValue) Set(val string) error { + var ss []string + n := strings.Count(val, "=") + switch n { + case 0: + return fmt.Errorf("%s must be formatted as key=value", val) + case 1: + ss = append(ss, strings.Trim(val, `"`)) + default: + r := csv.NewReader(strings.NewReader(val)) + var err error + ss, err = r.Read() + if err != nil { + return err + } + } + + out := make(map[string]string, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return fmt.Errorf("%s must be formatted as key=value", pair) + } + out[kv[0]] = kv[1] + } + if !s.changed { + *s.value = out + } else { + for k, v := range out { + (*s.value)[k] = v + } + } + s.changed = true + return nil +} + +func (s *stringToStringValue) Type() string { + return "stringToString" +} + +func (s *stringToStringValue) String() string { + records := make([]string, 0, len(*s.value)>>1) + for k, v := range *s.value { + records = append(records, k+"="+v) + } + + var buf bytes.Buffer + w := csv.NewWriter(&buf) + if err := w.Write(records); err != nil { + panic(err) + } + w.Flush() + return "[" + strings.TrimSpace(buf.String()) + "]" +} + +func stringToStringConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // An empty string would cause an empty map + if len(val) == 0 { + return map[string]string{}, nil + } + r := csv.NewReader(strings.NewReader(val)) + ss, err := r.Read() + if err != nil { + return nil, err + } + out := make(map[string]string, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return nil, fmt.Errorf("%s must be formatted as key=value", pair) + } + out[kv[0]] = kv[1] + } + return out, nil +} + +// GetStringToString return the map[string]string value of a flag with the given name +func (f *FlagSet) GetStringToString(name string) (map[string]string, error) { + val, err := f.getFlagType(name, "stringToString", stringToStringConv) + if err != nil { + return map[string]string{}, err + } + return val.(map[string]string), nil +} + +// StringToStringVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a map[string]string variable in which to store the values of the multiple flags. +// The value of each argument will not try to be separated by comma +func (f *FlagSet) StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) { + f.VarP(newStringToStringValue(value, p), name, "", usage) +} + +// StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) { + f.VarP(newStringToStringValue(value, p), name, shorthand, usage) +} + +// StringToStringVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a map[string]string variable in which to store the value of the flag. +// The value of each argument will not try to be separated by comma +func StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) { + CommandLine.VarP(newStringToStringValue(value, p), name, "", usage) +} + +// StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash. +func StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) { + CommandLine.VarP(newStringToStringValue(value, p), name, shorthand, usage) +} + +// StringToString defines a string flag with specified name, default value, and usage string. +// The return value is the address of a map[string]string variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma +func (f *FlagSet) StringToString(name string, value map[string]string, usage string) *map[string]string { + p := map[string]string{} + f.StringToStringVarP(&p, name, "", value, usage) + return &p +} + +// StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string { + p := map[string]string{} + f.StringToStringVarP(&p, name, shorthand, value, usage) + return &p +} + +// StringToString defines a string flag with specified name, default value, and usage string. +// The return value is the address of a map[string]string variable that stores the value of the flag. +// The value of each argument will not try to be separated by comma +func StringToString(name string, value map[string]string, usage string) *map[string]string { + return CommandLine.StringToStringP(name, "", value, usage) +} + +// StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash. +func StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string { + return CommandLine.StringToStringP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/uint.go b/vendor/github.com/spf13/pflag/uint.go new file mode 100644 index 0000000..dcbc2b7 --- /dev/null +++ b/vendor/github.com/spf13/pflag/uint.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- uint Value +type uintValue uint + +func newUintValue(val uint, p *uint) *uintValue { + *p = val + return (*uintValue)(p) +} + +func (i *uintValue) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 64) + *i = uintValue(v) + return err +} + +func (i *uintValue) Type() string { + return "uint" +} + +func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i), 10) } + +func uintConv(sval string) (interface{}, error) { + v, err := strconv.ParseUint(sval, 0, 0) + if err != nil { + return 0, err + } + return uint(v), nil +} + +// GetUint return the uint value of a flag with the given name +func (f *FlagSet) GetUint(name string) (uint, error) { + val, err := f.getFlagType(name, "uint", uintConv) + if err != nil { + return 0, err + } + return val.(uint), nil +} + +// UintVar defines a uint flag with specified name, default value, and usage string. +// The argument p points to a uint variable in which to store the value of the flag. +func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { + f.VarP(newUintValue(value, p), name, "", usage) +} + +// UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) UintVarP(p *uint, name, shorthand string, value uint, usage string) { + f.VarP(newUintValue(value, p), name, shorthand, usage) +} + +// UintVar defines a uint flag with specified name, default value, and usage string. +// The argument p points to a uint variable in which to store the value of the flag. +func UintVar(p *uint, name string, value uint, usage string) { + CommandLine.VarP(newUintValue(value, p), name, "", usage) +} + +// UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash. +func UintVarP(p *uint, name, shorthand string, value uint, usage string) { + CommandLine.VarP(newUintValue(value, p), name, shorthand, usage) +} + +// Uint defines a uint flag with specified name, default value, and usage string. +// The return value is the address of a uint variable that stores the value of the flag. +func (f *FlagSet) Uint(name string, value uint, usage string) *uint { + p := new(uint) + f.UintVarP(p, name, "", value, usage) + return p +} + +// UintP is like Uint, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) UintP(name, shorthand string, value uint, usage string) *uint { + p := new(uint) + f.UintVarP(p, name, shorthand, value, usage) + return p +} + +// Uint defines a uint flag with specified name, default value, and usage string. +// The return value is the address of a uint variable that stores the value of the flag. +func Uint(name string, value uint, usage string) *uint { + return CommandLine.UintP(name, "", value, usage) +} + +// UintP is like Uint, but accepts a shorthand letter that can be used after a single dash. +func UintP(name, shorthand string, value uint, usage string) *uint { + return CommandLine.UintP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/uint16.go b/vendor/github.com/spf13/pflag/uint16.go new file mode 100644 index 0000000..7e9914e --- /dev/null +++ b/vendor/github.com/spf13/pflag/uint16.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- uint16 value +type uint16Value uint16 + +func newUint16Value(val uint16, p *uint16) *uint16Value { + *p = val + return (*uint16Value)(p) +} + +func (i *uint16Value) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 16) + *i = uint16Value(v) + return err +} + +func (i *uint16Value) Type() string { + return "uint16" +} + +func (i *uint16Value) String() string { return strconv.FormatUint(uint64(*i), 10) } + +func uint16Conv(sval string) (interface{}, error) { + v, err := strconv.ParseUint(sval, 0, 16) + if err != nil { + return 0, err + } + return uint16(v), nil +} + +// GetUint16 return the uint16 value of a flag with the given name +func (f *FlagSet) GetUint16(name string) (uint16, error) { + val, err := f.getFlagType(name, "uint16", uint16Conv) + if err != nil { + return 0, err + } + return val.(uint16), nil +} + +// Uint16Var defines a uint flag with specified name, default value, and usage string. +// The argument p points to a uint variable in which to store the value of the flag. +func (f *FlagSet) Uint16Var(p *uint16, name string, value uint16, usage string) { + f.VarP(newUint16Value(value, p), name, "", usage) +} + +// Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) { + f.VarP(newUint16Value(value, p), name, shorthand, usage) +} + +// Uint16Var defines a uint flag with specified name, default value, and usage string. +// The argument p points to a uint variable in which to store the value of the flag. +func Uint16Var(p *uint16, name string, value uint16, usage string) { + CommandLine.VarP(newUint16Value(value, p), name, "", usage) +} + +// Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash. +func Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) { + CommandLine.VarP(newUint16Value(value, p), name, shorthand, usage) +} + +// Uint16 defines a uint flag with specified name, default value, and usage string. +// The return value is the address of a uint variable that stores the value of the flag. +func (f *FlagSet) Uint16(name string, value uint16, usage string) *uint16 { + p := new(uint16) + f.Uint16VarP(p, name, "", value, usage) + return p +} + +// Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint16P(name, shorthand string, value uint16, usage string) *uint16 { + p := new(uint16) + f.Uint16VarP(p, name, shorthand, value, usage) + return p +} + +// Uint16 defines a uint flag with specified name, default value, and usage string. +// The return value is the address of a uint variable that stores the value of the flag. +func Uint16(name string, value uint16, usage string) *uint16 { + return CommandLine.Uint16P(name, "", value, usage) +} + +// Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash. +func Uint16P(name, shorthand string, value uint16, usage string) *uint16 { + return CommandLine.Uint16P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/uint32.go b/vendor/github.com/spf13/pflag/uint32.go new file mode 100644 index 0000000..d802453 --- /dev/null +++ b/vendor/github.com/spf13/pflag/uint32.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- uint32 value +type uint32Value uint32 + +func newUint32Value(val uint32, p *uint32) *uint32Value { + *p = val + return (*uint32Value)(p) +} + +func (i *uint32Value) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 32) + *i = uint32Value(v) + return err +} + +func (i *uint32Value) Type() string { + return "uint32" +} + +func (i *uint32Value) String() string { return strconv.FormatUint(uint64(*i), 10) } + +func uint32Conv(sval string) (interface{}, error) { + v, err := strconv.ParseUint(sval, 0, 32) + if err != nil { + return 0, err + } + return uint32(v), nil +} + +// GetUint32 return the uint32 value of a flag with the given name +func (f *FlagSet) GetUint32(name string) (uint32, error) { + val, err := f.getFlagType(name, "uint32", uint32Conv) + if err != nil { + return 0, err + } + return val.(uint32), nil +} + +// Uint32Var defines a uint32 flag with specified name, default value, and usage string. +// The argument p points to a uint32 variable in which to store the value of the flag. +func (f *FlagSet) Uint32Var(p *uint32, name string, value uint32, usage string) { + f.VarP(newUint32Value(value, p), name, "", usage) +} + +// Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) { + f.VarP(newUint32Value(value, p), name, shorthand, usage) +} + +// Uint32Var defines a uint32 flag with specified name, default value, and usage string. +// The argument p points to a uint32 variable in which to store the value of the flag. +func Uint32Var(p *uint32, name string, value uint32, usage string) { + CommandLine.VarP(newUint32Value(value, p), name, "", usage) +} + +// Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash. +func Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) { + CommandLine.VarP(newUint32Value(value, p), name, shorthand, usage) +} + +// Uint32 defines a uint32 flag with specified name, default value, and usage string. +// The return value is the address of a uint32 variable that stores the value of the flag. +func (f *FlagSet) Uint32(name string, value uint32, usage string) *uint32 { + p := new(uint32) + f.Uint32VarP(p, name, "", value, usage) + return p +} + +// Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint32P(name, shorthand string, value uint32, usage string) *uint32 { + p := new(uint32) + f.Uint32VarP(p, name, shorthand, value, usage) + return p +} + +// Uint32 defines a uint32 flag with specified name, default value, and usage string. +// The return value is the address of a uint32 variable that stores the value of the flag. +func Uint32(name string, value uint32, usage string) *uint32 { + return CommandLine.Uint32P(name, "", value, usage) +} + +// Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash. +func Uint32P(name, shorthand string, value uint32, usage string) *uint32 { + return CommandLine.Uint32P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/uint64.go b/vendor/github.com/spf13/pflag/uint64.go new file mode 100644 index 0000000..f62240f --- /dev/null +++ b/vendor/github.com/spf13/pflag/uint64.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- uint64 Value +type uint64Value uint64 + +func newUint64Value(val uint64, p *uint64) *uint64Value { + *p = val + return (*uint64Value)(p) +} + +func (i *uint64Value) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 64) + *i = uint64Value(v) + return err +} + +func (i *uint64Value) Type() string { + return "uint64" +} + +func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i), 10) } + +func uint64Conv(sval string) (interface{}, error) { + v, err := strconv.ParseUint(sval, 0, 64) + if err != nil { + return 0, err + } + return uint64(v), nil +} + +// GetUint64 return the uint64 value of a flag with the given name +func (f *FlagSet) GetUint64(name string) (uint64, error) { + val, err := f.getFlagType(name, "uint64", uint64Conv) + if err != nil { + return 0, err + } + return val.(uint64), nil +} + +// Uint64Var defines a uint64 flag with specified name, default value, and usage string. +// The argument p points to a uint64 variable in which to store the value of the flag. +func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) { + f.VarP(newUint64Value(value, p), name, "", usage) +} + +// Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) { + f.VarP(newUint64Value(value, p), name, shorthand, usage) +} + +// Uint64Var defines a uint64 flag with specified name, default value, and usage string. +// The argument p points to a uint64 variable in which to store the value of the flag. +func Uint64Var(p *uint64, name string, value uint64, usage string) { + CommandLine.VarP(newUint64Value(value, p), name, "", usage) +} + +// Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash. +func Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) { + CommandLine.VarP(newUint64Value(value, p), name, shorthand, usage) +} + +// Uint64 defines a uint64 flag with specified name, default value, and usage string. +// The return value is the address of a uint64 variable that stores the value of the flag. +func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { + p := new(uint64) + f.Uint64VarP(p, name, "", value, usage) + return p +} + +// Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint64P(name, shorthand string, value uint64, usage string) *uint64 { + p := new(uint64) + f.Uint64VarP(p, name, shorthand, value, usage) + return p +} + +// Uint64 defines a uint64 flag with specified name, default value, and usage string. +// The return value is the address of a uint64 variable that stores the value of the flag. +func Uint64(name string, value uint64, usage string) *uint64 { + return CommandLine.Uint64P(name, "", value, usage) +} + +// Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash. +func Uint64P(name, shorthand string, value uint64, usage string) *uint64 { + return CommandLine.Uint64P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/uint8.go b/vendor/github.com/spf13/pflag/uint8.go new file mode 100644 index 0000000..bb0e83c --- /dev/null +++ b/vendor/github.com/spf13/pflag/uint8.go @@ -0,0 +1,88 @@ +package pflag + +import "strconv" + +// -- uint8 Value +type uint8Value uint8 + +func newUint8Value(val uint8, p *uint8) *uint8Value { + *p = val + return (*uint8Value)(p) +} + +func (i *uint8Value) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 8) + *i = uint8Value(v) + return err +} + +func (i *uint8Value) Type() string { + return "uint8" +} + +func (i *uint8Value) String() string { return strconv.FormatUint(uint64(*i), 10) } + +func uint8Conv(sval string) (interface{}, error) { + v, err := strconv.ParseUint(sval, 0, 8) + if err != nil { + return 0, err + } + return uint8(v), nil +} + +// GetUint8 return the uint8 value of a flag with the given name +func (f *FlagSet) GetUint8(name string) (uint8, error) { + val, err := f.getFlagType(name, "uint8", uint8Conv) + if err != nil { + return 0, err + } + return val.(uint8), nil +} + +// Uint8Var defines a uint8 flag with specified name, default value, and usage string. +// The argument p points to a uint8 variable in which to store the value of the flag. +func (f *FlagSet) Uint8Var(p *uint8, name string, value uint8, usage string) { + f.VarP(newUint8Value(value, p), name, "", usage) +} + +// Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) { + f.VarP(newUint8Value(value, p), name, shorthand, usage) +} + +// Uint8Var defines a uint8 flag with specified name, default value, and usage string. +// The argument p points to a uint8 variable in which to store the value of the flag. +func Uint8Var(p *uint8, name string, value uint8, usage string) { + CommandLine.VarP(newUint8Value(value, p), name, "", usage) +} + +// Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash. +func Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) { + CommandLine.VarP(newUint8Value(value, p), name, shorthand, usage) +} + +// Uint8 defines a uint8 flag with specified name, default value, and usage string. +// The return value is the address of a uint8 variable that stores the value of the flag. +func (f *FlagSet) Uint8(name string, value uint8, usage string) *uint8 { + p := new(uint8) + f.Uint8VarP(p, name, "", value, usage) + return p +} + +// Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) Uint8P(name, shorthand string, value uint8, usage string) *uint8 { + p := new(uint8) + f.Uint8VarP(p, name, shorthand, value, usage) + return p +} + +// Uint8 defines a uint8 flag with specified name, default value, and usage string. +// The return value is the address of a uint8 variable that stores the value of the flag. +func Uint8(name string, value uint8, usage string) *uint8 { + return CommandLine.Uint8P(name, "", value, usage) +} + +// Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash. +func Uint8P(name, shorthand string, value uint8, usage string) *uint8 { + return CommandLine.Uint8P(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/uint_slice.go b/vendor/github.com/spf13/pflag/uint_slice.go new file mode 100644 index 0000000..5fa9248 --- /dev/null +++ b/vendor/github.com/spf13/pflag/uint_slice.go @@ -0,0 +1,168 @@ +package pflag + +import ( + "fmt" + "strconv" + "strings" +) + +// -- uintSlice Value +type uintSliceValue struct { + value *[]uint + changed bool +} + +func newUintSliceValue(val []uint, p *[]uint) *uintSliceValue { + uisv := new(uintSliceValue) + uisv.value = p + *uisv.value = val + return uisv +} + +func (s *uintSliceValue) Set(val string) error { + ss := strings.Split(val, ",") + out := make([]uint, len(ss)) + for i, d := range ss { + u, err := strconv.ParseUint(d, 10, 0) + if err != nil { + return err + } + out[i] = uint(u) + } + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + s.changed = true + return nil +} + +func (s *uintSliceValue) Type() string { + return "uintSlice" +} + +func (s *uintSliceValue) String() string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = fmt.Sprintf("%d", d) + } + return "[" + strings.Join(out, ",") + "]" +} + +func (s *uintSliceValue) fromString(val string) (uint, error) { + t, err := strconv.ParseUint(val, 10, 0) + if err != nil { + return 0, err + } + return uint(t), nil +} + +func (s *uintSliceValue) toString(val uint) string { + return fmt.Sprintf("%d", val) +} + +func (s *uintSliceValue) Append(val string) error { + i, err := s.fromString(val) + if err != nil { + return err + } + *s.value = append(*s.value, i) + return nil +} + +func (s *uintSliceValue) Replace(val []string) error { + out := make([]uint, len(val)) + for i, d := range val { + var err error + out[i], err = s.fromString(d) + if err != nil { + return err + } + } + *s.value = out + return nil +} + +func (s *uintSliceValue) GetSlice() []string { + out := make([]string, len(*s.value)) + for i, d := range *s.value { + out[i] = s.toString(d) + } + return out +} + +func uintSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []uint{}, nil + } + ss := strings.Split(val, ",") + out := make([]uint, len(ss)) + for i, d := range ss { + u, err := strconv.ParseUint(d, 10, 0) + if err != nil { + return nil, err + } + out[i] = uint(u) + } + return out, nil +} + +// GetUintSlice returns the []uint value of a flag with the given name. +func (f *FlagSet) GetUintSlice(name string) ([]uint, error) { + val, err := f.getFlagType(name, "uintSlice", uintSliceConv) + if err != nil { + return []uint{}, err + } + return val.([]uint), nil +} + +// UintSliceVar defines a uintSlice flag with specified name, default value, and usage string. +// The argument p points to a []uint variable in which to store the value of the flag. +func (f *FlagSet) UintSliceVar(p *[]uint, name string, value []uint, usage string) { + f.VarP(newUintSliceValue(value, p), name, "", usage) +} + +// UintSliceVarP is like UintSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) { + f.VarP(newUintSliceValue(value, p), name, shorthand, usage) +} + +// UintSliceVar defines a uint[] flag with specified name, default value, and usage string. +// The argument p points to a uint[] variable in which to store the value of the flag. +func UintSliceVar(p *[]uint, name string, value []uint, usage string) { + CommandLine.VarP(newUintSliceValue(value, p), name, "", usage) +} + +// UintSliceVarP is like the UintSliceVar, but accepts a shorthand letter that can be used after a single dash. +func UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) { + CommandLine.VarP(newUintSliceValue(value, p), name, shorthand, usage) +} + +// UintSlice defines a []uint flag with specified name, default value, and usage string. +// The return value is the address of a []uint variable that stores the value of the flag. +func (f *FlagSet) UintSlice(name string, value []uint, usage string) *[]uint { + p := []uint{} + f.UintSliceVarP(&p, name, "", value, usage) + return &p +} + +// UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) UintSliceP(name, shorthand string, value []uint, usage string) *[]uint { + p := []uint{} + f.UintSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// UintSlice defines a []uint flag with specified name, default value, and usage string. +// The return value is the address of a []uint variable that stores the value of the flag. +func UintSlice(name string, value []uint, usage string) *[]uint { + return CommandLine.UintSliceP(name, "", value, usage) +} + +// UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash. +func UintSliceP(name, shorthand string, value []uint, usage string) *[]uint { + return CommandLine.UintSliceP(name, shorthand, value, usage) +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/.gitignore b/vendor/gopkg.in/go-playground/validator.v9/.gitignore new file mode 100644 index 0000000..792ca00 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/.gitignore @@ -0,0 +1,29 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.test +*.out +*.txt +cover.html +README.html \ No newline at end of file diff --git a/vendor/gopkg.in/go-playground/validator.v9/LICENSE b/vendor/gopkg.in/go-playground/validator.v9/LICENSE new file mode 100644 index 0000000..6a2ae9a --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Dean Karn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/gopkg.in/go-playground/validator.v9/Makefile b/vendor/gopkg.in/go-playground/validator.v9/Makefile new file mode 100644 index 0000000..aeeee9d --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/Makefile @@ -0,0 +1,16 @@ +GOCMD=go + +linters-install: + $(GOCMD) get -u github.com/alecthomas/gometalinter + gometalinter --install + +lint: linters-install + gometalinter --vendor --disable-all --enable=vet --enable=vetshadow --enable=golint --enable=maligned --enable=megacheck --enable=ineffassign --enable=misspell --enable=errcheck --enable=goconst ./... + +test: + $(GOCMD) test -cover -race ./... + +bench: + $(GOCMD) test -bench=. -benchmem ./... + +.PHONY: test lint linters-install \ No newline at end of file diff --git a/vendor/gopkg.in/go-playground/validator.v9/README.md b/vendor/gopkg.in/go-playground/validator.v9/README.md new file mode 100644 index 0000000..74a8156 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/README.md @@ -0,0 +1,153 @@ +Package validator +================ +[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +![Project status](https://img.shields.io/badge/version-9.17.1-green.svg) +[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator) +[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) +[![GoDoc](https://godoc.org/gopkg.in/go-playground/validator.v9?status.svg)](https://godoc.org/gopkg.in/go-playground/validator.v9) +![License](https://img.shields.io/dub/l/vibe-d.svg) + +Package validator implements value validations for structs and individual fields based on tags. + +It has the following **unique** features: + +- Cross Field and Cross Struct validations by using validation tags or custom validators. +- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated. +- Ability to dive into both map keys and values for validation +- Handles type interface by determining it's underlying type prior to validation. +- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29) +- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs +- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError +- Customizable i18n aware error messages. +- Default validator for the [gin](https://github.com/gin-gonic/gin) web framework; upgrading from v8 to v9 in gin see [here](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding) + +Installation +------------ + +Use go get. + + go get gopkg.in/go-playground/validator.v9 + +Then import the validator package into your own code. + + import "gopkg.in/go-playground/validator.v9" + +Error Return Value +------- + +Validation functions return type error + +They return type error to avoid the issue discussed in the following, where err is always != nil: + +* http://stackoverflow.com/a/29138676/3158232 +* https://github.com/go-playground/validator/issues/134 + +Validator only InvalidValidationError for bad validation input, nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not check if error is InvalidValidationError ( if necessary, most of the time it isn't ) type cast it to type ValidationErrors like so: + +```go +err := validate.Struct(mystruct) +validationErrors := err.(validator.ValidationErrors) + ``` + +Usage and documentation +------ + +Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usage docs. + +##### Examples: + +- [Simple](https://github.com/go-playground/validator/blob/v9/_examples/simple/main.go) +- [Custom Field Types](https://github.com/go-playground/validator/blob/v9/_examples/custom/main.go) +- [Struct Level](https://github.com/go-playground/validator/blob/v9/_examples/struct-level/main.go) +- [Translations & Custom Errors](https://github.com/go-playground/validator/blob/v9/_examples/translations/main.go) +- [Gin upgrade and/or override validator](https://github.com/go-playground/validator/tree/v9/_examples/gin-upgrading-overriding) +- [wash - an example application putting it all together](https://github.com/bluesuncorp/wash) + +Benchmarks +------ +###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64 +```go +goos: darwin +goarch: amd64 +pkg: github.com/go-playground/validator +BenchmarkFieldSuccess-8 20000000 83.6 ns/op 0 B/op 0 allocs/op +BenchmarkFieldSuccessParallel-8 50000000 26.8 ns/op 0 B/op 0 allocs/op +BenchmarkFieldFailure-8 5000000 291 ns/op 208 B/op 4 allocs/op +BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op +BenchmarkFieldArrayDiveSuccess-8 2000000 623 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveSuccessParallel-8 10000000 237 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveFailure-8 2000000 859 ns/op 412 B/op 16 allocs/op +BenchmarkFieldArrayDiveFailureParallel-8 5000000 335 ns/op 413 B/op 16 allocs/op +BenchmarkFieldMapDiveSuccess-8 1000000 1292 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveSuccessParallel-8 3000000 467 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveFailure-8 1000000 1082 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveFailureParallel-8 5000000 425 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1539 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 613 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1413 ns/op 721 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 575 ns/op 721 B/op 21 allocs/op +BenchmarkFieldCustomTypeSuccess-8 10000000 216 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.2 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeFailure-8 5000000 274 ns/op 208 B/op 4 allocs/op +BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op +BenchmarkFieldOrTagSuccess-8 2000000 740 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagSuccessParallel-8 3000000 474 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagFailure-8 3000000 471 ns/op 224 B/op 5 allocs/op +BenchmarkFieldOrTagFailureParallel-8 3000000 414 ns/op 224 B/op 5 allocs/op +BenchmarkStructLevelValidationSuccess-8 10000000 213 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationSuccessParallel-8 20000000 91.8 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationFailure-8 3000000 473 ns/op 304 B/op 8 allocs/op +BenchmarkStructLevelValidationFailureParallel-8 10000000 234 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCustomTypeSuccess-8 5000000 385 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 161 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeFailure-8 2000000 640 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 318 ns/op 440 B/op 10 allocs/op +BenchmarkStructFilteredSuccess-8 2000000 597 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredSuccessParallel-8 10000000 266 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredFailure-8 3000000 454 ns/op 256 B/op 7 allocs/op +BenchmarkStructFilteredFailureParallel-8 10000000 214 ns/op 256 B/op 7 allocs/op +BenchmarkStructPartialSuccess-8 3000000 502 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialSuccessParallel-8 10000000 225 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialFailure-8 2000000 702 ns/op 480 B/op 11 allocs/op +BenchmarkStructPartialFailureParallel-8 5000000 329 ns/op 480 B/op 11 allocs/op +BenchmarkStructExceptSuccess-8 2000000 793 ns/op 496 B/op 12 allocs/op +BenchmarkStructExceptSuccessParallel-8 10000000 193 ns/op 240 B/op 5 allocs/op +BenchmarkStructExceptFailure-8 2000000 639 ns/op 464 B/op 10 allocs/op +BenchmarkStructExceptFailureParallel-8 5000000 300 ns/op 464 B/op 10 allocs/op +BenchmarkStructSimpleCrossFieldSuccess-8 3000000 417 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 163 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldFailure-8 2000000 645 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 285 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 588 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 221 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 868 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 337 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleSuccess-8 5000000 260 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleSuccessParallel-8 20000000 90.6 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleFailure-8 2000000 619 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleFailureParallel-8 5000000 296 ns/op 424 B/op 9 allocs/op +BenchmarkStructComplexSuccess-8 1000000 1454 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexSuccessParallel-8 3000000 579 ns/op 128 B/op 8 allocs/op +BenchmarkStructComplexFailure-8 300000 4140 ns/op 3041 B/op 53 allocs/op +BenchmarkStructComplexFailureParallel-8 1000000 2127 ns/op 3041 B/op 53 allocs/op +BenchmarkOneof-8 10000000 140 ns/op 0 B/op 0 allocs/op +BenchmarkOneofParallel-8 20000000 70.1 ns/op 0 B/op 0 allocs/op +``` + +Complementary Software +---------------------- + +Here is a list of software that complements using this library either pre or post validation. + +* [form](https://github.com/go-playground/form) - Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support. +* [mold](https://github.com/go-playground/mold) - A general library to help modify or set data within data structures and other objects + +How to Contribute +------ + +Make a pull request... + +License +------ +Distributed under MIT License, please see license file within the code for more details. diff --git a/vendor/gopkg.in/go-playground/validator.v9/baked_in.go b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go new file mode 100644 index 0000000..3f87da4 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/baked_in.go @@ -0,0 +1,1741 @@ +package validator + +import ( + "bytes" + "context" + "crypto/sha256" + "fmt" + "net" + "net/url" + "reflect" + "strconv" + "strings" + "sync" + "time" + "unicode/utf8" +) + +// Func accepts a FieldLevel interface for all validation needs. The return +// value should be true when validation succeeds. +type Func func(fl FieldLevel) bool + +// FuncCtx accepts a context.Context and FieldLevel interface for all +// validation needs. The return value should be true when validation succeeds. +type FuncCtx func(ctx context.Context, fl FieldLevel) bool + +// wrapFunc wraps noramal Func makes it compatible with FuncCtx +func wrapFunc(fn Func) FuncCtx { + if fn == nil { + return nil // be sure not to wrap a bad function. + } + return func(ctx context.Context, fl FieldLevel) bool { + return fn(fl) + } +} + +var ( + restrictedTags = map[string]struct{}{ + diveTag: {}, + keysTag: {}, + endKeysTag: {}, + structOnlyTag: {}, + omitempty: {}, + skipValidationTag: {}, + utf8HexComma: {}, + utf8Pipe: {}, + noStructLevelTag: {}, + requiredTag: {}, + isdefault: {}, + } + + // BakedInAliasValidators is a default mapping of a single validation tag that + // defines a common or complex set of validation(s) to simplify + // adding validation to structs. + bakedInAliases = map[string]string{ + "iscolor": "hexcolor|rgb|rgba|hsl|hsla", + } + + // BakedInValidators is the default map of ValidationFunc + // you can add, remove or even replace items to suite your needs, + // or even disregard and use your own map if so desired. + bakedInValidators = map[string]Func{ + "required": hasValue, + "isdefault": isDefault, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + "eq": isEq, + "ne": isNe, + "lt": isLt, + "lte": isLte, + "gt": isGt, + "gte": isGte, + "eqfield": isEqField, + "eqcsfield": isEqCrossStructField, + "necsfield": isNeCrossStructField, + "gtcsfield": isGtCrossStructField, + "gtecsfield": isGteCrossStructField, + "ltcsfield": isLtCrossStructField, + "ltecsfield": isLteCrossStructField, + "nefield": isNeField, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, + "alpha": isAlpha, + "alphanum": isAlphanum, + "alphaunicode": isAlphaUnicode, + "alphanumunicode": isAlphanumUnicode, + "numeric": isNumeric, + "number": isNumber, + "hexadecimal": isHexadecimal, + "hexcolor": isHEXColor, + "rgb": isRGB, + "rgba": isRGBA, + "hsl": isHSL, + "hsla": isHSLA, + "email": isEmail, + "url": isURL, + "uri": isURI, + "base64": isBase64, + "base64url": isBase64URL, + "contains": contains, + "containsany": containsAny, + "containsrune": containsRune, + "excludes": excludes, + "excludesall": excludesAll, + "excludesrune": excludesRune, + "isbn": isISBN, + "isbn10": isISBN10, + "isbn13": isISBN13, + "eth_addr": isEthereumAddress, + "btc_addr": isBitcoinAddress, + "btc_addr_bech32": isBitcoinBech32Address, + "uuid": isUUID, + "uuid3": isUUID3, + "uuid4": isUUID4, + "uuid5": isUUID5, + "ascii": isASCII, + "printascii": isPrintableASCII, + "multibyte": hasMultiByteCharacter, + "datauri": isDataURI, + "latitude": isLatitude, + "longitude": isLongitude, + "ssn": isSSN, + "ipv4": isIPv4, + "ipv6": isIPv6, + "ip": isIP, + "cidrv4": isCIDRv4, + "cidrv6": isCIDRv6, + "cidr": isCIDR, + "tcp4_addr": isTCP4AddrResolvable, + "tcp6_addr": isTCP6AddrResolvable, + "tcp_addr": isTCPAddrResolvable, + "udp4_addr": isUDP4AddrResolvable, + "udp6_addr": isUDP6AddrResolvable, + "udp_addr": isUDPAddrResolvable, + "ip4_addr": isIP4AddrResolvable, + "ip6_addr": isIP6AddrResolvable, + "ip_addr": isIPAddrResolvable, + "unix_addr": isUnixAddrResolvable, + "mac": isMAC, + "hostname": isHostnameRFC952, // RFC 952 + "hostname_rfc1123": isHostnameRFC1123, // RFC 1123 + "fqdn": isFQDN, + "unique": isUnique, + "oneof": isOneOf, + } +) + +var oneofValsCache = map[string][]string{} +var oneofValsCacheRWLock = sync.RWMutex{} + +func parseOneOfParam2(s string) []string { + oneofValsCacheRWLock.RLock() + vals, ok := oneofValsCache[s] + oneofValsCacheRWLock.RUnlock() + if !ok { + oneofValsCacheRWLock.Lock() + vals = strings.Fields(s) + oneofValsCache[s] = vals + oneofValsCacheRWLock.Unlock() + } + return vals +} + +func isOneOf(fl FieldLevel) bool { + vals := parseOneOfParam2(fl.Param()) + + field := fl.Field() + + var v string + switch field.Kind() { + case reflect.String: + v = field.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v = strconv.FormatInt(field.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v = strconv.FormatUint(field.Uint(), 10) + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } + for i := 0; i < len(vals); i++ { + if vals[i] == v { + return true + } + } + return false +} + +// isUnique is the validation function for validating if each array|slice|map value is unique +func isUnique(fl FieldLevel) bool { + + field := fl.Field() + v := reflect.ValueOf(struct{}{}) + + switch field.Kind() { + case reflect.Slice, reflect.Array: + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + + for i := 0; i < field.Len(); i++ { + m.SetMapIndex(field.Index(i), v) + } + return field.Len() == m.Len() + case reflect.Map: + m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) + + for _, k := range field.MapKeys() { + m.SetMapIndex(field.MapIndex(k), v) + } + return field.Len() == m.Len() + default: + panic(fmt.Sprintf("Bad field type %T", field.Interface())) + } +} + +// IsMAC is the validation function for validating if the field's value is a valid MAC address. +func isMAC(fl FieldLevel) bool { + + _, err := net.ParseMAC(fl.Field().String()) + + return err == nil +} + +// IsCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address. +func isCIDRv4(fl FieldLevel) bool { + + ip, _, err := net.ParseCIDR(fl.Field().String()) + + return err == nil && ip.To4() != nil +} + +// IsCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address. +func isCIDRv6(fl FieldLevel) bool { + + ip, _, err := net.ParseCIDR(fl.Field().String()) + + return err == nil && ip.To4() == nil +} + +// IsCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address. +func isCIDR(fl FieldLevel) bool { + + _, _, err := net.ParseCIDR(fl.Field().String()) + + return err == nil +} + +// IsIPv4 is the validation function for validating if a value is a valid v4 IP address. +func isIPv4(fl FieldLevel) bool { + + ip := net.ParseIP(fl.Field().String()) + + return ip != nil && ip.To4() != nil +} + +// IsIPv6 is the validation function for validating if the field's value is a valid v6 IP address. +func isIPv6(fl FieldLevel) bool { + + ip := net.ParseIP(fl.Field().String()) + + return ip != nil && ip.To4() == nil +} + +// IsIP is the validation function for validating if the field's value is a valid v4 or v6 IP address. +func isIP(fl FieldLevel) bool { + + ip := net.ParseIP(fl.Field().String()) + + return ip != nil +} + +// IsSSN is the validation function for validating if the field's value is a valid SSN. +func isSSN(fl FieldLevel) bool { + + field := fl.Field() + + if field.Len() != 11 { + return false + } + + return sSNRegex.MatchString(field.String()) +} + +// IsLongitude is the validation function for validating if the field's value is a valid longitude coordinate. +func isLongitude(fl FieldLevel) bool { + return longitudeRegex.MatchString(fl.Field().String()) +} + +// IsLatitude is the validation function for validating if the field's value is a valid latitude coordinate. +func isLatitude(fl FieldLevel) bool { + return latitudeRegex.MatchString(fl.Field().String()) +} + +// IsDataURI is the validation function for validating if the field's value is a valid data URI. +func isDataURI(fl FieldLevel) bool { + + uri := strings.SplitN(fl.Field().String(), ",", 2) + + if len(uri) != 2 { + return false + } + + if !dataURIRegex.MatchString(uri[0]) { + return false + } + + return base64Regex.MatchString(uri[1]) +} + +// HasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character. +func hasMultiByteCharacter(fl FieldLevel) bool { + + field := fl.Field() + + if field.Len() == 0 { + return true + } + + return multibyteRegex.MatchString(field.String()) +} + +// IsPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character. +func isPrintableASCII(fl FieldLevel) bool { + return printableASCIIRegex.MatchString(fl.Field().String()) +} + +// IsASCII is the validation function for validating if the field's value is a valid ASCII character. +func isASCII(fl FieldLevel) bool { + return aSCIIRegex.MatchString(fl.Field().String()) +} + +// IsUUID5 is the validation function for validating if the field's value is a valid v5 UUID. +func isUUID5(fl FieldLevel) bool { + return uUID5Regex.MatchString(fl.Field().String()) +} + +// IsUUID4 is the validation function for validating if the field's value is a valid v4 UUID. +func isUUID4(fl FieldLevel) bool { + return uUID4Regex.MatchString(fl.Field().String()) +} + +// IsUUID3 is the validation function for validating if the field's value is a valid v3 UUID. +func isUUID3(fl FieldLevel) bool { + return uUID3Regex.MatchString(fl.Field().String()) +} + +// IsUUID is the validation function for validating if the field's value is a valid UUID of any version. +func isUUID(fl FieldLevel) bool { + return uUIDRegex.MatchString(fl.Field().String()) +} + +// IsISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. +func isISBN(fl FieldLevel) bool { + return isISBN10(fl) || isISBN13(fl) +} + +// IsISBN13 is the validation function for validating if the field's value is a valid v13 ISBN. +func isISBN13(fl FieldLevel) bool { + + s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4) + + if !iSBN13Regex.MatchString(s) { + return false + } + + var checksum int32 + var i int32 + + factor := []int32{1, 3} + + for i = 0; i < 12; i++ { + checksum += factor[i%2] * int32(s[i]-'0') + } + + return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 +} + +// IsISBN10 is the validation function for validating if the field's value is a valid v10 ISBN. +func isISBN10(fl FieldLevel) bool { + + s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3) + + if !iSBN10Regex.MatchString(s) { + return false + } + + var checksum int32 + var i int32 + + for i = 0; i < 9; i++ { + checksum += (i + 1) * int32(s[i]-'0') + } + + if s[9] == 'X' { + checksum += 10 * 10 + } else { + checksum += 10 * int32(s[9]-'0') + } + + return checksum%11 == 0 +} + +// IsEthereumAddress is the validation function for validating if the field's value is a valid ethereum address based currently only on the format +func isEthereumAddress(fl FieldLevel) bool { + address := fl.Field().String() + + if !ethAddressRegex.MatchString(address) { + return false + } + + if ethaddressRegexUpper.MatchString(address) || ethAddressRegexLower.MatchString(address) { + return true + } + + // checksum validation is blocked by https://github.com/golang/crypto/pull/28 + + return true +} + +// IsBitcoinAddress is the validation function for validating if the field's value is a valid btc address +func isBitcoinAddress(fl FieldLevel) bool { + address := fl.Field().String() + + if !btcAddressRegex.MatchString(address) { + return false + } + + alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + + decode := [25]byte{} + + for _, n := range []byte(address) { + d := bytes.IndexByte(alphabet, n) + + for i := 24; i >= 0; i-- { + d += 58 * int(decode[i]) + decode[i] = byte(d % 256) + d /= 256 + } + } + + h := sha256.New() + _, _ = h.Write(decode[:21]) + d := h.Sum([]byte{}) + h = sha256.New() + _, _ = h.Write(d) + + validchecksum := [4]byte{} + computedchecksum := [4]byte{} + + copy(computedchecksum[:], h.Sum(d[:0])) + copy(validchecksum[:], decode[21:]) + + return validchecksum == computedchecksum +} + +// IsBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address +func isBitcoinBech32Address(fl FieldLevel) bool { + address := fl.Field().String() + + if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) { + return false + } + + am := len(address) % 8 + + if am == 0 || am == 3 || am == 5 { + return false + } + + address = strings.ToLower(address) + + alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc + addr := address[3:] + dp := make([]int, 0, len(addr)) + + for _, c := range addr { + dp = append(dp, strings.IndexRune(alphabet, c)) + } + + ver := dp[0] + + if ver < 0 || ver > 16 { + return false + } + + if ver == 0 { + if len(address) != 42 && len(address) != 62 { + return false + } + } + + values := append(hr, dp...) + + GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + + p := 1 + + for _, v := range values { + b := p >> 25 + p = (p&0x1ffffff)<<5 ^ v + + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + p ^= GEN[i] + } + } + } + + if p != 1 { + return false + } + + b := uint(0) + acc := 0 + mv := (1 << 5) - 1 + var sw []int + + for _, v := range dp[1 : len(dp)-6] { + acc = (acc << 5) | v + b += 5 + for b >= 8 { + b -= 8 + sw = append(sw, (acc>>b)&mv) + } + } + + if len(sw) < 2 || len(sw) > 40 { + return false + } + + return true +} + +// ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param. +func excludesRune(fl FieldLevel) bool { + return !containsRune(fl) +} + +// ExcludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param. +func excludesAll(fl FieldLevel) bool { + return !containsAny(fl) +} + +// Excludes is the validation function for validating that the field's value does not contain the text specified within the param. +func excludes(fl FieldLevel) bool { + return !contains(fl) +} + +// ContainsRune is the validation function for validating that the field's value contains the rune specified within the param. +func containsRune(fl FieldLevel) bool { + + r, _ := utf8.DecodeRuneInString(fl.Param()) + + return strings.ContainsRune(fl.Field().String(), r) +} + +// ContainsAny is the validation function for validating that the field's value contains any of the characters specified within the param. +func containsAny(fl FieldLevel) bool { + return strings.ContainsAny(fl.Field().String(), fl.Param()) +} + +// Contains is the validation function for validating that the field's value contains the text specified within the param. +func contains(fl FieldLevel) bool { + return strings.Contains(fl.Field().String(), fl.Param()) +} + +// IsNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value. +func isNeField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + + if !ok || currentKind != kind { + return true + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() != currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() != currentField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() != currentField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) != int64(currentField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return true + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return !fieldTime.Equal(t) + } + + } + + // default reflect.String: + return field.String() != currentField.String() +} + +// IsNe is the validation function for validating that the field's value does not equal the provided param value. +func isNe(fl FieldLevel) bool { + return !isEq(fl) +} + +// IsLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value. +func isLteCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() <= topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() <= topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() <= topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) <= int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.Before(topTime) || fieldTime.Equal(topTime) + } + } + + // default reflect.String: + return field.String() <= topField.String() +} + +// IsLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value. +// NOTE: This is exposed for use within your own custom functions and not intended to be called directly. +func isLtCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() < topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() < topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() < topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) < int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.Before(topTime) + } + } + + // default reflect.String: + return field.String() < topField.String() +} + +// IsGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value. +func isGteCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() >= topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() >= topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() >= topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) >= int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.After(topTime) || fieldTime.Equal(topTime) + } + } + + // default reflect.String: + return field.String() >= topField.String() +} + +// IsGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value. +func isGtCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() > topField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() > topField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() > topField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) > int64(topField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + fieldTime := field.Interface().(time.Time) + topTime := topField.Interface().(time.Time) + + return fieldTime.After(topTime) + } + } + + // default reflect.String: + return field.String() > topField.String() +} + +// IsNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value. +func isNeCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return true + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return topField.Int() != field.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return topField.Uint() != field.Uint() + + case reflect.Float32, reflect.Float64: + return topField.Float() != field.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(topField.Len()) != int64(field.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return true + } + + if fieldType == timeType { + + t := field.Interface().(time.Time) + fieldTime := topField.Interface().(time.Time) + + return !fieldTime.Equal(t) + } + } + + // default reflect.String: + return topField.String() != field.String() +} + +// IsEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value. +func isEqCrossStructField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + topField, topKind, ok := fl.GetStructFieldOK() + if !ok || topKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return topField.Int() == field.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return topField.Uint() == field.Uint() + + case reflect.Float32, reflect.Float64: + return topField.Float() == field.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(topField.Len()) == int64(field.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != topField.Type() { + return false + } + + if fieldType == timeType { + + t := field.Interface().(time.Time) + fieldTime := topField.Interface().(time.Time) + + return fieldTime.Equal(t) + } + } + + // default reflect.String: + return topField.String() == field.String() +} + +// IsEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value. +func isEqField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return field.Int() == currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return field.Uint() == currentField.Uint() + + case reflect.Float32, reflect.Float64: + return field.Float() == currentField.Float() + + case reflect.Slice, reflect.Map, reflect.Array: + return int64(field.Len()) == int64(currentField.Len()) + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Equal(t) + } + + } + + // default reflect.String: + return field.String() == currentField.String() +} + +// IsEq is the validation function for validating if the current field's value is equal to the param's value. +func isEq(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + return field.String() == param + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsBase64 is the validation function for validating if the current field's value is a valid base 64. +func isBase64(fl FieldLevel) bool { + return base64Regex.MatchString(fl.Field().String()) +} + +// IsBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string. +func isBase64URL(fl FieldLevel) bool { + return base64URLRegex.MatchString(fl.Field().String()) +} + +// IsURI is the validation function for validating if the current field's value is a valid URI. +func isURI(fl FieldLevel) bool { + + field := fl.Field() + + switch field.Kind() { + + case reflect.String: + + s := field.String() + + // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 + // emulate browser and strip the '#' suffix prior to validation. see issue-#237 + if i := strings.Index(s, "#"); i > -1 { + s = s[:i] + } + + if len(s) == 0 { + return false + } + + _, err := url.ParseRequestURI(s) + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsURL is the validation function for validating if the current field's value is a valid URL. +func isURL(fl FieldLevel) bool { + + field := fl.Field() + + switch field.Kind() { + + case reflect.String: + + var i int + s := field.String() + + // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 + // emulate browser and strip the '#' suffix prior to validation. see issue-#237 + if i = strings.Index(s, "#"); i > -1 { + s = s[:i] + } + + if len(s) == 0 { + return false + } + + url, err := url.ParseRequestURI(s) + + if err != nil || url.Scheme == "" { + return false + } + + return err == nil + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsEmail is the validation function for validating if the current field's value is a valid email address. +func isEmail(fl FieldLevel) bool { + return emailRegex.MatchString(fl.Field().String()) +} + +// IsHSLA is the validation function for validating if the current field's value is a valid HSLA color. +func isHSLA(fl FieldLevel) bool { + return hslaRegex.MatchString(fl.Field().String()) +} + +// IsHSL is the validation function for validating if the current field's value is a valid HSL color. +func isHSL(fl FieldLevel) bool { + return hslRegex.MatchString(fl.Field().String()) +} + +// IsRGBA is the validation function for validating if the current field's value is a valid RGBA color. +func isRGBA(fl FieldLevel) bool { + return rgbaRegex.MatchString(fl.Field().String()) +} + +// IsRGB is the validation function for validating if the current field's value is a valid RGB color. +func isRGB(fl FieldLevel) bool { + return rgbRegex.MatchString(fl.Field().String()) +} + +// IsHEXColor is the validation function for validating if the current field's value is a valid HEX color. +func isHEXColor(fl FieldLevel) bool { + return hexcolorRegex.MatchString(fl.Field().String()) +} + +// IsHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal. +func isHexadecimal(fl FieldLevel) bool { + return hexadecimalRegex.MatchString(fl.Field().String()) +} + +// IsNumber is the validation function for validating if the current field's value is a valid number. +func isNumber(fl FieldLevel) bool { + switch fl.Field().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: + return true + default: + return numberRegex.MatchString(fl.Field().String()) + } +} + +// IsNumeric is the validation function for validating if the current field's value is a valid numeric value. +func isNumeric(fl FieldLevel) bool { + switch fl.Field().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: + return true + default: + return numericRegex.MatchString(fl.Field().String()) + } +} + +// IsAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value. +func isAlphanum(fl FieldLevel) bool { + return alphaNumericRegex.MatchString(fl.Field().String()) +} + +// IsAlpha is the validation function for validating if the current field's value is a valid alpha value. +func isAlpha(fl FieldLevel) bool { + return alphaRegex.MatchString(fl.Field().String()) +} + +// IsAlphanumUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value. +func isAlphanumUnicode(fl FieldLevel) bool { + return alphaUnicodeNumericRegex.MatchString(fl.Field().String()) +} + +// IsAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value. +func isAlphaUnicode(fl FieldLevel) bool { + return alphaUnicodeRegex.MatchString(fl.Field().String()) +} + +// isDefault is the opposite of required aka hasValue +func isDefault(fl FieldLevel) bool { + return !hasValue(fl) +} + +// HasValue is the validation function for validating if the current field's value is not the default static value. +func hasValue(fl FieldLevel) bool { + + field := fl.Field() + + switch field.Kind() { + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return !field.IsNil() + default: + + if fl.(*validate).fldIsPointer && field.Interface() != nil { + return true + } + + return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() + } +} + +// IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. +func isGteField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() >= currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() >= currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() >= currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.After(t) || fieldTime.Equal(t) + } + } + + // default reflect.String + return len(field.String()) >= len(currentField.String()) +} + +// IsGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value. +func isGtField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() > currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() > currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() > currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.After(t) + } + } + + // default reflect.String + return len(field.String()) > len(currentField.String()) +} + +// IsGte is the validation function for validating if the current field's value is greater than or equal to the param's value. +func isGte(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) >= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) >= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() >= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() >= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() >= p + + case reflect.Struct: + + if field.Type() == timeType { + + now := time.Now().UTC() + t := field.Interface().(time.Time) + + return t.After(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsGt is the validation function for validating if the current field's value is greater than the param's value. +func isGt(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) > p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) > p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() > p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() > p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() > p + case reflect.Struct: + + if field.Type() == timeType { + + return field.Interface().(time.Time).After(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasLengthOf is the validation function for validating if the current field's value is equal to the param's value. +func hasLengthOf(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) == p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) == p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() == p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() == p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() == p + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value. +func hasMinOf(fl FieldLevel) bool { + return isGte(fl) +} + +// IsLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value. +func isLteField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() <= currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() <= currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() <= currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Before(t) || fieldTime.Equal(t) + } + } + + // default reflect.String + return len(field.String()) <= len(currentField.String()) +} + +// IsLtField is the validation function for validating if the current field's value is less than the field specified by the param's value. +func isLtField(fl FieldLevel) bool { + + field := fl.Field() + kind := field.Kind() + + currentField, currentKind, ok := fl.GetStructFieldOK() + if !ok || currentKind != kind { + return false + } + + switch kind { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + + return field.Int() < currentField.Int() + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + + return field.Uint() < currentField.Uint() + + case reflect.Float32, reflect.Float64: + + return field.Float() < currentField.Float() + + case reflect.Struct: + + fieldType := field.Type() + + // Not Same underlying type i.e. struct and time + if fieldType != currentField.Type() { + return false + } + + if fieldType == timeType { + + t := currentField.Interface().(time.Time) + fieldTime := field.Interface().(time.Time) + + return fieldTime.Before(t) + } + } + + // default reflect.String + return len(field.String()) < len(currentField.String()) +} + +// IsLte is the validation function for validating if the current field's value is less than or equal to the param's value. +func isLte(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) <= p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) <= p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() <= p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() <= p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() <= p + + case reflect.Struct: + + if field.Type() == timeType { + + now := time.Now().UTC() + t := field.Interface().(time.Time) + + return t.Before(now) || t.Equal(now) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// IsLt is the validation function for validating if the current field's value is less than the param's value. +func isLt(fl FieldLevel) bool { + + field := fl.Field() + param := fl.Param() + + switch field.Kind() { + + case reflect.String: + p := asInt(param) + + return int64(utf8.RuneCountInString(field.String())) < p + + case reflect.Slice, reflect.Map, reflect.Array: + p := asInt(param) + + return int64(field.Len()) < p + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p := asInt(param) + + return field.Int() < p + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p := asUint(param) + + return field.Uint() < p + + case reflect.Float32, reflect.Float64: + p := asFloat(param) + + return field.Float() < p + + case reflect.Struct: + + if field.Type() == timeType { + + return field.Interface().(time.Time).Before(time.Now().UTC()) + } + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + +// HasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value. +func hasMaxOf(fl FieldLevel) bool { + return isLte(fl) +} + +// IsTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address. +func isTCP4AddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) { + return false + } + + _, err := net.ResolveTCPAddr("tcp4", fl.Field().String()) + return err == nil +} + +// IsTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address. +func isTCP6AddrResolvable(fl FieldLevel) bool { + + if !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveTCPAddr("tcp6", fl.Field().String()) + + return err == nil +} + +// IsTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address. +func isTCPAddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) && !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveTCPAddr("tcp", fl.Field().String()) + + return err == nil +} + +// IsUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address. +func isUDP4AddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) { + return false + } + + _, err := net.ResolveUDPAddr("udp4", fl.Field().String()) + + return err == nil +} + +// IsUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address. +func isUDP6AddrResolvable(fl FieldLevel) bool { + + if !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveUDPAddr("udp6", fl.Field().String()) + + return err == nil +} + +// IsUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address. +func isUDPAddrResolvable(fl FieldLevel) bool { + + if !isIP4Addr(fl) && !isIP6Addr(fl) { + return false + } + + _, err := net.ResolveUDPAddr("udp", fl.Field().String()) + + return err == nil +} + +// IsIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address. +func isIP4AddrResolvable(fl FieldLevel) bool { + + if !isIPv4(fl) { + return false + } + + _, err := net.ResolveIPAddr("ip4", fl.Field().String()) + + return err == nil +} + +// IsIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address. +func isIP6AddrResolvable(fl FieldLevel) bool { + + if !isIPv6(fl) { + return false + } + + _, err := net.ResolveIPAddr("ip6", fl.Field().String()) + + return err == nil +} + +// IsIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address. +func isIPAddrResolvable(fl FieldLevel) bool { + + if !isIP(fl) { + return false + } + + _, err := net.ResolveIPAddr("ip", fl.Field().String()) + + return err == nil +} + +// IsUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address. +func isUnixAddrResolvable(fl FieldLevel) bool { + + _, err := net.ResolveUnixAddr("unix", fl.Field().String()) + + return err == nil +} + +func isIP4Addr(fl FieldLevel) bool { + + val := fl.Field().String() + + if idx := strings.LastIndex(val, ":"); idx != -1 { + val = val[0:idx] + } + + ip := net.ParseIP(val) + + return ip != nil && ip.To4() != nil +} + +func isIP6Addr(fl FieldLevel) bool { + + val := fl.Field().String() + + if idx := strings.LastIndex(val, ":"); idx != -1 { + if idx != 0 && val[idx-1:idx] == "]" { + val = val[1 : idx-1] + } + } + + ip := net.ParseIP(val) + + return ip != nil && ip.To4() == nil +} + +func isHostnameRFC952(fl FieldLevel) bool { + return hostnameRegexRFC952.MatchString(fl.Field().String()) +} + +func isHostnameRFC1123(fl FieldLevel) bool { + return hostnameRegexRFC1123.MatchString(fl.Field().String()) +} + +func isFQDN(fl FieldLevel) bool { + val := fl.Field().String() + + if val == "" { + return false + } + + if val[len(val)-1] == '.' { + val = val[0 : len(val)-1] + } + + return strings.ContainsAny(val, ".") && + hostnameRegexRFC952.MatchString(val) +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/cache.go b/vendor/gopkg.in/go-playground/validator.v9/cache.go new file mode 100644 index 0000000..a7a4202 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/cache.go @@ -0,0 +1,337 @@ +package validator + +import ( + "fmt" + "reflect" + "strings" + "sync" + "sync/atomic" +) + +type tagType uint8 + +const ( + typeDefault tagType = iota + typeOmitEmpty + typeIsDefault + typeNoStructLevel + typeStructOnly + typeDive + typeOr + typeKeys + typeEndKeys +) + +const ( + invalidValidation = "Invalid validation tag on field '%s'" + undefinedValidation = "Undefined validation function '%s' on field '%s'" + keysTagNotDefined = "'" + endKeysTag + "' tag encountered without a corresponding '" + keysTag + "' tag" +) + +type structCache struct { + lock sync.Mutex + m atomic.Value // map[reflect.Type]*cStruct +} + +func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) { + c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key] + return +} + +func (sc *structCache) Set(key reflect.Type, value *cStruct) { + + m := sc.m.Load().(map[reflect.Type]*cStruct) + + nm := make(map[reflect.Type]*cStruct, len(m)+1) + for k, v := range m { + nm[k] = v + } + nm[key] = value + sc.m.Store(nm) +} + +type tagCache struct { + lock sync.Mutex + m atomic.Value // map[string]*cTag +} + +func (tc *tagCache) Get(key string) (c *cTag, found bool) { + c, found = tc.m.Load().(map[string]*cTag)[key] + return +} + +func (tc *tagCache) Set(key string, value *cTag) { + + m := tc.m.Load().(map[string]*cTag) + + nm := make(map[string]*cTag, len(m)+1) + for k, v := range m { + nm[k] = v + } + nm[key] = value + tc.m.Store(nm) +} + +type cStruct struct { + name string + fields []*cField + fn StructLevelFuncCtx +} + +type cField struct { + idx int + name string + altName string + namesEqual bool + cTags *cTag +} + +type cTag struct { + tag string + aliasTag string + actualAliasTag string + param string + keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation + next *cTag + fn FuncCtx + typeof tagType + hasTag bool + hasAlias bool + hasParam bool // true if parameter used eg. eq= where the equal sign has been set + isBlockEnd bool // indicates the current tag represents the last validation in the block +} + +func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { + + v.structCache.lock.Lock() + defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise! + + typ := current.Type() + + // could have been multiple trying to access, but once first is done this ensures struct + // isn't parsed again. + cs, ok := v.structCache.Get(typ) + if ok { + return cs + } + + cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]} + + numFields := current.NumField() + + var ctag *cTag + var fld reflect.StructField + var tag string + var customName string + + for i := 0; i < numFields; i++ { + + fld = typ.Field(i) + + if !fld.Anonymous && len(fld.PkgPath) > 0 { + continue + } + + tag = fld.Tag.Get(v.tagName) + + if tag == skipValidationTag { + continue + } + + customName = fld.Name + + if v.hasTagNameFunc { + + name := v.tagNameFunc(fld) + + if len(name) > 0 { + customName = name + } + } + + // NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different + // and so only struct level caching can be used instead of combined with Field tag caching + + if len(tag) > 0 { + ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, "", false) + } else { + // even if field doesn't have validations need cTag for traversing to potential inner/nested + // elements of the field. + ctag = new(cTag) + } + + cs.fields = append(cs.fields, &cField{ + idx: i, + name: fld.Name, + altName: customName, + cTags: ctag, + namesEqual: fld.Name == customName, + }) + } + + v.structCache.Set(typ, cs) + + return cs +} + +func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { + + var t string + var ok bool + noAlias := len(alias) == 0 + tags := strings.Split(tag, tagSeparator) + + for i := 0; i < len(tags); i++ { + + t = tags[i] + + if noAlias { + alias = t + } + + // check map for alias and process new tags, otherwise process as usual + if tagsVal, found := v.aliases[t]; found { + if i == 0 { + firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) + } else { + next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) + current.next, current = next, curr + + } + + continue + } + + var prevTag tagType + + if i == 0 { + current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} + firstCtag = current + } else { + prevTag = current.typeof + current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} + current = current.next + } + + switch t { + + case diveTag: + current.typeof = typeDive + continue + + case keysTag: + current.typeof = typeKeys + + if i == 0 || prevTag != typeDive { + panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag)) + } + + current.typeof = typeKeys + + // need to pass along only keys tag + // need to increment i to skip over the keys tags + b := make([]byte, 0, 64) + + i++ + + for ; i < len(tags); i++ { + + b = append(b, tags[i]...) + b = append(b, ',') + + if tags[i] == endKeysTag { + break + } + } + + current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false) + continue + + case endKeysTag: + current.typeof = typeEndKeys + + // if there are more in tags then there was no keysTag defined + // and an error should be thrown + if i != len(tags)-1 { + panic(keysTagNotDefined) + } + return + + case omitempty: + current.typeof = typeOmitEmpty + continue + + case structOnlyTag: + current.typeof = typeStructOnly + continue + + case noStructLevelTag: + current.typeof = typeNoStructLevel + continue + + default: + + if t == isdefault { + current.typeof = typeIsDefault + } + + // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" + orVals := strings.Split(t, orSeparator) + + for j := 0; j < len(orVals); j++ { + + vals := strings.SplitN(orVals[j], tagKeySeparator, 2) + + if noAlias { + alias = vals[0] + current.aliasTag = alias + } else { + current.actualAliasTag = t + } + + if j > 0 { + current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true} + current = current.next + } + current.hasParam = len(vals) > 1 + + current.tag = vals[0] + if len(current.tag) == 0 { + panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) + } + + if current.fn, ok = v.validations[current.tag]; !ok { + panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName))) + } + + if len(orVals) > 1 { + current.typeof = typeOr + } + + if len(vals) > 1 { + current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) + } + } + current.isBlockEnd = true + } + } + return +} + +func (v *Validate) fetchCacheTag(tag string) *cTag { + // find cached tag + ctag, found := v.tagCache.Get(tag) + if !found { + v.tagCache.lock.Lock() + defer v.tagCache.lock.Unlock() + + // could have been multiple trying to access, but once first is done this ensures tag + // isn't parsed again. + ctag, found = v.tagCache.Get(tag) + if !found { + ctag, _ = v.parseFieldTagsRecursive(tag, "", "", false) + v.tagCache.Set(tag, ctag) + } + } + return ctag +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/doc.go b/vendor/gopkg.in/go-playground/validator.v9/doc.go new file mode 100644 index 0000000..be93d79 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/doc.go @@ -0,0 +1,942 @@ +/* +Package validator implements value validations for structs and individual fields +based on tags. + +It can also handle Cross-Field and Cross-Struct validation for nested structs +and has the ability to dive into arrays and maps of any type. + +see more examples https://github.com/go-playground/validator/tree/v9/_examples + +Validation Functions Return Type error + +Doing things this way is actually the way the standard library does, see the +file.Open method here: + + https://golang.org/pkg/os/#Open. + +The authors return type "error" to avoid the issue discussed in the following, +where err is always != nil: + + http://stackoverflow.com/a/29138676/3158232 + https://github.com/go-playground/validator/issues/134 + +Validator only InvalidValidationError for bad validation input, nil or +ValidationErrors as type error; so, in your code all you need to do is check +if the error returned is not nil, and if it's not check if error is +InvalidValidationError ( if necessary, most of the time it isn't ) type cast +it to type ValidationErrors like so err.(validator.ValidationErrors). + +Custom Validation Functions + +Custom Validation functions can be added. Example: + + // Structure + func customFunc(fl FieldLevel) bool { + + if fl.Field().String() == "invalid" { + return false + } + + return true + } + + validate.RegisterValidation("custom tag name", customFunc) + // NOTES: using the same tag name as an existing function + // will overwrite the existing one + +Cross-Field Validation + +Cross-Field Validation can be done via the following tags: + - eqfield + - nefield + - gtfield + - gtefield + - ltfield + - ltefield + - eqcsfield + - necsfield + - gtcsfield + - gtecsfield + - ltcsfield + - ltecsfield + +If, however, some custom cross-field validation is required, it can be done +using a custom validation. + +Why not just have cross-fields validation tags (i.e. only eqcsfield and not +eqfield)? + +The reason is efficiency. If you want to check a field within the same struct +"eqfield" only has to find the field on the same struct (1 level). But, if we +used "eqcsfield" it could be multiple levels down. Example: + + type Inner struct { + StartDate time.Time + } + + type Outer struct { + InnerStructField *Inner + CreatedAt time.Time `validate:"ltecsfield=InnerStructField.StartDate"` + } + + now := time.Now() + + inner := &Inner{ + StartDate: now, + } + + outer := &Outer{ + InnerStructField: inner, + CreatedAt: now, + } + + errs := validate.Struct(outer) + + // NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed + // into the function + // when calling validate.VarWithValue(val, field, tag) val will be + // whatever you pass, struct, field... + // when calling validate.Field(field, tag) val will be nil + +Multiple Validators + +Multiple validators on a field will process in the order defined. Example: + + type Test struct { + Field `validate:"max=10,min=1"` + } + + // max will be checked then min + +Bad Validator definitions are not handled by the library. Example: + + type Test struct { + Field `validate:"min=10,max=0"` + } + + // this definition of min max will never succeed + +Using Validator Tags + +Baked In Cross-Field validation only compares fields on the same struct. +If Cross-Field + Cross-Struct validation is needed you should implement your +own custom validator. + +Comma (",") is the default separator of validation tags. If you wish to +have a comma included within the parameter (i.e. excludesall=,) you will need to +use the UTF-8 hex representation 0x2C, which is replaced in the code as a comma, +so the above will become excludesall=0x2C. + + type Test struct { + Field `validate:"excludesall=,"` // BAD! Do not include a comma. + Field `validate:"excludesall=0x2C"` // GOOD! Use the UTF-8 hex representation. + } + +Pipe ("|") is the 'or' validation tags deparator. If you wish to +have a pipe included within the parameter i.e. excludesall=| you will need to +use the UTF-8 hex representation 0x7C, which is replaced in the code as a pipe, +so the above will become excludesall=0x7C + + type Test struct { + Field `validate:"excludesall=|"` // BAD! Do not include a a pipe! + Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation. + } + + +Baked In Validators and Tags + +Here is a list of the current built in validators: + + +Skip Field + +Tells the validation to skip this struct field; this is particularly +handy in ignoring embedded structs from being validated. (Usage: -) + Usage: - + + +Or Operator + +This is the 'or' operator allowing multiple validators to be used and +accepted. (Usage: rbg|rgba) <-- this would allow either rgb or rgba +colors to be accepted. This can also be combined with 'and' for example +( Usage: omitempty,rgb|rgba) + + Usage: | + +StructOnly + +When a field that is a nested struct is encountered, and contains this flag +any validation on the nested struct will be run, but none of the nested +struct fields will be validated. This is useful if inside of you program +you know the struct will be valid, but need to verify it has been assigned. +NOTE: only "required" and "omitempty" can be used on a struct itself. + + Usage: structonly + +NoStructLevel + +Same as structonly tag except that any struct level validations will not run. + + Usage: nostructlevel + +Omit Empty + +Allows conditional validation, for example if a field is not set with +a value (Determined by the "required" validator) then other validation +such as min or max won't run, but if a value is set validation will run. + + Usage: omitempty + +Dive + +This tells the validator to dive into a slice, array or map and validate that +level of the slice, array or map with the validation tags that follow. +Multidimensional nesting is also supported, each level you wish to dive will +require another dive tag. dive has some sub-tags, 'keys' & 'endkeys', please see +the Keys & EndKeys section just below. + + Usage: dive + +Example #1 + + [][]string with validation tag "gt=0,dive,len=1,dive,required" + // gt=0 will be applied to [] + // len=1 will be applied to []string + // required will be applied to string + +Example #2 + + [][]string with validation tag "gt=0,dive,dive,required" + // gt=0 will be applied to [] + // []string will be spared validation + // required will be applied to string + +Keys & EndKeys + +These are to be used together directly after the dive tag and tells the validator +that anything between 'keys' and 'endkeys' applies to the keys of a map and not the +values; think of it like the 'dive' tag, but for map keys instead of values. +Multidimensional nesting is also supported, each level you wish to validate will +require another 'keys' and 'endkeys' tag. These tags are only valid for maps. + + Usage: dive,keys,othertagvalidation(s),endkeys,valuevalidationtags + +Example #1 + + map[string]string with validation tag "gt=0,dive,keys,eg=1|eq=2,endkeys,required" + // gt=0 will be applied to the map itself + // eg=1|eq=2 will be applied to the map keys + // required will be applied to map values + +Example #2 + + map[[2]string]string with validation tag "gt=0,dive,keys,dive,eq=1|eq=2,endkeys,required" + // gt=0 will be applied to the map itself + // eg=1|eq=2 will be applied to each array element in the the map keys + // required will be applied to map values + +Required + +This validates that the value is not the data types default zero value. +For numbers ensures value is not zero. For strings ensures value is +not "". For slices, maps, pointers, interfaces, channels and functions +ensures the value is not nil. + + Usage: required + +Is Default + +This validates that the value is the default value and is almost the +opposite of required. + + Usage: isdefault + +Length + +For numbers, length will ensure that the value is +equal to the parameter given. For strings, it checks that +the string length is exactly that number of characters. For slices, +arrays, and maps, validates the number of items. + + Usage: len=10 + +Maximum + +For numbers, max will ensure that the value is +less than or equal to the parameter given. For strings, it checks +that the string length is at most that number of characters. For +slices, arrays, and maps, validates the number of items. + + Usage: max=10 + +Minimum + +For numbers, min will ensure that the value is +greater or equal to the parameter given. For strings, it checks that +the string length is at least that number of characters. For slices, +arrays, and maps, validates the number of items. + + Usage: min=10 + +Equals + +For strings & numbers, eq will ensure that the value is +equal to the parameter given. For slices, arrays, and maps, +validates the number of items. + + Usage: eq=10 + +Not Equal + +For strings & numbers, ne will ensure that the value is not +equal to the parameter given. For slices, arrays, and maps, +validates the number of items. + + Usage: ne=10 + +One Of + +For strings, ints, and uints, oneof will ensure that the value +is one of the values in the parameter. The parameter should be +a list of values separated by whitespace. Values may be +strings or numbers. + + Usage: oneof=red green + oneof=5 7 9 + +Greater Than + +For numbers, this will ensure that the value is greater than the +parameter given. For strings, it checks that the string length +is greater than that number of characters. For slices, arrays +and maps it validates the number of items. + +Example #1 + + Usage: gt=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is greater than time.Now.UTC(). + + Usage: gt + +Greater Than or Equal + +Same as 'min' above. Kept both to make terminology with 'len' easier. + + +Example #1 + + Usage: gte=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is greater than or equal to time.Now.UTC(). + + Usage: gte + +Less Than + +For numbers, this will ensure that the value is less than the parameter given. +For strings, it checks that the string length is less than that number of +characters. For slices, arrays, and maps it validates the number of items. + +Example #1 + + Usage: lt=10 + +Example #2 (time.Time) +For time.Time ensures the time value is less than time.Now.UTC(). + + Usage: lt + +Less Than or Equal + +Same as 'max' above. Kept both to make terminology with 'len' easier. + +Example #1 + + Usage: lte=10 + +Example #2 (time.Time) + +For time.Time ensures the time value is less than or equal to time.Now.UTC(). + + Usage: lte + +Field Equals Another Field + +This will validate the field value against another fields value either within +a struct or passed in field. + +Example #1: + + // Validation on Password field using: + Usage: eqfield=ConfirmPassword + +Example #2: + + // Validating by field: + validate.VarWithValue(password, confirmpassword, "eqfield") + +Field Equals Another Field (relative) + +This does the same as eqfield except that it validates the field provided relative +to the top level struct. + + Usage: eqcsfield=InnerStructField.Field) + +Field Does Not Equal Another Field + +This will validate the field value against another fields value either within +a struct or passed in field. + +Examples: + + // Confirm two colors are not the same: + // + // Validation on Color field: + Usage: nefield=Color2 + + // Validating by field: + validate.VarWithValue(color1, color2, "nefield") + +Field Does Not Equal Another Field (relative) + +This does the same as nefield except that it validates the field provided +relative to the top level struct. + + Usage: necsfield=InnerStructField.Field + +Field Greater Than Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(gtfield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "gtfield") + + +Field Greater Than Another Relative Field + +This does the same as gtfield except that it validates the field provided +relative to the top level struct. + + Usage: gtcsfield=InnerStructField.Field + +Field Greater Than or Equal To Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(gtefield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "gtefield") + +Field Greater Than or Equal To Another Relative Field + +This does the same as gtefield except that it validates the field provided relative +to the top level struct. + + Usage: gtecsfield=InnerStructField.Field + +Less Than Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(ltfield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "ltfield") + +Less Than Another Relative Field + +This does the same as ltfield except that it validates the field provided relative +to the top level struct. + + Usage: ltcsfield=InnerStructField.Field + +Less Than or Equal To Another Field + +Only valid for Numbers and time.Time types, this will validate the field value +against another fields value either within a struct or passed in field. +usage examples are for validation of a Start and End date: + +Example #1: + + // Validation on End field using: + validate.Struct Usage(ltefield=Start) + +Example #2: + + // Validating by field: + validate.VarWithValue(start, end, "ltefield") + +Less Than or Equal To Another Relative Field + +This does the same as ltefield except that it validates the field provided relative +to the top level struct. + + Usage: ltecsfield=InnerStructField.Field + +Unique + +For arrays & slices, unique will ensure that there are no duplicates. +For maps, unique will ensure that there are no duplicate values. + + Usage: unique + +Alpha Only + +This validates that a string value contains ASCII alpha characters only + + Usage: alpha + +Alphanumeric + +This validates that a string value contains ASCII alphanumeric characters only + + Usage: alphanum + +Alpha Unicode + +This validates that a string value contains unicode alpha characters only + + Usage: alphaunicode + +Alphanumeric Unicode + +This validates that a string value contains unicode alphanumeric characters only + + Usage: alphanumunicode + +Numeric + +This validates that a string value contains a basic numeric value. +basic excludes exponents etc... +for integers or float it returns true. + + Usage: numeric + +Hexadecimal String + +This validates that a string value contains a valid hexadecimal. + + Usage: hexadecimal + +Hexcolor String + +This validates that a string value contains a valid hex color including +hashtag (#) + + Usage: hexcolor + +RGB String + +This validates that a string value contains a valid rgb color + + Usage: rgb + +RGBA String + +This validates that a string value contains a valid rgba color + + Usage: rgba + +HSL String + +This validates that a string value contains a valid hsl color + + Usage: hsl + +HSLA String + +This validates that a string value contains a valid hsla color + + Usage: hsla + +E-mail String + +This validates that a string value contains a valid email +This may not conform to all possibilities of any rfc standard, but neither +does any email provider accept all posibilities. + + Usage: email + +URL String + +This validates that a string value contains a valid url +This will accept any url the golang request uri accepts but must contain +a schema for example http:// or rtmp:// + + Usage: url + +URI String + +This validates that a string value contains a valid uri +This will accept any uri the golang request uri accepts + + Usage: uri + +Base64 String + +This validates that a string value contains a valid base64 value. +Although an empty string is valid base64 this will report an empty string +as an error, if you wish to accept an empty string as valid you can use +this with the omitempty tag. + + Usage: base64 + +Base64URL String + +This validates that a string value contains a valid base64 URL safe value +according the the RFC4648 spec. +Although an empty string is a valid base64 URL safe value, this will report +an empty string as an error, if you wish to accept an empty string as valid +you can use this with the omitempty tag. + + Usage: base64url + +Bitcoin Address + +This validates that a string value contains a valid bitcoin address. +The format of the string is checked to ensure it matches one of the three formats +P2PKH, P2SH and performs checksum validation. + + Usage: btc_addr + +Bitcoin Bech32 Address (segwit) + +This validates that a string value contains a valid bitcoin Bech32 address as defined +by bip-0173 (https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) +Special thanks to Pieter Wuille for providng reference implementations. + + Usage: btc_addr_bech32 + +Ethereum Address + +This validates that a string value contains a valid ethereum address. +The format of the string is checked to ensure it matches the standard Ethereum address format +Full validation is blocked by https://github.com/golang/crypto/pull/28 + + Usage: eth_addr + +Contains + +This validates that a string value contains the substring value. + + Usage: contains=@ + +Contains Any + +This validates that a string value contains any Unicode code points +in the substring value. + + Usage: containsany=!@#? + +Contains Rune + +This validates that a string value contains the supplied rune value. + + Usage: containsrune=@ + +Excludes + +This validates that a string value does not contain the substring value. + + Usage: excludes=@ + +Excludes All + +This validates that a string value does not contain any Unicode code +points in the substring value. + + Usage: excludesall=!@#? + +Excludes Rune + +This validates that a string value does not contain the supplied rune value. + + Usage: excludesrune=@ + +International Standard Book Number + +This validates that a string value contains a valid isbn10 or isbn13 value. + + Usage: isbn + +International Standard Book Number 10 + +This validates that a string value contains a valid isbn10 value. + + Usage: isbn10 + +International Standard Book Number 13 + +This validates that a string value contains a valid isbn13 value. + + Usage: isbn13 + +Universally Unique Identifier UUID + +This validates that a string value contains a valid UUID. + + Usage: uuid + +Universally Unique Identifier UUID v3 + +This validates that a string value contains a valid version 3 UUID. + + Usage: uuid3 + +Universally Unique Identifier UUID v4 + +This validates that a string value contains a valid version 4 UUID. + + Usage: uuid4 + +Universally Unique Identifier UUID v5 + +This validates that a string value contains a valid version 5 UUID. + + Usage: uuid5 + +ASCII + +This validates that a string value contains only ASCII characters. +NOTE: if the string is blank, this validates as true. + + Usage: ascii + +Printable ASCII + +This validates that a string value contains only printable ASCII characters. +NOTE: if the string is blank, this validates as true. + + Usage: printascii + +Multi-Byte Characters + +This validates that a string value contains one or more multibyte characters. +NOTE: if the string is blank, this validates as true. + + Usage: multibyte + +Data URL + +This validates that a string value contains a valid DataURI. +NOTE: this will also validate that the data portion is valid base64 + + Usage: datauri + +Latitude + +This validates that a string value contains a valid latitude. + + Usage: latitude + +Longitude + +This validates that a string value contains a valid longitude. + + Usage: longitude + +Social Security Number SSN + +This validates that a string value contains a valid U.S. Social Security Number. + + Usage: ssn + +Internet Protocol Address IP + +This validates that a string value contains a valid IP Address. + + Usage: ip + +Internet Protocol Address IPv4 + +This validates that a string value contains a valid v4 IP Address. + + Usage: ipv4 + +Internet Protocol Address IPv6 + +This validates that a string value contains a valid v6 IP Address. + + Usage: ipv6 + +Classless Inter-Domain Routing CIDR + +This validates that a string value contains a valid CIDR Address. + + Usage: cidr + +Classless Inter-Domain Routing CIDRv4 + +This validates that a string value contains a valid v4 CIDR Address. + + Usage: cidrv4 + +Classless Inter-Domain Routing CIDRv6 + +This validates that a string value contains a valid v6 CIDR Address. + + Usage: cidrv6 + +Transmission Control Protocol Address TCP + +This validates that a string value contains a valid resolvable TCP Address. + + Usage: tcp_addr + +Transmission Control Protocol Address TCPv4 + +This validates that a string value contains a valid resolvable v4 TCP Address. + + Usage: tcp4_addr + +Transmission Control Protocol Address TCPv6 + +This validates that a string value contains a valid resolvable v6 TCP Address. + + Usage: tcp6_addr + +User Datagram Protocol Address UDP + +This validates that a string value contains a valid resolvable UDP Address. + + Usage: udp_addr + +User Datagram Protocol Address UDPv4 + +This validates that a string value contains a valid resolvable v4 UDP Address. + + Usage: udp4_addr + +User Datagram Protocol Address UDPv6 + +This validates that a string value contains a valid resolvable v6 UDP Address. + + Usage: udp6_addr + +Internet Protocol Address IP + +This validates that a string value contains a valid resolvable IP Address. + + Usage: ip_addr + +Internet Protocol Address IPv4 + +This validates that a string value contains a valid resolvable v4 IP Address. + + Usage: ip4_addr + +Internet Protocol Address IPv6 + +This validates that a string value contains a valid resolvable v6 IP Address. + + Usage: ip6_addr + +Unix domain socket end point Address + +This validates that a string value contains a valid Unix Address. + + Usage: unix_addr + +Media Access Control Address MAC + +This validates that a string value contains a valid MAC Address. + + Usage: mac + +Note: See Go's ParseMAC for accepted formats and types: + + http://golang.org/src/net/mac.go?s=866:918#L29 + +Hostname RFC 952 + +This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952 + + Usage: hostname + +Hostname RFC 1123 + +This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123 + + Usage: hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias. + +Full Qualified Domain Name (FQDN) + +This validates that a string value contains a valid FQDN. + + Usage: fqdn + +Alias Validators and Tags + +NOTE: When returning an error, the tag returned in "FieldError" will be +the alias tag unless the dive tag is part of the alias. Everything after the +dive tag is not reported as the alias tag. Also, the "ActualTag" in the before +case will be the actual tag within the alias that failed. + +Here is a list of the current built in alias tags: + + "iscolor" + alias is "hexcolor|rgb|rgba|hsl|hsla" (Usage: iscolor) + +Validator notes: + + regex + a regex validator won't be added because commas and = signs can be part + of a regex which conflict with the validation definitions. Although + workarounds can be made, they take away from using pure regex's. + Furthermore it's quick and dirty but the regex's become harder to + maintain and are not reusable, so it's as much a programming philosophy + as anything. + + In place of this new validator functions should be created; a regex can + be used within the validator function and even be precompiled for better + efficiency within regexes.go. + + And the best reason, you can submit a pull request and we can keep on + adding to the validation library of this package! + +Panics + +This package panics when bad input is provided, this is by design, bad code like +that should not make it to production. + + type Test struct { + TestField string `validate:"nonexistantfunction=1"` + } + + t := &Test{ + TestField: "Test" + } + + validate.Struct(t) // this will panic +*/ +package validator diff --git a/vendor/gopkg.in/go-playground/validator.v9/errors.go b/vendor/gopkg.in/go-playground/validator.v9/errors.go new file mode 100644 index 0000000..85f65ef --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/errors.go @@ -0,0 +1,272 @@ +package validator + +import ( + "bytes" + "fmt" + "reflect" + "strings" + + ut "github.com/go-playground/universal-translator" +) + +const ( + fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag" +) + +// ValidationErrorsTranslations is the translation return type +type ValidationErrorsTranslations map[string]string + +// InvalidValidationError describes an invalid argument passed to +// `Struct`, `StructExcept`, StructPartial` or `Field` +type InvalidValidationError struct { + Type reflect.Type +} + +// Error returns InvalidValidationError message +func (e *InvalidValidationError) Error() string { + + if e.Type == nil { + return "validator: (nil)" + } + + return "validator: (nil " + e.Type.String() + ")" +} + +// ValidationErrors is an array of FieldError's +// for use in custom error messages post validation. +type ValidationErrors []FieldError + +// Error is intended for use in development + debugging and not intended to be a production error message. +// It allows ValidationErrors to subscribe to the Error interface. +// All information to create an error message specific to your application is contained within +// the FieldError found within the ValidationErrors array +func (ve ValidationErrors) Error() string { + + buff := bytes.NewBufferString("") + + var fe *fieldError + + for i := 0; i < len(ve); i++ { + + fe = ve[i].(*fieldError) + buff.WriteString(fe.Error()) + buff.WriteString("\n") + } + + return strings.TrimSpace(buff.String()) +} + +// Translate translates all of the ValidationErrors +func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations { + + trans := make(ValidationErrorsTranslations) + + var fe *fieldError + + for i := 0; i < len(ve); i++ { + fe = ve[i].(*fieldError) + + // // in case an Anonymous struct was used, ensure that the key + // // would be 'Username' instead of ".Username" + // if len(fe.ns) > 0 && fe.ns[:1] == "." { + // trans[fe.ns[1:]] = fe.Translate(ut) + // continue + // } + + trans[fe.ns] = fe.Translate(ut) + } + + return trans +} + +// FieldError contains all functions to get error details +type FieldError interface { + + // returns the validation tag that failed. if the + // validation was an alias, this will return the + // alias name and not the underlying tag that failed. + // + // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla" + // will return "iscolor" + Tag() string + + // returns the validation tag that failed, even if an + // alias the actual tag within the alias will be returned. + // If an 'or' validation fails the entire or will be returned. + // + // eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla" + // will return "hexcolor|rgb|rgba|hsl|hsla" + ActualTag() string + + // returns the namespace for the field error, with the tag + // name taking precedence over the fields actual name. + // + // eg. JSON name "User.fname" + // + // See StructNamespace() for a version that returns actual names. + // + // NOTE: this field can be blank when validating a single primitive field + // using validate.Field(...) as there is no way to extract it's name + Namespace() string + + // returns the namespace for the field error, with the fields + // actual name. + // + // eq. "User.FirstName" see Namespace for comparison + // + // NOTE: this field can be blank when validating a single primitive field + // using validate.Field(...) as there is no way to extract it's name + StructNamespace() string + + // returns the fields name with the tag name taking precedence over the + // fields actual name. + // + // eq. JSON name "fname" + // see ActualField for comparison + Field() string + + // returns the fields actual name from the struct, when able to determine. + // + // eq. "FirstName" + // see Field for comparison + StructField() string + + // returns the actual fields value in case needed for creating the error + // message + Value() interface{} + + // returns the param value, in string form for comparison; this will also + // help with generating an error message + Param() string + + // Kind returns the Field's reflect Kind + // + // eg. time.Time's kind is a struct + Kind() reflect.Kind + + // Type returns the Field's reflect Type + // + // // eg. time.Time's type is time.Time + Type() reflect.Type + + // returns the FieldError's translated error + // from the provided 'ut.Translator' and registered 'TranslationFunc' + // + // NOTE: is not registered translation can be found it returns the same + // as calling fe.Error() + Translate(ut ut.Translator) string +} + +// compile time interface checks +var _ FieldError = new(fieldError) +var _ error = new(fieldError) + +// fieldError contains a single field's validation error along +// with other properties that may be needed for error message creation +// it complies with the FieldError interface +type fieldError struct { + v *Validate + tag string + actualTag string + ns string + structNs string + fieldLen uint8 + structfieldLen uint8 + value interface{} + param string + kind reflect.Kind + typ reflect.Type +} + +// Tag returns the validation tag that failed. +func (fe *fieldError) Tag() string { + return fe.tag +} + +// ActualTag returns the validation tag that failed, even if an +// alias the actual tag within the alias will be returned. +func (fe *fieldError) ActualTag() string { + return fe.actualTag +} + +// Namespace returns the namespace for the field error, with the tag +// name taking precedence over the fields actual name. +func (fe *fieldError) Namespace() string { + return fe.ns +} + +// StructNamespace returns the namespace for the field error, with the fields +// actual name. +func (fe *fieldError) StructNamespace() string { + return fe.structNs +} + +// Field returns the fields name with the tag name taking precedence over the +// fields actual name. +func (fe *fieldError) Field() string { + + return fe.ns[len(fe.ns)-int(fe.fieldLen):] + // // return fe.field + // fld := fe.ns[len(fe.ns)-int(fe.fieldLen):] + + // log.Println("FLD:", fld) + + // if len(fld) > 0 && fld[:1] == "." { + // return fld[1:] + // } + + // return fld +} + +// returns the fields actual name from the struct, when able to determine. +func (fe *fieldError) StructField() string { + // return fe.structField + return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):] +} + +// Value returns the actual fields value in case needed for creating the error +// message +func (fe *fieldError) Value() interface{} { + return fe.value +} + +// Param returns the param value, in string form for comparison; this will +// also help with generating an error message +func (fe *fieldError) Param() string { + return fe.param +} + +// Kind returns the Field's reflect Kind +func (fe *fieldError) Kind() reflect.Kind { + return fe.kind +} + +// Type returns the Field's reflect Type +func (fe *fieldError) Type() reflect.Type { + return fe.typ +} + +// Error returns the fieldError's error message +func (fe *fieldError) Error() string { + return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag) +} + +// Translate returns the FieldError's translated error +// from the provided 'ut.Translator' and registered 'TranslationFunc' +// +// NOTE: is not registered translation can be found it returns the same +// as calling fe.Error() +func (fe *fieldError) Translate(ut ut.Translator) string { + + m, ok := fe.v.transTagFunc[ut] + if !ok { + return fe.Error() + } + + fn, ok := m[fe.tag] + if !ok { + return fe.Error() + } + + return fn(ut, fe) +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/field_level.go b/vendor/gopkg.in/go-playground/validator.v9/field_level.go new file mode 100644 index 0000000..cbfbc15 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/field_level.go @@ -0,0 +1,69 @@ +package validator + +import "reflect" + +// FieldLevel contains all the information and helper functions +// to validate a field +type FieldLevel interface { + + // returns the top level struct, if any + Top() reflect.Value + + // returns the current fields parent struct, if any or + // the comparison value if called 'VarWithValue' + Parent() reflect.Value + + // returns current field for validation + Field() reflect.Value + + // returns the field's name with the tag + // name taking precedence over the fields actual name. + FieldName() string + + // returns the struct field's name + StructFieldName() string + + // returns param for validation against current field + Param() string + + // ExtractType gets the actual underlying type of field value. + // It will dive into pointers, customTypes and return you the + // underlying value and it's kind. + ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool) + + // traverses the parent struct to retrieve a specific field denoted by the provided namespace + // in the param and returns the field, field kind and whether is was successful in retrieving + // the field at all. + // + // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field + // could not be retrieved because it didn't exist. + GetStructFieldOK() (reflect.Value, reflect.Kind, bool) +} + +var _ FieldLevel = new(validate) + +// Field returns current field for validation +func (v *validate) Field() reflect.Value { + return v.flField +} + +// FieldName returns the field's name with the tag +// name takeing precedence over the fields actual name. +func (v *validate) FieldName() string { + return v.cf.altName +} + +// StructFieldName returns the struct field's name +func (v *validate) StructFieldName() string { + return v.cf.name +} + +// Param returns param for validation against current field +func (v *validate) Param() string { + return v.ct.param +} + +// GetStructFieldOK returns Param returns param for validation against current field +func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) { + return v.getStructFieldOKInternal(v.slflParent, v.ct.param) +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/logo.png b/vendor/gopkg.in/go-playground/validator.v9/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..355000f5247d50e979cf5db5de38188ef4649a34 GIT binary patch literal 13443 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 V^Rh-@K?jDw= current.Len() { + return current, kind, false + } + + startIdx := idx2 + 1 + + if startIdx < len(namespace) { + if namespace[startIdx:startIdx+1] == namespaceSeparator { + startIdx++ + } + } + + val = current.Index(arrIdx) + namespace = namespace[startIdx:] + goto BEGIN + + case reflect.Map: + idx := strings.Index(namespace, leftBracket) + 1 + idx2 := strings.Index(namespace, rightBracket) + + endIdx := idx2 + + if endIdx+1 < len(namespace) { + if namespace[endIdx+1:endIdx+2] == namespaceSeparator { + endIdx++ + } + } + + key := namespace[idx:idx2] + + switch current.Type().Key().Kind() { + case reflect.Int: + i, _ := strconv.Atoi(key) + val = current.MapIndex(reflect.ValueOf(i)) + namespace = namespace[endIdx+1:] + + case reflect.Int8: + i, _ := strconv.ParseInt(key, 10, 8) + val = current.MapIndex(reflect.ValueOf(int8(i))) + namespace = namespace[endIdx+1:] + + case reflect.Int16: + i, _ := strconv.ParseInt(key, 10, 16) + val = current.MapIndex(reflect.ValueOf(int16(i))) + namespace = namespace[endIdx+1:] + + case reflect.Int32: + i, _ := strconv.ParseInt(key, 10, 32) + val = current.MapIndex(reflect.ValueOf(int32(i))) + namespace = namespace[endIdx+1:] + + case reflect.Int64: + i, _ := strconv.ParseInt(key, 10, 64) + val = current.MapIndex(reflect.ValueOf(i)) + namespace = namespace[endIdx+1:] + + case reflect.Uint: + i, _ := strconv.ParseUint(key, 10, 0) + val = current.MapIndex(reflect.ValueOf(uint(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint8: + i, _ := strconv.ParseUint(key, 10, 8) + val = current.MapIndex(reflect.ValueOf(uint8(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint16: + i, _ := strconv.ParseUint(key, 10, 16) + val = current.MapIndex(reflect.ValueOf(uint16(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint32: + i, _ := strconv.ParseUint(key, 10, 32) + val = current.MapIndex(reflect.ValueOf(uint32(i))) + namespace = namespace[endIdx+1:] + + case reflect.Uint64: + i, _ := strconv.ParseUint(key, 10, 64) + val = current.MapIndex(reflect.ValueOf(i)) + namespace = namespace[endIdx+1:] + + case reflect.Float32: + f, _ := strconv.ParseFloat(key, 32) + val = current.MapIndex(reflect.ValueOf(float32(f))) + namespace = namespace[endIdx+1:] + + case reflect.Float64: + f, _ := strconv.ParseFloat(key, 64) + val = current.MapIndex(reflect.ValueOf(f)) + namespace = namespace[endIdx+1:] + + case reflect.Bool: + b, _ := strconv.ParseBool(key) + val = current.MapIndex(reflect.ValueOf(b)) + namespace = namespace[endIdx+1:] + + // reflect.Type = string + default: + val = current.MapIndex(reflect.ValueOf(key)) + namespace = namespace[endIdx+1:] + } + + goto BEGIN + } + + // if got here there was more namespace, cannot go any deeper + panic("Invalid field namespace") +} + +// asInt returns the parameter as a int64 +// or panics if it can't convert +func asInt(param string) int64 { + + i, err := strconv.ParseInt(param, 0, 64) + panicIf(err) + + return i +} + +// asUint returns the parameter as a uint64 +// or panics if it can't convert +func asUint(param string) uint64 { + + i, err := strconv.ParseUint(param, 0, 64) + panicIf(err) + + return i +} + +// asFloat returns the parameter as a float64 +// or panics if it can't convert +func asFloat(param string) float64 { + + i, err := strconv.ParseFloat(param, 64) + panicIf(err) + + return i +} + +func panicIf(err error) { + if err != nil { + panic(err.Error()) + } +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator.go b/vendor/gopkg.in/go-playground/validator.v9/validator.go new file mode 100644 index 0000000..67473f1 --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/validator.go @@ -0,0 +1,475 @@ +package validator + +import ( + "context" + "fmt" + "reflect" + "strconv" +) + +// per validate contruct +type validate struct { + v *Validate + top reflect.Value + ns []byte + actualNs []byte + errs ValidationErrors + includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise + ffn FilterFunc + slflParent reflect.Value // StructLevel & FieldLevel + slCurrent reflect.Value // StructLevel & FieldLevel + flField reflect.Value // StructLevel & FieldLevel + cf *cField // StructLevel & FieldLevel + ct *cTag // StructLevel & FieldLevel + misc []byte // misc reusable + str1 string // misc reusable + str2 string // misc reusable + fldIsPointer bool // StructLevel & FieldLevel + isPartial bool + hasExcludes bool +} + +// parent and current will be the same the first run of validateStruct +func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) { + + cs, ok := v.v.structCache.Get(typ) + if !ok { + cs = v.v.extractStructCache(current, typ.Name()) + } + + if len(ns) == 0 && len(cs.name) != 0 { + + ns = append(ns, cs.name...) + ns = append(ns, '.') + + structNs = append(structNs, cs.name...) + structNs = append(structNs, '.') + } + + // ct is nil on top level struct, and structs as fields that have no tag info + // so if nil or if not nil and the structonly tag isn't present + if ct == nil || ct.typeof != typeStructOnly { + + var f *cField + + for i := 0; i < len(cs.fields); i++ { + + f = cs.fields[i] + + if v.isPartial { + + if v.ffn != nil { + // used with StructFiltered + if v.ffn(append(structNs, f.name...)) { + continue + } + + } else { + // used with StructPartial & StructExcept + _, ok = v.includeExclude[string(append(structNs, f.name...))] + + if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) { + continue + } + } + } + + v.traverseField(ctx, parent, current.Field(f.idx), ns, structNs, f, f.cTags) + } + } + + // check if any struct level validations, after all field validations already checked. + // first iteration will have no info about nostructlevel tag, and is checked prior to + // calling the next iteration of validateStruct called from traverseField. + if cs.fn != nil { + + v.slflParent = parent + v.slCurrent = current + v.ns = ns + v.actualNs = structNs + + cs.fn(ctx, v) + } +} + +// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options +func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) { + + var typ reflect.Type + var kind reflect.Kind + + current, kind, v.fldIsPointer = v.extractTypeInternal(current, false) + + switch kind { + case reflect.Ptr, reflect.Interface, reflect.Invalid: + + if ct == nil { + return + } + + if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault { + return + } + + if ct.hasTag { + + v.str1 = string(append(ns, cf.altName...)) + + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + + if kind == reflect.Invalid { + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + param: ct.param, + kind: kind, + }, + ) + + return + } + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: current.Type(), + }, + ) + + return + } + + case reflect.Struct: + + typ = current.Type() + + if typ != timeType { + + if ct != nil { + + if ct.typeof == typeStructOnly { + goto CONTINUE + } else if ct.typeof == typeIsDefault { + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if !ct.fn(ctx, v) { + v.str1 = string(append(ns, cf.altName...)) + + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + return + } + } + + ct = ct.next + } + + if ct != nil && ct.typeof == typeNoStructLevel { + return + } + + CONTINUE: + // if len == 0 then validating using 'Var' or 'VarWithValue' + // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm... + // VarWithField - this allows for validating against each field within the struct against a specific value + // pretty handy in certain situations + if len(cf.name) > 0 { + ns = append(append(ns, cf.altName...), '.') + structNs = append(append(structNs, cf.name...), '.') + } + + v.validateStruct(ctx, current, current, typ, ns, structNs, ct) + return + } + } + + if !ct.hasTag { + return + } + + typ = current.Type() + +OUTER: + for { + if ct == nil { + return + } + + switch ct.typeof { + + case typeOmitEmpty: + + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if !v.fldIsPointer && !hasValue(v) { + return + } + + ct = ct.next + continue + + case typeEndKeys: + return + + case typeDive: + + ct = ct.next + + // traverse slice or map here + // or panic ;) + switch kind { + case reflect.Slice, reflect.Array: + + var i64 int64 + reusableCF := &cField{} + + for i := 0; i < current.Len(); i++ { + + i64 = int64(i) + + v.misc = append(v.misc[0:0], cf.name...) + v.misc = append(v.misc, '[') + v.misc = strconv.AppendInt(v.misc, i64, 10) + v.misc = append(v.misc, ']') + + reusableCF.name = string(v.misc) + + if cf.namesEqual { + reusableCF.altName = reusableCF.name + } else { + + v.misc = append(v.misc[0:0], cf.altName...) + v.misc = append(v.misc, '[') + v.misc = strconv.AppendInt(v.misc, i64, 10) + v.misc = append(v.misc, ']') + + reusableCF.altName = string(v.misc) + } + v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct) + } + + case reflect.Map: + + var pv string + reusableCF := &cField{} + + for _, key := range current.MapKeys() { + + pv = fmt.Sprintf("%v", key.Interface()) + + v.misc = append(v.misc[0:0], cf.name...) + v.misc = append(v.misc, '[') + v.misc = append(v.misc, pv...) + v.misc = append(v.misc, ']') + + reusableCF.name = string(v.misc) + + if cf.namesEqual { + reusableCF.altName = reusableCF.name + } else { + v.misc = append(v.misc[0:0], cf.altName...) + v.misc = append(v.misc, '[') + v.misc = append(v.misc, pv...) + v.misc = append(v.misc, ']') + + reusableCF.altName = string(v.misc) + } + + if ct != nil && ct.typeof == typeKeys && ct.keys != nil { + v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys) + // can be nil when just keys being validated + if ct.next != nil { + v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next) + } + } else { + v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct) + } + } + + default: + // throw error, if not a slice or map then should not have gotten here + // bad dive tag + panic("dive error! can't dive on a non slice or map") + } + + return + + case typeOr: + + v.misc = v.misc[0:0] + + for { + + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if ct.fn(ctx, v) { + + // drain rest of the 'or' values, then continue or leave + for { + + ct = ct.next + + if ct == nil { + return + } + + if ct.typeof != typeOr { + continue OUTER + } + } + } + + v.misc = append(v.misc, '|') + v.misc = append(v.misc, ct.tag...) + + if ct.hasParam { + v.misc = append(v.misc, '=') + v.misc = append(v.misc, ct.param...) + } + + if ct.isBlockEnd || ct.next == nil { + // if we get here, no valid 'or' value and no more tags + v.str1 = string(append(ns, cf.altName...)) + + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + + if ct.hasAlias { + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.actualAliasTag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + + } else { + + tVal := string(v.misc)[1:] + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: tVal, + actualTag: tVal, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + } + + return + } + + ct = ct.next + } + + default: + + // set Field Level fields + v.slflParent = parent + v.flField = current + v.cf = cf + v.ct = ct + + if !ct.fn(ctx, v) { + + v.str1 = string(append(ns, cf.altName...)) + + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + value: current.Interface(), + param: ct.param, + kind: kind, + typ: typ, + }, + ) + + return + } + ct = ct.next + } + } + +} diff --git a/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go new file mode 100644 index 0000000..400efcf --- /dev/null +++ b/vendor/gopkg.in/go-playground/validator.v9/validator_instance.go @@ -0,0 +1,586 @@ +package validator + +import ( + "context" + "errors" + "fmt" + "reflect" + "strings" + "sync" + "time" + + ut "github.com/go-playground/universal-translator" +) + +const ( + defaultTagName = "validate" + utf8HexComma = "0x2C" + utf8Pipe = "0x7C" + tagSeparator = "," + orSeparator = "|" + tagKeySeparator = "=" + structOnlyTag = "structonly" + noStructLevelTag = "nostructlevel" + omitempty = "omitempty" + isdefault = "isdefault" + skipValidationTag = "-" + diveTag = "dive" + keysTag = "keys" + endKeysTag = "endkeys" + requiredTag = "required" + namespaceSeparator = "." + leftBracket = "[" + rightBracket = "]" + restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" + restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" + restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" +) + +var ( + timeType = reflect.TypeOf(time.Time{}) + defaultCField = &cField{namesEqual: true} +) + +// FilterFunc is the type used to filter fields using +// StructFiltered(...) function. +// returning true results in the field being filtered/skiped from +// validation +type FilterFunc func(ns []byte) bool + +// CustomTypeFunc allows for overriding or adding custom field type handler functions +// field = field value of the type to return a value to be validated +// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29 +type CustomTypeFunc func(field reflect.Value) interface{} + +// TagNameFunc allows for adding of a custom tag name parser +type TagNameFunc func(field reflect.StructField) string + +// Validate contains the validator settings and cache +type Validate struct { + tagName string + pool *sync.Pool + hasCustomFuncs bool + hasTagNameFunc bool + tagNameFunc TagNameFunc + structLevelFuncs map[reflect.Type]StructLevelFuncCtx + customFuncs map[reflect.Type]CustomTypeFunc + aliases map[string]string + validations map[string]FuncCtx + transTagFunc map[ut.Translator]map[string]TranslationFunc // map[]map[]TranslationFunc + tagCache *tagCache + structCache *structCache +} + +// New returns a new instance of 'validate' with sane defaults. +func New() *Validate { + + tc := new(tagCache) + tc.m.Store(make(map[string]*cTag)) + + sc := new(structCache) + sc.m.Store(make(map[reflect.Type]*cStruct)) + + v := &Validate{ + tagName: defaultTagName, + aliases: make(map[string]string, len(bakedInAliases)), + validations: make(map[string]FuncCtx, len(bakedInValidators)), + tagCache: tc, + structCache: sc, + } + + // must copy alias validators for separate validations to be used in each validator instance + for k, val := range bakedInAliases { + v.RegisterAlias(k, val) + } + + // must copy validators for separate validations to be used in each instance + for k, val := range bakedInValidators { + + // no need to error check here, baked in will always be valid + _ = v.registerValidation(k, wrapFunc(val), true) + } + + v.pool = &sync.Pool{ + New: func() interface{} { + return &validate{ + v: v, + ns: make([]byte, 0, 64), + actualNs: make([]byte, 0, 64), + misc: make([]byte, 32), + } + }, + } + + return v +} + +// SetTagName allows for changing of the default tag name of 'validate' +func (v *Validate) SetTagName(name string) { + v.tagName = name +} + +// RegisterTagNameFunc registers a function to get another name from the +// StructField eg. the JSON name +func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { + v.tagNameFunc = fn + v.hasTagNameFunc = true +} + +// RegisterValidation adds a validation with the given tag +// +// NOTES: +// - if the key already exists, the previous validation function will be replaced. +// - this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterValidation(tag string, fn Func) error { + return v.RegisterValidationCtx(tag, wrapFunc(fn)) +} + +// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation +// allowing context.Context validation support. +func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx) error { + return v.registerValidation(tag, fn, false) +} + +func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool) error { + + if len(tag) == 0 { + return errors.New("Function Key cannot be empty") + } + + if fn == nil { + return errors.New("Function cannot be empty") + } + + _, ok := restrictedTags[tag] + + if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) { + panic(fmt.Sprintf(restrictedTagErr, tag)) + } + + v.validations[tag] = fn + + return nil +} + +// RegisterAlias registers a mapping of a single validation tag that +// defines a common or complex set of validation(s) to simplify adding validation +// to structs. +// +// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterAlias(alias, tags string) { + + _, ok := restrictedTags[alias] + + if ok || strings.ContainsAny(alias, restrictedTagChars) { + panic(fmt.Sprintf(restrictedAliasErr, alias)) + } + + v.aliases[alias] = tags +} + +// RegisterStructValidation registers a StructLevelFunc against a number of types. +// +// NOTE: +// - this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) { + v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...) +} + +// RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing +// of contextual validation information via context.Context. +// +// NOTE: +// - this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) { + + if v.structLevelFuncs == nil { + v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx) + } + + for _, t := range types { + v.structLevelFuncs[reflect.TypeOf(t)] = fn + } +} + +// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types +// +// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation +func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { + + if v.customFuncs == nil { + v.customFuncs = make(map[reflect.Type]CustomTypeFunc) + } + + for _, t := range types { + v.customFuncs[reflect.TypeOf(t)] = fn + } + + v.hasCustomFuncs = true +} + +// RegisterTranslation registers translations against the provided tag. +func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) { + + if v.transTagFunc == nil { + v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc) + } + + if err = registerFn(trans); err != nil { + return + } + + m, ok := v.transTagFunc[trans] + if !ok { + m = make(map[string]TranslationFunc) + v.transTagFunc[trans] = m + } + + m[tag] = translationFn + + return +} + +// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified. +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) Struct(s interface{}) error { + return v.StructCtx(context.Background(), s) +} + +// StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified +// and also allows passing of context.Context for contextual validation information. +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) { + + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = false + // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept + + vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates +// nested structs, unless otherwise specified. +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error { + return v.StructFilteredCtx(context.Background(), s, fn) +} + +// StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates +// nested structs, unless otherwise specified and also allows passing of contextual validation information via +// context.Context +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) { + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = true + vd.ffn = fn + // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept + + vd.validateStruct(context.Background(), top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// StructPartial validates the fields passed in only, ignoring all others. +// Fields may be provided in a namespaced fashion relative to the struct provided +// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructPartial(s interface{}, fields ...string) error { + return v.StructPartialCtx(context.Background(), s, fields...) +} + +// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual +// validation validation information via context.Context +// Fields may be provided in a namespaced fashion relative to the struct provided +// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) { + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = true + vd.ffn = nil + vd.hasExcludes = false + vd.includeExclude = make(map[string]struct{}) + + typ := val.Type() + name := typ.Name() + + for _, k := range fields { + + flds := strings.Split(k, namespaceSeparator) + if len(flds) > 0 { + + vd.misc = append(vd.misc[0:0], name...) + vd.misc = append(vd.misc, '.') + + for _, s := range flds { + + idx := strings.Index(s, leftBracket) + + if idx != -1 { + for idx != -1 { + vd.misc = append(vd.misc, s[:idx]...) + vd.includeExclude[string(vd.misc)] = struct{}{} + + idx2 := strings.Index(s, rightBracket) + idx2++ + vd.misc = append(vd.misc, s[idx:idx2]...) + vd.includeExclude[string(vd.misc)] = struct{}{} + s = s[idx2:] + idx = strings.Index(s, leftBracket) + } + } else { + + vd.misc = append(vd.misc, s...) + vd.includeExclude[string(vd.misc)] = struct{}{} + } + + vd.misc = append(vd.misc, '.') + } + } + } + + vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// StructExcept validates all fields except the ones passed in. +// Fields may be provided in a namespaced fashion relative to the struct provided +// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructExcept(s interface{}, fields ...string) error { + return v.StructExceptCtx(context.Background(), s, fields...) +} + +// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual +// validation validation information via context.Context +// Fields may be provided in a namespaced fashion relative to the struct provided +// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) { + val := reflect.ValueOf(s) + top := val + + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if val.Kind() != reflect.Struct || val.Type() == timeType { + return &InvalidValidationError{Type: reflect.TypeOf(s)} + } + + // good to validate + vd := v.pool.Get().(*validate) + vd.top = top + vd.isPartial = true + vd.ffn = nil + vd.hasExcludes = true + vd.includeExclude = make(map[string]struct{}) + + typ := val.Type() + name := typ.Name() + + for _, key := range fields { + + vd.misc = vd.misc[0:0] + + if len(name) > 0 { + vd.misc = append(vd.misc, name...) + vd.misc = append(vd.misc, '.') + } + + vd.misc = append(vd.misc, key...) + vd.includeExclude[string(vd.misc)] = struct{}{} + } + + vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + + v.pool.Put(vd) + + return +} + +// Var validates a single variable using tag style validation. +// eg. +// var i int +// validate.Var(i, "gt=1,lt=10") +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) Var(field interface{}, tag string) error { + return v.VarCtx(context.Background(), field, tag) +} + +// VarCtx validates a single variable using tag style validation and allows passing of contextual +// validation validation information via context.Context. +// eg. +// var i int +// validate.Var(i, "gt=1,lt=10") +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (err error) { + if len(tag) == 0 || tag == skipValidationTag { + return nil + } + + ctag := v.fetchCacheTag(tag) + val := reflect.ValueOf(field) + vd := v.pool.Get().(*validate) + vd.top = val + vd.isPartial = false + vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + v.pool.Put(vd) + return +} + +// VarWithValue validates a single variable, against another variable/field's value using tag style validation +// eg. +// s1 := "abcd" +// s2 := "abcd" +// validate.VarWithValue(s1, s2, "eqcsfield") // returns true +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error { + return v.VarWithValueCtx(context.Background(), field, other, tag) +} + +// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and +// allows passing of contextual validation validation information via context.Context. +// eg. +// s1 := "abcd" +// s2 := "abcd" +// validate.VarWithValue(s1, s2, "eqcsfield") // returns true +// +// WARNING: a struct can be passed for validation eg. time.Time is a struct or +// if you have a custom type and have registered a custom type handler, so must +// allow it; however unforeseen validations will occur if trying to validate a +// struct that is meant to be passed to 'validate.Struct' +// +// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise. +// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors. +// validate Array, Slice and maps fields which may contain more than one error +func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) { + if len(tag) == 0 || tag == skipValidationTag { + return nil + } + ctag := v.fetchCacheTag(tag) + otherVal := reflect.ValueOf(other) + vd := v.pool.Get().(*validate) + vd.top = otherVal + vd.isPartial = false + vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag) + + if len(vd.errs) > 0 { + err = vd.errs + vd.errs = nil + } + v.pool.Put(vd) + return +} diff --git a/vendor/modules.txt b/vendor/modules.txt new file mode 100644 index 0000000..45bc956 --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,19 @@ +# github.com/caicloud/nirvana v0.2.3 +github.com/caicloud/nirvana +github.com/caicloud/nirvana/definition +github.com/caicloud/nirvana/errors +github.com/caicloud/nirvana/log +github.com/caicloud/nirvana/operators/validator +github.com/caicloud/nirvana/plugins/reqlog +github.com/caicloud/nirvana/service +github.com/caicloud/nirvana/service/router +github.com/caicloud/nirvana/utils/api +# github.com/go-playground/locales v0.12.1 +github.com/go-playground/locales +github.com/go-playground/locales/currency +# github.com/go-playground/universal-translator v0.16.0 +github.com/go-playground/universal-translator +# github.com/spf13/pflag v1.0.5 +github.com/spf13/pflag +# gopkg.in/go-playground/validator.v9 v9.17.1 +gopkg.in/go-playground/validator.v9 From 40e2c1c00c3dff5d70fec7f78f18d6b9589ed1a6 Mon Sep 17 00:00:00 2001 From: Robin Yue Date: Wed, 22 Apr 2020 18:34:19 +0800 Subject: [PATCH 2/5] chore(*): best practice for api definition --- cmd/server/main.go | 10 +- nirvana.yaml | 18 ++++ pkg/apis/api.go | 22 +++++ pkg/apis/install.go | 38 -------- pkg/apis/v1alpha1/customers.go | 26 +++++ pkg/apis/v1alpha1/descriptors/customers.go | 84 ++++++++++++++++ pkg/apis/v1alpha1/descriptors/install.go | 22 +++++ pkg/apis/v1alpha1/descriptors/products.go | 106 ++++++++++++++------- pkg/apis/v1alpha1/handlers/customers.go | 50 ++++++++++ pkg/apis/v1alpha1/handlers/products.go | 37 ++++++- 10 files changed, 332 insertions(+), 81 deletions(-) create mode 100644 nirvana.yaml create mode 100644 pkg/apis/api.go delete mode 100644 pkg/apis/install.go create mode 100644 pkg/apis/v1alpha1/customers.go create mode 100644 pkg/apis/v1alpha1/descriptors/customers.go create mode 100644 pkg/apis/v1alpha1/descriptors/install.go create mode 100644 pkg/apis/v1alpha1/handlers/customers.go diff --git a/cmd/server/main.go b/cmd/server/main.go index fe24e11..68feb0d 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,6 +6,7 @@ import ( "github.com/caicloud/nirvana" "github.com/caicloud/nirvana/log" + "github.com/caicloud/nirvana/plugins/reqlog" "github.com/spf13/pflag" "github.com/caicloud/nirvana-practice/pkg/apis" @@ -30,10 +31,11 @@ func main() { } // initialize Server config - config := nirvana.NewDefaultConfig().Configure(nirvana.Port(httpPort)) - - // install APIs - apis.Install(config) + config := nirvana.NewDefaultConfig().Configure( + nirvana.Port(httpPort), + reqlog.Default(), + nirvana.Descriptor(apis.Descriptor()), + ) // create the server and server server := nirvana.NewServer(config) diff --git a/nirvana.yaml b/nirvana.yaml new file mode 100644 index 0000000..0ac6189 --- /dev/null +++ b/nirvana.yaml @@ -0,0 +1,18 @@ +# This file describes your project. It's used to generate api docs and +# clients. All fields in this file won't affect nirvana configurations. + +project: nirvana-practice-server +description: This is a practice project which using Nirvana API framework +schemes: +- http +hosts: +- localhost:8080 +contacts: +- name: Owl + email: owl@caicloud.io + description: Maintainer this project +versions: +- name: v1alpha1 + description: The v1alpha1 version of this project + rules: + - prefix: /api/v1alpha1/ diff --git a/pkg/apis/api.go b/pkg/apis/api.go new file mode 100644 index 0000000..ef20ea2 --- /dev/null +++ b/pkg/apis/api.go @@ -0,0 +1,22 @@ +// +nirvana:api=descriptors:"Descriptor" + +package apis + +import ( + v1alpha1 "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/descriptors" + + def "github.com/caicloud/nirvana/definition" +) + +// Descriptor returns a combined descriptor for APIs of all versions. +func Descriptor() def.Descriptor { + return def.Descriptor{ + Description: "APIs", + Path: "/api", + Consumes: []string{def.MIMEJSON}, + Produces: []string{def.MIMEJSON}, + Children: []def.Descriptor{ + v1alpha1.Descriptor(), + }, + } +} diff --git a/pkg/apis/install.go b/pkg/apis/install.go deleted file mode 100644 index 1b06e7b..0000000 --- a/pkg/apis/install.go +++ /dev/null @@ -1,38 +0,0 @@ -package apis - -import ( - "github.com/caicloud/nirvana" - "github.com/caicloud/nirvana/definition" - "github.com/caicloud/nirvana/plugins/reqlog" - - "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/descriptors" -) - -// Install configures the given Nirvana Config object with the API Descriptor. -func Install(config *nirvana.Config) { - config.Configure( - reqlog.Default(), - nirvana.Descriptor( - APIDescriptor(), - ), - ) -} - -// APIDescriptor builds and returns a descriptor of all APIs. -func APIDescriptor() definition.Descriptor { - return definition.Descriptor{ - Path: "/api", - Consumes: []string{definition.MIMEJSON}, - Produces: []string{definition.MIMEJSON}, - Children: []definition.Descriptor{ - { - Path: "/v1alpha1", - Children: []definition.Descriptor{ - descriptors.ProductDescriptor(), - }, - Description: "all v1alpha1 APIs", - }, - }, - Description: "all APIs", - } -} diff --git a/pkg/apis/v1alpha1/customers.go b/pkg/apis/v1alpha1/customers.go new file mode 100644 index 0000000..aee0a08 --- /dev/null +++ b/pkg/apis/v1alpha1/customers.go @@ -0,0 +1,26 @@ +package v1alpha1 + +import ( + "time" + + meta "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" +) + +type Customer struct { + meta.Metadata `json:",inline"` + Spec *CustomerSpec `json:"spec,omitempty"` + Status *CustomerStatus `json:"status,omitempty"` +} + +type CustomerSpec struct { + Sex string `json:"sex,omitempty"` +} + +type CustomerStatus struct { + LatestLoginTimestamp *time.Time `json:"latestLoginTimestamp,omitempty"` +} + +type CustomersList struct { + Total int `json:"total"` + Items []*Customer `json:"items,omitempty"` +} diff --git a/pkg/apis/v1alpha1/descriptors/customers.go b/pkg/apis/v1alpha1/descriptors/customers.go new file mode 100644 index 0000000..f392415 --- /dev/null +++ b/pkg/apis/v1alpha1/descriptors/customers.go @@ -0,0 +1,84 @@ +package descriptors + +import ( + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/operators/validator" + + meta "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" + "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/handlers" +) + +func init() { + register(customers...) +} + +var customers = []definition.Descriptor{ + { + Path: "/customers", + Description: "Customer API", + Tags: []string{"customer"}, + Definitions: []definition.Definition{listCustomers, createCustomer}, + Children: []definition.Descriptor{ + { + Path: "/{customer}", + Definitions: []definition.Definition{getCustomer, updateCustomer, deleteCustomer}, + }, + }, + }, +} + +var listCustomers = definition.Definition{ + Method: definition.List, + Description: "list customers", + Function: handlers.ListCustomers, + Parameters: []definition.Parameter{ + { + Source: definition.Auto, + Name: "options", + Description: "generic list options", + Operators: []definition.Operator{validator.Struct(&meta.ListOptions{})}, + }, + }, + Results: definition.DataErrorResults("listed customers"), +} + +var createCustomer = definition.Definition{ + Method: definition.Create, + Description: "create customer", + Function: handlers.CreateCustomer, + Parameters: []definition.Parameter{ + definition.BodyParameterFor("JSON body to describe the new customer"), + }, + Results: definition.DataErrorResults("customer"), +} + +var getCustomer = definition.Definition{ + Method: definition.Get, + Description: "get customer", + Function: handlers.GetCustomer, + Parameters: []definition.Parameter{ + definition.PathParameterFor("customer", "name of the customer to get"), + }, + Results: definition.DataErrorResults("customer"), +} + +var updateCustomer = definition.Definition{ + Method: definition.Update, + Description: "update customer", + Function: handlers.UpdateCustomer, + Parameters: []definition.Parameter{ + definition.PathParameterFor("customer", "name of the customer to update"), + definition.BodyParameterFor("JSON body to describe the new customer"), + }, + Results: definition.DataErrorResults("customer"), +} + +var deleteCustomer = definition.Definition{ + Method: definition.Delete, + Description: "delete customer", + Function: handlers.DeleteCustomer, + Parameters: []definition.Parameter{ + definition.PathParameterFor("customer", "name of the customer to delete"), + }, + Results: []definition.Result{definition.ErrorResult()}, +} diff --git a/pkg/apis/v1alpha1/descriptors/install.go b/pkg/apis/v1alpha1/descriptors/install.go new file mode 100644 index 0000000..0eaf924 --- /dev/null +++ b/pkg/apis/v1alpha1/descriptors/install.go @@ -0,0 +1,22 @@ +package descriptors + +import ( + "github.com/caicloud/nirvana/definition" +) + +var descriptors []definition.Descriptor + +func register(ds ...definition.Descriptor) { + descriptors = append(descriptors, ds...) +} + +// Descriptor returns a combined descriptor for current version. +func Descriptor() definition.Descriptor { + return definition.Descriptor{ + Description: "v1alpha1 API", + Path: "/v1alpha1", + Consumes: []string{definition.MIMEJSON}, + Produces: []string{definition.MIMEJSON}, + Children: descriptors, + } +} diff --git a/pkg/apis/v1alpha1/descriptors/products.go b/pkg/apis/v1alpha1/descriptors/products.go index 68a9e24..2ae51d7 100644 --- a/pkg/apis/v1alpha1/descriptors/products.go +++ b/pkg/apis/v1alpha1/descriptors/products.go @@ -8,45 +8,77 @@ import ( "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/handlers" ) -// ProductDescriptor builds and returns a Descriptor for all Product APIs. -func ProductDescriptor() definition.Descriptor { - return definition.Descriptor{ - Path: "/products", - Definitions: []definition.Definition{ - { - Method: definition.List, - Function: handlers.ListProducts, - Parameters: []definition.Parameter{ - { - Source: definition.Auto, - Name: "options", - Description: "generic list options", - Operators: []definition.Operator{validator.Struct(&meta.ListOptions{})}, - }, - }, - Results: definition.DataErrorResults("listed products"), - Description: "list products", - }, - // TODO - }, +func init() { + register(products...) +} + +var products = []definition.Descriptor{ + { + Path: "/products", + Description: "Product API", + Tags: []string{"product"}, + Definitions: []definition.Definition{listProducts, createProduct}, Children: []definition.Descriptor{ { - Path: "/{product}", - Definitions: []definition.Definition{ - { - Method: definition.Get, - Function: handlers.GetProduct, - Parameters: []definition.Parameter{ - definition.PathParameterFor("product", "name of the product to get"), - }, - Results: definition.DataErrorResults("the get result (or error)"), - Description: "get product", - }, - // TODO - }, - Description: "single-target Product APIs", + Path: "/{product}", + Definitions: []definition.Definition{getProduct, updateProduct, deleteProduct}, }, }, - Description: "all Product APIs", - } + }, +} + +var listProducts = definition.Definition{ + Method: definition.List, + Description: "list products", + Function: handlers.ListProducts, + Parameters: []definition.Parameter{ + { + Source: definition.Auto, + Name: "options", + Description: "generic list options", + Operators: []definition.Operator{validator.Struct(&meta.ListOptions{})}, + }, + }, + Results: definition.DataErrorResults("listed products"), +} + +var createProduct = definition.Definition{ + Method: definition.Create, + Description: "create product", + Function: handlers.CreateProduct, + Parameters: []definition.Parameter{ + definition.BodyParameterFor("JSON body to describe the new product"), + }, + Results: definition.DataErrorResults("product"), +} + +var getProduct = definition.Definition{ + Method: definition.Get, + Description: "get product", + Function: handlers.GetProduct, + Parameters: []definition.Parameter{ + definition.PathParameterFor("product", "name of the product to get"), + }, + Results: definition.DataErrorResults("product"), +} + +var updateProduct = definition.Definition{ + Method: definition.Update, + Description: "update product", + Function: handlers.UpdateProduct, + Parameters: []definition.Parameter{ + definition.PathParameterFor("product", "name of the product to update"), + definition.BodyParameterFor("JSON body to describe the new product"), + }, + Results: definition.DataErrorResults("product"), +} + +var deleteProduct = definition.Definition{ + Method: definition.Delete, + Description: "delete product", + Function: handlers.DeleteProduct, + Parameters: []definition.Parameter{ + definition.PathParameterFor("product", "name of the product to delete"), + }, + Results: []definition.Result{definition.ErrorResult()}, } diff --git a/pkg/apis/v1alpha1/handlers/customers.go b/pkg/apis/v1alpha1/handlers/customers.go new file mode 100644 index 0000000..a87be13 --- /dev/null +++ b/pkg/apis/v1alpha1/handlers/customers.go @@ -0,0 +1,50 @@ +package handlers + +import ( + "context" + "fmt" + + "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" + api "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1" + "github.com/caicloud/nirvana-practice/pkg/errors" +) + +var defaultCustomer = &api.Customer{ + Metadata: v1.Metadata{ + Name: "Tony", + }, + Spec: &api.CustomerSpec{ + Sex: "Male", + }, +} + +func CreateCustomer(ctx context.Context, customer *api.Customer) (*api.Customer, error) { + return nil, errors.ErrorNotImplemented.Error() +} + +func ListCustomers(ctx context.Context, options *v1.ListOptions) (*api.CustomersList, error) { + results := &api.CustomersList{ + Total: 1, + Items: []*api.Customer{defaultCustomer}, + } + + return results, nil +} + +func GetCustomer(ctx context.Context, name string) (*api.Customer, error) { + fmt.Printf("Get customer: %s\n", name) + + return defaultCustomer, nil +} + +func UpdateCustomer(ctx context.Context, name string, customer *api.Customer) (*api.Customer, error) { + fmt.Printf("Update customer: %s\n", name) + + return customer, nil +} + +func DeleteCustomer(ctx context.Context, name string) error { + fmt.Printf("Delete customer: %s\n", name) + + return nil +} diff --git a/pkg/apis/v1alpha1/handlers/products.go b/pkg/apis/v1alpha1/handlers/products.go index fb44a75..e6f1725 100644 --- a/pkg/apis/v1alpha1/handlers/products.go +++ b/pkg/apis/v1alpha1/handlers/products.go @@ -2,16 +2,49 @@ package handlers import ( "context" + "fmt" "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" api "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1" "github.com/caicloud/nirvana-practice/pkg/errors" ) -func GetProduct(ctx context.Context, name string) (*api.Product, error) { +var defaultProduct = &api.Product{ + Metadata: v1.Metadata{ + Name: "Apple", + }, + Spec: &api.ProductSpec{ + Category: "Fruit", + }, +} + +func CreateProduct(ctx context.Context, product *api.Product) (*api.Product, error) { return nil, errors.ErrorNotImplemented.Error() } func ListProducts(ctx context.Context, options *v1.ListOptions) (*api.ProductsList, error) { - return nil, errors.ErrorNotImplemented.Error() + results := &api.ProductsList{ + Total: 1, + Items: []*api.Product{defaultProduct}, + } + + return results, nil +} + +func GetProduct(ctx context.Context, name string) (*api.Product, error) { + fmt.Printf("Get product: %s\n", name) + + return defaultProduct, nil +} + +func UpdateProduct(ctx context.Context, name string, product *api.Product) (*api.Product, error) { + fmt.Printf("Update product: %s\n", name) + + return product, nil +} + +func DeleteProduct(ctx context.Context, name string) error { + fmt.Printf("Delete product: %s\n", name) + + return nil } From 4ec02a086d1206497455ddb06d5f7ff99bc1ae8b Mon Sep 17 00:00:00 2001 From: Robin Yue Date: Mon, 27 Apr 2020 16:35:47 +0800 Subject: [PATCH 3/5] fix comments --- cmd/server/main.go | 2 -- pkg/apis/api.go | 9 ++++-- pkg/apis/v1alpha1/descriptors/customers.go | 2 +- pkg/apis/v1alpha1/descriptors/install.go | 8 +++-- pkg/apis/v1alpha1/descriptors/products.go | 2 +- pkg/{apis/v1alpha1 => }/handlers/customers.go | 29 +++---------------- pkg/{apis/v1alpha1 => }/handlers/products.go | 29 +++---------------- 7 files changed, 22 insertions(+), 59 deletions(-) rename pkg/{apis/v1alpha1 => }/handlers/customers.go (61%) rename pkg/{apis/v1alpha1 => }/handlers/products.go (61%) diff --git a/cmd/server/main.go b/cmd/server/main.go index 68feb0d..ee1cb8f 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,7 +6,6 @@ import ( "github.com/caicloud/nirvana" "github.com/caicloud/nirvana/log" - "github.com/caicloud/nirvana/plugins/reqlog" "github.com/spf13/pflag" "github.com/caicloud/nirvana-practice/pkg/apis" @@ -33,7 +32,6 @@ func main() { // initialize Server config config := nirvana.NewDefaultConfig().Configure( nirvana.Port(httpPort), - reqlog.Default(), nirvana.Descriptor(apis.Descriptor()), ) diff --git a/pkg/apis/api.go b/pkg/apis/api.go index ef20ea2..f08b763 100644 --- a/pkg/apis/api.go +++ b/pkg/apis/api.go @@ -5,7 +5,9 @@ package apis import ( v1alpha1 "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/descriptors" + middleware "github.com/caicloud/go-common/nirvana/middleware" def "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/log" ) // Descriptor returns a combined descriptor for APIs of all versions. @@ -13,8 +15,11 @@ func Descriptor() def.Descriptor { return def.Descriptor{ Description: "APIs", Path: "/api", - Consumes: []string{def.MIMEJSON}, - Produces: []string{def.MIMEJSON}, + Middlewares: []def.Middleware{ + middleware.Reqlog(log.DefaultLogger()), + }, + Consumes: []string{def.MIMEJSON}, + Produces: []string{def.MIMEJSON}, Children: []def.Descriptor{ v1alpha1.Descriptor(), }, diff --git a/pkg/apis/v1alpha1/descriptors/customers.go b/pkg/apis/v1alpha1/descriptors/customers.go index f392415..98c183e 100644 --- a/pkg/apis/v1alpha1/descriptors/customers.go +++ b/pkg/apis/v1alpha1/descriptors/customers.go @@ -5,7 +5,7 @@ import ( "github.com/caicloud/nirvana/operators/validator" meta "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" - "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/handlers" + "github.com/caicloud/nirvana-practice/pkg/handlers" ) func init() { diff --git a/pkg/apis/v1alpha1/descriptors/install.go b/pkg/apis/v1alpha1/descriptors/install.go index 0eaf924..b202d99 100644 --- a/pkg/apis/v1alpha1/descriptors/install.go +++ b/pkg/apis/v1alpha1/descriptors/install.go @@ -15,8 +15,10 @@ func Descriptor() definition.Descriptor { return definition.Descriptor{ Description: "v1alpha1 API", Path: "/v1alpha1", - Consumes: []string{definition.MIMEJSON}, - Produces: []string{definition.MIMEJSON}, - Children: descriptors, + // Add other content types exclude JSON for consume and produce if necessary, + // like text, octet-stream etc. + // Consumes: []string{definition.MIMEOctetStream}, + // Produces: []string{definition.MIMEText}, + Children: descriptors, } } diff --git a/pkg/apis/v1alpha1/descriptors/products.go b/pkg/apis/v1alpha1/descriptors/products.go index 2ae51d7..4351380 100644 --- a/pkg/apis/v1alpha1/descriptors/products.go +++ b/pkg/apis/v1alpha1/descriptors/products.go @@ -5,7 +5,7 @@ import ( "github.com/caicloud/nirvana/operators/validator" meta "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" - "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/handlers" + "github.com/caicloud/nirvana-practice/pkg/handlers" ) func init() { diff --git a/pkg/apis/v1alpha1/handlers/customers.go b/pkg/handlers/customers.go similarity index 61% rename from pkg/apis/v1alpha1/handlers/customers.go rename to pkg/handlers/customers.go index a87be13..e32e3d4 100644 --- a/pkg/apis/v1alpha1/handlers/customers.go +++ b/pkg/handlers/customers.go @@ -2,49 +2,28 @@ package handlers import ( "context" - "fmt" "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" api "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1" "github.com/caicloud/nirvana-practice/pkg/errors" ) -var defaultCustomer = &api.Customer{ - Metadata: v1.Metadata{ - Name: "Tony", - }, - Spec: &api.CustomerSpec{ - Sex: "Male", - }, -} - func CreateCustomer(ctx context.Context, customer *api.Customer) (*api.Customer, error) { return nil, errors.ErrorNotImplemented.Error() } func ListCustomers(ctx context.Context, options *v1.ListOptions) (*api.CustomersList, error) { - results := &api.CustomersList{ - Total: 1, - Items: []*api.Customer{defaultCustomer}, - } - - return results, nil + return nil, errors.ErrorNotImplemented.Error() } func GetCustomer(ctx context.Context, name string) (*api.Customer, error) { - fmt.Printf("Get customer: %s\n", name) - - return defaultCustomer, nil + return nil, errors.ErrorNotImplemented.Error() } func UpdateCustomer(ctx context.Context, name string, customer *api.Customer) (*api.Customer, error) { - fmt.Printf("Update customer: %s\n", name) - - return customer, nil + return nil, errors.ErrorNotImplemented.Error() } func DeleteCustomer(ctx context.Context, name string) error { - fmt.Printf("Delete customer: %s\n", name) - - return nil + return errors.ErrorNotImplemented.Error() } diff --git a/pkg/apis/v1alpha1/handlers/products.go b/pkg/handlers/products.go similarity index 61% rename from pkg/apis/v1alpha1/handlers/products.go rename to pkg/handlers/products.go index e6f1725..91b7331 100644 --- a/pkg/apis/v1alpha1/handlers/products.go +++ b/pkg/handlers/products.go @@ -2,49 +2,28 @@ package handlers import ( "context" - "fmt" "github.com/caicloud/nirvana-practice/pkg/apis/meta/v1" api "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1" "github.com/caicloud/nirvana-practice/pkg/errors" ) -var defaultProduct = &api.Product{ - Metadata: v1.Metadata{ - Name: "Apple", - }, - Spec: &api.ProductSpec{ - Category: "Fruit", - }, -} - func CreateProduct(ctx context.Context, product *api.Product) (*api.Product, error) { return nil, errors.ErrorNotImplemented.Error() } func ListProducts(ctx context.Context, options *v1.ListOptions) (*api.ProductsList, error) { - results := &api.ProductsList{ - Total: 1, - Items: []*api.Product{defaultProduct}, - } - - return results, nil + return nil, errors.ErrorNotImplemented.Error() } func GetProduct(ctx context.Context, name string) (*api.Product, error) { - fmt.Printf("Get product: %s\n", name) - - return defaultProduct, nil + return nil, errors.ErrorNotImplemented.Error() } func UpdateProduct(ctx context.Context, name string, product *api.Product) (*api.Product, error) { - fmt.Printf("Update product: %s\n", name) - - return product, nil + return nil, errors.ErrorNotImplemented.Error() } func DeleteProduct(ctx context.Context, name string) error { - fmt.Printf("Delete product: %s\n", name) - - return nil + return errors.ErrorNotImplemented.Error() } From ebf002b476345b5926df29a9b34c910bd3d75290 Mon Sep 17 00:00:00 2001 From: Robin Yue Date: Mon, 27 Apr 2020 16:59:21 +0800 Subject: [PATCH 4/5] update vendor --- go.mod | 2 +- go.sum | 28 +- vendor/github.com/caicloud/nirvana/go.mod | 11 +- vendor/github.com/caicloud/nirvana/go.sum | 34 +- .../caicloud/nirvana/plugins/reqlog/reqlog.go | 312 ------------------ vendor/modules.txt | 3 +- 6 files changed, 47 insertions(+), 343 deletions(-) delete mode 100644 vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go diff --git a/go.mod b/go.mod index b26cf65..841d65e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/caicloud/nirvana-practice go 1.13 require ( - github.com/caicloud/nirvana v0.2.3 + github.com/caicloud/nirvana v0.2.4 github.com/spf13/pflag v1.0.5 ) diff --git a/go.sum b/go.sum index 3383aab..55c5f77 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -8,8 +8,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/caicloud/nirvana v0.2.3 h1:/uGk7DswiFyvqGNYre+B7LS8lv4Bw/vAuUptDxbE6Rs= -github.com/caicloud/nirvana v0.2.3/go.mod h1:VixZq2ALgoVR8VyuiM+dxuX6Izk3T/NwlSMa1aSrE7Q= +github.com/caicloud/nirvana v0.2.4 h1:urCpyTufSTVYllpRJE6vTXOfsdgoDRv6KAqIlQlz+Ps= +github.com/caicloud/nirvana v0.2.4/go.mod h1:m1HTGrIULzQX6YvMJJtZ9nr/YXL3XwNAq7dR1MezLAY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -19,10 +19,13 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= @@ -46,7 +49,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -90,12 +94,15 @@ github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMW github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -104,14 +111,19 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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= diff --git a/vendor/github.com/caicloud/nirvana/go.mod b/vendor/github.com/caicloud/nirvana/go.mod index 48d7811..d17e4b8 100644 --- a/vendor/github.com/caicloud/nirvana/go.mod +++ b/vendor/github.com/caicloud/nirvana/go.mod @@ -4,20 +4,15 @@ go 1.13 require ( github.com/BurntSushi/toml v0.3.1 // indirect - github.com/PuerkitoBio/purell v1.1.0 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect github.com/fsnotify/fsnotify v1.4.7 // indirect - github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa // indirect - github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d // indirect - github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee - github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 // indirect + github.com/go-openapi/spec v0.19.7 + github.com/go-openapi/swag v0.19.9 // indirect github.com/go-playground/locales v0.12.1 // indirect github.com/go-playground/universal-translator v0.16.0 // indirect github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/magiconair/properties v1.8.0 // indirect - github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57 // indirect github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675 // indirect github.com/opentracing/opentracing-go v1.1.0 github.com/pelletier/go-toml v1.1.0 // indirect @@ -32,7 +27,7 @@ require ( github.com/uber/jaeger-client-go v2.22.1+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible // indirect go.uber.org/atomic v1.6.0 // indirect - golang.org/x/text v0.3.0 + golang.org/x/text v0.3.2 gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.17.1 gopkg.in/yaml.v2 v2.2.5 diff --git a/vendor/github.com/caicloud/nirvana/go.sum b/vendor/github.com/caicloud/nirvana/go.sum index d0c008e..ce6aced 100644 --- a/vendor/github.com/caicloud/nirvana/go.sum +++ b/vendor/github.com/caicloud/nirvana/go.sum @@ -1,7 +1,7 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -26,14 +26,17 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa h1:hr8WVDjg4JKtQptZpzyb196TmruCs7PIsdJz8KAOZp8= -github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d h1:k3UQ7Z8yFYq0BNkYykKIheY0HlZBl1Hku+pO9HE9FNU= -github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee h1:eo0HQoNFtbiEc7+1gRF9pgW6azx8a1cO2fXcqq1MuD0= -github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 h1:+vsw187FKvA2QUGAcE+vQSfyxqLbUXixPYRRMAzwu04= -github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/spec v0.19.7 h1:0xWSeMd35y5avQAThZR2PkEuqSosoS5t6gDH4L8n11M= +github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= @@ -64,8 +67,9 @@ 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/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57 h1:qhv1ir3dIyOFmFU+5KqG4dF3zSQTA4nn1DFhu2NQC44= -github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675 h1:/rdJjIiKG5rRdwG5yxHmSE/7ZREjpyC0kL7GxGT/qJw= @@ -136,6 +140,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -149,6 +155,9 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgm golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -158,6 +167,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= diff --git a/vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go b/vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go deleted file mode 100644 index 5461d49..0000000 --- a/vendor/github.com/caicloud/nirvana/plugins/reqlog/reqlog.go +++ /dev/null @@ -1,312 +0,0 @@ -/* -Copyright 2018 Caicloud Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package reqlog - -import ( - "context" - "time" - - "github.com/caicloud/nirvana" - "github.com/caicloud/nirvana/definition" - "github.com/caicloud/nirvana/log" - "github.com/caicloud/nirvana/service" -) - -func init() { - nirvana.RegisterConfigInstaller(&reqlogInstaller{}) -} - -// ExternalConfigName is the external config name of request logger. -const ExternalConfigName = "reqlog" - -// config is reqlog config. -type config struct { - doubleLog bool - sourceAddr bool - requestKey string - requestID bool - logger log.Logger -} - -type reqlogInstaller struct{} - -// Name is the external config name. -func (i *reqlogInstaller) Name() string { - return ExternalConfigName -} - -// Install installs stuffs before server starting. -func (i *reqlogInstaller) Install(builder service.Builder, cfg *nirvana.Config) error { - var err error - wrapper(cfg, func(c *config) { - begin, end := i.buildPrinters(c) - err = builder.AddDescriptor(definition.Descriptor{ - Path: "/", - Middlewares: []definition.Middleware{ - func(ctx context.Context, next definition.Chain) error { - start := time.Now() - httpCtx := service.HTTPContextFrom(ctx) - begin(httpCtx, nil) - - err := next.Continue(ctx) - end(httpCtx, map[string]interface{}{ - intervalDuration: time.Since(start), - responseError: err, - }) - return err - }, - }, - }) - }) - return err -} - -type printer func(ctx service.HTTPContext, data map[string]interface{}) - -const ( - intervalDuration = "intervalDuration" - responseError = "responseError" -) - -func (i *reqlogInstaller) buildPrinters(c *config) (begin printer, end printer) { - logger := c.logger - output := func(ctx service.HTTPContext, data map[string]interface{}, - components []func(ctx service.HTTPContext, data map[string]interface{}) interface{}) { - results := make([]interface{}, 0, len(components)) - for _, c := range components { - result := c(ctx, data) - if result == nil { - continue - } - results = append(results, result) - } - if logger != nil { - logger.Infoln(results...) - } else { - log.Infoln(results...) - } - } - method := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - return ctx.Request().Method - } - url := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - return ctx.Request().URL.String() - } - clientAddr := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - return ctx.Request().RemoteAddr - } - statusCode := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - return ctx.ResponseWriter().StatusCode() - } - contentLength := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - return ctx.ResponseWriter().ContentLength() - } - requestID := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - id := ctx.Request().Header.Get(c.requestKey) - if id == "" { - return nil - } - return id - } - interval := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - if data != nil { - if result, ok := data[intervalDuration]; ok { - return result - } - } - return nil - } - respErr := func(ctx service.HTTPContext, data map[string]interface{}) interface{} { - if data != nil { - if result, ok := data[responseError]; ok { - return result - } - } - return nil - } - - beginning := []func(ctx service.HTTPContext, data map[string]interface{}) interface{}{} - if c.doubleLog { - beginning = append(beginning, method, url) - if c.requestID { - beginning = append(beginning, requestID) - } - if c.sourceAddr { - beginning = append(beginning, clientAddr) - } - } - - ending := []func(ctx service.HTTPContext, data map[string]interface{}) interface{}{ - method, - statusCode, - contentLength, - interval, - url, - } - if c.requestID { - ending = append(ending, requestID) - } - if c.sourceAddr { - ending = append(ending, clientAddr) - } - ending = append(ending, respErr) - return func(ctx service.HTTPContext, data map[string]interface{}) { - if c.doubleLog { - output(ctx, data, beginning) - } - }, - func(ctx service.HTTPContext, data map[string]interface{}) { - output(ctx, data, ending) - } -} - -// Uninstall uninstalls stuffs after server terminating. -func (i *reqlogInstaller) Uninstall(builder service.Builder, cfg *nirvana.Config) error { - return nil -} - -// Disable returns a configurer to disable reqlog. -func Disable() nirvana.Configurer { - return func(c *nirvana.Config) error { - c.Set(ExternalConfigName, nil) - return nil - } -} - -// Default Configurer does nothing but ensure default config was set. -func Default() nirvana.Configurer { - return func(c *nirvana.Config) error { - wrapper(c, func(c *config) { - c.logger = nil - }) - return nil - } -} - -// Logger Configurer sets logger. -func Logger(l log.Logger) nirvana.Configurer { - return func(c *nirvana.Config) error { - wrapper(c, func(c *config) { - c.logger = l - }) - return nil - } -} - -// DoubleLog returns a configurer to enable or -// disable double log. If it's enabled, every -// request outputs two entries. One for starting -// and another for ending. If it's disabled, only -// outputs ending entry. -// Defaults to false. -func DoubleLog(enable bool) nirvana.Configurer { - return func(c *nirvana.Config) error { - wrapper(c, func(c *config) { - c.doubleLog = enable - }) - return nil - } -} - -// SourceAddr returns a configurer to enable or -// disable showing source addr. -// Defaults to false. -func SourceAddr(enable bool) nirvana.Configurer { - return func(c *nirvana.Config) error { - wrapper(c, func(c *config) { - c.sourceAddr = enable - }) - return nil - } -} - -// RequestID returns a configurer to enable or -// disable showing request id. -// Defaults to false. -func RequestID(enable bool) nirvana.Configurer { - return func(c *nirvana.Config) error { - wrapper(c, func(c *config) { - c.requestID = enable - }) - return nil - } -} - -// RequestIDKey returns a configurer to set header key -// of request id. -// Defaults to X-Request-Id. -func RequestIDKey(key string) nirvana.Configurer { - if key == "" { - key = "X-Request-Id" - } - return func(c *nirvana.Config) error { - wrapper(c, func(c *config) { - c.requestKey = key - }) - return nil - } -} - -func wrapper(c *nirvana.Config, f func(c *config)) { - conf := c.Config(ExternalConfigName) - var cfg *config - if conf == nil { - // Default config. - cfg = &config{ - requestKey: "X-Request-Id", - } - } else { - // Panic if config type is wrong. - cfg = conf.(*config) - } - f(cfg) - c.Set(ExternalConfigName, cfg) -} - -// Option contains basic configurations of reqlog. -type Option struct { - DoubleLog bool `desc:"Output two entries for every request"` - SourceAddr bool `desc:"Output source addr for request log"` - RequestID bool `desc:"Output request id for request log"` - RequestIDKey string `desc:"Request header key for request id"` -} - -// NewDefaultOption creates default option. -func NewDefaultOption() *Option { - return &Option{ - DoubleLog: false, - SourceAddr: false, - RequestID: false, - RequestIDKey: "X-Request-Id", - } -} - -// Name returns plugin name. -func (p *Option) Name() string { - return ExternalConfigName -} - -// Configure configures nirvana config via current options. -func (p *Option) Configure(cfg *nirvana.Config) error { - cfg.Configure( - DoubleLog(p.DoubleLog), - SourceAddr(p.SourceAddr), - RequestID(p.RequestID), - RequestIDKey(p.RequestIDKey), - ) - return nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 45bc956..c6823a4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,10 +1,9 @@ -# github.com/caicloud/nirvana v0.2.3 +# github.com/caicloud/nirvana v0.2.4 github.com/caicloud/nirvana github.com/caicloud/nirvana/definition github.com/caicloud/nirvana/errors github.com/caicloud/nirvana/log github.com/caicloud/nirvana/operators/validator -github.com/caicloud/nirvana/plugins/reqlog github.com/caicloud/nirvana/service github.com/caicloud/nirvana/service/router github.com/caicloud/nirvana/utils/api From a263e9b42f3ab8c3d678a75a1a47784fb942ff25 Mon Sep 17 00:00:00 2001 From: Robin Yue Date: Tue, 28 Apr 2020 00:20:46 +0800 Subject: [PATCH 5/5] implement log middleware --- pkg/apis/api.go | 2 +- pkg/middleware/middleware.go | 33 +++++++ pkg/middleware/middleware_test.go | 144 ++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 pkg/middleware/middleware.go create mode 100644 pkg/middleware/middleware_test.go diff --git a/pkg/apis/api.go b/pkg/apis/api.go index f08b763..adaa5f6 100644 --- a/pkg/apis/api.go +++ b/pkg/apis/api.go @@ -4,8 +4,8 @@ package apis import ( v1alpha1 "github.com/caicloud/nirvana-practice/pkg/apis/v1alpha1/descriptors" + "github.com/caicloud/nirvana-practice/pkg/middleware" - middleware "github.com/caicloud/go-common/nirvana/middleware" def "github.com/caicloud/nirvana/definition" "github.com/caicloud/nirvana/log" ) diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go new file mode 100644 index 0000000..6829e4b --- /dev/null +++ b/pkg/middleware/middleware.go @@ -0,0 +1,33 @@ +package middleware + +import ( + "context" + "time" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/log" + "github.com/caicloud/nirvana/service" +) + +// Reqlog middleware functions similarly to the reqlog plugin from Nirvana. It prints the basic +// information on every request and response that passes through it. +func Reqlog(logger log.Logger) definition.Middleware { + return func(ctx context.Context, chain definition.Chain) error { + start := time.Now() + httpCtx := service.HTTPContextFrom(ctx) + + err := chain.Continue(ctx) + + request := httpCtx.Request() + response := httpCtx.ResponseWriter() + logger.Infoln( + request.Method, + response.StatusCode(), + response.ContentLength(), + time.Since(start).String(), + request.URL.String(), + ) + + return err + } +} diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go new file mode 100644 index 0000000..e58a851 --- /dev/null +++ b/pkg/middleware/middleware_test.go @@ -0,0 +1,144 @@ +package middleware + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "regexp" + "strings" + "testing" + + "github.com/caicloud/nirvana/definition" + "github.com/caicloud/nirvana/log" + "github.com/caicloud/nirvana/service" +) + +// responseWriter is a trivial implementation of service.ResponseWriter from Nirvana; it makes it +// convenient to build fake HTTP requests for testing purposes. +// TODO: move this to a dedicated package for testing utilities so it can be reused. +type responseWriter struct { + code int + header http.Header + buf *bytes.Buffer +} + +func newRW() *responseWriter { + return &responseWriter{0, http.Header{}, bytes.NewBuffer(nil)} +} + +func (r *responseWriter) Header() http.Header { + return r.header +} + +func (r *responseWriter) Write(d []byte) (int, error) { + return r.buf.Write(d) +} + +func (r *responseWriter) WriteHeader(code int) { + r.code = code +} + +func mustURL(rawURL string) *url.URL { + u, _ := url.Parse(rawURL) + return u +} + +func TestReqlog(t *testing.T) { + buildService := func() (service.Service, error) { + builder := service.NewBuilder() + builder.SetModifier(service.FirstContextParameter()) + if err := builder.AddDescriptor( + definition.Descriptor{ + Path: "/api/v1", + Definitions: []definition.Definition{}, + Consumes: []string{"application/json"}, + Produces: []string{"application/json"}, + Middlewares: []definition.Middleware{Reqlog(log.NewStdLogger(0))}, + Children: []definition.Descriptor{ + { + Path: "/foo", + Definitions: []definition.Definition{ + { + Method: definition.Create, + Parameters: []definition.Parameter{ + definition.QueryParameterFor("query", ""), + definition.BodyParameterFor(""), + }, + Function: func(ctx context.Context, query string, body []byte) (string, error) { + request := service.HTTPContextFrom(ctx).Request() + return fmt.Sprintf( + "%s %s %s %s", + request.Method, request.URL.String(), + query, string(body), + ), nil + }, + Results: definition.DataErrorResults(""), + }, + }, + }, + }, + }, + ); err != nil { + return nil, err + } + return builder.Build() + } + tests := []struct { + req http.Request + wantRegex string + respStr string + respCode int + }{ + { + req: http.Request{ + Method: http.MethodPost, + URL: mustURL("/api/v1/foo?query=bar"), + Header: http.Header{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + }, + Body: ioutil.NopCloser(strings.NewReader("{\"key\": \"value\"}")), + }, + wantRegex: `^INFO\s(.+) | POST 201 61 [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ /api/v1/foo?query=bar$`, + respStr: `POST /api/v1/foo?query=bar bar {"key": "value"}`, + respCode: 201, + }, + } + for _, tc := range tests { + old := os.Stderr + r, w, _ := os.Pipe() + os.Stderr = w + outC := make(chan string) + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + + svc, err := buildService() + if err != nil { + t.Fatal(err) + } + rw := newRW() + svc.ServeHTTP(rw, &tc.req) + + w.Close() + out := <-outC + os.Stderr = old + + if rw.buf.String() != tc.respStr { + t.Fatal("unexpected response content, the middleware might have altered the request/response") + } + if rw.code != tc.respCode { + t.Fatal("unexpected response code, the middleware might have altered the request/response") + } + if !regexp.MustCompile(tc.wantRegex).MatchString(out) { + t.Fatalf("unexpected log content: \"%s\"", out) + } + } +}