v1.0.0
This commit is contained in:
兔子
2023-03-27 13:29:19 +08:00
parent f2697ed9d1
commit 059ab698b6
22 changed files with 2864 additions and 0 deletions

26
go.mod Normal file
View File

@@ -0,0 +1,26 @@
module github.com/unti-io/go-utils
go 1.20
require (
github.com/goccy/go-json v0.10.2
github.com/spf13/cast v1.5.0
github.com/spf13/viper v1.15.0
golang.org/x/crypto v0.7.0
)
require (
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

481
go.sum Normal file
View File

@@ -0,0 +1,481 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
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/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
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.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20190227155943-e225da77a7e6/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/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

174
utils/app.go Normal file
View File

@@ -0,0 +1,174 @@
package utils
import "net/url"
func init() {
Is.Ip = IsIp
Is.Url = IsUrl
Is.Email = IsEmail
Is.Phone = IsPhone
Is.Mobile = IsMobile
Is.Empty = IsEmpty
Is.True = IsTrue
Is.False = IsFalse
Is.Number = IsNumber
Is.Float = IsFloat
Is.Bool = IsBool
Is.Accepted = IsAccepted
Is.Date = IsDate
Is.Alpha = IsAlpha
Is.AlphaNum = IsAlphaNum
Is.AlphaDash = IsAlphaDash
Is.Chs = IsChs
Is.ChsAlpha = IsChsAlpha
Is.ChsAlphaNum = IsChsAlphaNum
Is.ChsDash = IsChsDash
Is.Cntrl = IsCntrl
Is.Graph = IsGraph
Is.Lower = IsLower
Is.Upper = IsUpper
Is.Space = IsSpace
Is.Xdigit = IsXdigit
Is.ActiveUrl = IsActiveUrl
Is.Domain = IsDomain
Is.IdCard = IsIdCard
Is.MacAddr = IsMacAddr
Is.Zip = IsZip
Is.String = IsString
Is.Slice = IsSlice
Is.Array = IsArray
Is.JsonString = IsJsonString
Is.Map = IsMap
Is.SliceSlice = IsSliceSlice
Is.MapAny = IsMapAny
// Env.Toml = EnvToml
Get.Type = GetType
Get.Ip = GetIp
In.Array = InArray
Array.Filter = ArrayFilter
Array.Remove = ArrayRemove
Password.Create = PasswordCreate
Password.Verify = PasswordVerify
Rand.Int = RandInt
Rand.String = RandString
Struct.Set = StructSet
Struct.Get = StructGet
Struct.Del = StructDel
Struct.Has = StructHas
Struct.Keys = StructKeys
Struct.Values = StructValues
Struct.Len = StructLen
Struct.Map = StructMap
Struct.Slice = StructSlice
Json.Encode = JsonEncode
Json.Decode = JsonDecode
Format.Query = FormatQuery
Parse.ParamsBefore = ParseParamsBefore
Parse.Params = ParseParams
Net.Tcping = NetTcping
File.List = FileList
File.Bytes = FileBytes
Mime.Type = MimeType
}
var Is struct {
Ip func(ip any) (ok bool)
Url func(url any) (ok bool)
Email func(email any) (ok bool)
Phone func(phone any) (ok bool)
Mobile func(mobile any) (ok bool)
Empty func(value any) (ok bool)
True func(value any) (ok bool)
False func(value any) (ok bool)
Number func(value any) (ok bool)
Float func(value any) (ok bool)
Bool func(value any) (ok bool)
Accepted func(value any) (ok bool)
Date func(date any) (ok bool)
Alpha func(value any) (ok bool)
AlphaNum func(value any) (ok bool)
AlphaDash func(value any) (ok bool)
Chs func(value any) (ok bool)
ChsAlpha func(value any) (ok bool)
ChsAlphaNum func(value any) (ok bool)
ChsDash func(value any) (ok bool)
Cntrl func(value any) (ok bool)
Graph func(value any) (ok bool)
Lower func(value any) (ok bool)
Upper func(value any) (ok bool)
Space func(value any) (ok bool)
Xdigit func(value any) (ok bool)
ActiveUrl func(value any) (ok bool)
Domain func(domain any) (ok bool)
IdCard func(value any) (ok bool)
MacAddr func(value any) (ok bool)
Zip func(value any) (ok bool)
String func(value any) (ok bool)
Slice func(value any) (ok bool)
Array func(value any) (ok bool)
JsonString func(value any) (ok bool)
Map func(value any) (ok bool)
SliceSlice func(value any) (ok bool)
MapAny func(value any) (ok bool)
}
var Get struct {
Type func(value any) (result string)
Ip func(key ...string) (result any)
Resolution func(index int) (size int)
}
var In struct {
Array func(value any, array []any) (ok bool)
}
var Array struct {
Filter func(array []string) (slice []string)
Remove func(array []string, args ...string) (slice []string)
}
var Password struct {
Create func(password any) (result string)
Verify func(encode any, password any) (ok bool)
}
var Rand struct {
Int func(max int, min ...int) (result int)
String func(length int, chars ...string) (result string)
}
var Struct struct {
Set func(obj any, key string, val any)
Get func(obj any, key string) (result any)
Del func(obj any, key string)
Has func(obj any, key string) (ok bool)
Keys func(obj any) (slice []string)
Values func(obj any) (slice []any)
Len func(obj any) (length int)
Map func(obj any) (result map[string]any)
Slice func(obj any) (slice []any)
}
var Json struct {
Encode func(value any) (result string)
Decode func(value string) (result any)
}
var Format struct {
Query func(data any) (result string)
}
var Parse struct {
ParamsBefore func(params url.Values) (result map[string]any)
Params func(params map[string]any) (result map[string]any)
}
var Net struct {
Tcping func(host any, opts ...map[string]any) (ok bool, detail []map[string]any)
}
var File struct {
List func(path any, opt ...map[string]any) (slice []string)
Bytes func(path any) []byte
}
var Mime struct {
Type func(suffix any) (mime string)
}

31
utils/array.go Normal file
View File

@@ -0,0 +1,31 @@
package utils
import (
"strings"
)
// ArrayFilter - 数组过滤
func ArrayFilter(array []string) (slice []string) {
for key, val := range array {
if (key > 0 && array[key-1] == val) || len(val) == 0 {
continue
}
slice = append(slice, val)
}
return
}
// ArrayRemove - 数组删除
func ArrayRemove(array []string, args ...string) []string {
for _, value := range args {
for key, val := range array {
// 去除空格
val = strings.TrimSpace(val)
// 根据索引删除
if val == value {
array = append(array[:key], array[key+1:]...)
}
}
}
return array
}

107
utils/async.go Normal file
View File

@@ -0,0 +1,107 @@
package utils
import (
"github.com/spf13/cast"
"reflect"
"sync"
)
type AsyncStruct[T any] struct {
// 读写锁
Mutex sync.RWMutex
// 等待组
Wait sync.WaitGroup
// 数据
Data T
}
// Async - 异步数据
func Async[T any]() *AsyncStruct[T] {
var data T
switch reflect.TypeOf(data).Kind() {
case reflect.Slice:
data = reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(data).Elem()), 0, 0).Interface().(T)
case reflect.Map:
data = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(data).Key(), reflect.TypeOf(data).Elem())).Interface().(T)
default:
data = reflect.Zero(reflect.TypeOf(data)).Interface().(T)
}
return &AsyncStruct[T]{
Mutex: sync.RWMutex{},
Wait : sync.WaitGroup{},
Data : data,
}
}
// Get - 获取数据
func (this *AsyncStruct[T]) Get(key string) any {
defer this.Mutex.Unlock()
this.Mutex.Lock()
if IsEmpty(this.Data) {
return nil
}
typeof := reflect.TypeOf(this.Data)
if typeof.Kind() == reflect.Map && typeof.Key().Kind() == reflect.String {
item := cast.ToStringMap(this.Data)
return item[key]
} else if typeof.Kind() == reflect.Slice {
item := cast.ToSlice(this.Data)
return item[cast.ToInt(key)]
}
return this.Data
}
// Set - 设置数据
func (this *AsyncStruct[T]) Set(key string, val any) {
defer this.Mutex.Unlock()
this.Mutex.Lock()
typeof := reflect.TypeOf(this.Data)
if typeof.Kind() == reflect.Map && typeof.Key().Kind() == reflect.String {
item := cast.ToStringMap(this.Data)
item[key] = val
} else if typeof.Kind() == reflect.Slice {
index := cast.ToInt(key)
item := cast.ToSlice(this.Data)
if len(item) > index {
item[index] = val
}
} else {
this.Data = val.(T)
}
}
// Has - 判断是否存在
func (this *AsyncStruct[T]) Has(key string) (ok bool) {
defer this.Mutex.Unlock()
this.Mutex.Lock()
if IsEmpty(this.Data) {
return false
}
typeof := reflect.TypeOf(this.Data)
if typeof.Kind() == reflect.Map && typeof.Key().Kind() == reflect.String {
item := cast.ToStringMap(this.Data)
_, ok = item[key]
} else {
ok = true
}
return ok
}
// Result - 获取所有数据
func (this *AsyncStruct[T]) Result() T {
defer this.Mutex.Unlock()
this.Mutex.Lock()
return this.Data
}

