Update go modules

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
Gabriel Adrian Samfira 2023-03-12 16:22:37 +02:00
parent 829db87f15
commit c61b7fd268
No known key found for this signature in database
GPG key ID: 7D073DCC2C074CB5
698 changed files with 69376 additions and 29461 deletions

74
go.mod
View file

@ -3,80 +3,82 @@ module github.com/cloudbase/garm
go 1.18 go 1.18
require ( require (
github.com/BurntSushi/toml v0.4.1 github.com/BurntSushi/toml v1.2.1
github.com/go-resty/resty/v2 v2.7.0 github.com/go-resty/resty/v2 v2.7.0
github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/go-github/v48 v48.0.0 github.com/google/go-github/v48 v48.2.0
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1 github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/jedib0t/go-pretty/v6 v6.3.1 github.com/jedib0t/go-pretty/v6 v6.4.6
github.com/juju/clock v0.0.0-20220704231616-a2b96c8eeb27 github.com/juju/clock v1.0.3
github.com/juju/retry v0.0.0-20220204093819-62423bf33287 github.com/juju/retry v1.0.0
github.com/lxc/lxd v0.0.0-20220415052741-1170f2806124 github.com/lxc/lxd v0.0.0-20230310224854-36b345fbd578
github.com/manifoldco/promptui v0.9.0 github.com/manifoldco/promptui v0.9.0
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_golang v1.14.0
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
github.com/spf13/cobra v1.4.1-0.20220504202302-9e88759b19cd github.com/spf13/cobra v1.6.1
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.2
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b golang.org/x/crypto v0.7.0
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b golang.org/x/oauth2 v0.6.0
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/sys v0.6.0
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gorm.io/datatypes v1.1.0 gorm.io/datatypes v1.1.1
gorm.io/driver/mysql v1.4.4 gorm.io/driver/mysql v1.4.7
gorm.io/driver/sqlite v1.4.3 gorm.io/driver/sqlite v1.4.4
gorm.io/gorm v1.24.2 gorm.io/gorm v1.24.6
) )
require ( require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/chzyer/readline v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect
github.com/go-macaroon-bakery/macaroon-bakery/v3 v3.0.1 // indirect
github.com/go-macaroon-bakery/macaroonpb v1.0.0 // indirect github.com/go-macaroon-bakery/macaroonpb v1.0.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/juju/errors v0.0.0-20220328032053-6664a20db930 // indirect github.com/juju/errors v1.0.0 // indirect
github.com/juju/webbrowser v1.0.0 // indirect github.com/juju/webbrowser v1.0.0 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kr/fs v0.1.0 // indirect github.com/kr/fs v0.1.0 // indirect
github.com/kr/pretty v0.3.0 // indirect github.com/kr/pretty v0.3.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/pborman/uuid v1.2.1 // indirect github.com/pborman/uuid v1.2.1 // indirect
github.com/pkg/sftp v1.13.4 // indirect github.com/pkg/sftp v1.13.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pkg/xattr v0.4.9 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect
golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect golang.org/x/net v0.8.0 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/term v0.6.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.29.0 // indirect
gopkg.in/errgo.v1 v1.0.1 // indirect gopkg.in/errgo.v1 v1.0.1 // indirect
gopkg.in/httprequest.v1 v1.2.1 // indirect gopkg.in/httprequest.v1 v1.2.1 // indirect
gopkg.in/macaroon-bakery.v2 v2.3.0 // indirect
gopkg.in/macaroon.v2 v2.1.0 // indirect gopkg.in/macaroon.v2 v2.1.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
) )

535
go.sum
View file

@ -1,121 +1,56 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 h1:fmFk0Wt3bBxxwZnu48jqMdaOR/IZ4vdtJFuaFV8MpIE= github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 h1:fmFk0Wt3bBxxwZnu48jqMdaOR/IZ4vdtJFuaFV8MpIE=
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3/go.mod h1:bJWSKrZyQvfTnb2OudyUjurSG4/edverV7n82+K3JiM= github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3/go.mod h1:bJWSKrZyQvfTnb2OudyUjurSG4/edverV7n82+K3JiM=
github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
github.com/frankban/quicktest v1.1.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k=
github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20=
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY= github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/go-macaroon-bakery/macaroon-bakery/v3 v3.0.1 h1:uvQJoKTHrFFu8zxoaopNKedRzwdy3+8H72we4T/5cGs=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-macaroon-bakery/macaroon-bakery/v3 v3.0.1/go.mod h1:H59IYeChwvD1po3dhGUPvq5na+4NVD7SJlbhGKvslr0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-macaroon-bakery/macaroonpb v1.0.0 h1:It9exBaRMZ9iix1iJ6gwzfwsDE6ExNuwtAJ9e09v6XE= github.com/go-macaroon-bakery/macaroonpb v1.0.0 h1:It9exBaRMZ9iix1iJ6gwzfwsDE6ExNuwtAJ9e09v6XE=
github.com/go-macaroon-bakery/macaroonpb v1.0.0/go.mod h1:UzrGOcbiwTXISFP2XDLDPjfhMINZa+fX/7A2lMd31zc= github.com/go-macaroon-bakery/macaroonpb v1.0.0/go.mod h1:UzrGOcbiwTXISFP2XDLDPjfhMINZa+fX/7A2lMd31zc=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/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.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -123,56 +58,35 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 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.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/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/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-github/v48 v48.0.0 h1:9H5fWVXFK6ZsRriyPbjtnFAkJnoj0WKFtTYfpCRrTm8= github.com/google/go-github/v48 v48.2.0 h1:68puzySE6WqUY9KWmpOsDEQfDZsso98rT6pZcz9HqcE=
github.com/google/go-github/v48 v48.0.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y= github.com/google/go-github/v48 v48.2.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 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/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
@ -181,434 +95,190 @@ github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jedib0t/go-pretty/v6 v6.3.1 h1:aOXiD9oqiuLH8btPQW6SfgtQN5zwhyfzZls8a6sPJ/I= github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw=
github.com/jedib0t/go-pretty/v6 v6.3.1/go.mod h1:FMkOpgGD3EZ91cW8g/96RfxoV7bdeJyzXPYgz1L1ln0= github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/juju/clock v1.0.3 h1:yJHIsWXeU8j3QcBdiess09SzfiXRRrsjKPn2whnMeds=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/juju/clock v1.0.3/go.mod h1:HIBvJ8kiV/n7UHwKuCkdYL4l/MDECztHR2sAvWDxxf0=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/juju/collections v1.0.2 h1:y9t99Nq/uUZksJgWehiWxIr2vB1UG3hUT7LBNy1xiH8=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/juju/loggo v1.0.0 h1:Y6ZMQOGR9Aj3BGkiWx7HBbIx6zNwNkxhVNOHU2i1bl0=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/juju/mgo/v2 v2.0.2 h1:ufYtW2OFNjniTuxOngecP3Mk5sSclo8Zl1mnmyGWUWA=
github.com/juju/clock v0.0.0-20220704231616-a2b96c8eeb27 h1:tzBYJiWEx2HcWUSdfBzdj9+FTQGWzsuSiiYgutZl2kA=
github.com/juju/clock v0.0.0-20220704231616-a2b96c8eeb27/go.mod h1:GZ/FY8Cqw3KHG6DwRVPUKbSPTAwyrU28xFi5cqZnLsc=
github.com/juju/collections v0.0.0-20220203020748-febd7cad8a7a h1:d7eZO8OS/ZXxdP0uq3E8CdoA1qNFaecAv90UxrxaY2k=
github.com/juju/errors v0.0.0-20220328032053-6664a20db930 h1:NavIEjCH2y/svEZ8mOSnFofpFeiAQTzUpByc28R6Hhc=
github.com/juju/errors v0.0.0-20220328032053-6664a20db930/go.mod h1:jMGj9DWF/qbo91ODcfJq6z/RYc3FX3taCBZMCcpI4Ls=
github.com/juju/loggo v0.0.0-20210728185423-eebad3a902c4 h1:NO5tuyw++EGLnz56Q8KMyDZRwJwWO8jQnj285J3FOmY=
github.com/juju/mgo/v2 v2.0.0-20220111072304-f200228f1090 h1:zX5GoH3Jp8k1EjUFkApu/YZAYEn0PYQfg/U6IDyNyYs=
github.com/juju/mgotest v1.0.1/go.mod h1:vTaDufYul+Ps8D7bgseHjq87X8eu0ivlKLp9mVc/Bfc=
github.com/juju/postgrestest v1.1.0/go.mod h1:/n17Y2T6iFozzXwSCO0JYJ5gSiz2caEtSwAjh/uLXDM=
github.com/juju/qthttptest v0.0.1/go.mod h1://LCf/Ls22/rPw2u1yWukUJvYtfPY4nYpWUl2uZhryo=
github.com/juju/qthttptest v0.1.1 h1:JPju5P5CDMCy8jmBJV2wGLjDItUsx2KKL514EfOYueM=
github.com/juju/qthttptest v0.1.1/go.mod h1:aTlAv8TYaflIiTDIQYzxnl1QdPjAg8Q8qJMErpKy6A4= github.com/juju/qthttptest v0.1.1/go.mod h1:aTlAv8TYaflIiTDIQYzxnl1QdPjAg8Q8qJMErpKy6A4=
github.com/juju/retry v0.0.0-20220204093819-62423bf33287 h1:U+7oMWEglXfiikIppNexButZRwKPlzLBGKYSNCXzXf8= github.com/juju/qthttptest v0.1.3 h1:M0HdpwsK/UTHRGRcIw5zvh5z+QOgdqyK+ecDMN+swwM=
github.com/juju/retry v0.0.0-20220204093819-62423bf33287/go.mod h1:SssN1eYeK3A2qjnFGTiVMbdzGJ2BfluaJblJXvuvgqA= github.com/juju/retry v1.0.0 h1:Tb1hFdDSPGLH/BGdYQOF7utQ9lA0ouVJX2imqgJK6tk=
github.com/juju/schema v1.0.0/go.mod h1:Y+ThzXpUJ0E7NYYocAbuvJ7vTivXfrof/IfRPq/0abI= github.com/juju/retry v1.0.0/go.mod h1:SssN1eYeK3A2qjnFGTiVMbdzGJ2BfluaJblJXvuvgqA=
github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 h1:XEDzpuZb8Ma7vLja3+5hzUqVTvAqm5Y+ygvnDs5iTMM= github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 h1:XEDzpuZb8Ma7vLja3+5hzUqVTvAqm5Y+ygvnDs5iTMM=
github.com/juju/utils v0.0.0-20200604140309-9d78121a29e0 h1:4XlJ/Wj/bH3zGa2GU+Us72FgtmL1n3dwjP7LW7+TF/o=
github.com/juju/utils/v3 v3.0.0-20220203023959-c3fbc78a33b0 h1:bn+2Adl1yWqYjm3KSFlFqsvfLg2eq+XNL7GGMYApdVw= github.com/juju/utils/v3 v3.0.0-20220203023959-c3fbc78a33b0 h1:bn+2Adl1yWqYjm3KSFlFqsvfLg2eq+XNL7GGMYApdVw=
github.com/juju/version v0.0.0-20210303051006-2015802527a8 h1:BTo6HzRR0zPBcXbs1Sy08aQNfvdm3ey8O+mBTiO3g00= github.com/juju/version v0.0.0-20210303051006-2015802527a8 h1:BTo6HzRR0zPBcXbs1Sy08aQNfvdm3ey8O+mBTiO3g00=
github.com/juju/version/v2 v2.0.0-20211007103408-2e8da085dc23 h1:wtEPbidt1VyHlb8RSztU6ySQj29FLsOQiI9XiJhXDM4= github.com/juju/version/v2 v2.0.0-20211007103408-2e8da085dc23 h1:wtEPbidt1VyHlb8RSztU6ySQj29FLsOQiI9XiJhXDM4=
github.com/juju/webbrowser v0.0.0-20160309143629-54b8c57083b4/go.mod h1:G6PCelgkM6cuvyD10iYJsjLBsSadVXtJ+nBxFAxE2BU=
github.com/juju/webbrowser v1.0.0 h1:JLdmbFtCGY6Qf2jmS6bVaenJFGIFkdF1/BjUm76af78= github.com/juju/webbrowser v1.0.0 h1:JLdmbFtCGY6Qf2jmS6bVaenJFGIFkdF1/BjUm76af78=
github.com/juju/webbrowser v1.0.0/go.mod h1:RwVlbBcF91Q4vS+iwlkJ6bZTE3EwlrjbYlM3WMVD6Bc= github.com/juju/webbrowser v1.0.0/go.mod h1:RwVlbBcF91Q4vS+iwlkJ6bZTE3EwlrjbYlM3WMVD6Bc=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lxc/lxd v0.0.0-20230310224854-36b345fbd578 h1:Rqzj0l43LLcTciHKIwKpTesAv9rSeC4LbUc+BsiIY6Q=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lxc/lxd v0.0.0-20230310224854-36b345fbd578/go.mod h1:6Z1AwZwLm5Y+tzoW5CdKOo51fyDiCKJz3QgAXJ9/IYI=
github.com/lxc/lxd v0.0.0-20220415052741-1170f2806124 h1:EmjWCASxSUz+ymsEJfiWN3yx3yTypoKJrnOSSzAWYds=
github.com/lxc/lxd v0.0.0-20220415052741-1170f2806124/go.mod h1:T4xjj62BmFg1L5JfY2wRyPZtKbBeTFgo/GLwV8DFZ8M=
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg= github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8= github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/cobra v1.4.1-0.20220504202302-9e88759b19cd h1:0Hv1DPpsKWp/xjP1sQRfLDIymRDu79mErd9H9+l0uaE=
github.com/spf13/cobra v1.4.1-0.20220504202302-9e88759b19cd/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI= github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI= github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI=
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-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-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.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.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/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 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-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 v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -617,67 +287,46 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 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= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 h1:FVCohIoYO7IJoDDVpV2pdq7SgrMH6wHnuTyrdrxJNoY=
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk= gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk=
gopkg.in/errgo.v1 v1.0.1 h1:oQFRXzZ7CkBGdm1XZm/EbQYaYNNEElNBOd09M6cqNso= gopkg.in/errgo.v1 v1.0.1 h1:oQFRXzZ7CkBGdm1XZm/EbQYaYNNEElNBOd09M6cqNso=
gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/httprequest.v1 v1.2.0/go.mod h1:T61ZUaJLpMnzvoJDO03ZD8yRXD4nZzBeDoW5e9sffjg=
gopkg.in/httprequest.v1 v1.2.1 h1:pEPLMdF/gjWHnKxLpuCYaHFjc8vAB2wrYjXrqDVC16E= gopkg.in/httprequest.v1 v1.2.1 h1:pEPLMdF/gjWHnKxLpuCYaHFjc8vAB2wrYjXrqDVC16E=
gopkg.in/httprequest.v1 v1.2.1/go.mod h1:x2Otw96yda5+8+6ZeWwHIJTFkEHWP/qP8pJOzqEtWPM= gopkg.in/httprequest.v1 v1.2.1/go.mod h1:x2Otw96yda5+8+6ZeWwHIJTFkEHWP/qP8pJOzqEtWPM=
gopkg.in/juju/environschema.v1 v1.0.0/go.mod h1:WTgU3KXKCVoO9bMmG/4KHzoaRvLeoxfjArpgd1MGWFA=
gopkg.in/macaroon-bakery.v2 v2.3.0 h1:b40knPgPTke1QLTE8BSYeH7+R/hiIozB1A8CTLYN0Ic=
gopkg.in/macaroon-bakery.v2 v2.3.0/go.mod h1:/8YhtPARXeRzbpEPLmRB66+gQE8/pzBBkWwg7Vz/guc=
gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI= gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI=
gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o= gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.1.0 h1:EVp1Z28N4ACpYFK1nHboEIJGIFfjY7vLeieDk8jSHJA= gorm.io/datatypes v1.1.1 h1:XAjO7NNfUKVUvnS3+BkqMrPXxCAcxDlpOYbjnizxNCw=
gorm.io/datatypes v1.1.0/go.mod h1:SH2K9R+2RMjuX1CkCONrPwoe9JzVv2hkQvEu4bXGojE= gorm.io/datatypes v1.1.1/go.mod h1:u8GEgFjJ+GpsGfgHmBUcQqHm/937t3sj/SO9dvbndTg=
gorm.io/driver/mysql v1.4.4 h1:MX0K9Qvy0Na4o7qSC/YI7XxqUw5KDw01umqgID+svdQ= gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y=
gorm.io/driver/mysql v1.4.4/go.mod h1:BCg8cKI+R0j/rZRQxeKis/forqRwRSYOR8OM3Wo6hOM= gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc= gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0= gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View file

@ -1,2 +1,2 @@
toml.test /toml.test
/toml-test /toml-test

View file

@ -1 +0,0 @@
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).

View file

@ -1,10 +1,5 @@
## TOML parser and encoder for Go with reflection
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
reflection interface similar to Go's standard library `json` and `xml` reflection interface similar to Go's standard library `json` and `xml` packages.
packages. This package also supports the `encoding.TextUnmarshaler` and
`encoding.TextMarshaler` interfaces so that you can define custom data
representations. (There is an example of this below.)
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0). Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
@ -14,28 +9,18 @@ See the [releases page](https://github.com/BurntSushi/toml/releases) for a
changelog; this information is also in the git tag annotations (e.g. `git show changelog; this information is also in the git tag annotations (e.g. `git show
v0.4.0`). v0.4.0`).
This library requires Go 1.13 or newer; install it with: This library requires Go 1.13 or newer; add it to your go.mod with:
$ go get github.com/BurntSushi/toml % go get github.com/BurntSushi/toml@latest
It also comes with a TOML validator CLI tool: It also comes with a TOML validator CLI tool:
$ go get github.com/BurntSushi/toml/cmd/tomlv % go install github.com/BurntSushi/toml/cmd/tomlv@latest
$ tomlv some-toml-file.toml % tomlv some-toml-file.toml
### Testing
This package passes all tests in
[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder
and the encoder.
### Examples ### Examples
For the simplest example, consider some TOML file as just a list of keys and
This package works similarly to how the Go standard library handles XML and values:
JSON. Namely, data is loaded into Go values via reflection.
For the simplest example, consider some TOML file as just a list of keys
and values:
```toml ```toml
Age = 25 Age = 25
@ -45,7 +30,7 @@ Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z DOB = 1987-07-05T05:45:00Z
``` ```
Which could be defined in Go as: Which can be decoded with:
```go ```go
type Config struct { type Config struct {
@ -53,21 +38,15 @@ type Config struct {
Cats []string Cats []string
Pi float64 Pi float64
Perfection []int Perfection []int
DOB time.Time // requires `import time` DOB time.Time
} }
```
And then decoded with:
```go
var conf Config var conf Config
if _, err := toml.Decode(tomlData, &conf); err != nil { _, err := toml.Decode(tomlData, &conf)
// handle error
}
``` ```
You can also use struct tags if your struct field name doesn't map to a TOML You can also use struct tags if your struct field name doesn't map to a TOML key
key value directly: value directly:
```toml ```toml
some_key_NAME = "wat" some_key_NAME = "wat"
@ -75,146 +54,67 @@ some_key_NAME = "wat"
```go ```go
type TOML struct { type TOML struct {
ObscureKey string `toml:"some_key_NAME"` ObscureKey string `toml:"some_key_NAME"`
} }
``` ```
Beware that like other most other decoders **only exported fields** are Beware that like other decoders **only exported fields** are considered when
considered when encoding and decoding; private fields are silently ignored. encoding and decoding; private fields are silently ignored.
### Using the `encoding.TextUnmarshaler` interface ### Using the `Marshaler` and `encoding.TextUnmarshaler` interfaces
Here's an example that automatically parses values in a `mail.Address`:
Here's an example that automatically parses duration strings into
`time.Duration` values:
```toml ```toml
[[song]] contacts = [
name = "Thunder Road" "Donald Duck <donald@duckburg.com>",
duration = "4m49s" "Scrooge McDuck <scrooge@duckburg.com>",
]
[[song]]
name = "Stairway to Heaven"
duration = "8m03s"
``` ```
Which can be decoded with: Can be decoded with:
```go ```go
type song struct { // Create address type which satisfies the encoding.TextUnmarshaler interface.
Name string type address struct {
Duration duration *mail.Address
}
type songs struct {
Song []song
}
var favorites songs
if _, err := toml.Decode(blob, &favorites); err != nil {
log.Fatal(err)
} }
for _, s := range favorites.Song { func (a *address) UnmarshalText(text []byte) error {
fmt.Printf("%s (%s)\n", s.Name, s.Duration)
}
```
And you'll also need a `duration` type that satisfies the
`encoding.TextUnmarshaler` interface:
```go
type duration struct {
time.Duration
}
func (d *duration) UnmarshalText(text []byte) error {
var err error var err error
d.Duration, err = time.ParseDuration(string(text)) a.Address, err = mail.ParseAddress(string(text))
return err return err
} }
// Decode it.
func decode() {
blob := `
contacts = [
"Donald Duck <donald@duckburg.com>",
"Scrooge McDuck <scrooge@duckburg.com>",
]
`
var contacts struct {
Contacts []address
}
_, err := toml.Decode(blob, &contacts)
if err != nil {
log.Fatal(err)
}
for _, c := range contacts.Contacts {
fmt.Printf("%#v\n", c.Address)
}
// Output:
// &mail.Address{Name:"Donald Duck", Address:"donald@duckburg.com"}
// &mail.Address{Name:"Scrooge McDuck", Address:"scrooge@duckburg.com"}
}
``` ```
To target TOML specifically you can implement `UnmarshalTOML` TOML interface in To target TOML specifically you can implement `UnmarshalTOML` TOML interface in
a similar way. a similar way.
### More complex usage ### More complex usage
See the [`_example/`](/_example) directory for a more complex example.
Here's an example of how to load the example from the official spec page:
```toml
# This is a TOML document. Boom.
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]
```
And the corresponding Go types are:
```go
type tomlConfig struct {
Title string
Owner ownerInfo
DB database `toml:"database"`
Servers map[string]server
Clients clients
}
type ownerInfo struct {
Name string
Org string `toml:"organization"`
Bio string
DOB time.Time
}
type database struct {
Server string
Ports []int
ConnMax int `toml:"connection_max"`
Enabled bool
}
type server struct {
IP string
DC string
}
type clients struct {
Data [][]interface{}
Hosts []string
}
```
Note that a case insensitive match will be tried if an exact match can't be
found.
A working example of the above can be found in `_examples/example.{go,toml}`.

View file

@ -1,13 +1,16 @@
package toml package toml
import ( import (
"bytes"
"encoding" "encoding"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math" "math"
"os" "os"
"reflect" "reflect"
"strconv"
"strings" "strings"
"time" "time"
) )
@ -18,16 +21,35 @@ type Unmarshaler interface {
UnmarshalTOML(interface{}) error UnmarshalTOML(interface{}) error
} }
// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. // Unmarshal decodes the contents of data in TOML format into a pointer v.
func Unmarshal(p []byte, v interface{}) error { //
_, err := Decode(string(p), v) // See [Decoder] for a description of the decoding process.
func Unmarshal(data []byte, v interface{}) error {
_, err := NewDecoder(bytes.NewReader(data)).Decode(v)
return err return err
} }
// Decode the TOML data in to the pointer v.
//
// See [Decoder] for a description of the decoding process.
func Decode(data string, v interface{}) (MetaData, error) {
return NewDecoder(strings.NewReader(data)).Decode(v)
}
// DecodeFile reads the contents of a file and decodes it with [Decode].
func DecodeFile(path string, v interface{}) (MetaData, error) {
fp, err := os.Open(path)
if err != nil {
return MetaData{}, err
}
defer fp.Close()
return NewDecoder(fp).Decode(v)
}
// Primitive is a TOML value that hasn't been decoded into a Go value. // Primitive is a TOML value that hasn't been decoded into a Go value.
// //
// This type can be used for any value, which will cause decoding to be delayed. // This type can be used for any value, which will cause decoding to be delayed.
// You can use the PrimitiveDecode() function to "manually" decode these values. // You can use [PrimitiveDecode] to "manually" decode these values.
// //
// NOTE: The underlying representation of a `Primitive` value is subject to // NOTE: The underlying representation of a `Primitive` value is subject to
// change. Do not rely on it. // change. Do not rely on it.
@ -40,32 +62,25 @@ type Primitive struct {
context Key context Key
} }
// PrimitiveDecode is just like the other `Decode*` functions, except it // The significand precision for float32 and float64 is 24 and 53 bits; this is
// decodes a TOML value that has already been parsed. Valid primitive values // the range a natural number can be stored in a float without loss of data.
// can *only* be obtained from values filled by the decoder functions, const (
// including this method. (i.e., `v` may contain more `Primitive` maxSafeFloat32Int = 16777215 // 2^24-1
// values.) maxSafeFloat64Int = int64(9007199254740991) // 2^53-1
// )
// Meta data for primitive values is included in the meta data returned by
// the `Decode*` functions with one exception: keys returned by the Undecoded
// method will only reflect keys that were decoded. Namely, any keys hidden
// behind a Primitive will be considered undecoded. Executing this method will
// update the undecoded keys in the meta data. (See the example.)
func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
md.context = primValue.context
defer func() { md.context = nil }()
return md.unify(primValue.undecoded, rvalue(v))
}
// Decoder decodes TOML data. // Decoder decodes TOML data.
// //
// TOML tables correspond to Go structs or maps (dealer's choice they can be // TOML tables correspond to Go structs or maps; they can be used
// used interchangeably). // interchangeably, but structs offer better type safety.
// //
// TOML table arrays correspond to either a slice of structs or a slice of maps. // TOML table arrays correspond to either a slice of structs or a slice of maps.
// //
// TOML datetimes correspond to Go time.Time values. Local datetimes are parsed // TOML datetimes correspond to [time.Time]. Local datetimes are parsed in the
// in the local timezone. // local timezone.
//
// [time.Duration] types are treated as nanoseconds if the TOML value is an
// integer, or they're parsed with time.ParseDuration() if they're strings.
// //
// All other TOML types (float, string, int, bool and array) correspond to the // All other TOML types (float, string, int, bool and array) correspond to the
// obvious Go types. // obvious Go types.
@ -74,9 +89,9 @@ func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
// interface, in which case any primitive TOML value (floats, strings, integers, // interface, in which case any primitive TOML value (floats, strings, integers,
// booleans, datetimes) will be converted to a []byte and given to the value's // booleans, datetimes) will be converted to a []byte and given to the value's
// UnmarshalText method. See the Unmarshaler example for a demonstration with // UnmarshalText method. See the Unmarshaler example for a demonstration with
// time duration strings. // email addresses.
// //
// Key mapping // ### Key mapping
// //
// TOML keys can map to either keys in a Go map or field names in a Go struct. // TOML keys can map to either keys in a Go map or field names in a Go struct.
// The special `toml` struct tag can be used to map TOML keys to struct fields // The special `toml` struct tag can be used to map TOML keys to struct fields
@ -100,18 +115,39 @@ func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r} return &Decoder{r: r}
} }
var (
unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
primitiveType = reflect.TypeOf((*Primitive)(nil)).Elem()
)
// Decode TOML data in to the pointer `v`. // Decode TOML data in to the pointer `v`.
func (dec *Decoder) Decode(v interface{}) (MetaData, error) { func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr { if rv.Kind() != reflect.Ptr {
return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v)) s := "%q"
if reflect.TypeOf(v) == nil {
s = "%v"
}
return MetaData{}, fmt.Errorf("toml: cannot decode to non-pointer "+s, reflect.TypeOf(v))
} }
if rv.IsNil() { if rv.IsNil() {
return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v)) return MetaData{}, fmt.Errorf("toml: cannot decode to nil value of %q", reflect.TypeOf(v))
} }
// TODO: have parser should read from io.Reader? Or at the very least, make // Check if this is a supported type: struct, map, interface{}, or something
// it read from []byte rather than string // that implements UnmarshalTOML or UnmarshalText.
rv = indirect(rv)
rt := rv.Type()
if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map &&
!(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) &&
!rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) {
return MetaData{}, fmt.Errorf("toml: cannot decode to type %s", rt)
}
// TODO: parser should read from io.Reader? Or at the very least, make it
// read from []byte rather than string
data, err := ioutil.ReadAll(dec.r) data, err := ioutil.ReadAll(dec.r)
if err != nil { if err != nil {
return MetaData{}, err return MetaData{}, err
@ -121,29 +157,32 @@ func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
if err != nil { if err != nil {
return MetaData{}, err return MetaData{}, err
} }
md := MetaData{ md := MetaData{
p.mapping, p.types, p.ordered, mapping: p.mapping,
make(map[string]bool, len(p.ordered)), nil, keyInfo: p.keyInfo,
keys: p.ordered,
decoded: make(map[string]struct{}, len(p.ordered)),
context: nil,
data: data,
} }
return md, md.unify(p.mapping, indirect(rv)) return md, md.unify(p.mapping, rv)
} }
// Decode the TOML data in to the pointer v. // PrimitiveDecode is just like the other Decode* functions, except it decodes a
// TOML value that has already been parsed. Valid primitive values can *only* be
// obtained from values filled by the decoder functions, including this method.
// (i.e., v may contain more [Primitive] values.)
// //
// See the documentation on Decoder for a description of the decoding process. // Meta data for primitive values is included in the meta data returned by the
func Decode(data string, v interface{}) (MetaData, error) { // Decode* functions with one exception: keys returned by the Undecoded method
return NewDecoder(strings.NewReader(data)).Decode(v) // will only reflect keys that were decoded. Namely, any keys hidden behind a
} // Primitive will be considered undecoded. Executing this method will update the
// undecoded keys in the meta data. (See the example.)
// DecodeFile is just like Decode, except it will automatically read the func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
// contents of the file at path and decode it for you. md.context = primValue.context
func DecodeFile(path string, v interface{}) (MetaData, error) { defer func() { md.context = nil }()
fp, err := os.Open(path) return md.unify(primValue.undecoded, rvalue(v))
if err != nil {
return MetaData{}, err
}
defer fp.Close()
return NewDecoder(fp).Decode(v)
} }
// unify performs a sort of type unification based on the structure of `rv`, // unify performs a sort of type unification based on the structure of `rv`,
@ -154,7 +193,7 @@ func DecodeFile(path string, v interface{}) (MetaData, error) {
func (md *MetaData) unify(data interface{}, rv reflect.Value) error { func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
// Special case. Look for a `Primitive` value. // Special case. Look for a `Primitive` value.
// TODO: #76 would make this superfluous after implemented. // TODO: #76 would make this superfluous after implemented.
if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { if rv.Type() == primitiveType {
// Save the undecoded data and the key context into the primitive // Save the undecoded data and the key context into the primitive
// value. // value.
context := make(Key, len(md.context)) context := make(Key, len(md.context))
@ -166,17 +205,14 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
return nil return nil
} }
// Special case. Unmarshaler Interface support. rvi := rv.Interface()
if rv.CanAddr() { if v, ok := rvi.(Unmarshaler); ok {
if v, ok := rv.Addr().Interface().(Unmarshaler); ok { return v.UnmarshalTOML(data)
return v.UnmarshalTOML(data)
}
} }
if v, ok := rvi.(encoding.TextUnmarshaler); ok {
// Special case. Look for a value satisfying the TextUnmarshaler interface.
if v, ok := rv.Interface().(encoding.TextUnmarshaler); ok {
return md.unifyText(data, v) return md.unifyText(data, v)
} }
// TODO: // TODO:
// The behavior here is incorrect whenever a Go type satisfies the // The behavior here is incorrect whenever a Go type satisfies the
// encoding.TextUnmarshaler interface but also corresponds to a TOML hash or // encoding.TextUnmarshaler interface but also corresponds to a TOML hash or
@ -187,7 +223,6 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
k := rv.Kind() k := rv.Kind()
// laziness
if k >= reflect.Int && k <= reflect.Uint64 { if k >= reflect.Int && k <= reflect.Uint64 {
return md.unifyInt(data, rv) return md.unifyInt(data, rv)
} }
@ -213,17 +248,14 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
case reflect.Bool: case reflect.Bool:
return md.unifyBool(data, rv) return md.unifyBool(data, rv)
case reflect.Interface: case reflect.Interface:
// we only support empty interfaces. if rv.NumMethod() > 0 { // Only support empty interfaces are supported.
if rv.NumMethod() > 0 { return md.e("unsupported type %s", rv.Type())
return e("unsupported type %s", rv.Type())
} }
return md.unifyAnything(data, rv) return md.unifyAnything(data, rv)
case reflect.Float32: case reflect.Float32, reflect.Float64:
fallthrough
case reflect.Float64:
return md.unifyFloat64(data, rv) return md.unifyFloat64(data, rv)
} }
return e("unsupported type %s", rv.Kind()) return md.e("unsupported type %s", rv.Kind())
} }
func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
@ -232,7 +264,7 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
if mapping == nil { if mapping == nil {
return nil return nil
} }
return e("type mismatch for %s: expected table but found %T", return md.e("type mismatch for %s: expected table but found %T",
rv.Type().String(), mapping) rv.Type().String(), mapping)
} }
@ -254,17 +286,18 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
for _, i := range f.index { for _, i := range f.index {
subv = indirect(subv.Field(i)) subv = indirect(subv.Field(i))
} }
if isUnifiable(subv) { if isUnifiable(subv) {
md.decoded[md.context.add(key).String()] = true md.decoded[md.context.add(key).String()] = struct{}{}
md.context = append(md.context, key) md.context = append(md.context, key)
if err := md.unify(datum, subv); err != nil {
err := md.unify(datum, subv)
if err != nil {
return err return err
} }
md.context = md.context[0 : len(md.context)-1] md.context = md.context[0 : len(md.context)-1]
} else if f.name != "" { } else if f.name != "" {
// Bad user! No soup for you! return md.e("cannot write unexported field %s.%s", rv.Type().String(), f.name)
return e("cannot write unexported field %s.%s",
rv.Type().String(), f.name)
} }
} }
} }
@ -272,10 +305,10 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
} }
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
if k := rv.Type().Key().Kind(); k != reflect.String { keyType := rv.Type().Key().Kind()
return fmt.Errorf( if keyType != reflect.String && keyType != reflect.Interface {
"toml: cannot decode to a map with non-string key type (%s in %q)", return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)",
k, rv.Type()) keyType, rv.Type())
} }
tmap, ok := mapping.(map[string]interface{}) tmap, ok := mapping.(map[string]interface{})
@ -283,23 +316,32 @@ func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
if tmap == nil { if tmap == nil {
return nil return nil
} }
return badtype("map", mapping) return md.badtype("map", mapping)
} }
if rv.IsNil() { if rv.IsNil() {
rv.Set(reflect.MakeMap(rv.Type())) rv.Set(reflect.MakeMap(rv.Type()))
} }
for k, v := range tmap { for k, v := range tmap {
md.decoded[md.context.add(k).String()] = true md.decoded[md.context.add(k).String()] = struct{}{}
md.context = append(md.context, k) md.context = append(md.context, k)
rvkey := indirect(reflect.New(rv.Type().Key()))
rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
if err := md.unify(v, rvval); err != nil {
err := md.unify(v, indirect(rvval))
if err != nil {
return err return err
} }
md.context = md.context[0 : len(md.context)-1] md.context = md.context[0 : len(md.context)-1]
rvkey.SetString(k) rvkey := indirect(reflect.New(rv.Type().Key()))
switch keyType {
case reflect.Interface:
rvkey.Set(reflect.ValueOf(k))
case reflect.String:
rvkey.SetString(k)
}
rv.SetMapIndex(rvkey, rvval) rv.SetMapIndex(rvkey, rvval)
} }
return nil return nil
@ -311,10 +353,10 @@ func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
if !datav.IsValid() { if !datav.IsValid() {
return nil return nil
} }
return badtype("slice", data) return md.badtype("slice", data)
} }
if l := datav.Len(); l != rv.Len() { if l := datav.Len(); l != rv.Len() {
return e("expected array length %d; got TOML array of length %d", rv.Len(), l) return md.e("expected array length %d; got TOML array of length %d", rv.Len(), l)
} }
return md.unifySliceArray(datav, rv) return md.unifySliceArray(datav, rv)
} }
@ -325,7 +367,7 @@ func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
if !datav.IsValid() { if !datav.IsValid() {
return nil return nil
} }
return badtype("slice", data) return md.badtype("slice", data)
} }
n := datav.Len() n := datav.Len()
if rv.IsNil() || rv.Cap() < n { if rv.IsNil() || rv.Cap() < n {
@ -346,26 +388,35 @@ func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
return nil return nil
} }
func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
if _, ok := data.(time.Time); ok { _, ok := rv.Interface().(json.Number)
rv.Set(reflect.ValueOf(data)) if ok {
if i, ok := data.(int64); ok {
rv.SetString(strconv.FormatInt(i, 10))
} else if f, ok := data.(float64); ok {
rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
} else {
return md.badtype("string", data)
}
return nil return nil
} }
return badtype("time.Time", data)
}
func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
if s, ok := data.(string); ok { if s, ok := data.(string); ok {
rv.SetString(s) rv.SetString(s)
return nil return nil
} }
return badtype("string", data) return md.badtype("string", data)
} }
func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
rvk := rv.Kind()
if num, ok := data.(float64); ok { if num, ok := data.(float64); ok {
switch rv.Kind() { switch rvk {
case reflect.Float32: case reflect.Float32:
if num < -math.MaxFloat32 || num > math.MaxFloat32 {
return md.parseErr(errParseRange{i: num, size: rvk.String()})
}
fallthrough fallthrough
case reflect.Float64: case reflect.Float64:
rv.SetFloat(num) rv.SetFloat(num)
@ -374,54 +425,60 @@ func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
} }
return nil return nil
} }
return badtype("float", data)
if num, ok := data.(int64); ok {
if (rvk == reflect.Float32 && (num < -maxSafeFloat32Int || num > maxSafeFloat32Int)) ||
(rvk == reflect.Float64 && (num < -maxSafeFloat64Int || num > maxSafeFloat64Int)) {
return md.parseErr(errParseRange{i: num, size: rvk.String()})
}
rv.SetFloat(float64(num))
return nil
}
return md.badtype("float", data)
} }
func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
if num, ok := data.(int64); ok { _, ok := rv.Interface().(time.Duration)
if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { if ok {
switch rv.Kind() { // Parse as string duration, and fall back to regular integer parsing
case reflect.Int, reflect.Int64: // (as nanosecond) if this is not a string.
// No bounds checking necessary. if s, ok := data.(string); ok {
case reflect.Int8: dur, err := time.ParseDuration(s)
if num < math.MinInt8 || num > math.MaxInt8 { if err != nil {
return e("value %d is out of range for int8", num) return md.parseErr(errParseDuration{s})
}
case reflect.Int16:
if num < math.MinInt16 || num > math.MaxInt16 {
return e("value %d is out of range for int16", num)
}
case reflect.Int32:
if num < math.MinInt32 || num > math.MaxInt32 {
return e("value %d is out of range for int32", num)
}
} }
rv.SetInt(num) rv.SetInt(int64(dur))
} else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { return nil
unum := uint64(num)
switch rv.Kind() {
case reflect.Uint, reflect.Uint64:
// No bounds checking necessary.
case reflect.Uint8:
if num < 0 || unum > math.MaxUint8 {
return e("value %d is out of range for uint8", num)
}
case reflect.Uint16:
if num < 0 || unum > math.MaxUint16 {
return e("value %d is out of range for uint16", num)
}
case reflect.Uint32:
if num < 0 || unum > math.MaxUint32 {
return e("value %d is out of range for uint32", num)
}
}
rv.SetUint(unum)
} else {
panic("unreachable")
} }
return nil
} }
return badtype("integer", data)
num, ok := data.(int64)
if !ok {
return md.badtype("integer", data)
}
rvk := rv.Kind()
switch {
case rvk >= reflect.Int && rvk <= reflect.Int64:
if (rvk == reflect.Int8 && (num < math.MinInt8 || num > math.MaxInt8)) ||
(rvk == reflect.Int16 && (num < math.MinInt16 || num > math.MaxInt16)) ||
(rvk == reflect.Int32 && (num < math.MinInt32 || num > math.MaxInt32)) {
return md.parseErr(errParseRange{i: num, size: rvk.String()})
}
rv.SetInt(num)
case rvk >= reflect.Uint && rvk <= reflect.Uint64:
unum := uint64(num)
if rvk == reflect.Uint8 && (num < 0 || unum > math.MaxUint8) ||
rvk == reflect.Uint16 && (num < 0 || unum > math.MaxUint16) ||
rvk == reflect.Uint32 && (num < 0 || unum > math.MaxUint32) {
return md.parseErr(errParseRange{i: num, size: rvk.String()})
}
rv.SetUint(unum)
default:
panic("unreachable")
}
return nil
} }
func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
@ -429,7 +486,7 @@ func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
rv.SetBool(b) rv.SetBool(b)
return nil return nil
} }
return badtype("boolean", data) return md.badtype("boolean", data)
} }
func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
@ -440,7 +497,13 @@ func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error { func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) error {
var s string var s string
switch sdata := data.(type) { switch sdata := data.(type) {
case TextMarshaler: case Marshaler:
text, err := sdata.MarshalTOML()
if err != nil {
return err
}
s = string(text)
case encoding.TextMarshaler:
text, err := sdata.MarshalText() text, err := sdata.MarshalText()
if err != nil { if err != nil {
return err return err
@ -457,7 +520,7 @@ func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) erro
case float64: case float64:
s = fmt.Sprintf("%f", sdata) s = fmt.Sprintf("%f", sdata)
default: default:
return badtype("primitive (string-like)", data) return md.badtype("primitive (string-like)", data)
} }
if err := v.UnmarshalText([]byte(s)); err != nil { if err := v.UnmarshalText([]byte(s)); err != nil {
return err return err
@ -465,22 +528,54 @@ func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) erro
return nil return nil
} }
func (md *MetaData) badtype(dst string, data interface{}) error {
return md.e("incompatible types: TOML value has type %T; destination has type %s", data, dst)
}
func (md *MetaData) parseErr(err error) error {
k := md.context.String()
return ParseError{
LastKey: k,
Position: md.keyInfo[k].pos,
Line: md.keyInfo[k].pos.Line,
err: err,
input: string(md.data),
}
}
func (md *MetaData) e(format string, args ...interface{}) error {
f := "toml: "
if len(md.context) > 0 {
f = fmt.Sprintf("toml: (last key %q): ", md.context)
p := md.keyInfo[md.context.String()].pos
if p.Line > 0 {
f = fmt.Sprintf("toml: line %d (last key %q): ", p.Line, md.context)
}
}
return fmt.Errorf(f+format, args...)
}
// rvalue returns a reflect.Value of `v`. All pointers are resolved. // rvalue returns a reflect.Value of `v`. All pointers are resolved.
func rvalue(v interface{}) reflect.Value { func rvalue(v interface{}) reflect.Value {
return indirect(reflect.ValueOf(v)) return indirect(reflect.ValueOf(v))
} }
// indirect returns the value pointed to by a pointer. // indirect returns the value pointed to by a pointer.
// Pointers are followed until the value is not a pointer.
// New values are allocated for each nil pointer.
// //
// An exception to this rule is if the value satisfies an interface of // Pointers are followed until the value is not a pointer. New values are
// interest to us (like encoding.TextUnmarshaler). // allocated for each nil pointer.
//
// An exception to this rule is if the value satisfies an interface of interest
// to us (like encoding.TextUnmarshaler).
func indirect(v reflect.Value) reflect.Value { func indirect(v reflect.Value) reflect.Value {
if v.Kind() != reflect.Ptr { if v.Kind() != reflect.Ptr {
if v.CanSet() { if v.CanSet() {
pv := v.Addr() pv := v.Addr()
if _, ok := pv.Interface().(encoding.TextUnmarshaler); ok { pvi := pv.Interface()
if _, ok := pvi.(encoding.TextUnmarshaler); ok {
return pv
}
if _, ok := pvi.(Unmarshaler); ok {
return pv return pv
} }
} }
@ -496,16 +591,12 @@ func isUnifiable(rv reflect.Value) bool {
if rv.CanSet() { if rv.CanSet() {
return true return true
} }
if _, ok := rv.Interface().(encoding.TextUnmarshaler); ok { rvi := rv.Interface()
if _, ok := rvi.(encoding.TextUnmarshaler); ok {
return true
}
if _, ok := rvi.(Unmarshaler); ok {
return true return true
} }
return false return false
} }
func e(format string, args ...interface{}) error {
return fmt.Errorf("toml: "+format, args...)
}
func badtype(expected string, data interface{}) error {
return e("cannot load TOML value of type %T into a Go %s", data, expected)
}

View file

@ -1,3 +1,4 @@
//go:build go1.16
// +build go1.16 // +build go1.16
package toml package toml
@ -6,8 +7,8 @@ import (
"io/fs" "io/fs"
) )
// DecodeFS is just like Decode, except it will automatically read the contents // DecodeFS reads the contents of a file from [fs.FS] and decodes it with
// of the file at `path` from a fs.FS instance. // [Decode].
func DecodeFS(fsys fs.FS, path string, v interface{}) (MetaData, error) { func DecodeFS(fsys fs.FS, path string, v interface{}) (MetaData, error) {
fp, err := fsys.Open(path) fp, err := fsys.Open(path)
if err != nil { if err != nil {

View file

@ -5,29 +5,17 @@ import (
"io" "io"
) )
// DEPRECATED! // Deprecated: use encoding.TextMarshaler
//
// Use the identical encoding.TextMarshaler instead. It is defined here to
// support Go 1.1 and older.
type TextMarshaler encoding.TextMarshaler type TextMarshaler encoding.TextMarshaler
// DEPRECATED! // Deprecated: use encoding.TextUnmarshaler
//
// Use the identical encoding.TextUnmarshaler instead. It is defined here to
// support Go 1.1 and older.
type TextUnmarshaler encoding.TextUnmarshaler type TextUnmarshaler encoding.TextUnmarshaler
// DEPRECATED! // Deprecated: use MetaData.PrimitiveDecode.
//
// Use MetaData.PrimitiveDecode instead.
func PrimitiveDecode(primValue Primitive, v interface{}) error { func PrimitiveDecode(primValue Primitive, v interface{}) error {
md := MetaData{decoded: make(map[string]bool)} md := MetaData{decoded: make(map[string]struct{})}
return md.unify(primValue.undecoded, rvalue(v)) return md.unify(primValue.undecoded, rvalue(v))
} }
// DEPRECATED! // Deprecated: use NewDecoder(reader).Decode(&value).
// func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { return NewDecoder(r).Decode(v) }
// Use NewDecoder(reader).Decode(&v) instead.
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
return NewDecoder(r).Decode(v)
}

View file

@ -1,13 +1,11 @@
/* // Package toml implements decoding and encoding of TOML files.
Package toml implements decoding and encoding of TOML files. //
// This package supports TOML v1.0.0, as specified at https://toml.io
This package supports TOML v1.0.0, as listed on https://toml.io //
// There is also support for delaying decoding with the Primitive type, and
There is also support for delaying decoding with the Primitive type, and // querying the set of keys in a TOML document with the MetaData type.
querying the set of keys in a TOML document with the MetaData type. //
// The github.com/BurntSushi/toml/cmd/tomlv package implements a TOML validator,
The github.com/BurntSushi/toml/cmd/tomlv package implements a TOML validator, // and can be used to verify if TOML document is valid. It can also be used to
and can be used to verify if TOML document is valid. It can also be used to // print the type of each key.
print the type of each key.
*/
package toml package toml

View file

@ -3,6 +3,7 @@ package toml
import ( import (
"bufio" "bufio"
"encoding" "encoding"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -21,12 +22,11 @@ type tomlEncodeError struct{ error }
var ( var (
errArrayNilElement = errors.New("toml: cannot encode array with nil element") errArrayNilElement = errors.New("toml: cannot encode array with nil element")
errNonString = errors.New("toml: cannot encode a map with non-string key type") errNonString = errors.New("toml: cannot encode a map with non-string key type")
errAnonNonStruct = errors.New("toml: cannot encode an anonymous field that is not a struct")
errNoKey = errors.New("toml: top-level values must be Go maps or structs") errNoKey = errors.New("toml: top-level values must be Go maps or structs")
errAnything = errors.New("") // used in testing errAnything = errors.New("") // used in testing
) )
var quotedReplacer = strings.NewReplacer( var dblQuotedReplacer = strings.NewReplacer(
"\"", "\\\"", "\"", "\\\"",
"\\", "\\\\", "\\", "\\\\",
"\x00", `\u0000`, "\x00", `\u0000`,
@ -64,35 +64,62 @@ var quotedReplacer = strings.NewReplacer(
"\x7f", `\u007f`, "\x7f", `\u007f`,
) )
var (
marshalToml = reflect.TypeOf((*Marshaler)(nil)).Elem()
marshalText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
)
// Marshaler is the interface implemented by types that can marshal themselves
// into valid TOML.
type Marshaler interface {
MarshalTOML() ([]byte, error)
}
// Encoder encodes a Go to a TOML document. // Encoder encodes a Go to a TOML document.
// //
// The mapping between Go values and TOML values should be precisely the same as // The mapping between Go values and TOML values should be precisely the same as
// for the Decode* functions. Similarly, the TextMarshaler interface is // for [Decode].
// supported by encoding the resulting bytes as strings. If you want to write //
// arbitrary binary data then you will need to use something like base64 since // time.Time is encoded as a RFC 3339 string, and time.Duration as its string
// TOML does not have any binary types. // representation.
//
// The [Marshaler] and [encoding.TextMarshaler] interfaces are supported to
// encoding the value as custom TOML.
//
// If you want to write arbitrary binary data then you will need to use
// something like base64 since TOML does not have any binary types.
// //
// When encoding TOML hashes (Go maps or structs), keys without any sub-hashes // When encoding TOML hashes (Go maps or structs), keys without any sub-hashes
// are encoded first. // are encoded first.
// //
// Go maps will be sorted alphabetically by key for deterministic output. // Go maps will be sorted alphabetically by key for deterministic output.
// //
// The toml struct tag can be used to provide the key name; if omitted the
// struct field name will be used. If the "omitempty" option is present the
// following value will be skipped:
//
// - arrays, slices, maps, and string with len of 0
// - struct with all zero values
// - bool false
//
// If omitzero is given all int and float types with a value of 0 will be
// skipped.
//
// Encoding Go values without a corresponding TOML representation will return an // Encoding Go values without a corresponding TOML representation will return an
// error. Examples of this includes maps with non-string keys, slices with nil // error. Examples of this includes maps with non-string keys, slices with nil
// elements, embedded non-struct types, and nested slices containing maps or // elements, embedded non-struct types, and nested slices containing maps or
// structs. (e.g. [][]map[string]string is not allowed but []map[string]string // structs. (e.g. [][]map[string]string is not allowed but []map[string]string
// is okay, as is []map[string][]string). // is okay, as is []map[string][]string).
// //
// NOTE: Only exported keys are encoded due to the use of reflection. Unexported // NOTE: only exported keys are encoded due to the use of reflection. Unexported
// keys are silently discarded. // keys are silently discarded.
type Encoder struct { type Encoder struct {
// The string to use for a single indentation level. The default is two // String to use for a single indentation level; default is two spaces.
// spaces.
Indent string Indent string
// hasWritten is whether we have written any output to w yet.
hasWritten bool
w *bufio.Writer w *bufio.Writer
hasWritten bool // written any output to w yet?
} }
// NewEncoder create a new Encoder. // NewEncoder create a new Encoder.
@ -103,7 +130,7 @@ func NewEncoder(w io.Writer) *Encoder {
} }
} }
// Encode writes a TOML representation of the Go value to the Encoder's writer. // Encode writes a TOML representation of the Go value to the [Encoder]'s writer.
// //
// An error is returned if the value given cannot be encoded to a valid TOML // An error is returned if the value given cannot be encoded to a valid TOML
// document. // document.
@ -130,17 +157,15 @@ func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
} }
func (enc *Encoder) encode(key Key, rv reflect.Value) { func (enc *Encoder) encode(key Key, rv reflect.Value) {
// Special case. Time needs to be in ISO8601 format. // If we can marshal the type to text, then we use that. This prevents the
// Special case. If we can marshal the type to text, then we used that. // encoder for handling these types as generic structs (or whatever the
// Basically, this prevents the encoder for handling these types as // underlying type of a TextMarshaler is).
// generic structs (or whatever the underlying type of a TextMarshaler is). switch {
switch t := rv.Interface().(type) { case isMarshaler(rv):
case time.Time, encoding.TextMarshaler:
enc.writeKeyValue(key, rv, false) enc.writeKeyValue(key, rv, false)
return return
// TODO: #76 would make this superfluous after implemented. case rv.Type() == primitiveType: // TODO: #76 would make this superfluous after implemented.
case Primitive: enc.encode(key, reflect.ValueOf(rv.Interface().(Primitive).undecoded))
enc.encode(key, reflect.ValueOf(t.undecoded))
return return
} }
@ -200,17 +225,49 @@ func (enc *Encoder) eElement(rv reflect.Value) {
enc.wf(v.In(time.UTC).Format(format)) enc.wf(v.In(time.UTC).Format(format))
} }
return return
case encoding.TextMarshaler: case Marshaler:
// Use text marshaler if it's available for this value. s, err := v.MarshalTOML()
if s, err := v.MarshalText(); err != nil { if err != nil {
encPanic(err) encPanic(err)
} else {
enc.writeQuoted(string(s))
} }
if s == nil {
encPanic(errors.New("MarshalTOML returned nil and no error"))
}
enc.w.Write(s)
return return
case encoding.TextMarshaler:
s, err := v.MarshalText()
if err != nil {
encPanic(err)
}
if s == nil {
encPanic(errors.New("MarshalText returned nil and no error"))
}
enc.writeQuoted(string(s))
return
case time.Duration:
enc.writeQuoted(v.String())
return
case json.Number:
n, _ := rv.Interface().(json.Number)
if n == "" { /// Useful zero value.
enc.w.WriteByte('0')
return
} else if v, err := n.Int64(); err == nil {
enc.eElement(reflect.ValueOf(v))
return
} else if v, err := n.Float64(); err == nil {
enc.eElement(reflect.ValueOf(v))
return
}
encPanic(fmt.Errorf("unable to convert %q to int64 or float64", n))
} }
switch rv.Kind() { switch rv.Kind() {
case reflect.Ptr:
enc.eElement(rv.Elem())
return
case reflect.String: case reflect.String:
enc.writeQuoted(rv.String()) enc.writeQuoted(rv.String())
case reflect.Bool: case reflect.Bool:
@ -246,7 +303,7 @@ func (enc *Encoder) eElement(rv reflect.Value) {
case reflect.Interface: case reflect.Interface:
enc.eElement(rv.Elem()) enc.eElement(rv.Elem())
default: default:
encPanic(fmt.Errorf("unexpected primitive type: %T", rv.Interface())) encPanic(fmt.Errorf("unexpected type: %T", rv.Interface()))
} }
} }
@ -260,14 +317,14 @@ func floatAddDecimal(fstr string) string {
} }
func (enc *Encoder) writeQuoted(s string) { func (enc *Encoder) writeQuoted(s string) {
enc.wf("\"%s\"", quotedReplacer.Replace(s)) enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
} }
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
length := rv.Len() length := rv.Len()
enc.wf("[") enc.wf("[")
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
elem := rv.Index(i) elem := eindirect(rv.Index(i))
enc.eElement(elem) enc.eElement(elem)
if i != length-1 { if i != length-1 {
enc.wf(", ") enc.wf(", ")
@ -281,12 +338,12 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
encPanic(errNoKey) encPanic(errNoKey)
} }
for i := 0; i < rv.Len(); i++ { for i := 0; i < rv.Len(); i++ {
trv := rv.Index(i) trv := eindirect(rv.Index(i))
if isNil(trv) { if isNil(trv) {
continue continue
} }
enc.newline() enc.newline()
enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) enc.wf("%s[[%s]]", enc.indentStr(key), key)
enc.newline() enc.newline()
enc.eMapOrStruct(key, trv, false) enc.eMapOrStruct(key, trv, false)
} }
@ -299,14 +356,14 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) {
enc.newline() enc.newline()
} }
if len(key) > 0 { if len(key) > 0 {
enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) enc.wf("%s[%s]", enc.indentStr(key), key)
enc.newline() enc.newline()
} }
enc.eMapOrStruct(key, rv, false) enc.eMapOrStruct(key, rv, false)
} }
func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) { func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {
switch rv := eindirect(rv); rv.Kind() { switch rv.Kind() {
case reflect.Map: case reflect.Map:
enc.eMap(key, rv, inline) enc.eMap(key, rv, inline)
case reflect.Struct: case reflect.Struct:
@ -328,7 +385,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
var mapKeysDirect, mapKeysSub []string var mapKeysDirect, mapKeysSub []string
for _, mapKey := range rv.MapKeys() { for _, mapKey := range rv.MapKeys() {
k := mapKey.String() k := mapKey.String()
if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
mapKeysSub = append(mapKeysSub, k) mapKeysSub = append(mapKeysSub, k)
} else { } else {
mapKeysDirect = append(mapKeysDirect, k) mapKeysDirect = append(mapKeysDirect, k)
@ -338,7 +395,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
var writeMapKeys = func(mapKeys []string, trailC bool) { var writeMapKeys = func(mapKeys []string, trailC bool) {
sort.Strings(mapKeys) sort.Strings(mapKeys)
for i, mapKey := range mapKeys { for i, mapKey := range mapKeys {
val := rv.MapIndex(reflect.ValueOf(mapKey)) val := eindirect(rv.MapIndex(reflect.ValueOf(mapKey)))
if isNil(val) { if isNil(val) {
continue continue
} }
@ -364,6 +421,15 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
} }
} }
const is32Bit = (32 << (^uint(0) >> 63)) == 32
func pointerTo(t reflect.Type) reflect.Type {
if t.Kind() == reflect.Ptr {
return pointerTo(t.Elem())
}
return t
}
func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
// Write keys for fields directly under this key first, because if we write // Write keys for fields directly under this key first, because if we write
// a field that creates a new table then all keys under it will be in that // a field that creates a new table then all keys under it will be in that
@ -380,38 +446,42 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
addFields = func(rt reflect.Type, rv reflect.Value, start []int) { addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
for i := 0; i < rt.NumField(); i++ { for i := 0; i < rt.NumField(); i++ {
f := rt.Field(i) f := rt.Field(i)
if f.PkgPath != "" && !f.Anonymous { /// Skip unexported fields. isEmbed := f.Anonymous && pointerTo(f.Type).Kind() == reflect.Struct
if f.PkgPath != "" && !isEmbed { /// Skip unexported fields.
continue
}
opts := getOptions(f.Tag)
if opts.skip {
continue continue
} }
frv := rv.Field(i) frv := eindirect(rv.Field(i))
// Treat anonymous struct fields with tag names as though they are // Treat anonymous struct fields with tag names as though they are
// not anonymous, like encoding/json does. // not anonymous, like encoding/json does.
// //
// Non-struct anonymous fields use the normal encoding logic. // Non-struct anonymous fields use the normal encoding logic.
if f.Anonymous { if isEmbed {
t := f.Type if getOptions(f.Tag).name == "" && frv.Kind() == reflect.Struct {
switch t.Kind() { addFields(frv.Type(), frv, append(start, f.Index...))
case reflect.Struct: continue
if getOptions(f.Tag).name == "" {
addFields(t, frv, append(start, f.Index...))
continue
}
case reflect.Ptr:
if t.Elem().Kind() == reflect.Struct && getOptions(f.Tag).name == "" {
if !frv.IsNil() {
addFields(t.Elem(), frv.Elem(), append(start, f.Index...))
}
continue
}
} }
} }
if typeIsHash(tomlTypeOfGo(frv)) { if typeIsTable(tomlTypeOfGo(frv)) {
fieldsSub = append(fieldsSub, append(start, f.Index...)) fieldsSub = append(fieldsSub, append(start, f.Index...))
} else { } else {
fieldsDirect = append(fieldsDirect, append(start, f.Index...)) // Copy so it works correct on 32bit archs; not clear why this
// is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4
// This also works fine on 64bit, but 32bit archs are somewhat
// rare and this is a wee bit faster.
if is32Bit {
copyStart := make([]int, len(start))
copy(copyStart, start)
fieldsDirect = append(fieldsDirect, append(copyStart, f.Index...))
} else {
fieldsDirect = append(fieldsDirect, append(start, f.Index...))
}
} }
} }
} }
@ -420,7 +490,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
writeFields := func(fields [][]int) { writeFields := func(fields [][]int) {
for _, fieldIndex := range fields { for _, fieldIndex := range fields {
fieldType := rt.FieldByIndex(fieldIndex) fieldType := rt.FieldByIndex(fieldIndex)
fieldVal := rv.FieldByIndex(fieldIndex) fieldVal := eindirect(rv.FieldByIndex(fieldIndex))
if isNil(fieldVal) { /// Don't write anything for nil fields. if isNil(fieldVal) { /// Don't write anything for nil fields.
continue continue
@ -434,7 +504,8 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
if opts.name != "" { if opts.name != "" {
keyName = opts.name keyName = opts.name
} }
if opts.omitempty && isEmpty(fieldVal) {
if opts.omitempty && enc.isEmpty(fieldVal) {
continue continue
} }
if opts.omitzero && isZero(fieldVal) { if opts.omitzero && isZero(fieldVal) {
@ -462,17 +533,32 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
} }
} }
// tomlTypeName returns the TOML type name of the Go value's type. It is // tomlTypeOfGo returns the TOML type name of the Go value's type.
// used to determine whether the types of array elements are mixed (which is //
// forbidden). If the Go value is nil, then it is illegal for it to be an array // It is used to determine whether the types of array elements are mixed (which
// element, and valueIsNil is returned as true. // is forbidden). If the Go value is nil, then it is illegal for it to be an
// array element, and valueIsNil is returned as true.
// Returns the TOML type of a Go value. The type may be `nil`, which means //
// no concrete TOML type could be found. // The type may be `nil`, which means no concrete TOML type could be found.
func tomlTypeOfGo(rv reflect.Value) tomlType { func tomlTypeOfGo(rv reflect.Value) tomlType {
if isNil(rv) || !rv.IsValid() { if isNil(rv) || !rv.IsValid() {
return nil return nil
} }
if rv.Kind() == reflect.Struct {
if rv.Type() == timeType {
return tomlDatetime
}
if isMarshaler(rv) {
return tomlString
}
return tomlHash
}
if isMarshaler(rv) {
return tomlString
}
switch rv.Kind() { switch rv.Kind() {
case reflect.Bool: case reflect.Bool:
return tomlBool return tomlBool
@ -484,7 +570,7 @@ func tomlTypeOfGo(rv reflect.Value) tomlType {
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return tomlFloat return tomlFloat
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
if typeEqual(tomlHash, tomlArrayType(rv)) { if isTableArray(rv) {
return tomlArrayHash return tomlArrayHash
} }
return tomlArray return tomlArray
@ -494,56 +580,35 @@ func tomlTypeOfGo(rv reflect.Value) tomlType {
return tomlString return tomlString
case reflect.Map: case reflect.Map:
return tomlHash return tomlHash
case reflect.Struct:
switch rv.Interface().(type) {
case time.Time:
return tomlDatetime
case encoding.TextMarshaler:
return tomlString
default:
// Someone used a pointer receiver: we can make it work for pointer
// values.
if rv.CanAddr() {
_, ok := rv.Addr().Interface().(encoding.TextMarshaler)
if ok {
return tomlString
}
}
return tomlHash
}
default: default:
_, ok := rv.Interface().(encoding.TextMarshaler)
if ok {
return tomlString
}
encPanic(errors.New("unsupported type: " + rv.Kind().String())) encPanic(errors.New("unsupported type: " + rv.Kind().String()))
panic("") // Need *some* return value panic("unreachable")
} }
} }
// tomlArrayType returns the element type of a TOML array. The type returned func isMarshaler(rv reflect.Value) bool {
// may be nil if it cannot be determined (e.g., a nil slice or a zero length return rv.Type().Implements(marshalText) || rv.Type().Implements(marshalToml)
// slize). This function may also panic if it finds a type that cannot be }
// expressed in TOML (such as nil elements, heterogeneous arrays or directly
// nested arrays of tables). // isTableArray reports if all entries in the array or slice are a table.
func tomlArrayType(rv reflect.Value) tomlType { func isTableArray(arr reflect.Value) bool {
if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { if isNil(arr) || !arr.IsValid() || arr.Len() == 0 {
return nil return false
} }
/// Don't allow nil. ret := true
rvlen := rv.Len() for i := 0; i < arr.Len(); i++ {
for i := 1; i < rvlen; i++ { tt := tomlTypeOfGo(eindirect(arr.Index(i)))
if tomlTypeOfGo(rv.Index(i)) == nil { // Don't allow nil.
if tt == nil {
encPanic(errArrayNilElement) encPanic(errArrayNilElement)
} }
}
firstType := tomlTypeOfGo(rv.Index(0)) if ret && !typeEqual(tomlHash, tt) {
if firstType == nil { ret = false
encPanic(errArrayNilElement) }
} }
return firstType return ret
} }
type tagOptions struct { type tagOptions struct {
@ -584,10 +649,26 @@ func isZero(rv reflect.Value) bool {
return false return false
} }
func isEmpty(rv reflect.Value) bool { func (enc *Encoder) isEmpty(rv reflect.Value) bool {
switch rv.Kind() { switch rv.Kind() {
case reflect.Array, reflect.Slice, reflect.Map, reflect.String: case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
return rv.Len() == 0 return rv.Len() == 0
case reflect.Struct:
if rv.Type().Comparable() {
return reflect.Zero(rv.Type()).Interface() == rv.Interface()
}
// Need to also check if all the fields are empty, otherwise something
// like this with uncomparable types will always return true:
//
// type a struct{ field b }
// type b struct{ s []string }
// s := a{field: b{s: []string{"AAA"}}}
for i := 0; i < rv.NumField(); i++ {
if !enc.isEmpty(rv.Field(i)) {
return false
}
}
return true
case reflect.Bool: case reflect.Bool:
return !rv.Bool() return !rv.Bool()
} }
@ -602,9 +683,15 @@ func (enc *Encoder) newline() {
// Write a key/value pair: // Write a key/value pair:
// //
// key = <any value> // key = <any value>
// //
// If inline is true it won't add a newline at the end. // This is also used for "k = v" in inline tables; so something like this will
// be written in three calls:
//
// ┌───────────────────┐
// │ ┌───┐ ┌────┐│
// v v v v vv
// key = {k = 1, k2 = 2}
func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
if len(key) == 0 { if len(key) == 0 {
encPanic(errNoKey) encPanic(errNoKey)
@ -617,7 +704,8 @@ func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
} }
func (enc *Encoder) wf(format string, v ...interface{}) { func (enc *Encoder) wf(format string, v ...interface{}) {
if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { _, err := fmt.Fprintf(enc.w, format, v...)
if err != nil {
encPanic(err) encPanic(err)
} }
enc.hasWritten = true enc.hasWritten = true
@ -631,13 +719,25 @@ func encPanic(err error) {
panic(tomlEncodeError{err}) panic(tomlEncodeError{err})
} }
// Resolve any level of pointers to the actual value (e.g. **string → string).
func eindirect(v reflect.Value) reflect.Value { func eindirect(v reflect.Value) reflect.Value {
switch v.Kind() { if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
case reflect.Ptr, reflect.Interface: if isMarshaler(v) {
return eindirect(v.Elem()) return v
default: }
if v.CanAddr() { /// Special case for marshalers; see #358.
if pv := v.Addr(); isMarshaler(pv) {
return pv
}
}
return v return v
} }
if v.IsNil() {
return v
}
return eindirect(v.Elem())
} }
func isNil(rv reflect.Value) bool { func isNil(rv reflect.Value) bool {

279
vendor/github.com/BurntSushi/toml/error.go generated vendored Normal file
View file

@ -0,0 +1,279 @@
package toml
import (
"fmt"
"strings"
)
// ParseError is returned when there is an error parsing the TOML syntax such as
// invalid syntax, duplicate keys, etc.
//
// In addition to the error message itself, you can also print detailed location
// information with context by using [ErrorWithPosition]:
//
// toml: error: Key 'fruit' was already created and cannot be used as an array.
//
// At line 4, column 2-7:
//
// 2 | fruit = []
// 3 |
// 4 | [[fruit]] # Not allowed
// ^^^^^
//
// [ErrorWithUsage] can be used to print the above with some more detailed usage
// guidance:
//
// toml: error: newlines not allowed within inline tables
//
// At line 1, column 18:
//
// 1 | x = [{ key = 42 #
// ^
//
// Error help:
//
// Inline tables must always be on a single line:
//
// table = {key = 42, second = 43}
//
// It is invalid to split them over multiple lines like so:
//
// # INVALID
// table = {
// key = 42,
// second = 43
// }
//
// Use regular for this:
//
// [table]
// key = 42
// second = 43
type ParseError struct {
Message string // Short technical message.
Usage string // Longer message with usage guidance; may be blank.
Position Position // Position of the error
LastKey string // Last parsed key, may be blank.
// Line the error occurred.
//
// Deprecated: use [Position].
Line int
err error
input string
}
// Position of an error.
type Position struct {
Line int // Line number, starting at 1.
Start int // Start of error, as byte offset starting at 0.
Len int // Lenght in bytes.
}
func (pe ParseError) Error() string {
msg := pe.Message
if msg == "" { // Error from errorf()
msg = pe.err.Error()
}
if pe.LastKey == "" {
return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, msg)
}
return fmt.Sprintf("toml: line %d (last key %q): %s",
pe.Position.Line, pe.LastKey, msg)
}
// ErrorWithUsage() returns the error with detailed location context.
//
// See the documentation on [ParseError].
func (pe ParseError) ErrorWithPosition() string {
if pe.input == "" { // Should never happen, but just in case.
return pe.Error()
}
var (
lines = strings.Split(pe.input, "\n")
col = pe.column(lines)
b = new(strings.Builder)
)
msg := pe.Message
if msg == "" {
msg = pe.err.Error()
}
// TODO: don't show control characters as literals? This may not show up
// well everywhere.
if pe.Position.Len == 1 {
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n",
msg, pe.Position.Line, col+1)
} else {
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n",
msg, pe.Position.Line, col, col+pe.Position.Len)
}
if pe.Position.Line > 2 {
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, lines[pe.Position.Line-3])
}
if pe.Position.Line > 1 {
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, lines[pe.Position.Line-2])
}
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, lines[pe.Position.Line-1])
fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col), strings.Repeat("^", pe.Position.Len))
return b.String()
}
// ErrorWithUsage() returns the error with detailed location context and usage
// guidance.
//
// See the documentation on [ParseError].
func (pe ParseError) ErrorWithUsage() string {
m := pe.ErrorWithPosition()
if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" {
lines := strings.Split(strings.TrimSpace(u.Usage()), "\n")
for i := range lines {
if lines[i] != "" {
lines[i] = " " + lines[i]
}
}
return m + "Error help:\n\n" + strings.Join(lines, "\n") + "\n"
}
return m
}
func (pe ParseError) column(lines []string) int {
var pos, col int
for i := range lines {
ll := len(lines[i]) + 1 // +1 for the removed newline
if pos+ll >= pe.Position.Start {
col = pe.Position.Start - pos
if col < 0 { // Should never happen, but just in case.
col = 0
}
break
}
pos += ll
}
return col
}
type (
errLexControl struct{ r rune }
errLexEscape struct{ r rune }
errLexUTF8 struct{ b byte }
errLexInvalidNum struct{ v string }
errLexInvalidDate struct{ v string }
errLexInlineTableNL struct{}
errLexStringNL struct{}
errParseRange struct {
i interface{} // int or float
size string // "int64", "uint16", etc.
}
errParseDuration struct{ d string }
)
func (e errLexControl) Error() string {
return fmt.Sprintf("TOML files cannot contain control characters: '0x%02x'", e.r)
}
func (e errLexControl) Usage() string { return "" }
func (e errLexEscape) Error() string { return fmt.Sprintf(`invalid escape in string '\%c'`, e.r) }
func (e errLexEscape) Usage() string { return usageEscape }
func (e errLexUTF8) Error() string { return fmt.Sprintf("invalid UTF-8 byte: 0x%02x", e.b) }
func (e errLexUTF8) Usage() string { return "" }
func (e errLexInvalidNum) Error() string { return fmt.Sprintf("invalid number: %q", e.v) }
func (e errLexInvalidNum) Usage() string { return "" }
func (e errLexInvalidDate) Error() string { return fmt.Sprintf("invalid date: %q", e.v) }
func (e errLexInvalidDate) Usage() string { return "" }
func (e errLexInlineTableNL) Error() string { return "newlines not allowed within inline tables" }
func (e errLexInlineTableNL) Usage() string { return usageInlineNewline }
func (e errLexStringNL) Error() string { return "strings cannot contain newlines" }
func (e errLexStringNL) Usage() string { return usageStringNewline }
func (e errParseRange) Error() string { return fmt.Sprintf("%v is out of range for %s", e.i, e.size) }
func (e errParseRange) Usage() string { return usageIntOverflow }
func (e errParseDuration) Error() string { return fmt.Sprintf("invalid duration: %q", e.d) }
func (e errParseDuration) Usage() string { return usageDuration }
const usageEscape = `
A '\' inside a "-delimited string is interpreted as an escape character.
The following escape sequences are supported:
\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX
To prevent a '\' from being recognized as an escape character, use either:
- a ' or '''-delimited string; escape characters aren't processed in them; or
- write two backslashes to get a single backslash: '\\'.
If you're trying to add a Windows path (e.g. "C:\Users\martin") then using '/'
instead of '\' will usually also work: "C:/Users/martin".
`
const usageInlineNewline = `
Inline tables must always be on a single line:
table = {key = 42, second = 43}
It is invalid to split them over multiple lines like so:
# INVALID
table = {
key = 42,
second = 43
}
Use regular for this:
[table]
key = 42
second = 43
`
const usageStringNewline = `
Strings must always be on a single line, and cannot span more than one line:
# INVALID
string = "Hello,
world!"
Instead use """ or ''' to split strings over multiple lines:
string = """Hello,
world!"""
`
const usageIntOverflow = `
This number is too large; this may be an error in the TOML, but it can also be a
bug in the program that uses too small of an integer.
The maximum and minimum values are:
size lowest highest
int8 -128 127
int16 -32,768 32,767
int32 -2,147,483,648 2,147,483,647
int64 -9.2 × 10¹ 9.2 × 10¹
uint8 0 255
uint16 0 65535
uint32 0 4294967295
uint64 0 1.8 × 10¹
int refers to int32 on 32-bit systems and int64 on 64-bit systems.
`
const usageDuration = `
A duration must be as "number<unit>", without any spaces. Valid units are:
ns nanoseconds (billionth of a second)
us, µs microseconds (millionth of a second)
ms milliseconds (thousands of a second)
s seconds
m minutes
h hours
You can combine multiple units; for example "5m10s" for 5 minutes and 10
seconds.
`

View file

@ -37,28 +37,14 @@ const (
itemInlineTableEnd itemInlineTableEnd
) )
const ( const eof = 0
eof = 0
comma = ','
tableStart = '['
tableEnd = ']'
arrayTableStart = '['
arrayTableEnd = ']'
tableSep = '.'
keySep = '='
arrayStart = '['
arrayEnd = ']'
commentStart = '#'
stringStart = '"'
stringEnd = '"'
rawStringStart = '\''
rawStringEnd = '\''
inlineTableStart = '{'
inlineTableEnd = '}'
)
type stateFn func(lx *lexer) stateFn type stateFn func(lx *lexer) stateFn
func (p Position) String() string {
return fmt.Sprintf("at line %d; start %d; length %d", p.Line, p.Start, p.Len)
}
type lexer struct { type lexer struct {
input string input string
start int start int
@ -67,26 +53,26 @@ type lexer struct {
state stateFn state stateFn
items chan item items chan item
// Allow for backing up up to four runes. // Allow for backing up up to 4 runes. This is necessary because TOML
// This is necessary because TOML contains 3-rune tokens (""" and '''). // contains 3-rune tokens (""" and ''').
prevWidths [4]int prevWidths [4]int
nprev int // how many of prevWidths are in use nprev int // how many of prevWidths are in use
// If we emit an eof, we can still back up, but it is not OK to call atEOF bool // If we emit an eof, we can still back up, but it is not OK to call next again.
// next again.
atEOF bool
// A stack of state functions used to maintain context. // A stack of state functions used to maintain context.
// The idea is to reuse parts of the state machine in various places. //
// For example, values can appear at the top level or within arbitrarily // The idea is to reuse parts of the state machine in various places. For
// nested arrays. The last state on the stack is used after a value has // example, values can appear at the top level or within arbitrarily nested
// been lexed. Similarly for comments. // arrays. The last state on the stack is used after a value has been lexed.
// Similarly for comments.
stack []stateFn stack []stateFn
} }
type item struct { type item struct {
typ itemType typ itemType
val string val string
line int err error
pos Position
} }
func (lx *lexer) nextItem() item { func (lx *lexer) nextItem() item {
@ -96,7 +82,7 @@ func (lx *lexer) nextItem() item {
return item return item
default: default:
lx.state = lx.state(lx) lx.state = lx.state(lx)
//fmt.Printf(" STATE %-24s current: %-10q stack: %s\n", lx.state, lx.current(), lx.stack) //fmt.Printf(" STATE %-24s current: %-10s stack: %s\n", lx.state, lx.current(), lx.stack)
} }
} }
} }
@ -105,9 +91,9 @@ func lex(input string) *lexer {
lx := &lexer{ lx := &lexer{
input: input, input: input,
state: lexTop, state: lexTop,
line: 1,
items: make(chan item, 10), items: make(chan item, 10),
stack: make([]stateFn, 0, 10), stack: make([]stateFn, 0, 10),
line: 1,
} }
return lx return lx
} }
@ -129,13 +115,30 @@ func (lx *lexer) current() string {
return lx.input[lx.start:lx.pos] return lx.input[lx.start:lx.pos]
} }
func (lx lexer) getPos() Position {
p := Position{
Line: lx.line,
Start: lx.start,
Len: lx.pos - lx.start,
}
if p.Len <= 0 {
p.Len = 1
}
return p
}
func (lx *lexer) emit(typ itemType) { func (lx *lexer) emit(typ itemType) {
lx.items <- item{typ, lx.current(), lx.line} // Needed for multiline strings ending with an incomplete UTF-8 sequence.
if lx.start > lx.pos {
lx.error(errLexUTF8{lx.input[lx.pos]})
return
}
lx.items <- item{typ: typ, pos: lx.getPos(), val: lx.current()}
lx.start = lx.pos lx.start = lx.pos
} }
func (lx *lexer) emitTrim(typ itemType) { func (lx *lexer) emitTrim(typ itemType) {
lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} lx.items <- item{typ: typ, pos: lx.getPos(), val: strings.TrimSpace(lx.current())}
lx.start = lx.pos lx.start = lx.pos
} }
@ -160,7 +163,13 @@ func (lx *lexer) next() (r rune) {
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
if r == utf8.RuneError { if r == utf8.RuneError {
lx.errorf("invalid UTF-8 byte at position %d (line %d): 0x%02x", lx.pos, lx.line, lx.input[lx.pos]) lx.error(errLexUTF8{lx.input[lx.pos]})
return utf8.RuneError
}
// Note: don't use peek() here, as this calls next().
if isControl(r) || (r == '\r' && (len(lx.input)-1 == lx.pos || lx.input[lx.pos+1] != '\n')) {
lx.errorControlChar(r)
return utf8.RuneError return utf8.RuneError
} }
@ -188,6 +197,7 @@ func (lx *lexer) backup() {
lx.prevWidths[1] = lx.prevWidths[2] lx.prevWidths[1] = lx.prevWidths[2]
lx.prevWidths[2] = lx.prevWidths[3] lx.prevWidths[2] = lx.prevWidths[3]
lx.nprev-- lx.nprev--
lx.pos -= w lx.pos -= w
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
lx.line-- lx.line--
@ -223,18 +233,58 @@ func (lx *lexer) skip(pred func(rune) bool) {
} }
} }
// errorf stops all lexing by emitting an error and returning `nil`. // error stops all lexing by emitting an error and returning `nil`.
//
// Note that any value that is a character is escaped if it's a special // Note that any value that is a character is escaped if it's a special
// character (newlines, tabs, etc.). // character (newlines, tabs, etc.).
func (lx *lexer) errorf(format string, values ...interface{}) stateFn { func (lx *lexer) error(err error) stateFn {
lx.items <- item{ if lx.atEOF {
itemError, return lx.errorPrevLine(err)
fmt.Sprintf(format, values...),
lx.line,
} }
lx.items <- item{typ: itemError, pos: lx.getPos(), err: err}
return nil return nil
} }
// errorfPrevline is like error(), but sets the position to the last column of
// the previous line.
//
// This is so that unexpected EOF or NL errors don't show on a new blank line.
func (lx *lexer) errorPrevLine(err error) stateFn {
pos := lx.getPos()
pos.Line--
pos.Len = 1
pos.Start = lx.pos - 1
lx.items <- item{typ: itemError, pos: pos, err: err}
return nil
}
// errorPos is like error(), but allows explicitly setting the position.
func (lx *lexer) errorPos(start, length int, err error) stateFn {
pos := lx.getPos()
pos.Start = start
pos.Len = length
lx.items <- item{typ: itemError, pos: pos, err: err}
return nil
}
// errorf is like error, and creates a new error.
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
if lx.atEOF {
pos := lx.getPos()
pos.Line--
pos.Len = 1
pos.Start = lx.pos - 1
lx.items <- item{typ: itemError, pos: pos, err: fmt.Errorf(format, values...)}
return nil
}
lx.items <- item{typ: itemError, pos: lx.getPos(), err: fmt.Errorf(format, values...)}
return nil
}
func (lx *lexer) errorControlChar(cc rune) stateFn {
return lx.errorPos(lx.pos-1, 1, errLexControl{cc})
}
// lexTop consumes elements at the top level of TOML data. // lexTop consumes elements at the top level of TOML data.
func lexTop(lx *lexer) stateFn { func lexTop(lx *lexer) stateFn {
r := lx.next() r := lx.next()
@ -242,10 +292,10 @@ func lexTop(lx *lexer) stateFn {
return lexSkip(lx, lexTop) return lexSkip(lx, lexTop)
} }
switch r { switch r {
case commentStart: case '#':
lx.push(lexTop) lx.push(lexTop)
return lexCommentStart return lexCommentStart
case tableStart: case '[':
return lexTableStart return lexTableStart
case eof: case eof:
if lx.pos > lx.start { if lx.pos > lx.start {
@ -268,7 +318,7 @@ func lexTop(lx *lexer) stateFn {
func lexTopEnd(lx *lexer) stateFn { func lexTopEnd(lx *lexer) stateFn {
r := lx.next() r := lx.next()
switch { switch {
case r == commentStart: case r == '#':
// a comment will read to a newline for us. // a comment will read to a newline for us.
lx.push(lexTop) lx.push(lexTop)
return lexCommentStart return lexCommentStart
@ -292,7 +342,7 @@ func lexTopEnd(lx *lexer) stateFn {
// It also handles the case that this is an item in an array of tables. // It also handles the case that this is an item in an array of tables.
// e.g., '[[name]]'. // e.g., '[[name]]'.
func lexTableStart(lx *lexer) stateFn { func lexTableStart(lx *lexer) stateFn {
if lx.peek() == arrayTableStart { if lx.peek() == '[' {
lx.next() lx.next()
lx.emit(itemArrayTableStart) lx.emit(itemArrayTableStart)
lx.push(lexArrayTableEnd) lx.push(lexArrayTableEnd)
@ -309,10 +359,8 @@ func lexTableEnd(lx *lexer) stateFn {
} }
func lexArrayTableEnd(lx *lexer) stateFn { func lexArrayTableEnd(lx *lexer) stateFn {
if r := lx.next(); r != arrayTableEnd { if r := lx.next(); r != ']' {
return lx.errorf( return lx.errorf("expected end of table array name delimiter ']', but got %q instead", r)
"expected end of table array name delimiter %q, but got %q instead",
arrayTableEnd, r)
} }
lx.emit(itemArrayTableEnd) lx.emit(itemArrayTableEnd)
return lexTopEnd return lexTopEnd
@ -321,11 +369,11 @@ func lexArrayTableEnd(lx *lexer) stateFn {
func lexTableNameStart(lx *lexer) stateFn { func lexTableNameStart(lx *lexer) stateFn {
lx.skip(isWhitespace) lx.skip(isWhitespace)
switch r := lx.peek(); { switch r := lx.peek(); {
case r == tableEnd || r == eof: case r == ']' || r == eof:
return lx.errorf("unexpected end of table name (table names cannot be empty)") return lx.errorf("unexpected end of table name (table names cannot be empty)")
case r == tableSep: case r == '.':
return lx.errorf("unexpected table separator (table names cannot be empty)") return lx.errorf("unexpected table separator (table names cannot be empty)")
case r == stringStart || r == rawStringStart: case r == '"' || r == '\'':
lx.ignore() lx.ignore()
lx.push(lexTableNameEnd) lx.push(lexTableNameEnd)
return lexQuotedName return lexQuotedName
@ -342,10 +390,10 @@ func lexTableNameEnd(lx *lexer) stateFn {
switch r := lx.next(); { switch r := lx.next(); {
case isWhitespace(r): case isWhitespace(r):
return lexTableNameEnd return lexTableNameEnd
case r == tableSep: case r == '.':
lx.ignore() lx.ignore()
return lexTableNameStart return lexTableNameStart
case r == tableEnd: case r == ']':
return lx.pop() return lx.pop()
default: default:
return lx.errorf("expected '.' or ']' to end table name, but got %q instead", r) return lx.errorf("expected '.' or ']' to end table name, but got %q instead", r)
@ -379,10 +427,10 @@ func lexQuotedName(lx *lexer) stateFn {
switch { switch {
case isWhitespace(r): case isWhitespace(r):
return lexSkip(lx, lexValue) return lexSkip(lx, lexValue)
case r == stringStart: case r == '"':
lx.ignore() // ignore the '"' lx.ignore() // ignore the '"'
return lexString return lexString
case r == rawStringStart: case r == '\'':
lx.ignore() // ignore the "'" lx.ignore() // ignore the "'"
return lexRawString return lexRawString
case r == eof: case r == eof:
@ -400,7 +448,7 @@ func lexKeyStart(lx *lexer) stateFn {
return lx.errorf("unexpected '=': key name appears blank") return lx.errorf("unexpected '=': key name appears blank")
case r == '.': case r == '.':
return lx.errorf("unexpected '.': keys cannot start with a '.'") return lx.errorf("unexpected '.': keys cannot start with a '.'")
case r == stringStart || r == rawStringStart: case r == '"' || r == '\'':
lx.ignore() lx.ignore()
fallthrough fallthrough
default: // Bare key default: // Bare key
@ -416,7 +464,7 @@ func lexKeyNameStart(lx *lexer) stateFn {
return lx.errorf("unexpected '='") return lx.errorf("unexpected '='")
case r == '.': case r == '.':
return lx.errorf("unexpected '.'") return lx.errorf("unexpected '.'")
case r == stringStart || r == rawStringStart: case r == '"' || r == '\'':
lx.ignore() lx.ignore()
lx.push(lexKeyEnd) lx.push(lexKeyEnd)
return lexQuotedName return lexQuotedName
@ -434,7 +482,7 @@ func lexKeyEnd(lx *lexer) stateFn {
case isWhitespace(r): case isWhitespace(r):
return lexSkip(lx, lexKeyEnd) return lexSkip(lx, lexKeyEnd)
case r == eof: case r == eof:
return lx.errorf("unexpected EOF; expected key separator %q", keySep) return lx.errorf("unexpected EOF; expected key separator '='")
case r == '.': case r == '.':
lx.ignore() lx.ignore()
return lexKeyNameStart return lexKeyNameStart
@ -461,17 +509,17 @@ func lexValue(lx *lexer) stateFn {
return lexNumberOrDateStart return lexNumberOrDateStart
} }
switch r { switch r {
case arrayStart: case '[':
lx.ignore() lx.ignore()
lx.emit(itemArray) lx.emit(itemArray)
return lexArrayValue return lexArrayValue
case inlineTableStart: case '{':
lx.ignore() lx.ignore()
lx.emit(itemInlineTableStart) lx.emit(itemInlineTableStart)
return lexInlineTableValue return lexInlineTableValue
case stringStart: case '"':
if lx.accept(stringStart) { if lx.accept('"') {
if lx.accept(stringStart) { if lx.accept('"') {
lx.ignore() // Ignore """ lx.ignore() // Ignore """
return lexMultilineString return lexMultilineString
} }
@ -479,9 +527,9 @@ func lexValue(lx *lexer) stateFn {
} }
lx.ignore() // ignore the '"' lx.ignore() // ignore the '"'
return lexString return lexString
case rawStringStart: case '\'':
if lx.accept(rawStringStart) { if lx.accept('\'') {
if lx.accept(rawStringStart) { if lx.accept('\'') {
lx.ignore() // Ignore """ lx.ignore() // Ignore """
return lexMultilineRawString return lexMultilineRawString
} }
@ -520,14 +568,12 @@ func lexArrayValue(lx *lexer) stateFn {
switch { switch {
case isWhitespace(r) || isNL(r): case isWhitespace(r) || isNL(r):
return lexSkip(lx, lexArrayValue) return lexSkip(lx, lexArrayValue)
case r == commentStart: case r == '#':
lx.push(lexArrayValue) lx.push(lexArrayValue)
return lexCommentStart return lexCommentStart
case r == comma: case r == ',':
return lx.errorf("unexpected comma") return lx.errorf("unexpected comma")
case r == arrayEnd: case r == ']':
// NOTE(caleb): The spec isn't clear about whether you can have
// a trailing comma or not, so we'll allow it.
return lexArrayEnd return lexArrayEnd
} }
@ -540,22 +586,20 @@ func lexArrayValue(lx *lexer) stateFn {
// the next value (or the end of the array): it ignores whitespace and newlines // the next value (or the end of the array): it ignores whitespace and newlines
// and expects either a ',' or a ']'. // and expects either a ',' or a ']'.
func lexArrayValueEnd(lx *lexer) stateFn { func lexArrayValueEnd(lx *lexer) stateFn {
r := lx.next() switch r := lx.next(); {
switch {
case isWhitespace(r) || isNL(r): case isWhitespace(r) || isNL(r):
return lexSkip(lx, lexArrayValueEnd) return lexSkip(lx, lexArrayValueEnd)
case r == commentStart: case r == '#':
lx.push(lexArrayValueEnd) lx.push(lexArrayValueEnd)
return lexCommentStart return lexCommentStart
case r == comma: case r == ',':
lx.ignore() lx.ignore()
return lexArrayValue // move on to the next value return lexArrayValue // move on to the next value
case r == arrayEnd: case r == ']':
return lexArrayEnd return lexArrayEnd
default:
return lx.errorf("expected a comma (',') or array terminator (']'), but got %s", runeOrEOF(r))
} }
return lx.errorf(
"expected a comma or array terminator %q, but got %s instead",
arrayEnd, runeOrEOF(r))
} }
// lexArrayEnd finishes the lexing of an array. // lexArrayEnd finishes the lexing of an array.
@ -574,13 +618,13 @@ func lexInlineTableValue(lx *lexer) stateFn {
case isWhitespace(r): case isWhitespace(r):
return lexSkip(lx, lexInlineTableValue) return lexSkip(lx, lexInlineTableValue)
case isNL(r): case isNL(r):
return lx.errorf("newlines not allowed within inline tables") return lx.errorPrevLine(errLexInlineTableNL{})
case r == commentStart: case r == '#':
lx.push(lexInlineTableValue) lx.push(lexInlineTableValue)
return lexCommentStart return lexCommentStart
case r == comma: case r == ',':
return lx.errorf("unexpected comma") return lx.errorf("unexpected comma")
case r == inlineTableEnd: case r == '}':
return lexInlineTableEnd return lexInlineTableEnd
} }
lx.backup() lx.backup()
@ -596,23 +640,21 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
case isWhitespace(r): case isWhitespace(r):
return lexSkip(lx, lexInlineTableValueEnd) return lexSkip(lx, lexInlineTableValueEnd)
case isNL(r): case isNL(r):
return lx.errorf("newlines not allowed within inline tables") return lx.errorPrevLine(errLexInlineTableNL{})
case r == commentStart: case r == '#':
lx.push(lexInlineTableValueEnd) lx.push(lexInlineTableValueEnd)
return lexCommentStart return lexCommentStart
case r == comma: case r == ',':
lx.ignore() lx.ignore()
lx.skip(isWhitespace) lx.skip(isWhitespace)
if lx.peek() == '}' { if lx.peek() == '}' {
return lx.errorf("trailing comma not allowed in inline tables") return lx.errorf("trailing comma not allowed in inline tables")
} }
return lexInlineTableValue return lexInlineTableValue
case r == inlineTableEnd: case r == '}':
return lexInlineTableEnd return lexInlineTableEnd
default: default:
return lx.errorf( return lx.errorf("expected a comma or an inline table terminator '}', but got %s instead", runeOrEOF(r))
"expected a comma or an inline table terminator %q, but got %s instead",
inlineTableEnd, runeOrEOF(r))
} }
} }
@ -638,14 +680,12 @@ func lexString(lx *lexer) stateFn {
switch { switch {
case r == eof: case r == eof:
return lx.errorf(`unexpected EOF; expected '"'`) return lx.errorf(`unexpected EOF; expected '"'`)
case isControl(r) || r == '\r':
return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r)
case isNL(r): case isNL(r):
return lx.errorf("strings cannot contain newlines") return lx.errorPrevLine(errLexStringNL{})
case r == '\\': case r == '\\':
lx.push(lexString) lx.push(lexString)
return lexStringEscape return lexStringEscape
case r == stringEnd: case r == '"':
lx.backup() lx.backup()
lx.emit(itemString) lx.emit(itemString)
lx.next() lx.next()
@ -660,26 +700,33 @@ func lexString(lx *lexer) stateFn {
func lexMultilineString(lx *lexer) stateFn { func lexMultilineString(lx *lexer) stateFn {
r := lx.next() r := lx.next()
switch r { switch r {
default:
return lexMultilineString
case eof: case eof:
return lx.errorf(`unexpected EOF; expected '"""'`) return lx.errorf(`unexpected EOF; expected '"""'`)
case '\r':
if lx.peek() != '\n' {
return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r)
}
return lexMultilineString
case '\\': case '\\':
return lexMultilineStringEscape return lexMultilineStringEscape
case stringEnd: case '"':
/// Found " → try to read two more "". /// Found " → try to read two more "".
if lx.accept(stringEnd) { if lx.accept('"') {
if lx.accept(stringEnd) { if lx.accept('"') {
/// Peek ahead: the string can contain " and "", including at the /// Peek ahead: the string can contain " and "", including at the
/// end: """str""""" /// end: """str"""""
/// 6 or more at the end, however, is an error. /// 6 or more at the end, however, is an error.
if lx.peek() == stringEnd { if lx.peek() == '"' {
/// Check if we already lexed 5 's; if so we have 6 now, and /// Check if we already lexed 5 's; if so we have 6 now, and
/// that's just too many man! /// that's just too many man!
if strings.HasSuffix(lx.current(), `"""""`) { ///
/// Second check is for the edge case:
///
/// two quotes allowed.
/// vv
/// """lol \""""""
/// ^^ ^^^---- closing three
/// escaped
///
/// But ugly, but it works
if strings.HasSuffix(lx.current(), `"""""`) && !strings.HasSuffix(lx.current(), `\"""""`) {
return lx.errorf(`unexpected '""""""'`) return lx.errorf(`unexpected '""""""'`)
} }
lx.backup() lx.backup()
@ -699,12 +746,8 @@ func lexMultilineString(lx *lexer) stateFn {
} }
lx.backup() lx.backup()
} }
return lexMultilineString
} }
if isControl(r) {
return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r)
}
return lexMultilineString
} }
// lexRawString consumes a raw string. Nothing can be escaped in such a string. // lexRawString consumes a raw string. Nothing can be escaped in such a string.
@ -712,43 +755,39 @@ func lexMultilineString(lx *lexer) stateFn {
func lexRawString(lx *lexer) stateFn { func lexRawString(lx *lexer) stateFn {
r := lx.next() r := lx.next()
switch { switch {
default:
return lexRawString
case r == eof: case r == eof:
return lx.errorf(`unexpected EOF; expected "'"`) return lx.errorf(`unexpected EOF; expected "'"`)
case isControl(r) || r == '\r':
return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r)
case isNL(r): case isNL(r):
return lx.errorf("strings cannot contain newlines") return lx.errorPrevLine(errLexStringNL{})
case r == rawStringEnd: case r == '\'':
lx.backup() lx.backup()
lx.emit(itemRawString) lx.emit(itemRawString)
lx.next() lx.next()
lx.ignore() lx.ignore()
return lx.pop() return lx.pop()
} }
return lexRawString
} }
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such // lexMultilineRawString consumes a raw string. Nothing can be escaped in such
// a string. It assumes that the beginning "'''" has already been consumed and // a string. It assumes that the beginning ''' has already been consumed and
// ignored. // ignored.
func lexMultilineRawString(lx *lexer) stateFn { func lexMultilineRawString(lx *lexer) stateFn {
r := lx.next() r := lx.next()
switch r { switch r {
default:
return lexMultilineRawString
case eof: case eof:
return lx.errorf(`unexpected EOF; expected "'''"`) return lx.errorf(`unexpected EOF; expected "'''"`)
case '\r': case '\'':
if lx.peek() != '\n' {
return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r)
}
return lexMultilineRawString
case rawStringEnd:
/// Found ' → try to read two more ''. /// Found ' → try to read two more ''.
if lx.accept(rawStringEnd) { if lx.accept('\'') {
if lx.accept(rawStringEnd) { if lx.accept('\'') {
/// Peek ahead: the string can contain ' and '', including at the /// Peek ahead: the string can contain ' and '', including at the
/// end: '''str''''' /// end: '''str'''''
/// 6 or more at the end, however, is an error. /// 6 or more at the end, however, is an error.
if lx.peek() == rawStringEnd { if lx.peek() == '\'' {
/// Check if we already lexed 5 's; if so we have 6 now, and /// Check if we already lexed 5 's; if so we have 6 now, and
/// that's just too many man! /// that's just too many man!
if strings.HasSuffix(lx.current(), "'''''") { if strings.HasSuffix(lx.current(), "'''''") {
@ -771,19 +810,14 @@ func lexMultilineRawString(lx *lexer) stateFn {
} }
lx.backup() lx.backup()
} }
return lexMultilineRawString
} }
if isControl(r) {
return lx.errorf("control characters are not allowed inside strings: '0x%02x'", r)
}
return lexMultilineRawString
} }
// lexMultilineStringEscape consumes an escaped character. It assumes that the // lexMultilineStringEscape consumes an escaped character. It assumes that the
// preceding '\\' has already been consumed. // preceding '\\' has already been consumed.
func lexMultilineStringEscape(lx *lexer) stateFn { func lexMultilineStringEscape(lx *lexer) stateFn {
// Handle the special case first: if isNL(lx.next()) { /// \ escaping newline.
if isNL(lx.next()) {
return lexMultilineString return lexMultilineString
} }
lx.backup() lx.backup()
@ -817,8 +851,7 @@ func lexStringEscape(lx *lexer) stateFn {
case 'U': case 'U':
return lexLongUnicodeEscape return lexLongUnicodeEscape
} }
return lx.errorf("invalid escape character %q; only the following escape characters are allowed: "+ return lx.error(errLexEscape{r})
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
} }
func lexShortUnicodeEscape(lx *lexer) stateFn { func lexShortUnicodeEscape(lx *lexer) stateFn {
@ -1108,8 +1141,6 @@ func lexComment(lx *lexer) stateFn {
lx.backup() lx.backup()
lx.emit(itemText) lx.emit(itemText)
return lx.pop() return lx.pop()
case isControl(r):
return lx.errorf("control characters are not allowed inside comments: '0x%02x'", r)
default: default:
return lexComment return lexComment
} }
@ -1121,52 +1152,6 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn {
return nextState return nextState
} }
// isWhitespace returns true if `r` is a whitespace character according
// to the spec.
func isWhitespace(r rune) bool {
return r == '\t' || r == ' '
}
func isNL(r rune) bool {
return r == '\n' || r == '\r'
}
// Control characters except \n, \t
func isControl(r rune) bool {
switch r {
case '\t', '\r', '\n':
return false
default:
return (r >= 0x00 && r <= 0x1f) || r == 0x7f
}
}
func isDigit(r rune) bool {
return r >= '0' && r <= '9'
}
func isHexadecimal(r rune) bool {
return (r >= '0' && r <= '9') ||
(r >= 'a' && r <= 'f') ||
(r >= 'A' && r <= 'F')
}
func isOctal(r rune) bool {
return r >= '0' && r <= '7'
}
func isBinary(r rune) bool {
return r == '0' || r == '1'
}
func isBareKeyChar(r rune) bool {
return (r >= 'A' && r <= 'Z') ||
(r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') ||
r == '_' ||
r == '-'
}
func (s stateFn) String() string { func (s stateFn) String() string {
name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name() name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name()
if i := strings.LastIndexByte(name, '.'); i > -1 { if i := strings.LastIndexByte(name, '.'); i > -1 {
@ -1223,3 +1208,26 @@ func (itype itemType) String() string {
func (item item) String() string { func (item item) String() string {
return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val)
} }
func isWhitespace(r rune) bool { return r == '\t' || r == ' ' }
func isNL(r rune) bool { return r == '\n' || r == '\r' }
func isControl(r rune) bool { // Control characters except \t, \r, \n
switch r {
case '\t', '\r', '\n':
return false
default:
return (r >= 0x00 && r <= 0x1f) || r == 0x7f
}
}
func isDigit(r rune) bool { return r >= '0' && r <= '9' }
func isBinary(r rune) bool { return r == '0' || r == '1' }
func isOctal(r rune) bool { return r >= '0' && r <= '7' }
func isHexadecimal(r rune) bool {
return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')
}
func isBareKeyChar(r rune) bool {
return (r >= 'A' && r <= 'Z') ||
(r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') ||
r == '_' || r == '-'
}

View file

@ -1,34 +1,40 @@
package toml package toml
import "strings" import (
"strings"
)
// MetaData allows access to meta information about TOML data that may not be // MetaData allows access to meta information about TOML data that's not
// inferable via reflection. In particular, whether a key has been defined and // accessible otherwise.
// the TOML type of a key. //
// It allows checking if a key is defined in the TOML data, whether any keys
// were undecoded, and the TOML type of a key.
type MetaData struct { type MetaData struct {
mapping map[string]interface{}
types map[string]tomlType
keys []Key
decoded map[string]bool
context Key // Used only during decoding. context Key // Used only during decoding.
keyInfo map[string]keyInfo
mapping map[string]interface{}
keys []Key
decoded map[string]struct{}
data []byte // Input file; for errors.
} }
// IsDefined reports if the key exists in the TOML data. // IsDefined reports if the key exists in the TOML data.
// //
// The key should be specified hierarchically, for example to access the TOML // The key should be specified hierarchically, for example to access the TOML
// key "a.b.c" you would use: // key "a.b.c" you would use IsDefined("a", "b", "c"). Keys are case sensitive.
// //
// IsDefined("a", "b", "c") // Returns false for an empty key.
//
// IsDefined will return false if an empty key given. Keys are case sensitive.
func (md *MetaData) IsDefined(key ...string) bool { func (md *MetaData) IsDefined(key ...string) bool {
if len(key) == 0 { if len(key) == 0 {
return false return false
} }
var hash map[string]interface{} var (
var ok bool hash map[string]interface{}
var hashOrVal interface{} = md.mapping ok bool
hashOrVal interface{} = md.mapping
)
for _, k := range key { for _, k := range key {
if hash, ok = hashOrVal.(map[string]interface{}); !ok { if hash, ok = hashOrVal.(map[string]interface{}); !ok {
return false return false
@ -45,51 +51,12 @@ func (md *MetaData) IsDefined(key ...string) bool {
// Type will return the empty string if given an empty key or a key that does // Type will return the empty string if given an empty key or a key that does
// not exist. Keys are case sensitive. // not exist. Keys are case sensitive.
func (md *MetaData) Type(key ...string) string { func (md *MetaData) Type(key ...string) string {
fullkey := strings.Join(key, ".") if ki, ok := md.keyInfo[Key(key).String()]; ok {
if typ, ok := md.types[fullkey]; ok { return ki.tomlType.typeString()
return typ.typeString()
} }
return "" return ""
} }
// Key represents any TOML key, including key groups. Use (MetaData).Keys to get
// values of this type.
type Key []string
func (k Key) String() string { return strings.Join(k, ".") }
func (k Key) maybeQuotedAll() string {
var ss []string
for i := range k {
ss = append(ss, k.maybeQuoted(i))
}
return strings.Join(ss, ".")
}
func (k Key) maybeQuoted(i int) string {
if k[i] == "" {
return `""`
}
quote := false
for _, c := range k[i] {
if !isBareKeyChar(c) {
quote = true
break
}
}
if quote {
return `"` + quotedReplacer.Replace(k[i]) + `"`
}
return k[i]
}
func (k Key) add(piece string) Key {
newKey := make(Key, len(k)+1)
copy(newKey, k)
newKey[len(k)] = piece
return newKey
}
// Keys returns a slice of every key in the TOML data, including key groups. // Keys returns a slice of every key in the TOML data, including key groups.
// //
// Each key is itself a slice, where the first element is the top of the // Each key is itself a slice, where the first element is the top of the
@ -104,7 +71,7 @@ func (md *MetaData) Keys() []Key {
// Undecoded returns all keys that have not been decoded in the order in which // Undecoded returns all keys that have not been decoded in the order in which
// they appear in the original TOML document. // they appear in the original TOML document.
// //
// This includes keys that haven't been decoded because of a Primitive value. // This includes keys that haven't been decoded because of a [Primitive] value.
// Once the Primitive value is decoded, the keys will be considered decoded. // Once the Primitive value is decoded, the keys will be considered decoded.
// //
// Also note that decoding into an empty interface will result in no decoding, // Also note that decoding into an empty interface will result in no decoding,
@ -115,9 +82,40 @@ func (md *MetaData) Keys() []Key {
func (md *MetaData) Undecoded() []Key { func (md *MetaData) Undecoded() []Key {
undecoded := make([]Key, 0, len(md.keys)) undecoded := make([]Key, 0, len(md.keys))
for _, key := range md.keys { for _, key := range md.keys {
if !md.decoded[key.String()] { if _, ok := md.decoded[key.String()]; !ok {
undecoded = append(undecoded, key) undecoded = append(undecoded, key)
} }
} }
return undecoded return undecoded
} }
// Key represents any TOML key, including key groups. Use [MetaData.Keys] to get
// values of this type.
type Key []string
func (k Key) String() string {
ss := make([]string, len(k))
for i := range k {
ss[i] = k.maybeQuoted(i)
}
return strings.Join(ss, ".")
}
func (k Key) maybeQuoted(i int) string {
if k[i] == "" {
return `""`
}
for _, c := range k[i] {
if !isBareKeyChar(c) {
return `"` + dblQuotedReplacer.Replace(k[i]) + `"`
}
}
return k[i]
}
func (k Key) add(piece string) Key {
newKey := make(Key, len(k)+1)
copy(newKey, k)
newKey[len(k)] = piece
return newKey
}

View file

@ -1,7 +1,6 @@
package toml package toml
import ( import (
"errors"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
@ -12,35 +11,29 @@ import (
) )
type parser struct { type parser struct {
mapping map[string]interface{} lx *lexer
types map[string]tomlType context Key // Full key for the current hash in scope.
lx *lexer currentKey string // Base key name for everything except hashes.
pos Position // Current position in the TOML file.
ordered []Key // List of keys in the order that they appear in the TOML data. ordered []Key // List of keys in the order that they appear in the TOML data.
context Key // Full key for the current hash in scope.
currentKey string // Base key name for everything except hashes. keyInfo map[string]keyInfo // Map keyname → info about the TOML key.
approxLine int // Rough approximation of line number mapping map[string]interface{} // Map keyname → key value.
implicits map[string]bool // Record implied keys (e.g. 'key.group.names'). implicits map[string]struct{} // Record implicit keys (e.g. "key.group.names").
} }
// ParseError is used when a file can't be parsed: for example invalid integer type keyInfo struct {
// literals, duplicate keys, etc. pos Position
type ParseError struct { tomlType tomlType
Message string
Line int
LastKey string
}
func (pe ParseError) Error() string {
return fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
pe.Line, pe.LastKey, pe.Message)
} }
func parse(data string) (p *parser, err error) { func parse(data string) (p *parser, err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
var ok bool if pErr, ok := r.(ParseError); ok {
if err, ok = r.(ParseError); ok { pErr.input = data
err = pErr
return return
} }
panic(r) panic(r)
@ -60,16 +53,21 @@ func parse(data string) (p *parser, err error) {
if len(data) < 6 { if len(data) < 6 {
ex = len(data) ex = len(data)
} }
if strings.ContainsRune(data[:ex], 0) { if i := strings.IndexRune(data[:ex], 0); i > -1 {
return nil, errors.New("files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8") return nil, ParseError{
Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8",
Position: Position{Line: 1, Start: i, Len: 1},
Line: 1,
input: data,
}
} }
p = &parser{ p = &parser{
keyInfo: make(map[string]keyInfo),
mapping: make(map[string]interface{}), mapping: make(map[string]interface{}),
types: make(map[string]tomlType),
lx: lex(data), lx: lex(data),
ordered: make([]Key, 0), ordered: make([]Key, 0),
implicits: make(map[string]bool), implicits: make(map[string]struct{}),
} }
for { for {
item := p.next() item := p.next()
@ -82,24 +80,57 @@ func parse(data string) (p *parser, err error) {
return p, nil return p, nil
} }
func (p *parser) panicf(format string, v ...interface{}) { func (p *parser) panicErr(it item, err error) {
msg := fmt.Sprintf(format, v...)
panic(ParseError{ panic(ParseError{
Message: msg, err: err,
Line: p.approxLine, Position: it.pos,
LastKey: p.current(), Line: it.pos.Len,
LastKey: p.current(),
})
}
func (p *parser) panicItemf(it item, format string, v ...interface{}) {
panic(ParseError{
Message: fmt.Sprintf(format, v...),
Position: it.pos,
Line: it.pos.Len,
LastKey: p.current(),
})
}
func (p *parser) panicf(format string, v ...interface{}) {
panic(ParseError{
Message: fmt.Sprintf(format, v...),
Position: p.pos,
Line: p.pos.Line,
LastKey: p.current(),
}) })
} }
func (p *parser) next() item { func (p *parser) next() item {
it := p.lx.nextItem() it := p.lx.nextItem()
//fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.line, it.val) //fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.pos.Line, it.val)
if it.typ == itemError { if it.typ == itemError {
p.panicf("%s", it.val) if it.err != nil {
panic(ParseError{
Position: it.pos,
Line: it.pos.Line,
LastKey: p.current(),
err: it.err,
})
}
p.panicItemf(it, "%s", it.val)
} }
return it return it
} }
func (p *parser) nextPos() item {
it := p.next()
p.pos = it.pos
return it
}
func (p *parser) bug(format string, v ...interface{}) { func (p *parser) bug(format string, v ...interface{}) {
panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
} }
@ -119,11 +150,9 @@ func (p *parser) assertEqual(expected, got itemType) {
func (p *parser) topLevel(item item) { func (p *parser) topLevel(item item) {
switch item.typ { switch item.typ {
case itemCommentStart: // # .. case itemCommentStart: // # ..
p.approxLine = item.line
p.expect(itemText) p.expect(itemText)
case itemTableStart: // [ .. ] case itemTableStart: // [ .. ]
name := p.next() name := p.nextPos()
p.approxLine = name.line
var key Key var key Key
for ; name.typ != itemTableEnd && name.typ != itemEOF; name = p.next() { for ; name.typ != itemTableEnd && name.typ != itemEOF; name = p.next() {
@ -132,11 +161,10 @@ func (p *parser) topLevel(item item) {
p.assertEqual(itemTableEnd, name.typ) p.assertEqual(itemTableEnd, name.typ)
p.addContext(key, false) p.addContext(key, false)
p.setType("", tomlHash) p.setType("", tomlHash, item.pos)
p.ordered = append(p.ordered, key) p.ordered = append(p.ordered, key)
case itemArrayTableStart: // [[ .. ]] case itemArrayTableStart: // [[ .. ]]
name := p.next() name := p.nextPos()
p.approxLine = name.line
var key Key var key Key
for ; name.typ != itemArrayTableEnd && name.typ != itemEOF; name = p.next() { for ; name.typ != itemArrayTableEnd && name.typ != itemEOF; name = p.next() {
@ -145,13 +173,12 @@ func (p *parser) topLevel(item item) {
p.assertEqual(itemArrayTableEnd, name.typ) p.assertEqual(itemArrayTableEnd, name.typ)
p.addContext(key, true) p.addContext(key, true)
p.setType("", tomlArrayHash) p.setType("", tomlArrayHash, item.pos)
p.ordered = append(p.ordered, key) p.ordered = append(p.ordered, key)
case itemKeyStart: // key = .. case itemKeyStart: // key = ..
outerContext := p.context outerContext := p.context
/// Read all the key parts (e.g. 'a' and 'b' in 'a.b') /// Read all the key parts (e.g. 'a' and 'b' in 'a.b')
k := p.next() k := p.nextPos()
p.approxLine = k.line
var key Key var key Key
for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() { for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
key = append(key, p.keyString(k)) key = append(key, p.keyString(k))
@ -169,8 +196,9 @@ func (p *parser) topLevel(item item) {
} }
/// Set value. /// Set value.
val, typ := p.value(p.next(), false) vItem := p.next()
p.set(p.currentKey, val, typ) val, typ := p.value(vItem, false)
p.set(p.currentKey, val, typ, vItem.pos)
p.ordered = append(p.ordered, p.context.add(p.currentKey)) p.ordered = append(p.ordered, p.context.add(p.currentKey))
/// Remove the context we added (preserving any context from [tbl] lines). /// Remove the context we added (preserving any context from [tbl] lines).
@ -206,9 +234,9 @@ var datetimeRepl = strings.NewReplacer(
func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) { func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) {
switch it.typ { switch it.typ {
case itemString: case itemString:
return p.replaceEscapes(it.val), p.typeOfPrimitive(it) return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it)
case itemMultilineString: case itemMultilineString:
return p.replaceEscapes(stripFirstNewline(stripEscapedNewlines(it.val))), p.typeOfPrimitive(it) return p.replaceEscapes(it, stripFirstNewline(p.stripEscapedNewlines(it.val))), p.typeOfPrimitive(it)
case itemRawString: case itemRawString:
return it.val, p.typeOfPrimitive(it) return it.val, p.typeOfPrimitive(it)
case itemRawMultilineString: case itemRawMultilineString:
@ -240,10 +268,10 @@ func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) {
func (p *parser) valueInteger(it item) (interface{}, tomlType) { func (p *parser) valueInteger(it item) (interface{}, tomlType) {
if !numUnderscoresOK(it.val) { if !numUnderscoresOK(it.val) {
p.panicf("Invalid integer %q: underscores must be surrounded by digits", it.val) p.panicItemf(it, "Invalid integer %q: underscores must be surrounded by digits", it.val)
} }
if numHasLeadingZero(it.val) { if numHasLeadingZero(it.val) {
p.panicf("Invalid integer %q: cannot have leading zeroes", it.val) p.panicItemf(it, "Invalid integer %q: cannot have leading zeroes", it.val)
} }
num, err := strconv.ParseInt(it.val, 0, 64) num, err := strconv.ParseInt(it.val, 0, 64)
@ -254,7 +282,7 @@ func (p *parser) valueInteger(it item) (interface{}, tomlType) {
// So mark the former as a bug but the latter as a legitimate user // So mark the former as a bug but the latter as a legitimate user
// error. // error.
if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
p.panicf("Integer '%s' is out of the range of 64-bit signed integers.", it.val) p.panicErr(it, errParseRange{i: it.val, size: "int64"})
} else { } else {
p.bug("Expected integer value, but got '%s'.", it.val) p.bug("Expected integer value, but got '%s'.", it.val)
} }
@ -272,18 +300,18 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) {
}) })
for _, part := range parts { for _, part := range parts {
if !numUnderscoresOK(part) { if !numUnderscoresOK(part) {
p.panicf("Invalid float %q: underscores must be surrounded by digits", it.val) p.panicItemf(it, "Invalid float %q: underscores must be surrounded by digits", it.val)
} }
} }
if len(parts) > 0 && numHasLeadingZero(parts[0]) { if len(parts) > 0 && numHasLeadingZero(parts[0]) {
p.panicf("Invalid float %q: cannot have leading zeroes", it.val) p.panicItemf(it, "Invalid float %q: cannot have leading zeroes", it.val)
} }
if !numPeriodsOK(it.val) { if !numPeriodsOK(it.val) {
// As a special case, numbers like '123.' or '1.e2', // As a special case, numbers like '123.' or '1.e2',
// which are valid as far as Go/strconv are concerned, // which are valid as far as Go/strconv are concerned,
// must be rejected because TOML says that a fractional // must be rejected because TOML says that a fractional
// part consists of '.' followed by 1+ digits. // part consists of '.' followed by 1+ digits.
p.panicf("Invalid float %q: '.' must be followed by one or more digits", it.val) p.panicItemf(it, "Invalid float %q: '.' must be followed by one or more digits", it.val)
} }
val := strings.Replace(it.val, "_", "", -1) val := strings.Replace(it.val, "_", "", -1)
if val == "+nan" || val == "-nan" { // Go doesn't support this, but TOML spec does. if val == "+nan" || val == "-nan" { // Go doesn't support this, but TOML spec does.
@ -292,9 +320,9 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) {
num, err := strconv.ParseFloat(val, 64) num, err := strconv.ParseFloat(val, 64)
if err != nil { if err != nil {
if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange { if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
p.panicf("Float '%s' is out of the range of 64-bit IEEE-754 floating-point numbers.", it.val) p.panicErr(it, errParseRange{i: it.val, size: "float64"})
} else { } else {
p.panicf("Invalid float value: %q", it.val) p.panicItemf(it, "Invalid float value: %q", it.val)
} }
} }
return num, p.typeOfPrimitive(it) return num, p.typeOfPrimitive(it)
@ -325,18 +353,21 @@ func (p *parser) valueDatetime(it item) (interface{}, tomlType) {
} }
} }
if !ok { if !ok {
p.panicf("Invalid TOML Datetime: %q.", it.val) p.panicItemf(it, "Invalid TOML Datetime: %q.", it.val)
} }
return t, p.typeOfPrimitive(it) return t, p.typeOfPrimitive(it)
} }
func (p *parser) valueArray(it item) (interface{}, tomlType) { func (p *parser) valueArray(it item) (interface{}, tomlType) {
p.setType(p.currentKey, tomlArray) p.setType(p.currentKey, tomlArray, it.pos)
// p.setType(p.currentKey, typ)
var ( var (
array []interface{}
types []tomlType types []tomlType
// Initialize to a non-nil empty slice. This makes it consistent with
// how S = [] decodes into a non-nil slice inside something like struct
// { S []string }. See #338
array = []interface{}{}
) )
for it = p.next(); it.typ != itemArrayEnd; it = p.next() { for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
if it.typ == itemCommentStart { if it.typ == itemCommentStart {
@ -347,6 +378,12 @@ func (p *parser) valueArray(it item) (interface{}, tomlType) {
val, typ := p.value(it, true) val, typ := p.value(it, true)
array = append(array, val) array = append(array, val)
types = append(types, typ) types = append(types, typ)
// XXX: types isn't used here, we need it to record the accurate type
// information.
//
// Not entirely sure how to best store this; could use "key[0]",
// "key[1]" notation, or maybe store it on the Array type?
} }
return array, tomlArray return array, tomlArray
} }
@ -373,8 +410,7 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom
} }
/// Read all key parts. /// Read all key parts.
k := p.next() k := p.nextPos()
p.approxLine = k.line
var key Key var key Key
for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() { for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
key = append(key, p.keyString(k)) key = append(key, p.keyString(k))
@ -393,7 +429,7 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom
/// Set the value. /// Set the value.
val, typ := p.value(p.next(), false) val, typ := p.value(p.next(), false)
p.set(p.currentKey, val, typ) p.set(p.currentKey, val, typ, it.pos)
p.ordered = append(p.ordered, p.context.add(p.currentKey)) p.ordered = append(p.ordered, p.context.add(p.currentKey))
hash[p.currentKey] = val hash[p.currentKey] = val
@ -408,7 +444,7 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom
// numHasLeadingZero checks if this number has leading zeroes, allowing for '0', // numHasLeadingZero checks if this number has leading zeroes, allowing for '0',
// +/- signs, and base prefixes. // +/- signs, and base prefixes.
func numHasLeadingZero(s string) bool { func numHasLeadingZero(s string) bool {
if len(s) > 1 && s[0] == '0' && isDigit(rune(s[1])) { // >1 to allow "0" and isDigit to allow 0x if len(s) > 1 && s[0] == '0' && !(s[1] == 'b' || s[1] == 'o' || s[1] == 'x') { // Allow 0b, 0o, 0x
return true return true
} }
if len(s) > 2 && (s[0] == '-' || s[0] == '+') && s[1] == '0' { if len(s) > 2 && (s[0] == '-' || s[0] == '+') && s[1] == '0' {
@ -503,7 +539,7 @@ func (p *parser) addContext(key Key, array bool) {
if hash, ok := hashContext[k].([]map[string]interface{}); ok { if hash, ok := hashContext[k].([]map[string]interface{}); ok {
hashContext[k] = append(hash, make(map[string]interface{})) hashContext[k] = append(hash, make(map[string]interface{}))
} else { } else {
p.panicf("Key '%s' was already created and cannot be used as an array.", keyContext) p.panicf("Key '%s' was already created and cannot be used as an array.", key)
} }
} else { } else {
p.setValue(key[len(key)-1], make(map[string]interface{})) p.setValue(key[len(key)-1], make(map[string]interface{}))
@ -512,9 +548,10 @@ func (p *parser) addContext(key Key, array bool) {
} }
// set calls setValue and setType. // set calls setValue and setType.
func (p *parser) set(key string, val interface{}, typ tomlType) { func (p *parser) set(key string, val interface{}, typ tomlType, pos Position) {
p.setValue(p.currentKey, val) p.setValue(key, val)
p.setType(p.currentKey, typ) p.setType(key, typ, pos)
} }
// setValue sets the given key to the given value in the current context. // setValue sets the given key to the given value in the current context.
@ -573,28 +610,32 @@ func (p *parser) setValue(key string, value interface{}) {
hash[key] = value hash[key] = value
} }
// setType sets the type of a particular value at a given key. // setType sets the type of a particular value at a given key. It should be
// It should be called immediately AFTER setValue. // called immediately AFTER setValue.
// //
// Note that if `key` is empty, then the type given will be applied to the // Note that if `key` is empty, then the type given will be applied to the
// current context (which is either a table or an array of tables). // current context (which is either a table or an array of tables).
func (p *parser) setType(key string, typ tomlType) { func (p *parser) setType(key string, typ tomlType, pos Position) {
keyContext := make(Key, 0, len(p.context)+1) keyContext := make(Key, 0, len(p.context)+1)
for _, k := range p.context { keyContext = append(keyContext, p.context...)
keyContext = append(keyContext, k)
}
if len(key) > 0 { // allow type setting for hashes if len(key) > 0 { // allow type setting for hashes
keyContext = append(keyContext, key) keyContext = append(keyContext, key)
} }
p.types[keyContext.String()] = typ // Special case to make empty keys ("" = 1) work.
// Without it it will set "" rather than `""`.
// TODO: why is this needed? And why is this only needed here?
if len(keyContext) == 0 {
keyContext = Key{""}
}
p.keyInfo[keyContext.String()] = keyInfo{tomlType: typ, pos: pos}
} }
// Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and // Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and
// "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly). // "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly).
func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = true } func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} }
func (p *parser) removeImplicit(key Key) { p.implicits[key.String()] = false } func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) }
func (p *parser) isImplicit(key Key) bool { return p.implicits[key.String()] } func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok }
func (p *parser) isArray(key Key) bool { return p.types[key.String()] == tomlArray } func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray }
func (p *parser) addImplicitContext(key Key) { func (p *parser) addImplicitContext(key Key) {
p.addImplicit(key) p.addImplicit(key)
p.addContext(key, false) p.addContext(key, false)
@ -622,7 +663,7 @@ func stripFirstNewline(s string) string {
} }
// Remove newlines inside triple-quoted strings if a line ends with "\". // Remove newlines inside triple-quoted strings if a line ends with "\".
func stripEscapedNewlines(s string) string { func (p *parser) stripEscapedNewlines(s string) string {
split := strings.Split(s, "\n") split := strings.Split(s, "\n")
if len(split) < 1 { if len(split) < 1 {
return s return s
@ -654,6 +695,10 @@ func stripEscapedNewlines(s string) string {
continue continue
} }
if i == len(split)-1 {
p.panicf("invalid escape: '\\ '")
}
split[i] = line[:len(line)-1] // Remove \ split[i] = line[:len(line)-1] // Remove \
if len(split)-1 > i { if len(split)-1 > i {
split[i+1] = strings.TrimLeft(split[i+1], " \t\r") split[i+1] = strings.TrimLeft(split[i+1], " \t\r")
@ -662,8 +707,8 @@ func stripEscapedNewlines(s string) string {
return strings.Join(split, "") return strings.Join(split, "")
} }
func (p *parser) replaceEscapes(str string) string { func (p *parser) replaceEscapes(it item, str string) string {
var replaced []rune replaced := make([]rune, 0, len(str))
s := []byte(str) s := []byte(str)
r := 0 r := 0
for r < len(s) { for r < len(s) {
@ -681,10 +726,8 @@ func (p *parser) replaceEscapes(str string) string {
switch s[r] { switch s[r] {
default: default:
p.bug("Expected valid escape code after \\, but got %q.", s[r]) p.bug("Expected valid escape code after \\, but got %q.", s[r])
return ""
case ' ', '\t': case ' ', '\t':
p.panicf("invalid escape: '\\%c'", s[r]) p.panicItemf(it, "invalid escape: '\\%c'", s[r])
return ""
case 'b': case 'b':
replaced = append(replaced, rune(0x0008)) replaced = append(replaced, rune(0x0008))
r += 1 r += 1
@ -710,14 +753,14 @@ func (p *parser) replaceEscapes(str string) string {
// At this point, we know we have a Unicode escape of the form // At this point, we know we have a Unicode escape of the form
// `uXXXX` at [r, r+5). (Because the lexer guarantees this // `uXXXX` at [r, r+5). (Because the lexer guarantees this
// for us.) // for us.)
escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) escaped := p.asciiEscapeToUnicode(it, s[r+1:r+5])
replaced = append(replaced, escaped) replaced = append(replaced, escaped)
r += 5 r += 5
case 'U': case 'U':
// At this point, we know we have a Unicode escape of the form // At this point, we know we have a Unicode escape of the form
// `uXXXX` at [r, r+9). (Because the lexer guarantees this // `uXXXX` at [r, r+9). (Because the lexer guarantees this
// for us.) // for us.)
escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) escaped := p.asciiEscapeToUnicode(it, s[r+1:r+9])
replaced = append(replaced, escaped) replaced = append(replaced, escaped)
r += 9 r += 9
} }
@ -725,15 +768,14 @@ func (p *parser) replaceEscapes(str string) string {
return string(replaced) return string(replaced)
} }
func (p *parser) asciiEscapeToUnicode(bs []byte) rune { func (p *parser) asciiEscapeToUnicode(it item, bs []byte) rune {
s := string(bs) s := string(bs)
hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
if err != nil { if err != nil {
p.bug("Could not parse '%s' as a hexadecimal number, but the "+ p.bug("Could not parse '%s' as a hexadecimal number, but the lexer claims it's OK: %s", s, err)
"lexer claims it's OK: %s", s, err)
} }
if !utf8.ValidRune(rune(hex)) { if !utf8.ValidRune(rune(hex)) {
p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) p.panicItemf(it, "Escaped character '\\u%s' is not valid UTF-8.", s)
} }
return rune(hex) return rune(hex)
} }

View file

@ -70,8 +70,8 @@ func typeFields(t reflect.Type) []field {
next := []field{{typ: t}} next := []field{{typ: t}}
// Count of queued names for current level and the next. // Count of queued names for current level and the next.
count := map[reflect.Type]int{} var count map[reflect.Type]int
nextCount := map[reflect.Type]int{} var nextCount map[reflect.Type]int
// Types already visited at an earlier level. // Types already visited at an earlier level.
visited := map[reflect.Type]bool{} visited := map[reflect.Type]bool{}

View file

@ -16,7 +16,7 @@ func typeEqual(t1, t2 tomlType) bool {
return t1.typeString() == t2.typeString() return t1.typeString() == t2.typeString()
} }
func typeIsHash(t tomlType) bool { func typeIsTable(t tomlType) bool {
return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
} }

View file

@ -3,8 +3,7 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) [![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) [![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 is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
high-quality hashing algorithm that is much faster than anything in the Go high-quality hashing algorithm that is much faster than anything in the Go
standard library. standard library.
@ -25,8 +24,11 @@ func (*Digest) WriteString(string) (int, error)
func (*Digest) Sum64() uint64 func (*Digest) Sum64() uint64
``` ```
This implementation provides a fast pure-Go implementation and an even faster The package is written with optimized pure Go and also contains even faster
assembly implementation for amd64. 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 ## Compatibility
@ -45,19 +47,20 @@ I recommend using the latest release of Go.
Here are some quick benchmarks comparing the pure-Go and assembly Here are some quick benchmarks comparing the pure-Go and assembly
implementations of Sum64. implementations of Sum64.
| input size | purego | asm | | input size | purego | asm |
| --- | --- | --- | | ---------- | --------- | --------- |
| 5 B | 979.66 MB/s | 1291.17 MB/s | | 4 B | 1.3 GB/s | 1.2 GB/s |
| 100 B | 7475.26 MB/s | 7973.40 MB/s | | 16 B | 2.9 GB/s | 3.5 GB/s |
| 4 KB | 17573.46 MB/s | 17602.65 MB/s | | 100 B | 6.9 GB/s | 8.1 GB/s |
| 10 MB | 17131.46 MB/s | 17142.16 MB/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 18.04 with an Intel i7-8700K CPU using These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C
the following commands under Go 1.11.2: CPU using the following commands under Go 1.19.2:
``` ```
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes' benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$')
$ go test -benchtime 10s -bench '/xxhash,direct,bytes' benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
``` ```
## Projects using this package ## Projects using this package

10
vendor/github.com/cespare/xxhash/v2/testall.sh generated vendored Normal file
View file

@ -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

View file

@ -16,19 +16,11 @@ const (
prime5 uint64 = 2870177450012600261 prime5 uint64 = 2870177450012600261
) )
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where // Store the primes in an array as well.
// possible in the Go code is worth a small (but measurable) performance boost //
// by avoiding some MOVQs. Vars are needed for the asm and also are useful for // The consts are used when possible in Go code to avoid MOVs but we need a
// convenience in the Go code in a few places where we need to intentionally // contiguous array of the assembly code.
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
// result overflows a uint64).
var (
prime1v = prime1
prime2v = prime2
prime3v = prime3
prime4v = prime4
prime5v = prime5
)
// Digest implements hash.Hash64. // Digest implements hash.Hash64.
type Digest struct { type Digest struct {
@ -50,10 +42,10 @@ func New() *Digest {
// Reset clears the Digest's state so that it can be reused. // Reset clears the Digest's state so that it can be reused.
func (d *Digest) Reset() { func (d *Digest) Reset() {
d.v1 = prime1v + prime2 d.v1 = primes[0] + prime2
d.v2 = prime2 d.v2 = prime2
d.v3 = 0 d.v3 = 0
d.v4 = -prime1v d.v4 = -primes[0]
d.total = 0 d.total = 0
d.n = 0 d.n = 0
} }
@ -69,21 +61,23 @@ func (d *Digest) Write(b []byte) (n int, err error) {
n = len(b) n = len(b)
d.total += uint64(n) d.total += uint64(n)
memleft := d.mem[d.n&(len(d.mem)-1):]
if d.n+n < 32 { if d.n+n < 32 {
// This new data doesn't even fill the current block. // This new data doesn't even fill the current block.
copy(d.mem[d.n:], b) copy(memleft, b)
d.n += n d.n += n
return return
} }
if d.n > 0 { if d.n > 0 {
// Finish off the partial block. // Finish off the partial block.
copy(d.mem[d.n:], b) c := copy(memleft, b)
d.v1 = round(d.v1, u64(d.mem[0:8])) d.v1 = round(d.v1, u64(d.mem[0:8]))
d.v2 = round(d.v2, u64(d.mem[8:16])) d.v2 = round(d.v2, u64(d.mem[8:16]))
d.v3 = round(d.v3, u64(d.mem[16:24])) d.v3 = round(d.v3, u64(d.mem[16:24]))
d.v4 = round(d.v4, u64(d.mem[24:32])) d.v4 = round(d.v4, u64(d.mem[24:32]))
b = b[32-d.n:] b = b[c:]
d.n = 0 d.n = 0
} }
@ -133,21 +127,20 @@ func (d *Digest) Sum64() uint64 {
h += d.total h += d.total
i, end := 0, d.n b := d.mem[:d.n&(len(d.mem)-1)]
for ; i+8 <= end; i += 8 { for ; len(b) >= 8; b = b[8:] {
k1 := round(0, u64(d.mem[i:i+8])) k1 := round(0, u64(b[:8]))
h ^= k1 h ^= k1
h = rol27(h)*prime1 + prime4 h = rol27(h)*prime1 + prime4
} }
if i+4 <= end { if len(b) >= 4 {
h ^= uint64(u32(d.mem[i:i+4])) * prime1 h ^= uint64(u32(b[:4])) * prime1
h = rol23(h)*prime2 + prime3 h = rol23(h)*prime2 + prime3
i += 4 b = b[4:]
} }
for i < end { for ; len(b) > 0; b = b[1:] {
h ^= uint64(d.mem[i]) * prime5 h ^= uint64(b[0]) * prime5
h = rol11(h) * prime1 h = rol11(h) * prime1
i++
} }
h ^= h >> 33 h ^= h >> 33

View file

@ -1,215 +1,209 @@
//go:build !appengine && gc && !purego
// +build !appengine // +build !appengine
// +build gc // +build gc
// +build !purego // +build !purego
#include "textflag.h" #include "textflag.h"
// Register allocation: // Registers:
// AX h #define h AX
// SI pointer to advance through b #define d AX
// DX n #define p SI // pointer to advance through b
// BX loop end #define n DX
// R8 v1, k1 #define end BX // loop end
// R9 v2 #define v1 R8
// R10 v3 #define v2 R9
// R11 v4 #define v3 R10
// R12 tmp #define v4 R11
// R13 prime1v #define x R12
// R14 prime2v #define prime1 R13
// DI prime4v #define prime2 R14
#define prime4 DI
// round reads from and advances the buffer pointer in SI. #define round(acc, x) \
// It assumes that R13 has prime1v and R14 has prime2v. IMULQ prime2, x \
#define round(r) \ ADDQ x, acc \
MOVQ (SI), R12 \ ROLQ $31, acc \
ADDQ $8, SI \ IMULQ prime1, acc
IMULQ R14, R12 \
ADDQ R12, r \
ROLQ $31, r \
IMULQ R13, r
// mergeRound applies a merge round on the two registers acc and val. // round0 performs the operation x = round(0, x).
// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v. #define round0(x) \
#define mergeRound(acc, val) \ IMULQ prime2, x \
IMULQ R14, val \ ROLQ $31, x \
ROLQ $31, val \ IMULQ prime1, x
IMULQ R13, val \
XORQ val, acc \ // mergeRound applies a merge round on the two registers acc and x.
IMULQ R13, acc \ // It assumes that prime1, prime2, and prime4 have been loaded.
ADDQ DI, acc #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 // func Sum64(b []byte) uint64
TEXT ·Sum64(SB), NOSPLIT, $0-32 TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
// Load fixed primes. // Load fixed primes.
MOVQ ·prime1v(SB), R13 MOVQ ·primes+0(SB), prime1
MOVQ ·prime2v(SB), R14 MOVQ ·primes+8(SB), prime2
MOVQ ·prime4v(SB), DI MOVQ ·primes+24(SB), prime4
// Load slice. // Load slice.
MOVQ b_base+0(FP), SI MOVQ b_base+0(FP), p
MOVQ b_len+8(FP), DX MOVQ b_len+8(FP), n
LEAQ (SI)(DX*1), BX LEAQ (p)(n*1), end
// The first loop limit will be len(b)-32. // The first loop limit will be len(b)-32.
SUBQ $32, BX SUBQ $32, end
// Check whether we have at least one block. // Check whether we have at least one block.
CMPQ DX, $32 CMPQ n, $32
JLT noBlocks JLT noBlocks
// Set up initial state (v1, v2, v3, v4). // Set up initial state (v1, v2, v3, v4).
MOVQ R13, R8 MOVQ prime1, v1
ADDQ R14, R8 ADDQ prime2, v1
MOVQ R14, R9 MOVQ prime2, v2
XORQ R10, R10 XORQ v3, v3
XORQ R11, R11 XORQ v4, v4
SUBQ R13, R11 SUBQ prime1, v4
// Loop until SI > BX. blockLoop()
blockLoop:
round(R8)
round(R9)
round(R10)
round(R11)
CMPQ SI, BX MOVQ v1, h
JLE blockLoop 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
MOVQ R8, AX mergeRound(h, v1)
ROLQ $1, AX mergeRound(h, v2)
MOVQ R9, R12 mergeRound(h, v3)
ROLQ $7, R12 mergeRound(h, v4)
ADDQ R12, AX
MOVQ R10, R12
ROLQ $12, R12
ADDQ R12, AX
MOVQ R11, R12
ROLQ $18, R12
ADDQ R12, AX
mergeRound(AX, R8)
mergeRound(AX, R9)
mergeRound(AX, R10)
mergeRound(AX, R11)
JMP afterBlocks JMP afterBlocks
noBlocks: noBlocks:
MOVQ ·prime5v(SB), AX MOVQ ·primes+32(SB), h
afterBlocks: afterBlocks:
ADDQ DX, AX ADDQ n, h
// Right now BX has len(b)-32, and we want to loop until SI > len(b)-8. ADDQ $24, end
ADDQ $24, BX CMPQ p, end
JG try4
CMPQ SI, BX loop8:
JG fourByte MOVQ (p), x
ADDQ $8, p
round0(x)
XORQ x, h
ROLQ $27, h
IMULQ prime1, h
ADDQ prime4, h
wordLoop: CMPQ p, end
// Calculate k1. JLE loop8
MOVQ (SI), R8
ADDQ $8, SI
IMULQ R14, R8
ROLQ $31, R8
IMULQ R13, R8
XORQ R8, AX try4:
ROLQ $27, AX ADDQ $4, end
IMULQ R13, AX CMPQ p, end
ADDQ DI, AX JG try1
CMPQ SI, BX MOVL (p), x
JLE wordLoop ADDQ $4, p
IMULQ prime1, x
XORQ x, h
fourByte: ROLQ $23, h
ADDQ $4, BX IMULQ prime2, h
CMPQ SI, BX ADDQ ·primes+16(SB), h
JG singles
MOVL (SI), R8 try1:
ADDQ $4, SI ADDQ $4, end
IMULQ R13, R8 CMPQ p, end
XORQ R8, AX
ROLQ $23, AX
IMULQ R14, AX
ADDQ ·prime3v(SB), AX
singles:
ADDQ $4, BX
CMPQ SI, BX
JGE finalize JGE finalize
singlesLoop: loop1:
MOVBQZX (SI), R12 MOVBQZX (p), x
ADDQ $1, SI ADDQ $1, p
IMULQ ·prime5v(SB), R12 IMULQ ·primes+32(SB), x
XORQ R12, AX XORQ x, h
ROLQ $11, h
IMULQ prime1, h
ROLQ $11, AX CMPQ p, end
IMULQ R13, AX JL loop1
CMPQ SI, BX
JL singlesLoop
finalize: finalize:
MOVQ AX, R12 MOVQ h, x
SHRQ $33, R12 SHRQ $33, x
XORQ R12, AX XORQ x, h
IMULQ R14, AX IMULQ prime2, h
MOVQ AX, R12 MOVQ h, x
SHRQ $29, R12 SHRQ $29, x
XORQ R12, AX XORQ x, h
IMULQ ·prime3v(SB), AX IMULQ ·primes+16(SB), h
MOVQ AX, R12 MOVQ h, x
SHRQ $32, R12 SHRQ $32, x
XORQ R12, AX XORQ x, h
MOVQ AX, ret+24(FP) MOVQ h, ret+24(FP)
RET RET
// writeBlocks uses the same registers as above except that it uses AX to store
// the d pointer.
// func writeBlocks(d *Digest, b []byte) int // func writeBlocks(d *Digest, b []byte) int
TEXT ·writeBlocks(SB), NOSPLIT, $0-40 TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
// Load fixed primes needed for round. // Load fixed primes needed for round.
MOVQ ·prime1v(SB), R13 MOVQ ·primes+0(SB), prime1
MOVQ ·prime2v(SB), R14 MOVQ ·primes+8(SB), prime2
// Load slice. // Load slice.
MOVQ b_base+8(FP), SI MOVQ b_base+8(FP), p
MOVQ b_len+16(FP), DX MOVQ b_len+16(FP), n
LEAQ (SI)(DX*1), BX LEAQ (p)(n*1), end
SUBQ $32, BX SUBQ $32, end
// Load vN from d. // Load vN from d.
MOVQ d+0(FP), AX MOVQ s+0(FP), d
MOVQ 0(AX), R8 // v1 MOVQ 0(d), v1
MOVQ 8(AX), R9 // v2 MOVQ 8(d), v2
MOVQ 16(AX), R10 // v3 MOVQ 16(d), v3
MOVQ 24(AX), R11 // v4 MOVQ 24(d), v4
// We don't need to check the loop condition here; this function is // We don't need to check the loop condition here; this function is
// always called with at least one block of data to process. // always called with at least one block of data to process.
blockLoop: blockLoop()
round(R8)
round(R9)
round(R10)
round(R11)
CMPQ SI, BX
JLE blockLoop
// Copy vN back to d. // Copy vN back to d.
MOVQ R8, 0(AX) MOVQ v1, 0(d)
MOVQ R9, 8(AX) MOVQ v2, 8(d)
MOVQ R10, 16(AX) MOVQ v3, 16(d)
MOVQ R11, 24(AX) MOVQ v4, 24(d)
// The number of bytes written is SI minus the old base pointer. // The number of bytes written is p minus the old base pointer.
SUBQ b_base+8(FP), SI SUBQ b_base+8(FP), p
MOVQ SI, ret+32(FP) MOVQ p, ret+32(FP)
RET RET

183
vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s generated vendored Normal file
View file

@ -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

View file

@ -1,3 +1,5 @@
//go:build (amd64 || arm64) && !appengine && gc && !purego
// +build amd64 arm64
// +build !appengine // +build !appengine
// +build gc // +build gc
// +build !purego // +build !purego

View file

@ -1,4 +1,5 @@
// +build !amd64 appengine !gc purego //go:build (!amd64 && !arm64) || appengine || !gc || purego
// +build !amd64,!arm64 appengine !gc purego
package xxhash package xxhash
@ -14,10 +15,10 @@ func Sum64(b []byte) uint64 {
var h uint64 var h uint64
if n >= 32 { if n >= 32 {
v1 := prime1v + prime2 v1 := primes[0] + prime2
v2 := prime2 v2 := prime2
v3 := uint64(0) v3 := uint64(0)
v4 := -prime1v v4 := -primes[0]
for len(b) >= 32 { for len(b) >= 32 {
v1 = round(v1, u64(b[0:8:len(b)])) v1 = round(v1, u64(b[0:8:len(b)]))
v2 = round(v2, u64(b[8:16:len(b)])) v2 = round(v2, u64(b[8:16:len(b)]))
@ -36,19 +37,18 @@ func Sum64(b []byte) uint64 {
h += uint64(n) h += uint64(n)
i, end := 0, len(b) for ; len(b) >= 8; b = b[8:] {
for ; i+8 <= end; i += 8 { k1 := round(0, u64(b[:8]))
k1 := round(0, u64(b[i:i+8:len(b)]))
h ^= k1 h ^= k1
h = rol27(h)*prime1 + prime4 h = rol27(h)*prime1 + prime4
} }
if i+4 <= end { if len(b) >= 4 {
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1 h ^= uint64(u32(b[:4])) * prime1
h = rol23(h)*prime2 + prime3 h = rol23(h)*prime2 + prime3
i += 4 b = b[4:]
} }
for ; i < end; i++ { for ; len(b) > 0; b = b[1:] {
h ^= uint64(b[i]) * prime5 h ^= uint64(b[0]) * prime5
h = rol11(h) * prime1 h = rol11(h) * prime1
} }

View file

@ -1,3 +1,4 @@
//go:build appengine
// +build appengine // +build appengine
// This file contains the safe implementations of otherwise unsafe-using code. // This file contains the safe implementations of otherwise unsafe-using code.

View file

@ -1,3 +1,4 @@
//go:build !appengine
// +build !appengine // +build !appengine
// This file encapsulates usage of unsafe. // This file encapsulates usage of unsafe.
@ -11,7 +12,7 @@ import (
// In the future it's possible that compiler optimizations will make these // In the future it's possible that compiler optimizations will make these
// XxxString functions unnecessary by realizing that calls such as // XxxString functions unnecessary by realizing that calls such as
// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205. // 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 // If that happens, even if we keep these functions they can be replaced with
// the trivial safe code. // the trivial safe code.

View file

@ -11,7 +11,7 @@
<img src="https://raw.githubusercontent.com/chzyer/readline/assets/logo_f.png" /> <img src="https://raw.githubusercontent.com/chzyer/readline/assets/logo_f.png" />
</p> </p>
A powerful readline library in `Linux` `macOS` `Windows` `Solaris` A powerful readline library in `Linux` `macOS` `Windows` `Solaris` `AIX`
## Guide ## Guide

View file

@ -109,10 +109,12 @@ func (o *Operation) ioloop() {
keepInSearchMode := false keepInSearchMode := false
keepInCompleteMode := false keepInCompleteMode := false
r := o.t.ReadRune() r := o.t.ReadRune()
if o.GetConfig().FuncFilterInputRune != nil { if o.GetConfig().FuncFilterInputRune != nil {
var process bool var process bool
r, process = o.GetConfig().FuncFilterInputRune(r) r, process = o.GetConfig().FuncFilterInputRune(r)
if !process { if !process {
o.t.KickRead()
o.buf.Refresh(nil) // to refresh the line o.buf.Refresh(nil) // to refresh the line
continue // ignore this rune continue // ignore this rune
} }
@ -434,6 +436,10 @@ func (o *Operation) Slice() ([]byte, error) {
} }
func (o *Operation) Close() { func (o *Operation) Close() {
select {
case o.errchan <- io.EOF:
default:
}
o.history.Close() o.history.Close()
} }

View file

@ -17,7 +17,9 @@
// //
package readline package readline
import "io" import (
"io"
)
type Instance struct { type Instance struct {
Config *Config Config *Config
@ -270,14 +272,24 @@ func (i *Instance) ReadSlice() ([]byte, error) {
} }
// we must make sure that call Close() before process exit. // we must make sure that call Close() before process exit.
// if there has a pending reading operation, that reading will be interrupted.
// so you can capture the signal and call Instance.Close(), it's thread-safe.
func (i *Instance) Close() error { func (i *Instance) Close() error {
i.Config.Stdin.Close()
i.Operation.Close()
if err := i.Terminal.Close(); err != nil { if err := i.Terminal.Close(); err != nil {
return err return err
} }
i.Config.Stdin.Close()
i.Operation.Close()
return nil return nil
} }
// call CaptureExitSignal when you want readline exit gracefully.
func (i *Instance) CaptureExitSignal() {
CaptureExitSignal(func() {
i.Close()
})
}
func (i *Instance) Clean() { func (i *Instance) Clean() {
i.Operation.Clean() i.Operation.Clean()
} }

View file

@ -35,7 +35,7 @@ type RuneBuffer struct {
sync.Mutex sync.Mutex
} }
func (r* RuneBuffer) pushKill(text []rune) { func (r *RuneBuffer) pushKill(text []rune) {
r.lastKill = append([]rune{}, text...) r.lastKill = append([]rune{}, text...)
} }
@ -221,7 +221,7 @@ func (r *RuneBuffer) DeleteWord() {
} }
for i := init + 1; i < len(r.buf); i++ { for i := init + 1; i < len(r.buf); i++ {
if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) { if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) {
r.pushKill(r.buf[r.idx:i-1]) r.pushKill(r.buf[r.idx : i-1])
r.Refresh(func() { r.Refresh(func() {
r.buf = append(r.buf[:r.idx], r.buf[i-1:]...) r.buf = append(r.buf[:r.idx], r.buf[i-1:]...)
}) })
@ -350,7 +350,7 @@ func (r *RuneBuffer) Yank() {
return return
} }
r.Refresh(func() { r.Refresh(func() {
buf := make([]rune, 0, len(r.buf) + len(r.lastKill)) buf := make([]rune, 0, len(r.buf)+len(r.lastKill))
buf = append(buf, r.buf[:r.idx]...) buf = append(buf, r.buf[:r.idx]...)
buf = append(buf, r.lastKill...) buf = append(buf, r.lastKill...)
buf = append(buf, r.buf[r.idx:]...) buf = append(buf, r.buf[r.idx:]...)

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd solaris // +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd os400 solaris
// Package terminal provides support functions for dealing with terminals, as // Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems. // commonly found on UNIX systems.

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build solaris // +build aix os400 solaris
package readline package readline

View file

@ -125,6 +125,7 @@ func (t *Terminal) ioloop() {
var ( var (
isEscape bool isEscape bool
isEscapeEx bool isEscapeEx bool
isEscapeSS3 bool
expectNextChar bool expectNextChar bool
) )
@ -152,9 +153,15 @@ func (t *Terminal) ioloop() {
if isEscape { if isEscape {
isEscape = false isEscape = false
if r == CharEscapeEx { if r == CharEscapeEx {
// ^][
expectNextChar = true expectNextChar = true
isEscapeEx = true isEscapeEx = true
continue continue
} else if r == CharO {
// ^]O
expectNextChar = true
isEscapeSS3 = true
continue
} }
r = escapeKey(r, buf) r = escapeKey(r, buf)
} else if isEscapeEx { } else if isEscapeEx {
@ -177,6 +184,15 @@ func (t *Terminal) ioloop() {
expectNextChar = true expectNextChar = true
continue continue
} }
} else if isEscapeSS3 {
isEscapeSS3 = false
if key := readEscKey(r, buf); key != nil {
r = escapeSS3Key(key)
}
if r == 0 {
expectNextChar = true
continue
}
} }
expectNextChar = true expectNextChar = true

View file

@ -6,9 +6,11 @@ import (
"container/list" "container/list"
"fmt" "fmt"
"os" "os"
"os/signal"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
"unicode" "unicode"
) )
@ -41,6 +43,7 @@ const (
CharCtrlY = 25 CharCtrlY = 25
CharCtrlZ = 26 CharCtrlZ = 26
CharEsc = 27 CharEsc = 27
CharO = 79
CharEscapeEx = 91 CharEscapeEx = 91
CharBackspace = 127 CharBackspace = 127
) )
@ -121,6 +124,27 @@ func escapeExKey(key *escapeKeyPair) rune {
return r return r
} }
// translate EscOX SS3 codes for up/down/etc.
func escapeSS3Key(key *escapeKeyPair) rune {
var r rune
switch key.typ {
case 'D':
r = CharBackward
case 'C':
r = CharForward
case 'A':
r = CharPrev
case 'B':
r = CharNext
case 'H':
r = CharLineStart
case 'F':
r = CharLineEnd
default:
}
return r
}
type escapeKeyPair struct { type escapeKeyPair struct {
attr string attr string
typ rune typ rune
@ -275,3 +299,13 @@ func Debug(o ...interface{}) {
fmt.Fprintln(f, o...) fmt.Fprintln(f, o...)
f.Close() f.Close()
} }
func CaptureExitSignal(f func()) {
cSignal := make(chan os.Signal, 1)
signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
go func() {
for range cSignal {
f()
}
}()
}

View file

@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd solaris // +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd os400 solaris
package readline package readline

View file

@ -65,7 +65,8 @@ being called, or called more than once, as well as concurrent calls to
Unfortunately this package is not perfect either. It's possible that it is Unfortunately this package is not perfect either. It's possible that it is
still missing some interfaces provided by the go core (let me know if you find still missing some interfaces provided by the go core (let me know if you find
one), and it won't work for applications adding their own interfaces into the one), and it won't work for applications adding their own interfaces into the
mix. mix. You can however use `httpsnoop.Unwrap(w)` to access the underlying
`http.ResponseWriter` and type-assert the result to its other interfaces.
However, hopefully the explanation above has sufficiently scared you of rolling However, hopefully the explanation above has sufficiently scared you of rolling
your own solution to this problem. httpsnoop may still break your application, your own solution to this problem. httpsnoop may still break your application,

View file

@ -3,7 +3,6 @@ package httpsnoop
import ( import (
"io" "io"
"net/http" "net/http"
"sync"
"time" "time"
) )
@ -36,17 +35,23 @@ func CaptureMetrics(hnd http.Handler, w http.ResponseWriter, r *http.Request) Me
// sugar on top of this func), but is a more usable interface if your // sugar on top of this func), but is a more usable interface if your
// application doesn't use the Go http.Handler interface. // application doesn't use the Go http.Handler interface.
func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics { func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics {
m := Metrics{Code: http.StatusOK}
m.CaptureMetrics(w, fn)
return m
}
// CaptureMetrics wraps w and calls fn with the wrapped w and updates
// Metrics m with the resulting metrics. This is similar to CaptureMetricsFn,
// but allows one to customize starting Metrics object.
func (m *Metrics) CaptureMetrics(w http.ResponseWriter, fn func(http.ResponseWriter)) {
var ( var (
start = time.Now() start = time.Now()
m = Metrics{Code: http.StatusOK}
headerWritten bool headerWritten bool
lock sync.Mutex
hooks = Hooks{ hooks = Hooks{
WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc { WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc {
return func(code int) { return func(code int) {
next(code) next(code)
lock.Lock()
defer lock.Unlock()
if !headerWritten { if !headerWritten {
m.Code = code m.Code = code
headerWritten = true headerWritten = true
@ -57,8 +62,7 @@ func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metri
Write: func(next WriteFunc) WriteFunc { Write: func(next WriteFunc) WriteFunc {
return func(p []byte) (int, error) { return func(p []byte) (int, error) {
n, err := next(p) n, err := next(p)
lock.Lock()
defer lock.Unlock()
m.Written += int64(n) m.Written += int64(n)
headerWritten = true headerWritten = true
return n, err return n, err
@ -68,8 +72,7 @@ func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metri
ReadFrom: func(next ReadFromFunc) ReadFromFunc { ReadFrom: func(next ReadFromFunc) ReadFromFunc {
return func(src io.Reader) (int64, error) { return func(src io.Reader) (int64, error) {
n, err := next(src) n, err := next(src)
lock.Lock()
defer lock.Unlock()
headerWritten = true headerWritten = true
m.Written += n m.Written += n
return n, err return n, err
@ -79,6 +82,5 @@ func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metri
) )
fn(Wrap(w, hooks)) fn(Wrap(w, hooks))
m.Duration = time.Since(start) m.Duration += time.Since(start)
return m
} }

View file

@ -74,243 +74,275 @@ func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter {
// combination 1/32 // combination 1/32
case !i0 && !i1 && !i2 && !i3 && !i4: case !i0 && !i1 && !i2 && !i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
}{rw} }{rw, rw}
// combination 2/32 // combination 2/32
case !i0 && !i1 && !i2 && !i3 && i4: case !i0 && !i1 && !i2 && !i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.Pusher http.Pusher
}{rw, rw} }{rw, rw, rw}
// combination 3/32 // combination 3/32
case !i0 && !i1 && !i2 && i3 && !i4: case !i0 && !i1 && !i2 && i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
io.ReaderFrom io.ReaderFrom
}{rw, rw} }{rw, rw, rw}
// combination 4/32 // combination 4/32
case !i0 && !i1 && !i2 && i3 && i4: case !i0 && !i1 && !i2 && i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
io.ReaderFrom io.ReaderFrom
http.Pusher http.Pusher
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 5/32 // combination 5/32
case !i0 && !i1 && i2 && !i3 && !i4: case !i0 && !i1 && i2 && !i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.Hijacker http.Hijacker
}{rw, rw} }{rw, rw, rw}
// combination 6/32 // combination 6/32
case !i0 && !i1 && i2 && !i3 && i4: case !i0 && !i1 && i2 && !i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.Hijacker http.Hijacker
http.Pusher http.Pusher
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 7/32 // combination 7/32
case !i0 && !i1 && i2 && i3 && !i4: case !i0 && !i1 && i2 && i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.Hijacker http.Hijacker
io.ReaderFrom io.ReaderFrom
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 8/32 // combination 8/32
case !i0 && !i1 && i2 && i3 && i4: case !i0 && !i1 && i2 && i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.Hijacker http.Hijacker
io.ReaderFrom io.ReaderFrom
http.Pusher http.Pusher
}{rw, rw, rw, rw} }{rw, rw, rw, rw, rw}
// combination 9/32 // combination 9/32
case !i0 && i1 && !i2 && !i3 && !i4: case !i0 && i1 && !i2 && !i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
}{rw, rw} }{rw, rw, rw}
// combination 10/32 // combination 10/32
case !i0 && i1 && !i2 && !i3 && i4: case !i0 && i1 && !i2 && !i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
http.Pusher http.Pusher
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 11/32 // combination 11/32
case !i0 && i1 && !i2 && i3 && !i4: case !i0 && i1 && !i2 && i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
io.ReaderFrom io.ReaderFrom
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 12/32 // combination 12/32
case !i0 && i1 && !i2 && i3 && i4: case !i0 && i1 && !i2 && i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
io.ReaderFrom io.ReaderFrom
http.Pusher http.Pusher
}{rw, rw, rw, rw} }{rw, rw, rw, rw, rw}
// combination 13/32 // combination 13/32
case !i0 && i1 && i2 && !i3 && !i4: case !i0 && i1 && i2 && !i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
http.Hijacker http.Hijacker
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 14/32 // combination 14/32
case !i0 && i1 && i2 && !i3 && i4: case !i0 && i1 && i2 && !i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
http.Hijacker http.Hijacker
http.Pusher http.Pusher
}{rw, rw, rw, rw} }{rw, rw, rw, rw, rw}
// combination 15/32 // combination 15/32
case !i0 && i1 && i2 && i3 && !i4: case !i0 && i1 && i2 && i3 && !i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
http.Hijacker http.Hijacker
io.ReaderFrom io.ReaderFrom
}{rw, rw, rw, rw} }{rw, rw, rw, rw, rw}
// combination 16/32 // combination 16/32
case !i0 && i1 && i2 && i3 && i4: case !i0 && i1 && i2 && i3 && i4:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
http.Hijacker http.Hijacker
io.ReaderFrom io.ReaderFrom
http.Pusher http.Pusher
}{rw, rw, rw, rw, rw}
// combination 17/32
case i0 && !i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
}{rw, rw}
// combination 18/32
case i0 && !i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.Pusher
}{rw, rw, rw}
// combination 19/32
case i0 && !i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{rw, rw, rw}
// combination 20/32
case i0 && !i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw}
// combination 21/32
case i0 && !i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
}{rw, rw, rw}
// combination 22/32
case i0 && !i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
http.Pusher
}{rw, rw, rw, rw}
// combination 23/32
case i0 && !i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 24/32
case i0 && !i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 25/32
case i0 && i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
}{rw, rw, rw}
// combination 26/32
case i0 && i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Pusher
}{rw, rw, rw, rw}
// combination 27/32
case i0 && i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 28/32
case i0 && i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 29/32
case i0 && i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
}{rw, rw, rw, rw}
// combination 30/32
case i0 && i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 31/32
case i0 && i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw}
// combination 32/32
case i0 && i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw, rw} }{rw, rw, rw, rw, rw, rw}
// combination 17/32
case i0 && !i1 && !i2 && !i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
}{rw, rw, rw}
// combination 18/32
case i0 && !i1 && !i2 && !i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.Pusher
}{rw, rw, rw, rw}
// combination 19/32
case i0 && !i1 && !i2 && i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 20/32
case i0 && !i1 && !i2 && i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 21/32
case i0 && !i1 && i2 && !i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.Hijacker
}{rw, rw, rw, rw}
// combination 22/32
case i0 && !i1 && i2 && !i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.Hijacker
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 23/32
case i0 && !i1 && i2 && i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw}
// combination 24/32
case i0 && !i1 && i2 && i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw, rw}
// combination 25/32
case i0 && i1 && !i2 && !i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
}{rw, rw, rw, rw}
// combination 26/32
case i0 && i1 && !i2 && !i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Pusher
}{rw, rw, rw, rw, rw}
// combination 27/32
case i0 && i1 && !i2 && i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw, rw, rw}
// combination 28/32
case i0 && i1 && !i2 && i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw, rw}
// combination 29/32
case i0 && i1 && i2 && !i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
}{rw, rw, rw, rw, rw}
// combination 30/32
case i0 && i1 && i2 && !i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
http.Pusher
}{rw, rw, rw, rw, rw, rw}
// combination 31/32
case i0 && i1 && i2 && i3 && !i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw, rw}
// combination 32/32
case i0 && i1 && i2 && i3 && i4:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
http.Pusher
}{rw, rw, rw, rw, rw, rw, rw}
} }
panic("unreachable") panic("unreachable")
} }
@ -320,6 +352,10 @@ type rw struct {
h Hooks h Hooks
} }
func (w *rw) Unwrap() http.ResponseWriter {
return w.w
}
func (w *rw) Header() http.Header { func (w *rw) Header() http.Header {
f := w.w.(http.ResponseWriter).Header f := w.w.(http.ResponseWriter).Header
if w.h.Header != nil { if w.h.Header != nil {
@ -383,3 +419,18 @@ func (w *rw) Push(target string, opts *http.PushOptions) error {
} }
return f(target, opts) return f(target, opts)
} }
type Unwrapper interface {
Unwrap() http.ResponseWriter
}
// Unwrap returns the underlying http.ResponseWriter from within zero or more
// layers of httpsnoop wrappers.
func Unwrap(w http.ResponseWriter) http.ResponseWriter {
if rw, ok := w.(Unwrapper); ok {
// recurse until rw.Unwrap() returns a non-Unwrapper
return Unwrap(rw.Unwrap())
} else {
return w
}
}

View file

@ -68,115 +68,131 @@ func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter {
// combination 1/16 // combination 1/16
case !i0 && !i1 && !i2 && !i3: case !i0 && !i1 && !i2 && !i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
}{rw} }{rw, rw}
// combination 2/16 // combination 2/16
case !i0 && !i1 && !i2 && i3: case !i0 && !i1 && !i2 && i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
io.ReaderFrom io.ReaderFrom
}{rw, rw} }{rw, rw, rw}
// combination 3/16 // combination 3/16
case !i0 && !i1 && i2 && !i3: case !i0 && !i1 && i2 && !i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.Hijacker http.Hijacker
}{rw, rw} }{rw, rw, rw}
// combination 4/16 // combination 4/16
case !i0 && !i1 && i2 && i3: case !i0 && !i1 && i2 && i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.Hijacker http.Hijacker
io.ReaderFrom io.ReaderFrom
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 5/16 // combination 5/16
case !i0 && i1 && !i2 && !i3: case !i0 && i1 && !i2 && !i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
}{rw, rw} }{rw, rw, rw}
// combination 6/16 // combination 6/16
case !i0 && i1 && !i2 && i3: case !i0 && i1 && !i2 && i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
io.ReaderFrom io.ReaderFrom
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 7/16 // combination 7/16
case !i0 && i1 && i2 && !i3: case !i0 && i1 && i2 && !i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
http.Hijacker http.Hijacker
}{rw, rw, rw} }{rw, rw, rw, rw}
// combination 8/16 // combination 8/16
case !i0 && i1 && i2 && i3: case !i0 && i1 && i2 && i3:
return struct { return struct {
Unwrapper
http.ResponseWriter http.ResponseWriter
http.CloseNotifier http.CloseNotifier
http.Hijacker http.Hijacker
io.ReaderFrom io.ReaderFrom
}{rw, rw, rw, rw}
// combination 9/16
case i0 && !i1 && !i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
}{rw, rw}
// combination 10/16
case i0 && !i1 && !i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{rw, rw, rw}
// combination 11/16
case i0 && !i1 && i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
}{rw, rw, rw}
// combination 12/16
case i0 && !i1 && i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 13/16
case i0 && i1 && !i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
}{rw, rw, rw}
// combination 14/16
case i0 && i1 && !i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 15/16
case i0 && i1 && i2 && !i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
}{rw, rw, rw, rw}
// combination 16/16
case i0 && i1 && i2 && i3:
return struct {
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw} }{rw, rw, rw, rw, rw}
// combination 9/16
case i0 && !i1 && !i2 && !i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
}{rw, rw, rw}
// combination 10/16
case i0 && !i1 && !i2 && i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{rw, rw, rw, rw}
// combination 11/16
case i0 && !i1 && i2 && !i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.Hijacker
}{rw, rw, rw, rw}
// combination 12/16
case i0 && !i1 && i2 && i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw}
// combination 13/16
case i0 && i1 && !i2 && !i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
}{rw, rw, rw, rw}
// combination 14/16
case i0 && i1 && !i2 && i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
io.ReaderFrom
}{rw, rw, rw, rw, rw}
// combination 15/16
case i0 && i1 && i2 && !i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
}{rw, rw, rw, rw, rw}
// combination 16/16
case i0 && i1 && i2 && i3:
return struct {
Unwrapper
http.ResponseWriter
http.Flusher
http.CloseNotifier
http.Hijacker
io.ReaderFrom
}{rw, rw, rw, rw, rw, rw}
} }
panic("unreachable") panic("unreachable")
} }
@ -186,6 +202,10 @@ type rw struct {
h Hooks h Hooks
} }
func (w *rw) Unwrap() http.ResponseWriter {
return w.w
}
func (w *rw) Header() http.Header { func (w *rw) Header() http.Header {
f := w.w.(http.ResponseWriter).Header f := w.w.(http.ResponseWriter).Header
if w.h.Header != nil { if w.h.Header != nil {
@ -241,3 +261,18 @@ func (w *rw) ReadFrom(src io.Reader) (int64, error) {
} }
return f(src) return f(src)
} }
type Unwrapper interface {
Unwrap() http.ResponseWriter
}
// Unwrap returns the underlying http.ResponseWriter from within zero or more
// layers of httpsnoop wrappers.
func Unwrap(w http.ResponseWriter) http.ResponseWriter {
if rw, ok := w.(Unwrapper); ok {
// recurse until rw.Unwrap() returns a non-Unwrapper
return Unwrap(rw.Unwrap())
} else {
return w
}
}

View file

@ -1,7 +1,7 @@
package bakery package bakery
import ( import (
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// Bakery is a convenience type that contains both an Oven // Bakery is a convenience type that contains both an Oven

View file

@ -6,10 +6,10 @@ import (
"sync" "sync"
"time" "time"
errgo "gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
macaroon "gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// Op holds an entity and action to be authorized on that entity. // Op holds an entity and action to be authorized on that entity.

View file

@ -6,7 +6,7 @@ import (
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
errgo "gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
) )
// Namespace holds maps from schema URIs to the // Namespace holds maps from schema URIs to the

View file

@ -10,7 +10,7 @@ import (
"golang.org/x/crypto/nacl/box" "golang.org/x/crypto/nacl/box"
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
type caveatRecord struct { type caveatRecord struct {

View file

@ -9,7 +9,7 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// LocalThirdPartyCaveat returns a third-party caveat that, when added // LocalThirdPartyCaveat returns a third-party caveat that, when added

View file

@ -6,7 +6,7 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// DischargeAll gathers discharge macaroons for all the third party // DischargeAll gathers discharge macaroons for all the third party

View file

@ -3,9 +3,9 @@ package bakery
import ( import (
"fmt" "fmt"
errgo "gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
var ( var (

View file

@ -10,7 +10,7 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// legacyNamespace holds the standard namespace as used by // legacyNamespace holds the standard namespace as used by

View file

@ -8,10 +8,10 @@ import (
"github.com/go-macaroon-bakery/macaroonpb" "github.com/go-macaroon-bakery/macaroonpb"
"github.com/rogpeppe/fastuuid" "github.com/rogpeppe/fastuuid"
errgo "gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// MacaroonVerifier verifies macaroons and returns the operations and // MacaroonVerifier verifies macaroons and returns the operations and

View file

@ -5,10 +5,10 @@ import (
"fmt" "fmt"
"time" "time"
errgo "gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
macaroon "gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// Slice holds a slice of unbound macaroons. // Slice holds a slice of unbound macaroons.

View file

@ -11,7 +11,7 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/httprequest.v1" "gopkg.in/httprequest.v1"
"gopkg.in/macaroon-bakery.v2/bakery" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
) )
const WebBrowserInteractionKind = "browser-window" const WebBrowserInteractionKind = "browser-window"

View file

@ -7,7 +7,7 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
type httpRequestKey struct{} type httpRequestKey struct{}

View file

@ -16,8 +16,8 @@ import (
"gopkg.in/httprequest.v1" "gopkg.in/httprequest.v1"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"gopkg.in/macaroon-bakery.v2/bakery" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
var unmarshalError = httprequest.ErrorUnmarshaler(&Error{}) var unmarshalError = httprequest.ErrorUnmarshaler(&Error{})

View file

@ -12,8 +12,8 @@ import (
"gopkg.in/httprequest.v1" "gopkg.in/httprequest.v1"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"gopkg.in/macaroon-bakery.v2/bakery" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// ThirdPartyCaveatChecker is used to check third party caveats. // ThirdPartyCaveatChecker is used to check third party caveats.
@ -206,7 +206,7 @@ func (d *Discharger) Handlers() []httprequest.Handler {
return srv.Handlers(f) return srv.Handlers(f)
} }
//go:generate httprequest-generate-client gopkg.in/macaroon-bakery.v2-unstable/httpbakery dischargeHandler dischargeClient //go:generate httprequest-generate-client github.com/go-macaroon-bakery/macaroon-bakery/v3-unstable/httpbakery dischargeHandler dischargeClient
// dischargeHandler is the type used to define the httprequest handler // dischargeHandler is the type used to define the httprequest handler
// methods for a discharger. // methods for a discharger.

View file

@ -10,8 +10,8 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/httprequest.v1" "gopkg.in/httprequest.v1"
"gopkg.in/macaroon-bakery.v2/bakery" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
"gopkg.in/macaroon-bakery.v2/internal/httputil" "github.com/go-macaroon-bakery/macaroon-bakery/v3/internal/httputil"
) )
// ErrorCode holds an error code that classifies // ErrorCode holds an error code that classifies

View file

@ -8,7 +8,7 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/httprequest.v1" "gopkg.in/httprequest.v1"
"gopkg.in/macaroon-bakery.v2/bakery" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
) )
var _ bakery.ThirdPartyLocator = (*ThirdPartyLocator)(nil) var _ bakery.ThirdPartyLocator = (*ThirdPartyLocator)(nil)

View file

@ -5,10 +5,10 @@ import (
"net/http" "net/http"
"time" "time"
errgo "gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/macaroon-bakery.v2/bakery" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
"gopkg.in/macaroon-bakery.v2/bakery/checkers" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery/checkers"
) )
// Oven is like bakery.Oven except it provides a method for // Oven is like bakery.Oven except it provides a method for

View file

@ -1,15 +1,15 @@
package httpbakery package httpbakery
import ( import (
"bytes"
"context" "context"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"reflect" "reflect"
"sync" "sync"
"sync/atomic" "sync/atomic"
errgo "gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
) )
// newRetrableRequest wraps an HTTP request so that it can // newRetrableRequest wraps an HTTP request so that it can
@ -159,7 +159,8 @@ func (r *readStopper) Close() error {
return r.rreq.close() return r.rreq.close()
} }
var nopCloserType = reflect.TypeOf(ioutil.NopCloser(nil)) var nopCloserType = reflect.TypeOf(io.NopCloser(nil))
var nopCloserWriterToType = reflect.TypeOf(io.NopCloser(bytes.NewReader([]byte{})))
type readSeekCloser interface { type readSeekCloser interface {
io.ReadSeeker io.ReadSeeker
@ -173,7 +174,7 @@ func seekerFromBody(r io.ReadCloser) readSeekCloser {
return r return r
} }
rv := reflect.ValueOf(r) rv := reflect.ValueOf(r)
if rv.Type() != nopCloserType { if rv.Type() != nopCloserType && rv.Type() != nopCloserWriterToType {
return nil return nil
} }
// It's a value created by nopCloser. Extract the // It's a value created by nopCloser. Extract the

View file

@ -8,7 +8,7 @@ import (
"gopkg.in/errgo.v1" "gopkg.in/errgo.v1"
"gopkg.in/httprequest.v1" "gopkg.in/httprequest.v1"
"gopkg.in/macaroon-bakery.v2/bakery" "github.com/go-macaroon-bakery/macaroon-bakery/v3/bakery"
) )
// TODO(rog) rename this file. // TODO(rog) rename this file.

View file

@ -11,7 +11,10 @@
178inaba <masahiro.furudate@gmail.com> 178inaba <masahiro.furudate@gmail.com>
2BFL <imqksl@gmail.com> 2BFL <imqksl@gmail.com>
413x <dedifferentiator@gmail.com> 413x <dedifferentiator@gmail.com>
Abed Kibbe <abed.kibbe@gmail.com>
Abhinav Gupta <mail@abhinavg.net> Abhinav Gupta <mail@abhinavg.net>
Abhishek Veeramalla <abhishek.veeramalla@gmail.com>
aboy <b1011211@gmail.com>
adrienzieba <adrien.zieba@appdirect.com> adrienzieba <adrien.zieba@appdirect.com>
afdesk <work@afdesk.com> afdesk <work@afdesk.com>
Ahmed Hagy <a.akram93@gmail.com> Ahmed Hagy <a.akram93@gmail.com>
@ -24,15 +27,18 @@ Alec Thomas <alec@swapoff.org>
Aleks Clark <aleks.clark@gmail.com> Aleks Clark <aleks.clark@gmail.com>
Alex Bramley <a.bramley@gmail.com> Alex Bramley <a.bramley@gmail.com>
Alex Orr <Alexorr.CSE@gmail.com> Alex Orr <Alexorr.CSE@gmail.com>
Alex Su <alexsu@17.media>
Alex Unger <zyxancf@gmail.com> Alex Unger <zyxancf@gmail.com>
Alexander Harkness <me@bearbin.net> Alexander Harkness <me@bearbin.net>
Alexis Gauthiez <alexis.gauthiez@gmail.com> Alexis Gauthiez <alexis.gauthiez@gmail.com>
Ali Farooq <ali.farooq0@pm.me> Ali Farooq <ali.farooq0@pm.me>
Allan Guwatudde <guwats10@gmail.com>
Allen Sun <shlallen1990@gmail.com> Allen Sun <shlallen1990@gmail.com>
Amey Sakhadeo <me@ameyms.com> Amey Sakhadeo <me@ameyms.com>
Anders Janmyr <anders@janmyr.com> Anders Janmyr <anders@janmyr.com>
Andreas Garnæs <https://github.com/andreas> Andreas Garnæs <https://github.com/andreas>
Andrew Ryabchun <aryabchun@mail.ua> Andrew Ryabchun <aryabchun@mail.ua>
Andrew Svoboda <svoboda.andrew@gmail.com>
Andy Grunwald <andygrunwald@gmail.com> Andy Grunwald <andygrunwald@gmail.com>
Andy Hume <andyhume@gmail.com> Andy Hume <andyhume@gmail.com>
Andy Lindeman <andy@lindeman.io> Andy Lindeman <andy@lindeman.io>
@ -58,21 +64,26 @@ Beshr Kayali <beshrkayali@gmail.com>
Beyang Liu <beyang.liu@gmail.com> Beyang Liu <beyang.liu@gmail.com>
Billy Keyes <bluekeyes@gmail.com> Billy Keyes <bluekeyes@gmail.com>
Billy Lynch <wlynch92@gmail.com> Billy Lynch <wlynch92@gmail.com>
Björn Häuser <b.haeuser@rebuy.de>
Bjorn Neergaard <bjorn@neersighted.com> Bjorn Neergaard <bjorn@neersighted.com>
Björn Häuser <b.haeuser@rebuy.de>
boljen <bol.christophe@gmail.com> boljen <bol.christophe@gmail.com>
Bracken <abdawson@gmail.com>
Brad Harris <bmharris@gmail.com> Brad Harris <bmharris@gmail.com>
Brad Moylan <moylan.brad@gmail.com> Brad Moylan <moylan.brad@gmail.com>
Bradley Falzon <brad@teambrad.net> Bradley Falzon <brad@teambrad.net>
Bradley McAllister <brad.mcallister@hotmail.com> Bradley McAllister <brad.mcallister@hotmail.com>
Brandon Butler <b.butler@chia.net>
Brandon Cook <phylake@gmail.com> Brandon Cook <phylake@gmail.com>
Brett Kuhlman <kuhlman-labs@github.com>
Brett Logan <lindluni@github.com> Brett Logan <lindluni@github.com>
Brian Egizi <brian@mojotech.com> Brian Egizi <brian@mojotech.com>
Bryan Boreham <bryan@weave.works> Bryan Boreham <bryan@weave.works>
Bryan Peterson <Lazyshot@gmail.com>
Cami Diez <diezcami@gmail.com> Cami Diez <diezcami@gmail.com>
Carl Johnson <me@carlmjohnson.net> Carl Johnson <me@carlmjohnson.net>
Carlos Alexandro Becker <caarlos0@gmail.com> Carlos Alexandro Becker <caarlos0@gmail.com>
Carlos Tadeu Panato Junior <ctadeu@gmail.com> Carlos Tadeu Panato Junior <ctadeu@gmail.com>
ChandanChainani <chainanichan@gmail.com>
chandresh-pancholi <chandreshpancholi007@gmail.com> chandresh-pancholi <chandreshpancholi007@gmail.com>
Charles Fenwick Elliott <Charles@FenwickElliott.io> Charles Fenwick Elliott <Charles@FenwickElliott.io>
Charlie Yan <charlieyan08@gmail.com> Charlie Yan <charlieyan08@gmail.com>
@ -83,12 +94,17 @@ Chris Raborg <craborg57@gmail.com>
Chris Roche <chris@vsco.co> Chris Roche <chris@vsco.co>
Chris Schaefer <chris@dtzq.com> Chris Schaefer <chris@dtzq.com>
chrisforrette <chris@chrisforrette.com> chrisforrette <chris@chrisforrette.com>
Christian Bargmann <chris@cbrgm.net>
Christian Muehlhaeuser <muesli@gmail.com> Christian Muehlhaeuser <muesli@gmail.com>
Christoph Sassenberg <defsprite@gmail.com> Christoph Sassenberg <defsprite@gmail.com>
CI Monk <ci-monk@protonmail.com>
Colin Misare <github.com/cmisare> Colin Misare <github.com/cmisare>
Craig Gumbley <craiggumbley@gmail.com>
Craig Peterson <cpeterson@stackoverflow.com> Craig Peterson <cpeterson@stackoverflow.com>
Cristian Maglie <c.maglie@bug.st> Cristian Maglie <c.maglie@bug.st>
Cyb3r Jak3 <jake@jwhite.network>
Daehyeok Mun <daehyeok@gmail.com> Daehyeok Mun <daehyeok@gmail.com>
Dalton Hubble <dghubble@gmail.com>
Daniel Lanner <lanner.dan@gmail.com> Daniel Lanner <lanner.dan@gmail.com>
Daniel Leavitt <daniel.leavitt@gmail.com> Daniel Leavitt <daniel.leavitt@gmail.com>
Daniel Nilsson <daniel.nilsson1989@gmail.com> Daniel Nilsson <daniel.nilsson1989@gmail.com>
@ -98,6 +114,7 @@ Dave Henderson <dhenderson@gmail.com>
Dave Perrett <hello@daveperrett.com> Dave Perrett <hello@daveperrett.com>
Dave Protasowski <dprotaso@gmail.com> Dave Protasowski <dprotaso@gmail.com>
David Deng <daviddengcn@gmail.com> David Deng <daviddengcn@gmail.com>
David Gamba <davidgamba@gmail.com>
David J. M. Karlsen <david@davidkarlsen.com> David J. M. Karlsen <david@davidkarlsen.com>
David Jannotta <djannotta@gmail.com> David Jannotta <djannotta@gmail.com>
David Ji <github.com/davidji99> David Ji <github.com/davidji99>
@ -105,6 +122,7 @@ David Lopez Reyes <davidlopezre@gmail.com>
Davide Zipeto <dawez1@gmail.com> Davide Zipeto <dawez1@gmail.com>
Dennis Webb <dennis@bluesentryit.com> Dennis Webb <dennis@bluesentryit.com>
Derek Jobst <derekjobst@gmail.com> Derek Jobst <derekjobst@gmail.com>
DeviousLab <deviouslab@gmail.com>
Dhi Aurrahman <diorahman@rockybars.com> Dhi Aurrahman <diorahman@rockybars.com>
Diego Lapiduz <diego.lapiduz@cfpb.gov> Diego Lapiduz <diego.lapiduz@cfpb.gov>
Dmitri Shuralyov <shurcooL@gmail.com> Dmitri Shuralyov <shurcooL@gmail.com>
@ -117,6 +135,8 @@ Eivind <eivindkn@gmail.com>
Eli Uriegas <seemethere101@gmail.com> Eli Uriegas <seemethere101@gmail.com>
Elliott Beach <elliott2.71828@gmail.com> Elliott Beach <elliott2.71828@gmail.com>
Emerson Wood <emersonwood94@gmail.com> Emerson Wood <emersonwood94@gmail.com>
Emil V <emil.vaagland@schibsted.com>
Eng Zer Jun <engzerjun@gmail.com>
eperm <staffordworrell@gmail.com> eperm <staffordworrell@gmail.com>
Erick Fejta <erick@fejta.com> Erick Fejta <erick@fejta.com>
Erik Nobel <hendrik.nobel@transferwise.com> Erik Nobel <hendrik.nobel@transferwise.com>
@ -124,13 +144,17 @@ erwinvaneyk <erwinvaneyk@gmail.com>
Evan Elias <evanjelias@gmail.com> Evan Elias <evanjelias@gmail.com>
Fabian Holler <fabian.holler@simplesurance.de> Fabian Holler <fabian.holler@simplesurance.de>
Fabrice <fabrice.vaillant@student.ecp.fr> Fabrice <fabrice.vaillant@student.ecp.fr>
Fatema-Moaiyadi <fatema.i.moaiyadi@gmail.com>
Felix Geisendörfer <felix@debuggable.com> Felix Geisendörfer <felix@debuggable.com>
Filippo Valsorda <hi@filippo.io> Filippo Valsorda <hi@filippo.io>
Florian Forster <ff@octo.it> Florian Forster <ff@octo.it>
Florian Wagner <h2floh@github.com>
Francesc Gil <xescugil@gmail.com> Francesc Gil <xescugil@gmail.com>
Francis <hello@francismakes.com> Francis <hello@francismakes.com>
Francisco Guimarães <francisco.cpg@gmail.com> Francisco Guimarães <francisco.cpg@gmail.com>
François de Metz <francois@2metz.fr>
Fredrik Jönsson <fredrik.jonsson@izettle.com> Fredrik Jönsson <fredrik.jonsson@izettle.com>
Gabriel <samfiragabriel@gmail.com>
Garrett Squire <garrettsquire@gmail.com> Garrett Squire <garrettsquire@gmail.com>
George Kontridze <george.kontridze@gmail.com> George Kontridze <george.kontridze@gmail.com>
Georgy Buranov <gburanov@gmail.com> Georgy Buranov <gburanov@gmail.com>
@ -144,11 +168,15 @@ Guz Alexander <kalimatas@gmail.com>
Guðmundur Bjarni Ólafsson <gudmundur@github.com> Guðmundur Bjarni Ólafsson <gudmundur@github.com>
Hanno Hecker <hanno.hecker@zalando.de> Hanno Hecker <hanno.hecker@zalando.de>
Hari haran <hariharan.uno@gmail.com> Hari haran <hariharan.uno@gmail.com>
Harikesh00 <prajapatiharikesh16@gmail.com>
haya14busa <haya14busa@gmail.com> haya14busa <haya14busa@gmail.com>
haya14busa <hayabusa1419@gmail.com> haya14busa <hayabusa1419@gmail.com>
Hiroki Ito <hiroki.ito.dev@gmail.com>
Hubot Jr <rreichel3@github.com>
Huy Tr <kingbazoka@gmail.com> Huy Tr <kingbazoka@gmail.com>
huydx <doxuanhuy@gmail.com> huydx <doxuanhuy@gmail.com>
i2bskn <i2bskn@gmail.com> i2bskn <i2bskn@gmail.com>
Iain Steers <iainsteers@gmail.com>
Ikko Ashimine <eltociear@gmail.com> Ikko Ashimine <eltociear@gmail.com>
Ioannis Georgoulas <igeorgoulas21@gmail.com> Ioannis Georgoulas <igeorgoulas21@gmail.com>
Isao Jonas <isao.jonas@gmail.com> Isao Jonas <isao.jonas@gmail.com>
@ -158,8 +186,10 @@ Jacob Valdemar <jan@lunar.app>
Jake Krammer <jake.krammer1@gmail.com> Jake Krammer <jake.krammer1@gmail.com>
Jake White <jake@jwhite.network> Jake White <jake@jwhite.network>
Jameel Haffejee <RC1140@republiccommandos.co.za> Jameel Haffejee <RC1140@republiccommandos.co.za>
James Bowes <jbowes@repl.ca>
James Cockbain <james.cockbain@ibm.com> James Cockbain <james.cockbain@ibm.com>
James Loh <github@jloh.co> James Loh <github@jloh.co>
Jamie West <jamieianwest@hotmail.com>
Jan Kosecki <jan.kosecki91@gmail.com> Jan Kosecki <jan.kosecki91@gmail.com>
Jan Švábík <jansvabik@jansvabik.cz> Jan Švábík <jansvabik@jansvabik.cz>
Javier Campanini <jcampanini@palantir.com> Javier Campanini <jcampanini@palantir.com>
@ -169,15 +199,18 @@ Jeremy Morris <jeremylevanmorris@gmail.com>
Jesse Haka <haka.jesse@gmail.com> Jesse Haka <haka.jesse@gmail.com>
Jesse Newland <jesse@jnewland.com> Jesse Newland <jesse@jnewland.com>
Jihoon Chung <j.c@navercorp.com> Jihoon Chung <j.c@navercorp.com>
Jille Timmermans <jille@quis.cx>
Jimmi Dyson <jimmidyson@gmail.com> Jimmi Dyson <jimmidyson@gmail.com>
Joan Saum <joan.saum@epitech.eu> Joan Saum <joan.saum@epitech.eu>
Joe Tsai <joetsai@digital-static.net> Joe Tsai <joetsai@digital-static.net>
John Barton <jrbarton@gmail.com> John Barton <jrbarton@gmail.com>
John Engelman <john.r.engelman@gmail.com> John Engelman <john.r.engelman@gmail.com>
John Jones <john@exthilion.org>
John Liu <john.liu@mongodb.com> John Liu <john.liu@mongodb.com>
Jordan Brockopp <jdbro94@gmail.com> Jordan Brockopp <jdbro94@gmail.com>
Jordan Sussman <jordansail22@gmail.com> Jordan Sussman <jordansail22@gmail.com>
Joshua Bezaleel Abednego <joshua.bezaleel@gmail.com> Joshua Bezaleel Abednego <joshua.bezaleel@gmail.com>
João Cerqueira <joao@cerqueira.io>
JP Phillips <jonphill9@gmail.com> JP Phillips <jonphill9@gmail.com>
jpbelanger-mtl <jp.belanger@gmail.com> jpbelanger-mtl <jp.belanger@gmail.com>
Juan <juan.rios.28@gmail.com> Juan <juan.rios.28@gmail.com>
@ -186,25 +219,34 @@ Julien Garcia Gonzalez <garciagonzalez.julien@gmail.com>
Julien Rostand <jrostand@users.noreply.github.com> Julien Rostand <jrostand@users.noreply.github.com>
Junya Kono <junya03dance@gmail.com> Junya Kono <junya03dance@gmail.com>
Justin Abrahms <justin@abrah.ms> Justin Abrahms <justin@abrah.ms>
Justin Toh <tohjustin@hotmail.com>
Jusung Lee <e.jusunglee@gmail.com> Jusung Lee <e.jusunglee@gmail.com>
jzhoucliqr <jzhou@cliqr.com> jzhoucliqr <jzhou@cliqr.com>
k1rnt <eru08tera15mora@gmail.com>
kadern0 <kaderno@gmail.com> kadern0 <kaderno@gmail.com>
Katrina Owen <kytrinyx@github.com> Katrina Owen <kytrinyx@github.com>
Kautilya Tripathi <tripathi.kautilya@gmail.com> Kautilya Tripathi <tripathi.kautilya@gmail.com>
Keita Urashima <ursm@ursm.jp> Keita Urashima <ursm@ursm.jp>
Kevin Burke <kev@inburke.com> Kevin Burke <kev@inburke.com>
Kevin Wang <kwangsan@gmail.com>
Kevin Zhao <kzhao@lyft.com>
Kirill <g4s8.public@gmail.com> Kirill <g4s8.public@gmail.com>
Konrad Malawski <konrad.malawski@project13.pl> Konrad Malawski <konrad.malawski@project13.pl>
Kookheon Kwon <kucuny@gmail.com> Kookheon Kwon <kucuny@gmail.com>
Krishna Indani <indanikrishna@gmail.com>
Krzysztof Kowalczyk <kkowalczyk@gmail.com> Krzysztof Kowalczyk <kkowalczyk@gmail.com>
Kshitij Saraogi <KshitijSaraogi@gmail.com> Kshitij Saraogi <KshitijSaraogi@gmail.com>
Kumar Saurabh <itsksaurabh@gmail.com> Kumar Saurabh <itsksaurabh@gmail.com>
Kyle Kurz <kyle@doublekaudio.com>
kyokomi <kyoko1220adword@gmail.com> kyokomi <kyoko1220adword@gmail.com>
Laurent Verdoïa <verdoialaurent@gmail.com> Laurent Verdoïa <verdoialaurent@gmail.com>
leopoldwang <leopold.wang@gmail.com>
Liam Galvin <liam@liam-galvin.co.uk> Liam Galvin <liam@liam-galvin.co.uk>
Lluis Campos <lluis.campos@northern.tech>
Lovro Mažgon <lovro.mazgon@gmail.com> Lovro Mažgon <lovro.mazgon@gmail.com>
Luca Campese <me@campesel.net> Luca Campese <me@campesel.net>
Lucas Alcantara <lucasalcantaraf@gmail.com> Lucas Alcantara <lucasalcantaraf@gmail.com>
Luis Davim <luis.davim@sendoso.com>
Luke Evers <me@lukevers.com> Luke Evers <me@lukevers.com>
Luke Kysow <lkysow@gmail.com> Luke Kysow <lkysow@gmail.com>
Luke Roberts <email@luke-roberts.co.uk> Luke Roberts <email@luke-roberts.co.uk>
@ -220,6 +262,7 @@ Martins Sipenko <martins.sipenko@gmail.com>
Marwan Sulaiman <marwan.sameer@gmail.com> Marwan Sulaiman <marwan.sameer@gmail.com>
Masayuki Izumi <m@izum.in> Masayuki Izumi <m@izum.in>
Mat Geist <matgeist@gmail.com> Mat Geist <matgeist@gmail.com>
Matija Horvat <horvat2112@gmail.com>
Matin Rahmanian <itsmatinx@gmail.com> Matin Rahmanian <itsmatinx@gmail.com>
Matt <alpmatthew@gmail.com> Matt <alpmatthew@gmail.com>
Matt Brender <mjbrender@gmail.com> Matt Brender <mjbrender@gmail.com>
@ -227,12 +270,17 @@ Matt Gaunt <matt@gauntface.co.uk>
Matt Landis <landis.matt@gmail.com> Matt Landis <landis.matt@gmail.com>
Matt Moore <mattmoor@vmware.com> Matt Moore <mattmoor@vmware.com>
Maxime Bury <maxime.bury@gmail.com> Maxime Bury <maxime.bury@gmail.com>
Michael Meng <mmeng@lyft.com>
Michael Spiegel <michael.m.spiegel@gmail.com> Michael Spiegel <michael.m.spiegel@gmail.com>
Michael Tiller <michael.tiller@gmail.com> Michael Tiller <michael.tiller@gmail.com>
Michał Glapa <michal.glapa@gmail.com> Michał Glapa <michal.glapa@gmail.com>
Michelangelo Morrillo <michelangelo@morrillo.it> Michelangelo Morrillo <michelangelo@morrillo.it>
Miguel Elias dos Santos <migueleliasweb@gmail.com>
Mike Chen <mchen300@gmail.com>
Mohammed AlDujaili <avainer11@gmail.com>
Mukundan Senthil <mukundan314@gmail.com> Mukundan Senthil <mukundan314@gmail.com>
Munia Balayil <munia.247@gmail.com> Munia Balayil <munia.247@gmail.com>
Mustafa Abban <mustafaabban@utexas.edu>
Nadav Kaner <nadavkaner1@gmail.com> Nadav Kaner <nadavkaner1@gmail.com>
Nathan VanBenschoten <nvanbenschoten@gmail.com> Nathan VanBenschoten <nvanbenschoten@gmail.com>
Navaneeth Suresh <navaneeths1998@gmail.com> Navaneeth Suresh <navaneeths1998@gmail.com>
@ -242,6 +290,7 @@ Nick Platt <hello@nickplatt.co.uk>
Nick Spragg <nick.spragg@bbc.co.uk> Nick Spragg <nick.spragg@bbc.co.uk>
Nikhita Raghunath <nikitaraghunath@gmail.com> Nikhita Raghunath <nikitaraghunath@gmail.com>
Nilesh Singh <nilesh.singh24@outlook.com> Nilesh Singh <nilesh.singh24@outlook.com>
Noah Hanjun Lee <noah.lee@buzzvil.com>
Noah Zoschke <noah+sso2@convox.com> Noah Zoschke <noah+sso2@convox.com>
ns-cweber <cweber@narrativescience.com> ns-cweber <cweber@narrativescience.com>
Ole Orhagen <ole.orhagen@northern.tech> Ole Orhagen <ole.orhagen@northern.tech>
@ -252,13 +301,17 @@ Pablo Pérez Schröder <pablo.perezschroder@wetransfer.com>
Palash Nigam <npalash25@gmail.com> Palash Nigam <npalash25@gmail.com>
Panagiotis Moustafellos <pmoust@gmail.com> Panagiotis Moustafellos <pmoust@gmail.com>
Parham Alvani <parham.alvani@gmail.com> Parham Alvani <parham.alvani@gmail.com>
pari-27 <sonamajumdar2012@gmail.com>
Parker Moore <parkrmoore@gmail.com> Parker Moore <parkrmoore@gmail.com>
parkhyukjun89 <park.hyukjun89@gmail.com> parkhyukjun89 <park.hyukjun89@gmail.com>
Pat Alwell <pat.alwell@gmail.com>
Patrick DeVivo <patrick.devivo@gmail.com> Patrick DeVivo <patrick.devivo@gmail.com>
Patrick Marabeas <patrick@marabeas.io> Patrick Marabeas <patrick@marabeas.io>
Pavel Dvoinos <pavel.dvoinos@transferwise.com>
Pavel Shtanko <pavel.shtanko@gmail.com> Pavel Shtanko <pavel.shtanko@gmail.com>
Pete Wagner <thepwagner@github.com> Pete Wagner <thepwagner@github.com>
Petr Shevtsov <petr.shevtsov@gmail.com> Petr Shevtsov <petr.shevtsov@gmail.com>
Pierce McEntagart <pierce@nightfall.ai>
Pierre Carrier <pierre@meteor.com> Pierre Carrier <pierre@meteor.com>
Piotr Zurek <p.zurek@gmail.com> Piotr Zurek <p.zurek@gmail.com>
Piyush Chugh <piyushchugh1993@gmail.com> Piyush Chugh <piyushchugh1993@gmail.com>
@ -272,18 +325,23 @@ Radek Simko <radek.simko@gmail.com>
Radliński Ignacy <radlinsk@student.agh.edu.pl> Radliński Ignacy <radlinsk@student.agh.edu.pl>
Rajat Jindal <rajatjindal83@gmail.com> Rajat Jindal <rajatjindal83@gmail.com>
Rajendra arora <rajendraarora16@yahoo.com> Rajendra arora <rajendraarora16@yahoo.com>
Rajkumar <princegosavi12@gmail.com>
Ranbir Singh <binkkatal.r@gmail.com> Ranbir Singh <binkkatal.r@gmail.com>
Ravi Shekhar Jethani <rsjethani@gmail.com> Ravi Shekhar Jethani <rsjethani@gmail.com>
RaviTeja Pothana <ravi-teja@live.com> RaviTeja Pothana <ravi-teja@live.com>
rc1140 <jameel@republiccommandos.co.za> rc1140 <jameel@republiccommandos.co.za>
Red Hat, Inc. Red Hat, Inc.
Reetuparna Mukherjee <reetuparna.1988@gmail.com> Reetuparna Mukherjee <reetuparna.1988@gmail.com>
reeves122 <reeves122@gmail.com>
Reinier Timmer <reinier.timmer@ah.nl> Reinier Timmer <reinier.timmer@ah.nl>
Renjith R <renjithr201097@gmail.com> Renjith R <renjithr201097@gmail.com>
Ricco Førgaard <ricco@fiskeben.dk> Ricco Førgaard <ricco@fiskeben.dk>
Richard de Vries <richard.de.vries@topicus.nl>
Rob Figueiredo <robfig@yext.com> Rob Figueiredo <robfig@yext.com>
Rohit Upadhyay <urohit011@gmail.com> Rohit Upadhyay <urohit011@gmail.com>
Rojan Dinc <rojand94@gmail.com>
Ronak Jain <ronakjain@outlook.in> Ronak Jain <ronakjain@outlook.in>
Ronan Pelliard <ronan.pelliard@datadoghq.com>
Ross Gustafson <srgustafson8@icloud.com> Ross Gustafson <srgustafson8@icloud.com>
Ruben Vereecken <rubenvereecken@gmail.com> Ruben Vereecken <rubenvereecken@gmail.com>
Russell Boley <raboley@gmail.com> Russell Boley <raboley@gmail.com>
@ -299,10 +357,13 @@ Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Sander Knape <s.knape88@gmail.com> Sander Knape <s.knape88@gmail.com>
Sander van Harmelen <svanharmelen@schubergphilis.com> Sander van Harmelen <svanharmelen@schubergphilis.com>
Sanket Payghan <sanket.payghan8@gmail.com> Sanket Payghan <sanket.payghan8@gmail.com>
Sarah Funkhouser <sarah.k.funkhouser@gmail.com>
Sarasa Kisaragi <lingsamuelgrace@gmail.com> Sarasa Kisaragi <lingsamuelgrace@gmail.com>
Sasha Melentyev <sasha@melentyev.io>
Sean Wang <sean@decrypted.org> Sean Wang <sean@decrypted.org>
Sebastian Mandrean <sebastian.mandrean@gmail.com> Sebastian Mandrean <sebastian.mandrean@gmail.com>
Sebastian Mæland Pedersen <sem.pedersen@stud.uis.no> Sebastian Mæland Pedersen <sem.pedersen@stud.uis.no>
Sergei Popinevskii <gurza000@gmail.com>
Sergey Romanov <xxsmotur@gmail.com> Sergey Romanov <xxsmotur@gmail.com>
Sergio Garcia <sergio.garcia@gmail.com> Sergio Garcia <sergio.garcia@gmail.com>
Seth Vargo <seth@sethvargo.com> Seth Vargo <seth@sethvargo.com>
@ -312,6 +373,7 @@ shakeelrao <shakeelrao79@gmail.com>
Shawn Catanzarite <me@shawncatz.com> Shawn Catanzarite <me@shawncatz.com>
Shawn Smith <shawnpsmith@gmail.com> Shawn Smith <shawnpsmith@gmail.com>
Shibasis Patel <patelshibasis@gmail.com> Shibasis Patel <patelshibasis@gmail.com>
Sho Okada <shokada3@gmail.com>
Shrikrishna Singh <krishnasingh.ss30@gmail.com> Shrikrishna Singh <krishnasingh.ss30@gmail.com>
Simon Davis <sdavis@hashicorp.com> Simon Davis <sdavis@hashicorp.com>
sona-tar <sona.zip@gmail.com> sona-tar <sona.zip@gmail.com>
@ -322,18 +384,27 @@ Stefan Sedich <stefan.sedich@gmail.com>
Steve Teuber <github@steveteuber.com> Steve Teuber <github@steveteuber.com>
Stian Eikeland <stian@eikeland.se> Stian Eikeland <stian@eikeland.se>
Suhaib Mujahid <suhaibmujahid@gmail.com> Suhaib Mujahid <suhaibmujahid@gmail.com>
sushmita wable <sw09007@gmail.com>
Szymon Kodrebski <simonkey007@gmail.com> Szymon Kodrebski <simonkey007@gmail.com>
Søren Hansen <soren@dinero.dk> Søren Hansen <soren@dinero.dk>
Takashi Yoneuchi <takashi.yoneuchi@shift-js.info>
Takayuki Watanabe <takanabe.w@gmail.com> Takayuki Watanabe <takanabe.w@gmail.com>
Taketoshi Fujiwara <fujiwara@leapmind.io> Taketoshi Fujiwara <fujiwara@leapmind.io>
Taketoshi Fujiwara <taketoshi.fujiwara@gmail.com> Taketoshi Fujiwara <taketoshi.fujiwara@gmail.com>
Takuma Kajikawa <kj1ktk@gmail.com>
Tasya Aditya Rukmana <tadityar@gmail.com> Tasya Aditya Rukmana <tadityar@gmail.com>
Theo Henson <theodorehenson@protonmail.com> Theo Henson <theodorehenson@protonmail.com>
Theofilos Petsios <theofilos.pe@gmail.com>
Thomas Aidan Curran <thomas@ory.sh> Thomas Aidan Curran <thomas@ory.sh>
Thomas Bruyelle <thomas.bruyelle@gmail.com> Thomas Bruyelle <thomas.bruyelle@gmail.com>
Tim Rogers <timrogers@github.com>
Timothée Peignier <timothee.peignier@tryphon.org> Timothée Peignier <timothee.peignier@tryphon.org>
Tingluo Huang <tingluohuang@github.com>
tkhandel <tarunkhandelwal.iitr@gmail.com> tkhandel <tarunkhandelwal.iitr@gmail.com>
Tobias Gesellchen <tobias@gesellix.de>
Tom Payne <twpayne@gmail.com>
Trey Tacon <ttacon@gmail.com> Trey Tacon <ttacon@gmail.com>
tsbkw <tsbkw0@gmail.com>
ttacon <ttacon@gmail.com> ttacon <ttacon@gmail.com>
Vaibhav Singh <vaibhav.singh.14cse@bml.edu.in> Vaibhav Singh <vaibhav.singh.14cse@bml.edu.in>
Varadarajan Aravamudhan <varadaraajan@gmail.com> Varadarajan Aravamudhan <varadaraajan@gmail.com>
@ -347,12 +418,16 @@ Will Maier <wcmaier@gmail.com>
Willem D'Haeseleer <dhwillem@gmail.com> Willem D'Haeseleer <dhwillem@gmail.com>
William Bailey <mail@williambailey.org.uk> William Bailey <mail@williambailey.org.uk>
William Cooke <pipeston@gmail.com> William Cooke <pipeston@gmail.com>
Xabi <xmartinez1702@gmail.com>
xibz <impactbchang@gmail.com> xibz <impactbchang@gmail.com>
Yann Malet <yann.malet@gmail.com> Yann Malet <yann.malet@gmail.com>
Yannick Utard <yannickutard@gmail.com> Yannick Utard <yannickutard@gmail.com>
Yicheng Qin <qycqycqycqycqyc@gmail.com> Yicheng Qin <qycqycqycqycqyc@gmail.com>
Yosuke Akatsuka <yosuke.akatsuka@access-company.com> Yosuke Akatsuka <yosuke.akatsuka@access-company.com>
Yumikiyo Osanai <yumios.art@gmail.com> Yumikiyo Osanai <yumios.art@gmail.com>
Yusef Mohamadi <yuseferi@gmail.com>
Yusuke Kuoka <ykuoka@gmail.com> Yusuke Kuoka <ykuoka@gmail.com>
Zach Latta <zach@zachlatta.com> Zach Latta <zach@zachlatta.com>
zhouhaibing089 <zhouhaibing089@gmail.com> zhouhaibing089 <zhouhaibing089@gmail.com>
六开箱 <lkxed@outlook.com>
缘生 <i@ysicing.me>

View file

@ -12,14 +12,17 @@ import (
// RunnerGroup represents a self-hosted runner group configured in an organization. // RunnerGroup represents a self-hosted runner group configured in an organization.
type RunnerGroup struct { type RunnerGroup struct {
ID *int64 `json:"id,omitempty"` ID *int64 `json:"id,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Visibility *string `json:"visibility,omitempty"` Visibility *string `json:"visibility,omitempty"`
Default *bool `json:"default,omitempty"` Default *bool `json:"default,omitempty"`
SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"` SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"`
RunnersURL *string `json:"runners_url,omitempty"` RunnersURL *string `json:"runners_url,omitempty"`
Inherited *bool `json:"inherited,omitempty"` Inherited *bool `json:"inherited,omitempty"`
AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"`
RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"`
SelectedWorkflows []string `json:"selected_workflows,omitempty"`
WorkflowRestrictionsReadOnly *bool `json:"workflow_restrictions_read_only,omitempty"`
} }
// RunnerGroups represents a collection of self-hosted runner groups configured for an organization. // RunnerGroups represents a collection of self-hosted runner groups configured for an organization.
@ -38,13 +41,19 @@ type CreateRunnerGroupRequest struct {
Runners []int64 `json:"runners,omitempty"` Runners []int64 `json:"runners,omitempty"`
// If set to True, public repos can use this runner group // If set to True, public repos can use this runner group
AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"`
// If true, the runner group will be restricted to running only the workflows specified in the SelectedWorkflows slice.
RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"`
// List of workflows the runner group should be allowed to run. This setting will be ignored unless RestrictedToWorkflows is set to true.
SelectedWorkflows []string `json:"selected_workflows,omitempty"`
} }
// UpdateRunnerGroupRequest represents a request to update a Runner group for an organization. // UpdateRunnerGroupRequest represents a request to update a Runner group for an organization.
type UpdateRunnerGroupRequest struct { type UpdateRunnerGroupRequest struct {
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Visibility *string `json:"visibility,omitempty"` Visibility *string `json:"visibility,omitempty"`
AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"`
RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"`
SelectedWorkflows []string `json:"selected_workflows,omitempty"`
} }
// SetRepoAccessRunnerGroupRequest represents a request to replace the list of repositories // SetRepoAccessRunnerGroupRequest represents a request to replace the list of repositories

View file

@ -44,6 +44,7 @@ type WorkflowJob struct {
RunnerName *string `json:"runner_name,omitempty"` RunnerName *string `json:"runner_name,omitempty"`
RunnerGroupID *int64 `json:"runner_group_id,omitempty"` RunnerGroupID *int64 `json:"runner_group_id,omitempty"`
RunnerGroupName *string `json:"runner_group_name,omitempty"` RunnerGroupName *string `json:"runner_group_name,omitempty"`
RunAttempt *int64 `json:"run_attempt,omitempty"`
} }
// Jobs represents a slice of repository action workflow job. // Jobs represents a slice of repository action workflow job.

View file

@ -87,6 +87,7 @@ type Alert struct {
DismissedBy *User `json:"dismissed_by,omitempty"` DismissedBy *User `json:"dismissed_by,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"` DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
DismissedReason *string `json:"dismissed_reason,omitempty"` DismissedReason *string `json:"dismissed_reason,omitempty"`
DismissedComment *string `json:"dismissed_comment,omitempty"`
InstancesURL *string `json:"instances_url,omitempty"` InstancesURL *string `json:"instances_url,omitempty"`
} }
@ -121,6 +122,10 @@ type AlertListOptions struct {
// Return code scanning alerts for a specific branch reference. The ref must be formatted as heads/<branch name>. // Return code scanning alerts for a specific branch reference. The ref must be formatted as heads/<branch name>.
Ref string `url:"ref,omitempty"` Ref string `url:"ref,omitempty"`
ListCursorOptions
// Add ListOptions so offset pagination with integer type "page" query parameter is accepted
// since ListCursorOptions accepts "page" as string only.
ListOptions ListOptions
} }

View file

@ -0,0 +1,134 @@
// Copyright 2022 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
)
// Dependency reprensents the vulnerable dependency.
type Dependency struct {
Package *VulnerabilityPackage `json:"package,omitempty"`
ManifestPath *string `json:"manifest_path,omitempty"`
Scope *string `json:"scope,omitempty"`
}
// AdvisoryCVSs represents the advisory pertaining to the Common Vulnerability Scoring System.
type AdvisoryCVSs struct {
Score *float64 `json:"score,omitempty"`
VectorString *string `json:"vector_string,omitempty"`
}
// AdvisoryCWEs reprensent the advisory pertaining to Common Weakness Enumeration.
type AdvisoryCWEs struct {
CWEID *string `json:"cwe_id,omitempty"`
Name *string `json:"name,omitempty"`
}
// DependabotSecurityAdvisory represents the GitHub Security Advisory.
type DependabotSecurityAdvisory struct {
GHSAID *string `json:"ghsa_id,omitempty"`
CVEID *string `json:"cve_id,omitempty"`
Summary *string `json:"summary,omitempty"`
Description *string `json:"description,omitempty"`
Vulnerabilities []*AdvisoryVulnerability `json:"vulnerabilities,omitempty"`
Severity *string `json:"severity,omitempty"`
CVSs *AdvisoryCVSs `json:"cvss,omitempty"`
CWEs []*AdvisoryCWEs `json:"cwes,omitempty"`
Identifiers []*AdvisoryIdentifier `json:"identifiers,omitempty"`
References []*AdvisoryReference `json:"references,omitempty"`
PublishedAt *Timestamp `json:"published_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
WithdrawnAt *Timestamp `json:"withdrawn_at,omitempty"`
}
// DependabotAlert represents a Dependabot alert.
type DependabotAlert struct {
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Dependency *Dependency `json:"dependency,omitempty"`
SecurityAdvisory *DependabotSecurityAdvisory `json:"security_advisory,omitempty"`
SecurityVulnerability *AdvisoryVulnerability `json:"security_vulnerability,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
DismissedBy *User `json:"dismissed_by,omitempty"`
DismissedReason *string `json:"dismissed_reason,omitempty"`
DismissedComment *string `json:"dismissed_comment,omitempty"`
FixedAt *Timestamp `json:"fixed_at,omitempty"`
}
// ListAlertsOptions specifies the optional parameters to the DependabotService.ListRepoAlerts
// and DependabotService.ListOrgAlerts methods.
type ListAlertsOptions struct {
State *string `url:"state,omitempty"`
Severity *string `url:"severity,omitempty"`
Ecosystem *string `url:"ecosystem,omitempty"`
Package *string `url:"package,omitempty"`
Scope *string `url:"scope,omitempty"`
Sort *string `url:"sort,omitempty"`
Direction *string `url:"direction,omitempty"`
ListCursorOptions
}
func (s *DependabotService) listAlerts(ctx context.Context, url string, opts *ListAlertsOptions) ([]*DependabotAlert, *Response, error) {
u, err := addOptions(url, opts)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var alerts []*DependabotAlert
resp, err := s.client.Do(ctx, req, &alerts)
if err != nil {
return nil, resp, err
}
return alerts, resp, nil
}
// ListRepoAlerts lists all Dependabot alerts of a repository.
//
// GitHub API docs: https://docs.github.com/en/rest/dependabot/alerts#list-dependabot-alerts-for-a-repository
func (s *DependabotService) ListRepoAlerts(ctx context.Context, owner, repo string, opts *ListAlertsOptions) ([]*DependabotAlert, *Response, error) {
url := fmt.Sprintf("repos/%v/%v/dependabot/alerts", owner, repo)
return s.listAlerts(ctx, url, opts)
}
// ListOrgAlerts lists all Dependabot alerts of an organization.
//
// GitHub API docs: https://docs.github.com/en/rest/dependabot/alerts#list-dependabot-alerts-for-an-organization
func (s *DependabotService) ListOrgAlerts(ctx context.Context, org string, opts *ListAlertsOptions) ([]*DependabotAlert, *Response, error) {
url := fmt.Sprintf("orgs/%v/dependabot/alerts", org)
return s.listAlerts(ctx, url, opts)
}
// GetRepoAlert gets a single repository Dependabot alert.
//
// GitHub API docs: https://docs.github.com/en/rest/dependabot/alerts#get-a-dependabot-alert
func (s *DependabotService) GetRepoAlert(ctx context.Context, owner, repo string, number int) (*DependabotAlert, *Response, error) {
url := fmt.Sprintf("repos/%v/%v/dependabot/alerts/%v", owner, repo, number)
req, err := s.client.NewRequest("GET", url, nil)
if err != nil {
return nil, nil, err
}
alert := new(DependabotAlert)
resp, err := s.client.Do(ctx, req, alert)
if err != nil {
return nil, resp, err
}
return alert, resp, nil
}

View file

@ -76,6 +76,8 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
payload = &MemberEvent{} payload = &MemberEvent{}
case "MembershipEvent": case "MembershipEvent":
payload = &MembershipEvent{} payload = &MembershipEvent{}
case "MergeGroupEvent":
payload = &MergeGroupEvent{}
case "MetaEvent": case "MetaEvent":
payload = &MetaEvent{} payload = &MetaEvent{}
case "MilestoneEvent": case "MilestoneEvent":

View file

@ -559,6 +559,37 @@ type MembershipEvent struct {
Installation *Installation `json:"installation,omitempty"` Installation *Installation `json:"installation,omitempty"`
} }
// MergeGroup represents the merge group in a merge queue.
type MergeGroup struct {
// The SHA of the merge group.
HeadSHA *string `json:"head_sha,omitempty"`
// The full ref of the merge group.
HeadRef *string `json:"head_ref,omitempty"`
// The SHA of the merge group's parent commit.
BaseSHA *string `json:"base_sha,omitempty"`
// The full ref of the branch the merge group will be merged into.
BaseRef *string `json:"base_ref,omitempty"`
// An expanded representation of the head_sha commit.
HeadCommit *Commit `json:"head_commit,omitempty"`
}
// MergeGroupEvent represents activity related to merge groups in a merge queue. The type of activity is specified
// in the action property of the payload object.
//
// GitHub API docs: https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#merge_group
type MergeGroupEvent struct {
// The action that was performed. Currently, can only be checks_requested.
Action *string `json:"action,omitempty"`
// The merge group.
MergeGroup *MergeGroup `json:"merge_group,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Installation *Installation `json:"installation,omitempty"`
Sender *User `json:"sender,omitempty"`
}
// MetaEvent is triggered when the webhook that this event is configured on is deleted. // MetaEvent is triggered when the webhook that this event is configured on is deleted.
// This event will only listen for changes to the particular hook the event is installed on. // This event will only listen for changes to the particular hook the event is installed on.
// Therefore, it must be selected for each hook that you'd like to receive meta events for. // Therefore, it must be selected for each hook that you'd like to receive meta events for.

View file

@ -206,6 +206,38 @@ func (a *AdvancedSecurityCommittersBreakdown) GetUserLogin() string {
return *a.UserLogin return *a.UserLogin
} }
// GetScore returns the Score field.
func (a *AdvisoryCVSs) GetScore() *float64 {
if a == nil {
return nil
}
return a.Score
}
// GetVectorString returns the VectorString field if it's non-nil, zero value otherwise.
func (a *AdvisoryCVSs) GetVectorString() string {
if a == nil || a.VectorString == nil {
return ""
}
return *a.VectorString
}
// GetCWEID returns the CWEID field if it's non-nil, zero value otherwise.
func (a *AdvisoryCWEs) GetCWEID() string {
if a == nil || a.CWEID == nil {
return ""
}
return *a.CWEID
}
// GetName returns the Name field if it's non-nil, zero value otherwise.
func (a *AdvisoryCWEs) GetName() string {
if a == nil || a.Name == nil {
return ""
}
return *a.Name
}
// GetType returns the Type field if it's non-nil, zero value otherwise. // GetType returns the Type field if it's non-nil, zero value otherwise.
func (a *AdvisoryIdentifier) GetType() string { func (a *AdvisoryIdentifier) GetType() string {
if a == nil || a.Type == nil { if a == nil || a.Type == nil {
@ -302,6 +334,14 @@ func (a *Alert) GetDismissedBy() *User {
return a.DismissedBy return a.DismissedBy
} }
// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise.
func (a *Alert) GetDismissedComment() string {
if a == nil || a.DismissedComment == nil {
return ""
}
return *a.DismissedComment
}
// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise. // GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise.
func (a *Alert) GetDismissedReason() string { func (a *Alert) GetDismissedReason() string {
if a == nil || a.DismissedReason == nil { if a == nil || a.DismissedReason == nil {
@ -3614,6 +3654,14 @@ func (c *CreateRunnerGroupRequest) GetName() string {
return *c.Name return *c.Name
} }
// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise.
func (c *CreateRunnerGroupRequest) GetRestrictedToWorkflows() bool {
if c == nil || c.RestrictedToWorkflows == nil {
return false
}
return *c.RestrictedToWorkflows
}
// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. // GetVisibility returns the Visibility field if it's non-nil, zero value otherwise.
func (c *CreateRunnerGroupRequest) GetVisibility() string { func (c *CreateRunnerGroupRequest) GetVisibility() string {
if c == nil || c.Visibility == nil { if c == nil || c.Visibility == nil {
@ -3726,6 +3774,214 @@ func (d *DeleteEvent) GetSender() *User {
return d.Sender return d.Sender
} }
// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetCreatedAt() Timestamp {
if d == nil || d.CreatedAt == nil {
return Timestamp{}
}
return *d.CreatedAt
}
// GetDependency returns the Dependency field.
func (d *DependabotAlert) GetDependency() *Dependency {
if d == nil {
return nil
}
return d.Dependency
}
// GetDismissedAt returns the DismissedAt field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetDismissedAt() Timestamp {
if d == nil || d.DismissedAt == nil {
return Timestamp{}
}
return *d.DismissedAt
}
// GetDismissedBy returns the DismissedBy field.
func (d *DependabotAlert) GetDismissedBy() *User {
if d == nil {
return nil
}
return d.DismissedBy
}
// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetDismissedComment() string {
if d == nil || d.DismissedComment == nil {
return ""
}
return *d.DismissedComment
}
// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetDismissedReason() string {
if d == nil || d.DismissedReason == nil {
return ""
}
return *d.DismissedReason
}
// GetFixedAt returns the FixedAt field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetFixedAt() Timestamp {
if d == nil || d.FixedAt == nil {
return Timestamp{}
}
return *d.FixedAt
}
// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetHTMLURL() string {
if d == nil || d.HTMLURL == nil {
return ""
}
return *d.HTMLURL
}
// GetNumber returns the Number field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetNumber() int {
if d == nil || d.Number == nil {
return 0
}
return *d.Number
}
// GetSecurityAdvisory returns the SecurityAdvisory field.
func (d *DependabotAlert) GetSecurityAdvisory() *DependabotSecurityAdvisory {
if d == nil {
return nil
}
return d.SecurityAdvisory
}
// GetSecurityVulnerability returns the SecurityVulnerability field.
func (d *DependabotAlert) GetSecurityVulnerability() *AdvisoryVulnerability {
if d == nil {
return nil
}
return d.SecurityVulnerability
}
// GetState returns the State field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetState() string {
if d == nil || d.State == nil {
return ""
}
return *d.State
}
// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetUpdatedAt() Timestamp {
if d == nil || d.UpdatedAt == nil {
return Timestamp{}
}
return *d.UpdatedAt
}
// GetURL returns the URL field if it's non-nil, zero value otherwise.
func (d *DependabotAlert) GetURL() string {
if d == nil || d.URL == nil {
return ""
}
return *d.URL
}
// GetCVEID returns the CVEID field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetCVEID() string {
if d == nil || d.CVEID == nil {
return ""
}
return *d.CVEID
}
// GetCVSs returns the CVSs field.
func (d *DependabotSecurityAdvisory) GetCVSs() *AdvisoryCVSs {
if d == nil {
return nil
}
return d.CVSs
}
// GetDescription returns the Description field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetDescription() string {
if d == nil || d.Description == nil {
return ""
}
return *d.Description
}
// GetGHSAID returns the GHSAID field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetGHSAID() string {
if d == nil || d.GHSAID == nil {
return ""
}
return *d.GHSAID
}
// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetPublishedAt() Timestamp {
if d == nil || d.PublishedAt == nil {
return Timestamp{}
}
return *d.PublishedAt
}
// GetSeverity returns the Severity field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetSeverity() string {
if d == nil || d.Severity == nil {
return ""
}
return *d.Severity
}
// GetSummary returns the Summary field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetSummary() string {
if d == nil || d.Summary == nil {
return ""
}
return *d.Summary
}
// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetUpdatedAt() Timestamp {
if d == nil || d.UpdatedAt == nil {
return Timestamp{}
}
return *d.UpdatedAt
}
// GetWithdrawnAt returns the WithdrawnAt field if it's non-nil, zero value otherwise.
func (d *DependabotSecurityAdvisory) GetWithdrawnAt() Timestamp {
if d == nil || d.WithdrawnAt == nil {
return Timestamp{}
}
return *d.WithdrawnAt
}
// GetManifestPath returns the ManifestPath field if it's non-nil, zero value otherwise.
func (d *Dependency) GetManifestPath() string {
if d == nil || d.ManifestPath == nil {
return ""
}
return *d.ManifestPath
}
// GetPackage returns the Package field.
func (d *Dependency) GetPackage() *VulnerabilityPackage {
if d == nil {
return nil
}
return d.Package
}
// GetScope returns the Scope field if it's non-nil, zero value otherwise.
func (d *Dependency) GetScope() string {
if d == nil || d.Scope == nil {
return ""
}
return *d.Scope
}
// GetAction returns the Action field if it's non-nil, zero value otherwise. // GetAction returns the Action field if it's non-nil, zero value otherwise.
func (d *DeployKeyEvent) GetAction() string { func (d *DeployKeyEvent) GetAction() string {
if d == nil || d.Action == nil { if d == nil || d.Action == nil {
@ -4558,6 +4814,14 @@ func (d *DiscussionEvent) GetSender() *User {
return d.Sender return d.Sender
} }
// GetApps returns the Apps field if it's non-nil, zero value otherwise.
func (d *DismissalRestrictionsRequest) GetApps() []string {
if d == nil || d.Apps == nil {
return nil
}
return *d.Apps
}
// GetTeams returns the Teams field if it's non-nil, zero value otherwise. // GetTeams returns the Teams field if it's non-nil, zero value otherwise.
func (d *DismissalRestrictionsRequest) GetTeams() []string { func (d *DismissalRestrictionsRequest) GetTeams() []string {
if d == nil || d.Teams == nil { if d == nil || d.Teams == nil {
@ -7206,6 +7470,14 @@ func (i *Issue) GetState() string {
return *i.State return *i.State
} }
// GetStateReason returns the StateReason field if it's non-nil, zero value otherwise.
func (i *Issue) GetStateReason() string {
if i == nil || i.StateReason == nil {
return ""
}
return *i.StateReason
}
// GetTitle returns the Title field if it's non-nil, zero value otherwise. // GetTitle returns the Title field if it's non-nil, zero value otherwise.
func (i *Issue) GetTitle() string { func (i *Issue) GetTitle() string {
if i == nil || i.Title == nil { if i == nil || i.Title == nil {
@ -8262,6 +8534,62 @@ func (l *LinearHistoryRequirementEnforcementLevelChanges) GetFrom() string {
return *l.From return *l.From
} }
// GetDirection returns the Direction field if it's non-nil, zero value otherwise.
func (l *ListAlertsOptions) GetDirection() string {
if l == nil || l.Direction == nil {
return ""
}
return *l.Direction
}
// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise.
func (l *ListAlertsOptions) GetEcosystem() string {
if l == nil || l.Ecosystem == nil {
return ""
}
return *l.Ecosystem
}
// GetPackage returns the Package field if it's non-nil, zero value otherwise.
func (l *ListAlertsOptions) GetPackage() string {
if l == nil || l.Package == nil {
return ""
}
return *l.Package
}
// GetScope returns the Scope field if it's non-nil, zero value otherwise.
func (l *ListAlertsOptions) GetScope() string {
if l == nil || l.Scope == nil {
return ""
}
return *l.Scope
}
// GetSeverity returns the Severity field if it's non-nil, zero value otherwise.
func (l *ListAlertsOptions) GetSeverity() string {
if l == nil || l.Severity == nil {
return ""
}
return *l.Severity
}
// GetSort returns the Sort field if it's non-nil, zero value otherwise.
func (l *ListAlertsOptions) GetSort() string {
if l == nil || l.Sort == nil {
return ""
}
return *l.Sort
}
// GetState returns the State field if it's non-nil, zero value otherwise.
func (l *ListAlertsOptions) GetState() string {
if l == nil || l.State == nil {
return ""
}
return *l.State
}
// GetAppID returns the AppID field if it's non-nil, zero value otherwise. // GetAppID returns the AppID field if it's non-nil, zero value otherwise.
func (l *ListCheckRunsOptions) GetAppID() int64 { func (l *ListCheckRunsOptions) GetAppID() int64 {
if l == nil || l.AppID == nil { if l == nil || l.AppID == nil {
@ -8862,6 +9190,94 @@ func (m *MembershipEvent) GetTeam() *Team {
return m.Team return m.Team
} }
// GetBaseRef returns the BaseRef field if it's non-nil, zero value otherwise.
func (m *MergeGroup) GetBaseRef() string {
if m == nil || m.BaseRef == nil {
return ""
}
return *m.BaseRef
}
// GetBaseSHA returns the BaseSHA field if it's non-nil, zero value otherwise.
func (m *MergeGroup) GetBaseSHA() string {
if m == nil || m.BaseSHA == nil {
return ""
}
return *m.BaseSHA
}
// GetHeadCommit returns the HeadCommit field.
func (m *MergeGroup) GetHeadCommit() *Commit {
if m == nil {
return nil
}
return m.HeadCommit
}
// GetHeadRef returns the HeadRef field if it's non-nil, zero value otherwise.
func (m *MergeGroup) GetHeadRef() string {
if m == nil || m.HeadRef == nil {
return ""
}
return *m.HeadRef
}
// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise.
func (m *MergeGroup) GetHeadSHA() string {
if m == nil || m.HeadSHA == nil {
return ""
}
return *m.HeadSHA
}
// GetAction returns the Action field if it's non-nil, zero value otherwise.
func (m *MergeGroupEvent) GetAction() string {
if m == nil || m.Action == nil {
return ""
}
return *m.Action
}
// GetInstallation returns the Installation field.
func (m *MergeGroupEvent) GetInstallation() *Installation {
if m == nil {
return nil
}
return m.Installation
}
// GetMergeGroup returns the MergeGroup field.
func (m *MergeGroupEvent) GetMergeGroup() *MergeGroup {
if m == nil {
return nil
}
return m.MergeGroup
}
// GetOrg returns the Org field.
func (m *MergeGroupEvent) GetOrg() *Organization {
if m == nil {
return nil
}
return m.Org
}
// GetRepo returns the Repo field.
func (m *MergeGroupEvent) GetRepo() *Repository {
if m == nil {
return nil
}
return m.Repo
}
// GetSender returns the Sender field.
func (m *MergeGroupEvent) GetSender() *User {
if m == nil {
return nil
}
return m.Sender
}
// GetText returns the Text field if it's non-nil, zero value otherwise. // GetText returns the Text field if it's non-nil, zero value otherwise.
func (m *Message) GetText() string { func (m *Message) GetText() string {
if m == nil || m.Text == nil { if m == nil || m.Text == nil {
@ -11846,6 +12262,14 @@ func (p *Protection) GetAllowForcePushes() *AllowForcePushes {
return p.AllowForcePushes return p.AllowForcePushes
} }
// GetAllowForkSyncing returns the AllowForkSyncing field if it's non-nil, zero value otherwise.
func (p *Protection) GetAllowForkSyncing() bool {
if p == nil || p.AllowForkSyncing == nil {
return false
}
return *p.AllowForkSyncing
}
// GetEnforceAdmins returns the EnforceAdmins field. // GetEnforceAdmins returns the EnforceAdmins field.
func (p *Protection) GetEnforceAdmins() *AdminEnforcement { func (p *Protection) GetEnforceAdmins() *AdminEnforcement {
if p == nil { if p == nil {
@ -11854,6 +12278,14 @@ func (p *Protection) GetEnforceAdmins() *AdminEnforcement {
return p.EnforceAdmins return p.EnforceAdmins
} }
// GetLockBranch returns the LockBranch field if it's non-nil, zero value otherwise.
func (p *Protection) GetLockBranch() bool {
if p == nil || p.LockBranch == nil {
return false
}
return *p.LockBranch
}
// GetRequiredConversationResolution returns the RequiredConversationResolution field. // GetRequiredConversationResolution returns the RequiredConversationResolution field.
func (p *Protection) GetRequiredConversationResolution() *RequiredConversationResolution { func (p *Protection) GetRequiredConversationResolution() *RequiredConversationResolution {
if p == nil { if p == nil {
@ -13262,6 +13694,14 @@ func (p *PullRequestReviewsEnforcementUpdate) GetRequireCodeOwnerReviews() bool
return *p.RequireCodeOwnerReviews return *p.RequireCodeOwnerReviews
} }
// GetRequireLastPushApproval returns the RequireLastPushApproval field if it's non-nil, zero value otherwise.
func (p *PullRequestReviewsEnforcementUpdate) GetRequireLastPushApproval() bool {
if p == nil || p.RequireLastPushApproval == nil {
return false
}
return *p.RequireLastPushApproval
}
// GetAction returns the Action field if it's non-nil, zero value otherwise. // GetAction returns the Action field if it's non-nil, zero value otherwise.
func (p *PullRequestReviewThreadEvent) GetAction() string { func (p *PullRequestReviewThreadEvent) GetAction() string {
if p == nil || p.Action == nil { if p == nil || p.Action == nil {
@ -14718,6 +15158,14 @@ func (r *Repository) GetGitURL() string {
return *r.GitURL return *r.GitURL
} }
// GetHasDiscussions returns the HasDiscussions field if it's non-nil, zero value otherwise.
func (r *Repository) GetHasDiscussions() bool {
if r == nil || r.HasDiscussions == nil {
return false
}
return *r.HasDiscussions
}
// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. // GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise.
func (r *Repository) GetHasDownloads() bool { func (r *Repository) GetHasDownloads() bool {
if r == nil || r.HasDownloads == nil { if r == nil || r.HasDownloads == nil {
@ -15230,6 +15678,14 @@ func (r *Repository) GetWatchersCount() int {
return *r.WatchersCount return *r.WatchersCount
} }
// GetAccessLevel returns the AccessLevel field if it's non-nil, zero value otherwise.
func (r *RepositoryActionsAccessLevel) GetAccessLevel() string {
if r == nil || r.AccessLevel == nil {
return ""
}
return *r.AccessLevel
}
// GetAdvancedSecurityCommitters returns the AdvancedSecurityCommitters field if it's non-nil, zero value otherwise. // GetAdvancedSecurityCommitters returns the AdvancedSecurityCommitters field if it's non-nil, zero value otherwise.
func (r *RepositoryActiveCommitters) GetAdvancedSecurityCommitters() int { func (r *RepositoryActiveCommitters) GetAdvancedSecurityCommitters() int {
if r == nil || r.AdvancedSecurityCommitters == nil { if r == nil || r.AdvancedSecurityCommitters == nil {
@ -15950,6 +16406,14 @@ func (r *RepositoryRelease) GetID() int64 {
return *r.ID return *r.ID
} }
// GetMakeLatest returns the MakeLatest field if it's non-nil, zero value otherwise.
func (r *RepositoryRelease) GetMakeLatest() string {
if r == nil || r.MakeLatest == nil {
return ""
}
return *r.MakeLatest
}
// GetName returns the Name field if it's non-nil, zero value otherwise. // GetName returns the Name field if it's non-nil, zero value otherwise.
func (r *RepositoryRelease) GetName() string { func (r *RepositoryRelease) GetName() string {
if r == nil || r.Name == nil { if r == nil || r.Name == nil {
@ -16582,6 +17046,14 @@ func (r *RunnerGroup) GetName() string {
return *r.Name return *r.Name
} }
// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise.
func (r *RunnerGroup) GetRestrictedToWorkflows() bool {
if r == nil || r.RestrictedToWorkflows == nil {
return false
}
return *r.RestrictedToWorkflows
}
// GetRunnersURL returns the RunnersURL field if it's non-nil, zero value otherwise. // GetRunnersURL returns the RunnersURL field if it's non-nil, zero value otherwise.
func (r *RunnerGroup) GetRunnersURL() string { func (r *RunnerGroup) GetRunnersURL() string {
if r == nil || r.RunnersURL == nil { if r == nil || r.RunnersURL == nil {
@ -16606,6 +17078,14 @@ func (r *RunnerGroup) GetVisibility() string {
return *r.Visibility return *r.Visibility
} }
// GetWorkflowRestrictionsReadOnly returns the WorkflowRestrictionsReadOnly field if it's non-nil, zero value otherwise.
func (r *RunnerGroup) GetWorkflowRestrictionsReadOnly() bool {
if r == nil || r.WorkflowRestrictionsReadOnly == nil {
return false
}
return *r.WorkflowRestrictionsReadOnly
}
// GetID returns the ID field if it's non-nil, zero value otherwise. // GetID returns the ID field if it's non-nil, zero value otherwise.
func (r *RunnerLabels) GetID() int64 { func (r *RunnerLabels) GetID() int64 {
if r == nil || r.ID == nil { if r == nil || r.ID == nil {
@ -19070,6 +19550,14 @@ func (u *UpdateRunnerGroupRequest) GetName() string {
return *u.Name return *u.Name
} }
// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise.
func (u *UpdateRunnerGroupRequest) GetRestrictedToWorkflows() bool {
if u == nil || u.RestrictedToWorkflows == nil {
return false
}
return *u.RestrictedToWorkflows
}
// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. // GetVisibility returns the Visibility field if it's non-nil, zero value otherwise.
func (u *UpdateRunnerGroupRequest) GetVisibility() string { func (u *UpdateRunnerGroupRequest) GetVisibility() string {
if u == nil || u.Visibility == nil { if u == nil || u.Visibility == nil {
@ -20166,6 +20654,14 @@ func (w *WorkflowJob) GetNodeID() string {
return *w.NodeID return *w.NodeID
} }
// GetRunAttempt returns the RunAttempt field if it's non-nil, zero value otherwise.
func (w *WorkflowJob) GetRunAttempt() int64 {
if w == nil || w.RunAttempt == nil {
return 0
}
return *w.RunAttempt
}
// GetRunID returns the RunID field if it's non-nil, zero value otherwise. // GetRunID returns the RunID field if it's non-nil, zero value otherwise.
func (w *WorkflowJob) GetRunID() int64 { func (w *WorkflowJob) GetRunID() int64 {
if w == nil || w.RunID == nil { if w == nil || w.RunID == nil {

View file

@ -15,7 +15,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
@ -28,12 +27,14 @@ import (
) )
const ( const (
Version = "v48.0.0" Version = "v48.2.0"
defaultBaseURL = "https://api.github.com/" defaultAPIVersion = "2022-11-28"
defaultUserAgent = "go-github" + "/" + Version defaultBaseURL = "https://api.github.com/"
uploadBaseURL = "https://uploads.github.com/" defaultUserAgent = "go-github" + "/" + Version
uploadBaseURL = "https://uploads.github.com/"
headerAPIVersion = "X-GitHub-Api-Version"
headerRateLimit = "X-RateLimit-Limit" headerRateLimit = "X-RateLimit-Limit"
headerRateRemaining = "X-RateLimit-Remaining" headerRateRemaining = "X-RateLimit-Remaining"
headerRateReset = "X-RateLimit-Reset" headerRateReset = "X-RateLimit-Reset"
@ -237,6 +238,14 @@ type ListCursorOptions struct {
// For paginated result sets, the number of results to include per page. // For paginated result sets, the number of results to include per page.
PerPage int `url:"per_page,omitempty"` PerPage int `url:"per_page,omitempty"`
// For paginated result sets, the number of results per page (max 100), starting from the first matching result.
// This parameter must not be used in combination with last.
First int `url:"first,omitempty"`
// For paginated result sets, the number of results per page (max 100), starting from the last matching result.
// This parameter must not be used in combination with first.
Last int `url:"last,omitempty"`
// A cursor, as given in the Link header. If specified, the query only searches for events after this cursor. // A cursor, as given in the Link header. If specified, the query only searches for events after this cursor.
After string `url:"after,omitempty"` After string `url:"after,omitempty"`
@ -385,12 +394,24 @@ func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*C
return c, nil return c, nil
} }
// RequestOption represents an option that can modify an http.Request.
type RequestOption func(req *http.Request)
// WithVersion overrides the GitHub v3 API version for this individual request.
// For more information, see:
// https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/
func WithVersion(version string) RequestOption {
return func(req *http.Request) {
req.Header.Set(headerAPIVersion, version)
}
}
// NewRequest creates an API request. A relative URL can be provided in urlStr, // NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client. // in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If // Relative URLs should always be specified without a preceding slash. If
// specified, the value pointed to by body is JSON encoded and included as the // specified, the value pointed to by body is JSON encoded and included as the
// request body. // request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { func (c *Client) NewRequest(method, urlStr string, body interface{}, opts ...RequestOption) (*http.Request, error) {
if !strings.HasSuffix(c.BaseURL.Path, "/") { if !strings.HasSuffix(c.BaseURL.Path, "/") {
return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL)
} }
@ -423,6 +444,12 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
if c.UserAgent != "" { if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent) req.Header.Set("User-Agent", c.UserAgent)
} }
req.Header.Set(headerAPIVersion, defaultAPIVersion)
for _, opt := range opts {
opt(req)
}
return req, nil return req, nil
} }
@ -430,7 +457,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
// in which case it is resolved relative to the BaseURL of the Client. // in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. // Relative URLs should always be specified without a preceding slash.
// Body is sent with Content-Type: application/x-www-form-urlencoded. // Body is sent with Content-Type: application/x-www-form-urlencoded.
func (c *Client) NewFormRequest(urlStr string, body io.Reader) (*http.Request, error) { func (c *Client) NewFormRequest(urlStr string, body io.Reader, opts ...RequestOption) (*http.Request, error) {
if !strings.HasSuffix(c.BaseURL.Path, "/") { if !strings.HasSuffix(c.BaseURL.Path, "/") {
return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL)
} }
@ -450,13 +477,19 @@ func (c *Client) NewFormRequest(urlStr string, body io.Reader) (*http.Request, e
if c.UserAgent != "" { if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent) req.Header.Set("User-Agent", c.UserAgent)
} }
req.Header.Set(headerAPIVersion, defaultAPIVersion)
for _, opt := range opts {
opt(req)
}
return req, nil return req, nil
} }
// NewUploadRequest creates an upload request. A relative URL can be provided in // NewUploadRequest creates an upload request. A relative URL can be provided in
// urlStr, in which case it is resolved relative to the UploadURL of the Client. // urlStr, in which case it is resolved relative to the UploadURL of the Client.
// Relative URLs should always be specified without a preceding slash. // Relative URLs should always be specified without a preceding slash.
func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string, opts ...RequestOption) (*http.Request, error) {
if !strings.HasSuffix(c.UploadURL.Path, "/") { if !strings.HasSuffix(c.UploadURL.Path, "/") {
return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL) return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL)
} }
@ -478,6 +511,12 @@ func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, m
req.Header.Set("Content-Type", mediaType) req.Header.Set("Content-Type", mediaType)
req.Header.Set("Accept", mediaTypeV3) req.Header.Set("Accept", mediaTypeV3)
req.Header.Set("User-Agent", c.UserAgent) req.Header.Set("User-Agent", c.UserAgent)
req.Header.Set(headerAPIVersion, defaultAPIVersion)
for _, opt := range opts {
opt(req)
}
return req, nil return req, nil
} }
@ -710,7 +749,7 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro
// Issue #1022 // Issue #1022
aerr, ok := err.(*AcceptedError) aerr, ok := err.(*AcceptedError)
if ok { if ok {
b, readErr := ioutil.ReadAll(resp.Body) b, readErr := io.ReadAll(resp.Body)
if readErr != nil { if readErr != nil {
return response, readErr return response, readErr
} }
@ -770,7 +809,7 @@ func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rat
StatusCode: http.StatusForbidden, StatusCode: http.StatusForbidden,
Request: req, Request: req,
Header: make(http.Header), Header: make(http.Header),
Body: ioutil.NopCloser(strings.NewReader("")), Body: io.NopCloser(strings.NewReader("")),
} }
return &RateLimitError{ return &RateLimitError{
Rate: rate, Rate: rate,
@ -1032,14 +1071,14 @@ func CheckResponse(r *http.Response) error {
} }
errorResponse := &ErrorResponse{Response: r} errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body) data, err := io.ReadAll(r.Body)
if err == nil && data != nil { if err == nil && data != nil {
json.Unmarshal(data, errorResponse) json.Unmarshal(data, errorResponse)
} }
// Re-populate error response body because GitHub error responses are often // Re-populate error response body because GitHub error responses are often
// undocumented and inconsistent. // undocumented and inconsistent.
// Issue #1136, #540. // Issue #1136, #540.
r.Body = ioutil.NopCloser(bytes.NewBuffer(data)) r.Body = io.NopCloser(bytes.NewBuffer(data))
switch { switch {
case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"): case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"):
return (*TwoFactorAuthError)(errorResponse) return (*TwoFactorAuthError)(errorResponse)
@ -1351,8 +1390,8 @@ func formatRateReset(d time.Duration) string {
// When using roundTripWithOptionalFollowRedirect, note that it // When using roundTripWithOptionalFollowRedirect, note that it
// is the responsibility of the caller to close the response body. // is the responsibility of the caller to close the response body.
func (c *Client) roundTripWithOptionalFollowRedirect(ctx context.Context, u string, followRedirects bool) (*http.Response, error) { func (c *Client) roundTripWithOptionalFollowRedirect(ctx context.Context, u string, followRedirects bool, opts ...RequestOption) (*http.Response, error) {
req, err := c.NewRequest("GET", u, nil) req, err := c.NewRequest("GET", u, nil, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1373,7 +1412,7 @@ func (c *Client) roundTripWithOptionalFollowRedirect(ctx context.Context, u stri
if followRedirects && resp.StatusCode == http.StatusMovedPermanently { if followRedirects && resp.StatusCode == http.StatusMovedPermanently {
resp.Body.Close() resp.Body.Close()
u = resp.Header.Get("Location") u = resp.Header.Get("Location")
resp, err = c.roundTripWithOptionalFollowRedirect(ctx, u, false) resp, err = c.roundTripWithOptionalFollowRedirect(ctx, u, false, opts...)
} }
return resp, err return resp, err
} }

View file

@ -25,9 +25,11 @@ type IssuesService service
// this is an issue, and if PullRequestLinks is not nil, this is a pull request. // this is an issue, and if PullRequestLinks is not nil, this is a pull request.
// The IsPullRequest helper method can be used to check that. // The IsPullRequest helper method can be used to check that.
type Issue struct { type Issue struct {
ID *int64 `json:"id,omitempty"` ID *int64 `json:"id,omitempty"`
Number *int `json:"number,omitempty"` Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"` State *string `json:"state,omitempty"`
// StateReason can be one of: "completed", "not_planned", "reopened".
StateReason *string `json:"state_reason,omitempty"`
Locked *bool `json:"locked,omitempty"` Locked *bool `json:"locked,omitempty"`
Title *string `json:"title,omitempty"` Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"` Body *string `json:"body,omitempty"`

View file

@ -35,6 +35,8 @@ type Timeline struct {
SHA *string `json:"sha,omitempty"` SHA *string `json:"sha,omitempty"`
// The commit message. // The commit message.
Message *string `json:"message,omitempty"` Message *string `json:"message,omitempty"`
// A list of parent commits.
Parents []*Commit `json:"parents,omitempty"`
// Event identifies the actual type of Event that occurred. Possible values // Event identifies the actual type of Event that occurred. Possible values
// are: // are:

View file

@ -19,7 +19,6 @@ import (
"fmt" "fmt"
"hash" "hash"
"io" "io"
"io/ioutil"
"mime" "mime"
"net/http" "net/http"
"net/url" "net/url"
@ -68,6 +67,7 @@ var (
"marketplace_purchase": "MarketplacePurchaseEvent", "marketplace_purchase": "MarketplacePurchaseEvent",
"member": "MemberEvent", "member": "MemberEvent",
"membership": "MembershipEvent", "membership": "MembershipEvent",
"merge_group": "MergeGroupEvent",
"meta": "MetaEvent", "meta": "MetaEvent",
"milestone": "MilestoneEvent", "milestone": "MilestoneEvent",
"organization": "OrganizationEvent", "organization": "OrganizationEvent",
@ -170,7 +170,7 @@ func ValidatePayloadFromBody(contentType string, readable io.Reader, signature s
switch contentType { switch contentType {
case "application/json": case "application/json":
var err error var err error
if body, err = ioutil.ReadAll(readable); err != nil { if body, err = io.ReadAll(readable); err != nil {
return nil, err return nil, err
} }
@ -184,7 +184,7 @@ func ValidatePayloadFromBody(contentType string, readable io.Reader, signature s
const payloadFormParam = "payload" const payloadFormParam = "payload"
var err error var err error
if body, err = ioutil.ReadAll(readable); err != nil { if body, err = io.ReadAll(readable); err != nil {
return nil, err return nil, err
} }

View file

@ -81,7 +81,7 @@ func (s *OrganizationsService) RestorePackage(ctx context.Context, org, packageT
// Get all versions of a package in an organization. // Get all versions of a package in an organization.
// //
// GitHub API docs: https://docs.github.com/en/rest/packages#get-all-package-versions-for-a-package-owned-by-an-organization // GitHub API docs: https://docs.github.com/en/rest/packages#list-package-versions-for-a-package-owned-by-an-organization
func (s *OrganizationsService) PackageGetAllVersions(ctx context.Context, org, packageType, packageName string, opts *PackageListOptions) ([]*PackageVersion, *Response, error) { func (s *OrganizationsService) PackageGetAllVersions(ctx context.Context, org, packageType, packageName string, opts *PackageListOptions) ([]*PackageVersion, *Response, error) {
u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions", org, packageType, packageName) u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions", org, packageType, packageName)
u, err := addOptions(u, opts) u, err := addOptions(u, opts)

View file

@ -0,0 +1,57 @@
// Copyright 2022 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
)
// ListSecurityManagerTeams lists all security manager teams for an organization.
//
// GitHub API docs: https://docs.github.com/en/rest/orgs/security-managers#list-security-manager-teams
func (s *OrganizationsService) ListSecurityManagerTeams(ctx context.Context, org string) ([]*Team, *Response, error) {
u := fmt.Sprintf("orgs/%v/security-managers", org)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// AddSecurityManagerTeam adds a team to the list of security managers for an organization.
//
// GitHub API docs: https://docs.github.com/en/rest/orgs/security-managers#add-a-security-manager-team
func (s *OrganizationsService) AddSecurityManagerTeam(ctx context.Context, org, team string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/security-managers/teams/%v", org, team)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveSecurityManagerTeam removes a team from the list of security managers for an organization.
//
// GitHub API docs: https://docs.github.com/en/rest/orgs/security-managers#remove-a-security-manager-team
func (s *OrganizationsService) RemoveSecurityManagerTeam(ctx context.Context, org, team string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/security-managers/teams/%v", org, team)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View file

@ -88,6 +88,7 @@ type Repository struct {
HasPages *bool `json:"has_pages,omitempty"` HasPages *bool `json:"has_pages,omitempty"`
HasProjects *bool `json:"has_projects,omitempty"` HasProjects *bool `json:"has_projects,omitempty"`
HasDownloads *bool `json:"has_downloads,omitempty"` HasDownloads *bool `json:"has_downloads,omitempty"`
HasDiscussions *bool `json:"has_discussions,omitempty"`
IsTemplate *bool `json:"is_template,omitempty"` IsTemplate *bool `json:"is_template,omitempty"`
LicenseTemplate *string `json:"license_template,omitempty"` LicenseTemplate *string `json:"license_template,omitempty"`
GitignoreTemplate *string `json:"gitignore_template,omitempty"` GitignoreTemplate *string `json:"gitignore_template,omitempty"`
@ -365,12 +366,13 @@ type createRepoRequest struct {
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
Homepage *string `json:"homepage,omitempty"` Homepage *string `json:"homepage,omitempty"`
Private *bool `json:"private,omitempty"` Private *bool `json:"private,omitempty"`
Visibility *string `json:"visibility,omitempty"` Visibility *string `json:"visibility,omitempty"`
HasIssues *bool `json:"has_issues,omitempty"` HasIssues *bool `json:"has_issues,omitempty"`
HasProjects *bool `json:"has_projects,omitempty"` HasProjects *bool `json:"has_projects,omitempty"`
HasWiki *bool `json:"has_wiki,omitempty"` HasWiki *bool `json:"has_wiki,omitempty"`
IsTemplate *bool `json:"is_template,omitempty"` HasDiscussions *bool `json:"has_discussions,omitempty"`
IsTemplate *bool `json:"is_template,omitempty"`
// Creating an organization repository. Required for non-owners. // Creating an organization repository. Required for non-owners.
TeamID *int64 `json:"team_id,omitempty"` TeamID *int64 `json:"team_id,omitempty"`
@ -423,6 +425,7 @@ func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repo
HasIssues: repo.HasIssues, HasIssues: repo.HasIssues,
HasProjects: repo.HasProjects, HasProjects: repo.HasProjects,
HasWiki: repo.HasWiki, HasWiki: repo.HasWiki,
HasDiscussions: repo.HasDiscussions,
IsTemplate: repo.IsTemplate, IsTemplate: repo.IsTemplate,
TeamID: repo.TeamID, TeamID: repo.TeamID,
AutoInit: repo.AutoInit, AutoInit: repo.AutoInit,
@ -840,6 +843,10 @@ type Protection struct {
AllowForcePushes *AllowForcePushes `json:"allow_force_pushes"` AllowForcePushes *AllowForcePushes `json:"allow_force_pushes"`
AllowDeletions *AllowDeletions `json:"allow_deletions"` AllowDeletions *AllowDeletions `json:"allow_deletions"`
RequiredConversationResolution *RequiredConversationResolution `json:"required_conversation_resolution"` RequiredConversationResolution *RequiredConversationResolution `json:"required_conversation_resolution"`
// LockBranch represents if the branch is marked as read-only. If this is true, users will not be able to push to the branch.
LockBranch *bool `json:"lock_branch,omitempty"`
// AllowForkSyncing represents whether users can pull changes from upstream when the branch is locked.
AllowForkSyncing *bool `json:"allow_fork_syncing,omitempty"`
} }
// BranchProtectionRule represents the rule applied to a repositories branch. // BranchProtectionRule represents the rule applied to a repositories branch.
@ -1019,7 +1026,7 @@ type RequiredStatusCheck struct {
type PullRequestReviewsEnforcement struct { type PullRequestReviewsEnforcement struct {
// Allow specific users, teams, or apps to bypass pull request requirements. // Allow specific users, teams, or apps to bypass pull request requirements.
BypassPullRequestAllowances *BypassPullRequestAllowances `json:"bypass_pull_request_allowances,omitempty"` BypassPullRequestAllowances *BypassPullRequestAllowances `json:"bypass_pull_request_allowances,omitempty"`
// Specifies which users and teams can dismiss pull request reviews. // Specifies which users, teams and apps can dismiss pull request reviews.
DismissalRestrictions *DismissalRestrictions `json:"dismissal_restrictions,omitempty"` DismissalRestrictions *DismissalRestrictions `json:"dismissal_restrictions,omitempty"`
// Specifies if approved reviews are dismissed automatically, when a new commit is pushed. // Specifies if approved reviews are dismissed automatically, when a new commit is pushed.
DismissStaleReviews bool `json:"dismiss_stale_reviews"` DismissStaleReviews bool `json:"dismiss_stale_reviews"`
@ -1028,6 +1035,8 @@ type PullRequestReviewsEnforcement struct {
// RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged. // RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged.
// Valid values are 1-6. // Valid values are 1-6.
RequiredApprovingReviewCount int `json:"required_approving_review_count"` RequiredApprovingReviewCount int `json:"required_approving_review_count"`
// RequireLastPushApproval specifies whether the last pusher to a pull request branch can approve it.
RequireLastPushApproval bool `json:"require_last_push_approval"`
} }
// PullRequestReviewsEnforcementRequest represents request to set the pull request review // PullRequestReviewsEnforcementRequest represents request to set the pull request review
@ -1036,8 +1045,8 @@ type PullRequestReviewsEnforcement struct {
type PullRequestReviewsEnforcementRequest struct { type PullRequestReviewsEnforcementRequest struct {
// Allow specific users, teams, or apps to bypass pull request requirements. // Allow specific users, teams, or apps to bypass pull request requirements.
BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"` BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"`
// Specifies which users and teams should be allowed to dismiss pull request reviews. // Specifies which users, teams and apps should be allowed to dismiss pull request reviews.
// User and team dismissal restrictions are only available for // User, team and app dismissal restrictions are only available for
// organization-owned repositories. Must be nil for personal repositories. // organization-owned repositories. Must be nil for personal repositories.
DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"` DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"`
// Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. (Required) // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. (Required)
@ -1055,7 +1064,7 @@ type PullRequestReviewsEnforcementRequest struct {
type PullRequestReviewsEnforcementUpdate struct { type PullRequestReviewsEnforcementUpdate struct {
// Allow specific users, teams, or apps to bypass pull request requirements. // Allow specific users, teams, or apps to bypass pull request requirements.
BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"` BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"`
// Specifies which users and teams can dismiss pull request reviews. Can be omitted. // Specifies which users, teams and apps can dismiss pull request reviews. Can be omitted.
DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"` DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"`
// Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. Can be omitted. // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. Can be omitted.
DismissStaleReviews *bool `json:"dismiss_stale_reviews,omitempty"` DismissStaleReviews *bool `json:"dismiss_stale_reviews,omitempty"`
@ -1064,6 +1073,8 @@ type PullRequestReviewsEnforcementUpdate struct {
// RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged. // RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged.
// Valid values are 1 - 6 or 0 to not require reviewers. // Valid values are 1 - 6 or 0 to not require reviewers.
RequiredApprovingReviewCount int `json:"required_approving_review_count"` RequiredApprovingReviewCount int `json:"required_approving_review_count"`
// RequireLastPushApproval specifies whether the last pusher to a pull request branch can approve it.
RequireLastPushApproval *bool `json:"require_last_push_approval,omitempty"`
} }
// RequireLinearHistory represents the configuration to enforce branches with no merge commit. // RequireLinearHistory represents the configuration to enforce branches with no merge commit.
@ -1113,7 +1124,7 @@ type BranchRestrictionsRequest struct {
// The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.) // The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.)
Teams []string `json:"teams"` Teams []string `json:"teams"`
// The list of app slugs with push access. // The list of app slugs with push access.
Apps []string `json:"apps,omitempty"` Apps []string `json:"apps"`
} }
// BypassPullRequestAllowances represents the people, teams, or apps who are allowed to bypass required pull requests. // BypassPullRequestAllowances represents the people, teams, or apps who are allowed to bypass required pull requests.
@ -1145,10 +1156,12 @@ type DismissalRestrictions struct {
Users []*User `json:"users"` Users []*User `json:"users"`
// The list of teams which can dismiss pull request reviews. // The list of teams which can dismiss pull request reviews.
Teams []*Team `json:"teams"` Teams []*Team `json:"teams"`
// The list of apps which can dismiss pull request reviews.
Apps []*App `json:"apps"`
} }
// DismissalRestrictionsRequest represents the request to create/edit the // DismissalRestrictionsRequest represents the request to create/edit the
// restriction to allows only specific users or teams to dimiss pull request reviews. It is // restriction to allows only specific users, teams or apps to dimiss pull request reviews. It is
// separate from DismissalRestrictions above because the request structure is // separate from DismissalRestrictions above because the request structure is
// different from the response structure. // different from the response structure.
// Note: Both Users and Teams must be nil, or both must be non-nil. // Note: Both Users and Teams must be nil, or both must be non-nil.
@ -1157,6 +1170,8 @@ type DismissalRestrictionsRequest struct {
Users *[]string `json:"users,omitempty"` Users *[]string `json:"users,omitempty"`
// The list of team slugs which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.) // The list of team slugs which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.)
Teams *[]string `json:"teams,omitempty"` Teams *[]string `json:"teams,omitempty"`
// The list of apps which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.)
Apps *[]string `json:"apps,omitempty"`
} }
// SignaturesProtectedBranch represents the protection status of an individual branch. // SignaturesProtectedBranch represents the protection status of an individual branch.

View file

@ -0,0 +1,55 @@
// Copyright 2022 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
)
// RepositoryActionsAccessLevel represents the repository actions access level.
//
// GitHub API docs: https://docs.github.com/en/rest/actions/permissions#set-the-level-of-access-for-workflows-outside-of-the-repository
type RepositoryActionsAccessLevel struct {
// AccessLevel specifies the level of access that workflows outside of the repository have
// to actions and reusable workflows within the repository.
// Possible values are: "none", "organization" "enterprise".
AccessLevel *string `json:"access_level,omitempty"`
}
// GetActionsAccessLevel gets the level of access that workflows outside of the repository have
// to actions and reusable workflows in the repository.
//
// GitHub API docs: https://docs.github.com/en/rest/actions/permissions#get-the-level-of-access-for-workflows-outside-of-the-repository
func (s *RepositoriesService) GetActionsAccessLevel(ctx context.Context, owner, repo string) (*RepositoryActionsAccessLevel, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/permissions/access", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
raal := new(RepositoryActionsAccessLevel)
resp, err := s.client.Do(ctx, req, raal)
if err != nil {
return nil, resp, err
}
return raal, resp, nil
}
// EditActionsAccessLevel sets the level of access that workflows outside of the repository have
// to actions and reusable workflows in the repository.
//
// GitHub API docs: https://docs.github.com/en/rest/actions/permissions#set-the-level-of-access-for-workflows-outside-of-the-repository
func (s *RepositoriesService) EditActionsAccessLevel(ctx context.Context, owner, repo string, repositoryActionsAccessLevel RepositoryActionsAccessLevel) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/permissions/access", owner, repo)
req, err := s.client.NewRequest("PUT", u, repositoryActionsAccessLevel)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View file

@ -7,9 +7,8 @@ package github
import ( import (
"context" "context"
"fmt"
"encoding/json" "encoding/json"
"fmt"
) )
// RepositoryListForksOptions specifies the optional parameters to the // RepositoryListForksOptions specifies the optional parameters to the
@ -53,9 +52,9 @@ func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string,
// RepositoriesService.CreateFork method. // RepositoriesService.CreateFork method.
type RepositoryCreateForkOptions struct { type RepositoryCreateForkOptions struct {
// The organization to fork the repository into. // The organization to fork the repository into.
Organization string `url:"organization,omitempty"` Organization string `json:"organization,omitempty"`
Name string `url:"name,omitempty"` Name string `json:"name,omitempty"`
DefaultBranchOnly bool `url:"default_branch_only,omitempty"` DefaultBranchOnly bool `json:"default_branch_only,omitempty"`
} }
// CreateFork creates a fork of the specified repository. // CreateFork creates a fork of the specified repository.
@ -70,12 +69,8 @@ type RepositoryCreateForkOptions struct {
// GitHub API docs: https://docs.github.com/en/rest/repos/forks#create-a-fork // GitHub API docs: https://docs.github.com/en/rest/repos/forks#create-a-fork
func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, opts *RepositoryCreateForkOptions) (*Repository, *Response, error) { func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, opts *RepositoryCreateForkOptions) (*Repository, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) u := fmt.Sprintf("repos/%v/%v/forks", owner, repo)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("POST", u, nil) req, err := s.client.NewRequest("POST", u, opts)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View file

@ -19,12 +19,14 @@ import (
// RepositoryRelease represents a GitHub release in a repository. // RepositoryRelease represents a GitHub release in a repository.
type RepositoryRelease struct { type RepositoryRelease struct {
TagName *string `json:"tag_name,omitempty"` TagName *string `json:"tag_name,omitempty"`
TargetCommitish *string `json:"target_commitish,omitempty"` TargetCommitish *string `json:"target_commitish,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Body *string `json:"body,omitempty"` Body *string `json:"body,omitempty"`
Draft *bool `json:"draft,omitempty"` Draft *bool `json:"draft,omitempty"`
Prerelease *bool `json:"prerelease,omitempty"` Prerelease *bool `json:"prerelease,omitempty"`
// MakeLatest can be one of: "true", "false", or "legacy".
MakeLatest *string `json:"make_latest,omitempty"`
DiscussionCategoryName *string `json:"discussion_category_name,omitempty"` DiscussionCategoryName *string `json:"discussion_category_name,omitempty"`
// The following fields are not used in EditRelease: // The following fields are not used in EditRelease:
@ -176,6 +178,7 @@ type repositoryReleaseRequest struct {
Body *string `json:"body,omitempty"` Body *string `json:"body,omitempty"`
Draft *bool `json:"draft,omitempty"` Draft *bool `json:"draft,omitempty"`
Prerelease *bool `json:"prerelease,omitempty"` Prerelease *bool `json:"prerelease,omitempty"`
MakeLatest *string `json:"make_latest,omitempty"`
GenerateReleaseNotes *bool `json:"generate_release_notes,omitempty"` GenerateReleaseNotes *bool `json:"generate_release_notes,omitempty"`
DiscussionCategoryName *string `json:"discussion_category_name,omitempty"` DiscussionCategoryName *string `json:"discussion_category_name,omitempty"`
} }
@ -196,6 +199,7 @@ func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo str
Body: release.Body, Body: release.Body,
Draft: release.Draft, Draft: release.Draft,
Prerelease: release.Prerelease, Prerelease: release.Prerelease,
MakeLatest: release.MakeLatest,
DiscussionCategoryName: release.DiscussionCategoryName, DiscussionCategoryName: release.DiscussionCategoryName,
GenerateReleaseNotes: release.GenerateReleaseNotes, GenerateReleaseNotes: release.GenerateReleaseNotes,
} }
@ -229,6 +233,7 @@ func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo strin
Body: release.Body, Body: release.Body,
Draft: release.Draft, Draft: release.Draft,
Prerelease: release.Prerelease, Prerelease: release.Prerelease,
MakeLatest: release.MakeLatest,
DiscussionCategoryName: release.DiscussionCategoryName, DiscussionCategoryName: release.DiscussionCategoryName,
} }

View file

@ -1,13 +1,201 @@
Copyright 2014 Alan Shreve Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Licensed under the Apache License, Version 2.0 (the "License"); TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 1. Definitions.
Unless required by applicable law or agreed to in writing, software "License" shall mean the terms and conditions for use, reproduction,
distributed under the License is distributed on an "AS IS" BASIS, and distribution as defined by Sections 1 through 9 of this document.
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and "Licensor" shall mean the copyright owner or entity authorized by
limitations under the License. the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2022 Alan Shreve (@inconshreveable)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package mousetrap package mousetrap

View file

@ -1,79 +1,30 @@
// +build windows
// +build !go1.4
package mousetrap package mousetrap
import ( import (
"fmt"
"os"
"syscall" "syscall"
"unsafe" "unsafe"
) )
const ( func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) {
// defined by the Win32 API snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
th32cs_snapprocess uintptr = 0x2
)
var (
kernel = syscall.MustLoadDLL("kernel32.dll")
CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot")
Process32First = kernel.MustFindProc("Process32FirstW")
Process32Next = kernel.MustFindProc("Process32NextW")
)
// ProcessEntry32 structure defined by the Win32 API
type processEntry32 struct {
dwSize uint32
cntUsage uint32
th32ProcessID uint32
th32DefaultHeapID int
th32ModuleID uint32
cntThreads uint32
th32ParentProcessID uint32
pcPriClassBase int32
dwFlags uint32
szExeFile [syscall.MAX_PATH]uint16
}
func getProcessEntry(pid int) (pe *processEntry32, err error) {
snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0))
if snapshot == uintptr(syscall.InvalidHandle) {
err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1)
return
}
defer syscall.CloseHandle(syscall.Handle(snapshot))
var processEntry processEntry32
processEntry.dwSize = uint32(unsafe.Sizeof(processEntry))
ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
if ok == 0 {
err = fmt.Errorf("Process32First: %v", e1)
return
}
for {
if processEntry.th32ProcessID == uint32(pid) {
pe = &processEntry
return
}
ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
if ok == 0 {
err = fmt.Errorf("Process32Next: %v", e1)
return
}
}
}
func getppid() (pid int, err error) {
pe, err := getProcessEntry(os.Getpid())
if err != nil { if err != nil {
return return nil, err
}
defer syscall.CloseHandle(snapshot)
var procEntry syscall.ProcessEntry32
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
if err = syscall.Process32First(snapshot, &procEntry); err != nil {
return nil, err
}
for {
if procEntry.ProcessID == uint32(pid) {
return &procEntry, nil
}
err = syscall.Process32Next(snapshot, &procEntry)
if err != nil {
return nil, err
}
} }
pid = int(pe.th32ParentProcessID)
return
} }
// StartedByExplorer returns true if the program was invoked by the user double-clicking // StartedByExplorer returns true if the program was invoked by the user double-clicking
@ -83,16 +34,9 @@ func getppid() (pid int, err error) {
// It does not guarantee that the program was run from a terminal. It only can tell you // It does not guarantee that the program was run from a terminal. It only can tell you
// whether it was launched from explorer.exe // whether it was launched from explorer.exe
func StartedByExplorer() bool { func StartedByExplorer() bool {
ppid, err := getppid() pe, err := getProcessEntry(syscall.Getppid())
if err != nil { if err != nil {
return false return false
} }
return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:])
pe, err := getProcessEntry(ppid)
if err != nil {
return false
}
name := syscall.UTF16ToString(pe.szExeFile[:])
return name == "explorer.exe"
} }

View file

@ -1,46 +0,0 @@
// +build windows
// +build go1.4
package mousetrap
import (
"os"
"syscall"
"unsafe"
)
func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) {
snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
if err != nil {
return nil, err
}
defer syscall.CloseHandle(snapshot)
var procEntry syscall.ProcessEntry32
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
if err = syscall.Process32First(snapshot, &procEntry); err != nil {
return nil, err
}
for {
if procEntry.ProcessID == uint32(pid) {
return &procEntry, nil
}
err = syscall.Process32Next(snapshot, &procEntry)
if err != nil {
return nil, err
}
}
}
// StartedByExplorer returns true if the program was invoked by the user double-clicking
// on the executable from explorer.exe
//
// It is conservative and returns false if any of the internal calls fail.
// It does not guarantee that the program was run from a terminal. It only can tell you
// whether it was launched from explorer.exe
func StartedByExplorer() bool {
pe, err := getProcessEntry(os.Getppid())
if err != nil {
return false
}
return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:])
}

View file

@ -37,7 +37,6 @@ Pretty-print tables into ASCII/Unicode strings.
- HTML Table (with custom CSS Class) - HTML Table (with custom CSS Class)
- Markdown Table - Markdown Table
``` ```
+---------------------------------------------------------------------+ +---------------------------------------------------------------------+
| Game of Thrones + | Game of Thrones +
@ -57,6 +56,11 @@ A demonstration of all the capabilities can be found here:
If you want very specific examples, read ahead. If you want very specific examples, read ahead.
**Hint**: I've tried to ensure that almost all supported use-cases are covered
by unit-tests and that they print the table rendered. Run
`go test -v github.com/jedib0t/go-pretty/v6/table` to see the test outputs and
help you figure out how to do something.
# Examples # Examples
All the examples below are going to start with the following block, although All the examples below are going to start with the following block, although

View file

@ -85,8 +85,19 @@ func (c ColumnConfig) getWidthMaxEnforcer() WidthEnforcer {
type RowConfig struct { type RowConfig struct {
// AutoMerge merges cells with similar values and prevents separators from // AutoMerge merges cells with similar values and prevents separators from
// being drawn. Caveats: // being drawn. Caveats:
// * Align is overridden to text.AlignCenter on the merged cell // * Align is overridden to text.AlignCenter on the merged cell (unless set
// by AutoMergeAlign value below)
// * Does not work in CSV/HTML/Markdown render modes // * Does not work in CSV/HTML/Markdown render modes
// * Does not work well with vertical auto-merge (ColumnConfig.AutoMerge) // * Does not work well with vertical auto-merge (ColumnConfig.AutoMerge)
AutoMerge bool AutoMerge bool
// Alignment to use on a merge (defaults to text.AlignCenter)
AutoMergeAlign text.Align
}
func (rc RowConfig) getAutoMergeAlign() text.Align {
if rc.AutoMergeAlign == text.AlignDefault {
return text.AlignCenter
}
return rc.AutoMergeAlign
} }

View file

@ -78,12 +78,16 @@ func (t *Table) renderColumn(out *strings.Builder, row rowStr, colIdx int, maxCo
// if horizontal cell merges are enabled, look ahead and see how many cells // if horizontal cell merges are enabled, look ahead and see how many cells
// have the same content and merge them all until a cell with a different // have the same content and merge them all until a cell with a different
// content is found; override alignment to Center in this case // content is found; override alignment to Center in this case
if t.getRowConfig(hint).AutoMerge && !hint.isSeparatorRow { rowConfig := t.getRowConfig(hint)
for idx := colIdx + 1; idx < len(row); idx++ { if rowConfig.AutoMerge && !hint.isSeparatorRow {
if row[colIdx] != row[idx] { // get the real row to consider all lines in each column instead of just
// looking at the current "line"
rowUnwrapped := t.getRow(hint.rowNumber-1, hint)
for idx := colIdx + 1; idx < len(rowUnwrapped); idx++ {
if rowUnwrapped[colIdx] != rowUnwrapped[idx] {
break break
} }
align = text.AlignCenter align = rowConfig.getAutoMergeAlign()
maxColumnLength += t.getMaxColumnLengthForMerging(idx) maxColumnLength += t.getMaxColumnLengthForMerging(idx)
numColumnsRendered++ numColumnsRendered++
} }
@ -220,7 +224,7 @@ func (t *Table) renderLine(out *strings.Builder, row rowStr, hint renderHint) {
func (t *Table) renderLineMergeOutputs(out *strings.Builder, outLine *strings.Builder) { func (t *Table) renderLineMergeOutputs(out *strings.Builder, outLine *strings.Builder) {
outLineStr := outLine.String() outLineStr := outLine.String()
if text.RuneCount(outLineStr) > t.allowedRowLength { if text.RuneWidthWithoutEscSequences(outLineStr) > t.allowedRowLength {
trimLength := t.allowedRowLength - utf8.RuneCountInString(t.style.Box.UnfinishedRow) trimLength := t.allowedRowLength - utf8.RuneCountInString(t.style.Box.UnfinishedRow)
if trimLength > 0 { if trimLength > 0 {
out.WriteString(text.Trim(outLineStr, trimLength)) out.WriteString(text.Trim(outLineStr, trimLength))
@ -232,6 +236,7 @@ func (t *Table) renderLineMergeOutputs(out *strings.Builder, outLine *strings.Bu
} }
func (t *Table) renderMarginLeft(out *strings.Builder, hint renderHint) { func (t *Table) renderMarginLeft(out *strings.Builder, hint renderHint) {
out.WriteString(t.style.Format.Direction.Modifier())
if t.style.Options.DrawBorder { if t.style.Options.DrawBorder {
border := t.getBorderLeft(hint) border := t.getBorderLeft(hint)
colors := t.getBorderColors(hint) colors := t.getBorderColors(hint)
@ -316,11 +321,19 @@ func (t *Table) renderRows(out *strings.Builder, rows []rowStr, hint renderHint)
} }
func (t *Table) renderRowsBorderBottom(out *strings.Builder) { func (t *Table) renderRowsBorderBottom(out *strings.Builder) {
t.renderRowSeparator(out, renderHint{isBorderBottom: true, isFooterRow: true}) if len(t.rowsFooter) > 0 {
t.renderRowSeparator(out, renderHint{isBorderBottom: true, isFooterRow: true, rowNumber: len(t.rowsFooter)})
} else {
t.renderRowSeparator(out, renderHint{isBorderBottom: true, isFooterRow: false, rowNumber: len(t.rows)})
}
} }
func (t *Table) renderRowsBorderTop(out *strings.Builder) { func (t *Table) renderRowsBorderTop(out *strings.Builder) {
t.renderRowSeparator(out, renderHint{isBorderTop: true, isHeaderRow: true}) if len(t.rowsHeader) > 0 || t.autoIndex {
t.renderRowSeparator(out, renderHint{isBorderTop: true, isHeaderRow: true, rowNumber: 0})
} else {
t.renderRowSeparator(out, renderHint{isBorderTop: true, isHeaderRow: false, rowNumber: 0})
}
} }
func (t *Table) renderRowsFooter(out *strings.Builder) { func (t *Table) renderRowsFooter(out *strings.Builder) {
@ -336,59 +349,59 @@ func (t *Table) renderRowsFooter(out *strings.Builder) {
func (t *Table) renderRowsHeader(out *strings.Builder) { func (t *Table) renderRowsHeader(out *strings.Builder) {
if len(t.rowsHeader) > 0 || t.autoIndex { if len(t.rowsHeader) > 0 || t.autoIndex {
hintSeparator := renderHint{isHeaderRow: true, isLastRow: true, isSeparatorRow: true}
if len(t.rowsHeader) > 0 { if len(t.rowsHeader) > 0 {
t.renderRows(out, t.rowsHeader, renderHint{isHeaderRow: true}) t.renderRows(out, t.rowsHeader, renderHint{isHeaderRow: true})
hintSeparator.rowNumber = len(t.rowsHeader)
} else if t.autoIndex { } else if t.autoIndex {
t.renderRow(out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true}) t.renderRow(out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true})
hintSeparator.rowNumber = 1
} }
t.renderRowSeparator(out, renderHint{ t.renderRowSeparator(out, hintSeparator)
isHeaderRow: true,
isLastRow: true,
isSeparatorRow: true,
rowNumber: len(t.rowsHeader),
})
} }
} }
func (t *Table) renderTitle(out *strings.Builder) { func (t *Table) renderTitle(out *strings.Builder) {
if t.title != "" { if t.title != "" {
colors := t.style.Title.Colors
colorsBorder := t.getBorderColors(renderHint{isTitleRow: true})
rowLength := t.maxRowLength rowLength := t.maxRowLength
if t.allowedRowLength != 0 && t.allowedRowLength < rowLength { if t.allowedRowLength != 0 && t.allowedRowLength < rowLength {
rowLength = t.allowedRowLength rowLength = t.allowedRowLength
} }
if t.style.Options.DrawBorder { if t.style.Options.DrawBorder {
lenBorder := rowLength - text.RuneCount(t.style.Box.TopLeft+t.style.Box.TopRight) lenBorder := rowLength - text.RuneWidthWithoutEscSequences(t.style.Box.TopLeft+t.style.Box.TopRight)
out.WriteString(t.style.Box.TopLeft) out.WriteString(colorsBorder.Sprint(t.style.Box.TopLeft))
out.WriteString(text.RepeatAndTrim(t.style.Box.MiddleHorizontal, lenBorder)) out.WriteString(colorsBorder.Sprint(text.RepeatAndTrim(t.style.Box.MiddleHorizontal, lenBorder)))
out.WriteString(t.style.Box.TopRight) out.WriteString(colorsBorder.Sprint(t.style.Box.TopRight))
} }
lenText := rowLength - text.RuneCount(t.style.Box.PaddingLeft+t.style.Box.PaddingRight) lenText := rowLength - text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft+t.style.Box.PaddingRight)
if t.style.Options.DrawBorder { if t.style.Options.DrawBorder {
lenText -= text.RuneCount(t.style.Box.Left + t.style.Box.Right) lenText -= text.RuneWidthWithoutEscSequences(t.style.Box.Left + t.style.Box.Right)
} }
titleText := text.WrapText(t.title, lenText) titleText := text.WrapText(t.title, lenText)
for _, titleLine := range strings.Split(titleText, "\n") { for _, titleLine := range strings.Split(titleText, "\n") {
t.renderTitleLine(out, lenText, titleLine) t.renderTitleLine(out, lenText, titleLine, colors, colorsBorder)
} }
} }
} }
func (t *Table) renderTitleLine(out *strings.Builder, lenText int, titleLine string) { func (t *Table) renderTitleLine(out *strings.Builder, lenText int, titleLine string, colors text.Colors, colorsBorder text.Colors) {
titleLine = strings.TrimSpace(titleLine) titleLine = strings.TrimSpace(titleLine)
titleLine = t.style.Title.Format.Apply(titleLine) titleLine = t.style.Title.Format.Apply(titleLine)
titleLine = t.style.Title.Align.Apply(titleLine, lenText) titleLine = t.style.Title.Align.Apply(titleLine, lenText)
titleLine = t.style.Box.PaddingLeft + titleLine + t.style.Box.PaddingRight titleLine = t.style.Box.PaddingLeft + titleLine + t.style.Box.PaddingRight
titleLine = t.style.Title.Colors.Sprint(titleLine)
if out.Len() > 0 { if out.Len() > 0 {
out.WriteRune('\n') out.WriteRune('\n')
} }
if t.style.Options.DrawBorder { if t.style.Options.DrawBorder {
out.WriteString(t.style.Box.Left) out.WriteString(colorsBorder.Sprint(t.style.Box.Left))
} }
out.WriteString(titleLine) out.WriteString(colors.Sprint(titleLine))
if t.style.Options.DrawBorder { if t.style.Options.DrawBorder {
out.WriteString(t.style.Box.Right) out.WriteString(colorsBorder.Sprint(t.style.Box.Right))
} }
} }

View file

@ -0,0 +1,39 @@
package table
// renderHint has hints for the Render*() logic
type renderHint struct {
isAutoIndexColumn bool // auto-index column?
isAutoIndexRow bool // auto-index row?
isBorderBottom bool // bottom-border?
isBorderTop bool // top-border?
isFirstRow bool // first-row of header/footer/regular-rows?
isFooterRow bool // footer row?
isHeaderRow bool // header row?
isLastLineOfRow bool // last-line of the current row?
isLastRow bool // last-row of header/footer/regular-rows?
isSeparatorRow bool // separator row?
isTitleRow bool // title row?
rowLineNumber int // the line number for a multi-line row
rowNumber int // the row number/index
}
func (h *renderHint) isBorderOrSeparator() bool {
return h.isBorderTop || h.isSeparatorRow || h.isBorderBottom
}
func (h *renderHint) isRegularRow() bool {
return !h.isHeaderRow && !h.isFooterRow
}
func (h *renderHint) isRegularNonSeparatorRow() bool {
return !h.isHeaderRow && !h.isFooterRow && !h.isSeparatorRow
}
func (h *renderHint) isHeaderOrFooterSeparator() bool {
return h.isSeparatorRow && !h.isBorderBottom && !h.isBorderTop &&
((h.isHeaderRow && !h.isLastRow) || (h.isFooterRow && (!h.isFirstRow || h.rowNumber > 0)))
}
func (h *renderHint) isLastLineOfLastRow() bool {
return h.isLastLineOfRow && h.isLastRow
}

View file

@ -0,0 +1,294 @@
package table
import (
"fmt"
"strings"
"github.com/jedib0t/go-pretty/v6/text"
)
func (t *Table) analyzeAndStringify(row Row, hint renderHint) rowStr {
// update t.numColumns if this row is the longest seen till now
if len(row) > t.numColumns {
// init the slice for the first time; and pad it the rest of the time
if t.numColumns == 0 {
t.columnIsNonNumeric = make([]bool, len(row))
} else {
t.columnIsNonNumeric = append(t.columnIsNonNumeric, make([]bool, len(row)-t.numColumns)...)
}
// update t.numColumns
t.numColumns = len(row)
}
// convert each column to string and figure out if it has non-numeric data
rowOut := make(rowStr, len(row))
for colIdx, col := range row {
// if the column is not a number, keep track of it
if !hint.isHeaderRow && !hint.isFooterRow && !t.columnIsNonNumeric[colIdx] && !isNumber(col) {
t.columnIsNonNumeric[colIdx] = true
}
rowOut[colIdx] = t.analyzeAndStringifyColumn(colIdx, col, hint)
}
return rowOut
}
func (t *Table) analyzeAndStringifyColumn(colIdx int, col interface{}, hint renderHint) string {
// convert to a string and store it in the row
var colStr string
if transformer := t.getColumnTransformer(colIdx, hint); transformer != nil {
colStr = transformer(col)
} else if colStrVal, ok := col.(string); ok {
colStr = colStrVal
} else {
colStr = fmt.Sprint(col)
}
if strings.Contains(colStr, "\t") {
colStr = strings.Replace(colStr, "\t", " ", -1)
}
if strings.Contains(colStr, "\r") {
colStr = strings.Replace(colStr, "\r", "", -1)
}
return fmt.Sprintf("%s%s", t.style.Format.Direction.Modifier(), colStr)
}
func (t *Table) extractMaxColumnLengths(rows []rowStr, hint renderHint) {
for rowIdx, row := range rows {
hint.rowNumber = rowIdx + 1
t.extractMaxColumnLengthsFromRow(row, t.getMergedColumnIndices(row, hint))
}
}
func (t *Table) extractMaxColumnLengthsFromRow(row rowStr, mci mergedColumnIndices) {
for colIdx, colStr := range row {
longestLineLen := text.LongestLineLen(colStr)
maxColWidth := t.getColumnWidthMax(colIdx)
if maxColWidth > 0 && maxColWidth < longestLineLen {
longestLineLen = maxColWidth
}
mergedColumnsLength := mci.mergedLength(colIdx, t.maxColumnLengths)
if longestLineLen > mergedColumnsLength {
if mergedColumnsLength > 0 {
t.extractMaxColumnLengthsFromRowForMergedColumns(colIdx, longestLineLen, mci)
} else {
t.maxColumnLengths[colIdx] = longestLineLen
}
} else if maxColWidth == 0 && longestLineLen > t.maxColumnLengths[colIdx] {
t.maxColumnLengths[colIdx] = longestLineLen
}
}
}
func (t *Table) extractMaxColumnLengthsFromRowForMergedColumns(colIdx int, mergedColumnLength int, mci mergedColumnIndices) {
numMergedColumns := mci.len(colIdx)
mergedColumnLength -= (numMergedColumns - 1) * text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator)
maxLengthSplitAcrossColumns := mergedColumnLength / numMergedColumns
if maxLengthSplitAcrossColumns > t.maxColumnLengths[colIdx] {
t.maxColumnLengths[colIdx] = maxLengthSplitAcrossColumns
}
for otherColIdx := range mci[colIdx] {
if maxLengthSplitAcrossColumns > t.maxColumnLengths[otherColIdx] {
t.maxColumnLengths[otherColIdx] = maxLengthSplitAcrossColumns
}
}
}
func (t *Table) initForRender() {
// pick a default style if none was set until now
t.Style()
// reset rendering state
t.reset()
// initialize the column configs and normalize them
t.initForRenderColumnConfigs()
// initialize and stringify all the raw rows
t.initForRenderRows()
// find the longest continuous line in each column
t.initForRenderColumnLengths()
// generate a separator row and calculate maximum row length
t.initForRenderRowSeparator()
// reset the counter for the number of lines rendered
t.numLinesRendered = 0
}
func (t *Table) initForRenderColumnConfigs() {
t.columnConfigMap = map[int]ColumnConfig{}
for _, colCfg := range t.columnConfigs {
// find the column number if none provided; this logic can work only if
// a header row is present and has a column with the given name
if colCfg.Number == 0 {
for _, row := range t.rowsHeaderRaw {
colCfg.Number = row.findColumnNumber(colCfg.Name)
if colCfg.Number > 0 {
break
}
}
}
if colCfg.Number > 0 {
t.columnConfigMap[colCfg.Number-1] = colCfg
}
}
}
func (t *Table) initForRenderColumnLengths() {
t.maxColumnLengths = make([]int, t.numColumns)
t.extractMaxColumnLengths(t.rowsHeader, renderHint{isHeaderRow: true})
t.extractMaxColumnLengths(t.rows, renderHint{})
t.extractMaxColumnLengths(t.rowsFooter, renderHint{isFooterRow: true})
// increase the column lengths if any are under the limits
for colIdx := range t.maxColumnLengths {
minWidth := t.getColumnWidthMin(colIdx)
if minWidth > 0 && t.maxColumnLengths[colIdx] < minWidth {
t.maxColumnLengths[colIdx] = minWidth
}
}
}
func (t *Table) initForRenderHideColumns() {
if !t.hasHiddenColumns() {
return
}
colIdxMap := t.hideColumns()
// re-create columnIsNonNumeric with new column indices
columnIsNonNumeric := make([]bool, t.numColumns)
for oldColIdx, nonNumeric := range t.columnIsNonNumeric {
if newColIdx, ok := colIdxMap[oldColIdx]; ok {
columnIsNonNumeric[newColIdx] = nonNumeric
}
}
t.columnIsNonNumeric = columnIsNonNumeric
// re-create columnConfigMap with new column indices
columnConfigMap := make(map[int]ColumnConfig)
for oldColIdx, cc := range t.columnConfigMap {
if newColIdx, ok := colIdxMap[oldColIdx]; ok {
columnConfigMap[newColIdx] = cc
}
}
t.columnConfigMap = columnConfigMap
}
func (t *Table) initForRenderRows() {
// auto-index: calc the index column's max length
t.autoIndexVIndexMaxLength = len(fmt.Sprint(len(t.rowsRaw)))
// stringify all the rows to make it easy to render
if t.rowPainter != nil {
t.rowsColors = make([]text.Colors, len(t.rowsRaw))
}
t.rows = t.initForRenderRowsStringify(t.rowsRaw, renderHint{})
t.rowsFooter = t.initForRenderRowsStringify(t.rowsFooterRaw, renderHint{isFooterRow: true})
t.rowsHeader = t.initForRenderRowsStringify(t.rowsHeaderRaw, renderHint{isHeaderRow: true})
// sort the rows as requested
t.initForRenderSortRows()
// suppress columns without any content
t.initForRenderSuppressColumns()
// strip out hidden columns
t.initForRenderHideColumns()
}
func (t *Table) initForRenderRowsStringify(rows []Row, hint renderHint) []rowStr {
rowsStr := make([]rowStr, len(rows))
for idx, row := range rows {
if t.rowPainter != nil && hint.isRegularRow() {
t.rowsColors[idx] = t.rowPainter(row)
}
rowsStr[idx] = t.analyzeAndStringify(row, hint)
}
return rowsStr
}
func (t *Table) initForRenderRowSeparator() {
t.maxRowLength = 0
if t.autoIndex {
t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft)
t.maxRowLength += len(fmt.Sprint(len(t.rows)))
t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingRight)
if t.style.Options.SeparateColumns {
t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator)
}
}
if t.style.Options.SeparateColumns {
t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator) * (t.numColumns - 1)
}
t.rowSeparator = make(rowStr, t.numColumns)
for colIdx, maxColumnLength := range t.maxColumnLengths {
maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft + t.style.Box.PaddingRight)
t.maxRowLength += maxColumnLength
t.rowSeparator[colIdx] = text.RepeatAndTrim(t.style.Box.MiddleHorizontal, maxColumnLength)
}
if t.style.Options.DrawBorder {
t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.Left + t.style.Box.Right)
}
}
func (t *Table) initForRenderSortRows() {
if len(t.sortBy) == 0 {
return
}
// sort the rows
sortedRowIndices := t.getSortedRowIndices()
sortedRows := make([]rowStr, len(t.rows))
for idx := range t.rows {
sortedRows[idx] = t.rows[sortedRowIndices[idx]]
}
t.rows = sortedRows
// sort the rowsColors
if len(t.rowsColors) > 0 {
sortedRowsColors := make([]text.Colors, len(t.rows))
for idx := range t.rows {
sortedRowsColors[idx] = t.rowsColors[sortedRowIndices[idx]]
}
t.rowsColors = sortedRowsColors
}
}
func (t *Table) initForRenderSuppressColumns() {
shouldSuppressColumn := func(colIdx int) bool {
for _, row := range t.rows {
if colIdx < len(row) && row[colIdx] != "" {
return false
}
}
return true
}
if t.suppressEmptyColumns {
for colIdx := 0; colIdx < t.numColumns; colIdx++ {
if shouldSuppressColumn(colIdx) {
cc := t.columnConfigMap[colIdx]
cc.Hidden = true
t.columnConfigMap[colIdx] = cc
}
}
}
}
// reset initializes all the variables used to maintain rendering information
// that are written to in this file
func (t *Table) reset() {
t.autoIndexVIndexMaxLength = 0
t.columnConfigMap = nil
t.columnIsNonNumeric = nil
t.maxColumnLengths = nil
t.maxRowLength = 0
t.numColumns = 0
t.numLinesRendered = 0
t.rowSeparator = nil
t.rows = nil
t.rowsColors = nil
t.rowsFooter = nil
t.rowsHeader = nil
}

View file

@ -358,7 +358,7 @@ var (
BottomLeft: "+", BottomLeft: "+",
BottomRight: "+", BottomRight: "+",
BottomSeparator: "+", BottomSeparator: "+",
EmptySeparator: text.RepeatAndTrim(" ", text.RuneCount("+")), EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("+")),
Left: "|", Left: "|",
LeftSeparator: "+", LeftSeparator: "+",
MiddleHorizontal: "-", MiddleHorizontal: "-",
@ -389,7 +389,7 @@ var (
BottomLeft: "┗", BottomLeft: "┗",
BottomRight: "┛", BottomRight: "┛",
BottomSeparator: "┻", BottomSeparator: "┻",
EmptySeparator: text.RepeatAndTrim(" ", text.RuneCount("╋")), EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("╋")),
Left: "┃", Left: "┃",
LeftSeparator: "┣", LeftSeparator: "┣",
MiddleHorizontal: "━", MiddleHorizontal: "━",
@ -420,7 +420,7 @@ var (
BottomLeft: "╚", BottomLeft: "╚",
BottomRight: "╝", BottomRight: "╝",
BottomSeparator: "╩", BottomSeparator: "╩",
EmptySeparator: text.RepeatAndTrim(" ", text.RuneCount("╬")), EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("╬")),
Left: "║", Left: "║",
LeftSeparator: "╠", LeftSeparator: "╠",
MiddleHorizontal: "═", MiddleHorizontal: "═",
@ -451,7 +451,7 @@ var (
BottomLeft: "└", BottomLeft: "└",
BottomRight: "┘", BottomRight: "┘",
BottomSeparator: "┴", BottomSeparator: "┴",
EmptySeparator: text.RepeatAndTrim(" ", text.RuneCount("┼")), EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("┼")),
Left: "│", Left: "│",
LeftSeparator: "├", LeftSeparator: "├",
MiddleHorizontal: "─", MiddleHorizontal: "─",
@ -482,7 +482,7 @@ var (
BottomLeft: "╰", BottomLeft: "╰",
BottomRight: "╯", BottomRight: "╯",
BottomSeparator: "┴", BottomSeparator: "┴",
EmptySeparator: text.RepeatAndTrim(" ", text.RuneCount("┼")), EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("┼")),
Left: "│", Left: "│",
LeftSeparator: "├", LeftSeparator: "├",
MiddleHorizontal: "─", MiddleHorizontal: "─",
@ -513,7 +513,7 @@ var (
BottomLeft: "\\", BottomLeft: "\\",
BottomRight: "/", BottomRight: "/",
BottomSeparator: "v", BottomSeparator: "v",
EmptySeparator: text.RepeatAndTrim(" ", text.RuneCount("+")), EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("+")),
Left: "[", Left: "[",
LeftSeparator: "{", LeftSeparator: "{",
MiddleHorizontal: "--", MiddleHorizontal: "--",
@ -533,11 +533,13 @@ var (
// ColorOptions defines the ANSI colors to use for parts of the Table. // ColorOptions defines the ANSI colors to use for parts of the Table.
type ColorOptions struct { type ColorOptions struct {
IndexColumn text.Colors // index-column colors (row #, etc.) Border text.Colors // borders (if nil, uses one of the below)
Footer text.Colors // footer row(s) colors Footer text.Colors // footer row(s) colors
Header text.Colors // header row(s) colors Header text.Colors // header row(s) colors
IndexColumn text.Colors // index-column colors (row #, etc.)
Row text.Colors // regular row(s) colors Row text.Colors // regular row(s) colors
RowAlternate text.Colors // regular row(s) colors for the even-numbered rows RowAlternate text.Colors // regular row(s) colors for the even-numbered rows
Separator text.Colors // separators (if nil, uses one of the above)
} }
var ( var (
@ -552,18 +554,18 @@ var (
// ColorOptionsBlackOnBlueWhite renders Black text on Blue/White background. // ColorOptionsBlackOnBlueWhite renders Black text on Blue/White background.
ColorOptionsBlackOnBlueWhite = ColorOptions{ ColorOptionsBlackOnBlueWhite = ColorOptions{
IndexColumn: text.Colors{text.BgHiBlue, text.FgBlack},
Footer: text.Colors{text.BgBlue, text.FgBlack}, Footer: text.Colors{text.BgBlue, text.FgBlack},
Header: text.Colors{text.BgHiBlue, text.FgBlack}, Header: text.Colors{text.BgHiBlue, text.FgBlack},
IndexColumn: text.Colors{text.BgHiBlue, text.FgBlack},
Row: text.Colors{text.BgHiWhite, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack},
RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
} }
// ColorOptionsBlackOnCyanWhite renders Black text on Cyan/White background. // ColorOptionsBlackOnCyanWhite renders Black text on Cyan/White background.
ColorOptionsBlackOnCyanWhite = ColorOptions{ ColorOptionsBlackOnCyanWhite = ColorOptions{
IndexColumn: text.Colors{text.BgHiCyan, text.FgBlack},
Footer: text.Colors{text.BgCyan, text.FgBlack}, Footer: text.Colors{text.BgCyan, text.FgBlack},
Header: text.Colors{text.BgHiCyan, text.FgBlack}, Header: text.Colors{text.BgHiCyan, text.FgBlack},
IndexColumn: text.Colors{text.BgHiCyan, text.FgBlack},
Row: text.Colors{text.BgHiWhite, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack},
RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
} }
@ -571,9 +573,9 @@ var (
// ColorOptionsBlackOnGreenWhite renders Black text on Green/White // ColorOptionsBlackOnGreenWhite renders Black text on Green/White
// background. // background.
ColorOptionsBlackOnGreenWhite = ColorOptions{ ColorOptionsBlackOnGreenWhite = ColorOptions{
IndexColumn: text.Colors{text.BgHiGreen, text.FgBlack},
Footer: text.Colors{text.BgGreen, text.FgBlack}, Footer: text.Colors{text.BgGreen, text.FgBlack},
Header: text.Colors{text.BgHiGreen, text.FgBlack}, Header: text.Colors{text.BgHiGreen, text.FgBlack},
IndexColumn: text.Colors{text.BgHiGreen, text.FgBlack},
Row: text.Colors{text.BgHiWhite, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack},
RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
} }
@ -581,18 +583,18 @@ var (
// ColorOptionsBlackOnMagentaWhite renders Black text on Magenta/White // ColorOptionsBlackOnMagentaWhite renders Black text on Magenta/White
// background. // background.
ColorOptionsBlackOnMagentaWhite = ColorOptions{ ColorOptionsBlackOnMagentaWhite = ColorOptions{
IndexColumn: text.Colors{text.BgHiMagenta, text.FgBlack},
Footer: text.Colors{text.BgMagenta, text.FgBlack}, Footer: text.Colors{text.BgMagenta, text.FgBlack},
Header: text.Colors{text.BgHiMagenta, text.FgBlack}, Header: text.Colors{text.BgHiMagenta, text.FgBlack},
IndexColumn: text.Colors{text.BgHiMagenta, text.FgBlack},
Row: text.Colors{text.BgHiWhite, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack},
RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
} }
// ColorOptionsBlackOnRedWhite renders Black text on Red/White background. // ColorOptionsBlackOnRedWhite renders Black text on Red/White background.
ColorOptionsBlackOnRedWhite = ColorOptions{ ColorOptionsBlackOnRedWhite = ColorOptions{
IndexColumn: text.Colors{text.BgHiRed, text.FgBlack},
Footer: text.Colors{text.BgRed, text.FgBlack}, Footer: text.Colors{text.BgRed, text.FgBlack},
Header: text.Colors{text.BgHiRed, text.FgBlack}, Header: text.Colors{text.BgHiRed, text.FgBlack},
IndexColumn: text.Colors{text.BgHiRed, text.FgBlack},
Row: text.Colors{text.BgHiWhite, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack},
RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
} }
@ -600,27 +602,27 @@ var (
// ColorOptionsBlackOnYellowWhite renders Black text on Yellow/White // ColorOptionsBlackOnYellowWhite renders Black text on Yellow/White
// background. // background.
ColorOptionsBlackOnYellowWhite = ColorOptions{ ColorOptionsBlackOnYellowWhite = ColorOptions{
IndexColumn: text.Colors{text.BgHiYellow, text.FgBlack},
Footer: text.Colors{text.BgYellow, text.FgBlack}, Footer: text.Colors{text.BgYellow, text.FgBlack},
Header: text.Colors{text.BgHiYellow, text.FgBlack}, Header: text.Colors{text.BgHiYellow, text.FgBlack},
IndexColumn: text.Colors{text.BgHiYellow, text.FgBlack},
Row: text.Colors{text.BgHiWhite, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack},
RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
} }
// ColorOptionsBlueWhiteOnBlack renders Blue/White text on Black background. // ColorOptionsBlueWhiteOnBlack renders Blue/White text on Black background.
ColorOptionsBlueWhiteOnBlack = ColorOptions{ ColorOptionsBlueWhiteOnBlack = ColorOptions{
IndexColumn: text.Colors{text.FgHiBlue, text.BgHiBlack},
Footer: text.Colors{text.FgBlue, text.BgHiBlack}, Footer: text.Colors{text.FgBlue, text.BgHiBlack},
Header: text.Colors{text.FgHiBlue, text.BgHiBlack}, Header: text.Colors{text.FgHiBlue, text.BgHiBlack},
IndexColumn: text.Colors{text.FgHiBlue, text.BgHiBlack},
Row: text.Colors{text.FgHiWhite, text.BgBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack},
RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
} }
// ColorOptionsCyanWhiteOnBlack renders Cyan/White text on Black background. // ColorOptionsCyanWhiteOnBlack renders Cyan/White text on Black background.
ColorOptionsCyanWhiteOnBlack = ColorOptions{ ColorOptionsCyanWhiteOnBlack = ColorOptions{
IndexColumn: text.Colors{text.FgHiCyan, text.BgHiBlack},
Footer: text.Colors{text.FgCyan, text.BgHiBlack}, Footer: text.Colors{text.FgCyan, text.BgHiBlack},
Header: text.Colors{text.FgHiCyan, text.BgHiBlack}, Header: text.Colors{text.FgHiCyan, text.BgHiBlack},
IndexColumn: text.Colors{text.FgHiCyan, text.BgHiBlack},
Row: text.Colors{text.FgHiWhite, text.BgBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack},
RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
} }
@ -628,9 +630,9 @@ var (
// ColorOptionsGreenWhiteOnBlack renders Green/White text on Black // ColorOptionsGreenWhiteOnBlack renders Green/White text on Black
// background. // background.
ColorOptionsGreenWhiteOnBlack = ColorOptions{ ColorOptionsGreenWhiteOnBlack = ColorOptions{
IndexColumn: text.Colors{text.FgHiGreen, text.BgHiBlack},
Footer: text.Colors{text.FgGreen, text.BgHiBlack}, Footer: text.Colors{text.FgGreen, text.BgHiBlack},
Header: text.Colors{text.FgHiGreen, text.BgHiBlack}, Header: text.Colors{text.FgHiGreen, text.BgHiBlack},
IndexColumn: text.Colors{text.FgHiGreen, text.BgHiBlack},
Row: text.Colors{text.FgHiWhite, text.BgBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack},
RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
} }
@ -638,18 +640,18 @@ var (
// ColorOptionsMagentaWhiteOnBlack renders Magenta/White text on Black // ColorOptionsMagentaWhiteOnBlack renders Magenta/White text on Black
// background. // background.
ColorOptionsMagentaWhiteOnBlack = ColorOptions{ ColorOptionsMagentaWhiteOnBlack = ColorOptions{
IndexColumn: text.Colors{text.FgHiMagenta, text.BgHiBlack},
Footer: text.Colors{text.FgMagenta, text.BgHiBlack}, Footer: text.Colors{text.FgMagenta, text.BgHiBlack},
Header: text.Colors{text.FgHiMagenta, text.BgHiBlack}, Header: text.Colors{text.FgHiMagenta, text.BgHiBlack},
IndexColumn: text.Colors{text.FgHiMagenta, text.BgHiBlack},
Row: text.Colors{text.FgHiWhite, text.BgBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack},
RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
} }
// ColorOptionsRedWhiteOnBlack renders Red/White text on Black background. // ColorOptionsRedWhiteOnBlack renders Red/White text on Black background.
ColorOptionsRedWhiteOnBlack = ColorOptions{ ColorOptionsRedWhiteOnBlack = ColorOptions{
IndexColumn: text.Colors{text.FgHiRed, text.BgHiBlack},
Footer: text.Colors{text.FgRed, text.BgHiBlack}, Footer: text.Colors{text.FgRed, text.BgHiBlack},
Header: text.Colors{text.FgHiRed, text.BgHiBlack}, Header: text.Colors{text.FgHiRed, text.BgHiBlack},
IndexColumn: text.Colors{text.FgHiRed, text.BgHiBlack},
Row: text.Colors{text.FgHiWhite, text.BgBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack},
RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
} }
@ -657,9 +659,9 @@ var (
// ColorOptionsYellowWhiteOnBlack renders Yellow/White text on Black // ColorOptionsYellowWhiteOnBlack renders Yellow/White text on Black
// background. // background.
ColorOptionsYellowWhiteOnBlack = ColorOptions{ ColorOptionsYellowWhiteOnBlack = ColorOptions{
IndexColumn: text.Colors{text.FgHiYellow, text.BgHiBlack},
Footer: text.Colors{text.FgYellow, text.BgHiBlack}, Footer: text.Colors{text.FgYellow, text.BgHiBlack},
Header: text.Colors{text.FgHiYellow, text.BgHiBlack}, Header: text.Colors{text.FgHiYellow, text.BgHiBlack},
IndexColumn: text.Colors{text.FgHiYellow, text.BgHiBlack},
Row: text.Colors{text.FgHiWhite, text.BgBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack},
RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
} }
@ -667,9 +669,10 @@ var (
// FormatOptions defines the text-formatting to perform on parts of the Table. // FormatOptions defines the text-formatting to perform on parts of the Table.
type FormatOptions struct { type FormatOptions struct {
Footer text.Format // footer row(s) text format Direction text.Direction // (forced) BiDi direction for each Column
Header text.Format // header row(s) text format Footer text.Format // footer row(s) text format
Row text.Format // (data) row(s) text format Header text.Format // header row(s) text format
Row text.Format // (data) row(s) text format
} }
var ( var (
@ -702,6 +705,10 @@ var (
// Options defines the global options that determine how the Table is // Options defines the global options that determine how the Table is
// rendered. // rendered.
type Options struct { type Options struct {
// DoNotColorBordersAndSeparators disables coloring all the borders and row
// or column separators.
DoNotColorBordersAndSeparators bool
// DrawBorder enables or disables drawing the border around the Table. // DrawBorder enables or disables drawing the border around the Table.
// Example of a table where it is disabled: // Example of a table where it is disabled:
// # │ FIRST NAME │ LAST NAME │ SALARY │ // # │ FIRST NAME │ LAST NAME │ SALARY │

View file

@ -296,51 +296,6 @@ func (t *Table) SuppressEmptyColumns() {
t.suppressEmptyColumns = true t.suppressEmptyColumns = true
} }
func (t *Table) analyzeAndStringify(row Row, hint renderHint) rowStr {
// update t.numColumns if this row is the longest seen till now
if len(row) > t.numColumns {
// init the slice for the first time; and pad it the rest of the time
if t.numColumns == 0 {
t.columnIsNonNumeric = make([]bool, len(row))
} else {
t.columnIsNonNumeric = append(t.columnIsNonNumeric, make([]bool, len(row)-t.numColumns)...)
}
// update t.numColumns
t.numColumns = len(row)
}
// convert each column to string and figure out if it has non-numeric data
rowOut := make(rowStr, len(row))
for colIdx, col := range row {
// if the column is not a number, keep track of it
if !hint.isHeaderRow && !hint.isFooterRow && !t.columnIsNonNumeric[colIdx] && !isNumber(col) {
t.columnIsNonNumeric[colIdx] = true
}
rowOut[colIdx] = t.analyzeAndStringifyColumn(colIdx, col, hint)
}
return rowOut
}
func (t *Table) analyzeAndStringifyColumn(colIdx int, col interface{}, hint renderHint) string {
// convert to a string and store it in the row
var colStr string
if transformer := t.getColumnTransformer(colIdx, hint); transformer != nil {
colStr = transformer(col)
} else if colStrVal, ok := col.(string); ok {
colStr = colStrVal
} else {
colStr = fmt.Sprint(col)
}
if strings.Contains(colStr, "\t") {
colStr = strings.Replace(colStr, "\t", " ", -1)
}
if strings.Contains(colStr, "\r") {
colStr = strings.Replace(colStr, "\r", "", -1)
}
return colStr
}
func (t *Table) getAlign(colIdx int, hint renderHint) text.Align { func (t *Table) getAlign(colIdx int, hint renderHint) text.Align {
align := text.AlignDefault align := text.AlignDefault
if cfg, ok := t.columnConfigMap[colIdx]; ok { if cfg, ok := t.columnConfigMap[colIdx]; ok {
@ -371,12 +326,22 @@ func (t *Table) getAutoIndexColumnIDs() rowStr {
} }
func (t *Table) getBorderColors(hint renderHint) text.Colors { func (t *Table) getBorderColors(hint renderHint) text.Colors {
if hint.isFooterRow { if t.style.Options.DoNotColorBordersAndSeparators {
return nil
} else if t.style.Color.Border != nil {
return t.style.Color.Border
} else if hint.isTitleRow {
return t.style.Title.Colors
} else if hint.isHeaderRow {
return t.style.Color.Header
} else if hint.isFooterRow {
return t.style.Color.Footer return t.style.Color.Footer
} else if t.autoIndex { } else if t.autoIndex {
return t.style.Color.IndexColumn return t.style.Color.IndexColumn
} else if hint.rowNumber%2 == 0 && t.style.Color.RowAlternate != nil {
return t.style.Color.RowAlternate
} }
return t.style.Color.Header return t.style.Color.Row
} }
func (t *Table) getBorderLeft(hint renderHint) string { func (t *Table) getBorderLeft(hint renderHint) string {
@ -422,9 +387,13 @@ func (t *Table) getBorderRight(hint renderHint) string {
} }
func (t *Table) getColumnColors(colIdx int, hint renderHint) text.Colors { func (t *Table) getColumnColors(colIdx int, hint renderHint) text.Colors {
if t.rowPainter != nil && hint.isRegularRow() && !t.isIndexColumn(colIdx, hint) { if hint.isBorderOrSeparator() {
colors := t.rowsColors[hint.rowNumber-1] if colors := t.getColumnColorsForBorderOrSeparator(colIdx, hint); colors != nil {
if colors != nil { return colors
}
}
if t.rowPainter != nil && hint.isRegularNonSeparatorRow() && !t.isIndexColumn(colIdx, hint) {
if colors := t.rowsColors[hint.rowNumber-1]; colors != nil {
return colors return colors
} }
} }
@ -441,6 +410,19 @@ func (t *Table) getColumnColors(colIdx int, hint renderHint) text.Colors {
return nil return nil
} }
func (t *Table) getColumnColorsForBorderOrSeparator(colIdx int, hint renderHint) text.Colors {
if t.style.Options.DoNotColorBordersAndSeparators {
return text.Colors{} // not nil to force caller to paint with no colors
}
if (hint.isBorderBottom || hint.isBorderTop) && t.style.Color.Border != nil {
return t.style.Color.Border
}
if hint.isSeparatorRow && t.style.Color.Separator != nil {
return t.style.Color.Separator
}
return nil
}
func (t *Table) getColumnSeparator(row rowStr, colIdx int, hint renderHint) string { func (t *Table) getColumnSeparator(row rowStr, colIdx int, hint renderHint) string {
separator := t.style.Box.MiddleVertical separator := t.style.Box.MiddleVertical
if hint.isSeparatorRow { if hint.isSeparatorRow {
@ -550,13 +532,40 @@ func (t *Table) getFormat(hint renderHint) text.Format {
func (t *Table) getMaxColumnLengthForMerging(colIdx int) int { func (t *Table) getMaxColumnLengthForMerging(colIdx int) int {
maxColumnLength := t.maxColumnLengths[colIdx] maxColumnLength := t.maxColumnLengths[colIdx]
maxColumnLength += text.RuneCount(t.style.Box.PaddingRight + t.style.Box.PaddingLeft) maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingRight + t.style.Box.PaddingLeft)
if t.style.Options.SeparateColumns { if t.style.Options.SeparateColumns {
maxColumnLength += text.RuneCount(t.style.Box.EmptySeparator) maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.EmptySeparator)
} }
return maxColumnLength return maxColumnLength
} }
// getMergedColumnIndices returns a map of colIdx values to all the other colIdx
// values (that are being merged) and their lengths.
func (t *Table) getMergedColumnIndices(row rowStr, hint renderHint) mergedColumnIndices {
if !t.getRowConfig(hint).AutoMerge {
return nil
}
mci := make(mergedColumnIndices)
for colIdx := 0; colIdx < t.numColumns-1; colIdx++ {
// look backward
for otherColIdx := colIdx - 1; colIdx >= 0 && otherColIdx >= 0; otherColIdx-- {
if row[colIdx] != row[otherColIdx] {
break
}
mci.safeAppend(colIdx, otherColIdx)
}
// look forward
for otherColIdx := colIdx + 1; colIdx < len(row) && otherColIdx < len(row); otherColIdx++ {
if row[colIdx] != row[otherColIdx] {
break
}
mci.safeAppend(colIdx, otherColIdx)
}
}
return mci
}
func (t *Table) getRow(rowIdx int, hint renderHint) rowStr { func (t *Table) getRow(rowIdx int, hint renderHint) rowStr {
switch { switch {
case hint.isHeaderRow: case hint.isHeaderRow:
@ -592,7 +601,13 @@ func (t *Table) getRowConfig(hint renderHint) RowConfig {
} }
func (t *Table) getSeparatorColors(hint renderHint) text.Colors { func (t *Table) getSeparatorColors(hint renderHint) text.Colors {
if hint.isHeaderRow { if t.style.Options.DoNotColorBordersAndSeparators {
return nil
} else if (hint.isBorderBottom || hint.isBorderTop) && t.style.Color.Border != nil {
return t.style.Color.Border
} else if t.style.Color.Separator != nil {
return t.style.Color.Separator
} else if hint.isHeaderRow {
return t.style.Color.Header return t.style.Color.Header
} else if hint.isFooterRow { } else if hint.isFooterRow {
return t.style.Color.Footer return t.style.Color.Footer
@ -627,64 +642,6 @@ func (t *Table) hasHiddenColumns() bool {
return false return false
} }
func (t *Table) initForRender() {
// pick a default style if none was set until now
t.Style()
// initialize the column configs and normalize them
t.initForRenderColumnConfigs()
// initialize and stringify all the raw rows
t.initForRenderRows()
// find the longest continuous line in each column
t.initForRenderColumnLengths()
// generate a separator row and calculate maximum row length
t.initForRenderRowSeparator()
// reset the counter for the number of lines rendered
t.numLinesRendered = 0
}
func (t *Table) initForRenderColumnConfigs() {
t.columnConfigMap = map[int]ColumnConfig{}
for _, colCfg := range t.columnConfigs {
// find the column number if none provided; this logic can work only if
// a header row is present and has a column with the given name
if colCfg.Number == 0 {
for _, row := range t.rowsHeaderRaw {
colCfg.Number = row.findColumnNumber(colCfg.Name)
if colCfg.Number > 0 {
break
}
}
}
if colCfg.Number > 0 {
t.columnConfigMap[colCfg.Number-1] = colCfg
}
}
}
func (t *Table) initForRenderColumnLengths() {
t.maxColumnLengths = make([]int, t.numColumns)
t.parseRowForMaxColumnLengths(t.rowsHeader)
t.parseRowForMaxColumnLengths(t.rows)
t.parseRowForMaxColumnLengths(t.rowsFooter)
// restrict the column lengths if any are over or under the limits
for colIdx := range t.maxColumnLengths {
maxWidth := t.getColumnWidthMax(colIdx)
if maxWidth > 0 && t.maxColumnLengths[colIdx] > maxWidth {
t.maxColumnLengths[colIdx] = maxWidth
}
minWidth := t.getColumnWidthMin(colIdx)
if minWidth > 0 && t.maxColumnLengths[colIdx] < minWidth {
t.maxColumnLengths[colIdx] = minWidth
}
}
}
func (t *Table) hideColumns() map[int]int { func (t *Table) hideColumns() map[int]int {
colIdxMap := make(map[int]int) colIdxMap := make(map[int]int)
numColumns := 0 numColumns := 0
@ -718,149 +675,10 @@ func (t *Table) hideColumns() map[int]int {
return colIdxMap return colIdxMap
} }
func (t *Table) initForRenderHideColumns() {
if !t.hasHiddenColumns() {
return
}
colIdxMap := t.hideColumns()
// re-create columnIsNonNumeric with new column indices
columnIsNonNumeric := make([]bool, t.numColumns)
for oldColIdx, nonNumeric := range t.columnIsNonNumeric {
if newColIdx, ok := colIdxMap[oldColIdx]; ok {
columnIsNonNumeric[newColIdx] = nonNumeric
}
}
t.columnIsNonNumeric = columnIsNonNumeric
// re-create columnConfigMap with new column indices
columnConfigMap := make(map[int]ColumnConfig)
for oldColIdx, cc := range t.columnConfigMap {
if newColIdx, ok := colIdxMap[oldColIdx]; ok {
columnConfigMap[newColIdx] = cc
}
}
t.columnConfigMap = columnConfigMap
}
func (t *Table) initForRenderRows() {
t.reset()
// auto-index: calc the index column's max length
t.autoIndexVIndexMaxLength = len(fmt.Sprint(len(t.rowsRaw)))
// stringify all the rows to make it easy to render
if t.rowPainter != nil {
t.rowsColors = make([]text.Colors, len(t.rowsRaw))
}
t.rows = t.initForRenderRowsStringify(t.rowsRaw, renderHint{})
t.rowsFooter = t.initForRenderRowsStringify(t.rowsFooterRaw, renderHint{isFooterRow: true})
t.rowsHeader = t.initForRenderRowsStringify(t.rowsHeaderRaw, renderHint{isHeaderRow: true})
// sort the rows as requested
t.initForRenderSortRows()
// suppress columns without any content
t.initForRenderSuppressColumns()
// strip out hidden columns
t.initForRenderHideColumns()
}
func (t *Table) initForRenderRowsStringify(rows []Row, hint renderHint) []rowStr {
rowsStr := make([]rowStr, len(rows))
for idx, row := range rows {
if t.rowPainter != nil && hint.isRegularRow() {
t.rowsColors[idx] = t.rowPainter(row)
}
rowsStr[idx] = t.analyzeAndStringify(row, hint)
}
return rowsStr
}
func (t *Table) initForRenderRowSeparator() {
t.maxRowLength = 0
if t.autoIndex {
t.maxRowLength += text.RuneCount(t.style.Box.PaddingLeft)
t.maxRowLength += len(fmt.Sprint(len(t.rows)))
t.maxRowLength += text.RuneCount(t.style.Box.PaddingRight)
if t.style.Options.SeparateColumns {
t.maxRowLength += text.RuneCount(t.style.Box.MiddleSeparator)
}
}
if t.style.Options.SeparateColumns {
t.maxRowLength += text.RuneCount(t.style.Box.MiddleSeparator) * (t.numColumns - 1)
}
t.rowSeparator = make(rowStr, t.numColumns)
for colIdx, maxColumnLength := range t.maxColumnLengths {
maxColumnLength += text.RuneCount(t.style.Box.PaddingLeft + t.style.Box.PaddingRight)
t.maxRowLength += maxColumnLength
t.rowSeparator[colIdx] = text.RepeatAndTrim(t.style.Box.MiddleHorizontal, maxColumnLength)
}
if t.style.Options.DrawBorder {
t.maxRowLength += text.RuneCount(t.style.Box.Left + t.style.Box.Right)
}
}
func (t *Table) initForRenderSortRows() {
if len(t.sortBy) == 0 {
return
}
// sort the rows
sortedRowIndices := t.getSortedRowIndices()
sortedRows := make([]rowStr, len(t.rows))
for idx := range t.rows {
sortedRows[idx] = t.rows[sortedRowIndices[idx]]
}
t.rows = sortedRows
// sort the rowsColors
if len(t.rowsColors) > 0 {
sortedRowsColors := make([]text.Colors, len(t.rows))
for idx := range t.rows {
sortedRowsColors[idx] = t.rowsColors[sortedRowIndices[idx]]
}
t.rowsColors = sortedRowsColors
}
}
func (t *Table) initForRenderSuppressColumns() {
shouldSuppressColumn := func(colIdx int) bool {
for _, row := range t.rows {
if colIdx < len(row) && row[colIdx] != "" {
return false
}
}
return true
}
if t.suppressEmptyColumns {
for colIdx := 0; colIdx < t.numColumns; colIdx++ {
if shouldSuppressColumn(colIdx) {
cc := t.columnConfigMap[colIdx]
cc.Hidden = true
t.columnConfigMap[colIdx] = cc
}
}
}
}
func (t *Table) isIndexColumn(colIdx int, hint renderHint) bool { func (t *Table) isIndexColumn(colIdx int, hint renderHint) bool {
return t.indexColumn == colIdx+1 || hint.isAutoIndexColumn return t.indexColumn == colIdx+1 || hint.isAutoIndexColumn
} }
func (t *Table) parseRowForMaxColumnLengths(rows []rowStr) {
for _, row := range rows {
for colIdx, colStr := range row {
longestLineLen := text.LongestLineLen(colStr)
if longestLineLen > t.maxColumnLengths[colIdx] {
t.maxColumnLengths[colIdx] = longestLineLen
}
}
}
}
func (t *Table) render(out *strings.Builder) string { func (t *Table) render(out *strings.Builder) string {
outStr := out.String() outStr := out.String()
if t.outputMirror != nil && len(outStr) > 0 { if t.outputMirror != nil && len(outStr) > 0 {
@ -870,19 +688,6 @@ func (t *Table) render(out *strings.Builder) string {
return outStr return outStr
} }
func (t *Table) reset() {
t.autoIndexVIndexMaxLength = 0
t.columnIsNonNumeric = nil
t.maxColumnLengths = nil
t.maxRowLength = 0
t.numColumns = 0
t.rowsColors = nil
t.rowSeparator = nil
t.rows = nil
t.rowsFooter = nil
t.rowsHeader = nil
}
func (t *Table) shouldMergeCellsHorizontallyAbove(row rowStr, colIdx int, hint renderHint) bool { func (t *Table) shouldMergeCellsHorizontallyAbove(row rowStr, colIdx int, hint renderHint) bool {
if hint.isAutoIndexColumn || hint.isAutoIndexRow { if hint.isAutoIndexColumn || hint.isAutoIndexRow {
return false return false
@ -922,6 +727,9 @@ func (t *Table) shouldMergeCellsHorizontallyBelow(row rowStr, colIdx int, hint r
} else if hint.isHeaderRow && hint.isLastRow { } else if hint.isHeaderRow && hint.isLastRow {
rowConfig = t.getRowConfig(renderHint{rowNumber: 1}) rowConfig = t.getRowConfig(renderHint{rowNumber: 1})
row = t.getRow(0, renderHint{}) row = t.getRow(0, renderHint{})
} else if hint.isHeaderRow {
rowConfig = t.getRowConfig(renderHint{isHeaderRow: true, rowNumber: hint.rowNumber + 1})
row = t.getRow(hint.rowNumber, hint)
} else if hint.isFooterRow && hint.rowNumber >= 0 { } else if hint.isFooterRow && hint.rowNumber >= 0 {
rowConfig = t.getRowConfig(renderHint{isFooterRow: true, rowNumber: 1}) rowConfig = t.getRowConfig(renderHint{isFooterRow: true, rowNumber: 1})
row = t.getRow(hint.rowNumber, renderHint{isFooterRow: true}) row = t.getRow(hint.rowNumber, renderHint{isFooterRow: true})
@ -961,7 +769,11 @@ func (t *Table) wrapRow(row rowStr) (int, rowStr) {
rowWrapped := make(rowStr, len(row)) rowWrapped := make(rowStr, len(row))
for colIdx, colStr := range row { for colIdx, colStr := range row {
widthEnforcer := t.columnConfigMap[colIdx].getWidthMaxEnforcer() widthEnforcer := t.columnConfigMap[colIdx].getWidthMaxEnforcer()
rowWrapped[colIdx] = widthEnforcer(colStr, t.maxColumnLengths[colIdx]) maxWidth := t.getColumnWidthMax(colIdx)
if maxWidth == 0 {
maxWidth = t.maxColumnLengths[colIdx]
}
rowWrapped[colIdx] = widthEnforcer(colStr, maxWidth)
colNumLines := strings.Count(rowWrapped[colIdx], "\n") + 1 colNumLines := strings.Count(rowWrapped[colIdx], "\n") + 1
if colNumLines > colMaxLines { if colNumLines > colMaxLines {
colMaxLines = colNumLines colMaxLines = colNumLines
@ -969,32 +781,3 @@ func (t *Table) wrapRow(row rowStr) (int, rowStr) {
} }
return colMaxLines, rowWrapped return colMaxLines, rowWrapped
} }
// renderHint has hints for the Render*() logic
type renderHint struct {
isAutoIndexColumn bool // auto-index column?
isAutoIndexRow bool // auto-index row?
isBorderBottom bool // bottom-border?
isBorderTop bool // top-border?
isFirstRow bool // first-row of header/footer/regular-rows?
isFooterRow bool // footer row?
isHeaderRow bool // header row?
isLastLineOfRow bool // last-line of the current row?
isLastRow bool // last-row of header/footer/regular-rows?
isSeparatorRow bool // separator row?
rowLineNumber int // the line number for a multi-line row
rowNumber int // the row number/index
}
func (h *renderHint) isRegularRow() bool {
return !h.isHeaderRow && !h.isFooterRow
}
func (h *renderHint) isHeaderOrFooterSeparator() bool {
return h.isSeparatorRow && !h.isBorderBottom && !h.isBorderTop &&
((h.isHeaderRow && !h.isLastRow) || (h.isFooterRow && (!h.isFirstRow || h.rowNumber > 0)))
}
func (h *renderHint) isLastLineOfLastRow() bool {
return h.isLastLineOfRow && h.isLastRow
}

View file

@ -17,6 +17,14 @@ func AutoIndexColumnID(colIdx int) string {
return out return out
} }
// WidthEnforcer is a function that helps enforce a width condition on a string.
type WidthEnforcer func(col string, maxLen int) string
// widthEnforcerNone returns the input string as is without any modifications.
func widthEnforcerNone(col string, maxLen int) string {
return col
}
// isNumber returns true if the argument is a numeric type; false otherwise. // isNumber returns true if the argument is a numeric type; false otherwise.
func isNumber(x interface{}) bool { func isNumber(x interface{}) bool {
if x == nil { if x == nil {
@ -32,10 +40,30 @@ func isNumber(x interface{}) bool {
return false return false
} }
// WidthEnforcer is a function that helps enforce a width condition on a string. type mergedColumnIndices map[int]map[int]bool
type WidthEnforcer func(col string, maxLen int) string
// widthEnforcerNone returns the input string as is without any modifications. func (m mergedColumnIndices) mergedLength(colIdx int, maxColumnLengths []int) int {
func widthEnforcerNone(col string, maxLen int) string { mergedLength := maxColumnLengths[colIdx]
return col for otherColIdx := range m[colIdx] {
mergedLength += maxColumnLengths[otherColIdx]
}
return mergedLength
}
func (m mergedColumnIndices) len(colIdx int) int {
return len(m[colIdx]) + 1
}
func (m mergedColumnIndices) safeAppend(colIdx, otherColIdx int) {
// map
if m[colIdx] == nil {
m[colIdx] = make(map[int]bool)
}
m[colIdx][otherColIdx] = true
// reverse map
if m[otherColIdx] == nil {
m[otherColIdx] = make(map[int]bool)
}
m[otherColIdx][colIdx] = true
} }

Some files were not shown because too many files have changed in this diff Show more