diff --git a/app/api/server.go b/app/api/server.go index ba14df79..a820a934 100644 --- a/app/api/server.go +++ b/app/api/server.go @@ -14,7 +14,7 @@ import ( "strings" "time" - "github.com/didip/tollbooth/v6" + "github.com/didip/tollbooth/v7" "github.com/didip/tollbooth_chi" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" diff --git a/go.mod b/go.mod index c8dcda38..e3cb84dc 100644 --- a/go.mod +++ b/go.mod @@ -4,47 +4,49 @@ go 1.17 require ( github.com/ChimeraCoder/anaconda v2.0.0+incompatible - github.com/bogem/id3v2/v2 v2.1.2 + github.com/bogem/id3v2/v2 v2.1.4 github.com/denisbrodbeck/striphtmltags v6.6.6+incompatible - github.com/didip/tollbooth/v6 v6.1.2 - github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21 - github.com/dustin/go-humanize v1.0.0 - github.com/go-chi/chi/v5 v5.0.7 - github.com/go-chi/render v1.0.1 - github.com/go-pkgz/lcw v0.8.1 + github.com/didip/tollbooth/v7 v7.0.1 + github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f + github.com/dustin/go-humanize v1.0.1 + github.com/go-chi/chi/v5 v5.0.8 + github.com/go-chi/render v1.0.2 + github.com/go-pkgz/lcw v1.0.2 github.com/go-pkgz/lgr v0.10.4 github.com/go-pkgz/repeater v1.1.3 - github.com/go-pkgz/rest v1.14.0 + github.com/go-pkgz/rest v1.17.0 github.com/go-pkgz/syncs v1.2.0 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 github.com/jessevdk/go-flags v1.5.0 - github.com/microcosm-cc/bluemonday v1.0.18 + github.com/microcosm-cc/bluemonday v1.0.22 github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.1 github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 - go.etcd.io/bbolt v1.3.6 - golang.org/x/net v0.0.0-20220225172249-27dd8689420f + go.etcd.io/bbolt v1.3.7 + golang.org/x/net v0.7.0 gopkg.in/tucnak/telebot.v2 v2.5.0 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7 // indirect + github.com/ajg/form v1.5.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc // indirect github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 // indirect - github.com/go-pkgz/expirable-cache v0.0.3 // indirect - github.com/go-redis/redis/v7 v7.4.1 // indirect + github.com/go-pkgz/expirable-cache v1.0.0 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d9759eb9..7198a9a3 100644 --- a/go.sum +++ b/go.sum @@ -2,16 +2,21 @@ github.com/ChimeraCoder/anaconda v2.0.0+incompatible h1:F0eD7CHXieZ+VLboCD5UAqCe github.com/ChimeraCoder/anaconda v2.0.0+incompatible/go.mod h1:TCt3MijIq3Qqo9SBtuW/rrM4x7rDfWqYWHj8T7hLcLg= github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7 h1:r+EmXjfPosKO4wfiMLe1XQictsIlhErTufbWUsjOTZs= github.com/ChimeraCoder/tokenbucket v0.0.0-20131201223612-c5a927568de7/go.mod h1:b2EuEMLSG9q3bZ95ql1+8oVqzzrTNSiOQqSXWFBzxeI= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.11.4 h1:GsuyeunTx7EllZBU3/6Ji3dhMQZDpC9rLf1luJ+6M5M= -github.com/alicebob/miniredis/v2 v2.11.4/go.mod h1:VL3UDEfAH59bSa7MuHMuFToxkqyHh69s/WUbYlOAuyg= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.22.0 h1:lIHHiSkEyS1MkKHCHzN+0mWrA4YdbGdimE5iZ2sHSzo= +github.com/alicebob/miniredis/v2 v2.22.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330 h1:ekDALXAVvY/Ub1UtNta3inKQwZ/jMB/zpOtD8rAYh78= github.com/azr/backoff v0.0.0-20160115115103-53511d3c7330/go.mod h1:nH+k0SvAt3HeiYyOlJpLLv1HG1p7KWP7qU9QPp2/pCo= -github.com/bogem/id3v2/v2 v2.1.2 h1:++1bbBfveCXdH8ep/Tuecua5BzHgljPL0c/msnAOO64= -github.com/bogem/id3v2/v2 v2.1.2/go.mod h1:hVfgiLdvXyAPqnCc3+CbdEvAv7uOkYn6a/rVpHmABmg= +github.com/bogem/id3v2/v2 v2.1.4 h1:CEwe+lS2p6dd9UZRlPc1zbFNIha2mb2qzT1cCEoNWoI= +github.com/bogem/id3v2/v2 v2.1.4/go.mod h1:l+gR8MZ6rc9ryPTPkX77smS5Me/36gxkMgDayZ9G1vY= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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= @@ -20,43 +25,58 @@ 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/denisbrodbeck/striphtmltags v6.6.6+incompatible h1:w4i4bsyWhAAqwUd9D/1NBi98citfaqCOI/8K3ZCh7KY= github.com/denisbrodbeck/striphtmltags v6.6.6+incompatible/go.mod h1:wex3txg8OlzJKhtozM75/Ucy+jKUq73hqzl7XAcNeOY= -github.com/didip/tollbooth/v6 v6.0.1/go.mod h1:j2pKs+JQ5PvU/K4jFnrnwntrmfUbYLJE5oSdxR37FD0= -github.com/didip/tollbooth/v6 v6.1.2 h1:Kdqxmqw9YTv0uKajBUiWQg+GURL/k4vy9gmLCL01PjQ= -github.com/didip/tollbooth/v6 v6.1.2/go.mod h1:xjcse6CTHCLuOkzsWrEgdy9WPJFv+p/x6v+MyfP+O9s= -github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21 h1:x7YpwKSBIBcKe9I3aTNOqgSyJ6QKDdtOxnEkxBTsi9w= -github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21/go.mod h1:0ZVa6kSzS011nfTC1rELyxK4tjVf6vqBnOv7oY2KlsA= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/didip/tollbooth/v7 v7.0.0/go.mod h1:VZhDSGl5bDSPj4wPsih3PFa4Uh9Ghv8hgacaTm5PRT4= +github.com/didip/tollbooth/v7 v7.0.1 h1:TkT4sBKoQoHQFPf7blQ54iHrZiTDnr8TceU+MulVAog= +github.com/didip/tollbooth/v7 v7.0.1/go.mod h1:VZhDSGl5bDSPj4wPsih3PFa4Uh9Ghv8hgacaTm5PRT4= +github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f h1:jtKwihcLmUC9BAhoJ9adCUqdSSZcOdH2KL7mPTUm2aw= +github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f/go.mod h1:q9C80dnsuVRP2dAskjnXRNWdUJqtGgwG9wNrzt0019s= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc h1:tP7tkU+vIsEOKiK+l/NSLN4uUtkyuxc6hgYpQeCWAeI= github.com/dustin/go-jsonpointer v0.0.0-20160814072949-ba0abeacc3dc/go.mod h1:ORH5Qp2bskd9NzSfKqAF7tKfONsEkCarTE5ESr/RVBw= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17 h1:GOfMz6cRgTJ9jWV0qAezv642OhPnKEG7gtUjJSdStHE= github.com/garyburd/go-oauth v0.0.0-20180319155456-bca2e7f09a17/go.mod h1:HfkOCN6fkKKaPSAeNq/er3xObxTW4VLeY6UUK895gLQ= -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= -github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= -github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc= -github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ= -github.com/go-pkgz/lcw v0.8.1 h1:Bpt2yYTE1J8hIhz8tjdm1WPOgH13eo5iTNsXyop7cMQ= -github.com/go-pkgz/lcw v0.8.1/go.mod h1:Xw0/ZfApATgbjVPYRZO4XHdWyxAjErDWDWJ7TLlw1Vc= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= +github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/go-pkgz/expirable-cache v0.1.0/go.mod h1:GTrEl0X+q0mPNqN6dtcQXksACnzCBQ5k/k1SwXJsZKs= +github.com/go-pkgz/expirable-cache v1.0.0 h1:ns5+1hjY8hntGv8bPaQd9Gr7Jyo+Uw5SLyII40aQdtA= +github.com/go-pkgz/expirable-cache v1.0.0/go.mod h1:GTrEl0X+q0mPNqN6dtcQXksACnzCBQ5k/k1SwXJsZKs= +github.com/go-pkgz/lcw v1.0.2 h1:68T8xRkVWSo1KIbMfFXBY1xbFF3XWl7YD+ub3jkUlNE= +github.com/go-pkgz/lcw v1.0.2/go.mod h1:CPJJzunpmGToOtD0Ga82TV152eL69sYEIIPcy9fbxlU= github.com/go-pkgz/lgr v0.10.4 h1:l7qyFjqEZgwRgaQQSEp6tve4A3OU80VrfzpvtEX8ngw= github.com/go-pkgz/lgr v0.10.4/go.mod h1:CD0s1z6EFpIUplV067gitF77tn25JItzwHNKAPqeCF0= github.com/go-pkgz/repeater v1.1.3 h1:q6+JQF14ESSy28Dd7F+wRelY4F+41HJ0LEy/szNnMiE= github.com/go-pkgz/repeater v1.1.3/go.mod h1:hVTavuO5x3Gxnu8zW7d6sQBfAneKV8X2FjU48kGfpKw= -github.com/go-pkgz/rest v1.14.0 h1:brDLCzIGoe0IiUZqRFpsiCVM9m3L88A7z62qS0V9Yfk= -github.com/go-pkgz/rest v1.14.0/go.mod h1:KUWAqbDteYGS/CiXftomQsKjtEOifXsJ36Ka0skYbmk= +github.com/go-pkgz/rest v1.17.0 h1:LoBI/lDBMuqwWhOOkc6thM9NnwJO+K9nWvCOjZ7BAgE= +github.com/go-pkgz/rest v1.17.0/go.mod h1:HHlLOt02NJc2sgffXBF6hYVMcRo4Gz3vjg43zTzN7VM= github.com/go-pkgz/syncs v1.2.0 h1:aiizQFILlMZ4KtRNaYLcDffRbUQZH9fclsgr5KybWyY= github.com/go-pkgz/syncs v1.2.0/go.mod h1:fjThZdM2FkC/oSeiqBTOZOtHpbrCh4HuHbipB5qZJJM= -github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= -github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= -github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= @@ -64,13 +84,12 @@ github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -78,74 +97,129 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= -github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= +github.com/microcosm-cc/bluemonday v1.0.22 h1:p2tT7RNzRdCi0qmwxG+HbqD6ILkmwter1ZwVZn1oTxA= +github.com/microcosm-cc/bluemonday v1.0.22/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 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/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 h1:XQdibLKagjdevRB6vAjVY4qbSr8rQ610YzTkWcxzxSI= github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300/go.mod h1:FNa/dfN95vAYCNFrIKRrlRo+MBLbwmR9Asa5f2ljmBI= -github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0= -github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= +github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/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-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= -golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +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/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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po= gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 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= diff --git a/vendor/github.com/ajg/form/.travis.yml b/vendor/github.com/ajg/form/.travis.yml new file mode 100644 index 00000000..14608c76 --- /dev/null +++ b/vendor/github.com/ajg/form/.travis.yml @@ -0,0 +1,25 @@ +## Copyright 2014 Alvaro J. Genial. All rights reserved. +## Use of this source code is governed by a BSD-style +## license that can be found in the LICENSE file. + +language: go + +go: + - tip + - 1.6 + - 1.5 + - 1.4 + - 1.3 + # 1.2 + +before_install: + # - go get -v golang.org/x/tools/cmd/cover + # - go get -v golang.org/x/tools/cmd/vet + # - go get -v golang.org/x/lint/golint + - export PATH=$PATH:/home/travis/gopath/bin + +script: + - go build -v ./... + - go test -v -cover ./... + - go vet ./... + # - golint . diff --git a/vendor/github.com/ajg/form/LICENSE b/vendor/github.com/ajg/form/LICENSE new file mode 100644 index 00000000..9190b165 --- /dev/null +++ b/vendor/github.com/ajg/form/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 Alvaro J. Genial. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/ajg/form/README.md b/vendor/github.com/ajg/form/README.md new file mode 100644 index 00000000..ad99be4b --- /dev/null +++ b/vendor/github.com/ajg/form/README.md @@ -0,0 +1,247 @@ +form +==== + +A Form Encoding & Decoding Package for Go, written by [Alvaro J. Genial](http://alva.ro). + +[![Build Status](https://travis-ci.org/ajg/form.png?branch=master)](https://travis-ci.org/ajg/form) +[![GoDoc](https://godoc.org/github.com/ajg/form?status.png)](https://godoc.org/github.com/ajg/form) + +Synopsis +-------- + +This library is designed to allow seamless, high-fidelity encoding and decoding of arbitrary data in `application/x-www-form-urlencoded` format and as [`url.Values`](http://golang.org/pkg/net/url/#Values). It is intended to be useful primarily in dealing with web forms and URI query strings, both of which natively employ said format. + +Unsurprisingly, `form` is modeled after other Go [`encoding`](http://golang.org/pkg/encoding/) packages, in particular [`encoding/json`](http://golang.org/pkg/encoding/json/), and follows the same conventions (see below for more.) It aims to automatically handle any kind of concrete Go [data value](#values) (i.e., not functions, channels, etc.) while providing mechanisms for custom behavior. + +Status +------ + +The implementation is in usable shape and is fairly well tested with its accompanying test suite. The API is unlikely to change much, but still may. Lastly, the code has not yet undergone a security review to ensure it is free of vulnerabilities. Please file an issue or send a pull request for fixes & improvements. + +Dependencies +------------ + +The only requirement is [Go 1.2](http://golang.org/doc/go1.2) or later. + +Usage +----- + +```go +import "github.com/ajg/form" +// or: "gopkg.in/ajg/form.v1" +``` + +Given a type like the following... + +```go +type User struct { + Name string `form:"name"` + Email string `form:"email"` + Joined time.Time `form:"joined,omitempty"` + Posts []int `form:"posts"` + Preferences map[string]string `form:"prefs"` + Avatar []byte `form:"avatar"` + PasswordHash int64 `form:"-"` +} +``` + +...it is easy to encode data of that type... + + +```go +func PostUser(url string, u User) error { + var c http.Client + _, err := c.PostForm(url, form.EncodeToValues(u)) + return err +} +``` + +...as well as decode it... + + +```go +func Handler(w http.ResponseWriter, r *http.Request) { + var u User + + d := form.NewDecoder(r.Body) + if err := d.Decode(&u); err != nil { + http.Error(w, "Form could not be decoded", http.StatusBadRequest) + return + } + + fmt.Fprintf(w, "Decoded: %#v", u) +} +``` + +...without having to do any grunt work. + +Field Tags +---------- + +Like other encoding packages, `form` supports the following options for fields: + + - `` `form:"-"` ``: Causes the field to be ignored during encoding and decoding. + - `` `form:""` ``: Overrides the field's name; useful especially when dealing with external identifiers in camelCase, as are commonly found on the web. + - `` `form:",omitempty"` ``: Elides the field during encoding if it is empty (typically meaning equal to the type's zero value.) + - `` `form:",omitempty"` ``: The way to combine the two options above. + +Values +------ + +### Simple Values + +Values of the following types are all considered simple: + + - `bool` + - `int`, `int8`, `int16`, `int32`, `int64`, `rune` + - `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `byte` + - `float32`, `float64` + - `complex64`, `complex128` + - `string` + - `[]byte` (see note) + - [`time.Time`](http://golang.org/pkg/time/#Time) + - [`url.URL`](http://golang.org/pkg/net/url/#URL) + - An alias of any of the above + - A pointer to any of the above + +### Composite Values + +A composite value is one that can contain other values. Values of the following kinds... + + - Maps + - Slices; except `[]byte` (see note) + - Structs; except [`time.Time`](http://golang.org/pkg/time/#Time) and [`url.URL`](http://golang.org/pkg/net/url/#URL) + - Arrays + - An alias of any of the above + - A pointer to any of the above + +...are considered composites in general, unless they implement custom marshaling/unmarshaling. Composite values are encoded as a flat mapping of paths to values, where the paths are constructed by joining the parent and child paths with a period (`.`). + +(Note: a byte slice is treated as a `string` by default because it's more efficient, but can also be decoded as a slice—i.e., with indexes.) + +### Untyped Values + +While encouraged, it is not necessary to define a type (e.g. a `struct`) in order to use `form`, since it is able to encode and decode untyped data generically using the following rules: + + - Simple values will be treated as a `string`. + - Composite values will be treated as a `map[string]interface{}`, itself able to contain nested values (both scalar and compound) ad infinitum. + - However, if there is a value (of any supported type) already present in a map for a given key, then it will be used when possible, rather than being replaced with a generic value as specified above; this makes it possible to handle partially typed, dynamic or schema-less values. + +### Zero Values + +By default, and without custom marshaling, zero values (also known as empty/default values) are encoded as the empty string. To disable this behavior, meaning to keep zero values in their literal form (e.g. `0` for integral types), `Encoder` offers a `KeepZeros` setter method, which will do just that when set to `true`. + +### Unsupported Values + +Values of the following kinds aren't supported and, if present, must be ignored. + + - Channel + - Function + - Unsafe pointer + - An alias of any of the above + - A pointer to any of the above + +Custom Marshaling +----------------- + +There is a default (generally lossless) marshaling & unmarshaling scheme for any concrete data value in Go, which is good enough in most cases. However, it is possible to override it and use a custom scheme. For instance, a "binary" field could be marshaled more efficiently using [base64](http://golang.org/pkg/encoding/base64/) to prevent it from being percent-escaped during serialization to `application/x-www-form-urlencoded` format. + +Because `form` provides support for [`encoding.TextMarshaler`](http://golang.org/pkg/encoding/#TextMarshaler) and [`encoding.TextUnmarshaler`](http://golang.org/pkg/encoding/#TextUnmarshaler) it is easy to do that; for instance, like this: + +```go +import "encoding" + +type Binary []byte + +var ( + _ encoding.TextMarshaler = &Binary{} + _ encoding.TextUnmarshaler = &Binary{} +) + +func (b Binary) MarshalText() ([]byte, error) { + return []byte(base64.URLEncoding.EncodeToString([]byte(b))), nil +} + +func (b *Binary) UnmarshalText(text []byte) error { + bs, err := base64.URLEncoding.DecodeString(string(text)) + if err == nil { + *b = Binary(bs) + } + return err +} +``` + +Now any value with type `Binary` will automatically be encoded using the [URL](http://golang.org/pkg/encoding/base64/#URLEncoding) variant of base64. It is left as an exercise to the reader to improve upon this scheme by eliminating the need for padding (which, besides being superfluous, uses `=`, a character that will end up percent-escaped.) + +Keys +---- + +In theory any value can be a key as long as it has a string representation. However, by default, periods have special meaning to `form`, and thus, under the hood (i.e. in encoded form) they are transparently escaped using a preceding backslash (`\`). Backslashes within keys, themselves, are also escaped in this manner (e.g. as `\\`) in order to permit representing `\.` itself (as `\\\.`). + +(Note: it is normally unnecessary to deal with this issue unless keys are being constructed manually—e.g. literally embedded in HTML or in a URI.) + +The default delimiter and escape characters used for encoding and decoding composite keys can be changed using the `DelimitWith` and `EscapeWith` setter methods of `Encoder` and `Decoder`, respectively. For example... + +```go +package main + +import ( + "os" + + "github.com/ajg/form" +) + +func main() { + type B struct { + Qux string `form:"qux"` + } + type A struct { + FooBar B `form:"foo.bar"` + } + a := A{FooBar: B{"XYZ"}} + os.Stdout.WriteString("Default: ") + form.NewEncoder(os.Stdout).Encode(a) + os.Stdout.WriteString("\nCustom: ") + form.NewEncoder(os.Stdout).DelimitWith('/').Encode(a) + os.Stdout.WriteString("\n") +} + +``` + +...will produce... + +``` +Default: foo%5C.bar.qux=XYZ +Custom: foo.bar%2Fqux=XYZ +``` + +(`%5C` and `%2F` represent `\` and `/`, respectively.) + +Limitations +----------- + + - Circular (self-referential) values are untested. + +Future Work +----------- + +The following items would be nice to have in the future—though they are not being worked on yet: + + - An option to treat all values as if they had been tagged with `omitempty`. + - An option to automatically treat all field names in `camelCase` or `underscore_case`. + - Built-in support for the types in [`math/big`](http://golang.org/pkg/math/big/). + - Built-in support for the types in [`image/color`](http://golang.org/pkg/image/color/). + - Improve encoding/decoding by reading/writing directly from/to the `io.Reader`/`io.Writer` when possible, rather than going through an intermediate representation (i.e. `node`) which requires more memory. + +(Feel free to implement any of these and then send a pull request.) + +Related Work +------------ + + - Package [gorilla/schema](https://github.com/gorilla/schema), which only implements decoding. + - Package [google/go-querystring](https://github.com/google/go-querystring), which only implements encoding. + +License +------- + +This library is distributed under a BSD-style [LICENSE](./LICENSE). diff --git a/vendor/github.com/ajg/form/TODO.md b/vendor/github.com/ajg/form/TODO.md new file mode 100644 index 00000000..d3447279 --- /dev/null +++ b/vendor/github.com/ajg/form/TODO.md @@ -0,0 +1,4 @@ +TODO +==== + + - Document IgnoreCase and IgnoreUnknownKeys in README. diff --git a/vendor/github.com/ajg/form/decode.go b/vendor/github.com/ajg/form/decode.go new file mode 100644 index 00000000..dd8bd4f2 --- /dev/null +++ b/vendor/github.com/ajg/form/decode.go @@ -0,0 +1,370 @@ +// Copyright 2014 Alvaro J. Genial. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package form + +import ( + "fmt" + "io" + "io/ioutil" + "net/url" + "reflect" + "strconv" + "time" +) + +// NewDecoder returns a new form Decoder. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{r, defaultDelimiter, defaultEscape, false, false} +} + +// Decoder decodes data from a form (application/x-www-form-urlencoded). +type Decoder struct { + r io.Reader + d rune + e rune + ignoreUnknown bool + ignoreCase bool +} + +// DelimitWith sets r as the delimiter used for composite keys by Decoder d and returns the latter; it is '.' by default. +func (d *Decoder) DelimitWith(r rune) *Decoder { + d.d = r + return d +} + +// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Decoder d and returns the latter; it is '\\' by default. +func (d *Decoder) EscapeWith(r rune) *Decoder { + d.e = r + return d +} + +// Decode reads in and decodes form-encoded data into dst. +func (d Decoder) Decode(dst interface{}) error { + bs, err := ioutil.ReadAll(d.r) + if err != nil { + return err + } + vs, err := url.ParseQuery(string(bs)) + if err != nil { + return err + } + v := reflect.ValueOf(dst) + return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) +} + +// IgnoreUnknownKeys if set to true it will make the Decoder ignore values +// that are not found in the destination object instead of returning an error. +func (d *Decoder) IgnoreUnknownKeys(ignoreUnknown bool) { + d.ignoreUnknown = ignoreUnknown +} + +// IgnoreCase if set to true it will make the Decoder try to set values in the +// destination object even if the case does not match. +func (d *Decoder) IgnoreCase(ignoreCase bool) { + d.ignoreCase = ignoreCase +} + +// DecodeString decodes src into dst. +func (d Decoder) DecodeString(dst interface{}, src string) error { + vs, err := url.ParseQuery(src) + if err != nil { + return err + } + v := reflect.ValueOf(dst) + return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) +} + +// DecodeValues decodes vs into dst. +func (d Decoder) DecodeValues(dst interface{}, vs url.Values) error { + v := reflect.ValueOf(dst) + return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) +} + +// DecodeString decodes src into dst. +func DecodeString(dst interface{}, src string) error { + return NewDecoder(nil).DecodeString(dst, src) +} + +// DecodeValues decodes vs into dst. +func DecodeValues(dst interface{}, vs url.Values) error { + return NewDecoder(nil).DecodeValues(dst, vs) +} + +func (d Decoder) decodeNode(v reflect.Value, n node) (err error) { + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("%v", e) + } + }() + + if v.Kind() == reflect.Slice { + return fmt.Errorf("could not decode directly into slice; use pointer to slice") + } + d.decodeValue(v, n) + return nil +} + +func (d Decoder) decodeValue(v reflect.Value, x interface{}) { + t := v.Type() + k := v.Kind() + + if k == reflect.Ptr && v.IsNil() { + v.Set(reflect.New(t.Elem())) + } + + if unmarshalValue(v, x) { + return + } + + empty := isEmpty(x) + + switch k { + case reflect.Ptr: + d.decodeValue(v.Elem(), x) + return + case reflect.Interface: + if !v.IsNil() { + d.decodeValue(v.Elem(), x) + return + + } else if empty { + return // Allow nil interfaces only if empty. + } else { + panic("form: cannot decode non-empty value into into nil interface") + } + } + + if empty { + v.Set(reflect.Zero(t)) // Treat the empty string as the zero value. + return + } + + switch k { + case reflect.Struct: + if t.ConvertibleTo(timeType) { + d.decodeTime(v, x) + } else if t.ConvertibleTo(urlType) { + d.decodeURL(v, x) + } else { + d.decodeStruct(v, x) + } + case reflect.Slice: + d.decodeSlice(v, x) + case reflect.Array: + d.decodeArray(v, x) + case reflect.Map: + d.decodeMap(v, x) + case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: + panic(t.String() + " has unsupported kind " + k.String()) + default: + d.decodeBasic(v, x) + } +} + +func (d Decoder) decodeStruct(v reflect.Value, x interface{}) { + t := v.Type() + for k, c := range getNode(x) { + if f, ok := findField(v, k, d.ignoreCase); !ok && k == "" { + panic(getString(x) + " cannot be decoded as " + t.String()) + } else if !ok { + if !d.ignoreUnknown { + panic(k + " doesn't exist in " + t.String()) + } + } else if !f.CanSet() { + panic(k + " cannot be set in " + t.String()) + } else { + d.decodeValue(f, c) + } + } +} + +func (d Decoder) decodeMap(v reflect.Value, x interface{}) { + t := v.Type() + if v.IsNil() { + v.Set(reflect.MakeMap(t)) + } + for k, c := range getNode(x) { + i := reflect.New(t.Key()).Elem() + d.decodeValue(i, k) + + w := v.MapIndex(i) + if w.IsValid() { // We have an actual element value to decode into. + if w.Kind() == reflect.Interface { + w = w.Elem() + } + w = reflect.New(w.Type()).Elem() + } else if t.Elem().Kind() != reflect.Interface { // The map's element type is concrete. + w = reflect.New(t.Elem()).Elem() + } else { + // The best we can do here is to decode as either a string (for scalars) or a map[string]interface {} (for the rest). + // We could try to guess the type based on the string (e.g. true/false => bool) but that'll get ugly fast, + // especially if we have to guess the kind (slice vs. array vs. map) and index type (e.g. string, int, etc.) + switch c.(type) { + case node: + w = reflect.MakeMap(stringMapType) + case string: + w = reflect.New(stringType).Elem() + default: + panic("value is neither node nor string") + } + } + + d.decodeValue(w, c) + v.SetMapIndex(i, w) + } +} + +func (d Decoder) decodeArray(v reflect.Value, x interface{}) { + t := v.Type() + for k, c := range getNode(x) { + i, err := strconv.Atoi(k) + if err != nil { + panic(k + " is not a valid index for type " + t.String()) + } + if l := v.Len(); i >= l { + panic("index is above array size") + } + d.decodeValue(v.Index(i), c) + } +} + +func (d Decoder) decodeSlice(v reflect.Value, x interface{}) { + t := v.Type() + if t.Elem().Kind() == reflect.Uint8 { + // Allow, but don't require, byte slices to be encoded as a single string. + if s, ok := x.(string); ok { + v.SetBytes([]byte(s)) + return + } + } + + // NOTE: Implicit indexing is currently done at the parseValues level, + // so if if an implicitKey reaches here it will always replace the last. + implicit := 0 + for k, c := range getNode(x) { + var i int + if k == implicitKey { + i = implicit + implicit++ + } else { + explicit, err := strconv.Atoi(k) + if err != nil { + panic(k + " is not a valid index for type " + t.String()) + } + i = explicit + implicit = explicit + 1 + } + // "Extend" the slice if it's too short. + if l := v.Len(); i >= l { + delta := i - l + 1 + v.Set(reflect.AppendSlice(v, reflect.MakeSlice(t, delta, delta))) + } + d.decodeValue(v.Index(i), c) + } +} + +func (d Decoder) decodeBasic(v reflect.Value, x interface{}) { + t := v.Type() + switch k, s := t.Kind(), getString(x); k { + case reflect.Bool: + if b, e := strconv.ParseBool(s); e == nil { + v.SetBool(b) + } else { + panic("could not parse bool from " + strconv.Quote(s)) + } + case reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64: + if i, e := strconv.ParseInt(s, 10, 64); e == nil { + v.SetInt(i) + } else { + panic("could not parse int from " + strconv.Quote(s)) + } + case reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64: + if u, e := strconv.ParseUint(s, 10, 64); e == nil { + v.SetUint(u) + } else { + panic("could not parse uint from " + strconv.Quote(s)) + } + case reflect.Float32, + reflect.Float64: + if f, e := strconv.ParseFloat(s, 64); e == nil { + v.SetFloat(f) + } else { + panic("could not parse float from " + strconv.Quote(s)) + } + case reflect.Complex64, + reflect.Complex128: + var c complex128 + if n, err := fmt.Sscanf(s, "%g", &c); n == 1 && err == nil { + v.SetComplex(c) + } else { + panic("could not parse complex from " + strconv.Quote(s)) + } + case reflect.String: + v.SetString(s) + default: + panic(t.String() + " has unsupported kind " + k.String()) + } +} + +func (d Decoder) decodeTime(v reflect.Value, x interface{}) { + t := v.Type() + s := getString(x) + // TODO: Find a more efficient way to do this. + for _, f := range allowedTimeFormats { + if p, err := time.Parse(f, s); err == nil { + v.Set(reflect.ValueOf(p).Convert(v.Type())) + return + } + } + panic("cannot decode string `" + s + "` as " + t.String()) +} + +func (d Decoder) decodeURL(v reflect.Value, x interface{}) { + t := v.Type() + s := getString(x) + if u, err := url.Parse(s); err == nil { + v.Set(reflect.ValueOf(*u).Convert(v.Type())) + return + } + panic("cannot decode string `" + s + "` as " + t.String()) +} + +var allowedTimeFormats = []string{ + "2006-01-02T15:04:05.999999999Z07:00", + "2006-01-02T15:04:05.999999999Z07", + "2006-01-02T15:04:05.999999999Z", + "2006-01-02T15:04:05.999999999", + "2006-01-02T15:04:05Z07:00", + "2006-01-02T15:04:05Z07", + "2006-01-02T15:04:05Z", + "2006-01-02T15:04:05", + "2006-01-02T15:04Z", + "2006-01-02T15:04", + "2006-01-02T15Z", + "2006-01-02T15", + "2006-01-02", + "2006-01", + "2006", + "15:04:05.999999999Z07:00", + "15:04:05.999999999Z07", + "15:04:05.999999999Z", + "15:04:05.999999999", + "15:04:05Z07:00", + "15:04:05Z07", + "15:04:05Z", + "15:04:05", + "15:04Z", + "15:04", + "15Z", + "15", +} diff --git a/vendor/github.com/ajg/form/encode.go b/vendor/github.com/ajg/form/encode.go new file mode 100644 index 00000000..57a0d0a5 --- /dev/null +++ b/vendor/github.com/ajg/form/encode.go @@ -0,0 +1,388 @@ +// Copyright 2014 Alvaro J. Genial. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package form + +import ( + "encoding" + "errors" + "fmt" + "io" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +// NewEncoder returns a new form Encoder. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{w, defaultDelimiter, defaultEscape, false} +} + +// Encoder provides a way to encode to a Writer. +type Encoder struct { + w io.Writer + d rune + e rune + z bool +} + +// DelimitWith sets r as the delimiter used for composite keys by Encoder e and returns the latter; it is '.' by default. +func (e *Encoder) DelimitWith(r rune) *Encoder { + e.d = r + return e +} + +// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Encoder e and returns the latter; it is '\\' by default. +func (e *Encoder) EscapeWith(r rune) *Encoder { + e.e = r + return e +} + +// KeepZeros sets whether Encoder e should keep zero (default) values in their literal form when encoding, and returns the former; by default zero values are not kept, but are rather encoded as the empty string. +func (e *Encoder) KeepZeros(z bool) *Encoder { + e.z = z + return e +} + +// Encode encodes dst as form and writes it out using the Encoder's Writer. +func (e Encoder) Encode(dst interface{}) error { + v := reflect.ValueOf(dst) + n, err := encodeToNode(v, e.z) + if err != nil { + return err + } + s := n.values(e.d, e.e).Encode() + l, err := io.WriteString(e.w, s) + switch { + case err != nil: + return err + case l != len(s): + return errors.New("could not write data completely") + } + return nil +} + +// EncodeToString encodes dst as a form and returns it as a string. +func EncodeToString(dst interface{}) (string, error) { + v := reflect.ValueOf(dst) + n, err := encodeToNode(v, false) + if err != nil { + return "", err + } + vs := n.values(defaultDelimiter, defaultEscape) + return vs.Encode(), nil +} + +// EncodeToValues encodes dst as a form and returns it as Values. +func EncodeToValues(dst interface{}) (url.Values, error) { + v := reflect.ValueOf(dst) + n, err := encodeToNode(v, false) + if err != nil { + return nil, err + } + vs := n.values(defaultDelimiter, defaultEscape) + return vs, nil +} + +func encodeToNode(v reflect.Value, z bool) (n node, err error) { + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("%v", e) + } + }() + return getNode(encodeValue(v, z)), nil +} + +func encodeValue(v reflect.Value, z bool) interface{} { + t := v.Type() + k := v.Kind() + + if s, ok := marshalValue(v); ok { + return s + } else if !z && isEmptyValue(v) { + return "" // Treat the zero value as the empty string. + } + + switch k { + case reflect.Ptr, reflect.Interface: + return encodeValue(v.Elem(), z) + case reflect.Struct: + if t.ConvertibleTo(timeType) { + return encodeTime(v) + } else if t.ConvertibleTo(urlType) { + return encodeURL(v) + } + return encodeStruct(v, z) + case reflect.Slice: + return encodeSlice(v, z) + case reflect.Array: + return encodeArray(v, z) + case reflect.Map: + return encodeMap(v, z) + case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: + panic(t.String() + " has unsupported kind " + t.Kind().String()) + default: + return encodeBasic(v) + } +} + +func encodeStruct(v reflect.Value, z bool) interface{} { + t := v.Type() + n := node{} + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + k, oe := fieldInfo(f) + + if k == "-" { + continue + } else if fv := v.Field(i); oe && isEmptyValue(fv) { + delete(n, k) + } else { + n[k] = encodeValue(fv, z) + } + } + return n +} + +func encodeMap(v reflect.Value, z bool) interface{} { + n := node{} + for _, i := range v.MapKeys() { + k := getString(encodeValue(i, z)) + n[k] = encodeValue(v.MapIndex(i), z) + } + return n +} + +func encodeArray(v reflect.Value, z bool) interface{} { + n := node{} + for i := 0; i < v.Len(); i++ { + n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) + } + return n +} + +func encodeSlice(v reflect.Value, z bool) interface{} { + t := v.Type() + if t.Elem().Kind() == reflect.Uint8 { + return string(v.Bytes()) // Encode byte slices as a single string by default. + } + n := node{} + for i := 0; i < v.Len(); i++ { + n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) + } + return n +} + +func encodeTime(v reflect.Value) string { + t := v.Convert(timeType).Interface().(time.Time) + if t.Year() == 0 && (t.Month() == 0 || t.Month() == 1) && (t.Day() == 0 || t.Day() == 1) { + return t.Format("15:04:05.999999999Z07:00") + } else if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 && t.Nanosecond() == 0 { + return t.Format("2006-01-02") + } + return t.Format("2006-01-02T15:04:05.999999999Z07:00") +} + +func encodeURL(v reflect.Value) string { + u := v.Convert(urlType).Interface().(url.URL) + return u.String() +} + +func encodeBasic(v reflect.Value) string { + t := v.Type() + switch k := t.Kind(); k { + case reflect.Bool: + return strconv.FormatBool(v.Bool()) + case reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64: + return strconv.FormatInt(v.Int(), 10) + case reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64: + return strconv.FormatUint(v.Uint(), 10) + case reflect.Float32: + return strconv.FormatFloat(v.Float(), 'g', -1, 32) + case reflect.Float64: + return strconv.FormatFloat(v.Float(), 'g', -1, 64) + case reflect.Complex64, reflect.Complex128: + s := fmt.Sprintf("%g", v.Complex()) + return strings.TrimSuffix(strings.TrimPrefix(s, "("), ")") + case reflect.String: + return v.String() + } + panic(t.String() + " has unsupported kind " + t.Kind().String()) +} + +func isEmptyValue(v reflect.Value) bool { + switch t := v.Type(); v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Complex64, reflect.Complex128: + return v.Complex() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Struct: + if t.ConvertibleTo(timeType) { + return v.Convert(timeType).Interface().(time.Time).IsZero() + } + return reflect.DeepEqual(v, reflect.Zero(t)) + } + return false +} + +// canIndexOrdinally returns whether a value contains an ordered sequence of elements. +func canIndexOrdinally(v reflect.Value) bool { + if !v.IsValid() { + return false + } + switch t := v.Type(); t.Kind() { + case reflect.Ptr, reflect.Interface: + return canIndexOrdinally(v.Elem()) + case reflect.Slice, reflect.Array: + return true + } + return false +} + +func fieldInfo(f reflect.StructField) (k string, oe bool) { + if f.PkgPath != "" { // Skip private fields. + return omittedKey, oe + } + + k = f.Name + tag := f.Tag.Get("form") + if tag == "" { + return k, oe + } + + ps := strings.SplitN(tag, ",", 2) + if ps[0] != "" { + k = ps[0] + } + if len(ps) == 2 { + oe = ps[1] == "omitempty" + } + return k, oe +} + +func findField(v reflect.Value, n string, ignoreCase bool) (reflect.Value, bool) { + t := v.Type() + l := v.NumField() + + var lowerN string + caseInsensitiveMatch := -1 + if ignoreCase { + lowerN = strings.ToLower(n) + } + + // First try named fields. + for i := 0; i < l; i++ { + f := t.Field(i) + k, _ := fieldInfo(f) + if k == omittedKey { + continue + } else if n == k { + return v.Field(i), true + } else if ignoreCase && lowerN == strings.ToLower(k) { + caseInsensitiveMatch = i + } + } + + // If no exact match was found try case insensitive match. + if caseInsensitiveMatch != -1 { + return v.Field(caseInsensitiveMatch), true + } + + // Then try anonymous (embedded) fields. + for i := 0; i < l; i++ { + f := t.Field(i) + k, _ := fieldInfo(f) + if k == omittedKey || !f.Anonymous { // || k != "" ? + continue + } + fv := v.Field(i) + fk := fv.Kind() + for fk == reflect.Ptr || fk == reflect.Interface { + fv = fv.Elem() + fk = fv.Kind() + } + + if fk != reflect.Struct { + continue + } + if ev, ok := findField(fv, n, ignoreCase); ok { + return ev, true + } + } + + return reflect.Value{}, false +} + +var ( + stringType = reflect.TypeOf(string("")) + stringMapType = reflect.TypeOf(map[string]interface{}{}) + timeType = reflect.TypeOf(time.Time{}) + timePtrType = reflect.TypeOf(&time.Time{}) + urlType = reflect.TypeOf(url.URL{}) +) + +func skipTextMarshalling(t reflect.Type) bool { + /*// Skip time.Time because its text unmarshaling is overly rigid: + return t == timeType || t == timePtrType*/ + // Skip time.Time & convertibles because its text unmarshaling is overly rigid: + return t.ConvertibleTo(timeType) || t.ConvertibleTo(timePtrType) +} + +func unmarshalValue(v reflect.Value, x interface{}) bool { + if skipTextMarshalling(v.Type()) { + return false + } + + tu, ok := v.Interface().(encoding.TextUnmarshaler) + if !ok && !v.CanAddr() { + return false + } else if !ok { + return unmarshalValue(v.Addr(), x) + } + + s := getString(x) + if err := tu.UnmarshalText([]byte(s)); err != nil { + panic(err) + } + return true +} + +func marshalValue(v reflect.Value) (string, bool) { + if skipTextMarshalling(v.Type()) { + return "", false + } + + tm, ok := v.Interface().(encoding.TextMarshaler) + if !ok && !v.CanAddr() { + return "", false + } else if !ok { + return marshalValue(v.Addr()) + } + + bs, err := tm.MarshalText() + if err != nil { + panic(err) + } + return string(bs), true +} diff --git a/vendor/github.com/ajg/form/form.go b/vendor/github.com/ajg/form/form.go new file mode 100644 index 00000000..4052369c --- /dev/null +++ b/vendor/github.com/ajg/form/form.go @@ -0,0 +1,14 @@ +// Copyright 2014 Alvaro J. Genial. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package form implements encoding and decoding of application/x-www-form-urlencoded data. +package form + +const ( + implicitKey = "_" + omittedKey = "-" + + defaultDelimiter = '.' + defaultEscape = '\\' +) diff --git a/vendor/github.com/ajg/form/node.go b/vendor/github.com/ajg/form/node.go new file mode 100644 index 00000000..567aaafd --- /dev/null +++ b/vendor/github.com/ajg/form/node.go @@ -0,0 +1,152 @@ +// Copyright 2014 Alvaro J. Genial. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package form + +import ( + "net/url" + "strconv" + "strings" +) + +type node map[string]interface{} + +func (n node) values(d, e rune) url.Values { + vs := url.Values{} + n.merge(d, e, "", &vs) + return vs +} + +func (n node) merge(d, e rune, p string, vs *url.Values) { + for k, x := range n { + switch y := x.(type) { + case string: + vs.Add(p+escape(d, e, k), y) + case node: + y.merge(d, e, p+escape(d, e, k)+string(d), vs) + default: + panic("value is neither string nor node") + } + } +} + +// TODO: Add tests for implicit indexing. +func parseValues(d, e rune, vs url.Values, canIndexFirstLevelOrdinally bool) node { + // NOTE: Because of the flattening of potentially multiple strings to one key, implicit indexing works: + // i. At the first level; e.g. Foo.Bar=A&Foo.Bar=B becomes 0.Foo.Bar=A&1.Foo.Bar=B + // ii. At the last level; e.g. Foo.Bar._=A&Foo.Bar._=B becomes Foo.Bar.0=A&Foo.Bar.1=B + // TODO: At in-between levels; e.g. Foo._.Bar=A&Foo._.Bar=B becomes Foo.0.Bar=A&Foo.1.Bar=B + // (This last one requires that there only be one placeholder in order for it to be unambiguous.) + + m := map[string]string{} + for k, ss := range vs { + indexLastLevelOrdinally := strings.HasSuffix(k, string(d)+implicitKey) + + for i, s := range ss { + if canIndexFirstLevelOrdinally { + k = strconv.Itoa(i) + string(d) + k + } else if indexLastLevelOrdinally { + k = strings.TrimSuffix(k, implicitKey) + strconv.Itoa(i) + } + + m[k] = s + } + } + + n := node{} + for k, s := range m { + n = n.split(d, e, k, s) + } + return n +} + +func splitPath(d, e rune, path string) (k, rest string) { + esc := false + for i, r := range path { + switch { + case !esc && r == e: + esc = true + case !esc && r == d: + return unescape(d, e, path[:i]), path[i+1:] + default: + esc = false + } + } + return unescape(d, e, path), "" +} + +func (n node) split(d, e rune, path, s string) node { + k, rest := splitPath(d, e, path) + if rest == "" { + return add(n, k, s) + } + if _, ok := n[k]; !ok { + n[k] = node{} + } + + c := getNode(n[k]) + n[k] = c.split(d, e, rest, s) + return n +} + +func add(n node, k, s string) node { + if n == nil { + return node{k: s} + } + + if _, ok := n[k]; ok { + panic("key " + k + " already set") + } + + n[k] = s + return n +} + +func isEmpty(x interface{}) bool { + switch y := x.(type) { + case string: + return y == "" + case node: + if s, ok := y[""].(string); ok { + return s == "" + } + return false + } + panic("value is neither string nor node") +} + +func getNode(x interface{}) node { + switch y := x.(type) { + case string: + return node{"": y} + case node: + return y + } + panic("value is neither string nor node") +} + +func getString(x interface{}) string { + switch y := x.(type) { + case string: + return y + case node: + if s, ok := y[""].(string); ok { + return s + } + return "" + } + panic("value is neither string nor node") +} + +func escape(d, e rune, s string) string { + s = strings.Replace(s, string(e), string(e)+string(e), -1) // Escape the escape (\ => \\) + s = strings.Replace(s, string(d), string(e)+string(d), -1) // Escape the delimiter (. => \.) + return s +} + +func unescape(d, e rune, s string) string { + s = strings.Replace(s, string(e)+string(d), string(d), -1) // Unescape the delimiter (\. => .) + s = strings.Replace(s, string(e)+string(e), string(e), -1) // Unescape the escape (\\ => \) + return s +} diff --git a/vendor/github.com/ajg/form/pre-commit.sh b/vendor/github.com/ajg/form/pre-commit.sh new file mode 100644 index 00000000..29ce311e --- /dev/null +++ b/vendor/github.com/ajg/form/pre-commit.sh @@ -0,0 +1,18 @@ +#!/bin/bash -eu + +# TODO: Only colorize messages given a suitable terminal. +# FIXME: Handle case in which no stash entry is created due to no changes. + +printf "\e[30m=== PRE-COMMIT STARTING ===\e[m\n" +git stash save --quiet --keep-index --include-untracked + +if go build -v ./... && go test -v -cover ./... && go vet ./... && golint . && travis-lint; then + result=$? + printf "\e[32m=== PRE-COMMIT SUCCEEDED ===\e[m\n" +else + result=$? + printf "\e[31m=== PRE-COMMIT FAILED ===\e[m\n" +fi + +git stash pop --quiet +exit $result diff --git a/vendor/github.com/bogem/id3v2/v2/encoding.go b/vendor/github.com/bogem/id3v2/v2/encoding.go index 65cb1e85..6c89e89e 100644 --- a/vendor/github.com/bogem/id3v2/v2/encoding.go +++ b/vendor/github.com/bogem/id3v2/v2/encoding.go @@ -4,7 +4,7 @@ import ( "bytes" "io/ioutil" - xencoding "golang.org/x/text/encoding" + "golang.org/x/text/encoding" "golang.org/x/text/encoding/charmap" "golang.org/x/text/encoding/unicode" ) @@ -24,29 +24,6 @@ func (e Encoding) String() string { return e.Name } -// xencodingWrapper is a struct that stores decoder and encoder for -// appropriate x/text/encoding. It's used to reduce allocations -// through creating decoder and encoder only one time and storing it. -type xencodingWrapper struct { - decoder *xencoding.Decoder - encoder *xencoding.Encoder -} - -func newXEncodingWrapper(e xencoding.Encoding) xencodingWrapper { - return xencodingWrapper{ - decoder: e.NewDecoder(), - encoder: e.NewEncoder(), - } -} - -func (e *xencodingWrapper) Decoder() *xencoding.Decoder { - return e.decoder -} - -func (e *xencodingWrapper) Encoder() *xencoding.Encoder { - return e.encoder -} - // Available encodings. var ( // EncodingISO is ISO-8859-1 encoding. @@ -79,11 +56,11 @@ var ( encodings = []Encoding{EncodingISO, EncodingUTF16, EncodingUTF16BE, EncodingUTF8} - xencodingISO = newXEncodingWrapper(charmap.ISO8859_1) - xencodingUTF16BEBOM = newXEncodingWrapper(unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM)) - xencodingUTF16LEBOM = newXEncodingWrapper(unicode.UTF16(unicode.LittleEndian, unicode.ExpectBOM)) - xencodingUTF16BE = newXEncodingWrapper(unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)) - xencodingUTF8 = newXEncodingWrapper(unicode.UTF8) + xencodingISO = charmap.ISO8859_1 + xencodingUTF16BEBOM = unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM) + xencodingUTF16LEBOM = unicode.UTF16(unicode.LittleEndian, unicode.ExpectBOM) + xencodingUTF16BE = unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM) + xencodingUTF8 = unicode.UTF8 ) // bom is used in UTF-16 encoded Unicode with BOM. @@ -127,7 +104,7 @@ func decodeText(src []byte, from Encoding) string { } fromXEncoding := resolveXEncoding(src, from) - result, err := fromXEncoding.Decoder().Bytes(src) + result, err := fromXEncoding.NewDecoder().Bytes(src) if err != nil { return string(src) } @@ -152,7 +129,7 @@ func encodeWriteText(bw *bufWriter, src string, to Encoding) error { } toXEncoding := resolveXEncoding(nil, to) - encoded, err := toXEncoding.Encoder().String(src) + encoded, err := toXEncoding.NewEncoder().String(src) if err != nil { return err } @@ -166,7 +143,7 @@ func encodeWriteText(bw *bufWriter, src string, to Encoding) error { return nil } -func resolveXEncoding(src []byte, encoding Encoding) xencodingWrapper { +func resolveXEncoding(src []byte, encoding Encoding) encoding.Encoding { switch encoding.Key { case 0: return xencodingISO diff --git a/vendor/github.com/cespare/xxhash/v2/LICENSE.txt b/vendor/github.com/cespare/xxhash/v2/LICENSE.txt new file mode 100644 index 00000000..24b53065 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2016 Caleb Spare + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cespare/xxhash/v2/README.md b/vendor/github.com/cespare/xxhash/v2/README.md new file mode 100644 index 00000000..8bf0e5b7 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/README.md @@ -0,0 +1,72 @@ +# xxhash + +[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) +[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml) + +xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a +high-quality hashing algorithm that is much faster than anything in the Go +standard library. + +This package provides a straightforward API: + +``` +func Sum64(b []byte) uint64 +func Sum64String(s string) uint64 +type Digest struct{ ... } + func New() *Digest +``` + +The `Digest` type implements hash.Hash64. Its key methods are: + +``` +func (*Digest) Write([]byte) (int, error) +func (*Digest) WriteString(string) (int, error) +func (*Digest) Sum64() uint64 +``` + +The package is written with optimized pure Go and also contains even faster +assembly implementations for amd64 and arm64. If desired, the `purego` build tag +opts into using the Go code even on those architectures. + +[xxHash]: http://cyan4973.github.io/xxHash/ + +## Compatibility + +This package is in a module and the latest code is in version 2 of the module. +You need a version of Go with at least "minimal module compatibility" to use +github.com/cespare/xxhash/v2: + +* 1.9.7+ for Go 1.9 +* 1.10.3+ for Go 1.10 +* Go 1.11 or later + +I recommend using the latest release of Go. + +## Benchmarks + +Here are some quick benchmarks comparing the pure-Go and assembly +implementations of Sum64. + +| input size | purego | asm | +| ---------- | --------- | --------- | +| 4 B | 1.3 GB/s | 1.2 GB/s | +| 16 B | 2.9 GB/s | 3.5 GB/s | +| 100 B | 6.9 GB/s | 8.1 GB/s | +| 4 KB | 11.7 GB/s | 16.7 GB/s | +| 10 MB | 12.0 GB/s | 17.3 GB/s | + +These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C +CPU using the following commands under Go 1.19.2: + +``` +benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$') +benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$') +``` + +## Projects using this package + +- [InfluxDB](https://github.com/influxdata/influxdb) +- [Prometheus](https://github.com/prometheus/prometheus) +- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) +- [FreeCache](https://github.com/coocood/freecache) +- [FastCache](https://github.com/VictoriaMetrics/fastcache) diff --git a/vendor/github.com/cespare/xxhash/v2/testall.sh b/vendor/github.com/cespare/xxhash/v2/testall.sh new file mode 100644 index 00000000..94b9c443 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/testall.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -eu -o pipefail + +# Small convenience script for running the tests with various combinations of +# arch/tags. This assumes we're running on amd64 and have qemu available. + +go test ./... +go test -tags purego ./... +GOARCH=arm64 go test +GOARCH=arm64 go test -tags purego diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash.go b/vendor/github.com/cespare/xxhash/v2/xxhash.go new file mode 100644 index 00000000..a9e0d45c --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash.go @@ -0,0 +1,228 @@ +// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described +// at http://cyan4973.github.io/xxHash/. +package xxhash + +import ( + "encoding/binary" + "errors" + "math/bits" +) + +const ( + prime1 uint64 = 11400714785074694791 + prime2 uint64 = 14029467366897019727 + prime3 uint64 = 1609587929392839161 + prime4 uint64 = 9650029242287828579 + prime5 uint64 = 2870177450012600261 +) + +// Store the primes in an array as well. +// +// The consts are used when possible in Go code to avoid MOVs but we need a +// contiguous array of the assembly code. +var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} + +// Digest implements hash.Hash64. +type Digest struct { + v1 uint64 + v2 uint64 + v3 uint64 + v4 uint64 + total uint64 + mem [32]byte + n int // how much of mem is used +} + +// New creates a new Digest that computes the 64-bit xxHash algorithm. +func New() *Digest { + var d Digest + d.Reset() + return &d +} + +// Reset clears the Digest's state so that it can be reused. +func (d *Digest) Reset() { + d.v1 = primes[0] + prime2 + d.v2 = prime2 + d.v3 = 0 + d.v4 = -primes[0] + d.total = 0 + d.n = 0 +} + +// Size always returns 8 bytes. +func (d *Digest) Size() int { return 8 } + +// BlockSize always returns 32 bytes. +func (d *Digest) BlockSize() int { return 32 } + +// Write adds more data to d. It always returns len(b), nil. +func (d *Digest) Write(b []byte) (n int, err error) { + n = len(b) + d.total += uint64(n) + + memleft := d.mem[d.n&(len(d.mem)-1):] + + if d.n+n < 32 { + // This new data doesn't even fill the current block. + copy(memleft, b) + d.n += n + return + } + + if d.n > 0 { + // Finish off the partial block. + c := copy(memleft, b) + d.v1 = round(d.v1, u64(d.mem[0:8])) + d.v2 = round(d.v2, u64(d.mem[8:16])) + d.v3 = round(d.v3, u64(d.mem[16:24])) + d.v4 = round(d.v4, u64(d.mem[24:32])) + b = b[c:] + d.n = 0 + } + + if len(b) >= 32 { + // One or more full blocks left. + nw := writeBlocks(d, b) + b = b[nw:] + } + + // Store any remaining partial block. + copy(d.mem[:], b) + d.n = len(b) + + return +} + +// Sum appends the current hash to b and returns the resulting slice. +func (d *Digest) Sum(b []byte) []byte { + s := d.Sum64() + return append( + b, + byte(s>>56), + byte(s>>48), + byte(s>>40), + byte(s>>32), + byte(s>>24), + byte(s>>16), + byte(s>>8), + byte(s), + ) +} + +// Sum64 returns the current hash. +func (d *Digest) Sum64() uint64 { + var h uint64 + + if d.total >= 32 { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = d.v3 + prime5 + } + + h += d.total + + b := d.mem[:d.n&(len(d.mem)-1)] + for ; len(b) >= 8; b = b[8:] { + k1 := round(0, u64(b[:8])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if len(b) >= 4 { + h ^= uint64(u32(b[:4])) * prime1 + h = rol23(h)*prime2 + prime3 + b = b[4:] + } + for ; len(b) > 0; b = b[1:] { + h ^= uint64(b[0]) * prime5 + h = rol11(h) * prime1 + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +const ( + magic = "xxh\x06" + marshaledSize = len(magic) + 8*5 + 32 +) + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d *Digest) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, marshaledSize) + b = append(b, magic...) + b = appendUint64(b, d.v1) + b = appendUint64(b, d.v2) + b = appendUint64(b, d.v3) + b = appendUint64(b, d.v4) + b = appendUint64(b, d.total) + b = append(b, d.mem[:d.n]...) + b = b[:len(b)+len(d.mem)-d.n] + return b, nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (d *Digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic) || string(b[:len(magic)]) != magic { + return errors.New("xxhash: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("xxhash: invalid hash state size") + } + b = b[len(magic):] + b, d.v1 = consumeUint64(b) + b, d.v2 = consumeUint64(b) + b, d.v3 = consumeUint64(b) + b, d.v4 = consumeUint64(b) + b, d.total = consumeUint64(b) + copy(d.mem[:], b) + d.n = int(d.total % uint64(len(d.mem))) + return nil +} + +func appendUint64(b []byte, x uint64) []byte { + var a [8]byte + binary.LittleEndian.PutUint64(a[:], x) + return append(b, a[:]...) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + x := u64(b) + return b[8:], x +} + +func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } +func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } + +func round(acc, input uint64) uint64 { + acc += input * prime2 + acc = rol31(acc) + acc *= prime1 + return acc +} + +func mergeRound(acc, val uint64) uint64 { + val = round(0, val) + acc ^= val + acc = acc*prime1 + prime4 + return acc +} + +func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } +func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } +func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } +func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } +func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } +func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } +func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } +func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s new file mode 100644 index 00000000..3e8b1325 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s @@ -0,0 +1,209 @@ +//go:build !appengine && gc && !purego +// +build !appengine +// +build gc +// +build !purego + +#include "textflag.h" + +// Registers: +#define h AX +#define d AX +#define p SI // pointer to advance through b +#define n DX +#define end BX // loop end +#define v1 R8 +#define v2 R9 +#define v3 R10 +#define v4 R11 +#define x R12 +#define prime1 R13 +#define prime2 R14 +#define prime4 DI + +#define round(acc, x) \ + IMULQ prime2, x \ + ADDQ x, acc \ + ROLQ $31, acc \ + IMULQ prime1, acc + +// round0 performs the operation x = round(0, x). +#define round0(x) \ + IMULQ prime2, x \ + ROLQ $31, x \ + IMULQ prime1, x + +// mergeRound applies a merge round on the two registers acc and x. +// It assumes that prime1, prime2, and prime4 have been loaded. +#define mergeRound(acc, x) \ + round0(x) \ + XORQ x, acc \ + IMULQ prime1, acc \ + ADDQ prime4, acc + +// blockLoop processes as many 32-byte blocks as possible, +// updating v1, v2, v3, and v4. It assumes that there is at least one block +// to process. +#define blockLoop() \ +loop: \ + MOVQ +0(p), x \ + round(v1, x) \ + MOVQ +8(p), x \ + round(v2, x) \ + MOVQ +16(p), x \ + round(v3, x) \ + MOVQ +24(p), x \ + round(v4, x) \ + ADDQ $32, p \ + CMPQ p, end \ + JLE loop + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 + // Load fixed primes. + MOVQ ·primes+0(SB), prime1 + MOVQ ·primes+8(SB), prime2 + MOVQ ·primes+24(SB), prime4 + + // Load slice. + MOVQ b_base+0(FP), p + MOVQ b_len+8(FP), n + LEAQ (p)(n*1), end + + // The first loop limit will be len(b)-32. + SUBQ $32, end + + // Check whether we have at least one block. + CMPQ n, $32 + JLT noBlocks + + // Set up initial state (v1, v2, v3, v4). + MOVQ prime1, v1 + ADDQ prime2, v1 + MOVQ prime2, v2 + XORQ v3, v3 + XORQ v4, v4 + SUBQ prime1, v4 + + blockLoop() + + MOVQ v1, h + ROLQ $1, h + MOVQ v2, x + ROLQ $7, x + ADDQ x, h + MOVQ v3, x + ROLQ $12, x + ADDQ x, h + MOVQ v4, x + ROLQ $18, x + ADDQ x, h + + mergeRound(h, v1) + mergeRound(h, v2) + mergeRound(h, v3) + mergeRound(h, v4) + + JMP afterBlocks + +noBlocks: + MOVQ ·primes+32(SB), h + +afterBlocks: + ADDQ n, h + + ADDQ $24, end + CMPQ p, end + JG try4 + +loop8: + MOVQ (p), x + ADDQ $8, p + round0(x) + XORQ x, h + ROLQ $27, h + IMULQ prime1, h + ADDQ prime4, h + + CMPQ p, end + JLE loop8 + +try4: + ADDQ $4, end + CMPQ p, end + JG try1 + + MOVL (p), x + ADDQ $4, p + IMULQ prime1, x + XORQ x, h + + ROLQ $23, h + IMULQ prime2, h + ADDQ ·primes+16(SB), h + +try1: + ADDQ $4, end + CMPQ p, end + JGE finalize + +loop1: + MOVBQZX (p), x + ADDQ $1, p + IMULQ ·primes+32(SB), x + XORQ x, h + ROLQ $11, h + IMULQ prime1, h + + CMPQ p, end + JL loop1 + +finalize: + MOVQ h, x + SHRQ $33, x + XORQ x, h + IMULQ prime2, h + MOVQ h, x + SHRQ $29, x + XORQ x, h + IMULQ ·primes+16(SB), h + MOVQ h, x + SHRQ $32, x + XORQ x, h + + MOVQ h, ret+24(FP) + RET + +// func writeBlocks(d *Digest, b []byte) int +TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 + // Load fixed primes needed for round. + MOVQ ·primes+0(SB), prime1 + MOVQ ·primes+8(SB), prime2 + + // Load slice. + MOVQ b_base+8(FP), p + MOVQ b_len+16(FP), n + LEAQ (p)(n*1), end + SUBQ $32, end + + // Load vN from d. + MOVQ s+0(FP), d + MOVQ 0(d), v1 + MOVQ 8(d), v2 + MOVQ 16(d), v3 + MOVQ 24(d), v4 + + // We don't need to check the loop condition here; this function is + // always called with at least one block of data to process. + blockLoop() + + // Copy vN back to d. + MOVQ v1, 0(d) + MOVQ v2, 8(d) + MOVQ v3, 16(d) + MOVQ v4, 24(d) + + // The number of bytes written is p minus the old base pointer. + SUBQ b_base+8(FP), p + MOVQ p, ret+32(FP) + + RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s new file mode 100644 index 00000000..7e3145a2 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s @@ -0,0 +1,183 @@ +//go:build !appengine && gc && !purego +// +build !appengine +// +build gc +// +build !purego + +#include "textflag.h" + +// Registers: +#define digest R1 +#define h R2 // return value +#define p R3 // input pointer +#define n R4 // input length +#define nblocks R5 // n / 32 +#define prime1 R7 +#define prime2 R8 +#define prime3 R9 +#define prime4 R10 +#define prime5 R11 +#define v1 R12 +#define v2 R13 +#define v3 R14 +#define v4 R15 +#define x1 R20 +#define x2 R21 +#define x3 R22 +#define x4 R23 + +#define round(acc, x) \ + MADD prime2, acc, x, acc \ + ROR $64-31, acc \ + MUL prime1, acc + +// round0 performs the operation x = round(0, x). +#define round0(x) \ + MUL prime2, x \ + ROR $64-31, x \ + MUL prime1, x + +#define mergeRound(acc, x) \ + round0(x) \ + EOR x, acc \ + MADD acc, prime4, prime1, acc + +// blockLoop processes as many 32-byte blocks as possible, +// updating v1, v2, v3, and v4. It assumes that n >= 32. +#define blockLoop() \ + LSR $5, n, nblocks \ + PCALIGN $16 \ + loop: \ + LDP.P 16(p), (x1, x2) \ + LDP.P 16(p), (x3, x4) \ + round(v1, x1) \ + round(v2, x2) \ + round(v3, x3) \ + round(v4, x4) \ + SUB $1, nblocks \ + CBNZ nblocks, loop + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 + LDP b_base+0(FP), (p, n) + + LDP ·primes+0(SB), (prime1, prime2) + LDP ·primes+16(SB), (prime3, prime4) + MOVD ·primes+32(SB), prime5 + + CMP $32, n + CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 } + BLT afterLoop + + ADD prime1, prime2, v1 + MOVD prime2, v2 + MOVD $0, v3 + NEG prime1, v4 + + blockLoop() + + ROR $64-1, v1, x1 + ROR $64-7, v2, x2 + ADD x1, x2 + ROR $64-12, v3, x3 + ROR $64-18, v4, x4 + ADD x3, x4 + ADD x2, x4, h + + mergeRound(h, v1) + mergeRound(h, v2) + mergeRound(h, v3) + mergeRound(h, v4) + +afterLoop: + ADD n, h + + TBZ $4, n, try8 + LDP.P 16(p), (x1, x2) + + round0(x1) + + // NOTE: here and below, sequencing the EOR after the ROR (using a + // rotated register) is worth a small but measurable speedup for small + // inputs. + ROR $64-27, h + EOR x1 @> 64-27, h, h + MADD h, prime4, prime1, h + + round0(x2) + ROR $64-27, h + EOR x2 @> 64-27, h, h + MADD h, prime4, prime1, h + +try8: + TBZ $3, n, try4 + MOVD.P 8(p), x1 + + round0(x1) + ROR $64-27, h + EOR x1 @> 64-27, h, h + MADD h, prime4, prime1, h + +try4: + TBZ $2, n, try2 + MOVWU.P 4(p), x2 + + MUL prime1, x2 + ROR $64-23, h + EOR x2 @> 64-23, h, h + MADD h, prime3, prime2, h + +try2: + TBZ $1, n, try1 + MOVHU.P 2(p), x3 + AND $255, x3, x1 + LSR $8, x3, x2 + + MUL prime5, x1 + ROR $64-11, h + EOR x1 @> 64-11, h, h + MUL prime1, h + + MUL prime5, x2 + ROR $64-11, h + EOR x2 @> 64-11, h, h + MUL prime1, h + +try1: + TBZ $0, n, finalize + MOVBU (p), x4 + + MUL prime5, x4 + ROR $64-11, h + EOR x4 @> 64-11, h, h + MUL prime1, h + +finalize: + EOR h >> 33, h + MUL prime2, h + EOR h >> 29, h + MUL prime3, h + EOR h >> 32, h + + MOVD h, ret+24(FP) + RET + +// func writeBlocks(d *Digest, b []byte) int +TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 + LDP ·primes+0(SB), (prime1, prime2) + + // Load state. Assume v[1-4] are stored contiguously. + MOVD d+0(FP), digest + LDP 0(digest), (v1, v2) + LDP 16(digest), (v3, v4) + + LDP b_base+8(FP), (p, n) + + blockLoop() + + // Store updated state. + STP (v1, v2), 0(digest) + STP (v3, v4), 16(digest) + + BIC $31, n + MOVD n, ret+32(FP) + RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go b/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go new file mode 100644 index 00000000..9216e0a4 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go @@ -0,0 +1,15 @@ +//go:build (amd64 || arm64) && !appengine && gc && !purego +// +build amd64 arm64 +// +build !appengine +// +build gc +// +build !purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +// +//go:noescape +func Sum64(b []byte) uint64 + +//go:noescape +func writeBlocks(d *Digest, b []byte) int diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_other.go b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go new file mode 100644 index 00000000..26df13bb --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go @@ -0,0 +1,76 @@ +//go:build (!amd64 && !arm64) || appengine || !gc || purego +// +build !amd64,!arm64 appengine !gc purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +func Sum64(b []byte) uint64 { + // A simpler version would be + // d := New() + // d.Write(b) + // return d.Sum64() + // but this is faster, particularly for small inputs. + + n := len(b) + var h uint64 + + if n >= 32 { + v1 := primes[0] + prime2 + v2 := prime2 + v3 := uint64(0) + v4 := -primes[0] + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = prime5 + } + + h += uint64(n) + + for ; len(b) >= 8; b = b[8:] { + k1 := round(0, u64(b[:8])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if len(b) >= 4 { + h ^= uint64(u32(b[:4])) * prime1 + h = rol23(h)*prime2 + prime3 + b = b[4:] + } + for ; len(b) > 0; b = b[1:] { + h ^= uint64(b[0]) * prime5 + h = rol11(h) * prime1 + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +func writeBlocks(d *Digest, b []byte) int { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + n := len(b) + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 + return n - len(b) +} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go new file mode 100644 index 00000000..e86f1b5f --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go @@ -0,0 +1,16 @@ +//go:build appengine +// +build appengine + +// This file contains the safe implementations of otherwise unsafe-using code. + +package xxhash + +// Sum64String computes the 64-bit xxHash digest of s. +func Sum64String(s string) uint64 { + return Sum64([]byte(s)) +} + +// WriteString adds more data to d. It always returns len(s), nil. +func (d *Digest) WriteString(s string) (n int, err error) { + return d.Write([]byte(s)) +} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go new file mode 100644 index 00000000..1c1638fd --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go @@ -0,0 +1,58 @@ +//go:build !appengine +// +build !appengine + +// This file encapsulates usage of unsafe. +// xxhash_safe.go contains the safe implementations. + +package xxhash + +import ( + "unsafe" +) + +// In the future it's possible that compiler optimizations will make these +// XxxString functions unnecessary by realizing that calls such as +// Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205. +// If that happens, even if we keep these functions they can be replaced with +// the trivial safe code. + +// NOTE: The usual way of doing an unsafe string-to-[]byte conversion is: +// +// var b []byte +// bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) +// bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data +// bh.Len = len(s) +// bh.Cap = len(s) +// +// Unfortunately, as of Go 1.15.3 the inliner's cost model assigns a high enough +// weight to this sequence of expressions that any function that uses it will +// not be inlined. Instead, the functions below use a different unsafe +// conversion designed to minimize the inliner weight and allow both to be +// inlined. There is also a test (TestInlining) which verifies that these are +// inlined. +// +// See https://github.com/golang/go/issues/42739 for discussion. + +// Sum64String computes the 64-bit xxHash digest of s. +// It may be faster than Sum64([]byte(s)) by avoiding a copy. +func Sum64String(s string) uint64 { + b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})) + return Sum64(b) +} + +// WriteString adds more data to d. It always returns len(s), nil. +// It may be faster than Write([]byte(s)) by avoiding a copy. +func (d *Digest) WriteString(s string) (n int, err error) { + d.Write(*(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))) + // d.Write always returns len(s), nil. + // Ignoring the return output and returning these fixed values buys a + // savings of 6 in the inliner's cost model. + return len(s), nil +} + +// sliceHeader is similar to reflect.SliceHeader, but it assumes that the layout +// of the first two words is the same as the layout of a string. +type sliceHeader struct { + s string + cap int +} diff --git a/vendor/github.com/dgryski/go-rendezvous/LICENSE b/vendor/github.com/dgryski/go-rendezvous/LICENSE new file mode 100644 index 00000000..22080f73 --- /dev/null +++ b/vendor/github.com/dgryski/go-rendezvous/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2020 Damian Gryski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/dgryski/go-rendezvous/rdv.go b/vendor/github.com/dgryski/go-rendezvous/rdv.go new file mode 100644 index 00000000..7a6f8203 --- /dev/null +++ b/vendor/github.com/dgryski/go-rendezvous/rdv.go @@ -0,0 +1,79 @@ +package rendezvous + +type Rendezvous struct { + nodes map[string]int + nstr []string + nhash []uint64 + hash Hasher +} + +type Hasher func(s string) uint64 + +func New(nodes []string, hash Hasher) *Rendezvous { + r := &Rendezvous{ + nodes: make(map[string]int, len(nodes)), + nstr: make([]string, len(nodes)), + nhash: make([]uint64, len(nodes)), + hash: hash, + } + + for i, n := range nodes { + r.nodes[n] = i + r.nstr[i] = n + r.nhash[i] = hash(n) + } + + return r +} + +func (r *Rendezvous) Lookup(k string) string { + // short-circuit if we're empty + if len(r.nodes) == 0 { + return "" + } + + khash := r.hash(k) + + var midx int + var mhash = xorshiftMult64(khash ^ r.nhash[0]) + + for i, nhash := range r.nhash[1:] { + if h := xorshiftMult64(khash ^ nhash); h > mhash { + midx = i + 1 + mhash = h + } + } + + return r.nstr[midx] +} + +func (r *Rendezvous) Add(node string) { + r.nodes[node] = len(r.nstr) + r.nstr = append(r.nstr, node) + r.nhash = append(r.nhash, r.hash(node)) +} + +func (r *Rendezvous) Remove(node string) { + // find index of node to remove + nidx := r.nodes[node] + + // remove from the slices + l := len(r.nstr) + r.nstr[nidx] = r.nstr[l] + r.nstr = r.nstr[:l] + + r.nhash[nidx] = r.nhash[l] + r.nhash = r.nhash[:l] + + // update the map + delete(r.nodes, node) + moved := r.nstr[nidx] + r.nodes[moved] = nidx +} + +func xorshiftMult64(x uint64) uint64 { + x ^= x >> 12 // a + x ^= x << 25 // b + x ^= x >> 27 // c + return x * 2685821657736338717 +} diff --git a/vendor/github.com/didip/tollbooth/v6/.gitignore b/vendor/github.com/didip/tollbooth/v7/.gitignore similarity index 100% rename from vendor/github.com/didip/tollbooth/v6/.gitignore rename to vendor/github.com/didip/tollbooth/v7/.gitignore diff --git a/vendor/github.com/didip/tollbooth/v6/.golangci.yml b/vendor/github.com/didip/tollbooth/v7/.golangci.yml similarity index 94% rename from vendor/github.com/didip/tollbooth/v6/.golangci.yml rename to vendor/github.com/didip/tollbooth/v7/.golangci.yml index 98b2ab1c..880786af 100644 --- a/vendor/github.com/didip/tollbooth/v6/.golangci.yml +++ b/vendor/github.com/didip/tollbooth/v7/.golangci.yml @@ -1,7 +1,7 @@ linters: enable: - megacheck - - golint + - revive - govet - unconvert - megacheck @@ -18,7 +18,7 @@ linters: - varcheck - stylecheck - gochecknoinits - - scopelint + - exportloopref - gocritic - nakedret - gosimple diff --git a/vendor/github.com/didip/tollbooth/v6/LICENSE b/vendor/github.com/didip/tollbooth/v7/LICENSE similarity index 100% rename from vendor/github.com/didip/tollbooth/v6/LICENSE rename to vendor/github.com/didip/tollbooth/v7/LICENSE diff --git a/vendor/github.com/didip/tollbooth/v6/README.md b/vendor/github.com/didip/tollbooth/v7/README.md similarity index 91% rename from vendor/github.com/didip/tollbooth/v6/README.md rename to vendor/github.com/didip/tollbooth/v7/README.md index 31f37b56..8a45bd89 100644 --- a/vendor/github.com/didip/tollbooth/v6/README.md +++ b/vendor/github.com/didip/tollbooth/v7/README.md @@ -23,6 +23,8 @@ This is a generic middleware to rate-limit HTTP requests. **v6.x.x:** Replaced `go-cache` with `github.com/go-pkgz/expirable-cache` because `go-cache` leaks goroutines. +**v7.x.x:** Replaced `time/rate` with `embedded time/rate` so that we can support more rate limit headers. + ## Five Minute Tutorial ```go @@ -31,7 +33,7 @@ package main import ( "net/http" - "github.com/didip/tollbooth/v6" + "github.com/didip/tollbooth/v7" ) func HelloHandler(w http.ResponseWriter, req *http.Request) { @@ -52,8 +54,8 @@ func main() { import ( "time" - "github.com/didip/tollbooth/v6" - "github.com/didip/tollbooth/v6/limiter" + "github.com/didip/tollbooth/v7" + "github.com/didip/tollbooth/v7/limiter" ) lmt := tollbooth.NewLimiter(1, nil) @@ -122,6 +124,13 @@ func main() { * `X-Rate-Limit-Request-Remote-Addr` The rejected request `RemoteAddr`. + Upon both success and rejection [RateLimit](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers) headers are sent: + + * `RateLimit-Limit` The maximum request limit within the time window (1s). + + * `RateLimit-Reset` The rate-limiter time window duration in seconds (always 1s). + + * `RateLimit-Remaining` The remaining tokens. 5. Customize your own message or function when limit is reached. diff --git a/vendor/github.com/didip/tollbooth/v6/errors/errors.go b/vendor/github.com/didip/tollbooth/v7/errors/errors.go similarity index 100% rename from vendor/github.com/didip/tollbooth/v6/errors/errors.go rename to vendor/github.com/didip/tollbooth/v7/errors/errors.go diff --git a/vendor/golang.org/x/net/AUTHORS b/vendor/github.com/didip/tollbooth/v7/internal/time/AUTHORS similarity index 100% rename from vendor/golang.org/x/net/AUTHORS rename to vendor/github.com/didip/tollbooth/v7/internal/time/AUTHORS diff --git a/vendor/golang.org/x/net/CONTRIBUTORS b/vendor/github.com/didip/tollbooth/v7/internal/time/CONTRIBUTORS similarity index 100% rename from vendor/golang.org/x/net/CONTRIBUTORS rename to vendor/github.com/didip/tollbooth/v7/internal/time/CONTRIBUTORS diff --git a/vendor/golang.org/x/time/LICENSE b/vendor/github.com/didip/tollbooth/v7/internal/time/LICENSE similarity index 100% rename from vendor/golang.org/x/time/LICENSE rename to vendor/github.com/didip/tollbooth/v7/internal/time/LICENSE diff --git a/vendor/golang.org/x/time/PATENTS b/vendor/github.com/didip/tollbooth/v7/internal/time/PATENTS similarity index 100% rename from vendor/golang.org/x/time/PATENTS rename to vendor/github.com/didip/tollbooth/v7/internal/time/PATENTS diff --git a/vendor/golang.org/x/time/rate/rate.go b/vendor/github.com/didip/tollbooth/v7/internal/time/rate/rate.go similarity index 97% rename from vendor/golang.org/x/time/rate/rate.go rename to vendor/github.com/didip/tollbooth/v7/internal/time/rate/rate.go index b0b982e9..6c3b442d 100644 --- a/vendor/golang.org/x/time/rate/rate.go +++ b/vendor/github.com/didip/tollbooth/v7/internal/time/rate/rate.go @@ -94,6 +94,14 @@ func (lim *Limiter) Allow() bool { return lim.AllowN(time.Now(), 1) } +// TokensAt returns the number of tokens available for the given time. +func (lim *Limiter) TokensAt(t time.Time) float64 { + lim.mu.Lock() + _, _, tokens := lim.advance(t) // does not mutate lim + lim.mu.Unlock() + return tokens +} + // AllowN reports whether n events may happen at time now. // Use this method if you intend to drop / skip events that exceed the rate limit. // Otherwise use Reserve or Wait. @@ -306,27 +314,15 @@ func (lim *Limiter) SetBurstAt(now time.Time, newBurst int) { // reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { lim.mu.Lock() - defer lim.mu.Unlock() if lim.limit == Inf { + lim.mu.Unlock() return Reservation{ ok: true, lim: lim, tokens: n, timeToAct: now, } - } else if lim.limit == 0 { - var ok bool - if lim.burst >= n { - ok = true - lim.burst -= n - } - return Reservation{ - ok: ok, - lim: lim, - tokens: lim.burst, - timeToAct: now, - } } now, last, tokens := lim.advance(now) @@ -363,6 +359,7 @@ func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duratio lim.last = last } + lim.mu.Unlock() return r } @@ -388,9 +385,6 @@ func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, // durationFromTokens is a unit conversion function from the number of tokens to the duration // of time it takes to accumulate them at a rate of limit tokens per second. func (limit Limit) durationFromTokens(tokens float64) time.Duration { - if limit <= 0 { - return InfDuration - } seconds := tokens / float64(limit) return time.Duration(float64(time.Second) * seconds) } @@ -398,8 +392,5 @@ func (limit Limit) durationFromTokens(tokens float64) time.Duration { // tokensFromDuration is a unit conversion function from a time duration to the number of tokens // which could be accumulated during that duration at a rate of limit tokens per second. func (limit Limit) tokensFromDuration(d time.Duration) float64 { - if limit <= 0 { - return 0 - } return d.Seconds() * float64(limit) } diff --git a/vendor/github.com/didip/tollbooth/v6/libstring/libstring.go b/vendor/github.com/didip/tollbooth/v7/libstring/libstring.go similarity index 99% rename from vendor/github.com/didip/tollbooth/v6/libstring/libstring.go rename to vendor/github.com/didip/tollbooth/v7/libstring/libstring.go index c9355e20..730b6549 100644 --- a/vendor/github.com/didip/tollbooth/v6/libstring/libstring.go +++ b/vendor/github.com/didip/tollbooth/v7/libstring/libstring.go @@ -69,7 +69,6 @@ func CanonicalizeIP(ip string) string { case ':': // IPv6 isIPv6 = true - break } } if !isIPv6 { diff --git a/vendor/github.com/didip/tollbooth/v6/limiter/limiter.go b/vendor/github.com/didip/tollbooth/v7/limiter/limiter.go similarity index 97% rename from vendor/github.com/didip/tollbooth/v6/limiter/limiter.go rename to vendor/github.com/didip/tollbooth/v7/limiter/limiter.go index 4a7a08a9..c64a7f26 100644 --- a/vendor/github.com/didip/tollbooth/v6/limiter/limiter.go +++ b/vendor/github.com/didip/tollbooth/v7/limiter/limiter.go @@ -7,7 +7,8 @@ import ( "time" cache "github.com/go-pkgz/expirable-cache" - "golang.org/x/time/rate" + + "github.com/didip/tollbooth/v7/internal/time/rate" ) // New is a constructor for Limiter. @@ -597,3 +598,13 @@ func (l *Limiter) LimitReached(key string) bool { return l.limitReachedWithTokenBucketTTL(key, ttl) } + +// Tokens returns current amount of tokens left in the Bucket identified by key. +func (l *Limiter) Tokens(key string) int { + expiringMap, found := l.tokenBuckets.Get(key) + if !found { + return 0 + } + + return int(expiringMap.(*rate.Limiter).TokensAt(time.Now())) +} diff --git a/vendor/github.com/didip/tollbooth/v6/limiter/limiter_options.go b/vendor/github.com/didip/tollbooth/v7/limiter/limiter_options.go similarity index 100% rename from vendor/github.com/didip/tollbooth/v6/limiter/limiter_options.go rename to vendor/github.com/didip/tollbooth/v7/limiter/limiter_options.go diff --git a/vendor/github.com/didip/tollbooth/v6/tollbooth.go b/vendor/github.com/didip/tollbooth/v7/tollbooth.go similarity index 85% rename from vendor/github.com/didip/tollbooth/v6/tollbooth.go rename to vendor/github.com/didip/tollbooth/v7/tollbooth.go index d3fa240d..0dcf82e0 100644 --- a/vendor/github.com/didip/tollbooth/v6/tollbooth.go +++ b/vendor/github.com/didip/tollbooth/v7/tollbooth.go @@ -7,9 +7,9 @@ import ( "net/http" "strings" - "github.com/didip/tollbooth/v6/errors" - "github.com/didip/tollbooth/v6/libstring" - "github.com/didip/tollbooth/v6/limiter" + "github.com/didip/tollbooth/v7/errors" + "github.com/didip/tollbooth/v7/libstring" + "github.com/didip/tollbooth/v7/limiter" ) // setResponseHeaders configures X-Rate-Limit-Limit and X-Rate-Limit-Duration @@ -25,6 +25,14 @@ func setResponseHeaders(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Req w.Header().Add("X-Rate-Limit-Request-Remote-Addr", r.RemoteAddr) } +// setRateLimitResponseHeaders configures RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset +// as seen at https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers +func setRateLimitResponseHeaders(lmt *limiter.Limiter, w http.ResponseWriter, tokensLeft int) { + w.Header().Add("RateLimit-Limit", fmt.Sprintf("%d", int(math.Round(lmt.GetMax())))) + w.Header().Add("RateLimit-Reset", "1") + w.Header().Add("RateLimit-Remaining", fmt.Sprintf("%d", tokensLeft)) +} + // NewLimiter is a convenience function to limiter.New. func NewLimiter(max float64, tbOptions *limiter.ExpirableOptions) *limiter.Limiter { return limiter.New(tbOptions). @@ -36,11 +44,18 @@ func NewLimiter(max float64, tbOptions *limiter.ExpirableOptions) *limiter.Limit // LimitByKeys keeps track number of request made by keys separated by pipe. // It returns HTTPError when limit is exceeded. func LimitByKeys(lmt *limiter.Limiter, keys []string) *errors.HTTPError { + err, _ := LimitByKeysAndReturn(lmt, keys) + return err +} + +// LimitByKeysAndReturn keeps track number of request made by keys separated by pipe. +// It returns HTTPError when limit is exceeded, and also returns the current limit value. +func LimitByKeysAndReturn(lmt *limiter.Limiter, keys []string) (*errors.HTTPError, int) { if lmt.LimitReached(strings.Join(keys, "|")) { - return &errors.HTTPError{Message: lmt.GetMessage(), StatusCode: lmt.GetStatusCode()} + return &errors.HTTPError{Message: lmt.GetMessage(), StatusCode: lmt.GetStatusCode()}, 0 } - return nil + return nil, lmt.Tokens(strings.Join(keys, "|")) } // ShouldSkipLimiter is a series of filter that decides if request should be limited or not. @@ -97,6 +112,10 @@ func ShouldSkipLimiter(lmt *limiter.Limiter, r *http.Request) bool { requestHeadersDefinedInLimiter = false for headerKey, headerValues := range lmtHeaders { + if len(headerValues) == 0 { + requestHeadersDefinedInLimiter = true + continue + } for _, headerValue := range headerValues { if r.Header.Get(headerKey) == headerValue { requestHeadersDefinedInLimiter = true @@ -281,14 +300,24 @@ func LimitByRequest(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Request sliceKeys := BuildKeys(lmt, r) + // Get the lowest value over all keys to return in headers. + // Start with high arbitrary number so that any limit returned would be lower and would + // overwrite the value we start with. + var tokensLeft = math.MaxInt32 + // Loop sliceKeys and check if one of them has error. for _, keys := range sliceKeys { - httpError := LimitByKeys(lmt, keys) + httpError, keysLimit := LimitByKeysAndReturn(lmt, keys) + if tokensLeft > keysLimit { + tokensLeft = keysLimit + } if httpError != nil { + setRateLimitResponseHeaders(lmt, w, tokensLeft) return httpError } } + setRateLimitResponseHeaders(lmt, w, tokensLeft) return nil } diff --git a/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go b/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go index d4333801..7e45c9a6 100644 --- a/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go +++ b/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go @@ -1,9 +1,10 @@ package tollbooth_chi import ( - "github.com/didip/tollbooth/v6" - "github.com/didip/tollbooth/v6/limiter" "net/http" + + "github.com/didip/tollbooth/v7" + "github.com/didip/tollbooth/v7/limiter" ) func LimitHandler(lmt *limiter.Limiter) func(http.Handler) http.Handler { @@ -32,6 +33,7 @@ func (l *limiterWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { default: httpError := tollbooth.LimitByRequest(l.lmt, w, r) if httpError != nil { + l.lmt.ExecOnLimitReached(w, r) w.Header().Add("Content-Type", l.lmt.GetMessageContentType()) w.WriteHeader(httpError.StatusCode) w.Write([]byte(httpError.Message)) diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml index ba95cdd1..ac12e485 100644 --- a/vendor/github.com/dustin/go-humanize/.travis.yml +++ b/vendor/github.com/dustin/go-humanize/.travis.yml @@ -1,12 +1,12 @@ sudo: false language: go +go_import_path: github.com/dustin/go-humanize go: - - 1.3.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x + - 1.13.x + - 1.14.x + - 1.15.x + - 1.16.x + - stable - master matrix: allow_failures: @@ -15,7 +15,7 @@ matrix: install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . + - go vet . + - go install -v -race ./... - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown index 91b4ae56..7d0b16b3 100644 --- a/vendor/github.com/dustin/go-humanize/README.markdown +++ b/vendor/github.com/dustin/go-humanize/README.markdown @@ -5,7 +5,7 @@ Just a few functions for helping humanize times and sizes. `go get` it as `github.com/dustin/go-humanize`, import it as `"github.com/dustin/go-humanize"`, use it as `humanize`. -See [godoc](https://godoc.org/github.com/dustin/go-humanize) for +See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for complete documentation. ## Sizes diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go index 1a2bf617..3b015fd5 100644 --- a/vendor/github.com/dustin/go-humanize/bigbytes.go +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go @@ -28,6 +28,10 @@ var ( BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) // BigYiByte is 1,024 z bytes in bit.Ints BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) + // BigRiByte is 1,024 y bytes in bit.Ints + BigRiByte = (&big.Int{}).Mul(BigYiByte, bigIECExp) + // BigQiByte is 1,024 r bytes in bit.Ints + BigQiByte = (&big.Int{}).Mul(BigRiByte, bigIECExp) ) var ( @@ -51,6 +55,10 @@ var ( BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) // BigYByte is 1,000 SI z bytes in big.Ints BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) + // BigRByte is 1,000 SI y bytes in big.Ints + BigRByte = (&big.Int{}).Mul(BigYByte, bigSIExp) + // BigQByte is 1,000 SI r bytes in big.Ints + BigQByte = (&big.Int{}).Mul(BigRByte, bigSIExp) ) var bigBytesSizeTable = map[string]*big.Int{ @@ -71,6 +79,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zb": BigZByte, "yib": BigYiByte, "yb": BigYByte, + "rib": BigRiByte, + "rb": BigRByte, + "qib": BigQiByte, + "qb": BigQByte, // Without suffix "": BigByte, "ki": BigKiByte, @@ -89,6 +101,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zi": BigZiByte, "y": BigYByte, "yi": BigYiByte, + "r": BigRByte, + "ri": BigRiByte, + "q": BigQByte, + "qi": BigQiByte, } var ten = big.NewInt(10) @@ -115,7 +131,7 @@ func humanateBigBytes(s, base *big.Int, sizes []string) string { // // BigBytes(82854982) -> 83 MB func BigBytes(s *big.Int) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB"} return humanateBigBytes(s, bigSIExp, sizes) } @@ -125,7 +141,7 @@ func BigBytes(s *big.Int) string { // // BigIBytes(82854982) -> 79 MiB func BigIBytes(s *big.Int) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"} return humanateBigBytes(s, bigIECExp, sizes) } diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go index 620690de..2bc83a03 100644 --- a/vendor/github.com/dustin/go-humanize/commaf.go +++ b/vendor/github.com/dustin/go-humanize/commaf.go @@ -1,3 +1,4 @@ +//go:build go1.6 // +build go1.6 package humanize diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go index 1c62b640..bce923f3 100644 --- a/vendor/github.com/dustin/go-humanize/ftoa.go +++ b/vendor/github.com/dustin/go-humanize/ftoa.go @@ -6,6 +6,9 @@ import ( ) func stripTrailingZeros(s string) string { + if !strings.ContainsRune(s, '.') { + return s + } offset := len(s) - 1 for offset > 0 { if s[offset] == '.' { diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go index dec61865..6470d0d4 100644 --- a/vendor/github.com/dustin/go-humanize/number.go +++ b/vendor/github.com/dustin/go-humanize/number.go @@ -73,7 +73,7 @@ func FormatFloat(format string, n float64) string { if n > math.MaxFloat64 { return "Infinity" } - if n < -math.MaxFloat64 { + if n < (0.0 - math.MaxFloat64) { return "-Infinity" } diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go index ae659e0e..8b850198 100644 --- a/vendor/github.com/dustin/go-humanize/si.go +++ b/vendor/github.com/dustin/go-humanize/si.go @@ -8,6 +8,8 @@ import ( ) var siPrefixTable = map[float64]string{ + -30: "q", // quecto + -27: "r", // ronto -24: "y", // yocto -21: "z", // zepto -18: "a", // atto @@ -25,6 +27,8 @@ var siPrefixTable = map[float64]string{ 18: "E", // exa 21: "Z", // zetta 24: "Y", // yotta + 27: "R", // ronna + 30: "Q", // quetta } var revSIPrefixTable = revfmap(siPrefixTable) diff --git a/vendor/github.com/go-chi/chi/v5/CHANGELOG.md b/vendor/github.com/go-chi/chi/v5/CHANGELOG.md index 88c68c6d..a1feeec0 100644 --- a/vendor/github.com/go-chi/chi/v5/CHANGELOG.md +++ b/vendor/github.com/go-chi/chi/v5/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## v5.0.8 (2022-12-07) + +- History of changes: see https://github.com/go-chi/chi/compare/v5.0.7...v5.0.8 + + ## v5.0.7 (2021-11-18) - History of changes: see https://github.com/go-chi/chi/compare/v5.0.6...v5.0.7 diff --git a/vendor/github.com/go-chi/chi/v5/Makefile b/vendor/github.com/go-chi/chi/v5/Makefile index 970a2196..e0f18c7d 100644 --- a/vendor/github.com/go-chi/chi/v5/Makefile +++ b/vendor/github.com/go-chi/chi/v5/Makefile @@ -1,15 +1,19 @@ +.PHONY: all all: @echo "**********************************************************" @echo "** chi build tool **" @echo "**********************************************************" +.PHONY: test test: go clean -testcache && $(MAKE) test-router && $(MAKE) test-middleware +.PHONY: test-router test-router: go test -race -v . +.PHONY: test-middleware test-middleware: go test -race -v ./middleware diff --git a/vendor/github.com/go-chi/chi/v5/README.md b/vendor/github.com/go-chi/chi/v5/README.md index 5d504d18..3e4cc4a2 100644 --- a/vendor/github.com/go-chi/chi/v5/README.md +++ b/vendor/github.com/go-chi/chi/v5/README.md @@ -30,7 +30,7 @@ and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too! * **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http` * **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and sub-router mounting * **Context control** - built on new `context` package, providing value chaining, cancellations and timeouts -* **Robust** - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) +* **Robust** - in production at Pressly, Cloudflare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) * **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown * **Go.mod support** - as of v5, go.mod support (see [CHANGELOG](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)) * **No external dependencies** - plain ol' Go stdlib + net/http diff --git a/vendor/github.com/go-chi/chi/v5/chi.go b/vendor/github.com/go-chi/chi/v5/chi.go index d2e5354d..a1691bbe 100644 --- a/vendor/github.com/go-chi/chi/v5/chi.go +++ b/vendor/github.com/go-chi/chi/v5/chi.go @@ -1,29 +1,29 @@ -// // Package chi is a small, idiomatic and composable router for building HTTP services. // -// chi requires Go 1.10 or newer. +// chi requires Go 1.14 or newer. // // Example: -// package main // -// import ( -// "net/http" +// package main +// +// import ( +// "net/http" // -// "github.com/go-chi/chi/v5" -// "github.com/go-chi/chi/v5/middleware" -// ) +// "github.com/go-chi/chi/v5" +// "github.com/go-chi/chi/v5/middleware" +// ) // -// func main() { -// r := chi.NewRouter() -// r.Use(middleware.Logger) -// r.Use(middleware.Recoverer) +// func main() { +// r := chi.NewRouter() +// r.Use(middleware.Logger) +// r.Use(middleware.Recoverer) // -// r.Get("/", func(w http.ResponseWriter, r *http.Request) { -// w.Write([]byte("root.")) -// }) +// r.Get("/", func(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("root.")) +// }) // -// http.ListenAndServe(":3333", r) -// } +// http.ListenAndServe(":3333", r) +// } // // See github.com/go-chi/chi/_examples/ for more in-depth examples. // @@ -47,12 +47,12 @@ // placeholder which will match / characters. // // Examples: -// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" -// "/user/{name}/info" matches "/user/jsmith/info" -// "/page/*" matches "/page/intro/latest" -// "/page/*/index" also matches "/page/intro/latest" -// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" // +// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" +// "/user/{name}/info" matches "/user/jsmith/info" +// "/page/*" matches "/page/intro/latest" +// "/page/{other}/index" also matches "/page/intro/latest" +// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" package chi import "net/http" diff --git a/vendor/github.com/go-chi/chi/v5/context.go b/vendor/github.com/go-chi/chi/v5/context.go index 814c2630..e78a2385 100644 --- a/vendor/github.com/go-chi/chi/v5/context.go +++ b/vendor/github.com/go-chi/chi/v5/context.go @@ -121,7 +121,10 @@ func (x *Context) URLParam(key string) string { // } func (x *Context) RoutePattern() string { routePattern := strings.Join(x.RoutePatterns, "") - return replaceWildcards(routePattern) + routePattern = replaceWildcards(routePattern) + routePattern = strings.TrimSuffix(routePattern, "//") + routePattern = strings.TrimSuffix(routePattern, "/") + return routePattern } // replaceWildcards takes a route pattern and recursively replaces all @@ -130,7 +133,6 @@ func replaceWildcards(p string) string { if strings.Contains(p, "/*/") { return replaceWildcards(strings.Replace(p, "/*/", "/", -1)) } - return p } diff --git a/vendor/github.com/go-chi/chi/v5/middleware/compress.go b/vendor/github.com/go-chi/chi/v5/middleware/compress.go index 6cafddc7..52f4e0b5 100644 --- a/vendor/github.com/go-chi/chi/v5/middleware/compress.go +++ b/vendor/github.com/go-chi/chi/v5/middleware/compress.go @@ -318,7 +318,7 @@ func (cw *compressResponseWriter) WriteHeader(code int) { if cw.encoding != "" { cw.compressable = true cw.Header().Set("Content-Encoding", cw.encoding) - cw.Header().Set("Vary", "Accept-Encoding") + cw.Header().Add("Vary", "Accept-Encoding") // The content-length after compression is unknown cw.Header().Del("Content-Length") diff --git a/vendor/github.com/go-chi/chi/v5/middleware/logger.go b/vendor/github.com/go-chi/chi/v5/middleware/logger.go index 66edc3dd..98250d82 100644 --- a/vendor/github.com/go-chi/chi/v5/middleware/logger.go +++ b/vendor/github.com/go-chi/chi/v5/middleware/logger.go @@ -30,14 +30,11 @@ var ( // http logger with structured logging support. // // IMPORTANT NOTE: Logger should go before any other middleware that may change -// the response, such as `middleware.Recoverer`. Example: -// -// ```go -// r := chi.NewRouter() -// r.Use(middleware.Logger) // <--<< Logger should come before Recoverer -// r.Use(middleware.Recoverer) -// r.Get("/", handler) -// ``` +// the response, such as middleware.Recoverer. Example: +// r := chi.NewRouter() +// r.Use(middleware.Logger) // <--<< Logger should come before Recoverer +// r.Use(middleware.Recoverer) +// r.Get("/", handler) func Logger(next http.Handler) http.Handler { return DefaultLogger(next) } diff --git a/vendor/github.com/go-chi/chi/v5/middleware/realip.go b/vendor/github.com/go-chi/chi/v5/middleware/realip.go index efc1894f..2c6b3b33 100644 --- a/vendor/github.com/go-chi/chi/v5/middleware/realip.go +++ b/vendor/github.com/go-chi/chi/v5/middleware/realip.go @@ -4,6 +4,7 @@ package middleware // https://github.com/zenazn/goji/tree/master/web/middleware import ( + "net" "net/http" "strings" ) @@ -46,12 +47,14 @@ func realIP(r *http.Request) string { } else if xrip := r.Header.Get(xRealIP); xrip != "" { ip = xrip } else if xff := r.Header.Get(xForwardedFor); xff != "" { - i := strings.Index(xff, ", ") + i := strings.Index(xff, ",") if i == -1 { i = len(xff) } ip = xff[:i] } - + if ip == "" || net.ParseIP(ip) == nil { + return "" + } return ip } diff --git a/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go b/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go index c9eafff4..8fa69bad 100644 --- a/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go +++ b/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go @@ -18,11 +18,16 @@ import ( // backtrace), and returns a HTTP 500 (Internal Server Error) status if // possible. Recoverer prints a request ID if one is provided. // -// Alternatively, look at https://github.com/pressly/lg middleware pkgs. +// Alternatively, look at https://github.com/go-chi/httplog middleware pkgs. func Recoverer(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { defer func() { - if rvr := recover(); rvr != nil && rvr != http.ErrAbortHandler { + if rvr := recover(); rvr != nil { + if rvr == http.ErrAbortHandler { + // we don't recover http.ErrAbortHandler so the response + // to the client is aborted, this should not be logged + panic(rvr) + } logEntry := GetLogEntry(r) if logEntry != nil { diff --git a/vendor/github.com/go-chi/chi/v5/middleware/url_format.go b/vendor/github.com/go-chi/chi/v5/middleware/url_format.go index 10d7134d..919eb0fe 100644 --- a/vendor/github.com/go-chi/chi/v5/middleware/url_format.go +++ b/vendor/github.com/go-chi/chi/v5/middleware/url_format.go @@ -51,6 +51,11 @@ func URLFormat(next http.Handler) http.Handler { var format string path := r.URL.Path + rctx := chi.RouteContext(r.Context()) + if rctx != nil && rctx.RoutePath != "" { + path = rctx.RoutePath + } + if strings.Index(path, ".") > 0 { base := strings.LastIndex(path, "/") idx := strings.LastIndex(path[base:], ".") @@ -59,7 +64,6 @@ func URLFormat(next http.Handler) http.Handler { idx += base format = path[idx+1:] - rctx := chi.RouteContext(r.Context()) rctx.RoutePath = path[:idx] } } diff --git a/vendor/github.com/go-chi/chi/v5/mux.go b/vendor/github.com/go-chi/chi/v5/mux.go index fff6a3c5..47e64cf2 100644 --- a/vendor/github.com/go-chi/chi/v5/mux.go +++ b/vendor/github.com/go-chi/chi/v5/mux.go @@ -29,8 +29,8 @@ type Mux struct { // Custom method not allowed handler methodNotAllowedHandler http.HandlerFunc - // Controls the behaviour of middleware chain generation when a mux - // is registered as an inline group inside another mux. + // A reference to the parent mux used by subrouters when mounting + // to a parent mux parent *Mux // Routing context pool @@ -42,6 +42,8 @@ type Mux struct { // The middleware stack middlewares []func(http.Handler) http.Handler + // Controls the behaviour of middleware chain generation when a mux + // is registered as an inline group inside another mux. inline bool } diff --git a/vendor/github.com/go-chi/render/.gitignore b/vendor/github.com/go-chi/render/.gitignore new file mode 100644 index 00000000..22d0d82f --- /dev/null +++ b/vendor/github.com/go-chi/render/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/vendor/github.com/go-chi/render/.travis.yml b/vendor/github.com/go-chi/render/.travis.yml deleted file mode 100644 index a0262045..00000000 --- a/vendor/github.com/go-chi/render/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go - -go: - - 1.7.x - - 1.8.x - - tip - -install: - - go get -u golang.org/x/tools/cmd/goimports - -script: - - go get -d -t ./... - - go test ./... - - > - goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || : diff --git a/vendor/github.com/go-chi/render/README.md b/vendor/github.com/go-chi/render/README.md index 068e0bff..4d371538 100644 --- a/vendor/github.com/go-chi/render/README.md +++ b/vendor/github.com/go-chi/render/README.md @@ -1,5 +1,9 @@ # render +![tests](https://github.com/go-chi/render/actions/workflows/test.yml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-chi/render)](https://goreportcard.com/report/github.com/go-chi/render) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-chi/render.svg)](https://pkg.go.dev/github.com/go-chi/render) + The `render` package helps manage HTTP request / response payloads. Every well-designed, robust and maintainable Web Service / REST API also needs @@ -21,4 +25,3 @@ request bodies. Please have a look at the [rest](https://github.com/go-chi/chi/b example which uses the latest chi/render sub-pkg. All feedback is welcome, thank you! - diff --git a/vendor/github.com/go-chi/render/decoder.go b/vendor/github.com/go-chi/render/decoder.go index 849c56b9..883b2f84 100644 --- a/vendor/github.com/go-chi/render/decoder.go +++ b/vendor/github.com/go-chi/render/decoder.go @@ -7,6 +7,8 @@ import ( "io" "io/ioutil" "net/http" + + "github.com/ajg/form" ) // Decode is a package-level variable set to our default Decoder. We do this @@ -17,6 +19,8 @@ import ( // bytes allowed to be read from the request body. var Decode = DefaultDecoder +// DefaultDecoder detects the correct decoder for use on an HTTP request and +// marshals into a given interface. func DefaultDecoder(r *http.Request, v interface{}) error { var err error @@ -25,7 +29,8 @@ func DefaultDecoder(r *http.Request, v interface{}) error { err = DecodeJSON(r.Body, v) case ContentTypeXML: err = DecodeXML(r.Body, v) - // case ContentTypeForm: // TODO + case ContentTypeForm: + err = DecodeForm(r.Body, v) default: err = errors.New("render: unable to automatically decode the request content type") } @@ -33,12 +38,20 @@ func DefaultDecoder(r *http.Request, v interface{}) error { return err } +// DecodeJSON decodes a given reader into an interface using the json decoder. func DecodeJSON(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) + defer io.Copy(ioutil.Discard, r) //nolint:errcheck return json.NewDecoder(r).Decode(v) } +// DecodeXML decodes a given reader into an interface using the xml decoder. func DecodeXML(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) + defer io.Copy(ioutil.Discard, r) //nolint:errcheck return xml.NewDecoder(r).Decode(v) } + +// DecodeForm decodes a given reader into an interface using the form decoder. +func DecodeForm(r io.Reader, v interface{}) error { + decoder := form.NewDecoder(r) //nolint:errcheck + return decoder.Decode(v) +} diff --git a/vendor/github.com/go-chi/render/responder.go b/vendor/github.com/go-chi/render/responder.go index 9365350f..66d6bbff 100644 --- a/vendor/github.com/go-chi/render/responder.go +++ b/vendor/github.com/go-chi/render/responder.go @@ -66,7 +66,7 @@ func PlainText(w http.ResponseWriter, r *http.Request, v string) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write([]byte(v)) + w.Write([]byte(v)) //nolint:errcheck } // Data writes raw bytes to the response, setting the Content-Type as @@ -76,7 +76,7 @@ func Data(w http.ResponseWriter, r *http.Request, v []byte) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write(v) + w.Write(v) //nolint:errcheck } // HTML writes a string to the response, setting the Content-Type as text/html. @@ -85,7 +85,7 @@ func HTML(w http.ResponseWriter, r *http.Request, v string) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write([]byte(v)) + w.Write([]byte(v)) //nolint:errcheck } // JSON marshals 'v' to JSON, automatically escaping HTML and setting the @@ -103,7 +103,7 @@ func JSON(w http.ResponseWriter, r *http.Request, v interface{}) { if status, ok := r.Context().Value(StatusCtxKey).(int); ok { w.WriteHeader(status) } - w.Write(buf.Bytes()) + w.Write(buf.Bytes()) //nolint:errcheck } // XML marshals 'v' to JSON, setting the Content-Type as application/xml. It @@ -128,15 +128,15 @@ func XML(w http.ResponseWriter, r *http.Request, v interface{}) { } if !bytes.Contains(b[:findHeaderUntil], []byte(" /tmp/cache-mock.tmp && mv /tmp/cache-mock.tmp cache_mock.go" - import ( "fmt" ) diff --git a/vendor/github.com/go-pkgz/lcw/eventbus/redis.go b/vendor/github.com/go-pkgz/lcw/eventbus/redis.go index 851a4e9e..9e961544 100644 --- a/vendor/github.com/go-pkgz/lcw/eventbus/redis.go +++ b/vendor/github.com/go-pkgz/lcw/eventbus/redis.go @@ -1,23 +1,24 @@ package eventbus import ( + "context" + "fmt" "strings" "time" - "github.com/go-redis/redis/v7" + "github.com/go-redis/redis/v8" "github.com/hashicorp/go-multierror" - "github.com/pkg/errors" ) // NewRedisPubSub creates new RedisPubSub with given parameters. // Returns an error in case of problems with creating PubSub client for specified channel. func NewRedisPubSub(addr, channel string) (*RedisPubSub, error) { client := redis.NewClient(&redis.Options{Addr: addr}) - pubSub := client.Subscribe(channel) + pubSub := client.Subscribe(context.Background(), channel) // wait for subscription to be created and ignore the message - if _, err := pubSub.Receive(); err != nil { + if _, err := pubSub.Receive(context.Background()); err != nil { _ = client.Close() - return nil, errors.Wrapf(err, "problem subscribing to channel %s on address %s", channel, addr) + return nil, fmt.Errorf("problem subscribing to channel %s on address %s: %w", channel, addr, err) } return &RedisPubSub{client: client, pubSub: pubSub, channel: channel, done: make(chan struct{})}, nil } @@ -41,7 +42,7 @@ func (m *RedisPubSub) Subscribe(fn func(fromID, key string)) error { return default: } - msg, err := pubsub.ReceiveTimeout(time.Second * 10) + msg, err := pubsub.ReceiveTimeout(context.Background(), time.Second*10) if err != nil { continue } @@ -59,14 +60,18 @@ func (m *RedisPubSub) Subscribe(fn func(fromID, key string)) error { // Publish publishes provided message to channel provided on new RedisPubSub instance creation func (m *RedisPubSub) Publish(fromID, key string) error { - return m.client.Publish(m.channel, fromID+"$"+key).Err() + return m.client.Publish(context.Background(), m.channel, fromID+"$"+key).Err() } // Close cleans up running goroutines and closes Redis clients func (m *RedisPubSub) Close() error { close(m.done) errs := new(multierror.Error) - errs = multierror.Append(errs, errors.Wrap(m.pubSub.Close(), "problem closing pubSub client")) - errs = multierror.Append(errs, errors.Wrap(m.client.Close(), "problem closing redis client")) + if err := m.pubSub.Close(); err != nil { + errs = multierror.Append(errs, fmt.Errorf("problem closing pubSub client: %w", err)) + } + if err := m.client.Close(); err != nil { + errs = multierror.Append(errs, fmt.Errorf("problem closing redis client: %w", err)) + } return errs.ErrorOrNil() } diff --git a/vendor/github.com/go-pkgz/lcw/expirable_cache.go b/vendor/github.com/go-pkgz/lcw/expirable_cache.go index 5e114b3d..513e0c0f 100644 --- a/vendor/github.com/go-pkgz/lcw/expirable_cache.go +++ b/vendor/github.com/go-pkgz/lcw/expirable_cache.go @@ -1,14 +1,13 @@ package lcw import ( + "fmt" "sync/atomic" "time" - "github.com/google/uuid" - "github.com/pkg/errors" - "github.com/go-pkgz/lcw/eventbus" "github.com/go-pkgz/lcw/internal/cache" + "github.com/google/uuid" ) // ExpirableCache implements LoadingCache with TTL. @@ -34,12 +33,12 @@ func NewExpirableCache(opts ...Option) (*ExpirableCache, error) { for _, opt := range opts { if err := opt(&res.options); err != nil { - return nil, errors.Wrap(err, "failed to set cache option") + return nil, fmt.Errorf("failed to set cache option: %w", err) } } if err := res.eventBus.Subscribe(res.onBusEvent); err != nil { - return nil, errors.Wrapf(err, "can't subscribe to event bus") + return nil, fmt.Errorf("can't subscribe to event bus: %w", err) } backend, err := cache.NewLoadingCache( @@ -61,7 +60,7 @@ func NewExpirableCache(opts ...Option) (*ExpirableCache, error) { }), ) if err != nil { - return nil, errors.Wrap(err, "error creating backend") + return nil, fmt.Errorf("error creating backend: %w", err) } res.backend = backend diff --git a/vendor/github.com/go-pkgz/lcw/internal/cache/cache.go b/vendor/github.com/go-pkgz/lcw/internal/cache/cache.go index 89518076..b1d386e3 100644 --- a/vendor/github.com/go-pkgz/lcw/internal/cache/cache.go +++ b/vendor/github.com/go-pkgz/lcw/internal/cache/cache.go @@ -4,11 +4,10 @@ package cache import ( + "fmt" "sort" "sync" "time" - - "github.com/pkg/errors" ) // LoadingCache provides expirable loading cache with LRC eviction. @@ -19,7 +18,7 @@ type LoadingCache struct { done chan struct{} onEvicted func(key string, value interface{}) - sync.Mutex + mu sync.Mutex data map[string]*cacheItem } @@ -39,7 +38,7 @@ func NewLoadingCache(options ...Option) (*LoadingCache, error) { for _, opt := range options { if err := opt(&res); err != nil { - return nil, errors.Wrap(err, "failed to set cache option") + return nil, fmt.Errorf("failed to set cache option: %w", err) } } @@ -54,9 +53,9 @@ func NewLoadingCache(options ...Option) (*LoadingCache, error) { case <-done: return case <-ticker.C: - res.Lock() + res.mu.Lock() res.purge(res.maxKeys) - res.Unlock() + res.mu.Unlock() } } }(res.done) @@ -66,8 +65,8 @@ func NewLoadingCache(options ...Option) (*LoadingCache, error) { // Set key func (c *LoadingCache) Set(key string, value interface{}) { - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() now := time.Now() if _, ok := c.data[key]; !ok { @@ -86,8 +85,8 @@ func (c *LoadingCache) Set(key string, value interface{}) { // Get returns the key value func (c *LoadingCache) Get(key string) (interface{}, bool) { - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() value, ok := c.getValue(key) if !ok { return nil, false @@ -97,8 +96,8 @@ func (c *LoadingCache) Get(key string) (interface{}, bool) { // Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key. func (c *LoadingCache) Peek(key string) (interface{}, bool) { - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() value, ok := c.getValue(key) if !ok { return nil, false @@ -108,19 +107,19 @@ func (c *LoadingCache) Peek(key string) (interface{}, bool) { // Invalidate key (item) from the cache func (c *LoadingCache) Invalidate(key string) { - c.Lock() + c.mu.Lock() if value, ok := c.data[key]; ok { delete(c.data, key) if c.onEvicted != nil { c.onEvicted(key, value.data) } } - c.Unlock() + c.mu.Unlock() } // InvalidateFn deletes multiple keys if predicate is true func (c *LoadingCache) InvalidateFn(fn func(key string) bool) { - c.Lock() + c.mu.Lock() for key, value := range c.data { if fn(key) { delete(c.data, key) @@ -129,13 +128,13 @@ func (c *LoadingCache) InvalidateFn(fn func(key string) bool) { } } } - c.Unlock() + c.mu.Unlock() } // Keys return slice of current keys in the cache func (c *LoadingCache) Keys() []string { - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() keys := make([]string, 0, len(c.data)) for k := range c.data { keys = append(keys, k) @@ -157,8 +156,8 @@ func (c *LoadingCache) getValue(key string) (interface{}, bool) { // Purge clears the cache completely. func (c *LoadingCache) Purge() { - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() for k, v := range c.data { delete(c.data, k) if c.onEvicted != nil { @@ -169,23 +168,29 @@ func (c *LoadingCache) Purge() { // DeleteExpired clears cache of expired items func (c *LoadingCache) DeleteExpired() { - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() c.purge(0) } // ItemCount return count of items in cache func (c *LoadingCache) ItemCount() int { - c.Lock() + c.mu.Lock() n := len(c.data) - c.Unlock() + c.mu.Unlock() return n } // Close cleans the cache and destroys running goroutines func (c *LoadingCache) Close() { - c.Lock() - defer c.Unlock() + c.mu.Lock() + defer c.mu.Unlock() + // don't panic in case service is already closed + select { + case <-c.done: + return + default: + } close(c.done) } diff --git a/vendor/github.com/go-pkgz/lcw/lru_cache.go b/vendor/github.com/go-pkgz/lcw/lru_cache.go index ba3c3b3d..72b8e57f 100644 --- a/vendor/github.com/go-pkgz/lcw/lru_cache.go +++ b/vendor/github.com/go-pkgz/lcw/lru_cache.go @@ -1,13 +1,12 @@ package lcw import ( + "fmt" "sync/atomic" + "github.com/go-pkgz/lcw/eventbus" "github.com/google/uuid" lru "github.com/hashicorp/golang-lru" - "github.com/pkg/errors" - - "github.com/go-pkgz/lcw/eventbus" ) // LruCache wraps lru.LruCache with loading cache Get and size limits @@ -31,7 +30,7 @@ func NewLruCache(opts ...Option) (*LruCache, error) { } for _, opt := range opts { if err := opt(&res.options); err != nil { - return nil, errors.Wrap(err, "failed to set cache option") + return nil, fmt.Errorf("failed to set cache option: %w", err) } } @@ -41,7 +40,7 @@ func NewLruCache(opts ...Option) (*LruCache, error) { func (c *LruCache) init() error { if err := c.eventBus.Subscribe(c.onBusEvent); err != nil { - return errors.Wrapf(err, "can't subscribe to event bus") + return fmt.Errorf("can't subscribe to event bus: %w", err) } onEvicted := func(key interface{}, value interface{}) { @@ -58,7 +57,7 @@ func (c *LruCache) init() error { var err error // OnEvicted called automatically for expired and manually deleted if c.backend, err = lru.NewWithEvict(c.maxKeys, onEvicted); err != nil { - return errors.Wrap(err, "failed to make lru cache backend") + return fmt.Errorf("failed to make lru cache backend: %w", err) } return nil diff --git a/vendor/github.com/go-pkgz/lcw/options.go b/vendor/github.com/go-pkgz/lcw/options.go index b46dad20..01de5667 100644 --- a/vendor/github.com/go-pkgz/lcw/options.go +++ b/vendor/github.com/go-pkgz/lcw/options.go @@ -1,7 +1,7 @@ package lcw import ( - "errors" + "fmt" "time" "github.com/go-pkgz/lcw/eventbus" @@ -25,7 +25,7 @@ type Option func(o *options) error func MaxValSize(max int) Option { return func(o *options) error { if max < 0 { - return errors.New("negative max value size") + return fmt.Errorf("negative max value size") } o.maxValueSize = max return nil @@ -37,7 +37,7 @@ func MaxValSize(max int) Option { func MaxKeySize(max int) Option { return func(o *options) error { if max < 0 { - return errors.New("negative max key size") + return fmt.Errorf("negative max key size") } o.maxKeySize = max return nil @@ -49,7 +49,7 @@ func MaxKeySize(max int) Option { func MaxKeys(max int) Option { return func(o *options) error { if max < 0 { - return errors.New("negative max keys") + return fmt.Errorf("negative max keys") } o.maxKeys = max return nil @@ -61,7 +61,7 @@ func MaxKeys(max int) Option { func MaxCacheSize(max int64) Option { return func(o *options) error { if max < 0 { - return errors.New("negative max cache size") + return fmt.Errorf("negative max cache size") } o.maxCacheSize = max return nil @@ -73,7 +73,7 @@ func MaxCacheSize(max int64) Option { func TTL(ttl time.Duration) Option { return func(o *options) error { if ttl < 0 { - return errors.New("negative ttl") + return fmt.Errorf("negative ttl") } o.ttl = ttl return nil diff --git a/vendor/github.com/go-pkgz/lcw/redis_cache.go b/vendor/github.com/go-pkgz/lcw/redis_cache.go index b7353a33..5c000e63 100644 --- a/vendor/github.com/go-pkgz/lcw/redis_cache.go +++ b/vendor/github.com/go-pkgz/lcw/redis_cache.go @@ -1,11 +1,12 @@ package lcw import ( + "context" + "fmt" "sync/atomic" "time" - "github.com/go-redis/redis/v7" - "github.com/pkg/errors" + "github.com/go-redis/redis/v8" ) // RedisValueSizeLimit is maximum allowed value size in Redis @@ -27,7 +28,7 @@ func NewRedisCache(backend *redis.Client, opts ...Option) (*RedisCache, error) { } for _, opt := range opts { if err := opt(&res.options); err != nil { - return nil, errors.Wrap(err, "failed to set cache option") + return nil, fmt.Errorf("failed to set cache option: %w", err) } } @@ -42,7 +43,7 @@ func NewRedisCache(backend *redis.Client, opts ...Option) (*RedisCache, error) { // Get gets value by key or load with fn if not found in cache func (c *RedisCache) Get(key string, fn func() (interface{}, error)) (data interface{}, err error) { - v, getErr := c.backend.Get(key).Result() + v, getErr := c.backend.Get(context.Background(), key).Result() switch getErr { // RedisClient returns nil when find a key in DB case nil: @@ -65,7 +66,7 @@ func (c *RedisCache) Get(key string, fn func() (interface{}, error)) (data inter return data, nil } - _, setErr := c.backend.Set(key, data, c.ttl).Result() + _, setErr := c.backend.Set(context.Background(), key, data, c.ttl).Result() if setErr != nil { atomic.AddInt64(&c.Errors, 1) return data, setErr @@ -76,16 +77,16 @@ func (c *RedisCache) Get(key string, fn func() (interface{}, error)) (data inter // Invalidate removes keys with passed predicate fn, i.e. fn(key) should be true to get evicted func (c *RedisCache) Invalidate(fn func(key string) bool) { - for _, key := range c.backend.Keys("*").Val() { // Keys() returns copy of cache's key, safe to remove directly + for _, key := range c.backend.Keys(context.Background(), "*").Val() { // Keys() returns copy of cache's key, safe to remove directly if fn(key) { - c.backend.Del(key) + c.backend.Del(context.Background(), key) } } } // Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key. func (c *RedisCache) Peek(key string) (interface{}, bool) { - ret, err := c.backend.Get(key).Result() + ret, err := c.backend.Get(context.Background(), key).Result() if err != nil { return nil, false } @@ -94,18 +95,18 @@ func (c *RedisCache) Peek(key string) (interface{}, bool) { // Purge clears the cache completely. func (c *RedisCache) Purge() { - c.backend.FlushDB() + c.backend.FlushDB(context.Background()) } // Delete cache item by key func (c *RedisCache) Delete(key string) { - c.backend.Del(key) + c.backend.Del(context.Background(), key) } // Keys gets all keys for the cache func (c *RedisCache) Keys() (res []string) { - return c.backend.Keys("*").Val() + return c.backend.Keys(context.Background(), "*").Val() } // Stat returns cache statistics @@ -129,11 +130,11 @@ func (c *RedisCache) size() int64 { } func (c *RedisCache) keys() int { - return int(c.backend.DBSize().Val()) + return int(c.backend.DBSize(context.Background()).Val()) } func (c *RedisCache) allowed(key string, data interface{}) bool { - if c.maxKeys > 0 && c.backend.DBSize().Val() >= int64(c.maxKeys) { + if c.maxKeys > 0 && c.backend.DBSize(context.Background()).Val() >= int64(c.maxKeys) { return false } if c.maxKeySize > 0 && len(key) > c.maxKeySize { diff --git a/vendor/github.com/go-pkgz/lcw/scache.go b/vendor/github.com/go-pkgz/lcw/scache.go index fe9023d4..2181b75c 100644 --- a/vendor/github.com/go-pkgz/lcw/scache.go +++ b/vendor/github.com/go-pkgz/lcw/scache.go @@ -1,9 +1,8 @@ package lcw import ( + "fmt" "strings" - - "github.com/pkg/errors" ) // Scache wraps LoadingCache with partitions (sub-system), and scopes. @@ -111,7 +110,7 @@ func (k Key) String() string { func parseKey(keyStr string) (Key, error) { elems := strings.Split(keyStr, "@@") if len(elems) != 3 { - return Key{}, errors.Errorf("can't parse cache key %s, invalid number of segments %d", keyStr, len(elems)) + return Key{}, fmt.Errorf("can't parse cache key %s, invalid number of segments %d", keyStr, len(elems)) } scopes := strings.Split(elems[2], "$$") diff --git a/vendor/github.com/go-pkgz/lcw/url.go b/vendor/github.com/go-pkgz/lcw/url.go index 2d0a01ee..67bfbb74 100644 --- a/vendor/github.com/go-pkgz/lcw/url.go +++ b/vendor/github.com/go-pkgz/lcw/url.go @@ -1,13 +1,13 @@ package lcw import ( + "fmt" "net/url" "strconv" "time" - "github.com/go-redis/redis/v7" + "github.com/go-redis/redis/v8" "github.com/hashicorp/go-multierror" - "github.com/pkg/errors" ) // New parses uri and makes any of supported caches @@ -19,23 +19,26 @@ import ( func New(uri string) (LoadingCache, error) { u, err := url.Parse(uri) if err != nil { - return nil, errors.Wrapf(err, "parse cache uri %s", uri) + return nil, fmt.Errorf("parse cache uri %s: %w", uri, err) } query := u.Query() opts, err := optionsFromQuery(query) if err != nil { - return nil, errors.Wrapf(err, "parse uri options %s", uri) + return nil, fmt.Errorf("parse uri options %s: %w", uri, err) } switch u.Scheme { case "redis": - redisOpts, err := redisOptionsFromURL(u) - if err != nil { - return nil, err + redisOpts, e := redisOptionsFromURL(u) + if e != nil { + return nil, e + } + res, e := NewRedisCache(redis.NewClient(redisOpts), opts...) + if e != nil { + return nil, fmt.Errorf("make redis for %s: %w", uri, e) } - res, err := NewRedisCache(redis.NewClient(redisOpts), opts...) - return res, errors.Wrapf(err, "make redis for %s", uri) + return res, nil case "mem": switch u.Hostname() { case "lru": @@ -43,12 +46,12 @@ func New(uri string) (LoadingCache, error) { case "expirable": return NewExpirableCache(opts...) default: - return nil, errors.Errorf("unsupported mem cache type %s", u.Hostname()) + return nil, fmt.Errorf("unsupported mem cache type %s", u.Hostname()) } case "nop": return NewNopCache(), nil } - return nil, errors.Errorf("unsupported cache type %s", u.Scheme) + return nil, fmt.Errorf("unsupported cache type %s", u.Scheme) } func optionsFromQuery(q url.Values) (opts []Option, err error) { @@ -57,7 +60,7 @@ func optionsFromQuery(q url.Values) (opts []Option, err error) { if v := q.Get("max_val_size"); v != "" { vv, e := strconv.Atoi(v) if e != nil { - errs = multierror.Append(errs, errors.Wrapf(e, "max_val_size query param %s", v)) + errs = multierror.Append(errs, fmt.Errorf("max_val_size query param %s: %w", v, e)) } else { opts = append(opts, MaxValSize(vv)) } @@ -66,7 +69,7 @@ func optionsFromQuery(q url.Values) (opts []Option, err error) { if v := q.Get("max_key_size"); v != "" { vv, e := strconv.Atoi(v) if e != nil { - errs = multierror.Append(errs, errors.Wrapf(e, "max_key_size query param %s", v)) + errs = multierror.Append(errs, fmt.Errorf("max_key_size query param %s: %w", v, e)) } else { opts = append(opts, MaxKeySize(vv)) } @@ -75,7 +78,7 @@ func optionsFromQuery(q url.Values) (opts []Option, err error) { if v := q.Get("max_keys"); v != "" { vv, e := strconv.Atoi(v) if e != nil { - errs = multierror.Append(errs, errors.Wrapf(e, "max_keys query param %s", v)) + errs = multierror.Append(errs, fmt.Errorf("max_keys query param %s: %w", v, e)) } else { opts = append(opts, MaxKeys(vv)) } @@ -84,7 +87,7 @@ func optionsFromQuery(q url.Values) (opts []Option, err error) { if v := q.Get("max_cache_size"); v != "" { vv, e := strconv.ParseInt(v, 10, 64) if e != nil { - errs = multierror.Append(errs, errors.Wrapf(e, "max_cache_size query param %s", v)) + errs = multierror.Append(errs, fmt.Errorf("max_cache_size query param %s: %w", v, e)) } else { opts = append(opts, MaxCacheSize(vv)) } @@ -93,7 +96,7 @@ func optionsFromQuery(q url.Values) (opts []Option, err error) { if v := q.Get("ttl"); v != "" { vv, e := time.ParseDuration(v) if e != nil { - errs = multierror.Append(errs, errors.Wrapf(e, "ttl query param %s", v)) + errs = multierror.Append(errs, fmt.Errorf("ttl query param %s: %w", v, e)) } else { opts = append(opts, TTL(vv)) } @@ -107,7 +110,7 @@ func redisOptionsFromURL(u *url.URL) (*redis.Options, error) { db, err := strconv.Atoi(query.Get("db")) if err != nil { - return nil, errors.Wrapf(err, "db from %s", u) + return nil, fmt.Errorf("db from %s: %w", u, err) } res := &redis.Options{ diff --git a/vendor/github.com/go-pkgz/rest/.golangci.yml b/vendor/github.com/go-pkgz/rest/.golangci.yml index 824667f8..77a0c5d5 100644 --- a/vendor/github.com/go-pkgz/rest/.golangci.yml +++ b/vendor/github.com/go-pkgz/rest/.golangci.yml @@ -31,17 +31,14 @@ linters: - govet - unconvert - megacheck - - structcheck - gas - gocyclo - dupl - misspell - unparam - - varcheck - - deadcode + - unused - typecheck - ineffassign - - varcheck - stylecheck - gochecknoinits - exportloopref diff --git a/vendor/github.com/go-pkgz/rest/README.md b/vendor/github.com/go-pkgz/rest/README.md index 82ad44e7..b85c999b 100644 --- a/vendor/github.com/go-pkgz/rest/README.md +++ b/vendor/github.com/go-pkgz/rest/README.md @@ -36,6 +36,45 @@ Org: Umputun pong ``` +### Health middleware + +Responds with the status 200 if all health checks passed, 503 if any failed. Both health path and check functions passed by consumer. +For production usage this middleware should be used with throttler/limiter and, optionally, with some auth middlewares + +Example of usage: + +```go + check1 := func(ctx context.Context) (name string, err error) { + // do some check, for example check DB connection + return "check1", nil // all good, passed + } + check2 := func(ctx context.Context) (name string, err error) { + // do some other check, for example ping an external service + return "check2", errors.New("some error") // check failed + } + + router := chi.NewRouter() + router.Use(rest.Health("/health", check1, check2)) +``` + +example of the actual call and response: + +``` +> http GET https://example.com/health + +HTTP/1.1 503 Service Unavailable +Date: Sun, 15 Jul 2018 19:40:31 GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 36 + +[ + {"name":"check1","status":"ok"}, + {"name":"check2","status":"failed","error":"some error"} +] +``` + +_this middleware is pretty basic, but can be used for simple health checks. For more complex cases, like async/cached health checks see [alexliesenfeld/health](https://github.com/alexliesenfeld/health)_ + ### Logger middleware Logs request, request handling time and response. Log record fields in order of occurrence: @@ -119,6 +158,50 @@ Maybe middleware will allow you to change the flow of the middleware stack execu value of maybeFn(request). This is useful for example if you'd like to skip a middleware handler if a request does not satisfy the maybeFn logic. +### Reject middleware + +Reject is a middleware that rejects requests with a given status code and message based on a user-defined function. +This is useful for example if you'd like to reject requests to a particular resource based on a request header, or want to implement a conditional request handler based on service parameters. + +example with chi router: + +```go + router := chi.NewRouter() + + rejectFn := func(r *http.Request) (bool) { + return r.Header.Get("X-Request-Id") == "" // reject if no X-Request-Id header + } + + router.Use(rest.Reject(http.StatusBadRequest, "X-Request-Id header is required", rejectFn)) +``` + +### Benchmarks middleware + +Benchmarks middleware allows to measure the time of request handling, number of request per second and report aggregated metrics. This middleware keeps track of the request in the memory and keep up to 900 points (15 minutes, data-point per second). + +In order to retrieve the data user should call `Stats(d duration)` method. duration is the time window for which the benchmark data should be returned. It can be any duration from 1s to 15m. Note: all the time data is in microseconds. + +example with chi router: + +```go + router := chi.NewRouter() + bench = rest.NewBenchmarks() + router.Use(bench.Middleware) + ... + router.Get("/bench", func(w http.ResponseWriter, r *http.Request) { + resp := struct { + OneMin rest.BenchmarkStats `json:"1min"` + FiveMin rest.BenchmarkStats `json:"5min"` + FifteenMin rest.BenchmarkStats `json:"15min"` + }{ + bench.Stats(time.Minute), + bench.Stats(time.Minute * 5), + bench.Stats(time.Minute * 15), + } + render.JSON(w, r, resp) + }) +``` + ## Helpers - `rest.Wrap` - converts a list of middlewares to nested handlers calls (in reverse order) diff --git a/vendor/github.com/go-pkgz/rest/basic_auth.go b/vendor/github.com/go-pkgz/rest/basic_auth.go index 6ed0c684..f5d77777 100644 --- a/vendor/github.com/go-pkgz/rest/basic_auth.go +++ b/vendor/github.com/go-pkgz/rest/basic_auth.go @@ -1,9 +1,13 @@ package rest import ( + "context" + "crypto/subtle" "net/http" ) +const baContextKey = "authorizedWithBasicAuth" + // BasicAuth middleware requires basic auth and matches user & passwd with client-provided checker func BasicAuth(checker func(user, passwd string) bool) func(http.Handler) http.Handler { @@ -19,8 +23,25 @@ func BasicAuth(checker func(user, passwd string) bool) func(http.Handler) http.H w.WriteHeader(http.StatusForbidden) return } - h.ServeHTTP(w, r) + h.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), contextKey(baContextKey), true))) } return http.HandlerFunc(fn) } } + +// BasicAuthWithUserPasswd middleware requires basic auth and matches user & passwd with client-provided values +func BasicAuthWithUserPasswd(user, passwd string) func(http.Handler) http.Handler { + checkFn := func(reqUser, reqPasswd string) bool { + matchUser := subtle.ConstantTimeCompare([]byte(user), []byte(reqUser)) + matchPass := subtle.ConstantTimeCompare([]byte(passwd), []byte(reqPasswd)) + return matchUser == 1 && matchPass == 1 + } + return BasicAuth(checkFn) +} + +// IsAuthorized returns true is user authorized. +// it can be used in handlers to check if BasicAuth middleware was applied +func IsAuthorized(ctx context.Context) bool { + v := ctx.Value(contextKey(baContextKey)) + return v != nil && v.(bool) +} diff --git a/vendor/github.com/go-pkgz/rest/benchmarks.go b/vendor/github.com/go-pkgz/rest/benchmarks.go new file mode 100644 index 00000000..702e517b --- /dev/null +++ b/vendor/github.com/go-pkgz/rest/benchmarks.go @@ -0,0 +1,159 @@ +package rest + +import ( + "container/list" + "net/http" + "sync" + "time" +) + +var maxTimeRangeDefault = time.Duration(15) * time.Minute + +// Benchmarks is a basic benchmarking middleware collecting and reporting performance metrics +// It keeps track of the requests speeds and counts in 1s benchData buckets ,limiting the number of buckets +// to maxTimeRange. User can request the benchmark for any time duration. This is intended to be used +// for retrieving the benchmark data for the last minute, 5 minutes and up to maxTimeRange. +type Benchmarks struct { + st time.Time + data *list.List + lock sync.RWMutex + maxTimeRange time.Duration + + nowFn func() time.Time // for testing only +} + +type benchData struct { + // 1s aggregates + requests int + respTime time.Duration + minRespTime time.Duration + maxRespTime time.Duration + ts time.Time +} + +// BenchmarkStats holds the stats for a given interval +type BenchmarkStats struct { + Requests int `json:"requests"` + RequestsSec float64 `json:"requests_sec"` + AverageRespTime int64 `json:"average_resp_time"` + MinRespTime int64 `json:"min_resp_time"` + MaxRespTime int64 `json:"max_resp_time"` +} + +// NewBenchmarks creates a new benchmark middleware +func NewBenchmarks() *Benchmarks { + res := &Benchmarks{ + st: time.Now(), + data: list.New(), + nowFn: time.Now, + maxTimeRange: maxTimeRangeDefault, + } + return res +} + +// WithTimeRange sets the maximum time range for the benchmark to keep data for. +// Default is 15 minutes. The increase of this range will change memory utilization as each second of the range +// kept as benchData aggregate. The default means 15*60 = 900 seconds of data aggregate. +// Larger range allows for longer time periods to be benchmarked. +func (b *Benchmarks) WithTimeRange(max time.Duration) *Benchmarks { + b.lock.Lock() + defer b.lock.Unlock() + b.maxTimeRange = max + return b +} + +// Handler calculates 1/5/10m request per second and allows to access those values +func (b *Benchmarks) Handler(next http.Handler) http.Handler { + + fn := func(w http.ResponseWriter, r *http.Request) { + st := b.nowFn() + defer func() { + b.update(time.Since(st)) + }() + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) +} + +func (b *Benchmarks) update(reqDuration time.Duration) { + now := b.nowFn().Truncate(time.Second) + + b.lock.Lock() + defer b.lock.Unlock() + + // keep maxTimeRange in the list, drop the rest + for e := b.data.Front(); e != nil; e = e.Next() { + if b.data.Front().Value.(benchData).ts.After(b.nowFn().Add(-b.maxTimeRange)) { + break + } + b.data.Remove(b.data.Front()) + } + + last := b.data.Back() + if last == nil || last.Value.(benchData).ts.Before(now) { + b.data.PushBack(benchData{requests: 1, respTime: reqDuration, ts: now, + minRespTime: reqDuration, maxRespTime: reqDuration}) + return + } + + bd := last.Value.(benchData) + bd.requests++ + bd.respTime += reqDuration + + if bd.minRespTime == 0 || reqDuration < bd.minRespTime { + bd.minRespTime = reqDuration + } + if bd.maxRespTime == 0 || reqDuration > bd.maxRespTime { + bd.maxRespTime = reqDuration + } + + last.Value = bd +} + +// Stats returns the current benchmark stats for the given duration +func (b *Benchmarks) Stats(interval time.Duration) BenchmarkStats { + if interval < time.Second { // minimum interval is 1s due to the bucket size + return BenchmarkStats{} + } + + b.lock.RLock() + defer b.lock.RUnlock() + + var ( + requests int + respTime time.Duration + ) + + stInterval, fnInterval := time.Time{}, time.Time{} + var minRespTime, maxRespTime time.Duration + for e := b.data.Back(); e != nil; e = e.Prev() { // reverse order + bd := e.Value.(benchData) + if bd.ts.Before(b.nowFn().Add(-interval)) { + break + } + if minRespTime == 0 || bd.minRespTime < minRespTime { + minRespTime = bd.minRespTime + } + if maxRespTime == 0 || bd.maxRespTime > maxRespTime { + maxRespTime = bd.maxRespTime + } + requests += bd.requests + respTime += bd.respTime + if fnInterval.IsZero() { + fnInterval = bd.ts.Add(time.Second) + } + stInterval = bd.ts + } + + if requests == 0 { + return BenchmarkStats{} + } + + return BenchmarkStats{ + Requests: requests, + RequestsSec: float64(requests) / (fnInterval.Sub(stInterval).Seconds()), + AverageRespTime: respTime.Microseconds() / int64(requests), + MinRespTime: minRespTime.Microseconds(), + MaxRespTime: maxRespTime.Microseconds(), + } +} diff --git a/vendor/github.com/go-pkgz/rest/middleware.go b/vendor/github.com/go-pkgz/rest/middleware.go index 3adb154c..6e503379 100644 --- a/vendor/github.com/go-pkgz/rest/middleware.go +++ b/vendor/github.com/go-pkgz/rest/middleware.go @@ -1,6 +1,7 @@ package rest import ( + "context" "net/http" "os" "runtime/debug" @@ -54,6 +55,47 @@ func Ping(next http.Handler) http.Handler { return http.HandlerFunc(fn) } +// Health middleware response with health info and status (200 if healthy). Stops chain if health request detected +// passed checkers implements custom health checks and returns error if health check failed. The check has to return name +// regardless to the error state. +// For production usage this middleware should be used with throttler and, optionally, with BasicAuth middlewares +func Health(path string, checkers ...func(ctx context.Context) (name string, err error)) func(http.Handler) http.Handler { + + type hr struct { + Name string `json:"name"` + Status string `json:"status"` + Error string `json:"error,omitempty"` + } + + return func(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" || !strings.EqualFold(r.URL.Path, path) { + h.ServeHTTP(w, r) // not the health check request, continue the chain + return + } + resp := []hr{} + var anyError bool + for _, check := range checkers { + name, err := check(r.Context()) + hh := hr{Name: name, Status: "ok"} + if err != nil { + hh.Status = "failed" + hh.Error = err.Error() + anyError = true + } + resp = append(resp, hh) + } + if anyError { + w.WriteHeader(http.StatusServiceUnavailable) + } else { + w.WriteHeader(http.StatusOK) + } + RenderJSON(w, resp) + } + return http.HandlerFunc(fn) + } +} + // Recoverer is a middleware that recovers from panics, logs the panic and returns a HTTP 500 status if possible. func Recoverer(l logger.Backend) func(http.Handler) http.Handler { return func(h http.Handler) http.Handler { @@ -125,3 +167,18 @@ func RealIP(h http.Handler) http.Handler { return http.HandlerFunc(fn) } + +// Reject is a middleware that conditionally rejects requests with a given status code and message. +// user-defined condition function rejectFn is used to determine if the request should be rejected. +func Reject(errCode int, errMsg string, rejectFn func(r *http.Request) bool) func(h http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if rejectFn(r) { + http.Error(w, errMsg, errCode) + return + } + h.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + } +} diff --git a/vendor/github.com/go-pkgz/rest/rest.go b/vendor/github.com/go-pkgz/rest/rest.go index 57fe4a7b..1b321ec0 100644 --- a/vendor/github.com/go-pkgz/rest/rest.go +++ b/vendor/github.com/go-pkgz/rest/rest.go @@ -4,10 +4,9 @@ package rest import ( "bytes" "encoding/json" + "fmt" "net/http" "time" - - "github.com/pkg/errors" ) // JSON is a map alias, just for convenience @@ -30,7 +29,7 @@ func RenderJSON(w http.ResponseWriter, data interface{}) { func RenderJSONFromBytes(w http.ResponseWriter, r *http.Request, data []byte) error { w.Header().Set("Content-Type", "application/json; charset=utf-8") if _, err := w.Write(data); err != nil { - return errors.Wrapf(err, "failed to send response to %s", r.RemoteAddr) + return fmt.Errorf("failed to send response to %s: %w", r.RemoteAddr, err) } return nil } @@ -43,7 +42,7 @@ func RenderJSONWithHTML(w http.ResponseWriter, r *http.Request, v interface{}) e enc := json.NewEncoder(buf) enc.SetEscapeHTML(false) if err := enc.Encode(v); err != nil { - return nil, errors.Wrap(err, "json encoding failed") + return nil, fmt.Errorf("json encoding failed: %w", err) } return buf.Bytes(), nil } @@ -86,15 +85,15 @@ func ParseFromTo(r *http.Request) (from, to time.Time, err error) { return t, nil } } - return time.Time{}, errors.Errorf("can't parse date %q", ts) + return time.Time{}, fmt.Errorf("can't parse date %q", ts) } if from, err = parseTimeStamp(r.URL.Query().Get("from")); err != nil { - return from, to, errors.Wrap(err, "incorrect from time") + return from, to, fmt.Errorf("incorrect from time: %w", err) } if to, err = parseTimeStamp(r.URL.Query().Get("to")); err != nil { - return from, to, errors.Wrap(err, "incorrect to time") + return from, to, fmt.Errorf("incorrect to time: %w", err) } return from, to, nil } diff --git a/vendor/github.com/go-redis/redis/v7/.golangci.yml b/vendor/github.com/go-redis/redis/v7/.golangci.yml deleted file mode 100644 index 912dab1e..00000000 --- a/vendor/github.com/go-redis/redis/v7/.golangci.yml +++ /dev/null @@ -1,15 +0,0 @@ -run: - concurrency: 8 - deadline: 5m - tests: false -linters: - enable-all: true - disable: - - funlen - - gochecknoglobals - - gocognit - - goconst - - godox - - gosec - - maligned - - wsl diff --git a/vendor/github.com/go-redis/redis/v7/.travis.yml b/vendor/github.com/go-redis/redis/v7/.travis.yml deleted file mode 100644 index 3f93932b..00000000 --- a/vendor/github.com/go-redis/redis/v7/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -dist: xenial -language: go - -services: - - redis-server - -go: - - 1.12.x - - 1.13.x - - tip - -matrix: - allow_failures: - - go: tip - -env: - - GO111MODULE=on - -go_import_path: github.com/go-redis/redis - -before_install: - - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.21.0 diff --git a/vendor/github.com/go-redis/redis/v7/CHANGELOG.md b/vendor/github.com/go-redis/redis/v7/CHANGELOG.md deleted file mode 100644 index bd4eccff..00000000 --- a/vendor/github.com/go-redis/redis/v7/CHANGELOG.md +++ /dev/null @@ -1,46 +0,0 @@ -# Changelog - -## v7.2 - -- Existing `HMSet` is renamed to `HSet` and old deprecated `HMSet` is restored for Redis 3 users. - -## v7.1 - -- Existing `Cmd.String` is renamed to `Cmd.Text`. New `Cmd.String` implements `fmt.Stringer` interface. - -## v7 - -- *Important*. Tx.Pipeline now returns a non-transactional pipeline. Use Tx.TxPipeline for a transactional pipeline. -- WrapProcess is replaced with more convenient AddHook that has access to context.Context. -- WithContext now can not be used to create a shallow copy of the client. -- New methods ProcessContext, DoContext, and ExecContext. -- Client respects Context.Deadline when setting net.Conn deadline. -- Client listens on Context.Done while waiting for a connection from the pool and returns an error when context context is cancelled. -- Add PubSub.ChannelWithSubscriptions that sends `*Subscription` in addition to `*Message` to allow detecting reconnections. -- `time.Time` is now marshalled in RFC3339 format. `rdb.Get("foo").Time()` helper is added to parse the time. -- `SetLimiter` is removed and added `Options.Limiter` instead. -- `HMSet` is deprecated as of Redis v4. - -## v6.15 - -- Cluster and Ring pipelines process commands for each node in its own goroutine. - -## 6.14 - -- Added Options.MinIdleConns. -- Added Options.MaxConnAge. -- PoolStats.FreeConns is renamed to PoolStats.IdleConns. -- Add Client.Do to simplify creating custom commands. -- Add Cmd.String, Cmd.Int, Cmd.Int64, Cmd.Uint64, Cmd.Float64, and Cmd.Bool helpers. -- Lower memory usage. - -## v6.13 - -- Ring got new options called `HashReplicas` and `Hash`. It is recommended to set `HashReplicas = 1000` for better keys distribution between shards. -- Cluster client was optimized to use much less memory when reloading cluster state. -- PubSub.ReceiveMessage is re-worked to not use ReceiveTimeout so it does not lose data when timeout occurres. In most cases it is recommended to use PubSub.Channel instead. -- Dialer.KeepAlive is set to 5 minutes by default. - -## v6.12 - -- ClusterClient got new option called `ClusterSlots` which allows to build cluster of normal Redis Servers that don't have cluster mode enabled. See https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup diff --git a/vendor/github.com/go-redis/redis/v7/Makefile b/vendor/github.com/go-redis/redis/v7/Makefile deleted file mode 100644 index 86609c6e..00000000 --- a/vendor/github.com/go-redis/redis/v7/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -all: testdeps - go test ./... - go test ./... -short -race - go test ./... -run=NONE -bench=. -benchmem - env GOOS=linux GOARCH=386 go test ./... - golangci-lint run - -testdeps: testdata/redis/src/redis-server - -bench: testdeps - go test ./... -test.run=NONE -test.bench=. -test.benchmem - -.PHONY: all test testdeps bench - -testdata/redis: - mkdir -p $@ - wget -qO- http://download.redis.io/redis-stable.tar.gz | tar xvz --strip-components=1 -C $@ - -testdata/redis/src/redis-server: testdata/redis - cd $< && make all diff --git a/vendor/github.com/go-redis/redis/v7/README.md b/vendor/github.com/go-redis/redis/v7/README.md deleted file mode 100644 index 0fbb506e..00000000 --- a/vendor/github.com/go-redis/redis/v7/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# Redis client for Golang - -[![Build Status](https://travis-ci.org/go-redis/redis.png?branch=master)](https://travis-ci.org/go-redis/redis) -[![GoDoc](https://godoc.org/github.com/go-redis/redis?status.svg)](https://godoc.org/github.com/go-redis/redis) -[![Airbrake](https://img.shields.io/badge/kudos-airbrake.io-orange.svg)](https://airbrake.io) - -Supports: - -- Redis 3 commands except QUIT, MONITOR, SLOWLOG and SYNC. -- Automatic connection pooling with [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support. -- [Pub/Sub](https://godoc.org/github.com/go-redis/redis#PubSub). -- [Transactions](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline). -- [Pipeline](https://godoc.org/github.com/go-redis/redis#example-Client-Pipeline) and [TxPipeline](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline). -- [Scripting](https://godoc.org/github.com/go-redis/redis#Script). -- [Timeouts](https://godoc.org/github.com/go-redis/redis#Options). -- [Redis Sentinel](https://godoc.org/github.com/go-redis/redis#NewFailoverClient). -- [Redis Cluster](https://godoc.org/github.com/go-redis/redis#NewClusterClient). -- [Cluster of Redis Servers](https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup) without using cluster mode and Redis Sentinel. -- [Ring](https://godoc.org/github.com/go-redis/redis#NewRing). -- [Instrumentation](https://godoc.org/github.com/go-redis/redis#ex-package--Instrumentation). -- [Cache friendly](https://github.com/go-redis/cache). -- [Rate limiting](https://github.com/go-redis/redis_rate). -- [Distributed Locks](https://github.com/bsm/redislock). - -API docs: https://godoc.org/github.com/go-redis/redis. -Examples: https://godoc.org/github.com/go-redis/redis#pkg-examples. - -## Installation - -go-redis requires a Go version with [Modules](https://github.com/golang/go/wiki/Modules) support and uses import versioning. So please make sure to initialize a Go module before installing go-redis: - -``` shell -go mod init github.com/my/repo -go get github.com/go-redis/redis/v7 -``` - -Import: - -``` go -import "github.com/go-redis/redis/v7" -``` - -## Quickstart - -``` go -func ExampleNewClient() { - client := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - }) - - pong, err := client.Ping().Result() - fmt.Println(pong, err) - // Output: PONG -} - -func ExampleClient() { - client := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - }) - err := client.Set("key", "value", 0).Err() - if err != nil { - panic(err) - } - - val, err := client.Get("key").Result() - if err != nil { - panic(err) - } - fmt.Println("key", val) - - val2, err := client.Get("key2").Result() - if err == redis.Nil { - fmt.Println("key2 does not exist") - } else if err != nil { - panic(err) - } else { - fmt.Println("key2", val2) - } - // Output: key value - // key2 does not exist -} -``` - -## Howto - -Please go through [examples](https://godoc.org/github.com/go-redis/redis#pkg-examples) to get an idea how to use this package. - -## Look and feel - -Some corner cases: - -``` go -// SET key value EX 10 NX -set, err := client.SetNX("key", "value", 10*time.Second).Result() - -// SORT list LIMIT 0 2 ASC -vals, err := client.Sort("list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result() - -// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2 -vals, err := client.ZRangeByScoreWithScores("zset", &redis.ZRangeBy{ - Min: "-inf", - Max: "+inf", - Offset: 0, - Count: 2, -}).Result() - -// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM -vals, err := client.ZInterStore("out", &redis.ZStore{ - Keys: []string{"zset1", "zset2"}, - Weights: []int64{2, 3} -}).Result() - -// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" -vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result() - -// custom command -res, err := client.Do("set", "key", "value").Result() -``` - -## See also - -- [Golang PostgreSQL ORM](https://github.com/go-pg/pg) -- [Golang msgpack](https://github.com/vmihailenco/msgpack) -- [Golang message task queue](https://github.com/vmihailenco/taskq) diff --git a/vendor/github.com/go-redis/redis/v7/cluster_commands.go b/vendor/github.com/go-redis/redis/v7/cluster_commands.go deleted file mode 100644 index c9b9b9de..00000000 --- a/vendor/github.com/go-redis/redis/v7/cluster_commands.go +++ /dev/null @@ -1,22 +0,0 @@ -package redis - -import "sync/atomic" - -func (c *ClusterClient) DBSize() *IntCmd { - cmd := NewIntCmd("dbsize") - var size int64 - err := c.ForEachMaster(func(master *Client) error { - n, err := master.DBSize().Result() - if err != nil { - return err - } - atomic.AddInt64(&size, n) - return nil - }) - if err != nil { - cmd.SetErr(err) - return cmd - } - cmd.val = size - return cmd -} diff --git a/vendor/github.com/go-redis/redis/v7/command.go b/vendor/github.com/go-redis/redis/v7/command.go deleted file mode 100644 index dd7fe4a9..00000000 --- a/vendor/github.com/go-redis/redis/v7/command.go +++ /dev/null @@ -1,2064 +0,0 @@ -package redis - -import ( - "fmt" - "net" - "strconv" - "strings" - "time" - - "github.com/go-redis/redis/v7/internal" - "github.com/go-redis/redis/v7/internal/proto" - "github.com/go-redis/redis/v7/internal/util" -) - -type Cmder interface { - Name() string - Args() []interface{} - String() string - stringArg(int) string - - readTimeout() *time.Duration - readReply(rd *proto.Reader) error - - SetErr(error) - Err() error -} - -func setCmdsErr(cmds []Cmder, e error) { - for _, cmd := range cmds { - if cmd.Err() == nil { - cmd.SetErr(e) - } - } -} - -func cmdsFirstErr(cmds []Cmder) error { - for _, cmd := range cmds { - if err := cmd.Err(); err != nil { - return err - } - } - return nil -} - -func writeCmds(wr *proto.Writer, cmds []Cmder) error { - for _, cmd := range cmds { - if err := writeCmd(wr, cmd); err != nil { - return err - } - } - return nil -} - -func writeCmd(wr *proto.Writer, cmd Cmder) error { - return wr.WriteArgs(cmd.Args()) -} - -func cmdString(cmd Cmder, val interface{}) string { - ss := make([]string, 0, len(cmd.Args())) - for _, arg := range cmd.Args() { - ss = append(ss, fmt.Sprint(arg)) - } - s := strings.Join(ss, " ") - if err := cmd.Err(); err != nil { - return s + ": " + err.Error() - } - if val != nil { - switch vv := val.(type) { - case []byte: - return s + ": " + string(vv) - default: - return s + ": " + fmt.Sprint(val) - } - } - return s -} - -func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int { - switch cmd.Name() { - case "eval", "evalsha": - if cmd.stringArg(2) != "0" { - return 3 - } - - return 0 - case "publish": - return 1 - } - if info == nil { - return 0 - } - return int(info.FirstKeyPos) -} - -//------------------------------------------------------------------------------ - -type baseCmd struct { - args []interface{} - err error - - _readTimeout *time.Duration -} - -var _ Cmder = (*Cmd)(nil) - -func (cmd *baseCmd) Name() string { - if len(cmd.args) == 0 { - return "" - } - // Cmd name must be lower cased. - return internal.ToLower(cmd.stringArg(0)) -} - -func (cmd *baseCmd) Args() []interface{} { - return cmd.args -} - -func (cmd *baseCmd) stringArg(pos int) string { - if pos < 0 || pos >= len(cmd.args) { - return "" - } - s, _ := cmd.args[pos].(string) - return s -} - -func (cmd *baseCmd) SetErr(e error) { - cmd.err = e -} - -func (cmd *baseCmd) Err() error { - return cmd.err -} - -func (cmd *baseCmd) readTimeout() *time.Duration { - return cmd._readTimeout -} - -func (cmd *baseCmd) setReadTimeout(d time.Duration) { - cmd._readTimeout = &d -} - -//------------------------------------------------------------------------------ - -type Cmd struct { - baseCmd - - val interface{} -} - -func NewCmd(args ...interface{}) *Cmd { - return &Cmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *Cmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *Cmd) Val() interface{} { - return cmd.val -} - -func (cmd *Cmd) Result() (interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *Cmd) Text() (string, error) { - if cmd.err != nil { - return "", cmd.err - } - switch val := cmd.val.(type) { - case string: - return val, nil - default: - err := fmt.Errorf("redis: unexpected type=%T for String", val) - return "", err - } -} - -func (cmd *Cmd) Int() (int, error) { - if cmd.err != nil { - return 0, cmd.err - } - switch val := cmd.val.(type) { - case int64: - return int(val), nil - case string: - return strconv.Atoi(val) - default: - err := fmt.Errorf("redis: unexpected type=%T for Int", val) - return 0, err - } -} - -func (cmd *Cmd) Int64() (int64, error) { - if cmd.err != nil { - return 0, cmd.err - } - switch val := cmd.val.(type) { - case int64: - return val, nil - case string: - return strconv.ParseInt(val, 10, 64) - default: - err := fmt.Errorf("redis: unexpected type=%T for Int64", val) - return 0, err - } -} - -func (cmd *Cmd) Uint64() (uint64, error) { - if cmd.err != nil { - return 0, cmd.err - } - switch val := cmd.val.(type) { - case int64: - return uint64(val), nil - case string: - return strconv.ParseUint(val, 10, 64) - default: - err := fmt.Errorf("redis: unexpected type=%T for Uint64", val) - return 0, err - } -} - -func (cmd *Cmd) Float32() (float32, error) { - if cmd.err != nil { - return 0, cmd.err - } - switch val := cmd.val.(type) { - case int64: - return float32(val), nil - case string: - f, err := strconv.ParseFloat(val, 32) - if err != nil { - return 0, err - } - return float32(f), nil - default: - err := fmt.Errorf("redis: unexpected type=%T for Float32", val) - return 0, err - } -} - -func (cmd *Cmd) Float64() (float64, error) { - if cmd.err != nil { - return 0, cmd.err - } - switch val := cmd.val.(type) { - case int64: - return float64(val), nil - case string: - return strconv.ParseFloat(val, 64) - default: - err := fmt.Errorf("redis: unexpected type=%T for Float64", val) - return 0, err - } -} - -func (cmd *Cmd) Bool() (bool, error) { - if cmd.err != nil { - return false, cmd.err - } - switch val := cmd.val.(type) { - case int64: - return val != 0, nil - case string: - return strconv.ParseBool(val) - default: - err := fmt.Errorf("redis: unexpected type=%T for Bool", val) - return false, err - } -} - -func (cmd *Cmd) readReply(rd *proto.Reader) error { - cmd.val, cmd.err = rd.ReadReply(sliceParser) - return cmd.err -} - -// Implements proto.MultiBulkParse -func sliceParser(rd *proto.Reader, n int64) (interface{}, error) { - vals := make([]interface{}, n) - for i := 0; i < len(vals); i++ { - v, err := rd.ReadReply(sliceParser) - if err != nil { - if err == Nil { - vals[i] = nil - continue - } - if err, ok := err.(proto.RedisError); ok { - vals[i] = err - continue - } - return nil, err - } - vals[i] = v - } - return vals, nil -} - -//------------------------------------------------------------------------------ - -type SliceCmd struct { - baseCmd - - val []interface{} -} - -var _ Cmder = (*SliceCmd)(nil) - -func NewSliceCmd(args ...interface{}) *SliceCmd { - return &SliceCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *SliceCmd) Val() []interface{} { - return cmd.val -} - -func (cmd *SliceCmd) Result() ([]interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *SliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *SliceCmd) readReply(rd *proto.Reader) error { - var v interface{} - v, cmd.err = rd.ReadArrayReply(sliceParser) - if cmd.err != nil { - return cmd.err - } - cmd.val = v.([]interface{}) - return nil -} - -//------------------------------------------------------------------------------ - -type StatusCmd struct { - baseCmd - - val string -} - -var _ Cmder = (*StatusCmd)(nil) - -func NewStatusCmd(args ...interface{}) *StatusCmd { - return &StatusCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *StatusCmd) Val() string { - return cmd.val -} - -func (cmd *StatusCmd) Result() (string, error) { - return cmd.val, cmd.err -} - -func (cmd *StatusCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StatusCmd) readReply(rd *proto.Reader) error { - cmd.val, cmd.err = rd.ReadString() - return cmd.err -} - -//------------------------------------------------------------------------------ - -type IntCmd struct { - baseCmd - - val int64 -} - -var _ Cmder = (*IntCmd)(nil) - -func NewIntCmd(args ...interface{}) *IntCmd { - return &IntCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *IntCmd) Val() int64 { - return cmd.val -} - -func (cmd *IntCmd) Result() (int64, error) { - return cmd.val, cmd.err -} - -func (cmd *IntCmd) Uint64() (uint64, error) { - return uint64(cmd.val), cmd.err -} - -func (cmd *IntCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *IntCmd) readReply(rd *proto.Reader) error { - cmd.val, cmd.err = rd.ReadIntReply() - return cmd.err -} - -//------------------------------------------------------------------------------ - -type IntSliceCmd struct { - baseCmd - - val []int64 -} - -var _ Cmder = (*IntSliceCmd)(nil) - -func NewIntSliceCmd(args ...interface{}) *IntSliceCmd { - return &IntSliceCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *IntSliceCmd) Val() []int64 { - return cmd.val -} - -func (cmd *IntSliceCmd) Result() ([]int64, error) { - return cmd.val, cmd.err -} - -func (cmd *IntSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]int64, n) - for i := 0; i < len(cmd.val); i++ { - num, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - cmd.val[i] = num - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type DurationCmd struct { - baseCmd - - val time.Duration - precision time.Duration -} - -var _ Cmder = (*DurationCmd)(nil) - -func NewDurationCmd(precision time.Duration, args ...interface{}) *DurationCmd { - return &DurationCmd{ - baseCmd: baseCmd{args: args}, - precision: precision, - } -} - -func (cmd *DurationCmd) Val() time.Duration { - return cmd.val -} - -func (cmd *DurationCmd) Result() (time.Duration, error) { - return cmd.val, cmd.err -} - -func (cmd *DurationCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *DurationCmd) readReply(rd *proto.Reader) error { - var n int64 - n, cmd.err = rd.ReadIntReply() - if cmd.err != nil { - return cmd.err - } - switch n { - // -2 if the key does not exist - // -1 if the key exists but has no associated expire - case -2, -1: - cmd.val = time.Duration(n) - default: - cmd.val = time.Duration(n) * cmd.precision - } - return nil -} - -//------------------------------------------------------------------------------ - -type TimeCmd struct { - baseCmd - - val time.Time -} - -var _ Cmder = (*TimeCmd)(nil) - -func NewTimeCmd(args ...interface{}) *TimeCmd { - return &TimeCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *TimeCmd) Val() time.Time { - return cmd.val -} - -func (cmd *TimeCmd) Result() (time.Time, error) { - return cmd.val, cmd.err -} - -func (cmd *TimeCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *TimeCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - if n != 2 { - return nil, fmt.Errorf("got %d elements, expected 2", n) - } - - sec, err := rd.ReadInt() - if err != nil { - return nil, err - } - - microsec, err := rd.ReadInt() - if err != nil { - return nil, err - } - - cmd.val = time.Unix(sec, microsec*1000) - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type BoolCmd struct { - baseCmd - - val bool -} - -var _ Cmder = (*BoolCmd)(nil) - -func NewBoolCmd(args ...interface{}) *BoolCmd { - return &BoolCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *BoolCmd) Val() bool { - return cmd.val -} - -func (cmd *BoolCmd) Result() (bool, error) { - return cmd.val, cmd.err -} - -func (cmd *BoolCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *BoolCmd) readReply(rd *proto.Reader) error { - var v interface{} - v, cmd.err = rd.ReadReply(nil) - // `SET key value NX` returns nil when key already exists. But - // `SETNX key value` returns bool (0/1). So convert nil to bool. - if cmd.err == Nil { - cmd.val = false - cmd.err = nil - return nil - } - if cmd.err != nil { - return cmd.err - } - switch v := v.(type) { - case int64: - cmd.val = v == 1 - return nil - case string: - cmd.val = v == "OK" - return nil - default: - cmd.err = fmt.Errorf("got %T, wanted int64 or string", v) - return cmd.err - } -} - -//------------------------------------------------------------------------------ - -type StringCmd struct { - baseCmd - - val string -} - -var _ Cmder = (*StringCmd)(nil) - -func NewStringCmd(args ...interface{}) *StringCmd { - return &StringCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *StringCmd) Val() string { - return cmd.val -} - -func (cmd *StringCmd) Result() (string, error) { - return cmd.Val(), cmd.err -} - -func (cmd *StringCmd) Bytes() ([]byte, error) { - return util.StringToBytes(cmd.val), cmd.err -} - -func (cmd *StringCmd) Int() (int, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.Atoi(cmd.Val()) -} - -func (cmd *StringCmd) Int64() (int64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.ParseInt(cmd.Val(), 10, 64) -} - -func (cmd *StringCmd) Uint64() (uint64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.ParseUint(cmd.Val(), 10, 64) -} - -func (cmd *StringCmd) Float32() (float32, error) { - if cmd.err != nil { - return 0, cmd.err - } - f, err := strconv.ParseFloat(cmd.Val(), 32) - if err != nil { - return 0, err - } - return float32(f), nil -} - -func (cmd *StringCmd) Float64() (float64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.ParseFloat(cmd.Val(), 64) -} - -func (cmd *StringCmd) Time() (time.Time, error) { - if cmd.err != nil { - return time.Time{}, cmd.err - } - return time.Parse(time.RFC3339Nano, cmd.Val()) -} - -func (cmd *StringCmd) Scan(val interface{}) error { - if cmd.err != nil { - return cmd.err - } - return proto.Scan([]byte(cmd.val), val) -} - -func (cmd *StringCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringCmd) readReply(rd *proto.Reader) error { - cmd.val, cmd.err = rd.ReadString() - return cmd.err -} - -//------------------------------------------------------------------------------ - -type FloatCmd struct { - baseCmd - - val float64 -} - -var _ Cmder = (*FloatCmd)(nil) - -func NewFloatCmd(args ...interface{}) *FloatCmd { - return &FloatCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *FloatCmd) Val() float64 { - return cmd.val -} - -func (cmd *FloatCmd) Result() (float64, error) { - return cmd.Val(), cmd.Err() -} - -func (cmd *FloatCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *FloatCmd) readReply(rd *proto.Reader) error { - cmd.val, cmd.err = rd.ReadFloatReply() - return cmd.err -} - -//------------------------------------------------------------------------------ - -type StringSliceCmd struct { - baseCmd - - val []string -} - -var _ Cmder = (*StringSliceCmd)(nil) - -func NewStringSliceCmd(args ...interface{}) *StringSliceCmd { - return &StringSliceCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *StringSliceCmd) Val() []string { - return cmd.val -} - -func (cmd *StringSliceCmd) Result() ([]string, error) { - return cmd.Val(), cmd.Err() -} - -func (cmd *StringSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringSliceCmd) ScanSlice(container interface{}) error { - return proto.ScanSlice(cmd.Val(), container) -} - -func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]string, n) - for i := 0; i < len(cmd.val); i++ { - switch s, err := rd.ReadString(); { - case err == Nil: - cmd.val[i] = "" - case err != nil: - return nil, err - default: - cmd.val[i] = s - } - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type BoolSliceCmd struct { - baseCmd - - val []bool -} - -var _ Cmder = (*BoolSliceCmd)(nil) - -func NewBoolSliceCmd(args ...interface{}) *BoolSliceCmd { - return &BoolSliceCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *BoolSliceCmd) Val() []bool { - return cmd.val -} - -func (cmd *BoolSliceCmd) Result() ([]bool, error) { - return cmd.val, cmd.err -} - -func (cmd *BoolSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]bool, n) - for i := 0; i < len(cmd.val); i++ { - n, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - cmd.val[i] = n == 1 - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type StringStringMapCmd struct { - baseCmd - - val map[string]string -} - -var _ Cmder = (*StringStringMapCmd)(nil) - -func NewStringStringMapCmd(args ...interface{}) *StringStringMapCmd { - return &StringStringMapCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *StringStringMapCmd) Val() map[string]string { - return cmd.val -} - -func (cmd *StringStringMapCmd) Result() (map[string]string, error) { - return cmd.val, cmd.err -} - -func (cmd *StringStringMapCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringStringMapCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make(map[string]string, n/2) - for i := int64(0); i < n; i += 2 { - key, err := rd.ReadString() - if err != nil { - return nil, err - } - - value, err := rd.ReadString() - if err != nil { - return nil, err - } - - cmd.val[key] = value - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type StringIntMapCmd struct { - baseCmd - - val map[string]int64 -} - -var _ Cmder = (*StringIntMapCmd)(nil) - -func NewStringIntMapCmd(args ...interface{}) *StringIntMapCmd { - return &StringIntMapCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *StringIntMapCmd) Val() map[string]int64 { - return cmd.val -} - -func (cmd *StringIntMapCmd) Result() (map[string]int64, error) { - return cmd.val, cmd.err -} - -func (cmd *StringIntMapCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringIntMapCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make(map[string]int64, n/2) - for i := int64(0); i < n; i += 2 { - key, err := rd.ReadString() - if err != nil { - return nil, err - } - - n, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - - cmd.val[key] = n - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type StringStructMapCmd struct { - baseCmd - - val map[string]struct{} -} - -var _ Cmder = (*StringStructMapCmd)(nil) - -func NewStringStructMapCmd(args ...interface{}) *StringStructMapCmd { - return &StringStructMapCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *StringStructMapCmd) Val() map[string]struct{} { - return cmd.val -} - -func (cmd *StringStructMapCmd) Result() (map[string]struct{}, error) { - return cmd.val, cmd.err -} - -func (cmd *StringStructMapCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make(map[string]struct{}, n) - for i := int64(0); i < n; i++ { - key, err := rd.ReadString() - if err != nil { - return nil, err - } - cmd.val[key] = struct{}{} - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type XMessage struct { - ID string - Values map[string]interface{} -} - -type XMessageSliceCmd struct { - baseCmd - - val []XMessage -} - -var _ Cmder = (*XMessageSliceCmd)(nil) - -func NewXMessageSliceCmd(args ...interface{}) *XMessageSliceCmd { - return &XMessageSliceCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *XMessageSliceCmd) Val() []XMessage { - return cmd.val -} - -func (cmd *XMessageSliceCmd) Result() ([]XMessage, error) { - return cmd.val, cmd.err -} - -func (cmd *XMessageSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XMessageSliceCmd) readReply(rd *proto.Reader) error { - var v interface{} - v, cmd.err = rd.ReadArrayReply(xMessageSliceParser) - if cmd.err != nil { - return cmd.err - } - cmd.val = v.([]XMessage) - return nil -} - -// Implements proto.MultiBulkParse -func xMessageSliceParser(rd *proto.Reader, n int64) (interface{}, error) { - msgs := make([]XMessage, n) - for i := 0; i < len(msgs); i++ { - i := i - _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - id, err := rd.ReadString() - if err != nil { - return nil, err - } - - var values map[string]interface{} - - v, err := rd.ReadArrayReply(stringInterfaceMapParser) - if err != nil { - if err != proto.Nil { - return nil, err - } - } else { - values = v.(map[string]interface{}) - } - - msgs[i] = XMessage{ - ID: id, - Values: values, - } - return nil, nil - }) - if err != nil { - return nil, err - } - } - return msgs, nil -} - -// Implements proto.MultiBulkParse -func stringInterfaceMapParser(rd *proto.Reader, n int64) (interface{}, error) { - m := make(map[string]interface{}, n/2) - for i := int64(0); i < n; i += 2 { - key, err := rd.ReadString() - if err != nil { - return nil, err - } - - value, err := rd.ReadString() - if err != nil { - return nil, err - } - - m[key] = value - } - return m, nil -} - -//------------------------------------------------------------------------------ - -type XStream struct { - Stream string - Messages []XMessage -} - -type XStreamSliceCmd struct { - baseCmd - - val []XStream -} - -var _ Cmder = (*XStreamSliceCmd)(nil) - -func NewXStreamSliceCmd(args ...interface{}) *XStreamSliceCmd { - return &XStreamSliceCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *XStreamSliceCmd) Val() []XStream { - return cmd.val -} - -func (cmd *XStreamSliceCmd) Result() ([]XStream, error) { - return cmd.val, cmd.err -} - -func (cmd *XStreamSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]XStream, n) - for i := 0; i < len(cmd.val); i++ { - i := i - _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - if n != 2 { - return nil, fmt.Errorf("got %d, wanted 2", n) - } - - stream, err := rd.ReadString() - if err != nil { - return nil, err - } - - v, err := rd.ReadArrayReply(xMessageSliceParser) - if err != nil { - return nil, err - } - - cmd.val[i] = XStream{ - Stream: stream, - Messages: v.([]XMessage), - } - return nil, nil - }) - if err != nil { - return nil, err - } - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type XPending struct { - Count int64 - Lower string - Higher string - Consumers map[string]int64 -} - -type XPendingCmd struct { - baseCmd - val *XPending -} - -var _ Cmder = (*XPendingCmd)(nil) - -func NewXPendingCmd(args ...interface{}) *XPendingCmd { - return &XPendingCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *XPendingCmd) Val() *XPending { - return cmd.val -} - -func (cmd *XPendingCmd) Result() (*XPending, error) { - return cmd.val, cmd.err -} - -func (cmd *XPendingCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XPendingCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - if n != 4 { - return nil, fmt.Errorf("got %d, wanted 4", n) - } - - count, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - - lower, err := rd.ReadString() - if err != nil && err != Nil { - return nil, err - } - - higher, err := rd.ReadString() - if err != nil && err != Nil { - return nil, err - } - - cmd.val = &XPending{ - Count: count, - Lower: lower, - Higher: higher, - } - _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - for i := int64(0); i < n; i++ { - _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - if n != 2 { - return nil, fmt.Errorf("got %d, wanted 2", n) - } - - consumerName, err := rd.ReadString() - if err != nil { - return nil, err - } - - consumerPending, err := rd.ReadInt() - if err != nil { - return nil, err - } - - if cmd.val.Consumers == nil { - cmd.val.Consumers = make(map[string]int64) - } - cmd.val.Consumers[consumerName] = consumerPending - - return nil, nil - }) - if err != nil { - return nil, err - } - } - return nil, nil - }) - if err != nil && err != Nil { - return nil, err - } - - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type XPendingExt struct { - ID string - Consumer string - Idle time.Duration - RetryCount int64 -} - -type XPendingExtCmd struct { - baseCmd - val []XPendingExt -} - -var _ Cmder = (*XPendingExtCmd)(nil) - -func NewXPendingExtCmd(args ...interface{}) *XPendingExtCmd { - return &XPendingExtCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *XPendingExtCmd) Val() []XPendingExt { - return cmd.val -} - -func (cmd *XPendingExtCmd) Result() ([]XPendingExt, error) { - return cmd.val, cmd.err -} - -func (cmd *XPendingExtCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]XPendingExt, 0, n) - for i := int64(0); i < n; i++ { - _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - if n != 4 { - return nil, fmt.Errorf("got %d, wanted 4", n) - } - - id, err := rd.ReadString() - if err != nil { - return nil, err - } - - consumer, err := rd.ReadString() - if err != nil && err != Nil { - return nil, err - } - - idle, err := rd.ReadIntReply() - if err != nil && err != Nil { - return nil, err - } - - retryCount, err := rd.ReadIntReply() - if err != nil && err != Nil { - return nil, err - } - - cmd.val = append(cmd.val, XPendingExt{ - ID: id, - Consumer: consumer, - Idle: time.Duration(idle) * time.Millisecond, - RetryCount: retryCount, - }) - return nil, nil - }) - if err != nil { - return nil, err - } - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type XInfoGroupsCmd struct { - baseCmd - val []XInfoGroups -} - -type XInfoGroups struct { - Name string - Consumers int64 - Pending int64 - LastDeliveredID string -} - -var _ Cmder = (*XInfoGroupsCmd)(nil) - -func NewXInfoGroupsCmd(stream string) *XInfoGroupsCmd { - return &XInfoGroupsCmd{ - baseCmd: baseCmd{args: []interface{}{"xinfo", "groups", stream}}, - } -} - -func (cmd *XInfoGroupsCmd) Val() []XInfoGroups { - return cmd.val -} - -func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroups, error) { - return cmd.val, cmd.err -} - -func (cmd *XInfoGroupsCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XInfoGroupsCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply( - func(rd *proto.Reader, n int64) (interface{}, error) { - for i := int64(0); i < n; i++ { - v, err := rd.ReadReply(xGroupInfoParser) - if err != nil { - return nil, err - } - cmd.val = append(cmd.val, v.(XInfoGroups)) - } - return nil, nil - }) - return nil -} - -func xGroupInfoParser(rd *proto.Reader, n int64) (interface{}, error) { - if n != 8 { - return nil, fmt.Errorf("redis: got %d elements in XINFO GROUPS reply,"+ - "wanted 8", n) - } - var ( - err error - grp XInfoGroups - key string - val string - ) - - for i := 0; i < 4; i++ { - key, err = rd.ReadString() - if err != nil { - return nil, err - } - val, err = rd.ReadString() - if err != nil { - return nil, err - } - switch key { - case "name": - grp.Name = val - case "consumers": - grp.Consumers, err = strconv.ParseInt(val, 0, 64) - case "pending": - grp.Pending, err = strconv.ParseInt(val, 0, 64) - case "last-delivered-id": - grp.LastDeliveredID = val - default: - return nil, fmt.Errorf("redis: unexpected content %s "+ - "in XINFO GROUPS reply", key) - } - if err != nil { - return nil, err - } - } - return grp, err -} - -//------------------------------------------------------------------------------ - -type ZSliceCmd struct { - baseCmd - - val []Z -} - -var _ Cmder = (*ZSliceCmd)(nil) - -func NewZSliceCmd(args ...interface{}) *ZSliceCmd { - return &ZSliceCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *ZSliceCmd) Val() []Z { - return cmd.val -} - -func (cmd *ZSliceCmd) Result() ([]Z, error) { - return cmd.val, cmd.err -} - -func (cmd *ZSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]Z, n/2) - for i := 0; i < len(cmd.val); i++ { - member, err := rd.ReadString() - if err != nil { - return nil, err - } - - score, err := rd.ReadFloatReply() - if err != nil { - return nil, err - } - - cmd.val[i] = Z{ - Member: member, - Score: score, - } - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type ZWithKeyCmd struct { - baseCmd - - val *ZWithKey -} - -var _ Cmder = (*ZWithKeyCmd)(nil) - -func NewZWithKeyCmd(args ...interface{}) *ZWithKeyCmd { - return &ZWithKeyCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *ZWithKeyCmd) Val() *ZWithKey { - return cmd.val -} - -func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) { - return cmd.Val(), cmd.Err() -} - -func (cmd *ZWithKeyCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - if n != 3 { - return nil, fmt.Errorf("got %d elements, expected 3", n) - } - - cmd.val = &ZWithKey{} - var err error - - cmd.val.Key, err = rd.ReadString() - if err != nil { - return nil, err - } - - cmd.val.Member, err = rd.ReadString() - if err != nil { - return nil, err - } - - cmd.val.Score, err = rd.ReadFloatReply() - if err != nil { - return nil, err - } - - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type ScanCmd struct { - baseCmd - - page []string - cursor uint64 - - process func(cmd Cmder) error -} - -var _ Cmder = (*ScanCmd)(nil) - -func NewScanCmd(process func(cmd Cmder) error, args ...interface{}) *ScanCmd { - return &ScanCmd{ - baseCmd: baseCmd{args: args}, - process: process, - } -} - -func (cmd *ScanCmd) Val() (keys []string, cursor uint64) { - return cmd.page, cmd.cursor -} - -func (cmd *ScanCmd) Result() (keys []string, cursor uint64, err error) { - return cmd.page, cmd.cursor, cmd.err -} - -func (cmd *ScanCmd) String() string { - return cmdString(cmd, cmd.page) -} - -func (cmd *ScanCmd) readReply(rd *proto.Reader) error { - cmd.page, cmd.cursor, cmd.err = rd.ReadScanReply() - return cmd.err -} - -// Iterator creates a new ScanIterator. -func (cmd *ScanCmd) Iterator() *ScanIterator { - return &ScanIterator{ - cmd: cmd, - } -} - -//------------------------------------------------------------------------------ - -type ClusterNode struct { - ID string - Addr string -} - -type ClusterSlot struct { - Start int - End int - Nodes []ClusterNode -} - -type ClusterSlotsCmd struct { - baseCmd - - val []ClusterSlot -} - -var _ Cmder = (*ClusterSlotsCmd)(nil) - -func NewClusterSlotsCmd(args ...interface{}) *ClusterSlotsCmd { - return &ClusterSlotsCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *ClusterSlotsCmd) Val() []ClusterSlot { - return cmd.val -} - -func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) { - return cmd.Val(), cmd.Err() -} - -func (cmd *ClusterSlotsCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]ClusterSlot, n) - for i := 0; i < len(cmd.val); i++ { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - if n < 2 { - err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n) - return nil, err - } - - start, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - - end, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - - nodes := make([]ClusterNode, n-2) - for j := 0; j < len(nodes); j++ { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - if n != 2 && n != 3 { - err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n) - return nil, err - } - - ip, err := rd.ReadString() - if err != nil { - return nil, err - } - - port, err := rd.ReadString() - if err != nil { - return nil, err - } - - nodes[j].Addr = net.JoinHostPort(ip, port) - - if n == 3 { - id, err := rd.ReadString() - if err != nil { - return nil, err - } - nodes[j].ID = id - } - } - - cmd.val[i] = ClusterSlot{ - Start: int(start), - End: int(end), - Nodes: nodes, - } - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -// GeoLocation is used with GeoAdd to add geospatial location. -type GeoLocation struct { - Name string - Longitude, Latitude, Dist float64 - GeoHash int64 -} - -// GeoRadiusQuery is used with GeoRadius to query geospatial index. -type GeoRadiusQuery struct { - Radius float64 - // Can be m, km, ft, or mi. Default is km. - Unit string - WithCoord bool - WithDist bool - WithGeoHash bool - Count int - // Can be ASC or DESC. Default is no sort order. - Sort string - Store string - StoreDist string -} - -type GeoLocationCmd struct { - baseCmd - - q *GeoRadiusQuery - locations []GeoLocation -} - -var _ Cmder = (*GeoLocationCmd)(nil) - -func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd { - return &GeoLocationCmd{ - baseCmd: baseCmd{args: geoLocationArgs(q, args...)}, - q: q, - } -} - -func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} { - args = append(args, q.Radius) - if q.Unit != "" { - args = append(args, q.Unit) - } else { - args = append(args, "km") - } - if q.WithCoord { - args = append(args, "withcoord") - } - if q.WithDist { - args = append(args, "withdist") - } - if q.WithGeoHash { - args = append(args, "withhash") - } - if q.Count > 0 { - args = append(args, "count", q.Count) - } - if q.Sort != "" { - args = append(args, q.Sort) - } - if q.Store != "" { - args = append(args, "store") - args = append(args, q.Store) - } - if q.StoreDist != "" { - args = append(args, "storedist") - args = append(args, q.StoreDist) - } - return args -} - -func (cmd *GeoLocationCmd) Val() []GeoLocation { - return cmd.locations -} - -func (cmd *GeoLocationCmd) Result() ([]GeoLocation, error) { - return cmd.locations, cmd.err -} - -func (cmd *GeoLocationCmd) String() string { - return cmdString(cmd, cmd.locations) -} - -func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error { - var v interface{} - v, cmd.err = rd.ReadArrayReply(newGeoLocationSliceParser(cmd.q)) - if cmd.err != nil { - return cmd.err - } - cmd.locations = v.([]GeoLocation) - return nil -} - -func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse { - return func(rd *proto.Reader, n int64) (interface{}, error) { - locs := make([]GeoLocation, 0, n) - for i := int64(0); i < n; i++ { - v, err := rd.ReadReply(newGeoLocationParser(q)) - if err != nil { - return nil, err - } - switch vv := v.(type) { - case string: - locs = append(locs, GeoLocation{ - Name: vv, - }) - case *GeoLocation: - //TODO: avoid copying - locs = append(locs, *vv) - default: - return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v) - } - } - return locs, nil - } -} - -func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse { - return func(rd *proto.Reader, n int64) (interface{}, error) { - var loc GeoLocation - var err error - - loc.Name, err = rd.ReadString() - if err != nil { - return nil, err - } - if q.WithDist { - loc.Dist, err = rd.ReadFloatReply() - if err != nil { - return nil, err - } - } - if q.WithGeoHash { - loc.GeoHash, err = rd.ReadIntReply() - if err != nil { - return nil, err - } - } - if q.WithCoord { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - if n != 2 { - return nil, fmt.Errorf("got %d coordinates, expected 2", n) - } - - loc.Longitude, err = rd.ReadFloatReply() - if err != nil { - return nil, err - } - loc.Latitude, err = rd.ReadFloatReply() - if err != nil { - return nil, err - } - } - - return &loc, nil - } -} - -//------------------------------------------------------------------------------ - -type GeoPos struct { - Longitude, Latitude float64 -} - -type GeoPosCmd struct { - baseCmd - - val []*GeoPos -} - -var _ Cmder = (*GeoPosCmd)(nil) - -func NewGeoPosCmd(args ...interface{}) *GeoPosCmd { - return &GeoPosCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *GeoPosCmd) Val() []*GeoPos { - return cmd.val -} - -func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) { - return cmd.Val(), cmd.Err() -} - -func (cmd *GeoPosCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make([]*GeoPos, n) - for i := 0; i < len(cmd.val); i++ { - i := i - _, err := rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) { - longitude, err := rd.ReadFloatReply() - if err != nil { - return nil, err - } - - latitude, err := rd.ReadFloatReply() - if err != nil { - return nil, err - } - - cmd.val[i] = &GeoPos{ - Longitude: longitude, - Latitude: latitude, - } - return nil, nil - }) - if err != nil { - if err == Nil { - cmd.val[i] = nil - continue - } - return nil, err - } - } - return nil, nil - }) - return cmd.err -} - -//------------------------------------------------------------------------------ - -type CommandInfo struct { - Name string - Arity int8 - Flags []string - ACLFlags []string - FirstKeyPos int8 - LastKeyPos int8 - StepCount int8 - ReadOnly bool -} - -type CommandsInfoCmd struct { - baseCmd - - val map[string]*CommandInfo -} - -var _ Cmder = (*CommandsInfoCmd)(nil) - -func NewCommandsInfoCmd(args ...interface{}) *CommandsInfoCmd { - return &CommandsInfoCmd{ - baseCmd: baseCmd{args: args}, - } -} - -func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo { - return cmd.val -} - -func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) { - return cmd.Val(), cmd.Err() -} - -func (cmd *CommandsInfoCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error { - _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.val = make(map[string]*CommandInfo, n) - for i := int64(0); i < n; i++ { - v, err := rd.ReadReply(commandInfoParser) - if err != nil { - return nil, err - } - vv := v.(*CommandInfo) - cmd.val[vv.Name] = vv - } - return nil, nil - }) - return cmd.err -} - -func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) { - const numArgRedis5 = 6 - const numArgRedis6 = 7 - - switch n { - case numArgRedis5, numArgRedis6: - // continue - default: - return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 7", n) - } - - var cmd CommandInfo - var err error - - cmd.Name, err = rd.ReadString() - if err != nil { - return nil, err - } - - arity, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - cmd.Arity = int8(arity) - - _, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.Flags = make([]string, n) - for i := 0; i < len(cmd.Flags); i++ { - switch s, err := rd.ReadString(); { - case err == Nil: - cmd.Flags[i] = "" - case err != nil: - return nil, err - default: - cmd.Flags[i] = s - } - } - return nil, nil - }) - if err != nil { - return nil, err - } - - firstKeyPos, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - cmd.FirstKeyPos = int8(firstKeyPos) - - lastKeyPos, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - cmd.LastKeyPos = int8(lastKeyPos) - - stepCount, err := rd.ReadIntReply() - if err != nil { - return nil, err - } - cmd.StepCount = int8(stepCount) - - for _, flag := range cmd.Flags { - if flag == "readonly" { - cmd.ReadOnly = true - break - } - } - - if n == numArgRedis5 { - return &cmd, nil - } - - _, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) { - cmd.ACLFlags = make([]string, n) - for i := 0; i < len(cmd.ACLFlags); i++ { - switch s, err := rd.ReadString(); { - case err == Nil: - cmd.ACLFlags[i] = "" - case err != nil: - return nil, err - default: - cmd.ACLFlags[i] = s - } - } - return nil, nil - }) - if err != nil { - return nil, err - } - - return &cmd, nil -} - -//------------------------------------------------------------------------------ - -type cmdsInfoCache struct { - fn func() (map[string]*CommandInfo, error) - - once internal.Once - cmds map[string]*CommandInfo -} - -func newCmdsInfoCache(fn func() (map[string]*CommandInfo, error)) *cmdsInfoCache { - return &cmdsInfoCache{ - fn: fn, - } -} - -func (c *cmdsInfoCache) Get() (map[string]*CommandInfo, error) { - err := c.once.Do(func() error { - cmds, err := c.fn() - if err != nil { - return err - } - - // Extensions have cmd names in upper case. Convert them to lower case. - for k, v := range cmds { - lower := internal.ToLower(k) - if lower != k { - cmds[lower] = v - } - } - - c.cmds = cmds - return nil - }) - return c.cmds, err -} diff --git a/vendor/github.com/go-redis/redis/v7/commands.go b/vendor/github.com/go-redis/redis/v7/commands.go deleted file mode 100644 index da5ceda1..00000000 --- a/vendor/github.com/go-redis/redis/v7/commands.go +++ /dev/null @@ -1,2643 +0,0 @@ -package redis - -import ( - "errors" - "io" - "time" - - "github.com/go-redis/redis/v7/internal" -) - -func usePrecise(dur time.Duration) bool { - return dur < time.Second || dur%time.Second != 0 -} - -func formatMs(dur time.Duration) int64 { - if dur > 0 && dur < time.Millisecond { - internal.Logger.Printf( - "specified duration is %s, but minimal supported value is %s", - dur, time.Millisecond, - ) - } - return int64(dur / time.Millisecond) -} - -func formatSec(dur time.Duration) int64 { - if dur > 0 && dur < time.Second { - internal.Logger.Printf( - "specified duration is %s, but minimal supported value is %s", - dur, time.Second, - ) - } - return int64(dur / time.Second) -} - -func appendArgs(dst, src []interface{}) []interface{} { - if len(src) == 1 { - switch v := src[0].(type) { - case []string: - for _, s := range v { - dst = append(dst, s) - } - return dst - case map[string]interface{}: - for k, v := range v { - dst = append(dst, k, v) - } - return dst - } - } - - dst = append(dst, src...) - return dst -} - -type Cmdable interface { - Pipeline() Pipeliner - Pipelined(fn func(Pipeliner) error) ([]Cmder, error) - - TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) - TxPipeline() Pipeliner - - Command() *CommandsInfoCmd - ClientGetName() *StringCmd - Echo(message interface{}) *StringCmd - Ping() *StatusCmd - Quit() *StatusCmd - Del(keys ...string) *IntCmd - Unlink(keys ...string) *IntCmd - Dump(key string) *StringCmd - Exists(keys ...string) *IntCmd - Expire(key string, expiration time.Duration) *BoolCmd - ExpireAt(key string, tm time.Time) *BoolCmd - Keys(pattern string) *StringSliceCmd - Migrate(host, port, key string, db int, timeout time.Duration) *StatusCmd - Move(key string, db int) *BoolCmd - ObjectRefCount(key string) *IntCmd - ObjectEncoding(key string) *StringCmd - ObjectIdleTime(key string) *DurationCmd - Persist(key string) *BoolCmd - PExpire(key string, expiration time.Duration) *BoolCmd - PExpireAt(key string, tm time.Time) *BoolCmd - PTTL(key string) *DurationCmd - RandomKey() *StringCmd - Rename(key, newkey string) *StatusCmd - RenameNX(key, newkey string) *BoolCmd - Restore(key string, ttl time.Duration, value string) *StatusCmd - RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd - Sort(key string, sort *Sort) *StringSliceCmd - SortStore(key, store string, sort *Sort) *IntCmd - SortInterfaces(key string, sort *Sort) *SliceCmd - Touch(keys ...string) *IntCmd - TTL(key string) *DurationCmd - Type(key string) *StatusCmd - Scan(cursor uint64, match string, count int64) *ScanCmd - SScan(key string, cursor uint64, match string, count int64) *ScanCmd - HScan(key string, cursor uint64, match string, count int64) *ScanCmd - ZScan(key string, cursor uint64, match string, count int64) *ScanCmd - Append(key, value string) *IntCmd - BitCount(key string, bitCount *BitCount) *IntCmd - BitOpAnd(destKey string, keys ...string) *IntCmd - BitOpOr(destKey string, keys ...string) *IntCmd - BitOpXor(destKey string, keys ...string) *IntCmd - BitOpNot(destKey string, key string) *IntCmd - BitPos(key string, bit int64, pos ...int64) *IntCmd - BitField(key string, args ...interface{}) *IntSliceCmd - Decr(key string) *IntCmd - DecrBy(key string, decrement int64) *IntCmd - Get(key string) *StringCmd - GetBit(key string, offset int64) *IntCmd - GetRange(key string, start, end int64) *StringCmd - GetSet(key string, value interface{}) *StringCmd - Incr(key string) *IntCmd - IncrBy(key string, value int64) *IntCmd - IncrByFloat(key string, value float64) *FloatCmd - MGet(keys ...string) *SliceCmd - MSet(values ...interface{}) *StatusCmd - MSetNX(values ...interface{}) *BoolCmd - Set(key string, value interface{}, expiration time.Duration) *StatusCmd - SetBit(key string, offset int64, value int) *IntCmd - SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd - SetXX(key string, value interface{}, expiration time.Duration) *BoolCmd - SetRange(key string, offset int64, value string) *IntCmd - StrLen(key string) *IntCmd - HDel(key string, fields ...string) *IntCmd - HExists(key, field string) *BoolCmd - HGet(key, field string) *StringCmd - HGetAll(key string) *StringStringMapCmd - HIncrBy(key, field string, incr int64) *IntCmd - HIncrByFloat(key, field string, incr float64) *FloatCmd - HKeys(key string) *StringSliceCmd - HLen(key string) *IntCmd - HMGet(key string, fields ...string) *SliceCmd - HSet(key string, values ...interface{}) *IntCmd - HMSet(key string, values ...interface{}) *BoolCmd - HSetNX(key, field string, value interface{}) *BoolCmd - HVals(key string) *StringSliceCmd - BLPop(timeout time.Duration, keys ...string) *StringSliceCmd - BRPop(timeout time.Duration, keys ...string) *StringSliceCmd - BRPopLPush(source, destination string, timeout time.Duration) *StringCmd - LIndex(key string, index int64) *StringCmd - LInsert(key, op string, pivot, value interface{}) *IntCmd - LInsertBefore(key string, pivot, value interface{}) *IntCmd - LInsertAfter(key string, pivot, value interface{}) *IntCmd - LLen(key string) *IntCmd - LPop(key string) *StringCmd - LPush(key string, values ...interface{}) *IntCmd - LPushX(key string, values ...interface{}) *IntCmd - LRange(key string, start, stop int64) *StringSliceCmd - LRem(key string, count int64, value interface{}) *IntCmd - LSet(key string, index int64, value interface{}) *StatusCmd - LTrim(key string, start, stop int64) *StatusCmd - RPop(key string) *StringCmd - RPopLPush(source, destination string) *StringCmd - RPush(key string, values ...interface{}) *IntCmd - RPushX(key string, values ...interface{}) *IntCmd - SAdd(key string, members ...interface{}) *IntCmd - SCard(key string) *IntCmd - SDiff(keys ...string) *StringSliceCmd - SDiffStore(destination string, keys ...string) *IntCmd - SInter(keys ...string) *StringSliceCmd - SInterStore(destination string, keys ...string) *IntCmd - SIsMember(key string, member interface{}) *BoolCmd - SMembers(key string) *StringSliceCmd - SMembersMap(key string) *StringStructMapCmd - SMove(source, destination string, member interface{}) *BoolCmd - SPop(key string) *StringCmd - SPopN(key string, count int64) *StringSliceCmd - SRandMember(key string) *StringCmd - SRandMemberN(key string, count int64) *StringSliceCmd - SRem(key string, members ...interface{}) *IntCmd - SUnion(keys ...string) *StringSliceCmd - SUnionStore(destination string, keys ...string) *IntCmd - XAdd(a *XAddArgs) *StringCmd - XDel(stream string, ids ...string) *IntCmd - XLen(stream string) *IntCmd - XRange(stream, start, stop string) *XMessageSliceCmd - XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd - XRevRange(stream string, start, stop string) *XMessageSliceCmd - XRevRangeN(stream string, start, stop string, count int64) *XMessageSliceCmd - XRead(a *XReadArgs) *XStreamSliceCmd - XReadStreams(streams ...string) *XStreamSliceCmd - XGroupCreate(stream, group, start string) *StatusCmd - XGroupCreateMkStream(stream, group, start string) *StatusCmd - XGroupSetID(stream, group, start string) *StatusCmd - XGroupDestroy(stream, group string) *IntCmd - XGroupDelConsumer(stream, group, consumer string) *IntCmd - XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd - XAck(stream, group string, ids ...string) *IntCmd - XPending(stream, group string) *XPendingCmd - XPendingExt(a *XPendingExtArgs) *XPendingExtCmd - XClaim(a *XClaimArgs) *XMessageSliceCmd - XClaimJustID(a *XClaimArgs) *StringSliceCmd - XTrim(key string, maxLen int64) *IntCmd - XTrimApprox(key string, maxLen int64) *IntCmd - XInfoGroups(key string) *XInfoGroupsCmd - BZPopMax(timeout time.Duration, keys ...string) *ZWithKeyCmd - BZPopMin(timeout time.Duration, keys ...string) *ZWithKeyCmd - ZAdd(key string, members ...*Z) *IntCmd - ZAddNX(key string, members ...*Z) *IntCmd - ZAddXX(key string, members ...*Z) *IntCmd - ZAddCh(key string, members ...*Z) *IntCmd - ZAddNXCh(key string, members ...*Z) *IntCmd - ZAddXXCh(key string, members ...*Z) *IntCmd - ZIncr(key string, member *Z) *FloatCmd - ZIncrNX(key string, member *Z) *FloatCmd - ZIncrXX(key string, member *Z) *FloatCmd - ZCard(key string) *IntCmd - ZCount(key, min, max string) *IntCmd - ZLexCount(key, min, max string) *IntCmd - ZIncrBy(key string, increment float64, member string) *FloatCmd - ZInterStore(destination string, store *ZStore) *IntCmd - ZPopMax(key string, count ...int64) *ZSliceCmd - ZPopMin(key string, count ...int64) *ZSliceCmd - ZRange(key string, start, stop int64) *StringSliceCmd - ZRangeWithScores(key string, start, stop int64) *ZSliceCmd - ZRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd - ZRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd - ZRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd - ZRank(key, member string) *IntCmd - ZRem(key string, members ...interface{}) *IntCmd - ZRemRangeByRank(key string, start, stop int64) *IntCmd - ZRemRangeByScore(key, min, max string) *IntCmd - ZRemRangeByLex(key, min, max string) *IntCmd - ZRevRange(key string, start, stop int64) *StringSliceCmd - ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd - ZRevRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd - ZRevRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd - ZRevRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd - ZRevRank(key, member string) *IntCmd - ZScore(key, member string) *FloatCmd - ZUnionStore(dest string, store *ZStore) *IntCmd - PFAdd(key string, els ...interface{}) *IntCmd - PFCount(keys ...string) *IntCmd - PFMerge(dest string, keys ...string) *StatusCmd - BgRewriteAOF() *StatusCmd - BgSave() *StatusCmd - ClientKill(ipPort string) *StatusCmd - ClientKillByFilter(keys ...string) *IntCmd - ClientList() *StringCmd - ClientPause(dur time.Duration) *BoolCmd - ClientID() *IntCmd - ConfigGet(parameter string) *SliceCmd - ConfigResetStat() *StatusCmd - ConfigSet(parameter, value string) *StatusCmd - ConfigRewrite() *StatusCmd - DBSize() *IntCmd - FlushAll() *StatusCmd - FlushAllAsync() *StatusCmd - FlushDB() *StatusCmd - FlushDBAsync() *StatusCmd - Info(section ...string) *StringCmd - LastSave() *IntCmd - Save() *StatusCmd - Shutdown() *StatusCmd - ShutdownSave() *StatusCmd - ShutdownNoSave() *StatusCmd - SlaveOf(host, port string) *StatusCmd - Time() *TimeCmd - Eval(script string, keys []string, args ...interface{}) *Cmd - EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd - ScriptExists(hashes ...string) *BoolSliceCmd - ScriptFlush() *StatusCmd - ScriptKill() *StatusCmd - ScriptLoad(script string) *StringCmd - DebugObject(key string) *StringCmd - Publish(channel string, message interface{}) *IntCmd - PubSubChannels(pattern string) *StringSliceCmd - PubSubNumSub(channels ...string) *StringIntMapCmd - PubSubNumPat() *IntCmd - ClusterSlots() *ClusterSlotsCmd - ClusterNodes() *StringCmd - ClusterMeet(host, port string) *StatusCmd - ClusterForget(nodeID string) *StatusCmd - ClusterReplicate(nodeID string) *StatusCmd - ClusterResetSoft() *StatusCmd - ClusterResetHard() *StatusCmd - ClusterInfo() *StringCmd - ClusterKeySlot(key string) *IntCmd - ClusterGetKeysInSlot(slot int, count int) *StringSliceCmd - ClusterCountFailureReports(nodeID string) *IntCmd - ClusterCountKeysInSlot(slot int) *IntCmd - ClusterDelSlots(slots ...int) *StatusCmd - ClusterDelSlotsRange(min, max int) *StatusCmd - ClusterSaveConfig() *StatusCmd - ClusterSlaves(nodeID string) *StringSliceCmd - ClusterFailover() *StatusCmd - ClusterAddSlots(slots ...int) *StatusCmd - ClusterAddSlotsRange(min, max int) *StatusCmd - GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd - GeoPos(key string, members ...string) *GeoPosCmd - GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd - GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd - GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd - GeoRadiusByMemberStore(key, member string, query *GeoRadiusQuery) *IntCmd - GeoDist(key string, member1, member2, unit string) *FloatCmd - GeoHash(key string, members ...string) *StringSliceCmd - ReadOnly() *StatusCmd - ReadWrite() *StatusCmd - MemoryUsage(key string, samples ...int) *IntCmd -} - -type StatefulCmdable interface { - Cmdable - Auth(password string) *StatusCmd - AuthACL(username, password string) *StatusCmd - Select(index int) *StatusCmd - SwapDB(index1, index2 int) *StatusCmd - ClientSetName(name string) *BoolCmd -} - -var _ Cmdable = (*Client)(nil) -var _ Cmdable = (*Tx)(nil) -var _ Cmdable = (*Ring)(nil) -var _ Cmdable = (*ClusterClient)(nil) - -type cmdable func(cmd Cmder) error - -type statefulCmdable func(cmd Cmder) error - -//------------------------------------------------------------------------------ - -func (c statefulCmdable) Auth(password string) *StatusCmd { - cmd := NewStatusCmd("auth", password) - _ = c(cmd) - return cmd -} - -// Perform an AUTH command, using the given user and pass. -// Should be used to authenticate the current connection with one of the connections defined in the ACL list -// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system. -func (c statefulCmdable) AuthACL(username, password string) *StatusCmd { - cmd := NewStatusCmd("auth", username, password) - _ = c(cmd) - return cmd -} - -func (c cmdable) Echo(message interface{}) *StringCmd { - cmd := NewStringCmd("echo", message) - _ = c(cmd) - return cmd -} - -func (c cmdable) Ping() *StatusCmd { - cmd := NewStatusCmd("ping") - _ = c(cmd) - return cmd -} - -func (c cmdable) Wait(numSlaves int, timeout time.Duration) *IntCmd { - cmd := NewIntCmd("wait", numSlaves, int(timeout/time.Millisecond)) - _ = c(cmd) - return cmd -} - -func (c cmdable) Quit() *StatusCmd { - panic("not implemented") -} - -func (c statefulCmdable) Select(index int) *StatusCmd { - cmd := NewStatusCmd("select", index) - _ = c(cmd) - return cmd -} - -func (c statefulCmdable) SwapDB(index1, index2 int) *StatusCmd { - cmd := NewStatusCmd("swapdb", index1, index2) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) Command() *CommandsInfoCmd { - cmd := NewCommandsInfoCmd("command") - _ = c(cmd) - return cmd -} - -func (c cmdable) Del(keys ...string) *IntCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "del" - for i, key := range keys { - args[1+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) Unlink(keys ...string) *IntCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "unlink" - for i, key := range keys { - args[1+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) Dump(key string) *StringCmd { - cmd := NewStringCmd("dump", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) Exists(keys ...string) *IntCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "exists" - for i, key := range keys { - args[1+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) Expire(key string, expiration time.Duration) *BoolCmd { - cmd := NewBoolCmd("expire", key, formatSec(expiration)) - _ = c(cmd) - return cmd -} - -func (c cmdable) ExpireAt(key string, tm time.Time) *BoolCmd { - cmd := NewBoolCmd("expireat", key, tm.Unix()) - _ = c(cmd) - return cmd -} - -func (c cmdable) Keys(pattern string) *StringSliceCmd { - cmd := NewStringSliceCmd("keys", pattern) - _ = c(cmd) - return cmd -} - -func (c cmdable) Migrate(host, port, key string, db int, timeout time.Duration) *StatusCmd { - cmd := NewStatusCmd( - "migrate", - host, - port, - key, - db, - formatMs(timeout), - ) - cmd.setReadTimeout(timeout) - _ = c(cmd) - return cmd -} - -func (c cmdable) Move(key string, db int) *BoolCmd { - cmd := NewBoolCmd("move", key, db) - _ = c(cmd) - return cmd -} - -func (c cmdable) ObjectRefCount(key string) *IntCmd { - cmd := NewIntCmd("object", "refcount", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) ObjectEncoding(key string) *StringCmd { - cmd := NewStringCmd("object", "encoding", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) ObjectIdleTime(key string) *DurationCmd { - cmd := NewDurationCmd(time.Second, "object", "idletime", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) Persist(key string) *BoolCmd { - cmd := NewBoolCmd("persist", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) PExpire(key string, expiration time.Duration) *BoolCmd { - cmd := NewBoolCmd("pexpire", key, formatMs(expiration)) - _ = c(cmd) - return cmd -} - -func (c cmdable) PExpireAt(key string, tm time.Time) *BoolCmd { - cmd := NewBoolCmd( - "pexpireat", - key, - tm.UnixNano()/int64(time.Millisecond), - ) - _ = c(cmd) - return cmd -} - -func (c cmdable) PTTL(key string) *DurationCmd { - cmd := NewDurationCmd(time.Millisecond, "pttl", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) RandomKey() *StringCmd { - cmd := NewStringCmd("randomkey") - _ = c(cmd) - return cmd -} - -func (c cmdable) Rename(key, newkey string) *StatusCmd { - cmd := NewStatusCmd("rename", key, newkey) - _ = c(cmd) - return cmd -} - -func (c cmdable) RenameNX(key, newkey string) *BoolCmd { - cmd := NewBoolCmd("renamenx", key, newkey) - _ = c(cmd) - return cmd -} - -func (c cmdable) Restore(key string, ttl time.Duration, value string) *StatusCmd { - cmd := NewStatusCmd( - "restore", - key, - formatMs(ttl), - value, - ) - _ = c(cmd) - return cmd -} - -func (c cmdable) RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd { - cmd := NewStatusCmd( - "restore", - key, - formatMs(ttl), - value, - "replace", - ) - _ = c(cmd) - return cmd -} - -type Sort struct { - By string - Offset, Count int64 - Get []string - Order string - Alpha bool -} - -func (sort *Sort) args(key string) []interface{} { - args := []interface{}{"sort", key} - if sort.By != "" { - args = append(args, "by", sort.By) - } - if sort.Offset != 0 || sort.Count != 0 { - args = append(args, "limit", sort.Offset, sort.Count) - } - for _, get := range sort.Get { - args = append(args, "get", get) - } - if sort.Order != "" { - args = append(args, sort.Order) - } - if sort.Alpha { - args = append(args, "alpha") - } - return args -} - -func (c cmdable) Sort(key string, sort *Sort) *StringSliceCmd { - cmd := NewStringSliceCmd(sort.args(key)...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SortStore(key, store string, sort *Sort) *IntCmd { - args := sort.args(key) - if store != "" { - args = append(args, "store", store) - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SortInterfaces(key string, sort *Sort) *SliceCmd { - cmd := NewSliceCmd(sort.args(key)...) - _ = c(cmd) - return cmd -} - -func (c cmdable) Touch(keys ...string) *IntCmd { - args := make([]interface{}, len(keys)+1) - args[0] = "touch" - for i, key := range keys { - args[i+1] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) TTL(key string) *DurationCmd { - cmd := NewDurationCmd(time.Second, "ttl", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) Type(key string) *StatusCmd { - cmd := NewStatusCmd("type", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) Scan(cursor uint64, match string, count int64) *ScanCmd { - args := []interface{}{"scan", cursor} - if match != "" { - args = append(args, "match", match) - } - if count > 0 { - args = append(args, "count", count) - } - cmd := NewScanCmd(c, args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SScan(key string, cursor uint64, match string, count int64) *ScanCmd { - args := []interface{}{"sscan", key, cursor} - if match != "" { - args = append(args, "match", match) - } - if count > 0 { - args = append(args, "count", count) - } - cmd := NewScanCmd(c, args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) HScan(key string, cursor uint64, match string, count int64) *ScanCmd { - args := []interface{}{"hscan", key, cursor} - if match != "" { - args = append(args, "match", match) - } - if count > 0 { - args = append(args, "count", count) - } - cmd := NewScanCmd(c, args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZScan(key string, cursor uint64, match string, count int64) *ScanCmd { - args := []interface{}{"zscan", key, cursor} - if match != "" { - args = append(args, "match", match) - } - if count > 0 { - args = append(args, "count", count) - } - cmd := NewScanCmd(c, args...) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) Append(key, value string) *IntCmd { - cmd := NewIntCmd("append", key, value) - _ = c(cmd) - return cmd -} - -type BitCount struct { - Start, End int64 -} - -func (c cmdable) BitCount(key string, bitCount *BitCount) *IntCmd { - args := []interface{}{"bitcount", key} - if bitCount != nil { - args = append( - args, - bitCount.Start, - bitCount.End, - ) - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) bitOp(op, destKey string, keys ...string) *IntCmd { - args := make([]interface{}, 3+len(keys)) - args[0] = "bitop" - args[1] = op - args[2] = destKey - for i, key := range keys { - args[3+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) BitOpAnd(destKey string, keys ...string) *IntCmd { - return c.bitOp("and", destKey, keys...) -} - -func (c cmdable) BitOpOr(destKey string, keys ...string) *IntCmd { - return c.bitOp("or", destKey, keys...) -} - -func (c cmdable) BitOpXor(destKey string, keys ...string) *IntCmd { - return c.bitOp("xor", destKey, keys...) -} - -func (c cmdable) BitOpNot(destKey string, key string) *IntCmd { - return c.bitOp("not", destKey, key) -} - -func (c cmdable) BitPos(key string, bit int64, pos ...int64) *IntCmd { - args := make([]interface{}, 3+len(pos)) - args[0] = "bitpos" - args[1] = key - args[2] = bit - switch len(pos) { - case 0: - case 1: - args[3] = pos[0] - case 2: - args[3] = pos[0] - args[4] = pos[1] - default: - panic("too many arguments") - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) BitField(key string, args ...interface{}) *IntSliceCmd { - a := make([]interface{}, 0, 2+len(args)) - a = append(a, "bitfield") - a = append(a, key) - a = append(a, args...) - cmd := NewIntSliceCmd(a...) - _ = c(cmd) - return cmd -} - -func (c cmdable) Decr(key string) *IntCmd { - cmd := NewIntCmd("decr", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) DecrBy(key string, decrement int64) *IntCmd { - cmd := NewIntCmd("decrby", key, decrement) - _ = c(cmd) - return cmd -} - -// Redis `GET key` command. It returns redis.Nil error when key does not exist. -func (c cmdable) Get(key string) *StringCmd { - cmd := NewStringCmd("get", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) GetBit(key string, offset int64) *IntCmd { - cmd := NewIntCmd("getbit", key, offset) - _ = c(cmd) - return cmd -} - -func (c cmdable) GetRange(key string, start, end int64) *StringCmd { - cmd := NewStringCmd("getrange", key, start, end) - _ = c(cmd) - return cmd -} - -func (c cmdable) GetSet(key string, value interface{}) *StringCmd { - cmd := NewStringCmd("getset", key, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) Incr(key string) *IntCmd { - cmd := NewIntCmd("incr", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) IncrBy(key string, value int64) *IntCmd { - cmd := NewIntCmd("incrby", key, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) IncrByFloat(key string, value float64) *FloatCmd { - cmd := NewFloatCmd("incrbyfloat", key, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) MGet(keys ...string) *SliceCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "mget" - for i, key := range keys { - args[1+i] = key - } - cmd := NewSliceCmd(args...) - _ = c(cmd) - return cmd -} - -// MSet is like Set but accepts multiple values: -// - MSet("key1", "value1", "key2", "value2") -// - MSet([]string{"key1", "value1", "key2", "value2"}) -// - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"}) -func (c cmdable) MSet(values ...interface{}) *StatusCmd { - args := make([]interface{}, 1, 1+len(values)) - args[0] = "mset" - args = appendArgs(args, values) - cmd := NewStatusCmd(args...) - _ = c(cmd) - return cmd -} - -// MSetNX is like SetNX but accepts multiple values: -// - MSetNX("key1", "value1", "key2", "value2") -// - MSetNX([]string{"key1", "value1", "key2", "value2"}) -// - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"}) -func (c cmdable) MSetNX(values ...interface{}) *BoolCmd { - args := make([]interface{}, 1, 1+len(values)) - args[0] = "msetnx" - args = appendArgs(args, values) - cmd := NewBoolCmd(args...) - _ = c(cmd) - return cmd -} - -// Redis `SET key value [expiration]` command. -// -// Use expiration for `SETEX`-like behavior. -// Zero expiration means the key has no expiration time. -func (c cmdable) Set(key string, value interface{}, expiration time.Duration) *StatusCmd { - args := make([]interface{}, 3, 5) - args[0] = "set" - args[1] = key - args[2] = value - if expiration > 0 { - if usePrecise(expiration) { - args = append(args, "px", formatMs(expiration)) - } else { - args = append(args, "ex", formatSec(expiration)) - } - } - cmd := NewStatusCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SetBit(key string, offset int64, value int) *IntCmd { - cmd := NewIntCmd( - "setbit", - key, - offset, - value, - ) - _ = c(cmd) - return cmd -} - -// Redis `SET key value [expiration] NX` command. -// -// Zero expiration means the key has no expiration time. -func (c cmdable) SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd { - var cmd *BoolCmd - if expiration == 0 { - // Use old `SETNX` to support old Redis versions. - cmd = NewBoolCmd("setnx", key, value) - } else { - if usePrecise(expiration) { - cmd = NewBoolCmd("set", key, value, "px", formatMs(expiration), "nx") - } else { - cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "nx") - } - } - _ = c(cmd) - return cmd -} - -// Redis `SET key value [expiration] XX` command. -// -// Zero expiration means the key has no expiration time. -func (c cmdable) SetXX(key string, value interface{}, expiration time.Duration) *BoolCmd { - var cmd *BoolCmd - if expiration == 0 { - cmd = NewBoolCmd("set", key, value, "xx") - } else { - if usePrecise(expiration) { - cmd = NewBoolCmd("set", key, value, "px", formatMs(expiration), "xx") - } else { - cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "xx") - } - } - _ = c(cmd) - return cmd -} - -func (c cmdable) SetRange(key string, offset int64, value string) *IntCmd { - cmd := NewIntCmd("setrange", key, offset, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) StrLen(key string) *IntCmd { - cmd := NewIntCmd("strlen", key) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) HDel(key string, fields ...string) *IntCmd { - args := make([]interface{}, 2+len(fields)) - args[0] = "hdel" - args[1] = key - for i, field := range fields { - args[2+i] = field - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) HExists(key, field string) *BoolCmd { - cmd := NewBoolCmd("hexists", key, field) - _ = c(cmd) - return cmd -} - -func (c cmdable) HGet(key, field string) *StringCmd { - cmd := NewStringCmd("hget", key, field) - _ = c(cmd) - return cmd -} - -func (c cmdable) HGetAll(key string) *StringStringMapCmd { - cmd := NewStringStringMapCmd("hgetall", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) HIncrBy(key, field string, incr int64) *IntCmd { - cmd := NewIntCmd("hincrby", key, field, incr) - _ = c(cmd) - return cmd -} - -func (c cmdable) HIncrByFloat(key, field string, incr float64) *FloatCmd { - cmd := NewFloatCmd("hincrbyfloat", key, field, incr) - _ = c(cmd) - return cmd -} - -func (c cmdable) HKeys(key string) *StringSliceCmd { - cmd := NewStringSliceCmd("hkeys", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) HLen(key string) *IntCmd { - cmd := NewIntCmd("hlen", key) - _ = c(cmd) - return cmd -} - -// HMGet returns the values for the specified fields in the hash stored at key. -// It returns an interface{} to distinguish between empty string and nil value. -func (c cmdable) HMGet(key string, fields ...string) *SliceCmd { - args := make([]interface{}, 2+len(fields)) - args[0] = "hmget" - args[1] = key - for i, field := range fields { - args[2+i] = field - } - cmd := NewSliceCmd(args...) - _ = c(cmd) - return cmd -} - -// HSet accepts values in following formats: -// - HMSet("myhash", "key1", "value1", "key2", "value2") -// - HMSet("myhash", []string{"key1", "value1", "key2", "value2"}) -// - HMSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"}) -// -// Note that it requires Redis v4 for multiple field/value pairs support. -func (c cmdable) HSet(key string, values ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "hset" - args[1] = key - args = appendArgs(args, values) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -// HMSet is a deprecated version of HSet left for compatibility with Redis 3. -func (c cmdable) HMSet(key string, values ...interface{}) *BoolCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "hmset" - args[1] = key - args = appendArgs(args, values) - cmd := NewBoolCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) HSetNX(key, field string, value interface{}) *BoolCmd { - cmd := NewBoolCmd("hsetnx", key, field, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) HVals(key string) *StringSliceCmd { - cmd := NewStringSliceCmd("hvals", key) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) BLPop(timeout time.Duration, keys ...string) *StringSliceCmd { - args := make([]interface{}, 1+len(keys)+1) - args[0] = "blpop" - for i, key := range keys { - args[1+i] = key - } - args[len(args)-1] = formatSec(timeout) - cmd := NewStringSliceCmd(args...) - cmd.setReadTimeout(timeout) - _ = c(cmd) - return cmd -} - -func (c cmdable) BRPop(timeout time.Duration, keys ...string) *StringSliceCmd { - args := make([]interface{}, 1+len(keys)+1) - args[0] = "brpop" - for i, key := range keys { - args[1+i] = key - } - args[len(keys)+1] = formatSec(timeout) - cmd := NewStringSliceCmd(args...) - cmd.setReadTimeout(timeout) - _ = c(cmd) - return cmd -} - -func (c cmdable) BRPopLPush(source, destination string, timeout time.Duration) *StringCmd { - cmd := NewStringCmd( - "brpoplpush", - source, - destination, - formatSec(timeout), - ) - cmd.setReadTimeout(timeout) - _ = c(cmd) - return cmd -} - -func (c cmdable) LIndex(key string, index int64) *StringCmd { - cmd := NewStringCmd("lindex", key, index) - _ = c(cmd) - return cmd -} - -func (c cmdable) LInsert(key, op string, pivot, value interface{}) *IntCmd { - cmd := NewIntCmd("linsert", key, op, pivot, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) LInsertBefore(key string, pivot, value interface{}) *IntCmd { - cmd := NewIntCmd("linsert", key, "before", pivot, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) LInsertAfter(key string, pivot, value interface{}) *IntCmd { - cmd := NewIntCmd("linsert", key, "after", pivot, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) LLen(key string) *IntCmd { - cmd := NewIntCmd("llen", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) LPop(key string) *StringCmd { - cmd := NewStringCmd("lpop", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) LPush(key string, values ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "lpush" - args[1] = key - args = appendArgs(args, values) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) LPushX(key string, values ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "lpushx" - args[1] = key - args = appendArgs(args, values) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) LRange(key string, start, stop int64) *StringSliceCmd { - cmd := NewStringSliceCmd( - "lrange", - key, - start, - stop, - ) - _ = c(cmd) - return cmd -} - -func (c cmdable) LRem(key string, count int64, value interface{}) *IntCmd { - cmd := NewIntCmd("lrem", key, count, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) LSet(key string, index int64, value interface{}) *StatusCmd { - cmd := NewStatusCmd("lset", key, index, value) - _ = c(cmd) - return cmd -} - -func (c cmdable) LTrim(key string, start, stop int64) *StatusCmd { - cmd := NewStatusCmd( - "ltrim", - key, - start, - stop, - ) - _ = c(cmd) - return cmd -} - -func (c cmdable) RPop(key string) *StringCmd { - cmd := NewStringCmd("rpop", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) RPopLPush(source, destination string) *StringCmd { - cmd := NewStringCmd("rpoplpush", source, destination) - _ = c(cmd) - return cmd -} - -func (c cmdable) RPush(key string, values ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "rpush" - args[1] = key - args = appendArgs(args, values) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) RPushX(key string, values ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "rpushx" - args[1] = key - args = appendArgs(args, values) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) SAdd(key string, members ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(members)) - args[0] = "sadd" - args[1] = key - args = appendArgs(args, members) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SCard(key string) *IntCmd { - cmd := NewIntCmd("scard", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) SDiff(keys ...string) *StringSliceCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "sdiff" - for i, key := range keys { - args[1+i] = key - } - cmd := NewStringSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SDiffStore(destination string, keys ...string) *IntCmd { - args := make([]interface{}, 2+len(keys)) - args[0] = "sdiffstore" - args[1] = destination - for i, key := range keys { - args[2+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SInter(keys ...string) *StringSliceCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "sinter" - for i, key := range keys { - args[1+i] = key - } - cmd := NewStringSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SInterStore(destination string, keys ...string) *IntCmd { - args := make([]interface{}, 2+len(keys)) - args[0] = "sinterstore" - args[1] = destination - for i, key := range keys { - args[2+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SIsMember(key string, member interface{}) *BoolCmd { - cmd := NewBoolCmd("sismember", key, member) - _ = c(cmd) - return cmd -} - -// Redis `SMEMBERS key` command output as a slice -func (c cmdable) SMembers(key string) *StringSliceCmd { - cmd := NewStringSliceCmd("smembers", key) - _ = c(cmd) - return cmd -} - -// Redis `SMEMBERS key` command output as a map -func (c cmdable) SMembersMap(key string) *StringStructMapCmd { - cmd := NewStringStructMapCmd("smembers", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) SMove(source, destination string, member interface{}) *BoolCmd { - cmd := NewBoolCmd("smove", source, destination, member) - _ = c(cmd) - return cmd -} - -// Redis `SPOP key` command. -func (c cmdable) SPop(key string) *StringCmd { - cmd := NewStringCmd("spop", key) - _ = c(cmd) - return cmd -} - -// Redis `SPOP key count` command. -func (c cmdable) SPopN(key string, count int64) *StringSliceCmd { - cmd := NewStringSliceCmd("spop", key, count) - _ = c(cmd) - return cmd -} - -// Redis `SRANDMEMBER key` command. -func (c cmdable) SRandMember(key string) *StringCmd { - cmd := NewStringCmd("srandmember", key) - _ = c(cmd) - return cmd -} - -// Redis `SRANDMEMBER key count` command. -func (c cmdable) SRandMemberN(key string, count int64) *StringSliceCmd { - cmd := NewStringSliceCmd("srandmember", key, count) - _ = c(cmd) - return cmd -} - -func (c cmdable) SRem(key string, members ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(members)) - args[0] = "srem" - args[1] = key - args = appendArgs(args, members) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SUnion(keys ...string) *StringSliceCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "sunion" - for i, key := range keys { - args[1+i] = key - } - cmd := NewStringSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) SUnionStore(destination string, keys ...string) *IntCmd { - args := make([]interface{}, 2+len(keys)) - args[0] = "sunionstore" - args[1] = destination - for i, key := range keys { - args[2+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -type XAddArgs struct { - Stream string - MaxLen int64 // MAXLEN N - MaxLenApprox int64 // MAXLEN ~ N - ID string - Values map[string]interface{} -} - -func (c cmdable) XAdd(a *XAddArgs) *StringCmd { - args := make([]interface{}, 0, 6+len(a.Values)*2) - args = append(args, "xadd") - args = append(args, a.Stream) - if a.MaxLen > 0 { - args = append(args, "maxlen", a.MaxLen) - } else if a.MaxLenApprox > 0 { - args = append(args, "maxlen", "~", a.MaxLenApprox) - } - if a.ID != "" { - args = append(args, a.ID) - } else { - args = append(args, "*") - } - for k, v := range a.Values { - args = append(args, k) - args = append(args, v) - } - - cmd := NewStringCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) XDel(stream string, ids ...string) *IntCmd { - args := []interface{}{"xdel", stream} - for _, id := range ids { - args = append(args, id) - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) XLen(stream string) *IntCmd { - cmd := NewIntCmd("xlen", stream) - _ = c(cmd) - return cmd -} - -func (c cmdable) XRange(stream, start, stop string) *XMessageSliceCmd { - cmd := NewXMessageSliceCmd("xrange", stream, start, stop) - _ = c(cmd) - return cmd -} - -func (c cmdable) XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd { - cmd := NewXMessageSliceCmd("xrange", stream, start, stop, "count", count) - _ = c(cmd) - return cmd -} - -func (c cmdable) XRevRange(stream, start, stop string) *XMessageSliceCmd { - cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop) - _ = c(cmd) - return cmd -} - -func (c cmdable) XRevRangeN(stream, start, stop string, count int64) *XMessageSliceCmd { - cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop, "count", count) - _ = c(cmd) - return cmd -} - -type XReadArgs struct { - Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2 - Count int64 - Block time.Duration -} - -func (c cmdable) XRead(a *XReadArgs) *XStreamSliceCmd { - args := make([]interface{}, 0, 5+len(a.Streams)) - args = append(args, "xread") - if a.Count > 0 { - args = append(args, "count") - args = append(args, a.Count) - } - if a.Block >= 0 { - args = append(args, "block") - args = append(args, int64(a.Block/time.Millisecond)) - } - - args = append(args, "streams") - for _, s := range a.Streams { - args = append(args, s) - } - - cmd := NewXStreamSliceCmd(args...) - if a.Block >= 0 { - cmd.setReadTimeout(a.Block) - } - _ = c(cmd) - return cmd -} - -func (c cmdable) XReadStreams(streams ...string) *XStreamSliceCmd { - return c.XRead(&XReadArgs{ - Streams: streams, - Block: -1, - }) -} - -func (c cmdable) XGroupCreate(stream, group, start string) *StatusCmd { - cmd := NewStatusCmd("xgroup", "create", stream, group, start) - _ = c(cmd) - return cmd -} - -func (c cmdable) XGroupCreateMkStream(stream, group, start string) *StatusCmd { - cmd := NewStatusCmd("xgroup", "create", stream, group, start, "mkstream") - _ = c(cmd) - return cmd -} - -func (c cmdable) XGroupSetID(stream, group, start string) *StatusCmd { - cmd := NewStatusCmd("xgroup", "setid", stream, group, start) - _ = c(cmd) - return cmd -} - -func (c cmdable) XGroupDestroy(stream, group string) *IntCmd { - cmd := NewIntCmd("xgroup", "destroy", stream, group) - _ = c(cmd) - return cmd -} - -func (c cmdable) XGroupDelConsumer(stream, group, consumer string) *IntCmd { - cmd := NewIntCmd("xgroup", "delconsumer", stream, group, consumer) - _ = c(cmd) - return cmd -} - -type XReadGroupArgs struct { - Group string - Consumer string - Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2 - Count int64 - Block time.Duration - NoAck bool -} - -func (c cmdable) XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd { - args := make([]interface{}, 0, 8+len(a.Streams)) - args = append(args, "xreadgroup", "group", a.Group, a.Consumer) - if a.Count > 0 { - args = append(args, "count", a.Count) - } - if a.Block >= 0 { - args = append(args, "block", int64(a.Block/time.Millisecond)) - } - if a.NoAck { - args = append(args, "noack") - } - args = append(args, "streams") - for _, s := range a.Streams { - args = append(args, s) - } - - cmd := NewXStreamSliceCmd(args...) - if a.Block >= 0 { - cmd.setReadTimeout(a.Block) - } - _ = c(cmd) - return cmd -} - -func (c cmdable) XAck(stream, group string, ids ...string) *IntCmd { - args := []interface{}{"xack", stream, group} - for _, id := range ids { - args = append(args, id) - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) XPending(stream, group string) *XPendingCmd { - cmd := NewXPendingCmd("xpending", stream, group) - _ = c(cmd) - return cmd -} - -type XPendingExtArgs struct { - Stream string - Group string - Start string - End string - Count int64 - Consumer string -} - -func (c cmdable) XPendingExt(a *XPendingExtArgs) *XPendingExtCmd { - args := make([]interface{}, 0, 7) - args = append(args, "xpending", a.Stream, a.Group, a.Start, a.End, a.Count) - if a.Consumer != "" { - args = append(args, a.Consumer) - } - cmd := NewXPendingExtCmd(args...) - _ = c(cmd) - return cmd -} - -type XClaimArgs struct { - Stream string - Group string - Consumer string - MinIdle time.Duration - Messages []string -} - -func (c cmdable) XClaim(a *XClaimArgs) *XMessageSliceCmd { - args := xClaimArgs(a) - cmd := NewXMessageSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) XClaimJustID(a *XClaimArgs) *StringSliceCmd { - args := xClaimArgs(a) - args = append(args, "justid") - cmd := NewStringSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func xClaimArgs(a *XClaimArgs) []interface{} { - args := make([]interface{}, 0, 4+len(a.Messages)) - args = append(args, - "xclaim", - a.Stream, - a.Group, a.Consumer, - int64(a.MinIdle/time.Millisecond)) - for _, id := range a.Messages { - args = append(args, id) - } - return args -} - -func (c cmdable) XTrim(key string, maxLen int64) *IntCmd { - cmd := NewIntCmd("xtrim", key, "maxlen", maxLen) - _ = c(cmd) - return cmd -} - -func (c cmdable) XTrimApprox(key string, maxLen int64) *IntCmd { - cmd := NewIntCmd("xtrim", key, "maxlen", "~", maxLen) - _ = c(cmd) - return cmd -} - -func (c cmdable) XInfoGroups(key string) *XInfoGroupsCmd { - cmd := NewXInfoGroupsCmd(key) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -// Z represents sorted set member. -type Z struct { - Score float64 - Member interface{} -} - -// ZWithKey represents sorted set member including the name of the key where it was popped. -type ZWithKey struct { - Z - Key string -} - -// ZStore is used as an arg to ZInterStore and ZUnionStore. -type ZStore struct { - Keys []string - Weights []float64 - // Can be SUM, MIN or MAX. - Aggregate string -} - -// Redis `BZPOPMAX key [key ...] timeout` command. -func (c cmdable) BZPopMax(timeout time.Duration, keys ...string) *ZWithKeyCmd { - args := make([]interface{}, 1+len(keys)+1) - args[0] = "bzpopmax" - for i, key := range keys { - args[1+i] = key - } - args[len(args)-1] = formatSec(timeout) - cmd := NewZWithKeyCmd(args...) - cmd.setReadTimeout(timeout) - _ = c(cmd) - return cmd -} - -// Redis `BZPOPMIN key [key ...] timeout` command. -func (c cmdable) BZPopMin(timeout time.Duration, keys ...string) *ZWithKeyCmd { - args := make([]interface{}, 1+len(keys)+1) - args[0] = "bzpopmin" - for i, key := range keys { - args[1+i] = key - } - args[len(args)-1] = formatSec(timeout) - cmd := NewZWithKeyCmd(args...) - cmd.setReadTimeout(timeout) - _ = c(cmd) - return cmd -} - -func (c cmdable) zAdd(a []interface{}, n int, members ...*Z) *IntCmd { - for i, m := range members { - a[n+2*i] = m.Score - a[n+2*i+1] = m.Member - } - cmd := NewIntCmd(a...) - _ = c(cmd) - return cmd -} - -// Redis `ZADD key score member [score member ...]` command. -func (c cmdable) ZAdd(key string, members ...*Z) *IntCmd { - const n = 2 - a := make([]interface{}, n+2*len(members)) - a[0], a[1] = "zadd", key - return c.zAdd(a, n, members...) -} - -// Redis `ZADD key NX score member [score member ...]` command. -func (c cmdable) ZAddNX(key string, members ...*Z) *IntCmd { - const n = 3 - a := make([]interface{}, n+2*len(members)) - a[0], a[1], a[2] = "zadd", key, "nx" - return c.zAdd(a, n, members...) -} - -// Redis `ZADD key XX score member [score member ...]` command. -func (c cmdable) ZAddXX(key string, members ...*Z) *IntCmd { - const n = 3 - a := make([]interface{}, n+2*len(members)) - a[0], a[1], a[2] = "zadd", key, "xx" - return c.zAdd(a, n, members...) -} - -// Redis `ZADD key CH score member [score member ...]` command. -func (c cmdable) ZAddCh(key string, members ...*Z) *IntCmd { - const n = 3 - a := make([]interface{}, n+2*len(members)) - a[0], a[1], a[2] = "zadd", key, "ch" - return c.zAdd(a, n, members...) -} - -// Redis `ZADD key NX CH score member [score member ...]` command. -func (c cmdable) ZAddNXCh(key string, members ...*Z) *IntCmd { - const n = 4 - a := make([]interface{}, n+2*len(members)) - a[0], a[1], a[2], a[3] = "zadd", key, "nx", "ch" - return c.zAdd(a, n, members...) -} - -// Redis `ZADD key XX CH score member [score member ...]` command. -func (c cmdable) ZAddXXCh(key string, members ...*Z) *IntCmd { - const n = 4 - a := make([]interface{}, n+2*len(members)) - a[0], a[1], a[2], a[3] = "zadd", key, "xx", "ch" - return c.zAdd(a, n, members...) -} - -func (c cmdable) zIncr(a []interface{}, n int, members ...*Z) *FloatCmd { - for i, m := range members { - a[n+2*i] = m.Score - a[n+2*i+1] = m.Member - } - cmd := NewFloatCmd(a...) - _ = c(cmd) - return cmd -} - -// Redis `ZADD key INCR score member` command. -func (c cmdable) ZIncr(key string, member *Z) *FloatCmd { - const n = 3 - a := make([]interface{}, n+2) - a[0], a[1], a[2] = "zadd", key, "incr" - return c.zIncr(a, n, member) -} - -// Redis `ZADD key NX INCR score member` command. -func (c cmdable) ZIncrNX(key string, member *Z) *FloatCmd { - const n = 4 - a := make([]interface{}, n+2) - a[0], a[1], a[2], a[3] = "zadd", key, "incr", "nx" - return c.zIncr(a, n, member) -} - -// Redis `ZADD key XX INCR score member` command. -func (c cmdable) ZIncrXX(key string, member *Z) *FloatCmd { - const n = 4 - a := make([]interface{}, n+2) - a[0], a[1], a[2], a[3] = "zadd", key, "incr", "xx" - return c.zIncr(a, n, member) -} - -func (c cmdable) ZCard(key string) *IntCmd { - cmd := NewIntCmd("zcard", key) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZCount(key, min, max string) *IntCmd { - cmd := NewIntCmd("zcount", key, min, max) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZLexCount(key, min, max string) *IntCmd { - cmd := NewIntCmd("zlexcount", key, min, max) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZIncrBy(key string, increment float64, member string) *FloatCmd { - cmd := NewFloatCmd("zincrby", key, increment, member) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZInterStore(destination string, store *ZStore) *IntCmd { - args := make([]interface{}, 3+len(store.Keys)) - args[0] = "zinterstore" - args[1] = destination - args[2] = len(store.Keys) - for i, key := range store.Keys { - args[3+i] = key - } - if len(store.Weights) > 0 { - args = append(args, "weights") - for _, weight := range store.Weights { - args = append(args, weight) - } - } - if store.Aggregate != "" { - args = append(args, "aggregate", store.Aggregate) - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZPopMax(key string, count ...int64) *ZSliceCmd { - args := []interface{}{ - "zpopmax", - key, - } - - switch len(count) { - case 0: - break - case 1: - args = append(args, count[0]) - default: - panic("too many arguments") - } - - cmd := NewZSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZPopMin(key string, count ...int64) *ZSliceCmd { - args := []interface{}{ - "zpopmin", - key, - } - - switch len(count) { - case 0: - break - case 1: - args = append(args, count[0]) - default: - panic("too many arguments") - } - - cmd := NewZSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) zRange(key string, start, stop int64, withScores bool) *StringSliceCmd { - args := []interface{}{ - "zrange", - key, - start, - stop, - } - if withScores { - args = append(args, "withscores") - } - cmd := NewStringSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRange(key string, start, stop int64) *StringSliceCmd { - return c.zRange(key, start, stop, false) -} - -func (c cmdable) ZRangeWithScores(key string, start, stop int64) *ZSliceCmd { - cmd := NewZSliceCmd("zrange", key, start, stop, "withscores") - _ = c(cmd) - return cmd -} - -type ZRangeBy struct { - Min, Max string - Offset, Count int64 -} - -func (c cmdable) zRangeBy(zcmd, key string, opt *ZRangeBy, withScores bool) *StringSliceCmd { - args := []interface{}{zcmd, key, opt.Min, opt.Max} - if withScores { - args = append(args, "withscores") - } - if opt.Offset != 0 || opt.Count != 0 { - args = append( - args, - "limit", - opt.Offset, - opt.Count, - ) - } - cmd := NewStringSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd { - return c.zRangeBy("zrangebyscore", key, opt, false) -} - -func (c cmdable) ZRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd { - return c.zRangeBy("zrangebylex", key, opt, false) -} - -func (c cmdable) ZRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd { - args := []interface{}{"zrangebyscore", key, opt.Min, opt.Max, "withscores"} - if opt.Offset != 0 || opt.Count != 0 { - args = append( - args, - "limit", - opt.Offset, - opt.Count, - ) - } - cmd := NewZSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRank(key, member string) *IntCmd { - cmd := NewIntCmd("zrank", key, member) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRem(key string, members ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(members)) - args[0] = "zrem" - args[1] = key - args = appendArgs(args, members) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRemRangeByRank(key string, start, stop int64) *IntCmd { - cmd := NewIntCmd( - "zremrangebyrank", - key, - start, - stop, - ) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRemRangeByScore(key, min, max string) *IntCmd { - cmd := NewIntCmd("zremrangebyscore", key, min, max) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRemRangeByLex(key, min, max string) *IntCmd { - cmd := NewIntCmd("zremrangebylex", key, min, max) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRevRange(key string, start, stop int64) *StringSliceCmd { - cmd := NewStringSliceCmd("zrevrange", key, start, stop) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd { - cmd := NewZSliceCmd("zrevrange", key, start, stop, "withscores") - _ = c(cmd) - return cmd -} - -func (c cmdable) zRevRangeBy(zcmd, key string, opt *ZRangeBy) *StringSliceCmd { - args := []interface{}{zcmd, key, opt.Max, opt.Min} - if opt.Offset != 0 || opt.Count != 0 { - args = append( - args, - "limit", - opt.Offset, - opt.Count, - ) - } - cmd := NewStringSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRevRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd { - return c.zRevRangeBy("zrevrangebyscore", key, opt) -} - -func (c cmdable) ZRevRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd { - return c.zRevRangeBy("zrevrangebylex", key, opt) -} - -func (c cmdable) ZRevRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd { - args := []interface{}{"zrevrangebyscore", key, opt.Max, opt.Min, "withscores"} - if opt.Offset != 0 || opt.Count != 0 { - args = append( - args, - "limit", - opt.Offset, - opt.Count, - ) - } - cmd := NewZSliceCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZRevRank(key, member string) *IntCmd { - cmd := NewIntCmd("zrevrank", key, member) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZScore(key, member string) *FloatCmd { - cmd := NewFloatCmd("zscore", key, member) - _ = c(cmd) - return cmd -} - -func (c cmdable) ZUnionStore(dest string, store *ZStore) *IntCmd { - args := make([]interface{}, 3+len(store.Keys)) - args[0] = "zunionstore" - args[1] = dest - args[2] = len(store.Keys) - for i, key := range store.Keys { - args[3+i] = key - } - if len(store.Weights) > 0 { - args = append(args, "weights") - for _, weight := range store.Weights { - args = append(args, weight) - } - } - if store.Aggregate != "" { - args = append(args, "aggregate", store.Aggregate) - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) PFAdd(key string, els ...interface{}) *IntCmd { - args := make([]interface{}, 2, 2+len(els)) - args[0] = "pfadd" - args[1] = key - args = appendArgs(args, els) - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) PFCount(keys ...string) *IntCmd { - args := make([]interface{}, 1+len(keys)) - args[0] = "pfcount" - for i, key := range keys { - args[1+i] = key - } - cmd := NewIntCmd(args...) - _ = c(cmd) - return cmd -} - -func (c cmdable) PFMerge(dest string, keys ...string) *StatusCmd { - args := make([]interface{}, 2+len(keys)) - args[0] = "pfmerge" - args[1] = dest - for i, key := range keys { - args[2+i] = key - } - cmd := NewStatusCmd(args...) - _ = c(cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) BgRewriteAOF() *StatusCmd { - cmd := NewStatusCmd("bgrewriteaof") - _ = c(cmd) - return cmd -} - -func (c cmdable) BgSave() *StatusCmd { - cmd := NewStatusCmd("bgsave") - _ = c(cmd) - return cmd -} - -func (c cmdable) ClientKill(ipPort string) *StatusCmd { - cmd := NewStatusCmd("client", "kill", ipPort) - _ = c(cmd) - return cmd -} - -// ClientKillByFilter is new style synx, while the ClientKill is old -// CLIENT KILL