311
utils/curl.go Normal file
View File

@@ -0,0 +1,311 @@
package utils
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/spf13/cast"
"io"
"mime/multipart"
"net/http"
"net/url"
"strings"
)
// CurlRequest - 发起请求的结构体
type CurlRequest struct {
Body any
Url string
Method string
Client *http.Client
Data map[string]string
Query map[string]string
Headers map[string]string
}
// CurlResponse - 响应的结构体
type CurlResponse struct {
StatusCode int
Request *http.Request
Headers *http.Header
Body *io.ReadCloser
Byte []byte
Text string
Json map[string]any
Error error
}
// CurlStruct - Curl 结构体
type CurlStruct struct {
request *CurlRequest
response *CurlResponse
}
// Curl - 发起请求 - 入口
func Curl(request ...CurlRequest) *CurlStruct {
if len(request) == 0 {
request = append(request, CurlRequest{})
}
if IsEmpty(request[0].Method) {
request[0].Method = "GET"
}
if IsEmpty(request[0].Data) {
request[0].Data = make(map[string]string)
}
if IsEmpty(request[0].Query) {
request[0].Query = make(map[string]string)
}
if IsEmpty(request[0].Headers) {
request[0].Headers = make(map[string]string)
}
if IsEmpty(request[0].Client) {
request[0].Client = &http.Client{}
}
return &CurlStruct{
request : &request[0],
response: &CurlResponse{
Json : make(map[string]any),
},
}
}
// Get - 发起 GET 请求
func (this *CurlStruct) Get(url string) *CurlStruct {
this.request.Url = url
this.request.Method = "GET"
return this
}
// Post - 发起 POST 请求
func (this *CurlStruct) Post(url string) *CurlStruct {
this.request.Url = url
this.request.Method = "POST"
return this
}
// Put - 发起 PUT 请求
func (this *CurlStruct) Put(url string) *CurlStruct {
this.request.Url = url
this.request.Method = "PUT"
return this
}
// Patch - 发起 PATCH 请求
func (this *CurlStruct) Patch(url string) *CurlStruct {
this.request.Url = url
this.request.Method = "PATCH"
return this
}
// Delete - 发起 DELETE 请求
func (this *CurlStruct) Delete(url string) *CurlStruct {
this.request.Url = url
this.request.Method = "DELETE"
return this
}
// Method - 定义请求类型 - 默认 GET
func (this *CurlStruct) Method(method string) *CurlStruct {
this.request.Method = strings.ToUpper(method)
return this
}
// Url - 定义请求地址
func (this *CurlStruct) Url(url string) *CurlStruct {
this.request.Url = url
return this
}
// Header - 定义请求头
func (this *CurlStruct) Header(key any, value any) *CurlStruct {
this.request.Headers[cast.ToString(key)] = cast.ToString(value)
return this
}
// Headers - 批量定义请求头
func (this *CurlStruct) Headers(headers map[string]any) *CurlStruct {
for key, val := range headers {
this.request.Headers[cast.ToString(key)] = cast.ToString(val)
}
return this
}
// Param - 定义请求参数
func (this *CurlStruct) Query(key any, value any) *CurlStruct {
this.request.Query[cast.ToString(key)] = cast.ToString(value)
return this
}
// Params - 批量定义请求参数
func (this *CurlStruct) Querys(params map[string]any) *CurlStruct {
for key, val := range params {
this.request.Query[cast.ToString(key)] = cast.ToString(val)
}
return this
}
// Data - 定义请求数据
func (this *CurlStruct) Data(key string, value any) *CurlStruct {
this.request.Data[key] = cast.ToString(value)
return this
}
// Datas - 批量定义请求数据
func (this *CurlStruct) Datas(data map[string]any) *CurlStruct {
for key, val := range data {
this.request.Data[key] = cast.ToString(val)
}
return this
}
// Body - 定义请求体
func (this *CurlStruct) Body(body any) *CurlStruct {
this.request.Body = body
return this
}
// Client - 定义请求客户端
func (this *CurlStruct) Client(client *http.Client) *CurlStruct {
this.request.Client = client
return this
}
// Send - 发起请求
func (this *CurlStruct) Send() *CurlResponse {
if IsEmpty(this.request.Url) {
this.response.Error = errors.New("url is required")
return this.response
}
// Encode query parameters if any
if len(this.request.Query) > 0 {
query := url.Values{}
for key, val := range this.request.Query {
query.Add(key, val)
}
this.request.Url += "?" + query.Encode()
}
// Create request object
var buffer []byte
contentType, ok := this.request.Headers["Content-Type"]
if ok {
switch {
case strings.Contains(contentType, "application/json"):
buffer, _ = json.Marshal(this.request.Body)
case strings.Contains(contentType, "application/x-www-form-urlencoded"):
form := url.Values{}
for key, val := range this.request.Data {
form.Add(key, val)
}
buffer = []byte(form.Encode())
case strings.Contains(contentType, "multipart/form-data"):
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for key, val := range this.request.Data {
writer.WriteField(key, val)
}
// add file field to request
if file, ok := this.request.Body.(*multipart.FileHeader); ok {
filePart, err := writer.CreateFormFile("file", file.Filename)
if err != nil {
this.response.Error = err
return this.response
}
item, err := file.Open()
if err != nil {
this.response.Error = err
return this.response
}
defer item.Close()
_, err = io.Copy(filePart, item)
if err != nil {
this.response.Error = err
return this.response
}
}
err := writer.Close()
if err != nil {
this.response.Error = err
return this.response
}
this.request.Headers["Content-Type"] = writer.FormDataContentType()
buffer = body.Bytes()
default:
buffer = []byte(fmt.Sprintf("%v", this.request.Body))
}
}
req, err := http.NewRequest(strings.ToUpper(this.request.Method), this.request.Url, bytes.NewBuffer(buffer))
if err != nil {
this.response.Error = err
return this.response
}
for key, val := range this.request.Headers {
req.Header.Set(key, val)
}
// Make HTTP request
response, err := this.request.Client.Do(req)
if err != nil {
this.response.Error = err
return this.response
}
defer response.Body.Close()
// Read response body
body, err := io.ReadAll(response.Body)
if err != nil {
this.response.Error = err
return this.response
}
// Set response
this.response.Byte = body
this.response.Body = &response.Body
this.response.Text = string(body)
this.response.Headers = &response.Header
this.response.Request = response.Request
this.response.Json = cast.ToStringMap(JsonDecode(string(body)))
this.response.StatusCode = response.StatusCode
return this.response
}
// Redirect - 获取重定向地址
func Redirect(url any) (result string) {
item := Curl(CurlRequest{
Method: "GET",
Url: cast.ToString(url),
Client: &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
},
}).Send()
if item.Error != nil {
result = item.Error.Error()
return
}
if item.StatusCode == 301 || item.StatusCode == 302 {
result = Redirect(item.Headers.Get("Location"))
return
}
result = item.Request.URL.String()
return
}

152
utils/env.go Normal file
View File

@@ -0,0 +1,152 @@
package utils
import (
"fmt"
"github.com/spf13/cast"
"github.com/spf13/viper"
"strings"
"sync"
)
// EnvToml - 读取配置文件
func EnvToml(args ...string) (result any) {
key, def, path := "app", "", "./config/app.toml"
if len(args) > 0 {
for k, v := range args {
if k == 0 {
key = v
} else if k == 1 {
def = v
} else {
path = v
}
}
}
keys := strings.Split(key, ".")
// 文件路径
viper.SetConfigFile(path)
err := viper.ReadInConfig()
if err != nil {
fmt.Println("读取配置文件失败:", err)
}
if len(keys) == 1 {
result := viper.GetStringMap(key)
if empty := Is.Empty(result); empty {
return def
}
return result
} else {
result = viper.GetStringMap(keys[0])[keys[1]]
if empty := Is.Empty(result); empty {
return def
}
return
}
}
type EnvModel struct {
path string
mode string
}
func Env() *EnvModel {
return &EnvModel{
path: "./config/app.toml",
mode: "toml",
}
}
func (this *EnvModel) Path(path string) *EnvModel {
this.path = path
return this
}
func (this *EnvModel) Mode(mode string) *EnvModel {
this.mode = mode
return this
}
func (this *EnvModel) All() (result map[string]any) {
if this.mode == "toml" {
return cast.ToStringMap(this.TomlAll())
}
return nil
}
func (this *EnvModel) Get(key any, def ...any) (result any) {
if this.mode == "toml" {
result = this.TomlAll()
}
item := cast.ToStringMap(result)
// 默认值处理
if item[cast.ToString(key)] == nil {
if len(def) > 0 {
return def[0]
}
return nil
}
return item[cast.ToString(key)]
}
func (this *EnvModel) TomlAll() (result any) {
// 文件路径
viper.SetConfigFile(this.path)
err := viper.ReadInConfig()
if err != nil {
fmt.Println("读取配置文件失败: %v", err)
return nil
}
toml := viper.AllSettings()
// 读写锁 - 防止并发写入
type wrLock struct {
Lock *sync.RWMutex
Data map[string]any
}
wr := wrLock{
Data: make(map[string]any),
Lock: &sync.RWMutex{},
}
wg := sync.WaitGroup{}
for key, val := range toml {
wg.Add(1)
go func(key string, val any) {
defer wg.Done()
for k, v := range cast.ToStringMap(val) {
wr.Lock.Lock()
wr.Data[key+"."+k] = v
wr.Lock.Unlock()
}
}(key, val)
}
wg.Wait()
return wr.Data
}

95
utils/file.go Normal file
View File

@@ -0,0 +1,95 @@
package utils
import (
"github.com/spf13/cast"
"os"
"path/filepath"
)
// FileList 获取指定目录下的所有文件
func FileList(path any, opt ...map[string]any) (slice []string) {
// 默认参数
defOpt := map[string]any{
// 获取指定后缀的文件
"ext": []string{"*"},
// 包含子目录
"sub": true,
// 返回路径格式
"format": "network",
// 域名
"domain": "",
// 过滤前缀
"prefix": "",
}
if len(opt) != 0 {
// 合并参数
for key, val := range defOpt {
if opt[0][key] == nil {
opt[0][key] = val
}
}
} else {
// 默认参数
opt = append(opt, defOpt)
}
conf := opt[0]
err := filepath.Walk(cast.ToString(path), func(path string, info os.FileInfo, err error) error {
// 忽略当前目录
if info.IsDir() {
return nil
}
// 忽略子目录
if !conf["sub"].(bool) && filepath.Dir(path) != path {
return nil
}
// []string 转 []any
var exts []any
for _, v := range conf["ext"].([]string) {
exts = append(exts, v)
}
// 忽略指定后缀
if !InArray("*", exts) && !InArray(filepath.Ext(path), exts) {
return nil
}
slice = append(slice, path)
return nil
})
if err != nil {
return []string{}
}
// 转码为网络路径
if conf["format"] == "network" {
for key, val := range slice {
slice[key] = filepath.ToSlash(val)
if !IsEmpty(conf["domain"]) {
// root, _ := os.Getwd()
// slice[key] = cast.ToString(conf["domain"]) + slice[key][len(root) + len(cast.ToString(conf["prefix"])):]
slice[key] = cast.ToString(conf["domain"]) + slice[key][len(cast.ToString(conf["prefix"])):]
}
}
}
return
}
// FileBytes 获取文件字节
func FileBytes(path any) []byte {
// 读取文件
file, err := os.Open(cast.ToString(path))
if err != nil {
return []byte{}
}
defer file.Close()
// 获取文件大小
info, _ := file.Stat()
size := info.Size()
// 读取文件
data := make([]byte, size)
file.Read(data)
return data
}

28
utils/format.go Normal file
View File

@@ -0,0 +1,28 @@
package utils
import (
"github.com/spf13/cast"
"sort"
"strings"
)
// FormatQuery 转 Query 格式
func FormatQuery(data any) (result string) {
body := cast.ToStringMap(data)
// ========== 此处解决 map 无序问题 - 开始 ==========
keys := make([]string, 0, len(body))
for key := range body {
keys = append(keys, key)
}
// 排序 keys
sort.Strings(keys)
// ========== 此处解决 map 无序问题 - 开始 ==========
for key := range keys {
result += keys[key] + "=" + cast.ToString(body[keys[key]]) + "&"
}
return strings.TrimRight(result, "&")
}

65
utils/get.go Normal file
View File

@@ -0,0 +1,65 @@
package utils
import (
"fmt"
"net"
"strings"
"sync"
)
// GetType - 获取数据类型
func GetType(value any) (result string) {
result, _ = typeof(value)
return result
}
// GetIp - 获取客户端IP
func GetIp(key ...string) (result any) {
type lock struct {
Lock *sync.RWMutex
Data map[string]any
}
wr := lock{
Data: make(map[string]any, 2),
Lock: &sync.RWMutex{},
}
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
conn, _ := net.Dial("udp", "8.8.8.8:80")
defer func(conn net.Conn) {
err := conn.Close()
if err != nil {
fmt.Println("intranet err", err)
}
}(conn)
localAddr := conn.LocalAddr().String()
idx := strings.LastIndex(localAddr, ":")
wr.Lock.Lock()
wr.Data["intranet"] = localAddr[0:idx]
wr.Lock.Unlock()
}()
go func() {
defer wg.Done()
// 外网IP - 替代品https://api.ipify.org https://ipinfo.io/ip https://api.ip.sb/ip
item := Curl().Get("https://myexternalip.com/raw").Send()
if item.Error != nil {
return
}
wr.Lock.Lock()
wr.Data["extranet"] = item.Text
wr.Lock.Unlock()
}()
wg.Wait()
if len(key) > 0 {
return wr.Data[key[0]]
}
return wr.Data
}

1
utils/image.go Normal file
View File

@@ -0,0 +1 @@
package utils

11
utils/in.go Normal file
View File

@@ -0,0 +1,11 @@
package utils
// InArray - 判断某个值是否在数组中
func InArray(value any, array []any) (ok bool) {
for _, v := range array {
if v == value {
return true
}
}
return false
}

199
utils/is.go Normal file
View File

@@ -0,0 +1,199 @@
package utils
import (
"github.com/spf13/cast"
"reflect"
"regexp"
)
// IsEmail - 是否为邮箱
func IsEmail(email any) (ok bool) {
return regexp.MustCompile(`\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}`).MatchString(cast.ToString(email))
}
// IsPhone - 是否为手机号
func IsPhone(phone any) (ok bool) {
return regexp.MustCompile(`^1[3456789]\d{9}$`).MatchString(cast.ToString(phone))
}
// IsMobile - 是否为手机号
func IsMobile(value any) (ok bool) {
return regexp.MustCompile(`^1[3456789]\d{9}$`).MatchString(cast.ToString(value))
}
// IsEmpty - 是否为空
func IsEmpty(value any) (ok bool) {
_, empty := typeof(value)
return empty
}
// IsDomain - 是否为域名
func IsDomain(domain any) (ok bool) {
return regexp.MustCompile(`^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+`).MatchString(cast.ToString(domain))
}
// IsTrue - 是否为真
func IsTrue(value any) (ok bool) {
return cast.ToBool(value)
}
// IsFalse - 是否为假
func IsFalse(value any) (ok bool) {
return !cast.ToBool(value)
}
// IsNumber - 是否为数字
func IsNumber(value any) (ok bool) {
return regexp.MustCompile(`^[0-9]+$`).MatchString(cast.ToString(value))
}
// IsFloat - 是否为浮点数
func IsFloat(value any) (ok bool) {
return regexp.MustCompile(`^[0-9]+(.[0-9]+)?$`).MatchString(cast.ToString(value))
}
// IsBool - 是否为bool
func IsBool(value any) (ok bool) {
_, check := value.(bool)
return check
}
// IsAccepted - 验证某个字段是否为为 yes, on, 或是 1
func IsAccepted(value any) (ok bool) {
return regexp.MustCompile(`^(yes|on|1)$`).MatchString(cast.ToString(value))
}
// IsDate - 是否为日期类型
func IsDate(date any) (ok bool) {
return regexp.MustCompile(`^\d{4}-\d{1,2}-\d{1,2}$`).MatchString(cast.ToString(date))
}
// IsAlpha - 只能包含字母
func IsAlpha(value any) (ok bool) {
return regexp.MustCompile(`^[a-zA-Z]+$`).MatchString(cast.ToString(value))
}
// IsAlphaNum - 只能包含字母和数字
func IsAlphaNum(value any) (ok bool) {
return regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(cast.ToString(value))
}
// IsAlphaDash - 只能包含字母、数字和下划线_及破折号-
func IsAlphaDash(value any) (ok bool) {
return regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString(cast.ToString(value))
}
// IsChs - 是否为汉字
func IsChs(value any) (ok bool) {
return regexp.MustCompile(`^[\x{4e00}-\x{9fa5}]+$`).MatchString(cast.ToString(value))
}
// IsChsAlpha - 只能是汉字、字母
func IsChsAlpha(value any) (ok bool) {
return regexp.MustCompile(`^[\x{4e00}-\x{9fa5}a-zA-Z]+$`).MatchString(cast.ToString(value))
}
// IsChsAlphaNum - 只能是汉字、字母和数字
func IsChsAlphaNum(value any) (ok bool) {
return regexp.MustCompile(`^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$`).MatchString(cast.ToString(value))
}
// IsChsDash - 只能是汉字、字母、数字和下划线_及破折号-
func IsChsDash(value any) (ok bool) {
return regexp.MustCompile(`^[\x{4e00}-\x{9fa5}a-zA-Z0-9_-]+$`).MatchString(cast.ToString(value))
}
// IsCntrl - 是否为控制字符 - (换行、缩进、空格)
func IsCntrl(value any) (ok bool) {
return regexp.MustCompile(`^[\x00-\x1F\x7F]+$`).MatchString(cast.ToString(value))
}
// IsGraph - 是否为可见字符 - (除空格外的所有可打印字符)
func IsGraph(value any) (ok bool) {
return regexp.MustCompile(`^[\x21-\x7E]+$`).MatchString(cast.ToString(value))
}
// IsLower - 是否为小写字母
func IsLower(value any) (ok bool) {
return regexp.MustCompile(`^[a-z]+$`).MatchString(cast.ToString(value))
}
// IsUpper - 是否为大写字母
func IsUpper(value any) (ok bool) {
return regexp.MustCompile(`^[A-Z]+$`).MatchString(cast.ToString(value))
}
// IsSpace - 是否为空白字符 - (空格、制表符、换页符等)
func IsSpace(value any) (ok bool) {
return regexp.MustCompile(`^[\s]+$`).MatchString(cast.ToString(value))
}
// IsXdigit - 是否为十六进制字符 - 0-9、a-f、A-F
func IsXdigit(value any) (ok bool) {
return regexp.MustCompile(`^[\da-fA-F]+$`).MatchString(cast.ToString(value))
}
// IsActiveUrl - 是否为有效的域名或者IP
func IsActiveUrl(value any) (ok bool) {
return regexp.MustCompile(`^([a-z0-9-]+\.)+[a-z]{2,6}$`).MatchString(cast.ToString(value))
}
// IsIp - 是否为IP
func IsIp(ip any) (ok bool) {
return regexp.MustCompile(`(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)`).MatchString(cast.ToString(ip))
}
// IsUrl - 是否为URL
func IsUrl(url any) (ok bool) {
return regexp.MustCompile(`^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+`).MatchString(cast.ToString(url))
}
// IsIdCard - 是否为有效的身份证号码
func IsIdCard(value any) (ok bool) {
return regexp.MustCompile(`(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)`).MatchString(cast.ToString(value))
}
// IsMacAddr - 是否为有效的MAC地址
func IsMacAddr(value any) (ok bool) {
return regexp.MustCompile(`^([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}$`).MatchString(cast.ToString(value))
}
// IsZip - 是否为有效的邮政编码
func IsZip(value any) (ok bool) {
return regexp.MustCompile(`^[1-9]\d{5}$`).MatchString(cast.ToString(value))
}
// IsString - 是否为字符串
func IsString(value any) (ok bool) {
return reflect.TypeOf(value).Kind() == reflect.String
}
// IsSlice - 是否为切片
func IsSlice(value any) (ok bool) {
return reflect.TypeOf(value).Kind() == reflect.Slice
}
// IsArray - 是否为数组
func IsArray(value any) (ok bool) {
return reflect.TypeOf(value).Kind() == reflect.Array
}
// IsJsonString - 是否为json字符串
func IsJsonString(value any) (ok bool) {
return regexp.MustCompile(`^[\{\[].*[\}\]]$`).MatchString(cast.ToString(value))
}
// IsMap - 是否为map
func IsMap(value any) (ok bool) {
return reflect.TypeOf(value).Kind() == reflect.Map
}
// IsSliceSlice - 是否为二维切片
func IsSliceSlice(value any) (ok bool) {
return reflect.TypeOf(value).Kind() == reflect.Slice && reflect.TypeOf(value).Elem().Kind() == reflect.Slice
}
// IsMapAny - 是否为[]map[string]any
func IsMapAny(value any) (ok bool) {
return reflect.TypeOf(value).Kind() == reflect.Map && reflect.TypeOf(value).Elem().Kind() == reflect.Interface
}

23
utils/json.go Normal file
View File

@@ -0,0 +1,23 @@
package utils
import (
"github.com/goccy/go-json"
)
// JsonEncode 编码
func JsonEncode(data any) (result string) {
text, err := json.Marshal(data)
if err != nil {
return ""
}
return string(text)
}
// JsonDecode 解码
func JsonDecode(data string) (result any) {
err := json.Unmarshal([]byte(data), &result)
if err != nil {
return nil
}
return result
}

49
utils/mime.go Normal file
View File

@@ -0,0 +1,49 @@
package utils
import (
"github.com/spf13/cast"
"strings"
)
var MimeMap = map[string]string{
"js": "application/javascript",
"json": "application/json",
"xml": "application/xml",
"css": "text/css",
"html": "text/html",
"txt": "text/plain",
"gif": "image/gif",
"png": "image/png",
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"svg": "image/svg+xml",
"ico": "image/x-icon",
"pdf": "application/pdf",
"zip": "application/zip",
"rar": "application/x-rar-compressed",
"gz": "application/x-gzip",
"tar": "application/x-tar",
"7z": "application/x-7z-compressed",
"mp3": "audio/mpeg",
"mp4": "video/mp4",
"avi": "video/x-msvideo",
"doc": "application/msword",
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"xls": "application/vnd.ms-excel",
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"ppt": "application/vnd.ms-powerpoint",
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"csv": "text/csv",
"md": "text/markdown",
}
// MimeType 获取后缀对应的 mime
func MimeType(suffix any) (mime string) {
// 获取后缀
suffix = strings.ToLower(cast.ToString(suffix))
// 取出 . 后面的内容
suffix = strings.TrimPrefix(cast.ToString(suffix), ".")
// 获取后缀对应的 mime
mime = MimeMap[cast.ToString(suffix)]
return
}

79
utils/net.go Normal file
View File

@@ -0,0 +1,79 @@
package utils
import (
"github.com/spf13/cast"
"net"
"sync"
"time"
)
func NetTcping(host any, opts ...map[string]any) (ok bool, detail []map[string]any) {
if len(opts) == 0 {
opts = append(opts, map[string]any{
"count": 4,
"timeout": 5,
})
}
opt := opts[0]
count := cast.ToInt(opt["count"])
timeout := cast.ToDuration(opt["timeout"]) * time.Second
// 读写锁 - 防止并发写入
type wrLock struct {
Lock *sync.RWMutex
Data []map[string]any
}
wr := wrLock{
Data: make([]map[string]any, 0),
Lock: &sync.RWMutex{},
}
wg := sync.WaitGroup{}
wg.Add(count)
for i := 0; i < count; i++ {
go func() {
defer wg.Done()
// 加锁
wr.Lock.Lock()
// 解锁
defer wr.Lock.Unlock()
start := time.Now()
_, err := net.DialTimeout("tcp", cast.ToString(host), timeout)
if err != nil {
wr.Data = append(wr.Data, map[string]any{
"host": host,
"status": false,
"waist": time.Now().Sub(start).Milliseconds(),
"message": "Site unreachable, error: " + err.Error(),
})
return
}
wr.Data = append(wr.Data, map[string]any{
"host": host,
"status": true,
"waist": time.Now().Sub(start).Milliseconds(),
"message": "tcp server is ok",
})
}()
}
wg.Wait()
// 只要有一个 ping 成功就返回 true
for _, val := range wr.Data {
if cast.ToBool(val["status"]) {
return true, wr.Data
}
}
return false, wr.Data
}

213
utils/other.go Normal file
View File

@@ -0,0 +1,213 @@
package utils
import (
"fmt"
"reflect"
"regexp"
"strings"
)
func typeof(args ...any) (typeof string, empty bool) {
typeof, empty = "string", false
for _, item := range args {
// 判断是否为空
if item == nil {
empty = true
continue
}
// 先利用反射获取数据类型,再进入不同类型的判空逻辑
switch reflect.TypeOf(item).Kind().String() {
case "int":
typeof = "int"
if item == 0 {
empty = true
}
case "string":
typeof = "string"
if item == "" {
empty = true
}
case "int64":
typeof = "int64"
if item == 0 {
empty = true
}
case "uint8":
typeof = "bool"
if item == false {
empty = true
}
case "float64":
typeof = "float"
if item == 0.0 {
empty = true
}
case "byte":
typeof = "byte"
if item == 0 {
empty = true
}
case "ptr":
typeof = "ptr"
// 接口状态下它不认为自己是nil所以要用反射判空
if item == nil {
empty = true
}
// 反射判空逻辑
if reflect.ValueOf(item).IsNil() {
// 利用反射直接判空
empty = true
}
case "struct":
typeof = "struct"
if item == nil {
empty = true
}
case "slice":
typeof = "slice"
s := reflect.ValueOf(item)
if s.Len() == 0 {
empty = true
}
case "array":
typeof = "array"
s := reflect.ValueOf(item)
if s.Len() == 0 {
empty = true
}
case "map":
typeof = "map"
s := reflect.ValueOf(item)
if s.Len() == 0 {
empty = true
}
case "chan":
typeof = "chan"
s := reflect.ValueOf(item)
if s.Len() == 0 {
empty = true
}
case "func":
typeof = "func"
if item == nil {
empty = true
}
default:
if item == "true" || item == "false" || item == true || item == false {
typeof = "bool"
if item == false {
empty = true
}
} else {
typeof = "other"
empty = true
fmt.Println("奇怪的数据类型")
}
}
}
return
}
func CustomProcessApi(url string, api string) (result string) {
if empty := IsEmpty(api); empty {
api = "api"
}
result = url
if empty := IsEmpty(url); !empty {
prefix := "//"
if check := strings.HasPrefix(url, "https://"); check {
prefix = "https://"
} else if check := strings.HasPrefix(url, "http://"); check {
prefix = "http://"
}
// 正则匹配 http(s):// - 并去除
url = regexp.MustCompile("^((https|http)?:\\/\\/)").ReplaceAllString(url, "")
array := ArrayFilter(strings.Split(url, `/`))
if len(array) == 1 {
result = prefix + array[0] + "/" + api + "/"
} else if len(array) == 2 {
result = prefix + array[0] + "/" + array[1] + "/"
}
}
return
}
// InMapKey
// 在 map key 中
func InMapKey(key string, array map[string]any) bool {
for k := range array {
if key == k {
return true
}
}
return false
}
// MapMerge
// map合并
func MapMerge(map1 map[any]any, map2 map[any]any) map[any]any {
result := make(map[any]any)
for i, v := range map1 {
for j, w := range map2 {
if i == j {
result[i] = w
} else {
if _, ok := result[i]; !ok {
result[i] = v
}
if _, ok := result[j]; !ok {
result[j] = w
}
}
}
}
return result
}
// MapMergeString
// map合并
func MapMergeString(map1 map[string]string, map2 map[string]string) map[string]string {
result := make(map[string]string)
for i, v := range map1 {
for j, w := range map2 {
if i == j {
result[i] = w
} else {
if _, ok := result[i]; !ok {
result[i] = v
}
if _, ok := result[j]; !ok {
result[j] = w
}
}
}
}
return result
}
// InMapValue
// 在 map value 中
func InMapValue(value any, array map[string]string) bool {
for _, val := range array {
if value == val {
return true
}
}
return false
}
// Ternary
// 三元运算符
func Ternary[T any](IF bool, TRUE T, FALSE T) T {
if IF {
return TRUE
}
return FALSE
}

170
utils/parse.go Normal file
View File

@@ -0,0 +1,170 @@
package utils
import (
"github.com/spf13/cast"
"net/url"
"regexp"
"strings"
"sync"
)
// 读写锁 - 防止并发写入
type wrLock struct {
Lock *sync.RWMutex
Data map[string]any
}
// Get - 获取数据
func (this *wrLock) get(key string) any {
defer this.Lock.Unlock()
this.Lock.Lock()
return this.Data[key]
}
// Set - 设置数据
func (this *wrLock) set(key string, val any) {
defer this.Lock.Unlock()
this.Lock.Lock()
this.Data[key] = val
}
// Has - 判断是否存在
func (this *wrLock) has(key string) (ok bool) {
defer this.Lock.Unlock()
this.Lock.Lock()
_, ok = this.Data[key]
return ok
}
// ParseParamsBefore - 解析参数
// 把 Content-Type = application/x-www-form-urlencoded 的参数解析成 object.deep.age = 10 的格式
func ParseParamsBefore(params url.Values) (result map[string]any) {
wg := sync.WaitGroup{}
wr := wrLock{
Data: make(map[string]any, 3),
Lock: &sync.RWMutex{},
}
worker := func(keys string, value []string, wg *sync.WaitGroup) {
defer wg.Done()
// 加锁
wr.Lock.Lock()
// 解锁
defer wr.Lock.Unlock()
// 判断是否为 [] 结尾 - 普通数据
if strings.HasSuffix(keys, "[]") {
// 将 [] 替换为 .
key := strings.Replace(keys, "[]", ".", -1)
wr.Data[key] = value
return
}
keys = strings.Replace(keys, "[", ".", -1)
keys = strings.Replace(keys, "]", "", -1)
// 正则匹配末尾是否为 .[0-9]+ - 【一维数组】
reg := regexp.MustCompile(`\.[0-9]+$`)
if reg.MatchString(keys) {
// 取到 .[0-9]+ 的内容
index := reg.FindString(keys)
// 去除 . 只要数字
index = strings.Replace(index, ".", "", -1)
// 将 .[0-9]+ 替换为 .
key := reg.ReplaceAllString(keys, ".")
// 判断 key 是否存在,如果不存在,则创建 - 【初始化一维数组】
if _, ok := wr.Data[key]; !ok {
item := make([]any, cast.ToInt(index)+1)
item[cast.ToInt(index)] = value[0]
wr.Data[key] = item
} else {
// 如果存在,则追加,先判断 index 是否超出数组长度 - 【追加一维数组】
if cast.ToInt(index) >= len(wr.Data[key].([]any)) {
// 超出长度,需要扩容
item := make([]any, cast.ToInt(index)+1)
// 将原来的数据复制到新数组
copy(item, wr.Data[key].([]any))
// 将新数据追加到新数组
item[cast.ToInt(index)] = value[0]
wr.Data[key] = item
} else {
// 未超出长度,直接追加
wr.Data[key].([]any)[cast.ToInt(index)] = value[0]
}
}
return
}
// 如果不是 [] 结尾,也不是 .[0-9]+ 结尾,则直接赋值 - 【普通数据】
wr.Data[keys] = value[0]
}
for key, value := range params {
wg.Add(1)
go worker(key, value, &wg)
}
wg.Wait()
return wr.Data
}
// ParseParams - 解析参数
// 把 Content-Type = application/x-www-form-urlencoded 的参数解析成 map[string]any
func ParseParams(params map[string]any) (result map[string]any) {
wg := sync.WaitGroup{}
wr := wrLock{
Data: make(map[string]any, 3),
Lock: &sync.RWMutex{},
}
var worker func(params map[string]any, key string, val any)
worker = func(params map[string]any, key string, val any) {
// wg.Add(1)
// 加锁
wr.Lock.Lock()
defer wg.Done()
// 解锁
defer wr.Lock.Unlock()
// 如果 key 的末尾为 . ,表示该 key 为一维数组
if strings.HasSuffix(key, ".") {
key = strings.TrimSuffix(key, ".")
}
// 通过 . 分割 key
keys := strings.Split(key, ".")
// 判断是否为最后一个元素
if len(keys) == 1 {
params[keys[0]] = val
} else {
// 如果不是最后一个元素,则继续递归
if _, ok := params[keys[0]]; !ok {
params[keys[0]] = make(map[string]any, 3)
}
wg.Add(1)
go worker(params[keys[0]].(map[string]any), strings.Join(keys[1:], "."), val)
}
return
}
for key, val := range params {
wg.Add(1)
go worker(wr.Data, key, val)
}
wg.Wait()
return wr.Data
}

22
utils/password.go Normal file
View File

@@ -0,0 +1,22 @@
package utils
import (
"github.com/spf13/cast"
"golang.org/x/crypto/bcrypt"
)
// PasswordCreate - 创建密码
func PasswordCreate(password any) (result string) {
item, _ := bcrypt.GenerateFromPassword([]byte(password.(string)), bcrypt.MinCost)
return string(item)
}
// PasswordVerify - 验证密码
func PasswordVerify(encode any, password any) (ok bool) {
if err := bcrypt.CompareHashAndPassword([]byte(cast.ToString(encode)), []byte(cast.ToString(password))); err != nil {
return false
}
return true
}

42
utils/rand.go Normal file
View File

@@ -0,0 +1,42 @@
package utils
import (
"math/rand"
"time"
)
// RandString - 生成随机字符串
func RandString(length int, chars ...string) (result string) {
var charset string
if IsEmpty(chars) {
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
} else {
charset = chars[0]
}
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
item := make([]byte, length)
for i := range item {
item[i] = charset[seededRand.Intn(len(charset))]
}
return string(item)
}
// RandInt - 生成随机整数
func RandInt(max int, min ...int) (result int) {
if IsEmpty(min) {
min = []int{0}
}
if max <= min[0] {
// 交换两个数
max, min[0] = min[0], max
}
if max == min[0] {
return max
}
rand.Seed(time.Now().UnixNano())
return rand.Intn(max-min[0]) + min[0]
}

174
utils/struct.go Normal file
View File

@@ -0,0 +1,174 @@
package utils
import (
"github.com/spf13/cast"
"reflect"
)
// StructSet - 动态给结构体赋值
func StructSet(obj any, key string, val any) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 获取结构体的类型
typ := value.Type()
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取结构体的字段
field := typ.Field(i)
// 获取字段的tag
tag := field.Tag.Get("json")
// 判断字段的tag是否等于传入的key
if tag == key {
// 类型断言
switch field.Type.Kind() {
case reflect.String:
value.Field(i).SetString(cast.ToString(val))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
value.Field(i).SetInt(cast.ToInt64(val))
case reflect.Bool:
value.Field(i).SetBool(cast.ToBool(val))
case reflect.Float32, reflect.Float64:
value.Field(i).SetFloat(cast.ToFloat64(val))
default:
value.Field(i).Set(reflect.ValueOf(val))
}
return
}
}
}
// StructGet - 动态获取结构体的值
func StructGet(obj any, key string) (result any) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 获取结构体的类型
typ := value.Type()
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取结构体的字段
field := typ.Field(i)
// 获取字段的tag
tag := field.Tag.Get("json")
// 判断字段的tag是否等于传入的key
if tag == key {
// 获取字段的值
return value.Field(i).Interface()
}
}
return nil
}
// StructDel - 删除结构体的字段
func StructDel(obj any, key string) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 获取结构体的类型
typ := value.Type()
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取结构体的字段
field := typ.Field(i)
// 获取字段的tag
tag := field.Tag.Get("json")
// 判断字段的tag是否等于传入的key
if tag == key {
// 获取字段的值
value.Field(i).Set(reflect.Zero(field.Type))
}
}
}
// StructHas - 判断结构体是否存在某个字段
func StructHas(obj any, key string) (ok bool) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 获取结构体的类型
typ := value.Type()
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取结构体的字段
field := typ.Field(i)
// 获取字段的tag
tag := field.Tag.Get("json")
// 判断字段的tag是否等于传入的key
if tag == key {
return true
}
}
return false
}
// StructKeys - 获取结构体的字段
func StructKeys(obj any) (slice []string) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 获取结构体的类型
typ := value.Type()
// 定义一个切片
keys := make([]string, 0)
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取结构体的字段
field := typ.Field(i)
// 获取字段的tag
tag := field.Tag.Get("json")
// 判断字段的tag是否等于传入的key
keys = append(keys, tag)
}
return keys
}
// StructValues - 获取结构体的值
func StructValues(obj any) (slice []any) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 定义一个切片
keys := make([]any, 0)
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取字段的值
keys = append(keys, value.Field(i).Interface())
}
return keys
}
// StructLen - 获取结构体的长度
func StructLen(obj any) (length int) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
return value.NumField()
}
// StructMap - 将结构体转换为map
func StructMap(obj any) (result map[string]any) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 获取结构体的类型
typ := value.Type()
// 定义一个map
// result := make(map[string]any)
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取结构体的字段
field := typ.Field(i)
// 获取字段的tag
tag := field.Tag.Get("json")
// 获取字段的值
result[tag] = value.Field(i).Interface()
}
return result
}
// StructSlice - 将结构体转换为切片
func StructSlice(obj any) (slice []any) {
// 获取结构体的值
value := reflect.ValueOf(obj).Elem()
// 定义一个切片
s := make([]any, 0)
// 遍历结构体的字段
for i := 0; i < value.NumField(); i++ {
// 获取字段的值
s = append(s, value.Field(i).Interface())
}
return s
}

411
utils/validate.go Normal file
View File

@@ -0,0 +1,411 @@
package utils
import (
"encoding/json"
"errors"
"fmt"
"github.com/spf13/cast"
"reflect"
"strings"
)
type validator struct {
model any
message map[string]string
rule func(name string, value any, rule string, message map[string]string) error
}
// Validate - 验证器入口
var Validate = func(model any) *validator {
return &validator{
model: model,
rule: ValidateRules,
}
}
// Validate - 验证模型
func (this *validator) Validate(model any) *validator {
this.model = model
return this
}
// Message - 自定义错误信息
func (this *validator) Message(message map[string]string) *validator {
this.message = message
return this
}
// Rule - 自定义校验规则
func (this *validator) Rule(callback func(name string, value any, rule string, message map[string]string) error) *validator {
this.rule = callback
return this
}
// Check - 验证
func (this *validator) Check(params any) (err error) {
if this.model == nil {
return errors.New("model is nil")
}
// params 转 string
data, _ := json.Marshal(params)
// string 转 map
var tempMap map[string]any
err = json.Unmarshal(data, &tempMap)
if err != nil {
return err
}
// 获取结构体的字段和标签
for i := 0; i < reflect.TypeOf(this.model).NumField(); i++ {
field := reflect.TypeOf(this.model).Field(i)
tag := field.Tag
rule := tag.Get("rule")
// 字段名
var name string
// 字段值
var value any
if !Is.Empty(cast.ToString(tag.Get("json"))) {
name = tag.Get("json")
} else {
name = field.Name
}
value = tempMap[name]
if Is.Empty(rule) {
// 结束本次循环,进入下一次循环
continue
}
err := this.rule(name, value, rule, this.message)
if err != nil {
return err
}
}
return nil
}
// ValidateRules - 验证规则
/**
* @rule - 内置规则 - 如下:
* required必填
* min最小值
* max最大值
* email是否为邮箱
* number是否为数字
* float是否为浮点数
* bool是否为布尔值
* slice是否为切片 - (数组)
* jsonStr是否为 JSON 字符串
* accepted验证某个字段是否为为 yes, on, 或是 1
* date是否为日期
* alpha只能包含字母
* alphaNum只能包含字母和数字
* alphaDash只能包含字母、数字和下划线_及破折号-
* chs只能包含汉字
* chsAlpha只能包含汉字、字母
* chsAlphaNum只能包含汉字、字母和数字
* chsDash只能是汉字、字母、数字和下划线_及破折号-
* cntrl是否为控制字符 - (换行、缩进、空格)
* graph是否为可见字符 - (除空格外的所有可打印字符)
* lower是否为小写字母
* upper是否为大写字母
* space是否为空白字符 - (空格、制表符、换页符等)
* xdigit是否为十六进制字符 - 0-9、a-f、A-F
* activeUrl是否为有效的域名或者IP
* url是否为有效的URL地址
* ip是否为IP地址
* mobile是否为手机号
* idCard是否为身份证号
* MacAddr是否为MAC地址
* zip是否为邮政编码
**/
func ValidateRules(name string, value any, rule string, message map[string]string) (err error) {
// 获取 rule 中的规则 - 字符串切片,逗号分隔
rules := strings.Split(rule, ",")
// 判断 rules 是否包含 required
if !InArray("required", cast.ToSlice(rules)) {
// 判断 value 是否为空
if IsEmpty(value) {
// 结束本次循环,进入下一次循环
return nil
}
}
for _, val := range rules {
if strings.Contains(val, "=") {
// 获取规则和参数
ruleAndParam := strings.Split(val, "=")
first := ruleAndParam[0]
second := ruleAndParam[1]
switch first {
case "min":
// 判断长度是否小于最小值
if second != "" && len(cast.ToString(value)) < cast.ToInt(second) {
msg := message[name+"."+first]
if msg == "" {
msg = name + " length cannot be less than " + second + ""
}
return fmt.Errorf(msg)
}
case "max":
// 判断长度是否大于最大值
if second != "" && len(cast.ToString(value)) > cast.ToInt(second) {
msg := message[name+"."+first]
if msg == "" {
msg = name + " length cannot be greater than " + second + ""
}
return fmt.Errorf(msg)
}
}
} else {
switch val {
case "required":
if IsEmpty(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " is not empty!"
}
return fmt.Errorf(msg)
}
case "email":
if !IsEmail(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect! Required in <email> format!"
}
return fmt.Errorf(msg)
}
case "number":
if !IsNumber(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect! Required in <number> format!"
}
return fmt.Errorf(msg)
}
case "float":
if !IsFloat(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect! Required in <float> format!"
}
return fmt.Errorf(msg)
}
case "bool":
if !IsBool(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect! Required in <bool> format!"
}
return fmt.Errorf(msg)
}
case "slice":
if !IsSlice(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect! Required in <array> format!"
}
return fmt.Errorf(msg)
}
case "jsonStr":
if !IsJsonString(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect! Required in <json string> format!"
}
return fmt.Errorf(msg)
}
case "accepted":
if !IsAccepted(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "date":
if !IsDate(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect! Required in <date> format!"
}
return fmt.Errorf(msg)
}
case "alpha":
if !IsAlpha(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "alphaNum":
if !IsAlphaNum(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "alphaDash":
if !IsAlphaDash(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "chs":
if !IsChs(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "chsAlpha":
if !IsChsAlpha(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "chsAlphaNum":
if !IsChsAlphaNum(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "chsDash":
if !IsChsDash(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "cntrl":
if !IsCntrl(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "graph":
if !IsGraph(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "lower":
if !IsLower(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "upper":
if !IsUpper(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "space":
if !IsSpace(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "xdigit":
if !IsXdigit(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "activeUrl":
if !IsActiveUrl(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "url":
if !IsUrl(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "ip":
if !IsIp(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "mobile":
if !IsMobile(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "idCard":
if !IsIdCard(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "MacAddr":
if !IsMacAddr(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
case "zip":
if !IsZip(value) {
msg := message[name+"."+val]
if msg == "" {
msg = name + " format is incorrect!"
}
return fmt.Errorf(msg)
}
}
}
}
return nil
}