diff --git a/CHANGELOG.md b/CHANGELOG.md index 0effcf58..73e3db9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), and is generated by [Changie](https://github.com/miniscruff/changie). +## v1.0.2 - 2024-01-18 + +### ⬆️ Dependencies + +- Bump dependencies. + Migrate to public maintained jwt repo since original was deleted from github. + ## v1.0.1 - 2023-08-09 ### 📘 Docs diff --git a/charts/dsv-k8s-controller/Chart.yaml b/charts/dsv-k8s-controller/Chart.yaml index 4641a880..8f0c6425 100644 --- a/charts/dsv-k8s-controller/Chart.yaml +++ b/charts/dsv-k8s-controller/Chart.yaml @@ -30,7 +30,7 @@ keywords: - vault type: application # WARNING: Version should be bumped by changie, not manually. -version: v1.0.1 +version: v1.0.2 appVersion: latest maintainers: - name: Delinea DSV Team diff --git a/charts/dsv-k8s-sidecar/Chart.yaml b/charts/dsv-k8s-sidecar/Chart.yaml index 05e628fd..7a5e0dfe 100644 --- a/charts/dsv-k8s-sidecar/Chart.yaml +++ b/charts/dsv-k8s-sidecar/Chart.yaml @@ -29,7 +29,7 @@ keywords: - vault type: application # WARNING: Version should be bumped by changie, not manually. -version: v1.0.1 +version: v1.0.2 appVersion: latest maintainers: - name: Delinea DSV Team diff --git a/go.mod b/go.mod index fc3fc6f1..48d3e78a 100644 --- a/go.mod +++ b/go.mod @@ -1,47 +1,46 @@ module github.com/DelineaXPM/dsv-k8s-sidecar -go 1.18 +go 1.21 require ( - github.com/bitfield/script v0.21.4 - github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 + github.com/bitfield/script v0.22.0 github.com/ericchiang/k8s v1.2.0 + github.com/golang-jwt/jwt/v5 v5.2.0 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.2.0 - github.com/gorilla/mux v1.8.0 - github.com/magefile/mage v1.14.0 - github.com/pterm/pterm v0.12.58 - github.com/sheldonhull/magetools v1.0.0 - github.com/sirupsen/logrus v1.2.0 - github.com/stretchr/testify v1.8.2 + github.com/gorilla/mux v1.8.1 + github.com/magefile/mage v1.15.0 + github.com/pterm/pterm v0.12.75 + github.com/sheldonhull/magetools v1.0.1 + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.8.4 google.golang.org/grpc v1.16.0 ) require ( - atomicgo.dev/cursor v0.1.1 // indirect + atomicgo.dev/schedule v0.1.0 // indirect + mvdan.cc/sh/v3 v3.7.0 // indirect +) + +require ( + atomicgo.dev/cursor v0.2.0 // indirect atomicgo.dev/keyboard v0.2.9 // indirect - bitbucket.org/creachadair/shell v0.0.7 // indirect github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/gookit/color v1.5.3 // indirect - github.com/itchyny/gojq v0.12.7 // indirect - github.com/itchyny/timefmt-go v0.1.3 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect - github.com/kr/pretty v0.2.1 // indirect - github.com/lithammer/fuzzysearch v1.1.5 // indirect - github.com/logrusorgru/aurora v2.0.3+incompatible // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gookit/color v1.5.4 // indirect + github.com/itchyny/gojq v0.12.14 // indirect + github.com/itchyny/timefmt-go v0.1.5 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - github.com/ztrue/tracerr v0.3.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.17.0 // indirect; indirect // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + github.com/ztrue/tracerr v0.4.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.20.0 // indirect; indirect // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index a32ad8dd..60af5dc6 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,11 @@ atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= -atomicgo.dev/cursor v0.1.1 h1:0t9sxQomCTRh5ug+hAMCs59x/UmC9QL6Ci5uosINKD4= -atomicgo.dev/cursor v0.1.1/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= -bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk= -bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= @@ -14,21 +15,27 @@ github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzX github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= -github.com/bitfield/script v0.21.4 h1:XPMD/ti7pa9KW1aPMq7Hfh+mVznQdlqxkbiZSM2lnbE= -github.com/bitfield/script v0.21.4/go.mod h1:l3AZPVAtKQrL03bwh7nlNTUtgrgSWurpJSbtqspYrOA= +github.com/bitfield/script v0.22.0 h1:LA7QHuEsXMPD52YLtxWrlqCCy+9FOpzNYfsRHC5Gsrc= +github.com/bitfield/script v0.22.0/go.mod h1:ms4w+9B8f2/W0mbsgWDVTtl7K94bYuZc3AunnJC4Ebs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU= -github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ericchiang/k8s v1.2.0 h1:vxrMwEzY43oxu8aZyD/7b1s8tsBM+xoUoxjWECWFbPI= github.com/ericchiang/k8s v1.2.0/go.mod h1:/OmBgSq2cd9IANnsGHGlEz27nwMZV2YxlpXuQtU3Bz4= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= +github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= @@ -38,45 +45,45 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= -github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ= -github.com/itchyny/gojq v0.12.7/go.mod h1:ZdvNHVlzPgUf8pgjnuDTmGfHA/21KoutQUJ3An/xNuw= -github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= -github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/itchyny/gojq v0.12.12/go.mod h1:j+3sVkjxwd7A7Z5jrbKibgOLn0ZfLWkV+Awxr/pyzJE= +github.com/itchyny/gojq v0.12.14 h1:6k8vVtsrhQSYgSGg827AD+PVVaB1NLXEdX+dda2oZCc= +github.com/itchyny/gojq v0.12.14/go.mod h1:y1G7oO7XkcR1LPZO59KyoCRy08T3j9vDYRV0GgYSS+s= +github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= +github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= -github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= -github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= -github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= -github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= +github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= +github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -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-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= @@ -86,59 +93,63 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= -github.com/pterm/pterm v0.12.58 h1:MEImvkbvty8JvoJH64bJ+CvoCkcuRw2iBIJvRwAEgHI= -github.com/pterm/pterm v0.12.58/go.mod h1:Ro9CV954hiaxt3mcpDx4a8XF5EmRDlIIpPdlfCKF9fE= +github.com/pterm/pterm v0.12.75 h1:sRoDOqowp0lOr2SBREsxLRzLOUmwBWfyOflsGnVIIbo= +github.com/pterm/pterm v0.12.75/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo= 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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY= +github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sheldonhull/magetools v1.0.0 h1:IkGv46ZShJvVMfzkgdFWW6VXigoKkzlVSN3Ir1OU8ZU= -github.com/sheldonhull/magetools v1.0.0/go.mod h1:2NgMhFb1nyBhCiP4lljJ54osHHChyxyi4FBO/dWGA28= -github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sheldonhull/magetools v1.0.1 h1:EzTRk0JUNl2MijssyrA2Cx+J/7SyjcTgXQmsMccDpaI= +github.com/sheldonhull/magetools v1.0.1/go.mod h1:K5W5pCBkaBlDqnheO2mSWZ8s9t8IOsKehxvj70g8kpg= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/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.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/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/ztrue/tracerr v0.3.0 h1:lDi6EgEYhPYPnKcjsYzmWw4EkFEoA/gfe+I9Y5f+h6Y= -github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/ztrue/tracerr v0.4.0 h1:vT5PFxwIGs7rCg9ZgJ/y0NmOpJkPCPFK8x0vVIYzd04= +github.com/ztrue/tracerr v0.4.0/go.mod h1:PaFfYlas0DfmXNpo7Eay4MFhZUONqvXM+T2HyGPpngk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -146,31 +157,42 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= @@ -187,3 +209,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0= +mvdan.cc/sh/v3 v3.6.0/go.mod h1:U4mhtBLZ32iWhif5/lD+ygy1zrgaQhUu+XFy7C8+TTA= +mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= +mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= diff --git a/pkg/auth/service.go b/pkg/auth/service.go index 25df5bca..336af93f 100644 --- a/pkg/auth/service.go +++ b/pkg/auth/service.go @@ -7,7 +7,7 @@ import ( "github.com/DelineaXPM/dsv-k8s-sidecar/pkg/pods" - "github.com/dgrijalva/jwt-go/v4" + "github.com/golang-jwt/jwt/v5" "github.com/sirupsen/logrus" "google.golang.org/grpc" "google.golang.org/grpc/codes" diff --git a/vendor/atomicgo.dev/cursor/.golangci.yml b/vendor/atomicgo.dev/cursor/.golangci.yml index d18a485d..796ca35c 100644 --- a/vendor/atomicgo.dev/cursor/.golangci.yml +++ b/vendor/atomicgo.dev/cursor/.golangci.yml @@ -17,55 +17,83 @@ linters-settings: - ptrToRefParam - paramTypeCombine - unnamedResult - misspell: - locale: US linters: disable-all: true enable: + # default linters - errcheck - gosimple - govet - ineffassign - staticcheck + - typecheck + - unused + # additional linters + - asasalint - asciicheck + - bidichk - bodyclose + - containedctx + - contextcheck + - decorder - dupl - durationcheck + - errchkjson + - errname - errorlint - exhaustive - - gci - - gocognit + - exhaustruct + - exportloopref + - forcetypeassert + - gocheckcompilerdirectives - gocritic - godot - godox - goerr113 - gofmt - - goimports - goprintffuncname - - misspell + - gosec + - gosmopolitan + - importas + - ireturn + - nakedret + - nestif - nilerr - - nlreturn - - noctx + - nilnil - prealloc - predeclared + - revive + - rowserrcheck + - tagalign + - tenv - thelper + - tparallel - unconvert - unparam + - usestdlibvars - wastedassign + - whitespace - wrapcheck + - wsl + - gocyclo + - misspell issues: - # Excluding configuration per-path, per-linter, per-text and per-source + include: + - EXC0012 + - EXC0014 exclude-rules: - path: _test\.go linters: + - gocyclo - errcheck - dupl + - gosec - gocritic - - wrapcheck - - goerr113 - # https://github.com/go-critic/go-critic/issues/926 - linters: - gocritic text: "unnecessaryDefer:" + - linters: + - gocritic + text: "preferDecodeRune:" service: - golangci-lint-version: 1.39.x # use the fixed version to not introduce new linters unexpectedly + golangci-lint-version: 1.53.x diff --git a/vendor/atomicgo.dev/cursor/README.md b/vendor/atomicgo.dev/cursor/README.md index c9d6d60d..0f799745 100644 --- a/vendor/atomicgo.dev/cursor/README.md +++ b/vendor/atomicgo.dev/cursor/README.md @@ -1,13 +1,14 @@

AtomicGo | cursor

+Downloads Latest Release -Tests +Tests @@ -18,21 +19,19 @@ Unit test count - -Issues - - License: MIT + + +Go report +

---

-Get The Module -| Documentation | Contributing @@ -49,11 +48,6 @@

-
-
- ----------------------------------------------------------------------------------------------------- - -

@@ -61,214 +55,542 @@

-
-
- ----------------------------------------------------------------------------------------------------- - -

-## Description + -Package cursor contains cross-platform methods to move the terminal cursor in -different directions. This package can be used to create interactive CLI tools -and games, live charts, algorithm visualizations and other updatable output of -any kind. + -Works niceley with https://github.com/atomicgo/keyboard +# cursor -Special thanks to github.com/k0kubun/go-ansi which this project is based on. +```go +import "atomicgo.dev/cursor" +``` +Package cursor contains cross\-platform methods to move the terminal cursor in different directions. This package can be used to create interactive CLI tools and games, live charts, algorithm visualizations and other updatable output of any kind. -## Usage +Works niceley with https://github.com/atomicgo/keyboard -#### func Bottom +Special thanks to github.com/k0kubun/go\-ansi which this project is based on. + +## Index + +- [func Bottom\(\)](<#Bottom>) +- [func Clear\(\)](<#Clear>) +- [func ClearLine\(\)](<#ClearLine>) +- [func ClearLinesDown\(n int\)](<#ClearLinesDown>) +- [func ClearLinesUp\(n int\)](<#ClearLinesUp>) +- [func Down\(n int\)](<#Down>) +- [func DownAndClear\(n int\)](<#DownAndClear>) +- [func Hide\(\)](<#Hide>) +- [func HorizontalAbsolute\(n int\)](<#HorizontalAbsolute>) +- [func Left\(n int\)](<#Left>) +- [func Move\(x, y int\)](<#Move>) +- [func Right\(n int\)](<#Right>) +- [func SetTarget\(w Writer\)](<#SetTarget>) +- [func Show\(\)](<#Show>) +- [func StartOfLine\(\)](<#StartOfLine>) +- [func StartOfLineDown\(n int\)](<#StartOfLineDown>) +- [func StartOfLineUp\(n int\)](<#StartOfLineUp>) +- [func TestCustomIOWriter\(t \*testing.T\)](<#TestCustomIOWriter>) +- [func Up\(n int\)](<#Up>) +- [func UpAndClear\(n int\)](<#UpAndClear>) +- [type Area](<#Area>) + - [func NewArea\(\) Area](<#NewArea>) + - [func \(area \*Area\) Bottom\(\)](<#Area.Bottom>) + - [func \(area \*Area\) Clear\(\)](<#Area.Clear>) + - [func \(area \*Area\) ClearLinesDown\(n int\)](<#Area.ClearLinesDown>) + - [func \(area \*Area\) ClearLinesUp\(n int\)](<#Area.ClearLinesUp>) + - [func \(area \*Area\) Down\(n int\)](<#Area.Down>) + - [func \(area \*Area\) DownAndClear\(n int\)](<#Area.DownAndClear>) + - [func \(area \*Area\) Move\(x, y int\)](<#Area.Move>) + - [func \(area \*Area\) StartOfLine\(\)](<#Area.StartOfLine>) + - [func \(area \*Area\) StartOfLineDown\(n int\)](<#Area.StartOfLineDown>) + - [func \(area \*Area\) StartOfLineUp\(n int\)](<#Area.StartOfLineUp>) + - [func \(area \*Area\) Top\(\)](<#Area.Top>) + - [func \(area \*Area\) Up\(n int\)](<#Area.Up>) + - [func \(area \*Area\) UpAndClear\(n int\)](<#Area.UpAndClear>) + - [func \(area \*Area\) Update\(content string\)](<#Area.Update>) + - [func \(area Area\) WithWriter\(writer Writer\) Area](<#Area.WithWriter>) +- [type Cursor](<#Cursor>) + - [func NewCursor\(\) \*Cursor](<#NewCursor>) + - [func \(c \*Cursor\) Clear\(\)](<#Cursor.Clear>) + - [func \(c \*Cursor\) ClearLine\(\)](<#Cursor.ClearLine>) + - [func \(c \*Cursor\) Down\(n int\)](<#Cursor.Down>) + - [func \(c \*Cursor\) Hide\(\)](<#Cursor.Hide>) + - [func \(c \*Cursor\) HorizontalAbsolute\(n int\)](<#Cursor.HorizontalAbsolute>) + - [func \(c \*Cursor\) Left\(n int\)](<#Cursor.Left>) + - [func \(c \*Cursor\) Right\(n int\)](<#Cursor.Right>) + - [func \(c \*Cursor\) Show\(\)](<#Cursor.Show>) + - [func \(c \*Cursor\) Up\(n int\)](<#Cursor.Up>) + - [func \(c \*Cursor\) WithWriter\(w Writer\) \*Cursor](<#Cursor.WithWriter>) +- [type Writer](<#Writer>) + + + +## func [Bottom]() ```go func Bottom() ``` -Bottom moves the cursor to the bottom of the terminal. This is done by -calculating how many lines were moved by Up and Down. -#### func ClearLine +Bottom moves the cursor to the bottom of the terminal. This is done by calculating how many lines were moved by Up and Down. + + +## func [Clear]() + +```go +func Clear() +``` + +Clear clears the current position and moves the cursor to the left. + + +## func [ClearLine]() ```go func ClearLine() ``` + ClearLine clears the current line and moves the cursor to it's start position. -#### func ClearLinesDown + +## func [ClearLinesDown]() ```go func ClearLinesDown(n int) ``` -ClearLinesDown clears n lines downwards from the current position and moves the -cursor. -#### func ClearLinesUp +ClearLinesDown clears n lines downwards from the current position and moves the cursor. + + +## func [ClearLinesUp]() ```go func ClearLinesUp(n int) ``` -ClearLinesUp clears n lines upwards from the current position and moves the -cursor. -#### func Down +ClearLinesUp clears n lines upwards from the current position and moves the cursor. + + +## func [Down]() ```go func Down(n int) ``` + Down moves the cursor n lines down relative to the current position. -#### func DownAndClear + +## func [DownAndClear]() ```go func DownAndClear(n int) ``` + DownAndClear moves the cursor down by n lines, then clears the line. -#### func Hide + +## func [Hide]() ```go func Hide() ``` -Hide the cursor. Don't forget to show the cursor at least at the end of your -application with Show. Otherwise the user might have a terminal with a -permanently hidden cursor, until he reopens the terminal. -#### func HorizontalAbsolute +Hide the cursor. Don't forget to show the cursor at least at the end of your application with Show. Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. + + +## func [HorizontalAbsolute]() ```go func HorizontalAbsolute(n int) ``` -HorizontalAbsolute moves the cursor to n horizontally. The position n is -absolute to the start of the line. -#### func Left +HorizontalAbsolute moves the cursor to n horizontally. The position n is absolute to the start of the line. + + +## func [Left]() ```go func Left(n int) ``` + Left moves the cursor n characters to the left relative to the current position. -#### func Move + +## func [Move]() ```go func Move(x, y int) ``` + Move moves the cursor relative by x and y. -#### func Right + +## func [Right]() ```go func Right(n int) ``` -Right moves the cursor n characters to the right relative to the current -position. -#### func SetTarget +Right moves the cursor n characters to the right relative to the current position. + + +## func [SetTarget]() ```go func SetTarget(w Writer) ``` -SetTarget allows for any arbitrary Writer to be used -#### func Show +SetTarget sets to output target of the default curser to the provided cursor.Writer \(wrapping io.Writer\). + + +## func [Show]() ```go func Show() ``` -Show the cursor if it was hidden previously. Don't forget to show the cursor at -least at the end of your application. Otherwise the user might have a terminal -with a permanently hidden cursor, until he reopens the terminal. -#### func StartOfLine +Show the cursor if it was hidden previously. Don't forget to show the cursor at least at the end of your application. Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. + + +## func [StartOfLine]() ```go func StartOfLine() ``` + StartOfLine moves the cursor to the start of the current line. -#### func StartOfLineDown + +## func [StartOfLineDown]() ```go func StartOfLineDown(n int) ``` -StartOfLineDown moves the cursor down by n lines, then moves to cursor to the -start of the line. -#### func StartOfLineUp +StartOfLineDown moves the cursor down by n lines, then moves to cursor to the start of the line. + + +## func [StartOfLineUp]() ```go func StartOfLineUp(n int) ``` -StartOfLineUp moves the cursor up by n lines, then moves to cursor to the start -of the line. -#### func TestCustomIOWriter +StartOfLineUp moves the cursor up by n lines, then moves to cursor to the start of the line. + + +## func [TestCustomIOWriter]() ```go func TestCustomIOWriter(t *testing.T) ``` -#### func Up +TestCustomIOWriter tests the cursor functions with a custom Writer. + + +## func [Up]() ```go func Up(n int) ``` + Up moves the cursor n lines up relative to the current position. -#### func UpAndClear + +## func [UpAndClear]() ```go func UpAndClear(n int) ``` + UpAndClear moves the cursor up by n lines, then clears the line. -#### type Area + +## type [Area]() + +Area displays content which can be updated on the fly. You can use this to create live output, charts, dropdowns, etc. ```go type Area struct { + // contains filtered or unexported fields } ``` -Area displays content which can be updated on the fly. You can use this to -create live output, charts, dropdowns, etc. - -#### func NewArea + +### func [NewArea]() ```go func NewArea() Area ``` + NewArea returns a new Area. -#### func (*Area) Clear + +### func \(\*Area\) [Bottom]() + +```go +func (area *Area) Bottom() +``` + +Bottom moves the cursor to the bottom of the terminal. This is done by calculating how many lines were moved by Up and Down. + + +### func \(\*Area\) [Clear]() ```go func (area *Area) Clear() ``` + Clear clears the content of the Area. -#### func (*Area) Update + +### func \(\*Area\) [ClearLinesDown]() + +```go +func (area *Area) ClearLinesDown(n int) +``` + +ClearLinesDown clears n lines downwards from the current position and moves the cursor. + + +### func \(\*Area\) [ClearLinesUp]() + +```go +func (area *Area) ClearLinesUp(n int) +``` + +ClearLinesUp clears n lines upwards from the current position and moves the cursor. + + +### func \(\*Area\) [Down]() + +```go +func (area *Area) Down(n int) +``` + +Down moves the cursor of the area down one line. + + +### func \(\*Area\) [DownAndClear]() + +```go +func (area *Area) DownAndClear(n int) +``` + +DownAndClear moves the cursor down by n lines, then clears the line. + + +### func \(\*Area\) [Move]() + +```go +func (area *Area) Move(x, y int) +``` + +Move moves the cursor relative by x and y. + + +### func \(\*Area\) [StartOfLine]() + +```go +func (area *Area) StartOfLine() +``` + +StartOfLine moves the cursor to the start of the current line. + + +### func \(\*Area\) [StartOfLineDown]() + +```go +func (area *Area) StartOfLineDown(n int) +``` + +StartOfLineDown moves the cursor down by n lines, then moves to cursor to the start of the line. + + +### func \(\*Area\) [StartOfLineUp]() + +```go +func (area *Area) StartOfLineUp(n int) +``` + +StartOfLineUp moves the cursor up by n lines, then moves to cursor to the start of the line. + + +### func \(\*Area\) [Top]() + +```go +func (area *Area) Top() +``` + +Top moves the cursor to the top of the area. This is done by calculating how many lines were moved by Up and Down. + + +### func \(\*Area\) [Up]() + +```go +func (area *Area) Up(n int) +``` + +Up moves the cursor of the area up one line. + + +### func \(\*Area\) [UpAndClear]() + +```go +func (area *Area) UpAndClear(n int) +``` + +UpAndClear moves the cursor up by n lines, then clears the line. + + +### func \(\*Area\) [Update]() ```go func (area *Area) Update(content string) ``` -Update overwrites the content of the Area. -#### type Writer +Update overwrites the content of the Area and adjusts its height based on content. + + +### func \(Area\) [WithWriter]() ```go -type Writer interface { - io.Writer - Fd() uintptr +func (area Area) WithWriter(writer Writer) Area +``` + +WithWriter sets the custom writer. + + +## type [Cursor]() + +Cursor displays content which can be updated on the fly. You can use this to create live output, charts, dropdowns, etc. + +```go +type Cursor struct { + // contains filtered or unexported fields } ``` + +### func [NewCursor]() + +```go +func NewCursor() *Cursor +``` + +NewCursor creates a new Cursor instance writing to os.Stdout. + + +### func \(\*Cursor\) [Clear]() + +```go +func (c *Cursor) Clear() +``` + +Clear clears the current position and moves the cursor to the left. + + +### func \(\*Cursor\) [ClearLine]() + +```go +func (c *Cursor) ClearLine() +``` + +ClearLine clears the current line and moves the cursor to it's start position. + + +### func \(\*Cursor\) [Down]() + +```go +func (c *Cursor) Down(n int) +``` + +Down moves the cursor n lines down relative to the current position. + + +### func \(\*Cursor\) [Hide]() + +```go +func (c *Cursor) Hide() +``` + +Hide the cursor. Don't forget to show the cursor at least at the end of your application with Show. Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. + + +### func \(\*Cursor\) [HorizontalAbsolute]() + +```go +func (c *Cursor) HorizontalAbsolute(n int) +``` + +HorizontalAbsolute moves the cursor to n horizontally. The position n is absolute to the start of the line. + + +### func \(\*Cursor\) [Left]() + +```go +func (c *Cursor) Left(n int) +``` + +Left moves the cursor n characters to the left relative to the current position. + + +### func \(\*Cursor\) [Right]() + +```go +func (c *Cursor) Right(n int) +``` + +Right moves the cursor n characters to the right relative to the current position. + + +### func \(\*Cursor\) [Show]() + +```go +func (c *Cursor) Show() +``` + +Show the cursor if it was hidden previously. Don't forget to show the cursor at least at the end of your application. Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. + + +### func \(\*Cursor\) [Up]() + +```go +func (c *Cursor) Up(n int) +``` + +Up moves the cursor n lines up relative to the current position. + + +### func \(\*Cursor\) [WithWriter]() + +```go +func (c *Cursor) WithWriter(w Writer) *Cursor +``` + +WithWriter allows for any arbitrary Writer to be used for cursor movement abstracted. + + +## type [Writer]() + Writer is an expanded io.Writer interface with a file descriptor. +```go +type Writer interface { + io.Writer + Fd() uintptr +} +``` + +Generated by [gomarkdoc]() + + + + --- > [AtomicGo.dev](https://atomicgo.dev)  ·  diff --git a/vendor/atomicgo.dev/cursor/area.go b/vendor/atomicgo.dev/cursor/area.go index e8cd72d5..5b26b5db 100644 --- a/vendor/atomicgo.dev/cursor/area.go +++ b/vendor/atomicgo.dev/cursor/area.go @@ -1,49 +1,164 @@ package cursor import ( - "fmt" - "runtime" + "os" "strings" ) // Area displays content which can be updated on the fly. // You can use this to create live output, charts, dropdowns, etc. type Area struct { - height int + height int + writer Writer + cursor *Cursor + cursorPosY int } // NewArea returns a new Area. func NewArea() Area { - return Area{} + return Area{ + height: 0, + writer: os.Stdout, + cursor: cursor, + cursorPosY: 0, + } +} + +// WithWriter sets the custom writer. +func (area Area) WithWriter(writer Writer) Area { + area.writer = writer + area.cursor = area.cursor.WithWriter(writer) + + return area } // Clear clears the content of the Area. func (area *Area) Clear() { - Bottom() + // Initialize writer if not done yet + if area.writer == nil { + area.writer = os.Stdout + } + if area.height > 0 { - ClearLinesUp(area.height) + area.Bottom() + area.ClearLinesUp(area.height) + area.StartOfLine() + } else { + area.StartOfLine() + area.cursor.ClearLine() } } -// Update overwrites the content of the Area. +// Update overwrites the content of the Area and adjusts its height based on content. func (area *Area) Update(content string) { area.Clear() - lines := strings.Split(content, "\n") - - fmt.Println(strings.Repeat("\n", len(lines)-1)) // This appends space if the terminal is at the bottom - Up(len(lines)) + area.writeArea(content) + area.cursorPosY = 0 + area.height = strings.Count(content, "\n") +} - if runtime.GOOS == "windows" { - for _, line := range lines { - fmt.Print(line) - StartOfLineDown(1) +// Up moves the cursor of the area up one line. +func (area *Area) Up(n int) { + if n > 0 { + if area.cursorPosY+n > area.height { + n = area.height - area.cursorPosY } - } else { - for _, line := range lines { - fmt.Println(line) + + area.cursor.Up(n) + area.cursorPosY += n + } +} + +// Down moves the cursor of the area down one line. +func (area *Area) Down(n int) { + if n > 0 { + if area.cursorPosY-n < 0 { + n = area.height - area.cursorPosY } + + area.cursor.Down(n) + area.cursorPosY -= n } - height = 0 +} + +// Bottom moves the cursor to the bottom of the terminal. +// This is done by calculating how many lines were moved by Up and Down. +func (area *Area) Bottom() { + if area.cursorPosY > 0 { + area.Down(area.cursorPosY) + area.cursorPosY = 0 + } +} + +// Top moves the cursor to the top of the area. +// This is done by calculating how many lines were moved by Up and Down. +func (area *Area) Top() { + if area.cursorPosY < area.height { + area.Up(area.height - area.cursorPosY) + area.cursorPosY = area.height + } +} - area.height = len(lines) +// StartOfLine moves the cursor to the start of the current line. +func (area *Area) StartOfLine() { + area.cursor.HorizontalAbsolute(0) +} + +// StartOfLineDown moves the cursor down by n lines, then moves to cursor to the start of the line. +func (area *Area) StartOfLineDown(n int) { + area.Down(n) + area.StartOfLine() +} + +// StartOfLineUp moves the cursor up by n lines, then moves to cursor to the start of the line. +func (area *Area) StartOfLineUp(n int) { + area.Up(n) + area.StartOfLine() +} + +// UpAndClear moves the cursor up by n lines, then clears the line. +func (area *Area) UpAndClear(n int) { + area.Up(n) + area.cursor.ClearLine() +} + +// DownAndClear moves the cursor down by n lines, then clears the line. +func (area *Area) DownAndClear(n int) { + area.Down(n) + area.cursor.ClearLine() +} + +// Move moves the cursor relative by x and y. +func (area *Area) Move(x, y int) { + if x > 0 { + area.cursor.Right(x) + } else if x < 0 { + area.cursor.Left(-x) + } + + if y > 0 { + area.Up(y) + } else if y < 0 { + area.Down(-y) + } +} + +// ClearLinesUp clears n lines upwards from the current position and moves the cursor. +func (area *Area) ClearLinesUp(n int) { + area.StartOfLine() + area.cursor.ClearLine() + + for i := 0; i < n; i++ { + area.UpAndClear(1) + } +} + +// ClearLinesDown clears n lines downwards from the current position and moves the cursor. +func (area *Area) ClearLinesDown(n int) { + area.StartOfLine() + area.cursor.ClearLine() + + for i := 0; i < n; i++ { + area.DownAndClear(1) + } } diff --git a/vendor/atomicgo.dev/cursor/area_other.go b/vendor/atomicgo.dev/cursor/area_other.go new file mode 100644 index 00000000..b92390de --- /dev/null +++ b/vendor/atomicgo.dev/cursor/area_other.go @@ -0,0 +1,13 @@ +//go:build !windows +// +build !windows + +package cursor + +import ( + "fmt" +) + +// Update overwrites the content of the Area and adjusts its height based on content. +func (area *Area) writeArea(content string) { + fmt.Fprint(area.writer, content) +} diff --git a/vendor/atomicgo.dev/cursor/area_windows.go b/vendor/atomicgo.dev/cursor/area_windows.go new file mode 100644 index 00000000..de7dd292 --- /dev/null +++ b/vendor/atomicgo.dev/cursor/area_windows.go @@ -0,0 +1,21 @@ +//go:build windows +// +build windows + +package cursor + +import ( + "fmt" +) + +// writeArea is a helper for platform dependant output. +// For Windows newlines '\n' in the content are replaced by '\r\n' +func (area *Area) writeArea(content string) { + last := ' ' + for _, r := range content { + if r == '\n' && last != '\r' { + fmt.Fprint(area.writer, "\r\n") + continue + } + fmt.Fprint(area.writer, string(r)) + } +} diff --git a/vendor/atomicgo.dev/cursor/cursor.go b/vendor/atomicgo.dev/cursor/cursor.go index fb4c010b..89a3efcf 100644 --- a/vendor/atomicgo.dev/cursor/cursor.go +++ b/vendor/atomicgo.dev/cursor/cursor.go @@ -1,69 +1,26 @@ -//go:build !windows -// +build !windows - package cursor import ( - "fmt" "os" ) -var target Writer = os.Stdout - -// SetTarget allows for any arbitrary io.Writer to be used -// for cursor movement (will not work on Windows). -func SetTarget(w Writer) { - target = w +// Cursor displays content which can be updated on the fly. +// You can use this to create live output, charts, dropdowns, etc. +type Cursor struct { + writer Writer } -// Up moves the cursor n lines up relative to the current position. -func Up(n int) { - fmt.Fprintf(target, "\x1b[%dA", n) - height += n +// NewCursor creates a new Cursor instance writing to os.Stdout. +func NewCursor() *Cursor { + return &Cursor{writer: os.Stdout} } -// Down moves the cursor n lines down relative to the current position. -func Down(n int) { - fmt.Fprintf(target, "\x1b[%dB", n) - if height-n <= 0 { - height = 0 - } else { - height -= n +// WithWriter allows for any arbitrary Writer to be used +// for cursor movement abstracted. +func (c *Cursor) WithWriter(w Writer) *Cursor { + if w != nil { + c.writer = w } -} - -// Right moves the cursor n characters to the right relative to the current position. -func Right(n int) { - fmt.Fprintf(target, "\x1b[%dC", n) -} - -// Left moves the cursor n characters to the left relative to the current position. -func Left(n int) { - fmt.Fprintf(target, "\x1b[%dD", n) -} - -// HorizontalAbsolute moves the cursor to n horizontally. -// The position n is absolute to the start of the line. -func HorizontalAbsolute(n int) { - n += 1 // Moves the line to the character after n - fmt.Fprintf(target, "\x1b[%dG", n) -} - -// Show the cursor if it was hidden previously. -// Don't forget to show the cursor at least at the end of your application. -// Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. -func Show() { - fmt.Fprint(target, "\x1b[?25h") -} - -// Hide the cursor. -// Don't forget to show the cursor at least at the end of your application with Show. -// Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. -func Hide() { - fmt.Fprintf(target, "\x1b[?25l") -} -// ClearLine clears the current line and moves the cursor to it's start position. -func ClearLine() { - fmt.Fprintf(target, "\x1b[2K") + return c } diff --git a/vendor/atomicgo.dev/cursor/cursor_other.go b/vendor/atomicgo.dev/cursor/cursor_other.go new file mode 100644 index 00000000..7c185578 --- /dev/null +++ b/vendor/atomicgo.dev/cursor/cursor_other.go @@ -0,0 +1,67 @@ +//go:build !windows +// +build !windows + +package cursor + +import ( + "fmt" +) + +// Up moves the cursor n lines up relative to the current position. +func (c *Cursor) Up(n int) { + if n > 0 { + fmt.Fprintf(c.writer, "\x1b[%dA", n) + } +} + +// Down moves the cursor n lines down relative to the current position. +func (c *Cursor) Down(n int) { + if n > 0 { + fmt.Fprintf(c.writer, "\x1b[%dB", n) + } +} + +// Right moves the cursor n characters to the right relative to the current position. +func (c *Cursor) Right(n int) { + if n > 0 { + fmt.Fprintf(c.writer, "\x1b[%dC", n) + } +} + +// Left moves the cursor n characters to the left relative to the current position. +func (c *Cursor) Left(n int) { + if n > 0 { + fmt.Fprintf(c.writer, "\x1b[%dD", n) + } +} + +// HorizontalAbsolute moves the cursor to n horizontally. +// The position n is absolute to the start of the line. +func (c *Cursor) HorizontalAbsolute(n int) { + n++ // Moves the line to the character after n + fmt.Fprintf(c.writer, "\x1b[%dG", n) +} + +// Show the cursor if it was hidden previously. +// Don't forget to show the cursor at least at the end of your application. +// Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. +func (c *Cursor) Show() { + fmt.Fprint(c.writer, "\x1b[?25h") +} + +// Hide the cursor. +// Don't forget to show the cursor at least at the end of your application with Show. +// Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. +func (c *Cursor) Hide() { + fmt.Fprintf(c.writer, "\x1b[?25l") +} + +// ClearLine clears the current line and moves the cursor to it's start position. +func (c *Cursor) ClearLine() { + fmt.Fprintf(c.writer, "\x1b[2K") +} + +// Clear clears the current position and moves the cursor to the left. +func (c *Cursor) Clear() { + fmt.Fprintf(c.writer, "\x1b[K") +} diff --git a/vendor/atomicgo.dev/cursor/cursor_test_linux.go b/vendor/atomicgo.dev/cursor/cursor_test_linux.go index 25179b23..6a37f406 100644 --- a/vendor/atomicgo.dev/cursor/cursor_test_linux.go +++ b/vendor/atomicgo.dev/cursor/cursor_test_linux.go @@ -6,75 +6,93 @@ import ( "testing" ) +// TestCustomIOWriter tests the cursor functions with a custom Writer. func TestCustomIOWriter(t *testing.T) { tmpFile, err := os.CreateTemp("", "testingTmpFile-") + defer os.Remove(tmpFile.Name()) + if err != nil { log.Fatal(err) } - defer os.Remove(tmpFile.Name()) w := tmpFile SetTarget(w) Up(2) + expected := "\x1b[2A" actual := getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } clearFile(t, w) Down(2) + expected = "\x1b[2B" actual = getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } clearFile(t, w) Right(2) + expected = "\x1b[2C" actual = getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } clearFile(t, w) Left(2) + expected = "\x1b[2D" actual = getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } clearFile(t, w) Hide() + expected = "\x1b[?25l" actual = getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } clearFile(t, w) Show() + expected = "\x1b[?25h" actual = getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } clearFile(t, w) ClearLine() + expected = "\x1b[2K" actual = getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } clearFile(t, w) HorizontalAbsolute(3) + expected = "\x1b[4G" actual = getFileContent(t, w.Name()) + if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } @@ -82,6 +100,7 @@ func TestCustomIOWriter(t *testing.T) { func getFileContent(t *testing.T, fileName string) string { t.Helper() + content, err := os.ReadFile(fileName) if err != nil { t.Errorf("failed to read file contents: %s", err) @@ -94,12 +113,14 @@ func getFileContent(t *testing.T, fileName string) string { func clearFile(t *testing.T, file *os.File) { t.Helper() + err := file.Truncate(0) if err != nil { t.Errorf("failed to clear file") return } + _, err = file.Seek(0, 0) if err != nil { t.Errorf("failed to clear file") diff --git a/vendor/atomicgo.dev/cursor/cursor_windows.go b/vendor/atomicgo.dev/cursor/cursor_windows.go index 0a3be0af..ebe2bc59 100644 --- a/vendor/atomicgo.dev/cursor/cursor_windows.go +++ b/vendor/atomicgo.dev/cursor/cursor_windows.go @@ -1,46 +1,35 @@ +//go:build windows +// +build windows + package cursor import ( - "os" "syscall" "unsafe" ) -var target Writer = os.Stdout - -// SetTarget allows for any arbitrary Writer to be used -func SetTarget(w Writer) { - target = w -} - // Up moves the cursor n lines up relative to the current position. -func Up(n int) { - move(0, -n) - height += n +func (c *Cursor) Up(n int) { + c.move(0, -n) } // Down moves the cursor n lines down relative to the current position. -func Down(n int) { - move(0, n) - if height-n <= 0 { - height = 0 - } else { - height -= n - } +func (c *Cursor) Down(n int) { + c.move(0, n) } // Right moves the cursor n characters to the right relative to the current position. -func Right(n int) { - move(n, 0) +func (c *Cursor) Right(n int) { + c.move(n, 0) } // Left moves the cursor n characters to the left relative to the current position. -func Left(n int) { - move(-n, 0) +func (c *Cursor) Left(n int) { + c.move(-n, 0) } -func move(x int, y int) { - handle := syscall.Handle(target.Fd()) +func (c *Cursor) move(x int, y int) { + handle := syscall.Handle(c.writer.Fd()) var csbi consoleScreenBufferInfo _, _, _ = procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) @@ -54,8 +43,8 @@ func move(x int, y int) { // HorizontalAbsolute moves the cursor to n horizontally. // The position n is absolute to the start of the line. -func HorizontalAbsolute(n int) { - handle := syscall.Handle(target.Fd()) +func (c *Cursor) HorizontalAbsolute(n int) { + handle := syscall.Handle(c.writer.Fd()) var csbi consoleScreenBufferInfo _, _, _ = procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) @@ -74,8 +63,8 @@ func HorizontalAbsolute(n int) { // Show the cursor if it was hidden previously. // Don't forget to show the cursor at least at the end of your application. // Otherwise the user might have a terminal with a permanently hidden cursor, until he reopens the terminal. -func Show() { - handle := syscall.Handle(target.Fd()) +func (c *Cursor) Show() { + handle := syscall.Handle(c.writer.Fd()) var cci consoleCursorInfo _, _, _ = procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) @@ -87,8 +76,8 @@ func Show() { // Hide the cursor. // Don't forget to show the cursor at least at the end of your application with Show. // Otherwise the user might have a terminal with a permanently hidden cursor, until he reopens the terminal. -func Hide() { - handle := syscall.Handle(target.Fd()) +func (c *Cursor) Hide() { + handle := syscall.Handle(c.writer.Fd()) var cci consoleCursorInfo _, _, _ = procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) @@ -97,9 +86,9 @@ func Hide() { _, _, _ = procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) } -// ClearLine clears the current line and moves the cursor to it's start position. -func ClearLine() { - handle := syscall.Handle(target.Fd()) +// ClearLine clears the current line and moves the cursor to its start position. +func (c *Cursor) ClearLine() { + handle := syscall.Handle(c.writer.Fd()) var csbi consoleScreenBufferInfo _, _, _ = procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) @@ -110,3 +99,20 @@ func ClearLine() { x = csbi.size.x _, _, _ = procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(x), uintptr(*(*int32)(unsafe.Pointer(&cursor))), uintptr(unsafe.Pointer(&w))) } + +// Clear clears the current position and moves the cursor to the left. +func (c *Cursor) Clear() { + handle := syscall.Handle(c.writer.Fd()) + + var csbi consoleScreenBufferInfo + _, _, _ = procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + + var w uint32 + cursor := csbi.cursorPosition + _, _, _ = procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(1), uintptr(*(*int32)(unsafe.Pointer(&cursor))), uintptr(unsafe.Pointer(&w))) + + if cursor.x > 0 { + cursor.x-- + } + _, _, _ = procSetConsoleCursorPosition.Call(uintptr(handle), uintptr(*(*int32)(unsafe.Pointer(&cursor)))) +} diff --git a/vendor/atomicgo.dev/cursor/go.work b/vendor/atomicgo.dev/cursor/go.work new file mode 100644 index 00000000..c71ff3ba --- /dev/null +++ b/vendor/atomicgo.dev/cursor/go.work @@ -0,0 +1,10 @@ +go 1.18 + +use . + +// replace git.neotel.at/go/c5rest => /home/rl/work/c5rest +// replace git.neotel.at/go/c5db => /home/rl/work/c5db + +// replace github.com/pterm/pterm => H:/work/github.com/pterm/pterm + +// replace atomicgo.dev/cursor => H:/github.com/atomicgo.dev/cursor diff --git a/vendor/atomicgo.dev/cursor/utils.go b/vendor/atomicgo.dev/cursor/utils.go index cde36686..4b75f09e 100644 --- a/vendor/atomicgo.dev/cursor/utils.go +++ b/vendor/atomicgo.dev/cursor/utils.go @@ -1,16 +1,92 @@ package cursor -import "io" +import ( + "io" + "os" +) -var height int +// +// Helpers for global cursor handling on os.Stdout +// + +var autoheight int +var cursor = &Cursor{writer: os.Stdout} + +// Writer is an expanded io.Writer interface with a file descriptor. +type Writer interface { + io.Writer + Fd() uintptr +} + +// SetTarget sets to output target of the default curser to the +// provided cursor.Writer (wrapping io.Writer). +func SetTarget(w Writer) { + cursor = cursor.WithWriter(w) +} + +// Up moves the cursor n lines up relative to the current position. +func Up(n int) { + cursor.Up(n) + autoheight += n +} + +// Down moves the cursor n lines down relative to the current position. +func Down(n int) { + cursor.Down(n) + + if autoheight > 0 { + autoheight -= n + } +} + +// Right moves the cursor n characters to the right relative to the current position. +func Right(n int) { + cursor.Right(n) +} + +// Left moves the cursor n characters to the left relative to the current position. +func Left(n int) { + cursor.Left(n) +} + +// HorizontalAbsolute moves the cursor to n horizontally. +// The position n is absolute to the start of the line. +func HorizontalAbsolute(n int) { + cursor.HorizontalAbsolute(n) +} + +// Show the cursor if it was hidden previously. +// Don't forget to show the cursor at least at the end of your application. +// Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. +func Show() { + cursor.Show() +} + +// Hide the cursor. +// Don't forget to show the cursor at least at the end of your application with Show. +// Otherwise the user might have a terminal with a permanently hidden cursor, until they reopen the terminal. +func Hide() { + cursor.Hide() +} + +// ClearLine clears the current line and moves the cursor to it's start position. +func ClearLine() { + cursor.ClearLine() +} + +// Clear clears the current position and moves the cursor to the left. +func Clear() { + cursor.Clear() +} // Bottom moves the cursor to the bottom of the terminal. // This is done by calculating how many lines were moved by Up and Down. func Bottom() { - if height > 0 { - Down(height) + if autoheight > 0 { + Down(autoheight) StartOfLine() - height = 0 + + autoheight = 0 } } @@ -73,9 +149,3 @@ func ClearLinesDown(n int) { DownAndClear(1) } } - -// Writer is an expanded io.Writer interface with a file descriptor. -type Writer interface { - io.Writer - Fd() uintptr -} diff --git a/vendor/atomicgo.dev/schedule/.gitignore b/vendor/atomicgo.dev/schedule/.gitignore new file mode 100644 index 00000000..7e5f3f45 --- /dev/null +++ b/vendor/atomicgo.dev/schedule/.gitignore @@ -0,0 +1,40 @@ +# Go template + +## Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +## Test binary, built with `go test -c` +*.test + +## Output of the go coverage tool, specifically when used with LiteIDE +*.out + +## Dependency directories (remove the comment below to include it) +vendor/ + +# IDEs + +## IntelliJ +.idea +*.iml +out +gen + +## Visual Studio Code +.vscode +*.code-workspace + +# Operating System Files + +## macOS +### General +.DS_Store + +# Other + +## Experimenting folder +experimenting diff --git a/vendor/atomicgo.dev/schedule/.golangci.yml b/vendor/atomicgo.dev/schedule/.golangci.yml new file mode 100644 index 00000000..796ca35c --- /dev/null +++ b/vendor/atomicgo.dev/schedule/.golangci.yml @@ -0,0 +1,99 @@ +linters-settings: + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport + - ifElseChain + - octalLiteral + - whyNoLint + - wrapperFunc + - exitAfterDefer + - hugeParam + - ptrToRefParam + - paramTypeCombine + - unnamedResult +linters: + disable-all: true + enable: + # default linters + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck + - unused + # additional linters + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - decorder + - dupl + - durationcheck + - errchkjson + - errname + - errorlint + - exhaustive + - exhaustruct + - exportloopref + - forcetypeassert + - gocheckcompilerdirectives + - gocritic + - godot + - godox + - goerr113 + - gofmt + - goprintffuncname + - gosec + - gosmopolitan + - importas + - ireturn + - nakedret + - nestif + - nilerr + - nilnil + - prealloc + - predeclared + - revive + - rowserrcheck + - tagalign + - tenv + - thelper + - tparallel + - unconvert + - unparam + - usestdlibvars + - wastedassign + - whitespace + - wrapcheck + - wsl + - gocyclo + - misspell +issues: + include: + - EXC0012 + - EXC0014 + exclude-rules: + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + - gocritic + - linters: + - gocritic + text: "unnecessaryDefer:" + - linters: + - gocritic + text: "preferDecodeRune:" +service: + golangci-lint-version: 1.53.x diff --git a/vendor/atomicgo.dev/schedule/LICENSE b/vendor/atomicgo.dev/schedule/LICENSE new file mode 100644 index 00000000..b42989ef --- /dev/null +++ b/vendor/atomicgo.dev/schedule/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Marvin Wendt (MarvinJWendt) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/atomicgo.dev/schedule/README.md b/vendor/atomicgo.dev/schedule/README.md new file mode 100644 index 00000000..3f7f9a84 --- /dev/null +++ b/vendor/atomicgo.dev/schedule/README.md @@ -0,0 +1,281 @@ +

AtomicGo | schedule

+ +

+Downloads + + +Latest Release + + + +Tests + + + +Coverage + + + +Unit test count + + + +License: MIT + + + +Go report + + +

+ +--- + +

+Documentation +| +Contributing +| +Code of Conduct +

+ +--- + +

+ AtomicGo +

+ +

+ + + +
+

+

go get atomicgo.dev/schedule

+

+ + + +
+

+ + + + + +# schedule + +```go +import "atomicgo.dev/schedule" +``` + +Package schedule provides a simple scheduler for Go. + +It can run a function at a given time, in a given duration, or repeatedly at a given interval. + +## Index + +- [type Task](<#Task>) + - [func After\(d time.Duration, task func\(\)\) \*Task](<#After>) + - [func At\(t time.Time, task func\(\)\) \*Task](<#At>) + - [func Every\(interval time.Duration, task func\(\) bool\) \*Task](<#Every>) + - [func \(s \*Task\) ExecutesIn\(\) time.Duration](<#Task.ExecutesIn>) + - [func \(s \*Task\) IsActive\(\) bool](<#Task.IsActive>) + - [func \(s \*Task\) NextExecutionTime\(\) time.Time](<#Task.NextExecutionTime>) + - [func \(s \*Task\) StartedAt\(\) time.Time](<#Task.StartedAt>) + - [func \(s \*Task\) Stop\(\)](<#Task.Stop>) + - [func \(s \*Task\) Wait\(\)](<#Task.Wait>) + + + +## type [Task]() + +Task holds information about the running task and can be used to stop running tasks. + +```go +type Task struct { + // contains filtered or unexported fields +} +``` + + +### func [After]() + +```go +func After(d time.Duration, task func()) *Task +``` + +After executes the task after the given duration. The function is non\-blocking. If you want to wait for the task to be executed, use the Task.Wait method. + +
Example +

+ + + +```go +package main + +import ( + "fmt" + "time" + + "atomicgo.dev/schedule" +) + +func main() { + task := schedule.After(5*time.Second, func() { + fmt.Println("5 seconds are over!") + }) + + fmt.Println("Some stuff happening...") + + task.Wait() +} +``` + +

+
+ + +### func [At]() + +```go +func At(t time.Time, task func()) *Task +``` + +At executes the task at the given time. The function is non\-blocking. If you want to wait for the task to be executed, use the Task.Wait method. + +
Example +

+ + + +```go +package main + +import ( + "fmt" + "time" + + "atomicgo.dev/schedule" +) + +func main() { + task := schedule.At(time.Now().Add(5*time.Second), func() { + fmt.Println("5 seconds are over!") + }) + + fmt.Println("Some stuff happening...") + + task.Wait() +} +``` + +

+
+ + +### func [Every]() + +```go +func Every(interval time.Duration, task func() bool) *Task +``` + +Every executes the task in the given interval, as long as the task function returns true. The function is non\-blocking. If you want to wait for the task to be executed, use the Task.Wait method. + +
Example +

+ + + +```go +package main + +import ( + "fmt" + "time" + + "atomicgo.dev/schedule" +) + +func main() { + task := schedule.Every(time.Second, func() bool { + fmt.Println("1 second is over!") + return true // return false to stop the task + }) + + fmt.Println("Some stuff happening...") + + time.Sleep(10 * time.Second) + + task.Stop() +} +``` + +

+
+ + +### func \(\*Task\) [ExecutesIn]() + +```go +func (s *Task) ExecutesIn() time.Duration +``` + +ExecutesIn returns the duration until the next execution. + + +### func \(\*Task\) [IsActive]() + +```go +func (s *Task) IsActive() bool +``` + +IsActive returns true if the scheduler is active. + + +### func \(\*Task\) [NextExecutionTime]() + +```go +func (s *Task) NextExecutionTime() time.Time +``` + +NextExecutionTime returns the time when the next execution will happen. + + +### func \(\*Task\) [StartedAt]() + +```go +func (s *Task) StartedAt() time.Time +``` + +StartedAt returns the time when the scheduler was started. + + +### func \(\*Task\) [Stop]() + +```go +func (s *Task) Stop() +``` + +Stop stops the scheduler. + + +### func \(\*Task\) [Wait]() + +```go +func (s *Task) Wait() +``` + +Wait blocks until the scheduler is stopped. After and At will stop automatically after the task is executed. + +Generated by [gomarkdoc]() + + + + +--- + +> [AtomicGo.dev](https://atomicgo.dev)  ·  +> with ❤️ by [@MarvinJWendt](https://github.com/MarvinJWendt) | +> [MarvinJWendt.com](https://marvinjwendt.com) diff --git a/vendor/atomicgo.dev/schedule/codecov.yml b/vendor/atomicgo.dev/schedule/codecov.yml new file mode 100644 index 00000000..bfdc9877 --- /dev/null +++ b/vendor/atomicgo.dev/schedule/codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true diff --git a/vendor/atomicgo.dev/schedule/doc.go b/vendor/atomicgo.dev/schedule/doc.go new file mode 100644 index 00000000..4801fdb5 --- /dev/null +++ b/vendor/atomicgo.dev/schedule/doc.go @@ -0,0 +1,6 @@ +/* +Package schedule provides a simple scheduler for Go. + +It can run a function at a given time, in a given duration, or repeatedly at a given interval. +*/ +package schedule diff --git a/vendor/atomicgo.dev/schedule/schedule.go b/vendor/atomicgo.dev/schedule/schedule.go new file mode 100644 index 00000000..635d29d3 --- /dev/null +++ b/vendor/atomicgo.dev/schedule/schedule.go @@ -0,0 +1,116 @@ +package schedule + +import "time" + +// Task holds information about the running task and can be used to stop running tasks. +type Task struct { + stop chan struct{} + nextExecution time.Time + startedAt time.Time +} + +// newTask creates a new Task. +func newTask() *Task { + return &Task{ + stop: make(chan struct{}), + startedAt: time.Now(), + } +} + +// StartedAt returns the time when the scheduler was started. +func (s *Task) StartedAt() time.Time { + return s.startedAt +} + +// NextExecutionTime returns the time when the next execution will happen. +func (s *Task) NextExecutionTime() time.Time { + return s.nextExecution +} + +// ExecutesIn returns the duration until the next execution. +func (s *Task) ExecutesIn() time.Duration { + return time.Until(s.nextExecution) +} + +// IsActive returns true if the scheduler is active. +func (s *Task) IsActive() bool { + select { + case <-s.stop: + return false + default: + return true + } +} + +// Wait blocks until the scheduler is stopped. +// After and At will stop automatically after the task is executed. +func (s *Task) Wait() { + <-s.stop +} + +// Stop stops the scheduler. +func (s *Task) Stop() { + close(s.stop) +} + +// After executes the task after the given duration. +// The function is non-blocking. If you want to wait for the task to be executed, use the Task.Wait method. +func After(d time.Duration, task func()) *Task { + scheduler := newTask() + scheduler.nextExecution = time.Now().Add(d) + + go func() { + select { + case <-time.After(d): + task() + scheduler.Stop() + case <-scheduler.stop: + return + } + }() + + return scheduler +} + +// At executes the task at the given time. +// The function is non-blocking. If you want to wait for the task to be executed, use the Task.Wait method. +func At(t time.Time, task func()) *Task { + scheduler := newTask() + scheduler.nextExecution = t + + go func() { + select { + case <-time.After(time.Until(t)): + task() + scheduler.Stop() + case <-scheduler.stop: + return + } + }() + + return scheduler +} + +// Every executes the task in the given interval, as long as the task function returns true. +// The function is non-blocking. If you want to wait for the task to be executed, use the Task.Wait method. +func Every(interval time.Duration, task func() bool) *Task { + scheduler := newTask() + scheduler.nextExecution = time.Now().Add(interval) + + ticker := time.NewTicker(interval) + + go func() { + for { + select { + case <-ticker.C: + task() + scheduler.nextExecution = time.Now().Add(interval) + case <-scheduler.stop: + ticker.Stop() + return + } + } + }() + + return scheduler +} diff --git a/vendor/bitbucket.org/creachadair/shell/LICENSE b/vendor/bitbucket.org/creachadair/shell/LICENSE deleted file mode 100644 index 10d72735..00000000 --- a/vendor/bitbucket.org/creachadair/shell/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2015, Michael J. Fromberger -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/bitbucket.org/creachadair/shell/README.md b/vendor/bitbucket.org/creachadair/shell/README.md deleted file mode 100644 index 73282bed..00000000 --- a/vendor/bitbucket.org/creachadair/shell/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# shell - -http://godoc.org/bitbucket.org/creachadair/shell - -The `shell` package implements basic shell command-line splitting. - - diff --git a/vendor/bitbucket.org/creachadair/shell/bitbucket-pipelines.yml b/vendor/bitbucket.org/creachadair/shell/bitbucket-pipelines.yml deleted file mode 100644 index 8acd906c..00000000 --- a/vendor/bitbucket.org/creachadair/shell/bitbucket-pipelines.yml +++ /dev/null @@ -1,23 +0,0 @@ -definitions: - steps: - - step: &Verify - script: - - PACKAGE_PATH="${GOPATH}/src/bitbucket.org/${BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}" - - mkdir -pv "${PACKAGE_PATH}" - - tar -cO --exclude-vcs --exclude=bitbucket-pipelines.yml . | tar -xv -C "${PACKAGE_PATH}" - - cd "${PACKAGE_PATH}" - - go version # log the version of Go we are using in this step - - export GO111MODULE=on # enable modules inside $GOPATH - - go get -v ./... - - go build -v ./... - - go test -v -race -cpu=1,4 ./... - - go vet -v ./... - -pipelines: - default: # run on each push - - step: - image: golang:1.16 - <<: *Verify - - step: - image: golang:1.17 - <<: *Verify diff --git a/vendor/bitbucket.org/creachadair/shell/shell.go b/vendor/bitbucket.org/creachadair/shell/shell.go deleted file mode 100644 index e4f8650f..00000000 --- a/vendor/bitbucket.org/creachadair/shell/shell.go +++ /dev/null @@ -1,325 +0,0 @@ -// Package shell supports splitting and joining of shell command strings. -// -// The Split function divides a string into whitespace-separated fields, -// respecting single and double quotation marks as defined by the Shell Command -// Language section of IEEE Std 1003.1 2013. The Quote function quotes -// characters that would otherwise be subject to shell evaluation, and the Join -// function concatenates quoted strings with spaces between them. -// -// The relationship between Split and Join is that given -// -// fields, ok := Split(Join(ss)) -// -// the following relationship will hold: -// -// fields == ss && ok -// -package shell - -import ( - "bufio" - "bytes" - "io" - "strings" -) - -// These characters must be quoted to escape special meaning. This list -// doesn't include the single quote. -const mustQuote = "|&;<>()$`\\\"\t\n" - -// These characters should be quoted to escape special meaning, since in some -// contexts they are special (e.g., "x=y" in command position, "*" for globs). -const shouldQuote = `*?[#~=%` - -// These are the separator characters in unquoted text. -const spaces = " \t\n" - -const allQuote = mustQuote + shouldQuote + spaces - -type state int - -const ( - stNone state = iota - stBreak - stBreakQ - stWord - stWordQ - stSingle - stDouble - stDoubleQ -) - -type class int - -const ( - clOther class = iota - clBreak - clNewline - clQuote - clSingle - clDouble -) - -type action int - -const ( - drop action = iota - push - xpush - emit -) - -// N.B. Benchmarking shows that array lookup is substantially faster than map -// lookup here, but it requires caution when changing the state machine. In -// particular: -// -// 1. The state and action values must be small integers. -// 2. The update table must completely cover the state values. -// 3. Each action slice must completely cover the action values. -// -var update = [...][]struct { - state - action -}{ - stNone: {}, - stBreak: { - clBreak: {stBreak, drop}, - clNewline: {stBreak, drop}, - clQuote: {stBreakQ, drop}, - clSingle: {stSingle, drop}, - clDouble: {stDouble, drop}, - clOther: {stWord, push}, - }, - stBreakQ: { - clBreak: {stWord, push}, - clNewline: {stBreak, drop}, - clQuote: {stWord, push}, - clSingle: {stWord, push}, - clDouble: {stWord, push}, - clOther: {stWord, push}, - }, - stWord: { - clBreak: {stBreak, emit}, - clNewline: {stBreak, emit}, - clQuote: {stWordQ, drop}, - clSingle: {stSingle, drop}, - clDouble: {stDouble, drop}, - clOther: {stWord, push}, - }, - stWordQ: { - clBreak: {stWord, push}, - clNewline: {stWord, drop}, - clQuote: {stWord, push}, - clSingle: {stWord, push}, - clDouble: {stWord, push}, - clOther: {stWord, push}, - }, - stSingle: { - clBreak: {stSingle, push}, - clNewline: {stSingle, push}, - clQuote: {stSingle, push}, - clSingle: {stWord, drop}, - clDouble: {stSingle, push}, - clOther: {stSingle, push}, - }, - stDouble: { - clBreak: {stDouble, push}, - clNewline: {stDouble, push}, - clQuote: {stDoubleQ, drop}, - clSingle: {stDouble, push}, - clDouble: {stWord, drop}, - clOther: {stDouble, push}, - }, - stDoubleQ: { - clBreak: {stDouble, xpush}, - clNewline: {stDouble, drop}, - clQuote: {stDouble, push}, - clSingle: {stDouble, xpush}, - clDouble: {stDouble, push}, - clOther: {stDouble, xpush}, - }, -} - -var classOf = [256]class{ - ' ': clBreak, - '\t': clBreak, - '\n': clNewline, - '\\': clQuote, - '\'': clSingle, - '"': clDouble, -} - -// A Scanner partitions input from a reader into tokens divided on space, tab, -// and newline characters. Single and double quotation marks are handled as -// described in http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02. -type Scanner struct { - buf *bufio.Reader - cur bytes.Buffer - st state - err error -} - -// NewScanner returns a Scanner that reads input from r. -func NewScanner(r io.Reader) *Scanner { - return &Scanner{ - buf: bufio.NewReader(r), - st: stBreak, - } -} - -// Next advances the scanner and reports whether there are any further tokens -// to be consumed. -func (s *Scanner) Next() bool { - if s.err != nil { - return false - } - s.cur.Reset() - for { - c, err := s.buf.ReadByte() - s.err = err - if err == io.EOF { - break - } else if err != nil { - return false - } - next := update[s.st][classOf[c]] - s.st = next.state - switch next.action { - case push: - s.cur.WriteByte(c) - case xpush: - s.cur.Write([]byte{'\\', c}) - case emit: - return true // s.cur has a complete token - case drop: - continue - default: - panic("unknown action") - } - } - return s.st != stBreak -} - -// Text returns the text of the current token, or "" if there is none. -func (s *Scanner) Text() string { return s.cur.String() } - -// Err returns the error, if any, that resulted from the most recent action. -func (s *Scanner) Err() error { return s.err } - -// Complete reports whether the current token is complete, meaning that it is -// unquoted or its quotes were balanced. -func (s *Scanner) Complete() bool { return s.st == stBreak || s.st == stWord } - -// Rest returns an io.Reader for the remainder of the unconsumed input in s. -// After calling this method, Next will always return false. The remainder -// does not include the text of the current token at the time Rest is called. -func (s *Scanner) Rest() io.Reader { - s.st = stNone - s.cur.Reset() - s.err = io.EOF - return s.buf -} - -// Each calls f for each token in the scanner until the input is exhausted, f -// returns false, or an error occurs. -func (s *Scanner) Each(f func(tok string) bool) error { - for s.Next() { - if !f(s.Text()) { - return nil - } - } - if err := s.Err(); err != io.EOF { - return err - } - return nil -} - -// Split returns the remaining tokens in s, not including the current token if -// there is one. Any tokens already consumed are still returned, even if there -// is an error. -func (s *Scanner) Split() []string { - var tokens []string - for s.Next() { - tokens = append(tokens, s.Text()) - } - return tokens -} - -// Split partitions s into tokens divided on space, tab, and newline characters -// using a *Scanner. Leading and trailing whitespace are ignored. -// -// The Boolean flag reports whether the final token is "valid", meaning there -// were no unclosed quotations in the string. -func Split(s string) ([]string, bool) { - sc := NewScanner(strings.NewReader(s)) - ss := sc.Split() - return ss, sc.Complete() -} - -func quotable(s string) (hasQ, hasOther bool) { - const ( - quote = 1 - other = 2 - all = quote + other - ) - var v uint - for i := 0; i < len(s) && v < all; i++ { - if s[i] == '\'' { - v |= quote - } else if strings.IndexByte(allQuote, s[i]) >= 0 { - v |= other - } - } - return v"e != 0, v&other != 0 -} - -// Quote returns a copy of s in which shell metacharacters are quoted to -// protect them from evaluation. -func Quote(s string) string { - var buf bytes.Buffer - return quote(s, &buf) -} - -// quote implements quotation, using the provided buffer as scratch space. The -// existing contents of the buffer are clobbered. -func quote(s string, buf *bytes.Buffer) string { - if s == "" { - return "''" - } - hasQ, hasOther := quotable(s) - if !hasQ && !hasOther { - return s // fast path: nothing needs quotation - } - - buf.Reset() - inq := false - for i := 0; i < len(s); i++ { - ch := s[i] - if ch == '\'' { - if inq { - buf.WriteByte('\'') - inq = false - } - buf.WriteByte('\\') - } else if !inq && hasOther { - buf.WriteByte('\'') - inq = true - } - buf.WriteByte(ch) - } - if inq { - buf.WriteByte('\'') - } - return buf.String() -} - -// Join quotes each element of ss with Quote and concatenates the resulting -// strings separated by spaces. -func Join(ss []string) string { - quoted := make([]string, len(ss)) - var buf bytes.Buffer - for i, s := range ss { - quoted[i] = quote(s, &buf) - } - return strings.Join(quoted, " ") -} diff --git a/vendor/github.com/bitfield/script/README.md b/vendor/github.com/bitfield/script/README.md index 4a9258d9..b57324e7 100644 --- a/vendor/github.com/bitfield/script/README.md +++ b/vendor/github.com/bitfield/script/README.md @@ -1,4 +1,7 @@ -[![Go Reference](https://pkg.go.dev/badge/github.com/bitfield/script.svg)](https://pkg.go.dev/github.com/bitfield/script)[![Go Report Card](https://goreportcard.com/badge/github.com/bitfield/script)](https://goreportcard.com/report/github.com/bitfield/script)[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go)[![CircleCI](https://circleci.com/gh/bitfield/script.svg?style=svg)](https://circleci.com/gh/bitfield/script) +[![Go Reference](https://pkg.go.dev/badge/github.com/bitfield/script.svg)](https://pkg.go.dev/github.com/bitfield/script) +[![Go Report Card](https://goreportcard.com/badge/github.com/bitfield/script)](https://goreportcard.com/report/github.com/bitfield/script) +[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go) +![Tests](https://github.com/bitfield/script/actions/workflows/test.yml/badge.svg) ```go import "github.com/bitfield/script" @@ -45,6 +48,7 @@ If you're already familiar with shell scripting and the Unix toolset, here is a | `sed` | [`Replace`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Replace) / [`ReplaceRegexp`](https://pkg.go.dev/github.com/bitfield/script#Pipe.ReplaceRegexp) | | `sha256sum` | [`SHA256Sum`](https://pkg.go.dev/github.com/bitfield/script#Pipe.SHA256Sum) / [`SHA256Sums`](https://pkg.go.dev/github.com/bitfield/script#Pipe.SHA256Sums) | | `tail` | [`Last`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Last) | +| `tee` | [`Tee`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Tee) | | `uniq -c` | [`Freq`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Freq) | | `wc -l` | [`CountLines`](https://pkg.go.dev/github.com/bitfield/script#Pipe.CountLines) | | `xargs` | [`ExecForEach`](https://pkg.go.dev/github.com/bitfield/script#Pipe.ExecForEach) | @@ -99,6 +103,12 @@ What's that? You want to append that output to a file instead of printing it to script.Args().Concat().Match("Error").First(10).AppendFile("/var/log/errors.txt") ``` +And if we'd like to send the output to the terminal *as well as* to the file, we can do that: + +```go +script.Echo("data").Tee().AppendFile("data.txt") +``` + We're not limited to getting data only from files or standard input. We can get it from HTTP requests too: ```go @@ -301,6 +311,7 @@ Filters are methods on an existing pipe that also return a pipe, allowing you to | [`Replace`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Replace) | matching text replaced with given string | | [`ReplaceRegexp`](https://pkg.go.dev/github.com/bitfield/script#Pipe.ReplaceRegexp) | matching text replaced with given string | | [`SHA256Sums`](https://pkg.go.dev/github.com/bitfield/script#Pipe.SHA256Sums) | SHA-256 hashes of each listed file | +| [`Tee`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Tee) | input copied to supplied writers | Note that filters run concurrently, rather than producing nothing until each stage has fully read its input. This is convenient for executing long-running comands, for example. If you do need to wait for the pipeline to complete, call [`Wait`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Wait). @@ -325,6 +336,7 @@ Sinks are methods that return some data from a pipe, ending the pipeline and ext | Version | New | | ----------- | ------- | +| v0.22.0 | [`Tee`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Tee), [`WithStderr`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithStderr) | | v0.21.0 | HTTP support: [`Do`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Do), [`Get`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Get), [`Post`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Post) | | v0.20.0 | [`JQ`](https://pkg.go.dev/github.com/bitfield/script#Pipe.JQ) | diff --git a/vendor/github.com/bitfield/script/script.go b/vendor/github.com/bitfield/script/script.go index f6a69e53..da7e2fe5 100644 --- a/vendor/github.com/bitfield/script/script.go +++ b/vendor/github.com/bitfield/script/script.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "math" "net/http" "os" "os/exec" @@ -19,16 +20,16 @@ import ( "sync" "text/template" - "bitbucket.org/creachadair/shell" "github.com/itchyny/gojq" + "mvdan.cc/sh/v3/shell" ) // Pipe represents a pipe object with an associated [ReadAutoCloser]. type Pipe struct { // Reader is the underlying reader. - Reader ReadAutoCloser - stdout io.Writer - httpClient *http.Client + Reader ReadAutoCloser + stdout, stderr io.Writer + httpClient *http.Client // because pipe stages are concurrent, protect 'err' mu *sync.Mutex @@ -38,11 +39,7 @@ type Pipe struct { // Args creates a pipe containing the program's command-line arguments from // [os.Args], excluding the program name, one per line. func Args() *Pipe { - var s strings.Builder - for _, a := range os.Args[1:] { - s.WriteString(a + "\n") - } - return Echo(s.String()) + return Slice(os.Args[1:]) } // Do creates a pipe that makes the HTTP request req and produces the response. @@ -68,16 +65,15 @@ func Exec(cmdLine string) *Pipe { // File creates a pipe that reads from the file path. func File(path string) *Pipe { - p := NewPipe() f, err := os.Open(path) if err != nil { - return p.WithError(err) + return NewPipe().WithError(err) } - return p.WithReader(f) + return NewPipe().WithReader(f) } -// FindFiles creates a pipe listing all the files in the directory path and its -// subdirectories recursively, one per line, like Unix find(1). If path doesn't +// FindFiles creates a pipe listing all the files in the directory dir and its +// subdirectories recursively, one per line, like Unix find(1). If dir doesn't // exist or can't be read, the pipe's error status will be set. // // Each line of the output consists of a slash-separated path, starting with @@ -91,21 +87,21 @@ func File(path string) *Pipe { // // test/1.txt // test/2.txt -func FindFiles(path string) *Pipe { - var fileNames []string - walkFn := func(path string, info os.FileInfo, err error) error { +func FindFiles(dir string) *Pipe { + var paths []string + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { - fileNames = append(fileNames, path) + paths = append(paths, path) } return nil - } - if err := filepath.Walk(path, walkFn); err != nil { + }) + if err != nil { return NewPipe().WithError(err) } - return Slice(fileNames) + return Slice(paths) } // Get creates a pipe that makes an HTTP GET request to URL, and produces the @@ -121,12 +117,11 @@ func Get(URL string) *Pipe { // // IfExists("/foo/bar").Exec("/usr/bin/something") func IfExists(path string) *Pipe { - p := NewPipe() _, err := os.Stat(path) if err != nil { - return p.WithError(err) + return NewPipe().WithError(err) } - return p + return NewPipe() } // ListFiles creates a pipe containing the files or directories specified by @@ -144,7 +139,7 @@ func ListFiles(path string) *Pipe { } return Slice(fileNames) } - files, err := os.ReadDir(path) + entries, err := os.ReadDir(path) if err != nil { // Check for the case where the path matches exactly one file s, err := os.Stat(path) @@ -156,11 +151,11 @@ func ListFiles(path string) *Pipe { } return NewPipe().WithError(err) } - fileNames := make([]string, len(files)) - for i, f := range files { - fileNames[i] = filepath.Join(path, f.Name()) + matches := make([]string, len(entries)) + for i, e := range entries { + matches[i] = filepath.Join(path, e.Name()) } - return Slice(fileNames) + return Slice(matches) } // NewPipe creates a new pipe with an empty reader (use [Pipe.WithReader] to @@ -168,8 +163,7 @@ func ListFiles(path string) *Pipe { func NewPipe() *Pipe { return &Pipe{ Reader: ReadAutoCloser{}, - mu: &sync.Mutex{}, - err: nil, + mu: new(sync.Mutex), stdout: os.Stdout, httpClient: http.DefaultClient, } @@ -199,8 +193,6 @@ func (p *Pipe) AppendFile(path string) (int64, error) { return p.writeOrAppendFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY) } -var exitStatusPattern = regexp.MustCompile(`exit status (\d+)$`) - // Basename reads paths from the pipe, one per line, and removes any leading // directory components from each. So, for example, /usr/local/bin/foo would // become just foo. This is the complementary operation to [Pipe.Dirname]. @@ -221,7 +213,7 @@ func (p *Pipe) Bytes() ([]byte, error) { if err != nil { p.SetError(err) } - return data, nil + return data, p.Error() } // Close closes the pipe's associated reader. This is a no-op if the reader is @@ -268,17 +260,15 @@ func (p *Pipe) Concat() *Pipe { var readers []io.Reader p.FilterScan(func(line string, w io.Writer) { input, err := os.Open(line) - if err != nil { - return // skip errors + if err == nil { + readers = append(readers, NewReadAutoCloser(input)) } - readers = append(readers, NewReadAutoCloser(input)) }).Wait() return p.WithReader(io.MultiReader(readers...)) } // CountLines returns the number of lines of input, or an error. -func (p *Pipe) CountLines() (int, error) { - lines := 0 +func (p *Pipe) CountLines() (lines int, err error) { p.FilterScan(func(line string, w io.Writer) { lines++ }).Wait() @@ -319,14 +309,14 @@ func (p *Pipe) Do(req *http.Request) *Pipe { return err } defer resp.Body.Close() - // Any HTTP 2xx status code is considered okay - if resp.StatusCode/100 != 2 { - return fmt.Errorf("unexpected HTTP response status: %s", resp.Status) - } _, err = io.Copy(w, resp.Body) if err != nil { return err } + // Any HTTP 2xx status code is considered okay + if resp.StatusCode/100 != 2 { + return fmt.Errorf("unexpected HTTP response status: %s", resp.Status) + } return nil }) } @@ -338,10 +328,10 @@ func (p *Pipe) Do(req *http.Request) *Pipe { // concurrently and don't do unnecessary reads on the input. func (p *Pipe) EachLine(process func(string, *strings.Builder)) *Pipe { return p.Filter(func(r io.Reader, w io.Writer) error { - scanner := bufio.NewScanner(r) - output := strings.Builder{} + scanner := newScanner(r) + output := new(strings.Builder) for scanner.Scan() { - process(scanner.Text(), &output) + process(scanner.Text(), output) } fmt.Fprint(w, output.String()) return scanner.Err() @@ -354,7 +344,7 @@ func (p *Pipe) Echo(s string) *Pipe { if p.Error() != nil { return p } - return p.WithReader(NewReadAutoCloser(strings.NewReader(s))) + return p.WithReader(strings.NewReader(s)) } // Error returns any error present on the pipe, or nil otherwise. @@ -368,8 +358,9 @@ func (p *Pipe) Error() error { } // Exec runs cmdLine as an external command, sending it the contents of the -// pipe as input, and produces the command's combined output. The effect of -// this is to filter the contents of the pipe through the external command. +// pipe as input, and produces the command's standard output (see below for +// error output). The effect of this is to filter the contents of the pipe +// through the external command. // // # Error handling // @@ -380,19 +371,26 @@ func (p *Pipe) Error() error { // because [Pipe.String] is a no-op if the pipe's error status is set, if you // want output you will need to reset the error status before calling // [Pipe.String]. +// +// If the command writes to its standard error stream, this will also go to the +// pipe, along with its standard output. However, the standard error text can +// instead be redirected to a supplied writer, using [Pipe.WithStderr]. func (p *Pipe) Exec(cmdLine string) *Pipe { return p.Filter(func(r io.Reader, w io.Writer) error { - args, ok := shell.Split(cmdLine) // strings.Fields doesn't handle quotes - if !ok { - return fmt.Errorf("unbalanced quotes or backslashes in [%s]", cmdLine) + args, err := shell.Fields(cmdLine, nil) + if err != nil { + return err } cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = r cmd.Stdout = w cmd.Stderr = w - err := cmd.Start() + if p.stderr != nil { + cmd.Stderr = p.stderr + } + err = cmd.Start() if err != nil { - fmt.Fprintln(w, err) + fmt.Fprintln(cmd.Stderr, err) return err } return cmd.Wait() @@ -413,29 +411,31 @@ func (p *Pipe) ExecForEach(cmdLine string) *Pipe { return p.WithError(err) } return p.Filter(func(r io.Reader, w io.Writer) error { - scanner := bufio.NewScanner(r) + scanner := newScanner(r) for scanner.Scan() { - cmdLine := strings.Builder{} - err := tpl.Execute(&cmdLine, scanner.Text()) + cmdLine := new(strings.Builder) + err := tpl.Execute(cmdLine, scanner.Text()) if err != nil { return err } - // strings.Fields doesn't handle quotes - args, ok := shell.Split(cmdLine.String()) - if !ok { - return fmt.Errorf("unbalanced quotes or backslashes in [%s]", cmdLine.String()) + args, err := shell.Fields(cmdLine.String(), nil) + if err != nil { + return err } cmd := exec.Command(args[0], args[1:]...) cmd.Stdout = w cmd.Stderr = w + if p.stderr != nil { + cmd.Stderr = p.stderr + } err = cmd.Start() if err != nil { - fmt.Fprintln(w, err) + fmt.Fprintln(cmd.Stderr, err) continue } err = cmd.Wait() if err != nil { - fmt.Fprintln(w, err) + fmt.Fprintln(cmd.Stderr, err) continue } } @@ -443,6 +443,8 @@ func (p *Pipe) ExecForEach(cmdLine string) *Pipe { }) } +var exitStatusPattern = regexp.MustCompile(`exit status (\d+)$`) + // ExitStatus returns the integer exit status of a previous command (for // example run by [Pipe.Exec]). This will be zero unless the pipe's error // status is set and the error matches the pattern “exit status %d”. @@ -502,7 +504,7 @@ func (p *Pipe) FilterLine(filter func(string) string) *Pipe { // handling. func (p *Pipe) FilterScan(filter func(string, io.Writer)) *Pipe { return p.Filter(func(r io.Reader, w io.Writer) error { - scanner := bufio.NewScanner(r) + scanner := newScanner(r) for scanner.Scan() { filter(scanner.Text(), w) } @@ -555,16 +557,16 @@ func (p *Pipe) Freq() *Pipe { count int } return p.Filter(func(r io.Reader, w io.Writer) error { - scanner := bufio.NewScanner(r) + scanner := newScanner(r) for scanner.Scan() { freq[scanner.Text()]++ } freqs := make([]frequency, 0, len(freq)) - var maxCount int + max := 0 for line, count := range freq { freqs = append(freqs, frequency{line, count}) - if count > maxCount { - maxCount = count + if count > max { + max = count } } sort.Slice(freqs, func(i, j int) bool { @@ -574,7 +576,7 @@ func (p *Pipe) Freq() *Pipe { } return x > y }) - fieldWidth := len(strconv.Itoa(maxCount)) + fieldWidth := len(strconv.Itoa(max)) for _, item := range freqs { fmt.Fprintf(w, "%*d %s\n", fieldWidth, item.count, item.line) } @@ -597,14 +599,13 @@ func (p *Pipe) Get(URL string) *Pipe { // space-separated string, which will always end with a newline. func (p *Pipe) Join() *Pipe { return p.Filter(func(r io.Reader, w io.Writer) error { - scanner := bufio.NewScanner(r) - var line string + scanner := newScanner(r) first := true for scanner.Scan() { if !first { fmt.Fprint(w, " ") } - line = scanner.Text() + line := scanner.Text() fmt.Fprint(w, line) first = false } @@ -659,7 +660,7 @@ func (p *Pipe) Last(n int) *Pipe { return NewPipe() } return p.Filter(func(r io.Reader, w io.Writer) error { - scanner := bufio.NewScanner(r) + scanner := newScanner(r) input := ring.New(n) for scanner.Scan() { input.Value = scanner.Text() @@ -703,16 +704,6 @@ func (p *Pipe) Post(URL string) *Pipe { return p.Do(req) } -// Read reads up to len(b) bytes from the pipe into b. It returns the number of -// bytes read and any error encountered. At end of file, or on a nil pipe, Read -// returns 0, [io.EOF]. -func (p *Pipe) Read(b []byte) (int, error) { - if p.Error() != nil { - return 0, p.Error() - } - return p.Reader.Read(b) -} - // Reject produces only lines that do not contain the string s. func (p *Pipe) Reject(s string) *Pipe { return p.FilterScan(func(line string, w io.Writer) { @@ -748,6 +739,16 @@ func (p *Pipe) ReplaceRegexp(re *regexp.Regexp, replace string) *Pipe { }) } +// Read reads up to len(b) bytes from the pipe into b. It returns the number of +// bytes read and any error encountered. At end of file, or on a nil pipe, Read +// returns 0, [io.EOF]. +func (p *Pipe) Read(b []byte) (int, error) { + if p.Error() != nil { + return 0, p.Error() + } + return p.Reader.Read(b) +} + // SetError sets the error err on the pipe. func (p *Pipe) SetError(err error) { if p.mu == nil { // uninitialised pipe @@ -833,11 +834,22 @@ func (p *Pipe) String() (string, error) { return string(data), p.Error() } +// Tee copies the pipe's contents to each of the supplied writers, like Unix +// tee(1). If no writers are supplied, the default is the pipe's standard +// output. +func (p *Pipe) Tee(writers ...io.Writer) *Pipe { + teeWriter := p.stdout + if len(writers) > 0 { + teeWriter = io.MultiWriter(writers...) + } + return p.WithReader(io.TeeReader(p.Reader, teeWriter)) +} + // Wait reads the pipe to completion and discards the result. This is mostly // useful for waiting until concurrent filters have completed (see // [Pipe.Filter]). func (p *Pipe) Wait() { - _, err := io.Copy(io.Discard, p) + _, err := io.ReadAll(p) if err != nil { p.SetError(err) } @@ -868,6 +880,14 @@ func (p *Pipe) WithReader(r io.Reader) *Pipe { return p } +// WithStderr redirects the standard error output for commands run via +// [Pipe.Exec] or [Pipe.ExecForEach] to the writer w, instead of going to the +// pipe as it normally would. +func (p *Pipe) WithStderr(w io.Writer) *Pipe { + p.stderr = w + return p +} + // WithStdout sets the pipe's standard output to the writer w, instead of the // default [os.Stdout]. func (p *Pipe) WithStdout(w io.Writer) *Pipe { @@ -894,9 +914,8 @@ func (p *Pipe) writeOrAppendFile(path string, mode int) (int64, error) { wrote, err := io.Copy(out, p) if err != nil { p.SetError(err) - return 0, err } - return wrote, nil + return wrote, p.Error() } // ReadAutoCloser wraps an [io.ReadCloser] so that it will be automatically @@ -939,3 +958,9 @@ func (ra ReadAutoCloser) Read(b []byte) (n int, err error) { } return n, err } + +func newScanner(r io.Reader) *bufio.Scanner { + scanner := bufio.NewScanner(r) + scanner.Buffer(make([]byte, 4096), math.MaxInt) + return scanner +} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/.travis.yml b/vendor/github.com/dgrijalva/jwt-go/v4/.travis.yml deleted file mode 100644 index ade3c583..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: go - -script: - - go vet ./... - - go test -v ./... - -go: - - "1.11" - - "1.12" - - "1.13" - - tip diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/MIGRATION_GUIDE.md b/vendor/github.com/dgrijalva/jwt-go/v4/MIGRATION_GUIDE.md deleted file mode 100644 index 343afd81..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/MIGRATION_GUIDE.md +++ /dev/null @@ -1,101 +0,0 @@ -## Migration Guide from v3 -> v4 - -TODO: write this - -## Migration Guide from v2 -> v3 - -Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code. - -### `Token.Claims` is now an interface type - -The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`. - -`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property. - -The old example for parsing a token looked like this.. - -```go - if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { - fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) - } -``` - -is now directly mapped to... - -```go - if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { - claims := token.Claims.(jwt.MapClaims) - fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"]) - } -``` - -`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type. - -```go - type MyCustomClaims struct { - User string - *StandardClaims - } - - if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil { - claims := token.Claims.(*MyCustomClaims) - fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt) - } -``` - -### `ParseFromRequest` has been moved - -To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`. - -`Extractors` do the work of picking the token string out of a request. The interface is simple and composable. - -This simple parsing example: - -```go - if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil { - fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) - } -``` - -is directly mapped to: - -```go - if token, err := request.ParseFromRequest(req, request.OAuth2Extractor, keyLookupFunc); err == nil { - claims := token.Claims.(jwt.MapClaims) - fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"]) - } -``` - -There are several concrete `Extractor` types provided for your convenience: - -* `HeaderExtractor` will search a list of headers until one contains content. -* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content. -* `MultiExtractor` will try a list of `Extractors` in order until one returns content. -* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token. -* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument -* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header - - -### RSA signing methods no longer accept `[]byte` keys - -Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse. - -To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types. - -```go - func keyLookupFunc(*Token) (interface{}, error) { - // Don't forget to validate the alg is what you expect: - if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - - // Look up key - key, err := lookupPublicKey(token.Header["kid"]) - if err != nil { - return nil, err - } - - // Unpack key from PEM encoded PKCS8 - return jwt.ParseRSAPublicKeyFromPEM(key) - } -``` diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/README.md b/vendor/github.com/dgrijalva/jwt-go/v4/README.md deleted file mode 100644 index 230589bf..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# jwt-go - -[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go) -[![GoDoc](https://godoc.org/github.com/dgrijalva/jwt-go?status.svg)](https://godoc.org/github.com/dgrijalva/jwt-go) - -A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html) - -**NEW VERSION:** Version 4 of this library is now available. This is the first non-backward-compatible version in a long time. There are a few changes that all users will notice, such as the new types introduced in members of `StandardClaims`. More changes are additive or only impact more advanced use. See VERSION_HISTORY.md for a list of changes as well as **TODO** MIGRATION_GUIDE.md for help updating your code. - -**SECURITY NOTICE:** Some older versions of Go have a security issue in the cryotp/elliptic. Recommendation is to upgrade to at least 1.8.3. See issue #216 for more detail. - -**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided. - -## What the heck is a JWT? - -JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens. - -In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way. - -The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. - -The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-jones-json-web-token.html) for information about reserved keys and the proper way to add your own. - -## What's in the box? - -This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. - -## Examples - -See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage: - -- [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac) -- [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac) -- [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples) - -## Extensions - -This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. - -Here's an example of an extension that integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS): https://github.com/someone1/gcp-jwt-go - -## Compliance - -This library was last reviewed to comply with [RTF 7519](http://www.rfc-editor.org/info/rfc7519) dated May 2015 with a few notable differences: - -- In order to protect against accidental use of [Unsecured JWTs](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#UnsecuredJWT), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key. - -## Project Status & Versioning - -This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). - -This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases). - -As of version 4, this project is compatible with go modules. You should use that to ensure you have no unpleasant surprises when updating. - -**BREAKING CHANGES:\*** - -- Version 4.0.0 includes _a lot_ of changes from the 3.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. - -## Usage Tips - -### Signing vs Encryption - -A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data: - -- The author of the token was in the possession of the signing secret -- The data has not been modified since it was signed - -It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. - -### Choosing a Signing Method - -There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric. - -Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation. - -Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. - -### Signing Methods and Key Types - -Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones: - -- The [HMAC signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation -- The [RSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation -- The [ECDSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation - -### JWT and OAuth - -It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. - -Without going too far down the rabbit hole, here's a description of the interaction of these technologies: - -- OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth. -- OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. -- Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL. - -## More - -Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go). - -The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation. diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/claim_strings.go b/vendor/github.com/dgrijalva/jwt-go/v4/claim_strings.go deleted file mode 100644 index 057b684d..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/claim_strings.go +++ /dev/null @@ -1,47 +0,0 @@ -package jwt - -import ( - "encoding/json" - "reflect" -) - -// ClaimStrings is used for parsing claim properties that -// can be either a string or array of strings -type ClaimStrings []string - -// ParseClaimStrings is used to produce a ClaimStrings value -// from the various forms it may present during encoding/decodeing -func ParseClaimStrings(value interface{}) (ClaimStrings, error) { - switch v := value.(type) { - case string: - return ClaimStrings{v}, nil - case []string: - return ClaimStrings(v), nil - case []interface{}: - result := make(ClaimStrings, 0, len(v)) - for i, vv := range v { - if x, ok := vv.(string); ok { - result = append(result, x) - } else { - return nil, &json.UnsupportedTypeError{Type: reflect.TypeOf(v[i])} - } - } - return result, nil - case nil: - return nil, nil - default: - return nil, &json.UnsupportedTypeError{Type: reflect.TypeOf(v)} - } -} - -// UnmarshalJSON implements the json package's Unmarshaler interface -func (c *ClaimStrings) UnmarshalJSON(data []byte) error { - var value interface{} - err := json.Unmarshal(data, &value) - if err != nil { - return err - } - - *c, err = ParseClaimStrings(value) - return err -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/claims.go b/vendor/github.com/dgrijalva/jwt-go/v4/claims.go deleted file mode 100644 index a065328b..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/claims.go +++ /dev/null @@ -1,64 +0,0 @@ -package jwt - -// Claims is the interface used to hold the claims values of a token -// For a type to be a Claims object, it must have a Valid method that determines -// if the token is invalid for any supported reason -// Claims are parsed and encoded using the standard library's encoding/json -// package. Claims are passed directly to that. -type Claims interface { - // A nil validation helper should use the default helper - Valid(*ValidationHelper) error -} - -// StandardClaims is a structured version of Claims Section, as referenced at -// https://tools.ietf.org/html/rfc7519#section-4.1 -// See examples for how to use this with your own claim types -type StandardClaims struct { - Audience ClaimStrings `json:"aud,omitempty"` - ExpiresAt *Time `json:"exp,omitempty"` - ID string `json:"jti,omitempty"` - IssuedAt *Time `json:"iat,omitempty"` - Issuer string `json:"iss,omitempty"` - NotBefore *Time `json:"nbf,omitempty"` - Subject string `json:"sub,omitempty"` -} - -// Valid validates standard claims using ValidationHelper -// Validates time based claims "exp, nbf" (see: WithLeeway) -// Validates "aud" if present in claims. (see: WithAudience, WithoutAudienceValidation) -// Validates "iss" if option is provided (see: WithIssuer) -func (c StandardClaims) Valid(h *ValidationHelper) error { - var vErr error - - if h == nil { - h = DefaultValidationHelper - } - - if err := h.ValidateExpiresAt(c.ExpiresAt); err != nil { - vErr = wrapError(err, vErr) - } - - if err := h.ValidateNotBefore(c.NotBefore); err != nil { - vErr = wrapError(err, vErr) - } - - if err := h.ValidateAudience(c.Audience); err != nil { - vErr = wrapError(err, vErr) - } - - if err := h.ValidateIssuer(c.Issuer); err != nil { - vErr = wrapError(err, vErr) - } - - return vErr -} - -// VerifyAudience compares the aud claim against cmp. -func (c *StandardClaims) VerifyAudience(h *ValidationHelper, cmp string) error { - return h.ValidateAudienceAgainst(c.Audience, cmp) -} - -// VerifyIssuer compares the iss claim against cmp. -func (c *StandardClaims) VerifyIssuer(h *ValidationHelper, cmp string) error { - return h.ValidateIssuerAgainst(c.Issuer, cmp) -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/ecdsa.go b/vendor/github.com/dgrijalva/jwt-go/v4/ecdsa.go deleted file mode 100644 index 9a0d6083..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/ecdsa.go +++ /dev/null @@ -1,173 +0,0 @@ -package jwt - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rand" - "encoding/asn1" - "fmt" - "math/big" -) - -// SigningMethodECDSA implements the ECDSA family of signing methods signing methods -// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification -type SigningMethodECDSA struct { - Name string - Hash crypto.Hash - KeySize int - CurveBits int -} - -// Mirrors the struct from crypto/ecdsa, we expect ecdsa.PrivateKey.Sign function to return this struct asn1 encoded -type ecdsaSignature struct { - R, S *big.Int -} - -// Specific instances for EC256 and company -var ( - SigningMethodES256 *SigningMethodECDSA - SigningMethodES384 *SigningMethodECDSA - SigningMethodES512 *SigningMethodECDSA -) - -func init() { - // ES256 - SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} - RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { - return SigningMethodES256 - }) - - // ES384 - SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} - RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { - return SigningMethodES384 - }) - - // ES512 - SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} - RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { - return SigningMethodES512 - }) -} - -// Alg implements SigningMethod -func (m *SigningMethodECDSA) Alg() string { - return m.Name -} - -// Verify implements the Verify method from SigningMethod -// For this verify method, key must be an ecdsa.PublicKey struct -func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - - // Get the key - var ecdsaKey *ecdsa.PublicKey - var ok bool - - switch k := key.(type) { - case *ecdsa.PublicKey: - ecdsaKey = k - case crypto.Signer: - pub := k.Public() - if ecdsaKey, ok = pub.(*ecdsa.PublicKey); !ok { - return &InvalidKeyError{Message: fmt.Sprintf("crypto.Signer returned an unexpected public key type: %T", pub)} - } - default: - return NewInvalidKeyTypeError("*ecdsa.PublicKey or crypto.Signer", key) - } - - if len(sig) != 2*m.KeySize { - return &UnverfiableTokenError{Message: "signature length is invalid"} - } - - r := big.NewInt(0).SetBytes(sig[:m.KeySize]) - s := big.NewInt(0).SetBytes(sig[m.KeySize:]) - - // Create hasher - if !m.Hash.Available() { - return ErrHashUnavailable - } - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Verify the signature - if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true { - return nil - } - return new(InvalidSignatureError) -} - -// Sign implements the Sign method from SigningMethod -// For this signing method, key must be an ecdsa.PrivateKey struct -func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { - var signer crypto.Signer - var pub *ecdsa.PublicKey - var ok bool - - if signer, ok = key.(crypto.Signer); !ok { - return "", NewInvalidKeyTypeError("*ecdsa.PrivateKey or crypto.Signer", key) - } - - //sanity check that the signer is an ecdsa signer - if pub, ok = signer.Public().(*ecdsa.PublicKey); !ok { - return "", &InvalidKeyError{Message: fmt.Sprintf("signer returned unexpected public key type: %T", pub)} - } - - // Create the hasher - if !m.Hash.Available() { - return "", ErrHashUnavailable - } - - hasher := m.Hash.New() - hasher.Write([]byte(signingString)) - - // Sign the string and return r, s - asn1Sig, err := signer.Sign(rand.Reader, hasher.Sum(nil), m.Hash) - if err != nil { - return "", err - } - - //the ecdsa.PrivateKey Sign function returns an asn1 encoded signature which is not what we want - // so we unmarshal it to get r and s to encode as described in rfc7518 section-3.4 - var ecdsaSig ecdsaSignature - rest, err := asn1.Unmarshal(asn1Sig, &ecdsaSig) - if err != nil { - return "", err - } - - if len(rest) != 0 { - return "", &UnverfiableTokenError{Message: "unexpected extra bytes in ecda signature"} - } - - curveBits := pub.Curve.Params().BitSize - - if m.CurveBits != curveBits { - return "", &InvalidKeyError{Message: "CurveBits in public key don't match those in signing method"} - } - - keyBytes := curveBits / 8 - if curveBits%8 > 0 { - keyBytes++ - } - - // We serialize the output (r and s) into big-endian byte arrays and pad - // them with zeros on the left to make sure the sizes work out. Both arrays - // must be keyBytes long, and the output must be 2*keyBytes long. - rBytes := ecdsaSig.R.Bytes() - rBytesPadded := make([]byte, keyBytes) - copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) - - sBytes := ecdsaSig.S.Bytes() - sBytesPadded := make([]byte, keyBytes) - copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) - - out := append(rBytesPadded, sBytesPadded...) - - return EncodeSegment(out), nil -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/errors.go b/vendor/github.com/dgrijalva/jwt-go/v4/errors.go deleted file mode 100644 index d7a53ba7..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/errors.go +++ /dev/null @@ -1,204 +0,0 @@ -package jwt - -import ( - "fmt" - "time" -) - -// Error constants -var ( - ErrHashUnavailable = new(HashUnavailableError) -) - -// Embeds b within a, if a is a valid wrapper. returns a -// If a is not a valid wrapper, b is dropped -// If one of the errors is nil, the other is returned -func wrapError(a, b error) error { - if b == nil { - return a - } - if a == nil { - return b - } - - type iErrorWrapper interface { - Wrap(error) - } - if w, ok := a.(iErrorWrapper); ok { - w.Wrap(b) - } - return a -} - -// ErrorWrapper provides a simple, concrete helper for implementing nestable errors -type ErrorWrapper struct{ err error } - -// Unwrap implements xerrors.Wrapper -func (w ErrorWrapper) Unwrap() error { - return w.err -} - -// Wrap stores the provided error value and returns it when Unwrap is called -func (w ErrorWrapper) Wrap(err error) { - w.err = err -} - -// InvalidKeyError is returned if the key is unusable for some reason other than type -type InvalidKeyError struct { - Message string - ErrorWrapper -} - -func (e *InvalidKeyError) Error() string { - return fmt.Sprintf("key is invalid: %v", e.Message) -} - -// InvalidKeyTypeError is returned if the key is unusable because it is of an incompatible type -type InvalidKeyTypeError struct { - Expected, Received string // String descriptions of expected and received types - ErrorWrapper -} - -func (e *InvalidKeyTypeError) Error() string { - if e.Expected == "" && e.Received == "" { - return "key is of invalid type" - } - return fmt.Sprintf("key is of invalid type: expected %v, received %v", e.Expected, e.Received) -} - -// NewInvalidKeyTypeError creates an InvalidKeyTypeError, automatically capturing the type -// of received -func NewInvalidKeyTypeError(expected string, received interface{}) error { - return &InvalidKeyTypeError{Expected: expected, Received: fmt.Sprintf("%T", received)} -} - -// MalformedTokenError means the token failed to parse or exhibits some other -// non-standard property that prevents it being processed by this library -type MalformedTokenError struct { - Message string - ErrorWrapper -} - -func (e *MalformedTokenError) Error() string { - if e.Message == "" { - return "token is malformed" - } - return fmt.Sprintf("token is malformed: %v", e.Message) -} - -// UnverfiableTokenError means there's something wrong with the signature that prevents -// this library from verifying it. -type UnverfiableTokenError struct { - Message string - ErrorWrapper -} - -func (e *UnverfiableTokenError) Error() string { - if e.Message == "" { - return "token is unverifiable" - } - return fmt.Sprintf("token is unverifiable: %v", e.Message) -} - -// InvalidSignatureError means the signature on the token is invalid -type InvalidSignatureError struct { - Message string - ErrorWrapper -} - -func (e *InvalidSignatureError) Error() string { - if e.Message == "" { - return "token signature is invalid" - } - return fmt.Sprintf("token signature is invalid: %v", e.Message) -} - -// TokenExpiredError allows the caller to know the delta between now and the expired time and the unvalidated claims. -// A client system may have a bug that doesn't refresh a token in time, or there may be clock skew so this information can help you understand. -type TokenExpiredError struct { - At time.Time // The time at which the exp was evaluated. Includes leeway. - ExpiredBy time.Duration // How long the token had been expired at time of evaluation - ErrorWrapper // Value for unwrapping -} - -func (e *TokenExpiredError) Error() string { - return fmt.Sprintf("token is expired by %v", e.ExpiredBy) -} - -// TokenNotValidYetError means the token failed the 'nbf' check. It's possible -// this token will become valid once the 'nbf' time is reached. If you are encountering -// this unexpectedly, you may want to provide a bit of Leeway to account for clock skew. See WithLeeway -type TokenNotValidYetError struct { - At time.Time // The time at which the exp was evaluated. Includes leeway. - EarlyBy time.Duration // How long the token had been expired at time of evaluation - ErrorWrapper // Value for unwrapping -} - -func (e *TokenNotValidYetError) Error() string { - return fmt.Sprintf("token is not valid yet; wait %v", e.EarlyBy) -} - -// InvalidAudienceError means the token failed the audience check -// per the spec, if an 'aud' claim is present, the value must be verified -// See: WithAudience and WithoutAudienceValidation -type InvalidAudienceError struct { - Message string - ErrorWrapper -} - -func (e *InvalidAudienceError) Error() string { - if e.Message == "" { - return "token audience is invalid" - } - return fmt.Sprintf("token audience is invalid: %v", e.Message) -} - -// InvalidIssuerError means the token failed issuer validation -// Issuer validation is only run, by default, if the WithIssuer option is provided -type InvalidIssuerError struct { - Message string - ErrorWrapper -} - -func (e *InvalidIssuerError) Error() string { - if e.Message == "" { - return "token issuer is invalid" - } - return fmt.Sprintf("token issuer is invalid: %v", e.Message) -} - -// InvalidClaimsError is a catchall type for claims errors that don't have their own type -type InvalidClaimsError struct { - Message string - ErrorWrapper -} - -func (e *InvalidClaimsError) Error() string { - if e.Message == "" { - return "token claim is invalid" - } - return fmt.Sprintf("token claim is invalid: %v", e.Message) -} - -// SigningError is a catchall type for signing errors -type SigningError struct { - Message string - ErrorWrapper -} - -func (e *SigningError) Error() string { - if e.Message == "" { - return "error encountered during signing" - } - return fmt.Sprintf("error encountered during signing: %v", e.Message) -} - -// HashUnavailableError measn the request hash function isn't available -// See: https://godoc.org/crypto#Hash.Available -type HashUnavailableError struct { - ErrorWrapper -} - -func (e *HashUnavailableError) Error() string { - return "the requested hash function is unavailable" -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/keyfunc.go b/vendor/github.com/dgrijalva/jwt-go/v4/keyfunc.go deleted file mode 100644 index 2f15a8fc..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/keyfunc.go +++ /dev/null @@ -1,21 +0,0 @@ -package jwt - -import "fmt" - -// Keyfunc is the type passed to Parse methods to supply -// the key for verification. The function receives the parsed, -// but unverified Token. This allows you to use properties in the -// Header of the token (such as `kid`) to identify which key to use. -type Keyfunc func(*Token) (interface{}, error) - -// KnownKeyfunc is a helper for generating a Keyfunc from a known -// signing method and key. If your implementation only supports a single signing method -// and key, this is for you. -func KnownKeyfunc(signingMethod SigningMethod, key interface{}) Keyfunc { - return func(t *Token) (interface{}, error) { - if signingMethod.Alg() != t.Header["alg"] { - return nil, fmt.Errorf("unexpected signing method: %v, expected: %v", t.Header["alg"], signingMethod.Alg()) - } - return key, nil - } -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/map_claims.go b/vendor/github.com/dgrijalva/jwt-go/v4/map_claims.go deleted file mode 100644 index d721c338..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/map_claims.go +++ /dev/null @@ -1,83 +0,0 @@ -package jwt - -// MapClaims is the Claims type that uses the map[string]interface{} for JSON decoding -// This is the default Claims type if you don't supply one -type MapClaims map[string]interface{} - -// VerifyAudience compares the aud claim against cmp. -func (m MapClaims) VerifyAudience(h *ValidationHelper, cmp string) error { - if aud, err := ParseClaimStrings(m["aud"]); err == nil && aud != nil { - return h.ValidateAudienceAgainst(aud, cmp) - } else if err != nil { - return &MalformedTokenError{Message: "couldn't parse 'aud' value"} - } - return nil -} - -// VerifyIssuer compares the iss claim against cmp. -func (m MapClaims) VerifyIssuer(h *ValidationHelper, cmp string) error { - iss, ok := m["iss"].(string) - if !ok { - return &InvalidIssuerError{Message: "'iss' expected but not present"} - } - return h.ValidateIssuerAgainst(iss, cmp) -} - -// Valid validates standard claims using ValidationHelper -// Validates time based claims "exp, nbf" (see: WithLeeway) -// Validates "aud" if present in claims. (see: WithAudience, WithoutAudienceValidation) -// Validates "iss" if option is provided (see: WithIssuer) -func (m MapClaims) Valid(h *ValidationHelper) error { - var vErr error - - if h == nil { - h = DefaultValidationHelper - } - - exp, err := m.LoadTimeValue("exp") - if err != nil { - return err - } - - if err = h.ValidateExpiresAt(exp); err != nil { - vErr = wrapError(err, vErr) - } - - nbf, err := m.LoadTimeValue("nbf") - if err != nil { - return err - } - - if err = h.ValidateNotBefore(nbf); err != nil { - vErr = wrapError(err, vErr) - } - - // Try to parse the 'aud' claim - if aud, err := ParseClaimStrings(m["aud"]); err == nil && aud != nil { - // If it's present and well formed, validate - if err = h.ValidateAudience(aud); err != nil { - vErr = wrapError(err, vErr) - } - } else if err != nil { - // If it's present and not well formed, return an error - return &MalformedTokenError{Message: "couldn't parse 'aud' value"} - } - - iss, _ := m["iss"].(string) - if err = h.ValidateIssuer(iss); err != nil { - vErr = wrapError(err, vErr) - } - - return vErr -} - -// LoadTimeValue extracts a *Time value from a key in m -func (m MapClaims) LoadTimeValue(key string) (*Time, error) { - value, ok := m[key] - if !ok { - // No value present in map - return nil, nil - } - - return ParseTime(value) -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/parser.go b/vendor/github.com/dgrijalva/jwt-go/v4/parser.go deleted file mode 100644 index 1f1a9c09..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/parser.go +++ /dev/null @@ -1,168 +0,0 @@ -package jwt - -import ( - "bytes" - "encoding/json" - "fmt" - "strings" -) - -// Parser is the type used to parse and validate a JWT token from string -type Parser struct { - validMethods []string // If populated, only these methods will be considered valid - useJSONNumber bool // Use JSON Number format in JSON decoder - skipClaimsValidation bool // Skip claims validation during token parsing - unmarshaller TokenUnmarshaller // Use this instead of encoding/json - *ValidationHelper -} - -// NewParser returns a new Parser with the specified options -func NewParser(options ...ParserOption) *Parser { - p := &Parser{ - ValidationHelper: new(ValidationHelper), - } - for _, option := range options { - option(p) - } - return p -} - -// Parse will parse, validate, and return a token. -// keyFunc will receive the parsed token and should return the key for validating. -// If everything is kosher, err will be nil -func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { - return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) -} - -// ParseWithClaims is just like parse, but with the claims type specified -func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { - token, parts, err := p.ParseUnverified(tokenString, claims) - if err != nil { - return token, err - } - - // Verify signing method is in the required set - if p.validMethods != nil { - var signingMethodValid = false - var alg = token.Method.Alg() - for _, m := range p.validMethods { - if m == alg { - signingMethodValid = true - break - } - } - if !signingMethodValid { - // signing method is not in the listed set - return token, &UnverfiableTokenError{Message: fmt.Sprintf("signing method %v is invalid", alg)} - } - } - - // Lookup key - var key interface{} - if keyFunc == nil { - // keyFunc was not provided. short circuiting validation - return token, &UnverfiableTokenError{Message: "no Keyfunc was provided."} - } - if key, err = keyFunc(token); err != nil { - // keyFunc returned an error - return token, wrapError(&UnverfiableTokenError{Message: "Keyfunc returned an error"}, err) - } - - var vErr error - - // Perform validation - token.Signature = parts[2] - if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { - vErr = wrapError(&InvalidSignatureError{}, err) - } - - // Validate Claims - if !p.skipClaimsValidation && vErr == nil { - if err := token.Claims.Valid(p.ValidationHelper); err != nil { - vErr = wrapError(err, vErr) - } - } - - if vErr == nil { - token.Valid = true - } - - return token, vErr -} - -// ParseUnverified is used to inspect a token without validating it -// WARNING: Don't use this method unless you know what you're doing -// -// This method parses the token but doesn't validate the signature. It's only -// ever useful in cases where you know the signature is valid (because it has -// been checked previously in the stack) and you want to extract values from -// it. Or for debuggery. -func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { - parts = strings.Split(tokenString, ".") - if len(parts) != 3 { - return nil, parts, &MalformedTokenError{Message: "token contains an invalid number of segments"} - } - - token = &Token{Raw: tokenString} - - // choose unmarshaller - var unmarshaller = p.unmarshaller - if unmarshaller == nil { - unmarshaller = p.defaultUnmarshaller - } - - // parse Header - var headerBytes []byte - if headerBytes, err = DecodeSegment(parts[0]); err != nil { - if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { - return token, parts, &MalformedTokenError{Message: "tokenstring should not contain 'bearer '"} - } - return token, parts, wrapError(&MalformedTokenError{Message: "failed to decode token header"}, err) - } - if err = unmarshaller(CodingContext{HeaderFieldDescriptor, nil}, headerBytes, &token.Header); err != nil { - return token, parts, wrapError(&MalformedTokenError{Message: "failed to unmarshal token header"}, err) - } - - // parse Claims - var claimBytes []byte - token.Claims = claims - - if claimBytes, err = DecodeSegment(parts[1]); err != nil { - return token, parts, wrapError(&MalformedTokenError{Message: "failed to decode token claims"}, err) - } - // JSON Decode. Special case for map type to avoid weird pointer behavior - ctx := CodingContext{ClaimsFieldDescriptor, token.Header} - if c, ok := token.Claims.(MapClaims); ok { - err = unmarshaller(ctx, claimBytes, &c) - } else { - err = unmarshaller(ctx, claimBytes, &claims) - } - // Handle decode error - if err != nil { - return token, parts, wrapError(&MalformedTokenError{Message: "failed to unmarshal token claims"}, err) - } - - // Lookup signature method - if method, ok := token.Header["alg"].(string); ok { - if token.Method = GetSigningMethod(method); token.Method == nil { - return token, parts, &UnverfiableTokenError{Message: "signing method (alg) is unavailable."} - } - } else { - return token, parts, &UnverfiableTokenError{Message: "signing method (alg) is unspecified."} - } - - return token, parts, nil -} - -func (p *Parser) defaultUnmarshaller(ctx CodingContext, data []byte, v interface{}) error { - // If we don't need a special parser, use Unmarshal - // We never use a special encoder for the header - if !p.useJSONNumber || ctx.FieldDescriptor == HeaderFieldDescriptor { - return json.Unmarshal(data, v) - } - - // To enable the JSONNumber mode, we must use Decoder instead of Unmarshal - dec := json.NewDecoder(bytes.NewBuffer(data)) - dec.UseNumber() - return dec.Decode(v) -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/parser_option.go b/vendor/github.com/dgrijalva/jwt-go/v4/parser_option.go deleted file mode 100644 index fd285ebe..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/parser_option.go +++ /dev/null @@ -1,74 +0,0 @@ -package jwt - -import "time" - -// ParserOption implements functional options for parser behavior -// see: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis -type ParserOption func(*Parser) - -// WithValidMethods returns the ParserOption for specifying valid signing methods -func WithValidMethods(valid []string) ParserOption { - return func(p *Parser) { - p.validMethods = valid - } -} - -// WithJSONNumber returns the ParserOption for using json.Number instead of float64 when parsing -// numeric values. Used most commonly with MapClaims, but it can be useful in some cases with -// structured claims types -func WithJSONNumber() ParserOption { - return func(p *Parser) { - p.useJSONNumber = true - } -} - -// WithoutClaimsValidation returns the ParserOption for disabling claims validation -// This does not disable signature validation. Use this if you want intend to implement -// claims validation via other means -func WithoutClaimsValidation() ParserOption { - return func(p *Parser) { - p.skipClaimsValidation = true - } -} - -// WithLeeway returns the ParserOption for specifying the leeway window. -func WithLeeway(d time.Duration) ParserOption { - return func(p *Parser) { - p.ValidationHelper.leeway = d - } -} - -// WithAudience returns the ParserOption for specifying an expected aud member value -func WithAudience(aud string) ParserOption { - return func(p *Parser) { - p.ValidationHelper.audience = &aud - } -} - -// WithoutAudienceValidation returns the ParserOption that specifies audience check should be skipped -func WithoutAudienceValidation() ParserOption { - return func(p *Parser) { - p.ValidationHelper.skipAudience = true - } -} - -// WithIssuer returns the ParserOption that specifies a value to compare against the iss claim -func WithIssuer(iss string) ParserOption { - return func(p *Parser) { - p.ValidationHelper.issuer = &iss - } -} - -// TokenUnmarshaller is the function signature required to supply custom JSON decoding logic. -// It is the same as json.Marshal with the addition of the FieldDescriptor. -// The field value will let your marshaller know which field is being processed. -// This is to facilitate things like compression, where you wouldn't want to compress -// the head. -type TokenUnmarshaller func(ctx CodingContext, data []byte, v interface{}) error - -// WithUnmarshaller returns the ParserOption that replaces the specified decoder -func WithUnmarshaller(um TokenUnmarshaller) ParserOption { - return func(p *Parser) { - p.unmarshaller = um - } -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/signing_method.go b/vendor/github.com/dgrijalva/jwt-go/v4/signing_method.go deleted file mode 100644 index 5e50243e..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/signing_method.go +++ /dev/null @@ -1,37 +0,0 @@ -package jwt - -import ( - "sync" -) - -var signingMethods = map[string]func() SigningMethod{} -var signingMethodLock = new(sync.RWMutex) - -// SigningMethod is the interface used for signing and verifying tokens -type SigningMethod interface { - Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid - Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error - Alg() string // returns the alg identifier for this method (example: 'HS256') -} - -// RegisterSigningMethod stores the "alg" name and a factory function pair -// used internally for looking up a signing method based on "alg". -// This is typically done during init() in the method's implementation -func RegisterSigningMethod(alg string, f func() SigningMethod) { - signingMethodLock.Lock() - defer signingMethodLock.Unlock() - - signingMethods[alg] = f -} - -// GetSigningMethod returns the signing method registered by RegisterSigningMethod -// This is used by the library internally during parsing and validation. -func GetSigningMethod(alg string) (method SigningMethod) { - signingMethodLock.RLock() - defer signingMethodLock.RUnlock() - - if methodF, ok := signingMethods[alg]; ok { - method = methodF() - } - return -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/signing_option.go b/vendor/github.com/dgrijalva/jwt-go/v4/signing_option.go deleted file mode 100644 index cecead29..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/signing_option.go +++ /dev/null @@ -1,38 +0,0 @@ -package jwt - -// CodingContext provides context to TokenMarshaller and TokenUnmarshaller -type CodingContext struct { - FieldDescriptor // Which field are we encoding/decoding? - Header map[string]interface{} // The token Header, if available -} - -// FieldDescriptor describes which field is being processed. Used by CodingContext -// This is to enable the marshaller to treat the head and body differently -type FieldDescriptor uint8 - -// Constants describe which field is being processed by custom Marshaller -const ( - HeaderFieldDescriptor FieldDescriptor = 0 - ClaimsFieldDescriptor FieldDescriptor = 1 -) - -// SigningOption can be passed to signing related methods on Token to customize behavior -type SigningOption func(*signingOptions) - -type signingOptions struct { - marshaller TokenMarshaller -} - -// TokenMarshaller is the interface you must implement to provide custom JSON marshalling -// behavior. It is the same as json.Marshal with the addition of the FieldDescriptor. -// The field value will let your marshaller know which field is being processed. -// This is to facilitate things like compression, where you wouldn't want to compress -// the head. -type TokenMarshaller func(ctx CodingContext, v interface{}) ([]byte, error) - -// WithMarshaller returns a SigningOption that will tell the signing code to use your custom Marshaller -func WithMarshaller(m TokenMarshaller) SigningOption { - return func(o *signingOptions) { - o.marshaller = m - } -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/time.go b/vendor/github.com/dgrijalva/jwt-go/v4/time.go deleted file mode 100644 index aee72aee..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/time.go +++ /dev/null @@ -1,78 +0,0 @@ -package jwt - -import ( - "encoding/json" - "reflect" - "time" -) - -// TimePrecision determines how precisely time is measured -// by this library. When serializing and deserialzing tokens, -// time values are automatically truncated to this precision. -// See the time package's Truncate method for more detail -const TimePrecision = time.Microsecond - -// Time is how this library represents time values. It's mostly -// a wrapper for the standard library's time.Time, but adds -// specialized JSON decoding behavior to interop with the way -// time is represented by JWT. Also makes it possible to represent -// nil values. -type Time struct { - time.Time -} - -// NewTime creates a new Time value from a float64, following -// the JWT spec. -func NewTime(t float64) *Time { - return At(time.Unix(0, int64(t*float64(time.Second)))) -} - -// Now returns a new Time value using the current time. -// You can override Now by changing the value of TimeFunc -func Now() *Time { - return At(TimeFunc()) -} - -// At makes a Time value from a standard library time.Time value -func At(at time.Time) *Time { - return &Time{at.Truncate(TimePrecision)} -} - -// ParseTime is used for creating a Time value from various -// possible representations that can occur in serialization. -func ParseTime(value interface{}) (*Time, error) { - switch v := value.(type) { - case int64: - return NewTime(float64(v)), nil - case float64: - return NewTime(v), nil - case json.Number: - vv, err := v.Float64() - if err != nil { - return nil, err - } - return NewTime(vv), nil - case nil: - return nil, nil - default: - return nil, &json.UnsupportedTypeError{Type: reflect.TypeOf(v)} - } -} - -// UnmarshalJSON implements the json package's Unmarshaler interface -func (t *Time) UnmarshalJSON(data []byte) error { - var value json.Number - err := json.Unmarshal(data, &value) - if err != nil { - return err - } - v, err := ParseTime(value) - *t = *v - return err -} - -// MarshalJSON implements the json package's Marshaler interface -func (t *Time) MarshalJSON() ([]byte, error) { - f := float64(t.Truncate(TimePrecision).UnixNano()) / float64(time.Second) - return json.Marshal(f) -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/token.go b/vendor/github.com/dgrijalva/jwt-go/v4/token.go deleted file mode 100644 index 5ab1eb55..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/token.go +++ /dev/null @@ -1,110 +0,0 @@ -package jwt - -import ( - "encoding/base64" - "encoding/json" - "strings" - "time" -) - -// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time). -// You can override it to use another time value. This is useful for testing or if your -// server uses a different time zone than your tokens. -var TimeFunc = time.Now - -// Token represents JWT Token. Different fields will be used depending on whether you're -// creating or parsing/verifying a token. -type Token struct { - Raw string // The raw token. Populated when you Parse a token - Method SigningMethod // The signing method used or to be used - Header map[string]interface{} // The first segment of the token - Claims Claims // The second segment of the token - Signature string // The third segment of the token. Populated when you Parse a token - Valid bool // Is the token valid? Populated when you Parse/Verify a token -} - -// New creates a new Token. Takes a signing method. Uses the default claims type, MapClaims. -func New(method SigningMethod) *Token { - return NewWithClaims(method, MapClaims{}) -} - -// NewWithClaims creats a new token with a specified signing method and claims type -func NewWithClaims(method SigningMethod, claims Claims) *Token { - return &Token{ - Header: map[string]interface{}{ - "typ": "JWT", - "alg": method.Alg(), - }, - Claims: claims, - Method: method, - } -} - -// SignedString returns the complete, signed token -func (t *Token) SignedString(key interface{}, opts ...SigningOption) (string, error) { - var sig, sstr string - var err error - if sstr, err = t.SigningString(opts...); err != nil { - return "", err - } - if sig, err = t.Method.Sign(sstr, key); err != nil { - return "", err - } - return strings.Join([]string{sstr, sig}, "."), nil -} - -// SigningString generates the signing string. This is the -// most expensive part of the whole deal. Unless you -// need this for something special, just go straight for -// the SignedString. -func (t *Token) SigningString(opts ...SigningOption) (string, error) { - // Process options - var cfg = new(signingOptions) - for _, opt := range opts { - opt(cfg) - } - // Setup default marshaller - if cfg.marshaller == nil { - cfg.marshaller = t.defaultMarshaller - } - - // Encode the two parts, then combine - inputParts := []interface{}{t.Header, t.Claims} - parts := make([]string, 2) - for i, v := range inputParts { - ctx := CodingContext{FieldDescriptor(i), t.Header} - jsonValue, err := cfg.marshaller(ctx, v) - if err != nil { - return "", err - } - parts[i] = EncodeSegment(jsonValue) - } - return strings.Join(parts, "."), nil -} - -func (t *Token) defaultMarshaller(ctx CodingContext, v interface{}) ([]byte, error) { - return json.Marshal(v) -} - -// Parse then validate, and return a token. -// keyFunc will receive the parsed token and should return the key for validating. -// If everything is kosher, err will be nil -// Claims type will be the default, MapClaims -func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { - return NewParser(options...).Parse(tokenString, keyFunc) -} - -// ParseWithClaims is Parse, but with a specified Claims type -func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { - return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc) -} - -// EncodeSegment is used internally for JWT specific base64url encoding with padding stripped -func EncodeSegment(seg []byte) string { - return base64.RawURLEncoding.EncodeToString(seg) -} - -// DecodeSegment is used internally for JWT specific base64url encoding with padding stripped -func DecodeSegment(seg string) ([]byte, error) { - return base64.RawURLEncoding.DecodeString(seg) -} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/validation_helper.go b/vendor/github.com/dgrijalva/jwt-go/v4/validation_helper.go deleted file mode 100644 index 2edd0a46..00000000 --- a/vendor/github.com/dgrijalva/jwt-go/v4/validation_helper.go +++ /dev/null @@ -1,151 +0,0 @@ -package jwt - -import ( - "crypto/subtle" - "fmt" - "time" -) - -// DefaultValidationHelper is used by Claims.Valid if none is provided -var DefaultValidationHelper = &ValidationHelper{} - -// ValidationHelper is built by the parser and passed -// to Claims.Value to carry parse/validation options -// This standalone type exists to allow implementations to do whatever custom -// behavior is required while still being able to call upon the standard behavior -// as necessary. -type ValidationHelper struct { - nowFunc func() time.Time // Override for time.Now. Mostly used for testing - leeway time.Duration // Leeway to provide when validating time values - audience *string // Expected audience value - skipAudience bool // Ignore aud check - issuer *string // Expected issuer value. ignored if nil -} - -// NewValidationHelper creates a validation helper from a list of parser options -// Not all parser options will impact validation -// If you already have a custom parser, you can use its ValidationHelper value -// instead of creating a new one -func NewValidationHelper(options ...ParserOption) *ValidationHelper { - p := NewParser(options...) - return p.ValidationHelper -} - -func (h *ValidationHelper) now() time.Time { - if h.nowFunc != nil { - return h.nowFunc() - } - return TimeFunc() -} - -// Before returns true if Now is before t -// Takes leeway into account -func (h *ValidationHelper) Before(t time.Time) bool { - return h.now().Before(t.Add(-h.leeway)) -} - -// After returns true if Now is after t -// Takes leeway into account -func (h *ValidationHelper) After(t time.Time) bool { - return h.now().After(t.Add(h.leeway)) -} - -// ValidateExpiresAt returns an error if the expiration time is invalid -// Takes leeway into account -func (h *ValidationHelper) ValidateExpiresAt(exp *Time) error { - // 'exp' claim is not set. ignore. - if exp == nil { - return nil - } - - // Expiration has passed - if h.After(exp.Time) { - delta := h.now().Sub(exp.Time) - return &TokenExpiredError{At: h.now(), ExpiredBy: delta} - } - - // Expiration has not passed - return nil -} - -// ValidateNotBefore returns an error if the nbf time has not been reached -// Takes leeway into account -func (h *ValidationHelper) ValidateNotBefore(nbf *Time) error { - // 'nbf' claim is not set. ignore. - if nbf == nil { - return nil - } - - // Nbf hasn't been reached - if h.Before(nbf.Time) { - delta := nbf.Time.Sub(h.now()) - return &TokenNotValidYetError{At: h.now(), EarlyBy: delta} - } - // Nbf has been reached. valid. - return nil -} - -// ValidateAudience verifies that aud contains the audience value provided -// by the WithAudience option. -// Per the spec (https://tools.ietf.org/html/rfc7519#section-4.1.3), if the aud -// claim is present, -func (h *ValidationHelper) ValidateAudience(aud ClaimStrings) error { - // Skip flag - if h.skipAudience { - return nil - } - - // If there's no audience claim, ignore - if aud == nil || len(aud) == 0 { - return nil - } - - // If there is an audience claim, but no value provided, fail - if h.audience == nil { - return &InvalidAudienceError{Message: "audience value was expected but not provided"} - } - - return h.ValidateAudienceAgainst(aud, *h.audience) -} - -// ValidateAudienceAgainst checks that the compare value is included in the aud list -// It is used by ValidateAudience, but exposed as a helper for other implementations -func (h *ValidationHelper) ValidateAudienceAgainst(aud ClaimStrings, compare string) error { - if aud == nil { - return nil - } - - // Compare provided value with aud claim. - // This code avoids the early return to make this check more or less constant time. - // I'm not certain that's actually required in this context. - var match = false - for _, audStr := range aud { - if subtle.ConstantTimeCompare([]byte(audStr), []byte(compare)) == 1 { - match = true - } - } - if !match { - return &InvalidAudienceError{Message: fmt.Sprintf("'%v' wasn't found in aud claim", compare)} - } - return nil - -} - -// ValidateIssuer checks the claim value against the value provided by WithIssuer -func (h *ValidationHelper) ValidateIssuer(iss string) error { - // Always passes validation if issuer is not provided - if h.issuer == nil { - return nil - } - - return h.ValidateIssuerAgainst(iss, *h.issuer) -} - -// ValidateIssuerAgainst checks the claim value against the value provided, ignoring the WithIssuer value -func (h *ValidationHelper) ValidateIssuerAgainst(iss string, compare string) error { - if subtle.ConstantTimeCompare([]byte(iss), []byte(compare)) == 1 { - return nil - } - - return &InvalidIssuerError{Message: "'iss' value doesn't match expectation"} -} diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml index ba95cdd1..ac12e485 100644 --- a/vendor/github.com/dustin/go-humanize/.travis.yml +++ b/vendor/github.com/dustin/go-humanize/.travis.yml @@ -1,12 +1,12 @@ sudo: false language: go +go_import_path: github.com/dustin/go-humanize go: - - 1.3.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x + - 1.13.x + - 1.14.x + - 1.15.x + - 1.16.x + - stable - master matrix: allow_failures: @@ -15,7 +15,7 @@ matrix: install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . + - go vet . + - go install -v -race ./... - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown index 91b4ae56..7d0b16b3 100644 --- a/vendor/github.com/dustin/go-humanize/README.markdown +++ b/vendor/github.com/dustin/go-humanize/README.markdown @@ -5,7 +5,7 @@ Just a few functions for helping humanize times and sizes. `go get` it as `github.com/dustin/go-humanize`, import it as `"github.com/dustin/go-humanize"`, use it as `humanize`. -See [godoc](https://godoc.org/github.com/dustin/go-humanize) for +See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for complete documentation. ## Sizes diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go index 1a2bf617..3b015fd5 100644 --- a/vendor/github.com/dustin/go-humanize/bigbytes.go +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go @@ -28,6 +28,10 @@ var ( BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) // BigYiByte is 1,024 z bytes in bit.Ints BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) + // BigRiByte is 1,024 y bytes in bit.Ints + BigRiByte = (&big.Int{}).Mul(BigYiByte, bigIECExp) + // BigQiByte is 1,024 r bytes in bit.Ints + BigQiByte = (&big.Int{}).Mul(BigRiByte, bigIECExp) ) var ( @@ -51,6 +55,10 @@ var ( BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) // BigYByte is 1,000 SI z bytes in big.Ints BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) + // BigRByte is 1,000 SI y bytes in big.Ints + BigRByte = (&big.Int{}).Mul(BigYByte, bigSIExp) + // BigQByte is 1,000 SI r bytes in big.Ints + BigQByte = (&big.Int{}).Mul(BigRByte, bigSIExp) ) var bigBytesSizeTable = map[string]*big.Int{ @@ -71,6 +79,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zb": BigZByte, "yib": BigYiByte, "yb": BigYByte, + "rib": BigRiByte, + "rb": BigRByte, + "qib": BigQiByte, + "qb": BigQByte, // Without suffix "": BigByte, "ki": BigKiByte, @@ -89,6 +101,10 @@ var bigBytesSizeTable = map[string]*big.Int{ "zi": BigZiByte, "y": BigYByte, "yi": BigYiByte, + "r": BigRByte, + "ri": BigRiByte, + "q": BigQByte, + "qi": BigQiByte, } var ten = big.NewInt(10) @@ -115,7 +131,7 @@ func humanateBigBytes(s, base *big.Int, sizes []string) string { // // BigBytes(82854982) -> 83 MB func BigBytes(s *big.Int) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB"} return humanateBigBytes(s, bigSIExp, sizes) } @@ -125,7 +141,7 @@ func BigBytes(s *big.Int) string { // // BigIBytes(82854982) -> 79 MiB func BigIBytes(s *big.Int) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"} return humanateBigBytes(s, bigIECExp, sizes) } diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go index 620690de..2bc83a03 100644 --- a/vendor/github.com/dustin/go-humanize/commaf.go +++ b/vendor/github.com/dustin/go-humanize/commaf.go @@ -1,3 +1,4 @@ +//go:build go1.6 // +build go1.6 package humanize diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go index 1c62b640..bce923f3 100644 --- a/vendor/github.com/dustin/go-humanize/ftoa.go +++ b/vendor/github.com/dustin/go-humanize/ftoa.go @@ -6,6 +6,9 @@ import ( ) func stripTrailingZeros(s string) string { + if !strings.ContainsRune(s, '.') { + return s + } offset := len(s) - 1 for offset > 0 { if s[offset] == '.' { diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go index dec61865..6470d0d4 100644 --- a/vendor/github.com/dustin/go-humanize/number.go +++ b/vendor/github.com/dustin/go-humanize/number.go @@ -73,7 +73,7 @@ func FormatFloat(format string, n float64) string { if n > math.MaxFloat64 { return "Infinity" } - if n < -math.MaxFloat64 { + if n < (0.0 - math.MaxFloat64) { return "-Infinity" } diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go index ae659e0e..8b850198 100644 --- a/vendor/github.com/dustin/go-humanize/si.go +++ b/vendor/github.com/dustin/go-humanize/si.go @@ -8,6 +8,8 @@ import ( ) var siPrefixTable = map[float64]string{ + -30: "q", // quecto + -27: "r", // ronto -24: "y", // yocto -21: "z", // zepto -18: "a", // atto @@ -25,6 +27,8 @@ var siPrefixTable = map[float64]string{ 18: "E", // exa 21: "Z", // zetta 24: "Y", // yotta + 27: "R", // ronna + 30: "Q", // quetta } var revSIPrefixTable = revfmap(siPrefixTable) diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/.gitignore b/vendor/github.com/golang-jwt/jwt/v5/.gitignore similarity index 68% rename from vendor/github.com/dgrijalva/jwt-go/v4/.gitignore rename to vendor/github.com/golang-jwt/jwt/v5/.gitignore index 80bed650..09573e01 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/.gitignore +++ b/vendor/github.com/golang-jwt/jwt/v5/.gitignore @@ -1,4 +1,4 @@ .DS_Store bin - +.idea/ diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/LICENSE b/vendor/github.com/golang-jwt/jwt/v5/LICENSE similarity index 96% rename from vendor/github.com/dgrijalva/jwt-go/v4/LICENSE rename to vendor/github.com/golang-jwt/jwt/v5/LICENSE index df83a9c2..35dbc252 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/LICENSE +++ b/vendor/github.com/golang-jwt/jwt/v5/LICENSE @@ -1,4 +1,5 @@ Copyright (c) 2012 Dave Grijalva +Copyright (c) 2021 golang-jwt maintainers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md b/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md new file mode 100644 index 00000000..ff9c57e1 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/MIGRATION_GUIDE.md @@ -0,0 +1,195 @@ +# Migration Guide (v5.0.0) + +Version `v5` contains a major rework of core functionalities in the `jwt-go` +library. This includes support for several validation options as well as a +re-design of the `Claims` interface. Lastly, we reworked how errors work under +the hood, which should provide a better overall developer experience. + +Starting from [v5.0.0](https://github.com/golang-jwt/jwt/releases/tag/v5.0.0), +the import path will be: + + "github.com/golang-jwt/jwt/v5" + +For most users, changing the import path *should* suffice. However, since we +intentionally changed and cleaned some of the public API, existing programs +might need to be updated. The following sections describe significant changes +and corresponding updates for existing programs. + +## Parsing and Validation Options + +Under the hood, a new `Validator` struct takes care of validating the claims. A +long awaited feature has been the option to fine-tune the validation of tokens. +This is now possible with several `ParserOption` functions that can be appended +to most `Parse` functions, such as `ParseWithClaims`. The most important options +and changes are: + * Added `WithLeeway` to support specifying the leeway that is allowed when + validating time-based claims, such as `exp` or `nbf`. + * Changed default behavior to not check the `iat` claim. Usage of this claim + is OPTIONAL according to the JWT RFC. The claim itself is also purely + informational according to the RFC, so a strict validation failure is not + recommended. If you want to check for sensible values in these claims, + please use the `WithIssuedAt` parser option. + * Added `WithAudience`, `WithSubject` and `WithIssuer` to support checking for + expected `aud`, `sub` and `iss`. + * Added `WithStrictDecoding` and `WithPaddingAllowed` options to allow + previously global settings to enable base64 strict encoding and the parsing + of base64 strings with padding. The latter is strictly speaking against the + standard, but unfortunately some of the major identity providers issue some + of these incorrect tokens. Both options are disabled by default. + +## Changes to the `Claims` interface + +### Complete Restructuring + +Previously, the claims interface was satisfied with an implementation of a +`Valid() error` function. This had several issues: + * The different claim types (struct claims, map claims, etc.) then contained + similar (but not 100 % identical) code of how this validation was done. This + lead to a lot of (almost) duplicate code and was hard to maintain + * It was not really semantically close to what a "claim" (or a set of claims) + really is; which is a list of defined key/value pairs with a certain + semantic meaning. + +Since all the validation functionality is now extracted into the validator, all +`VerifyXXX` and `Valid` functions have been removed from the `Claims` interface. +Instead, the interface now represents a list of getters to retrieve values with +a specific meaning. This allows us to completely decouple the validation logic +with the underlying storage representation of the claim, which could be a +struct, a map or even something stored in a database. + +```go +type Claims interface { + GetExpirationTime() (*NumericDate, error) + GetIssuedAt() (*NumericDate, error) + GetNotBefore() (*NumericDate, error) + GetIssuer() (string, error) + GetSubject() (string, error) + GetAudience() (ClaimStrings, error) +} +``` + +Users that previously directly called the `Valid` function on their claims, +e.g., to perform validation independently of parsing/verifying a token, can now +use the `jwt.NewValidator` function to create a `Validator` independently of the +`Parser`. + +```go +var v = jwt.NewValidator(jwt.WithLeeway(5*time.Second)) +v.Validate(myClaims) +``` + +### Supported Claim Types and Removal of `StandardClaims` + +The two standard claim types supported by this library, `MapClaims` and +`RegisteredClaims` both implement the necessary functions of this interface. The +old `StandardClaims` struct, which has already been deprecated in `v4` is now +removed. + +Users using custom claims, in most cases, will not experience any changes in the +behavior as long as they embedded `RegisteredClaims`. If they created a new +claim type from scratch, they now need to implemented the proper getter +functions. + +### Migrating Application Specific Logic of the old `Valid` + +Previously, users could override the `Valid` method in a custom claim, for +example to extend the validation with application-specific claims. However, this +was always very dangerous, since once could easily disable the standard +validation and signature checking. + +In order to avoid that, while still supporting the use-case, a new +`ClaimsValidator` interface has been introduced. This interface consists of the +`Validate() error` function. If the validator sees, that a `Claims` struct +implements this interface, the errors returned to the `Validate` function will +be *appended* to the regular standard validation. It is not possible to disable +the standard validation anymore (even only by accident). + +Usage examples can be found in [example_test.go](./example_test.go), to build +claims structs like the following. + +```go +// MyCustomClaims includes all registered claims, plus Foo. +type MyCustomClaims struct { + Foo string `json:"foo"` + jwt.RegisteredClaims +} + +// Validate can be used to execute additional application-specific claims +// validation. +func (m MyCustomClaims) Validate() error { + if m.Foo != "bar" { + return errors.New("must be foobar") + } + + return nil +} +``` + +## Changes to the `Token` and `Parser` struct + +The previously global functions `DecodeSegment` and `EncodeSegment` were moved +to the `Parser` and `Token` struct respectively. This will allow us in the +future to configure the behavior of these two based on options supplied on the +parser or the token (creation). This also removes two previously global +variables and moves them to parser options `WithStrictDecoding` and +`WithPaddingAllowed`. + +In order to do that, we had to adjust the way signing methods work. Previously +they were given a base64 encoded signature in `Verify` and were expected to +return a base64 encoded version of the signature in `Sign`, both as a `string`. +However, this made it necessary to have `DecodeSegment` and `EncodeSegment` +global and was a less than perfect design because we were repeating +encoding/decoding steps for all signing methods. Now, `Sign` and `Verify` +operate on a decoded signature as a `[]byte`, which feels more natural for a +cryptographic operation anyway. Lastly, `Parse` and `SignedString` take care of +the final encoding/decoding part. + +In addition to that, we also changed the `Signature` field on `Token` from a +`string` to `[]byte` and this is also now populated with the decoded form. This +is also more consistent, because the other parts of the JWT, mainly `Header` and +`Claims` were already stored in decoded form in `Token`. Only the signature was +stored in base64 encoded form, which was redundant with the information in the +`Raw` field, which contains the complete token as base64. + +```go +type Token struct { + Raw string // Raw contains the raw token + Method SigningMethod // Method is the signing method used or to be used + Header map[string]interface{} // Header is the first segment of the token in decoded form + Claims Claims // Claims is the second segment of the token in decoded form + Signature []byte // Signature is the third segment of the token in decoded form + Valid bool // Valid specifies if the token is valid +} +``` + +Most (if not all) of these changes should not impact the normal usage of this +library. Only users directly accessing the `Signature` field as well as +developers of custom signing methods should be affected. + +# Migration Guide (v4.0.0) + +Starting from [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0), +the import path will be: + + "github.com/golang-jwt/jwt/v4" + +The `/v4` version will be backwards compatible with existing `v3.x.y` tags in +this repo, as well as `github.com/dgrijalva/jwt-go`. For most users this should +be a drop-in replacement, if you're having troubles migrating, please open an +issue. + +You can replace all occurrences of `github.com/dgrijalva/jwt-go` or +`github.com/golang-jwt/jwt` with `github.com/golang-jwt/jwt/v4`, either manually +or by using tools such as `sed` or `gofmt`. + +And then you'd typically run: + +``` +go get github.com/golang-jwt/jwt/v4 +go mod tidy +``` + +# Older releases (before v3.2.0) + +The original migration guide for older releases can be found at +https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md. diff --git a/vendor/github.com/golang-jwt/jwt/v5/README.md b/vendor/github.com/golang-jwt/jwt/v5/README.md new file mode 100644 index 00000000..964598a3 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/README.md @@ -0,0 +1,167 @@ +# jwt-go + +[![build](https://github.com/golang-jwt/jwt/actions/workflows/build.yml/badge.svg)](https://github.com/golang-jwt/jwt/actions/workflows/build.yml) +[![Go +Reference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt/v5.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt/v5) +[![Coverage Status](https://coveralls.io/repos/github/golang-jwt/jwt/badge.svg?branch=main)](https://coveralls.io/github/golang-jwt/jwt?branch=main) + +A [go](http://www.golang.org) (or 'golang' for search engine friendliness) +implementation of [JSON Web +Tokens](https://datatracker.ietf.org/doc/html/rfc7519). + +Starting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0) +this project adds Go module support, but maintains backwards compatibility with +older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`. See the +[`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information. Version +v5.0.0 introduces major improvements to the validation of tokens, but is not +entirely backwards compatible. + +> After the original author of the library suggested migrating the maintenance +> of `jwt-go`, a dedicated team of open source maintainers decided to clone the +> existing library into this repository. See +> [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a +> detailed discussion on this topic. + + +**SECURITY NOTICE:** Some older versions of Go have a security issue in the +crypto/elliptic. Recommendation is to upgrade to at least 1.15 See issue +[dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more +detail. + +**SECURITY NOTICE:** It's important that you [validate the `alg` presented is +what you +expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). +This library attempts to make it easy to do the right thing by requiring key +types match the expected alg, but you should take the extra step to verify it in +your usage. See the examples provided. + +### Supported Go versions + +Our support of Go versions is aligned with Go's [version release +policy](https://golang.org/doc/devel/release#policy). So we will support a major +version of Go until there are two newer major releases. We no longer support +building jwt-go with unsupported Go versions, as these contain security +vulnerabilities which will not be fixed. + +## What the heck is a JWT? + +JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web +Tokens. + +In short, it's a signed JSON object that does something useful (for example, +authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is +made of three parts, separated by `.`'s. The first two parts are JSON objects, +that have been [base64url](https://datatracker.ietf.org/doc/html/rfc4648) +encoded. The last part is the signature, encoded the same way. + +The first part is called the header. It contains the necessary information for +verifying the last part, the signature. For example, which encryption method +was used for signing and what key was used. + +The part in the middle is the interesting bit. It's called the Claims and +contains the actual stuff you care about. Refer to [RFC +7519](https://datatracker.ietf.org/doc/html/rfc7519) for information about +reserved keys and the proper way to add your own. + +## What's in the box? + +This library supports the parsing and verification as well as the generation and +signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, +RSA-PSS, and ECDSA, though hooks are present for adding your own. + +## Installation Guidelines + +1. To install the jwt package, you first need to have + [Go](https://go.dev/doc/install) installed, then you can use the command + below to add `jwt-go` as a dependency in your Go program. + +```sh +go get -u github.com/golang-jwt/jwt/v5 +``` + +2. Import it in your code: + +```go +import "github.com/golang-jwt/jwt/v5" +``` + +## Usage + +A detailed usage guide, including how to sign and verify tokens can be found on +our [documentation website](https://golang-jwt.github.io/jwt/usage/create/). + +## Examples + +See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt/v5) +for examples of usage: + +* [Simple example of parsing and validating a + token](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-Parse-Hmac) +* [Simple example of building and signing a + token](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-New-Hmac) +* [Directory of + Examples](https://pkg.go.dev/github.com/golang-jwt/jwt/v5#pkg-examples) + +## Compliance + +This library was last reviewed to comply with [RFC +7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few +notable differences: + +* In order to protect against accidental use of [Unsecured + JWTs](https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens using + `alg=none` will only be accepted if the constant + `jwt.UnsafeAllowNoneSignatureType` is provided as the key. + +## Project Status & Versioning + +This library is considered production ready. Feedback and feature requests are +appreciated. The API should be considered stable. There should be very few +backwards-incompatible changes outside of major version updates (and only with +good reason). + +This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull +requests will land on `main`. Periodically, versions will be tagged from +`main`. You can find all the releases on [the project releases +page](https://github.com/golang-jwt/jwt/releases). + +**BREAKING CHANGES:*** A full list of breaking changes is available in +`VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating +your code. + +## Extensions + +This library publishes all the necessary components for adding your own signing +methods or key functions. Simply implement the `SigningMethod` interface and +register a factory method using `RegisterSigningMethod` or provide a +`jwt.Keyfunc`. + +A common use case would be integrating with different 3rd party signature +providers, like key management services from various cloud providers or Hardware +Security Modules (HSMs) or to implement additional standards. + +| Extension | Purpose | Repo | +| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------ | +| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | +| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | +| JWKS | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc | + +*Disclaimer*: Unless otherwise specified, these integrations are maintained by +third parties and should not be considered as a primary offer by any of the +mentioned cloud providers + +## More + +Go package documentation can be found [on +pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt/v5). Additional +documentation can be found on [our project +page](https://golang-jwt.github.io/jwt/). + +The command line utility included in this project (cmd/jwt) provides a +straightforward example of token creation and parsing as well as a useful tool +for debugging your own integration. You'll also find several implementation +examples in the documentation. + +[golang-jwt](https://github.com/orgs/golang-jwt) incorporates a modified version +of the JWT logo, which is distributed under the terms of the [MIT +License](https://github.com/jsonwebtoken/jsonwebtoken.github.io/blob/master/LICENSE.txt). diff --git a/vendor/github.com/golang-jwt/jwt/v5/SECURITY.md b/vendor/github.com/golang-jwt/jwt/v5/SECURITY.md new file mode 100644 index 00000000..b08402c3 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +As of February 2022 (and until this document is updated), the latest version `v4` is supported. + +## Reporting a Vulnerability + +If you think you found a vulnerability, and even if you are not sure, please report it to jwt-go-security@googlegroups.com or one of the other [golang-jwt maintainers](https://github.com/orgs/golang-jwt/people). Please try be explicit, describe steps to reproduce the security issue with code example(s). + +You will receive a response within a timely manner. If the issue is confirmed, we will do our best to release a patch as soon as possible given the complexity of the problem. + +## Public Discussions + +Please avoid publicly discussing a potential security vulnerability. + +Let's take this offline and find a solution first, this limits the potential impact as much as possible. + +We appreciate your help! diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/VERSION_HISTORY.md b/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md similarity index 68% rename from vendor/github.com/dgrijalva/jwt-go/v4/VERSION_HISTORY.md rename to vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md index fffaa37f..b5039e49 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/VERSION_HISTORY.md +++ b/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md @@ -1,27 +1,23 @@ -## `jwt-go` Version History - -#### 4.0.0 - -* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code - * Errors have been updated significantly to take advantage of the changes to errors in go1.13/go2/xerrors - * The previous 'enum' describing error types has been replaced with unique types for every kind of error - * The new error types carry more information and interoperate properly with errors.As - * Dropping (official) support for go1.10 or older. Older versions dating back to 1.4 **may** continue to work, but are not being considered or tested against. 1.3 and previous will no longer work with this library. - * Behavior of time values has changed significantly, primarily to handle nil values, but also to be more consistent with Go: - * All time values used by the library are expressed using time.Time and time.Duration. This includes automatic parsing and encoding of the JWT unix timestamp format. - * `StandardClaims` time values use the new `Time` type, which wraps time.Time to handle things nil values gracefully - * The method for describing custom parsing/validating behaviors has changed. The properties exposed on Parser have been replaced with `ParserOption`s. See https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis for the theory. See `ParserOption` and `SigningOption` for more details - * Per the spec, if the `aud` claim is present, it will be automatically validated. See `WithAudience` and `WithoutAudienceValidation` - * The `Valid` method on `Claims` now takes an argument, `ValidationHelper`. -* Added support for Leeway. If you have issues with clock sku between the issuing machine and the consuming machine, you can allow for a grace period. See `WithLeeway` -* Added support for custom JSON encoder/decoders. See `WithMarshaller` and `WithUnmarshaller` -* Added support for issuer validation. See `WithIssuer` -* Updated error messages and comments to comply with linter recommendations -* Added `KnownKeyfunc` for when you know both the signing method and the key without needing to look at the token Header. This should dramatically simplify many common use cases. -* Added `ValidationHelper` to make it easier to implement custom Claims types without having to rewrite a bunch of built-in behavior, such as time comparison and leeway. `ValidationHelper` is built with `ParserOptions` and provides the same methods used by built in claims types to handle validation. -* Added support for `crypto.Signer` on several signing methods. This was a common request. -* Added new type, `ClaimStrings`, which will correctly handle properties such as `aud` that can be either an array of strings or a single string. `ClaimStrings` is an alias to `[]string`, but with custom decoding behavior. This means all the built in `aud` validation behavior now expects `[]string` instead of `string`. This was a common request. +# `jwt-go` Version History +The following version history is kept for historic purposes. To retrieve the current changes of each version, please refer to the change-log of the specific release versions on https://github.com/golang-jwt/jwt/releases. + +## 4.0.0 + +* Introduces support for Go modules. The `v4` version will be backwards compatible with `v3.x.y`. + +## 3.2.2 + +* Starting from this release, we are adopting the policy to support the most 2 recent versions of Go currently available. By the time of this release, this is Go 1.15 and 1.16 ([#28](https://github.com/golang-jwt/jwt/pull/28)). +* Fixed a potential issue that could occur when the verification of `exp`, `iat` or `nbf` was not required and contained invalid contents, i.e. non-numeric/date. Thanks for @thaJeztah for making us aware of that and @giorgos-f3 for originally reporting it to the formtech fork ([#40](https://github.com/golang-jwt/jwt/pull/40)). +* Added support for EdDSA / ED25519 ([#36](https://github.com/golang-jwt/jwt/pull/36)). +* Optimized allocations ([#33](https://github.com/golang-jwt/jwt/pull/33)). + +## 3.2.1 + +* **Import Path Change**: See MIGRATION_GUIDE.md for tips on updating your code + * Changed the import path from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt` +* Fixed type confusing issue between `string` and `[]string` in `VerifyAudience` ([#12](https://github.com/golang-jwt/jwt/pull/12)). This fixes CVE-2020-26160 #### 3.2.0 @@ -123,19 +119,19 @@ It is likely the only integration change required here will be to change `func(t * Refactored the RSA implementation to be easier to read * Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM` -#### 1.0.2 +## 1.0.2 * Fixed bug in parsing public keys from certificates * Added more tests around the parsing of keys for RS256 * Code refactoring in RS256 implementation. No functional changes -#### 1.0.1 +## 1.0.1 * Fixed panic if RS256 signing method was passed an invalid key -#### 1.0.0 +## 1.0.0 * First versioned release * API stabilized * Supports creating, signing, parsing, and validating JWT tokens -* Supports RS256 and HS256 signing methods \ No newline at end of file +* Supports RS256 and HS256 signing methods diff --git a/vendor/github.com/golang-jwt/jwt/v5/claims.go b/vendor/github.com/golang-jwt/jwt/v5/claims.go new file mode 100644 index 00000000..d50ff3da --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/claims.go @@ -0,0 +1,16 @@ +package jwt + +// Claims represent any form of a JWT Claims Set according to +// https://datatracker.ietf.org/doc/html/rfc7519#section-4. In order to have a +// common basis for validation, it is required that an implementation is able to +// supply at least the claim names provided in +// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 namely `exp`, +// `iat`, `nbf`, `iss`, `sub` and `aud`. +type Claims interface { + GetExpirationTime() (*NumericDate, error) + GetIssuedAt() (*NumericDate, error) + GetNotBefore() (*NumericDate, error) + GetIssuer() (string, error) + GetSubject() (string, error) + GetAudience() (ClaimStrings, error) +} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/doc.go b/vendor/github.com/golang-jwt/jwt/v5/doc.go similarity index 100% rename from vendor/github.com/dgrijalva/jwt-go/v4/doc.go rename to vendor/github.com/golang-jwt/jwt/v5/doc.go diff --git a/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go b/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go new file mode 100644 index 00000000..ca85659b --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/ecdsa.go @@ -0,0 +1,134 @@ +package jwt + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "errors" + "math/big" +) + +var ( + // Sadly this is missing from crypto/ecdsa compared to crypto/rsa + ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") +) + +// SigningMethodECDSA implements the ECDSA family of signing methods. +// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification +type SigningMethodECDSA struct { + Name string + Hash crypto.Hash + KeySize int + CurveBits int +} + +// Specific instances for EC256 and company +var ( + SigningMethodES256 *SigningMethodECDSA + SigningMethodES384 *SigningMethodECDSA + SigningMethodES512 *SigningMethodECDSA +) + +func init() { + // ES256 + SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} + RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { + return SigningMethodES256 + }) + + // ES384 + SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} + RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { + return SigningMethodES384 + }) + + // ES512 + SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} + RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { + return SigningMethodES512 + }) +} + +func (m *SigningMethodECDSA) Alg() string { + return m.Name +} + +// Verify implements token verification for the SigningMethod. +// For this verify method, key must be an ecdsa.PublicKey struct +func (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key interface{}) error { + // Get the key + var ecdsaKey *ecdsa.PublicKey + switch k := key.(type) { + case *ecdsa.PublicKey: + ecdsaKey = k + default: + return newError("ECDSA verify expects *ecsda.PublicKey", ErrInvalidKeyType) + } + + if len(sig) != 2*m.KeySize { + return ErrECDSAVerification + } + + r := big.NewInt(0).SetBytes(sig[:m.KeySize]) + s := big.NewInt(0).SetBytes(sig[m.KeySize:]) + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Verify the signature + if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus { + return nil + } + + return ErrECDSAVerification +} + +// Sign implements token signing for the SigningMethod. +// For this signing method, key must be an ecdsa.PrivateKey struct +func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) ([]byte, error) { + // Get the key + var ecdsaKey *ecdsa.PrivateKey + switch k := key.(type) { + case *ecdsa.PrivateKey: + ecdsaKey = k + default: + return nil, newError("ECDSA sign expects *ecsda.PrivateKey", ErrInvalidKeyType) + } + + // Create the hasher + if !m.Hash.Available() { + return nil, ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return r, s + if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { + curveBits := ecdsaKey.Curve.Params().BitSize + + if m.CurveBits != curveBits { + return nil, ErrInvalidKey + } + + keyBytes := curveBits / 8 + if curveBits%8 > 0 { + keyBytes += 1 + } + + // We serialize the outputs (r and s) into big-endian byte arrays + // padded with zeros on the left to make sure the sizes work out. + // Output must be 2*keyBytes long. + out := make([]byte, 2*keyBytes) + r.FillBytes(out[0:keyBytes]) // r is assigned to the first half of output. + s.FillBytes(out[keyBytes:]) // s is assigned to the second half of output. + + return out, nil + } else { + return nil, err + } +} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/ecdsa_utils.go b/vendor/github.com/golang-jwt/jwt/v5/ecdsa_utils.go similarity index 82% rename from vendor/github.com/dgrijalva/jwt-go/v4/ecdsa_utils.go rename to vendor/github.com/golang-jwt/jwt/v5/ecdsa_utils.go index 627e7db3..5700636d 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/ecdsa_utils.go +++ b/vendor/github.com/golang-jwt/jwt/v5/ecdsa_utils.go @@ -7,14 +7,12 @@ import ( "errors" ) -// Errors returned by EC signing methods var ( ErrNotECPublicKey = errors.New("key is not a valid ECDSA public key") ErrNotECPrivateKey = errors.New("key is not a valid ECDSA private key") ) -// ParseECPrivateKeyFromPEM is a helper function for -// parsing a PEM encoded Elliptic Curve Private Key Structure +// ParseECPrivateKeyFromPEM parses a PEM encoded Elliptic Curve Private Key Structure func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { var err error @@ -27,7 +25,9 @@ func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { // Parse the key var parsedKey interface{} if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { - return nil, err + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } } var pkey *ecdsa.PrivateKey @@ -39,8 +39,7 @@ func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { return pkey, nil } -// ParseECPublicKeyFromPEM is a helper function for -// parsing a PEM encoded PKCS1 or PKCS8 public key +// ParseECPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { var err error diff --git a/vendor/github.com/golang-jwt/jwt/v5/ed25519.go b/vendor/github.com/golang-jwt/jwt/v5/ed25519.go new file mode 100644 index 00000000..c2138119 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/ed25519.go @@ -0,0 +1,79 @@ +package jwt + +import ( + "crypto" + "crypto/ed25519" + "crypto/rand" + "errors" +) + +var ( + ErrEd25519Verification = errors.New("ed25519: verification error") +) + +// SigningMethodEd25519 implements the EdDSA family. +// Expects ed25519.PrivateKey for signing and ed25519.PublicKey for verification +type SigningMethodEd25519 struct{} + +// Specific instance for EdDSA +var ( + SigningMethodEdDSA *SigningMethodEd25519 +) + +func init() { + SigningMethodEdDSA = &SigningMethodEd25519{} + RegisterSigningMethod(SigningMethodEdDSA.Alg(), func() SigningMethod { + return SigningMethodEdDSA + }) +} + +func (m *SigningMethodEd25519) Alg() string { + return "EdDSA" +} + +// Verify implements token verification for the SigningMethod. +// For this verify method, key must be an ed25519.PublicKey +func (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key interface{}) error { + var ed25519Key ed25519.PublicKey + var ok bool + + if ed25519Key, ok = key.(ed25519.PublicKey); !ok { + return newError("Ed25519 verify expects ed25519.PublicKey", ErrInvalidKeyType) + } + + if len(ed25519Key) != ed25519.PublicKeySize { + return ErrInvalidKey + } + + // Verify the signature + if !ed25519.Verify(ed25519Key, []byte(signingString), sig) { + return ErrEd25519Verification + } + + return nil +} + +// Sign implements token signing for the SigningMethod. +// For this signing method, key must be an ed25519.PrivateKey +func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) ([]byte, error) { + var ed25519Key crypto.Signer + var ok bool + + if ed25519Key, ok = key.(crypto.Signer); !ok { + return nil, newError("Ed25519 sign expects crypto.Signer", ErrInvalidKeyType) + } + + if _, ok := ed25519Key.Public().(ed25519.PublicKey); !ok { + return nil, ErrInvalidKey + } + + // Sign the string and return the result. ed25519 performs a two-pass hash + // as part of its algorithm. Therefore, we need to pass a non-prehashed + // message into the Sign function, as indicated by crypto.Hash(0) + sig, err := ed25519Key.Sign(rand.Reader, []byte(signingString), crypto.Hash(0)) + if err != nil { + return nil, err + } + + return sig, nil +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/ed25519_utils.go b/vendor/github.com/golang-jwt/jwt/v5/ed25519_utils.go new file mode 100644 index 00000000..cdb5e68e --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/ed25519_utils.go @@ -0,0 +1,64 @@ +package jwt + +import ( + "crypto" + "crypto/ed25519" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrNotEdPrivateKey = errors.New("key is not a valid Ed25519 private key") + ErrNotEdPublicKey = errors.New("key is not a valid Ed25519 public key") +) + +// ParseEdPrivateKeyFromPEM parses a PEM-encoded Edwards curve private key +func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + + var pkey ed25519.PrivateKey + var ok bool + if pkey, ok = parsedKey.(ed25519.PrivateKey); !ok { + return nil, ErrNotEdPrivateKey + } + + return pkey, nil +} + +// ParseEdPublicKeyFromPEM parses a PEM-encoded Edwards curve public key +func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + return nil, err + } + + var pkey ed25519.PublicKey + var ok bool + if pkey, ok = parsedKey.(ed25519.PublicKey); !ok { + return nil, ErrNotEdPublicKey + } + + return pkey, nil +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/errors.go b/vendor/github.com/golang-jwt/jwt/v5/errors.go new file mode 100644 index 00000000..23bb616d --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/errors.go @@ -0,0 +1,49 @@ +package jwt + +import ( + "errors" + "strings" +) + +var ( + ErrInvalidKey = errors.New("key is invalid") + ErrInvalidKeyType = errors.New("key is of invalid type") + ErrHashUnavailable = errors.New("the requested hash function is unavailable") + ErrTokenMalformed = errors.New("token is malformed") + ErrTokenUnverifiable = errors.New("token is unverifiable") + ErrTokenSignatureInvalid = errors.New("token signature is invalid") + ErrTokenRequiredClaimMissing = errors.New("token is missing required claim") + ErrTokenInvalidAudience = errors.New("token has invalid audience") + ErrTokenExpired = errors.New("token is expired") + ErrTokenUsedBeforeIssued = errors.New("token used before issued") + ErrTokenInvalidIssuer = errors.New("token has invalid issuer") + ErrTokenInvalidSubject = errors.New("token has invalid subject") + ErrTokenNotValidYet = errors.New("token is not valid yet") + ErrTokenInvalidId = errors.New("token has invalid id") + ErrTokenInvalidClaims = errors.New("token has invalid claims") + ErrInvalidType = errors.New("invalid type for claim") +) + +// joinedError is an error type that works similar to what [errors.Join] +// produces, with the exception that it has a nice error string; mainly its +// error messages are concatenated using a comma, rather than a newline. +type joinedError struct { + errs []error +} + +func (je joinedError) Error() string { + msg := []string{} + for _, err := range je.errs { + msg = append(msg, err.Error()) + } + + return strings.Join(msg, ", ") +} + +// joinErrors joins together multiple errors. Useful for scenarios where +// multiple errors next to each other occur, e.g., in claims validation. +func joinErrors(errs ...error) error { + return &joinedError{ + errs: errs, + } +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/errors_go1_20.go b/vendor/github.com/golang-jwt/jwt/v5/errors_go1_20.go new file mode 100644 index 00000000..a893d355 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/errors_go1_20.go @@ -0,0 +1,47 @@ +//go:build go1.20 +// +build go1.20 + +package jwt + +import ( + "fmt" +) + +// Unwrap implements the multiple error unwrapping for this error type, which is +// possible in Go 1.20. +func (je joinedError) Unwrap() []error { + return je.errs +} + +// newError creates a new error message with a detailed error message. The +// message will be prefixed with the contents of the supplied error type. +// Additionally, more errors, that provide more context can be supplied which +// will be appended to the message. This makes use of Go 1.20's possibility to +// include more than one %w formatting directive in [fmt.Errorf]. +// +// For example, +// +// newError("no keyfunc was provided", ErrTokenUnverifiable) +// +// will produce the error string +// +// "token is unverifiable: no keyfunc was provided" +func newError(message string, err error, more ...error) error { + var format string + var args []any + if message != "" { + format = "%w: %s" + args = []any{err, message} + } else { + format = "%w" + args = []any{err} + } + + for _, e := range more { + format += ": %w" + args = append(args, e) + } + + err = fmt.Errorf(format, args...) + return err +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go b/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go new file mode 100644 index 00000000..2ad542f0 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/errors_go_other.go @@ -0,0 +1,78 @@ +//go:build !go1.20 +// +build !go1.20 + +package jwt + +import ( + "errors" + "fmt" +) + +// Is implements checking for multiple errors using [errors.Is], since multiple +// error unwrapping is not possible in versions less than Go 1.20. +func (je joinedError) Is(err error) bool { + for _, e := range je.errs { + if errors.Is(e, err) { + return true + } + } + + return false +} + +// wrappedErrors is a workaround for wrapping multiple errors in environments +// where Go 1.20 is not available. It basically uses the already implemented +// functionality of joinedError to handle multiple errors with supplies a +// custom error message that is identical to the one we produce in Go 1.20 using +// multiple %w directives. +type wrappedErrors struct { + msg string + joinedError +} + +// Error returns the stored error string +func (we wrappedErrors) Error() string { + return we.msg +} + +// newError creates a new error message with a detailed error message. The +// message will be prefixed with the contents of the supplied error type. +// Additionally, more errors, that provide more context can be supplied which +// will be appended to the message. Since we cannot use of Go 1.20's possibility +// to include more than one %w formatting directive in [fmt.Errorf], we have to +// emulate that. +// +// For example, +// +// newError("no keyfunc was provided", ErrTokenUnverifiable) +// +// will produce the error string +// +// "token is unverifiable: no keyfunc was provided" +func newError(message string, err error, more ...error) error { + // We cannot wrap multiple errors here with %w, so we have to be a little + // bit creative. Basically, we are using %s instead of %w to produce the + // same error message and then throw the result into a custom error struct. + var format string + var args []any + if message != "" { + format = "%s: %s" + args = []any{err, message} + } else { + format = "%s" + args = []any{err} + } + errs := []error{err} + + for _, e := range more { + format += ": %s" + args = append(args, e) + errs = append(errs, e) + } + + err = &wrappedErrors{ + msg: fmt.Sprintf(format, args...), + joinedError: joinedError{errs: errs}, + } + return err +} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/hmac.go b/vendor/github.com/golang-jwt/jwt/v5/hmac.go similarity index 51% rename from vendor/github.com/dgrijalva/jwt-go/v4/hmac.go rename to vendor/github.com/golang-jwt/jwt/v5/hmac.go index 2c910125..96c62722 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/hmac.go +++ b/vendor/github.com/golang-jwt/jwt/v5/hmac.go @@ -6,7 +6,7 @@ import ( "errors" ) -// SigningMethodHMAC implements the HMAC-SHA family of signing methods +// SigningMethodHMAC implements the HMAC-SHA family of signing methods. // Expects key type of []byte for both signing and validation type SigningMethodHMAC struct { Name string @@ -41,23 +41,25 @@ func init() { }) } -// Alg implements SigningMethod func (m *SigningMethodHMAC) Alg() string { return m.Name } -// Verify the signature of HSXXX tokens. Returns nil if the signature is valid. -func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { +// Verify implements token verification for the SigningMethod. Returns nil if +// the signature is valid. Key must be []byte. +// +// Note it is not advised to provide a []byte which was converted from a 'human +// readable' string using a subset of ASCII characters. To maximize entropy, you +// should ideally be providing a []byte key which was produced from a +// cryptographically random source, e.g. crypto/rand. Additional information +// about this, and why we intentionally are not supporting string as a key can +// be found on our usage guide +// https://golang-jwt.github.io/jwt/usage/signing_methods/#signing-methods-and-key-types. +func (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key interface{}) error { // Verify the key is the right type keyBytes, ok := key.([]byte) if !ok { - return NewInvalidKeyTypeError("[]byte", key) - } - - // Decode signature, for comparison - sig, err := DecodeSegment(signature) - if err != nil { - return err + return newError("HMAC verify expects []byte", ErrInvalidKeyType) } // Can we use the specified hashing method? @@ -78,20 +80,25 @@ func (m *SigningMethodHMAC) Verify(signingString, signature string, key interfac return nil } -// Sign implements the Sign method from SigningMethod -// Key must be []byte -func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { - keyBytes, ok := key.([]byte) - if !ok { - return "", NewInvalidKeyTypeError("[]byte", key) - } - - if !m.Hash.Available() { - return "", ErrHashUnavailable +// Sign implements token signing for the SigningMethod. Key must be []byte. +// +// Note it is not advised to provide a []byte which was converted from a 'human +// readable' string using a subset of ASCII characters. To maximize entropy, you +// should ideally be providing a []byte key which was produced from a +// cryptographically random source, e.g. crypto/rand. Additional information +// about this, and why we intentionally are not supporting string as a key can +// be found on our usage guide https://golang-jwt.github.io/jwt/usage/signing_methods/. +func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) ([]byte, error) { + if keyBytes, ok := key.([]byte); ok { + if !m.Hash.Available() { + return nil, newError("HMAC sign expects []byte", ErrInvalidKeyType) + } + + hasher := hmac.New(m.Hash.New, keyBytes) + hasher.Write([]byte(signingString)) + + return hasher.Sum(nil), nil } - hasher := hmac.New(m.Hash.New, keyBytes) - hasher.Write([]byte(signingString)) - - return EncodeSegment(hasher.Sum(nil)), nil + return nil, ErrInvalidKeyType } diff --git a/vendor/github.com/golang-jwt/jwt/v5/map_claims.go b/vendor/github.com/golang-jwt/jwt/v5/map_claims.go new file mode 100644 index 00000000..b2b51a1f --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/map_claims.go @@ -0,0 +1,109 @@ +package jwt + +import ( + "encoding/json" + "fmt" +) + +// MapClaims is a claims type that uses the map[string]interface{} for JSON +// decoding. This is the default claims type if you don't supply one +type MapClaims map[string]interface{} + +// GetExpirationTime implements the Claims interface. +func (m MapClaims) GetExpirationTime() (*NumericDate, error) { + return m.parseNumericDate("exp") +} + +// GetNotBefore implements the Claims interface. +func (m MapClaims) GetNotBefore() (*NumericDate, error) { + return m.parseNumericDate("nbf") +} + +// GetIssuedAt implements the Claims interface. +func (m MapClaims) GetIssuedAt() (*NumericDate, error) { + return m.parseNumericDate("iat") +} + +// GetAudience implements the Claims interface. +func (m MapClaims) GetAudience() (ClaimStrings, error) { + return m.parseClaimsString("aud") +} + +// GetIssuer implements the Claims interface. +func (m MapClaims) GetIssuer() (string, error) { + return m.parseString("iss") +} + +// GetSubject implements the Claims interface. +func (m MapClaims) GetSubject() (string, error) { + return m.parseString("sub") +} + +// parseNumericDate tries to parse a key in the map claims type as a number +// date. This will succeed, if the underlying type is either a [float64] or a +// [json.Number]. Otherwise, nil will be returned. +func (m MapClaims) parseNumericDate(key string) (*NumericDate, error) { + v, ok := m[key] + if !ok { + return nil, nil + } + + switch exp := v.(type) { + case float64: + if exp == 0 { + return nil, nil + } + + return newNumericDateFromSeconds(exp), nil + case json.Number: + v, _ := exp.Float64() + + return newNumericDateFromSeconds(v), nil + } + + return nil, newError(fmt.Sprintf("%s is invalid", key), ErrInvalidType) +} + +// parseClaimsString tries to parse a key in the map claims type as a +// [ClaimsStrings] type, which can either be a string or an array of string. +func (m MapClaims) parseClaimsString(key string) (ClaimStrings, error) { + var cs []string + switch v := m[key].(type) { + case string: + cs = append(cs, v) + case []string: + cs = v + case []interface{}: + for _, a := range v { + vs, ok := a.(string) + if !ok { + return nil, newError(fmt.Sprintf("%s is invalid", key), ErrInvalidType) + } + cs = append(cs, vs) + } + } + + return cs, nil +} + +// parseString tries to parse a key in the map claims type as a [string] type. +// If the key does not exist, an empty string is returned. If the key has the +// wrong type, an error is returned. +func (m MapClaims) parseString(key string) (string, error) { + var ( + ok bool + raw interface{} + iss string + ) + raw, ok = m[key] + if !ok { + return "", nil + } + + iss, ok = raw.(string) + if !ok { + return "", newError(fmt.Sprintf("%s is invalid", key), ErrInvalidType) + } + + return iss, nil +} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/none.go b/vendor/github.com/golang-jwt/jwt/v5/none.go similarity index 61% rename from vendor/github.com/dgrijalva/jwt-go/v4/none.go rename to vendor/github.com/golang-jwt/jwt/v5/none.go index a5caed37..685c2ea3 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/none.go +++ b/vendor/github.com/golang-jwt/jwt/v5/none.go @@ -4,13 +4,8 @@ package jwt // but you probably should never use it. var SigningMethodNone *signingMethodNone -// UnsafeAllowNoneSignatureType must be returned from Keyfunc in order for the -// none signing method to be allowed. This is intended to make is possible to use -// this signing method, but not by accident const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed" -// NoneSignatureTypeDisallowedError is the error value returned when the none signing method -// is used without UnsafeAllowNoneSignatureType var NoneSignatureTypeDisallowedError error type signingMethodNone struct{} @@ -18,7 +13,7 @@ type unsafeNoneMagicConstant string func init() { SigningMethodNone = &signingMethodNone{} - NoneSignatureTypeDisallowedError = &InvalidSignatureError{Message: "'none' signature type is not allowed"} + NoneSignatureTypeDisallowedError = newError("'none' signature type is not allowed", ErrTokenUnverifiable) RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod { return SigningMethodNone @@ -30,15 +25,15 @@ func (m *signingMethodNone) Alg() string { } // Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) { +func (m *signingMethodNone) Verify(signingString string, sig []byte, key interface{}) (err error) { // Key must be UnsafeAllowNoneSignatureType to prevent accidentally // accepting 'none' signing method if _, ok := key.(unsafeNoneMagicConstant); !ok { return NoneSignatureTypeDisallowedError } // If signing method is none, signature must be an empty string - if signature != "" { - return &InvalidSignatureError{Message: "'none' signing method with non-empty signature"} + if len(sig) != 0 { + return newError("'none' signing method with non-empty signature", ErrTokenUnverifiable) } // Accept 'none' signing method. @@ -46,9 +41,10 @@ func (m *signingMethodNone) Verify(signingString, signature string, key interfac } // Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key -func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) { +func (m *signingMethodNone) Sign(signingString string, key interface{}) ([]byte, error) { if _, ok := key.(unsafeNoneMagicConstant); ok { - return "", nil + return []byte{}, nil } - return "", NoneSignatureTypeDisallowedError + + return nil, NoneSignatureTypeDisallowedError } diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser.go b/vendor/github.com/golang-jwt/jwt/v5/parser.go new file mode 100644 index 00000000..ecf99af7 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/parser.go @@ -0,0 +1,238 @@ +package jwt + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "strings" +) + +type Parser struct { + // If populated, only these methods will be considered valid. + validMethods []string + + // Use JSON Number format in JSON decoder. + useJSONNumber bool + + // Skip claims validation during token parsing. + skipClaimsValidation bool + + validator *Validator + + decodeStrict bool + + decodePaddingAllowed bool +} + +// NewParser creates a new Parser with the specified options +func NewParser(options ...ParserOption) *Parser { + p := &Parser{ + validator: &Validator{}, + } + + // Loop through our parsing options and apply them + for _, option := range options { + option(p) + } + + return p +} + +// Parse parses, validates, verifies the signature and returns the parsed token. +// keyFunc will receive the parsed token and should return the key for validating. +func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { + return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) +} + +// ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object implementing the Claims +// interface. This provides default values which can be overridden and allows a caller to use their own type, rather +// than the default MapClaims implementation of Claims. +// +// Note: If you provide a custom claim implementation that embeds one of the standard claims (such as RegisteredClaims), +// make sure that a) you either embed a non-pointer version of the claims or b) if you are using a pointer, allocate the +// proper memory for it before passing in the overall claims, otherwise you might run into a panic. +func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { + token, parts, err := p.ParseUnverified(tokenString, claims) + if err != nil { + return token, err + } + + // Verify signing method is in the required set + if p.validMethods != nil { + var signingMethodValid = false + var alg = token.Method.Alg() + for _, m := range p.validMethods { + if m == alg { + signingMethodValid = true + break + } + } + if !signingMethodValid { + // signing method is not in the listed set + return token, newError(fmt.Sprintf("signing method %v is invalid", alg), ErrTokenSignatureInvalid) + } + } + + // Decode signature + token.Signature, err = p.DecodeSegment(parts[2]) + if err != nil { + return token, newError("could not base64 decode signature", ErrTokenMalformed, err) + } + text := strings.Join(parts[0:2], ".") + + // Lookup key(s) + if keyFunc == nil { + // keyFunc was not provided. short circuiting validation + return token, newError("no keyfunc was provided", ErrTokenUnverifiable) + } + + got, err := keyFunc(token) + if err != nil { + return token, newError("error while executing keyfunc", ErrTokenUnverifiable, err) + } + + switch have := got.(type) { + case VerificationKeySet: + if len(have.Keys) == 0 { + return token, newError("keyfunc returned empty verification key set", ErrTokenUnverifiable) + } + // Iterate through keys and verify signature, skipping the rest when a match is found. + // Return the last error if no match is found. + for _, key := range have.Keys { + if err = token.Method.Verify(text, token.Signature, key); err == nil { + break + } + } + default: + err = token.Method.Verify(text, token.Signature, have) + } + if err != nil { + return token, newError("", ErrTokenSignatureInvalid, err) + } + + // Validate Claims + if !p.skipClaimsValidation { + // Make sure we have at least a default validator + if p.validator == nil { + p.validator = NewValidator() + } + + if err := p.validator.Validate(claims); err != nil { + return token, newError("", ErrTokenInvalidClaims, err) + } + } + + // No errors so far, token is valid. + token.Valid = true + + return token, nil +} + +// ParseUnverified parses the token but doesn't validate the signature. +// +// WARNING: Don't use this method unless you know what you're doing. +// +// It's only ever useful in cases where you know the signature is valid (since it has already +// been or will be checked elsewhere in the stack) and you want to extract values from it. +func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { + parts = strings.Split(tokenString, ".") + if len(parts) != 3 { + return nil, parts, newError("token contains an invalid number of segments", ErrTokenMalformed) + } + + token = &Token{Raw: tokenString} + + // parse Header + var headerBytes []byte + if headerBytes, err = p.DecodeSegment(parts[0]); err != nil { + return token, parts, newError("could not base64 decode header", ErrTokenMalformed, err) + } + if err = json.Unmarshal(headerBytes, &token.Header); err != nil { + return token, parts, newError("could not JSON decode header", ErrTokenMalformed, err) + } + + // parse Claims + token.Claims = claims + + claimBytes, err := p.DecodeSegment(parts[1]) + if err != nil { + return token, parts, newError("could not base64 decode claim", ErrTokenMalformed, err) + } + + // If `useJSONNumber` is enabled then we must use *json.Decoder to decode + // the claims. However, this comes with a performance penalty so only use + // it if we must and, otherwise, simple use json.Unmarshal. + if !p.useJSONNumber { + // JSON Unmarshal. Special case for map type to avoid weird pointer behavior. + if c, ok := token.Claims.(MapClaims); ok { + err = json.Unmarshal(claimBytes, &c) + } else { + err = json.Unmarshal(claimBytes, &claims) + } + } else { + dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) + dec.UseNumber() + // JSON Decode. Special case for map type to avoid weird pointer behavior. + if c, ok := token.Claims.(MapClaims); ok { + err = dec.Decode(&c) + } else { + err = dec.Decode(&claims) + } + } + if err != nil { + return token, parts, newError("could not JSON decode claim", ErrTokenMalformed, err) + } + + // Lookup signature method + if method, ok := token.Header["alg"].(string); ok { + if token.Method = GetSigningMethod(method); token.Method == nil { + return token, parts, newError("signing method (alg) is unavailable", ErrTokenUnverifiable) + } + } else { + return token, parts, newError("signing method (alg) is unspecified", ErrTokenUnverifiable) + } + + return token, parts, nil +} + +// DecodeSegment decodes a JWT specific base64url encoding. This function will +// take into account whether the [Parser] is configured with additional options, +// such as [WithStrictDecoding] or [WithPaddingAllowed]. +func (p *Parser) DecodeSegment(seg string) ([]byte, error) { + encoding := base64.RawURLEncoding + + if p.decodePaddingAllowed { + if l := len(seg) % 4; l > 0 { + seg += strings.Repeat("=", 4-l) + } + encoding = base64.URLEncoding + } + + if p.decodeStrict { + encoding = encoding.Strict() + } + return encoding.DecodeString(seg) +} + +// Parse parses, validates, verifies the signature and returns the parsed token. +// keyFunc will receive the parsed token and should return the cryptographic key +// for verifying the signature. The caller is strongly encouraged to set the +// WithValidMethods option to validate the 'alg' claim in the token matches the +// expected algorithm. For more details about the importance of validating the +// 'alg' claim, see +// https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ +func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { + return NewParser(options...).Parse(tokenString, keyFunc) +} + +// ParseWithClaims is a shortcut for NewParser().ParseWithClaims(). +// +// Note: If you provide a custom claim implementation that embeds one of the +// standard claims (such as RegisteredClaims), make sure that a) you either +// embed a non-pointer version of the claims or b) if you are using a pointer, +// allocate the proper memory for it before passing in the overall claims, +// otherwise you might run into a panic. +func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { + return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc) +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser_option.go b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go new file mode 100644 index 00000000..88a780fb --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go @@ -0,0 +1,128 @@ +package jwt + +import "time" + +// ParserOption is used to implement functional-style options that modify the +// behavior of the parser. To add new options, just create a function (ideally +// beginning with With or Without) that returns an anonymous function that takes +// a *Parser type as input and manipulates its configuration accordingly. +type ParserOption func(*Parser) + +// WithValidMethods is an option to supply algorithm methods that the parser +// will check. Only those methods will be considered valid. It is heavily +// encouraged to use this option in order to prevent attacks such as +// https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/. +func WithValidMethods(methods []string) ParserOption { + return func(p *Parser) { + p.validMethods = methods + } +} + +// WithJSONNumber is an option to configure the underlying JSON parser with +// UseNumber. +func WithJSONNumber() ParserOption { + return func(p *Parser) { + p.useJSONNumber = true + } +} + +// WithoutClaimsValidation is an option to disable claims validation. This +// option should only be used if you exactly know what you are doing. +func WithoutClaimsValidation() ParserOption { + return func(p *Parser) { + p.skipClaimsValidation = true + } +} + +// WithLeeway returns the ParserOption for specifying the leeway window. +func WithLeeway(leeway time.Duration) ParserOption { + return func(p *Parser) { + p.validator.leeway = leeway + } +} + +// WithTimeFunc returns the ParserOption for specifying the time func. The +// primary use-case for this is testing. If you are looking for a way to account +// for clock-skew, WithLeeway should be used instead. +func WithTimeFunc(f func() time.Time) ParserOption { + return func(p *Parser) { + p.validator.timeFunc = f + } +} + +// WithIssuedAt returns the ParserOption to enable verification +// of issued-at. +func WithIssuedAt() ParserOption { + return func(p *Parser) { + p.validator.verifyIat = true + } +} + +// WithExpirationRequired returns the ParserOption to make exp claim required. +// By default exp claim is optional. +func WithExpirationRequired() ParserOption { + return func(p *Parser) { + p.validator.requireExp = true + } +} + +// WithAudience configures the validator to require the specified audience in +// the `aud` claim. Validation will fail if the audience is not listed in the +// token or the `aud` claim is missing. +// +// NOTE: While the `aud` claim is OPTIONAL in a JWT, the handling of it is +// application-specific. Since this validation API is helping developers in +// writing secure application, we decided to REQUIRE the existence of the claim, +// if an audience is expected. +func WithAudience(aud string) ParserOption { + return func(p *Parser) { + p.validator.expectedAud = aud + } +} + +// WithIssuer configures the validator to require the specified issuer in the +// `iss` claim. Validation will fail if a different issuer is specified in the +// token or the `iss` claim is missing. +// +// NOTE: While the `iss` claim is OPTIONAL in a JWT, the handling of it is +// application-specific. Since this validation API is helping developers in +// writing secure application, we decided to REQUIRE the existence of the claim, +// if an issuer is expected. +func WithIssuer(iss string) ParserOption { + return func(p *Parser) { + p.validator.expectedIss = iss + } +} + +// WithSubject configures the validator to require the specified subject in the +// `sub` claim. Validation will fail if a different subject is specified in the +// token or the `sub` claim is missing. +// +// NOTE: While the `sub` claim is OPTIONAL in a JWT, the handling of it is +// application-specific. Since this validation API is helping developers in +// writing secure application, we decided to REQUIRE the existence of the claim, +// if a subject is expected. +func WithSubject(sub string) ParserOption { + return func(p *Parser) { + p.validator.expectedSub = sub + } +} + +// WithPaddingAllowed will enable the codec used for decoding JWTs to allow +// padding. Note that the JWS RFC7515 states that the tokens will utilize a +// Base64url encoding with no padding. Unfortunately, some implementations of +// JWT are producing non-standard tokens, and thus require support for decoding. +func WithPaddingAllowed() ParserOption { + return func(p *Parser) { + p.decodePaddingAllowed = true + } +} + +// WithStrictDecoding will switch the codec used for decoding JWTs into strict +// mode. In this mode, the decoder requires that trailing padding bits are zero, +// as described in RFC 4648 section 3.5. +func WithStrictDecoding() ParserOption { + return func(p *Parser) { + p.decodeStrict = true + } +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/registered_claims.go b/vendor/github.com/golang-jwt/jwt/v5/registered_claims.go new file mode 100644 index 00000000..77951a53 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/registered_claims.go @@ -0,0 +1,63 @@ +package jwt + +// RegisteredClaims are a structured version of the JWT Claims Set, +// restricted to Registered Claim Names, as referenced at +// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 +// +// This type can be used on its own, but then additional private and +// public claims embedded in the JWT will not be parsed. The typical use-case +// therefore is to embedded this in a user-defined claim type. +// +// See examples for how to use this with your own claim types. +type RegisteredClaims struct { + // the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1 + Issuer string `json:"iss,omitempty"` + + // the `sub` (Subject) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2 + Subject string `json:"sub,omitempty"` + + // the `aud` (Audience) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3 + Audience ClaimStrings `json:"aud,omitempty"` + + // the `exp` (Expiration Time) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4 + ExpiresAt *NumericDate `json:"exp,omitempty"` + + // the `nbf` (Not Before) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5 + NotBefore *NumericDate `json:"nbf,omitempty"` + + // the `iat` (Issued At) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6 + IssuedAt *NumericDate `json:"iat,omitempty"` + + // the `jti` (JWT ID) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7 + ID string `json:"jti,omitempty"` +} + +// GetExpirationTime implements the Claims interface. +func (c RegisteredClaims) GetExpirationTime() (*NumericDate, error) { + return c.ExpiresAt, nil +} + +// GetNotBefore implements the Claims interface. +func (c RegisteredClaims) GetNotBefore() (*NumericDate, error) { + return c.NotBefore, nil +} + +// GetIssuedAt implements the Claims interface. +func (c RegisteredClaims) GetIssuedAt() (*NumericDate, error) { + return c.IssuedAt, nil +} + +// GetAudience implements the Claims interface. +func (c RegisteredClaims) GetAudience() (ClaimStrings, error) { + return c.Audience, nil +} + +// GetIssuer implements the Claims interface. +func (c RegisteredClaims) GetIssuer() (string, error) { + return c.Issuer, nil +} + +// GetSubject implements the Claims interface. +func (c RegisteredClaims) GetSubject() (string, error) { + return c.Subject, nil +} diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/rsa.go b/vendor/github.com/golang-jwt/jwt/v5/rsa.go similarity index 56% rename from vendor/github.com/dgrijalva/jwt-go/v4/rsa.go rename to vendor/github.com/golang-jwt/jwt/v5/rsa.go index 72ba2d66..83cbee6a 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/rsa.go +++ b/vendor/github.com/golang-jwt/jwt/v5/rsa.go @@ -4,10 +4,9 @@ import ( "crypto" "crypto/rand" "crypto/rsa" - "fmt" ) -// SigningMethodRSA implements the RSA family of signing methods signing methods +// SigningMethodRSA implements the RSA family of signing methods. // Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation type SigningMethodRSA struct { Name string @@ -41,35 +40,18 @@ func init() { }) } -// Alg implements the Alg method from SigningMethod func (m *SigningMethodRSA) Alg() string { return m.Name } -// Verify implements the Verify method from SigningMethod +// Verify implements token verification for the SigningMethod // For this signing method, must be an *rsa.PublicKey structure. -func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - +func (m *SigningMethodRSA) Verify(signingString string, sig []byte, key interface{}) error { var rsaKey *rsa.PublicKey var ok bool - switch k := key.(type) { - case *rsa.PublicKey: - rsaKey = k - case crypto.Signer: - pub := k.Public() - if rsaKey, ok = pub.(*rsa.PublicKey); !ok { - return &InvalidKeyError{Message: fmt.Sprintf("signer returned unexpected public key type: %T", pub)} - } - default: - return NewInvalidKeyTypeError("*rsa.PublicKey or crypto.Signer", key) + if rsaKey, ok = key.(*rsa.PublicKey); !ok { + return newError("RSA verify expects *rsa.PublicKey", ErrInvalidKeyType) } // Create hasher @@ -83,33 +65,29 @@ func (m *SigningMethodRSA) Verify(signingString, signature string, key interface return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) } -// Sign implements the Sign method from SigningMethod +// Sign implements token signing for the SigningMethod // For this signing method, must be an *rsa.PrivateKey structure. -func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { - var signer crypto.Signer +func (m *SigningMethodRSA) Sign(signingString string, key interface{}) ([]byte, error) { + var rsaKey *rsa.PrivateKey var ok bool - if signer, ok = key.(crypto.Signer); !ok { - return "", NewInvalidKeyTypeError("*rsa.PublicKey or crypto.Signer", key) - } - - //sanity check that the signer is an rsa signer - if pub, ok := signer.Public().(*rsa.PublicKey); !ok { - return "", &InvalidKeyError{Message: fmt.Sprintf("signer returned unexpected public key type: %T", pub)} + // Validate type of key + if rsaKey, ok = key.(*rsa.PrivateKey); !ok { + return nil, newError("RSA sign expects *rsa.PrivateKey", ErrInvalidKeyType) } // Create the hasher if !m.Hash.Available() { - return "", ErrHashUnavailable + return nil, ErrHashUnavailable } hasher := m.Hash.New() hasher.Write([]byte(signingString)) // Sign the string and return the encoded bytes - sigBytes, err := signer.Sign(rand.Reader, hasher.Sum(nil), m.Hash) - if err != nil { - return "", err + if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil { + return sigBytes, nil + } else { + return nil, err } - return EncodeSegment(sigBytes), nil } diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/rsa_pss.go b/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go similarity index 50% rename from vendor/github.com/dgrijalva/jwt-go/v4/rsa_pss.go rename to vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go index aa3ba399..28c386ec 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/rsa_pss.go +++ b/vendor/github.com/golang-jwt/jwt/v5/rsa_pss.go @@ -1,3 +1,4 @@ +//go:build go1.4 // +build go1.4 package jwt @@ -6,16 +7,20 @@ import ( "crypto" "crypto/rand" "crypto/rsa" - "fmt" ) -// SigningMethodRSAPSS implements the RSAPSS family of signing methods +// SigningMethodRSAPSS implements the RSAPSS family of signing methods signing methods type SigningMethodRSAPSS struct { *SigningMethodRSA Options *rsa.PSSOptions + // VerifyOptions is optional. If set overrides Options for rsa.VerifyPPS. + // Used to accept tokens signed with rsa.PSSSaltLengthAuto, what doesn't follow + // https://tools.ietf.org/html/rfc7518#section-3.5 but was used previously. + // See https://github.com/dgrijalva/jwt-go/issues/285#issuecomment-437451244 for details. + VerifyOptions *rsa.PSSOptions } -// Specific instances for RS/PS and company +// Specific instances for RS/PS and company. var ( SigningMethodPS256 *SigningMethodRSAPSS SigningMethodPS384 *SigningMethodRSAPSS @@ -25,13 +30,15 @@ var ( func init() { // PS256 SigningMethodPS256 = &SigningMethodRSAPSS{ - &SigningMethodRSA{ + SigningMethodRSA: &SigningMethodRSA{ Name: "PS256", Hash: crypto.SHA256, }, - &rsa.PSSOptions{ + Options: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + }, + VerifyOptions: &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA256, }, } RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { @@ -40,13 +47,15 @@ func init() { // PS384 SigningMethodPS384 = &SigningMethodRSAPSS{ - &SigningMethodRSA{ + SigningMethodRSA: &SigningMethodRSA{ Name: "PS384", Hash: crypto.SHA384, }, - &rsa.PSSOptions{ + Options: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + }, + VerifyOptions: &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA384, }, } RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { @@ -55,13 +64,15 @@ func init() { // PS512 SigningMethodPS512 = &SigningMethodRSAPSS{ - &SigningMethodRSA{ + SigningMethodRSA: &SigningMethodRSA{ Name: "PS512", Hash: crypto.SHA512, }, - &rsa.PSSOptions{ + Options: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + }, + VerifyOptions: &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA512, }, } RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { @@ -69,30 +80,15 @@ func init() { }) } -// Verify implements the Verify method from SigningMethod +// Verify implements token verification for the SigningMethod. // For this verify method, key must be an rsa.PublicKey struct -func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { - var err error - - // Decode the signature - var sig []byte - if sig, err = DecodeSegment(signature); err != nil { - return err - } - +func (m *SigningMethodRSAPSS) Verify(signingString string, sig []byte, key interface{}) error { var rsaKey *rsa.PublicKey - var ok bool - switch k := key.(type) { case *rsa.PublicKey: rsaKey = k - case crypto.Signer: - pub := k.Public() - if rsaKey, ok = pub.(*rsa.PublicKey); !ok { - return &InvalidKeyError{Message: fmt.Sprintf("signer returned unexpected public key type: %T", pub)} - } default: - return NewInvalidKeyTypeError("*rsa.PublicKey or crypto.Signer", key) + return newError("RSA-PSS verify expects *rsa.PublicKey", ErrInvalidKeyType) } // Create hasher @@ -102,37 +98,38 @@ func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interf hasher := m.Hash.New() hasher.Write([]byte(signingString)) - return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options) + opts := m.Options + if m.VerifyOptions != nil { + opts = m.VerifyOptions + } + + return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts) } -// Sign implements the Sign method from SigningMethod +// Sign implements token signing for the SigningMethod. // For this signing method, key must be an rsa.PrivateKey struct -func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { - var signer crypto.Signer - var ok bool - - if signer, ok = key.(crypto.Signer); !ok { - return "", NewInvalidKeyTypeError("*rsa.PrivateKey or crypto.Signer", key) - } +func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) ([]byte, error) { + var rsaKey *rsa.PrivateKey - //sanity check that the signer is an rsa signer - if pub, ok := signer.Public().(*rsa.PublicKey); !ok { - return "", &InvalidKeyError{Message: fmt.Sprintf("signer returned unexpected public key type: %T", pub)} + switch k := key.(type) { + case *rsa.PrivateKey: + rsaKey = k + default: + return nil, newError("RSA-PSS sign expects *rsa.PrivateKey", ErrInvalidKeyType) } // Create the hasher if !m.Hash.Available() { - return "", ErrHashUnavailable + return nil, ErrHashUnavailable } hasher := m.Hash.New() hasher.Write([]byte(signingString)) // Sign the string and return the encoded bytes - sigBytes, err := signer.Sign(rand.Reader, hasher.Sum(nil), m.Options) - if err != nil { - return "", err + if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { + return sigBytes, nil + } else { + return nil, err } - return EncodeSegment(sigBytes), nil - } diff --git a/vendor/github.com/dgrijalva/jwt-go/v4/rsa_utils.go b/vendor/github.com/golang-jwt/jwt/v5/rsa_utils.go similarity index 72% rename from vendor/github.com/dgrijalva/jwt-go/v4/rsa_utils.go rename to vendor/github.com/golang-jwt/jwt/v5/rsa_utils.go index b0dae4bf..b3aeebbe 100644 --- a/vendor/github.com/dgrijalva/jwt-go/v4/rsa_utils.go +++ b/vendor/github.com/golang-jwt/jwt/v5/rsa_utils.go @@ -7,15 +7,13 @@ import ( "errors" ) -// Errors returned by RSA Signing Method and helpers var ( - ErrKeyMustBePEMEncoded = errors.New("invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key") + ErrKeyMustBePEMEncoded = errors.New("invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key") ErrNotRSAPrivateKey = errors.New("key is not a valid RSA private key") ErrNotRSAPublicKey = errors.New("key is not a valid RSA public key") ) -// ParseRSAPrivateKeyFromPEM is a helper method for -// parsing PEM encoded PKCS1 or PKCS8 private key +// ParseRSAPrivateKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 private key func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { var err error @@ -41,8 +39,11 @@ func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { return pkey, nil } -// ParseRSAPrivateKeyFromPEMWithPassword is a helper method for -// parsing PEM encoded PKCS1 or PKCS8 private key, encrypted with a password +// ParseRSAPrivateKeyFromPEMWithPassword parses a PEM encoded PKCS1 or PKCS8 private key protected with password +// +// Deprecated: This function is deprecated and should not be used anymore. It uses the deprecated x509.DecryptPEMBlock +// function, which was deprecated since RFC 1423 is regarded insecure by design. Unfortunately, there is no alternative +// in the Go standard library for now. See https://github.com/golang/go/issues/8860. func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) { var err error @@ -74,8 +75,7 @@ func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.Pr return pkey, nil } -// ParseRSAPublicKeyFromPEM is a helper method for -// parsing a PEM encoded PKCS1 or PKCS8 public key +// ParseRSAPublicKeyFromPEM parses a certificate or a PEM encoded PKCS1 or PKIX public key func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { var err error @@ -91,7 +91,9 @@ func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { if cert, err := x509.ParseCertificate(block.Bytes); err == nil { parsedKey = cert.PublicKey } else { - return nil, err + if parsedKey, err = x509.ParsePKCS1PublicKey(block.Bytes); err != nil { + return nil, err + } } } diff --git a/vendor/github.com/golang-jwt/jwt/v5/signing_method.go b/vendor/github.com/golang-jwt/jwt/v5/signing_method.go new file mode 100644 index 00000000..0d73631c --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/signing_method.go @@ -0,0 +1,49 @@ +package jwt + +import ( + "sync" +) + +var signingMethods = map[string]func() SigningMethod{} +var signingMethodLock = new(sync.RWMutex) + +// SigningMethod can be used add new methods for signing or verifying tokens. It +// takes a decoded signature as an input in the Verify function and produces a +// signature in Sign. The signature is then usually base64 encoded as part of a +// JWT. +type SigningMethod interface { + Verify(signingString string, sig []byte, key interface{}) error // Returns nil if signature is valid + Sign(signingString string, key interface{}) ([]byte, error) // Returns signature or error + Alg() string // returns the alg identifier for this method (example: 'HS256') +} + +// RegisterSigningMethod registers the "alg" name and a factory function for signing method. +// This is typically done during init() in the method's implementation +func RegisterSigningMethod(alg string, f func() SigningMethod) { + signingMethodLock.Lock() + defer signingMethodLock.Unlock() + + signingMethods[alg] = f +} + +// GetSigningMethod retrieves a signing method from an "alg" string +func GetSigningMethod(alg string) (method SigningMethod) { + signingMethodLock.RLock() + defer signingMethodLock.RUnlock() + + if methodF, ok := signingMethods[alg]; ok { + method = methodF() + } + return +} + +// GetAlgorithms returns a list of registered "alg" names +func GetAlgorithms() (algs []string) { + signingMethodLock.RLock() + defer signingMethodLock.RUnlock() + + for alg := range signingMethods { + algs = append(algs, alg) + } + return +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/staticcheck.conf b/vendor/github.com/golang-jwt/jwt/v5/staticcheck.conf new file mode 100644 index 00000000..53745d51 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/staticcheck.conf @@ -0,0 +1 @@ +checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1023"] diff --git a/vendor/github.com/golang-jwt/jwt/v5/token.go b/vendor/github.com/golang-jwt/jwt/v5/token.go new file mode 100644 index 00000000..352873a2 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/token.go @@ -0,0 +1,100 @@ +package jwt + +import ( + "crypto" + "encoding/base64" + "encoding/json" +) + +// Keyfunc will be used by the Parse methods as a callback function to supply +// the key for verification. The function receives the parsed, but unverified +// Token. This allows you to use properties in the Header of the token (such as +// `kid`) to identify which key to use. +// +// The returned interface{} may be a single key or a VerificationKeySet containing +// multiple keys. +type Keyfunc func(*Token) (interface{}, error) + +// VerificationKey represents a public or secret key for verifying a token's signature. +type VerificationKey interface { + crypto.PublicKey | []uint8 +} + +// VerificationKeySet is a set of public or secret keys. It is used by the parser to verify a token. +type VerificationKeySet struct { + Keys []VerificationKey +} + +// Token represents a JWT Token. Different fields will be used depending on +// whether you're creating or parsing/verifying a token. +type Token struct { + Raw string // Raw contains the raw token. Populated when you [Parse] a token + Method SigningMethod // Method is the signing method used or to be used + Header map[string]interface{} // Header is the first segment of the token in decoded form + Claims Claims // Claims is the second segment of the token in decoded form + Signature []byte // Signature is the third segment of the token in decoded form. Populated when you Parse a token + Valid bool // Valid specifies if the token is valid. Populated when you Parse/Verify a token +} + +// New creates a new [Token] with the specified signing method and an empty map +// of claims. Additional options can be specified, but are currently unused. +func New(method SigningMethod, opts ...TokenOption) *Token { + return NewWithClaims(method, MapClaims{}, opts...) +} + +// NewWithClaims creates a new [Token] with the specified signing method and +// claims. Additional options can be specified, but are currently unused. +func NewWithClaims(method SigningMethod, claims Claims, opts ...TokenOption) *Token { + return &Token{ + Header: map[string]interface{}{ + "typ": "JWT", + "alg": method.Alg(), + }, + Claims: claims, + Method: method, + } +} + +// SignedString creates and returns a complete, signed JWT. The token is signed +// using the SigningMethod specified in the token. Please refer to +// https://golang-jwt.github.io/jwt/usage/signing_methods/#signing-methods-and-key-types +// for an overview of the different signing methods and their respective key +// types. +func (t *Token) SignedString(key interface{}) (string, error) { + sstr, err := t.SigningString() + if err != nil { + return "", err + } + + sig, err := t.Method.Sign(sstr, key) + if err != nil { + return "", err + } + + return sstr + "." + t.EncodeSegment(sig), nil +} + +// SigningString generates the signing string. This is the most expensive part +// of the whole deal. Unless you need this for something special, just go +// straight for the SignedString. +func (t *Token) SigningString() (string, error) { + h, err := json.Marshal(t.Header) + if err != nil { + return "", err + } + + c, err := json.Marshal(t.Claims) + if err != nil { + return "", err + } + + return t.EncodeSegment(h) + "." + t.EncodeSegment(c), nil +} + +// EncodeSegment encodes a JWT specific base64url encoding with padding +// stripped. In the future, this function might take into account a +// [TokenOption]. Therefore, this function exists as a method of [Token], rather +// than a global function. +func (*Token) EncodeSegment(seg []byte) string { + return base64.RawURLEncoding.EncodeToString(seg) +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/token_option.go b/vendor/github.com/golang-jwt/jwt/v5/token_option.go new file mode 100644 index 00000000..b4ae3bad --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/token_option.go @@ -0,0 +1,5 @@ +package jwt + +// TokenOption is a reserved type, which provides some forward compatibility, +// if we ever want to introduce token creation-related options. +type TokenOption func(*Token) diff --git a/vendor/github.com/golang-jwt/jwt/v5/types.go b/vendor/github.com/golang-jwt/jwt/v5/types.go new file mode 100644 index 00000000..b2655a9e --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/types.go @@ -0,0 +1,149 @@ +package jwt + +import ( + "encoding/json" + "fmt" + "math" + "strconv" + "time" +) + +// TimePrecision sets the precision of times and dates within this library. This +// has an influence on the precision of times when comparing expiry or other +// related time fields. Furthermore, it is also the precision of times when +// serializing. +// +// For backwards compatibility the default precision is set to seconds, so that +// no fractional timestamps are generated. +var TimePrecision = time.Second + +// MarshalSingleStringAsArray modifies the behavior of the ClaimStrings type, +// especially its MarshalJSON function. +// +// If it is set to true (the default), it will always serialize the type as an +// array of strings, even if it just contains one element, defaulting to the +// behavior of the underlying []string. If it is set to false, it will serialize +// to a single string, if it contains one element. Otherwise, it will serialize +// to an array of strings. +var MarshalSingleStringAsArray = true + +// NumericDate represents a JSON numeric date value, as referenced at +// https://datatracker.ietf.org/doc/html/rfc7519#section-2. +type NumericDate struct { + time.Time +} + +// NewNumericDate constructs a new *NumericDate from a standard library time.Time struct. +// It will truncate the timestamp according to the precision specified in TimePrecision. +func NewNumericDate(t time.Time) *NumericDate { + return &NumericDate{t.Truncate(TimePrecision)} +} + +// newNumericDateFromSeconds creates a new *NumericDate out of a float64 representing a +// UNIX epoch with the float fraction representing non-integer seconds. +func newNumericDateFromSeconds(f float64) *NumericDate { + round, frac := math.Modf(f) + return NewNumericDate(time.Unix(int64(round), int64(frac*1e9))) +} + +// MarshalJSON is an implementation of the json.RawMessage interface and serializes the UNIX epoch +// represented in NumericDate to a byte array, using the precision specified in TimePrecision. +func (date NumericDate) MarshalJSON() (b []byte, err error) { + var prec int + if TimePrecision < time.Second { + prec = int(math.Log10(float64(time.Second) / float64(TimePrecision))) + } + truncatedDate := date.Truncate(TimePrecision) + + // For very large timestamps, UnixNano would overflow an int64, but this + // function requires nanosecond level precision, so we have to use the + // following technique to get round the issue: + // + // 1. Take the normal unix timestamp to form the whole number part of the + // output, + // 2. Take the result of the Nanosecond function, which returns the offset + // within the second of the particular unix time instance, to form the + // decimal part of the output + // 3. Concatenate them to produce the final result + seconds := strconv.FormatInt(truncatedDate.Unix(), 10) + nanosecondsOffset := strconv.FormatFloat(float64(truncatedDate.Nanosecond())/float64(time.Second), 'f', prec, 64) + + output := append([]byte(seconds), []byte(nanosecondsOffset)[1:]...) + + return output, nil +} + +// UnmarshalJSON is an implementation of the json.RawMessage interface and +// deserializes a [NumericDate] from a JSON representation, i.e. a +// [json.Number]. This number represents an UNIX epoch with either integer or +// non-integer seconds. +func (date *NumericDate) UnmarshalJSON(b []byte) (err error) { + var ( + number json.Number + f float64 + ) + + if err = json.Unmarshal(b, &number); err != nil { + return fmt.Errorf("could not parse NumericData: %w", err) + } + + if f, err = number.Float64(); err != nil { + return fmt.Errorf("could not convert json number value to float: %w", err) + } + + n := newNumericDateFromSeconds(f) + *date = *n + + return nil +} + +// ClaimStrings is basically just a slice of strings, but it can be either +// serialized from a string array or just a string. This type is necessary, +// since the "aud" claim can either be a single string or an array. +type ClaimStrings []string + +func (s *ClaimStrings) UnmarshalJSON(data []byte) (err error) { + var value interface{} + + if err = json.Unmarshal(data, &value); err != nil { + return err + } + + var aud []string + + switch v := value.(type) { + case string: + aud = append(aud, v) + case []string: + aud = ClaimStrings(v) + case []interface{}: + for _, vv := range v { + vs, ok := vv.(string) + if !ok { + return ErrInvalidType + } + aud = append(aud, vs) + } + case nil: + return nil + default: + return ErrInvalidType + } + + *s = aud + + return +} + +func (s ClaimStrings) MarshalJSON() (b []byte, err error) { + // This handles a special case in the JWT RFC. If the string array, e.g. + // used by the "aud" field, only contains one element, it MAY be serialized + // as a single string. This may or may not be desired based on the ecosystem + // of other JWT library used, so we make it configurable by the variable + // MarshalSingleStringAsArray. + if len(s) == 1 && !MarshalSingleStringAsArray { + return json.Marshal(s[0]) + } + + return json.Marshal([]string(s)) +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/validator.go b/vendor/github.com/golang-jwt/jwt/v5/validator.go new file mode 100644 index 00000000..008ecd87 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v5/validator.go @@ -0,0 +1,316 @@ +package jwt + +import ( + "crypto/subtle" + "fmt" + "time" +) + +// ClaimsValidator is an interface that can be implemented by custom claims who +// wish to execute any additional claims validation based on +// application-specific logic. The Validate function is then executed in +// addition to the regular claims validation and any error returned is appended +// to the final validation result. +// +// type MyCustomClaims struct { +// Foo string `json:"foo"` +// jwt.RegisteredClaims +// } +// +// func (m MyCustomClaims) Validate() error { +// if m.Foo != "bar" { +// return errors.New("must be foobar") +// } +// return nil +// } +type ClaimsValidator interface { + Claims + Validate() error +} + +// Validator is the core of the new Validation API. It is automatically used by +// a [Parser] during parsing and can be modified with various parser options. +// +// The [NewValidator] function should be used to create an instance of this +// struct. +type Validator struct { + // leeway is an optional leeway that can be provided to account for clock skew. + leeway time.Duration + + // timeFunc is used to supply the current time that is needed for + // validation. If unspecified, this defaults to time.Now. + timeFunc func() time.Time + + // requireExp specifies whether the exp claim is required + requireExp bool + + // verifyIat specifies whether the iat (Issued At) claim will be verified. + // According to https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 this + // only specifies the age of the token, but no validation check is + // necessary. However, if wanted, it can be checked if the iat is + // unrealistic, i.e., in the future. + verifyIat bool + + // expectedAud contains the audience this token expects. Supplying an empty + // string will disable aud checking. + expectedAud string + + // expectedIss contains the issuer this token expects. Supplying an empty + // string will disable iss checking. + expectedIss string + + // expectedSub contains the subject this token expects. Supplying an empty + // string will disable sub checking. + expectedSub string +} + +// NewValidator can be used to create a stand-alone validator with the supplied +// options. This validator can then be used to validate already parsed claims. +// +// Note: Under normal circumstances, explicitly creating a validator is not +// needed and can potentially be dangerous; instead functions of the [Parser] +// class should be used. +// +// The [Validator] is only checking the *validity* of the claims, such as its +// expiration time, but it does NOT perform *signature verification* of the +// token. +func NewValidator(opts ...ParserOption) *Validator { + p := NewParser(opts...) + return p.validator +} + +// Validate validates the given claims. It will also perform any custom +// validation if claims implements the [ClaimsValidator] interface. +// +// Note: It will NOT perform any *signature verification* on the token that +// contains the claims and expects that the [Claim] was already successfully +// verified. +func (v *Validator) Validate(claims Claims) error { + var ( + now time.Time + errs []error = make([]error, 0, 6) + err error + ) + + // Check, if we have a time func + if v.timeFunc != nil { + now = v.timeFunc() + } else { + now = time.Now() + } + + // We always need to check the expiration time, but usage of the claim + // itself is OPTIONAL by default. requireExp overrides this behavior + // and makes the exp claim mandatory. + if err = v.verifyExpiresAt(claims, now, v.requireExp); err != nil { + errs = append(errs, err) + } + + // We always need to check not-before, but usage of the claim itself is + // OPTIONAL. + if err = v.verifyNotBefore(claims, now, false); err != nil { + errs = append(errs, err) + } + + // Check issued-at if the option is enabled + if v.verifyIat { + if err = v.verifyIssuedAt(claims, now, false); err != nil { + errs = append(errs, err) + } + } + + // If we have an expected audience, we also require the audience claim + if v.expectedAud != "" { + if err = v.verifyAudience(claims, v.expectedAud, true); err != nil { + errs = append(errs, err) + } + } + + // If we have an expected issuer, we also require the issuer claim + if v.expectedIss != "" { + if err = v.verifyIssuer(claims, v.expectedIss, true); err != nil { + errs = append(errs, err) + } + } + + // If we have an expected subject, we also require the subject claim + if v.expectedSub != "" { + if err = v.verifySubject(claims, v.expectedSub, true); err != nil { + errs = append(errs, err) + } + } + + // Finally, we want to give the claim itself some possibility to do some + // additional custom validation based on a custom Validate function. + cvt, ok := claims.(ClaimsValidator) + if ok { + if err := cvt.Validate(); err != nil { + errs = append(errs, err) + } + } + + if len(errs) == 0 { + return nil + } + + return joinErrors(errs...) +} + +// verifyExpiresAt compares the exp claim in claims against cmp. This function +// will succeed if cmp < exp. Additional leeway is taken into account. +// +// If exp is not set, it will succeed if the claim is not required, +// otherwise ErrTokenRequiredClaimMissing will be returned. +// +// Additionally, if any error occurs while retrieving the claim, e.g., when its +// the wrong type, an ErrTokenUnverifiable error will be returned. +func (v *Validator) verifyExpiresAt(claims Claims, cmp time.Time, required bool) error { + exp, err := claims.GetExpirationTime() + if err != nil { + return err + } + + if exp == nil { + return errorIfRequired(required, "exp") + } + + return errorIfFalse(cmp.Before((exp.Time).Add(+v.leeway)), ErrTokenExpired) +} + +// verifyIssuedAt compares the iat claim in claims against cmp. This function +// will succeed if cmp >= iat. Additional leeway is taken into account. +// +// If iat is not set, it will succeed if the claim is not required, +// otherwise ErrTokenRequiredClaimMissing will be returned. +// +// Additionally, if any error occurs while retrieving the claim, e.g., when its +// the wrong type, an ErrTokenUnverifiable error will be returned. +func (v *Validator) verifyIssuedAt(claims Claims, cmp time.Time, required bool) error { + iat, err := claims.GetIssuedAt() + if err != nil { + return err + } + + if iat == nil { + return errorIfRequired(required, "iat") + } + + return errorIfFalse(!cmp.Before(iat.Add(-v.leeway)), ErrTokenUsedBeforeIssued) +} + +// verifyNotBefore compares the nbf claim in claims against cmp. This function +// will return true if cmp >= nbf. Additional leeway is taken into account. +// +// If nbf is not set, it will succeed if the claim is not required, +// otherwise ErrTokenRequiredClaimMissing will be returned. +// +// Additionally, if any error occurs while retrieving the claim, e.g., when its +// the wrong type, an ErrTokenUnverifiable error will be returned. +func (v *Validator) verifyNotBefore(claims Claims, cmp time.Time, required bool) error { + nbf, err := claims.GetNotBefore() + if err != nil { + return err + } + + if nbf == nil { + return errorIfRequired(required, "nbf") + } + + return errorIfFalse(!cmp.Before(nbf.Add(-v.leeway)), ErrTokenNotValidYet) +} + +// verifyAudience compares the aud claim against cmp. +// +// If aud is not set or an empty list, it will succeed if the claim is not required, +// otherwise ErrTokenRequiredClaimMissing will be returned. +// +// Additionally, if any error occurs while retrieving the claim, e.g., when its +// the wrong type, an ErrTokenUnverifiable error will be returned. +func (v *Validator) verifyAudience(claims Claims, cmp string, required bool) error { + aud, err := claims.GetAudience() + if err != nil { + return err + } + + if len(aud) == 0 { + return errorIfRequired(required, "aud") + } + + // use a var here to keep constant time compare when looping over a number of claims + result := false + + var stringClaims string + for _, a := range aud { + if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 { + result = true + } + stringClaims = stringClaims + a + } + + // case where "" is sent in one or many aud claims + if stringClaims == "" { + return errorIfRequired(required, "aud") + } + + return errorIfFalse(result, ErrTokenInvalidAudience) +} + +// verifyIssuer compares the iss claim in claims against cmp. +// +// If iss is not set, it will succeed if the claim is not required, +// otherwise ErrTokenRequiredClaimMissing will be returned. +// +// Additionally, if any error occurs while retrieving the claim, e.g., when its +// the wrong type, an ErrTokenUnverifiable error will be returned. +func (v *Validator) verifyIssuer(claims Claims, cmp string, required bool) error { + iss, err := claims.GetIssuer() + if err != nil { + return err + } + + if iss == "" { + return errorIfRequired(required, "iss") + } + + return errorIfFalse(iss == cmp, ErrTokenInvalidIssuer) +} + +// verifySubject compares the sub claim against cmp. +// +// If sub is not set, it will succeed if the claim is not required, +// otherwise ErrTokenRequiredClaimMissing will be returned. +// +// Additionally, if any error occurs while retrieving the claim, e.g., when its +// the wrong type, an ErrTokenUnverifiable error will be returned. +func (v *Validator) verifySubject(claims Claims, cmp string, required bool) error { + sub, err := claims.GetSubject() + if err != nil { + return err + } + + if sub == "" { + return errorIfRequired(required, "sub") + } + + return errorIfFalse(sub == cmp, ErrTokenInvalidSubject) +} + +// errorIfFalse returns the error specified in err, if the value is true. +// Otherwise, nil is returned. +func errorIfFalse(value bool, err error) error { + if value { + return nil + } else { + return err + } +} + +// errorIfRequired returns an ErrTokenRequiredClaimMissing error if required is +// true. Otherwise, nil is returned. +func errorIfRequired(required bool, claim string) error { + if required { + return newError(fmt.Sprintf("%s claim is required", claim), ErrTokenRequiredClaimMissing) + } else { + return nil + } +} diff --git a/vendor/github.com/gookit/color/color_16.go b/vendor/github.com/gookit/color/color_16.go index 0b70efe4..eda226a1 100644 --- a/vendor/github.com/gookit/color/color_16.go +++ b/vendor/github.com/gookit/color/color_16.go @@ -41,15 +41,27 @@ func (o Opts) String() string { * Basic 16 color definition *************************************************************/ -// Base value for foreground/background color -// base: fg 30~37, bg 40~47 -// light: fg 90~97, bg 100~107 +const ( + // OptMax max option value. range: 0 - 9 + OptMax = 10 + // DiffFgBg diff foreground and background color + DiffFgBg = 10 +) + +// Boundary value for foreground/background color 16 +// +// - base: fg 30~37, bg 40~47 +// - light: fg 90~97, bg 100~107 const ( FgBase uint8 = 30 + FgMax uint8 = 37 BgBase uint8 = 40 + BgMax uint8 = 47 HiFgBase uint8 = 90 + HiFgMax uint8 = 97 HiBgBase uint8 = 100 + HiBgMax uint8 = 107 ) // Foreground colors. basic foreground colors 30 - 37 @@ -94,7 +106,7 @@ const ( BgDefault Color = 49 ) -// Extra background color 100 - 107(非标准) +// Extra background color 100 - 107 (non-standard) const ( BgDarkGray Color = iota + 100 BgLightRed @@ -108,7 +120,7 @@ const ( BgGray Color = 100 ) -// Option settings +// Option settings. range: 0 - 9 const ( OpReset Color = iota // 0 重置所有设置 OpBold // 1 加粗 @@ -248,9 +260,9 @@ func (c Color) Println(a ...any) { doPrintlnV2(c.String(), a) } // lightCyan := Cyan.Light() // lightCyan.Print("message") func (c Color) Light() Color { - val := int(c) + val := uint8(c) if val >= 30 && val <= 47 { - return Color(uint8(c) + 60) + return Color(val + 60) } // don't change @@ -264,9 +276,9 @@ func (c Color) Light() Color { // cyan := LightCyan.Darken() // cyan.Print("message") func (c Color) Darken() Color { - val := int(c) + val := uint8(c) if val >= 90 && val <= 107 { - return Color(uint8(c) - 60) + return Color(val - 60) } // don't change @@ -324,7 +336,7 @@ func (c Color) RGB() RGBColor { return emptyRGBColor } - return HEX(Basic2hex(val)) + return HEX(Basic2hex(val), c.IsBg()) } // Code convert to code string. eg "35" @@ -337,8 +349,23 @@ func (c Color) String() string { return strconv.FormatInt(int64(c), 10) } +// IsBg check is background color +func (c Color) IsBg() bool { + val := uint8(c) + return val >= BgBase && val <= BgMax || val >= HiBgBase && val <= HiBgMax +} + +// IsFg check is foreground color +func (c Color) IsFg() bool { + val := uint8(c) + return val >= FgBase && val <= FgMax || val >= HiFgBase && val <= HiFgMax +} + +// IsOption check is option code: 0-9 +func (c Color) IsOption() bool { return uint8(c) < OptMax } + // IsValid color value -func (c Color) IsValid() bool { return c < 107 } +func (c Color) IsValid() bool { return uint8(c) < HiBgMax } /************************************************************* * basic color maps diff --git a/vendor/github.com/gookit/color/color_256.go b/vendor/github.com/gookit/color/color_256.go index 991e604c..79ae5f8d 100644 --- a/vendor/github.com/gookit/color/color_256.go +++ b/vendor/github.com/gookit/color/color_256.go @@ -43,7 +43,8 @@ const ( * 8bit(256) Color: Bit8Color Color256 *************************************************************/ -// Color256 256 color (8 bit), uint8 range at 0 - 255 +// Color256 256 color (8 bit), uint8 range at 0 - 255. +// Support 256 color on windows CMD, PowerShell // // 颜色值使用10进制和16进制都可 0x98 = 152 // @@ -54,10 +55,9 @@ const ( // // example: // -// fg color: [152, 0] -// bg color: [152, 1] +// fg color: [152, 0] +// bg color: [152, 1] // -// NOTICE: now support 256 color on windows CMD, PowerShell // lint warn - Name starts with package name type Color256 [2]uint8 type Bit8Color = Color256 // alias @@ -164,9 +164,7 @@ func (c Color256) String() string { } // IsFg color -func (c Color256) IsFg() bool { - return c[1] == AsFg -} +func (c Color256) IsFg() bool { return c[1] == AsFg } // ToFg 256 color func (c Color256) ToFg() Color256 { @@ -175,9 +173,7 @@ func (c Color256) ToFg() Color256 { } // IsBg color -func (c Color256) IsBg() bool { - return c[1] == AsBg -} +func (c Color256) IsBg() bool { return c[1] == AsBg } // ToBg 256 color func (c Color256) ToBg() Color256 { @@ -186,9 +182,7 @@ func (c Color256) ToBg() Color256 { } // IsEmpty value -func (c Color256) IsEmpty() bool { - return c[1] > 1 -} +func (c Color256) IsEmpty() bool { return c[1] > 1 } /************************************************************* * 8bit(256) Style diff --git a/vendor/github.com/gookit/color/color_rgb.go b/vendor/github.com/gookit/color/color_rgb.go index 724cf665..bc129b71 100644 --- a/vendor/github.com/gookit/color/color_rgb.go +++ b/vendor/github.com/gookit/color/color_rgb.go @@ -44,6 +44,7 @@ const ( *************************************************************/ // RGBColor definition. +// Support RGB color on Windows CMD, PowerShell // // The first to third digits represent the color value. // The last digit represents the foreground(0), background(1), >1 is unset value @@ -54,8 +55,6 @@ const ( // // 3rd: Fg=0, Bg=1, >1: unset value // RGBColor{30,144,255, 0} // RGBColor{30,144,255, 1} -// -// NOTICE: now support RGB color on Windows CMD, PowerShell type RGBColor [4]uint8 // create an empty RGBColor @@ -251,6 +250,18 @@ func (c RGBColor) String() string { return "" } +// ToBg convert to background color +func (c RGBColor) ToBg() RGBColor { + c[3] = AsBg + return c +} + +// ToFg convert to foreground color +func (c RGBColor) ToFg() RGBColor { + c[3] = AsFg + return c +} + // IsEmpty value func (c RGBColor) IsEmpty() bool { return c[3] > AsBg diff --git a/vendor/github.com/gookit/color/convert.go b/vendor/github.com/gookit/color/convert.go index 39aac7d2..c7103536 100644 --- a/vendor/github.com/gookit/color/convert.go +++ b/vendor/github.com/gookit/color/convert.go @@ -52,6 +52,7 @@ var ( // ---------- basic(16) <=> RGB color convert ---------- // refer from Hyper app + // Tip: only keep foreground color, background color need convert to foreground color for convert to RGB basic2hexMap = map[uint8]string{ 30: "000000", // black 31: "c51e14", // red @@ -61,7 +62,7 @@ var ( 35: "c839c5", // magenta 36: "20c5c6", // cyan 37: "c7c7c7", // white - // - don't add bg color + // - don't add bg color, convert to fg color for convert to RGB // 40: "000000", // black // 41: "c51e14", // red // 42: "1dc121", // green @@ -428,10 +429,11 @@ func HexToRGB(hex string) []int { return HexToRgb(hex) } // HexToRgb convert hex color string to RGB numbers // // Usage: -// rgb := HexToRgb("ccc") // rgb: [204 204 204] -// rgb := HexToRgb("aabbcc") // rgb: [170 187 204] -// rgb := HexToRgb("#aabbcc") // rgb: [170 187 204] -// rgb := HexToRgb("0xad99c0") // rgb: [170 187 204] +// +// rgb := HexToRgb("ccc") // rgb: [204 204 204] +// rgb := HexToRgb("aabbcc") // rgb: [170 187 204] +// rgb := HexToRgb("#aabbcc") // rgb: [170 187 204] +// rgb := HexToRgb("0xad99c0") // rgb: [170 187 204] func HexToRgb(hex string) (rgb []int) { hex = strings.TrimSpace(hex) if hex == "" { @@ -474,6 +476,7 @@ func Rgb2hex(rgb []int) string { return RgbToHex(rgb) } // RgbToHex convert RGB-code to hex-code // // Usage: +// // hex := RgbToHex([]int{170, 187, 204}) // hex: "aabbcc" func RgbToHex(rgb []int) string { hexNodes := make([]string, len(rgb)) @@ -488,10 +491,15 @@ func RgbToHex(rgb []int) string { * 4bit(16) color <=> RGB/True color *************************************************************/ +// BasicToHex convert basic color to hex string. +func BasicToHex(val uint8) string { + val = Bg2Fg(val) + return basic2hexMap[val] +} + // Basic2hex convert basic color to hex string. func Basic2hex(val uint8) string { - val = Fg2Bg(val) - return basic2hexMap[val] + return BasicToHex(val) } // Hex2basic convert hex string to basic color code. @@ -663,6 +671,7 @@ func C256ToRgbV1(val uint8) (rgb []uint8) { // returns r, g, and b in the set [0, 255]. // // Usage: +// // HslIntToRgb(0, 100, 50) // red // HslIntToRgb(120, 100, 50) // lime // HslIntToRgb(120, 100, 25) // dark green @@ -677,6 +686,7 @@ func HslIntToRgb(h, s, l int) (rgb []uint8) { // returns r, g, and b in the set [0, 255]. // // Usage: +// // rgbVals := HslToRgb(0, 1, 0.5) // red func HslToRgb(h, s, l float64) (rgb []uint8) { var r, g, b float64 diff --git a/vendor/github.com/gookit/color/style.go b/vendor/github.com/gookit/color/style.go index a009d1d6..353d39f1 100644 --- a/vendor/github.com/gookit/color/style.go +++ b/vendor/github.com/gookit/color/style.go @@ -37,7 +37,8 @@ func (s *Style) Add(cs ...Color) { *s = append(*s, cs...) } -// Render render text +// Render colored text +// // Usage: // // color.New(color.FgGreen).Render("text") @@ -46,8 +47,9 @@ func (s Style) Render(a ...any) string { return RenderCode(s.String(), a...) } -// Renderln render text line. +// Renderln render text with newline. // like Println, will add spaces for each argument +// // Usage: // // color.New(color.FgGreen).Renderln("text", "more") diff --git a/vendor/github.com/gorilla/mux/.editorconfig b/vendor/github.com/gorilla/mux/.editorconfig new file mode 100644 index 00000000..c6b74c3e --- /dev/null +++ b/vendor/github.com/gorilla/mux/.editorconfig @@ -0,0 +1,20 @@ +; https://editorconfig.org/ + +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_style = tab +indent_size = 4 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +eclint_indent_style = unset \ No newline at end of file diff --git a/vendor/github.com/gorilla/mux/.gitignore b/vendor/github.com/gorilla/mux/.gitignore new file mode 100644 index 00000000..84039fec --- /dev/null +++ b/vendor/github.com/gorilla/mux/.gitignore @@ -0,0 +1 @@ +coverage.coverprofile diff --git a/vendor/github.com/gorilla/mux/AUTHORS b/vendor/github.com/gorilla/mux/AUTHORS deleted file mode 100644 index b722392e..00000000 --- a/vendor/github.com/gorilla/mux/AUTHORS +++ /dev/null @@ -1,8 +0,0 @@ -# This is the official list of gorilla/mux authors for copyright purposes. -# -# Please keep the list sorted. - -Google LLC (https://opensource.google.com/) -Kamil Kisielk -Matt Silverlock -Rodrigo Moraes (https://github.com/moraes) diff --git a/vendor/github.com/gorilla/mux/LICENSE b/vendor/github.com/gorilla/mux/LICENSE index 6903df63..bb9d80bc 100644 --- a/vendor/github.com/gorilla/mux/LICENSE +++ b/vendor/github.com/gorilla/mux/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. +Copyright (c) 2023 The Gorilla Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/gorilla/mux/Makefile b/vendor/github.com/gorilla/mux/Makefile new file mode 100644 index 00000000..98f5ab75 --- /dev/null +++ b/vendor/github.com/gorilla/mux/Makefile @@ -0,0 +1,34 @@ +GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '') +GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +GO_SEC=$(shell which gosec 2> /dev/null || echo '') +GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest + +GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '') +GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest + +.PHONY: golangci-lint +golangci-lint: + $(if $(GO_LINT), ,go install $(GO_LINT_URI)) + @echo "##### Running golangci-lint" + golangci-lint run -v + +.PHONY: gosec +gosec: + $(if $(GO_SEC), ,go install $(GO_SEC_URI)) + @echo "##### Running gosec" + gosec ./... + +.PHONY: govulncheck +govulncheck: + $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI)) + @echo "##### Running govulncheck" + govulncheck ./... + +.PHONY: verify +verify: golangci-lint gosec govulncheck + +.PHONY: test +test: + @echo "##### Running tests" + go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./... \ No newline at end of file diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md index 35eea9f1..382513d5 100644 --- a/vendor/github.com/gorilla/mux/README.md +++ b/vendor/github.com/gorilla/mux/README.md @@ -1,12 +1,12 @@ # gorilla/mux -[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) -[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux) -[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) +![testing](https://github.com/gorilla/mux/actions/workflows/test.yml/badge.svg) +[![codecov](https://codecov.io/github/gorilla/mux/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/mux) +[![godoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) +[![sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) -![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png) -https://www.gorillatoolkit.org/pkg/mux +![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to their respective handler. @@ -247,32 +247,25 @@ type spaHandler struct { // file located at the index path on the SPA handler will be served. This // is suitable behavior for serving an SPA (single page application). func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // get the absolute path to prevent directory traversal - path, err := filepath.Abs(r.URL.Path) - if err != nil { - // if we failed to get the absolute path respond with a 400 bad request - // and stop - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // prepend the path with the path to the static directory - path = filepath.Join(h.staticPath, path) + // Join internally call path.Clean to prevent directory traversal + path := filepath.Join(h.staticPath, r.URL.Path) - // check whether a file exists at the given path - _, err = os.Stat(path) - if os.IsNotExist(err) { - // file does not exist, serve index.html + // check whether a file exists or is a directory at the given path + fi, err := os.Stat(path) + if os.IsNotExist(err) || fi.IsDir() { + // file does not exist or path is a directory, serve index.html http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath)) return - } else if err != nil { - // if we got an error (that wasn't that the file doesn't exist) stating the - // file, return a 500 internal server error and stop + } + + if err != nil { + // if we got an error (that wasn't that the file doesn't exist) stating the + // file, return a 500 internal server error and stop http.Error(w, err.Error(), http.StatusInternalServerError) - return + return } - // otherwise, use http.FileServer to serve the static dir + // otherwise, use http.FileServer to serve the static file http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r) } @@ -375,6 +368,19 @@ url, err := r.Get("article").URL("subdomain", "news", "id", "42") ``` +To find all the required variables for a given route when calling `URL()`, the method `GetVarNames()` is available: +```go +r := mux.NewRouter() +r.Host("{domain}"). + Path("/{group}/{item_id}"). + Queries("some_data1", "{some_data1}"). + Queries("some_data2", "{some_data2}"). + Name("article") + +// Will print [domain group item_id some_data1 some_data2] +fmt.Println(r.Get("article").GetVarNames()) + +``` ### Walking Routes The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example, @@ -572,7 +578,7 @@ func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler r := mux.NewRouter() r.HandleFunc("/", handler) -amw := authenticationMiddleware{} +amw := authenticationMiddleware{tokenUsers: make(map[string]string)} amw.Populate() r.Use(amw.Middleware) @@ -758,7 +764,8 @@ func TestMetricsHandler(t *testing.T) { rr := httptest.NewRecorder() - // Need to create a router that we can pass the request through so that the vars will be added to the context + // To add the vars to the context, + // we need to create a router through which we can pass the request. router := mux.NewRouter() router.HandleFunc("/metrics/{type}", MetricsHandler) router.ServeHTTP(rr, req) diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go index bd5a38b5..80601351 100644 --- a/vendor/github.com/gorilla/mux/doc.go +++ b/vendor/github.com/gorilla/mux/doc.go @@ -10,18 +10,18 @@ http.ServeMux, mux.Router matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are: - * Requests can be matched based on URL host, path, path prefix, schemes, - header and query values, HTTP methods or using custom matchers. - * URL hosts, paths and query values can have variables with an optional - regular expression. - * Registered URLs can be built, or "reversed", which helps maintaining - references to resources. - * Routes can be used as subrouters: nested routes are only tested if the - parent route matches. This is useful to define groups of routes that - share common conditions like a host, a path prefix or other repeated - attributes. As a bonus, this optimizes request matching. - * It implements the http.Handler interface so it is compatible with the - standard http.ServeMux. + - Requests can be matched based on URL host, path, path prefix, schemes, + header and query values, HTTP methods or using custom matchers. + - URL hosts, paths and query values can have variables with an optional + regular expression. + - Registered URLs can be built, or "reversed", which helps maintaining + references to resources. + - Routes can be used as subrouters: nested routes are only tested if the + parent route matches. This is useful to define groups of routes that + share common conditions like a host, a path prefix or other repeated + attributes. As a bonus, this optimizes request matching. + - It implements the http.Handler interface so it is compatible with the + standard http.ServeMux. Let's start registering a couple of URL paths and handlers: @@ -301,6 +301,5 @@ A more complex authentication middleware, which maps session token to users, cou r.Use(amw.Middleware) Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. - */ package mux diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go index 782a34b2..1e089906 100644 --- a/vendor/github.com/gorilla/mux/mux.go +++ b/vendor/github.com/gorilla/mux/mux.go @@ -31,24 +31,26 @@ func NewRouter() *Router { // It implements the http.Handler interface, so it can be registered to serve // requests: // -// var router = mux.NewRouter() +// var router = mux.NewRouter() // -// func main() { -// http.Handle("/", router) -// } +// func main() { +// http.Handle("/", router) +// } // // Or, for Google App Engine, register it in a init() function: // -// func init() { -// http.Handle("/", router) -// } +// func init() { +// http.Handle("/", router) +// } // // This will send all incoming requests to the router. type Router struct { // Configurable Handler to be used when no route matches. + // This can be used to render your own 404 Not Found errors. NotFoundHandler http.Handler // Configurable Handler to be used when the request method does not match the route. + // This can be used to render your own 405 Method Not Allowed errors. MethodNotAllowedHandler http.Handler // Routes to be matched, in order. diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go index 0144842b..5d05cfa0 100644 --- a/vendor/github.com/gorilla/mux/regexp.go +++ b/vendor/github.com/gorilla/mux/regexp.go @@ -22,10 +22,10 @@ type routeRegexpOptions struct { type regexpType int const ( - regexpTypePath regexpType = 0 - regexpTypeHost regexpType = 1 - regexpTypePrefix regexpType = 2 - regexpTypeQuery regexpType = 3 + regexpTypePath regexpType = iota + regexpTypeHost + regexpTypePrefix + regexpTypeQuery ) // newRouteRegexp parses a route template and returns a routeRegexp, @@ -195,7 +195,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { // url builds a URL part using the given values. func (r *routeRegexp) url(values map[string]string) (string, error) { - urlValues := make([]interface{}, len(r.varsN), len(r.varsN)) + urlValues := make([]interface{}, len(r.varsN)) for k, v := range r.varsN { value, ok := values[v] if !ok { diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go index 750afe57..e8f11df2 100644 --- a/vendor/github.com/gorilla/mux/route.go +++ b/vendor/github.com/gorilla/mux/route.go @@ -64,8 +64,18 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { match.MatchErr = nil } - matchErr = nil + matchErr = nil // nolint:ineffassign return false + } else { + // Multiple routes may share the same path but use different HTTP methods. For instance: + // Route 1: POST "/users/{id}". + // Route 2: GET "/users/{id}", parameters: "id": "[0-9]+". + // + // The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2", + // The router should return a "Not Found" error as no route fully matches this request. + if match.MatchErr == ErrMethodMismatch { + match.MatchErr = nil + } } } @@ -230,9 +240,9 @@ func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { // Headers adds a matcher for request header values. // It accepts a sequence of key/value pairs to be matched. For example: // -// r := mux.NewRouter() -// r.Headers("Content-Type", "application/json", -// "X-Requested-With", "XMLHttpRequest") +// r := mux.NewRouter().NewRoute() +// r.Headers("Content-Type", "application/json", +// "X-Requested-With", "XMLHttpRequest") // // The above route will only match if both request header values match. // If the value is an empty string, it will match any value if the key is set. @@ -255,9 +265,9 @@ func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { // HeadersRegexp accepts a sequence of key/value pairs, where the value has regex // support. For example: // -// r := mux.NewRouter() -// r.HeadersRegexp("Content-Type", "application/(text|json)", -// "X-Requested-With", "XMLHttpRequest") +// r := mux.NewRouter().NewRoute() +// r.HeadersRegexp("Content-Type", "application/(text|json)", +// "X-Requested-With", "XMLHttpRequest") // // The above route will only match if both the request header matches both regular expressions. // If the value is an empty string, it will match any value if the key is set. @@ -283,10 +293,10 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route { // // For example: // -// r := mux.NewRouter() -// r.Host("www.example.com") -// r.Host("{subdomain}.domain.com") -// r.Host("{subdomain:[a-z]+}.domain.com") +// r := mux.NewRouter().NewRoute() +// r.Host("www.example.com") +// r.Host("{subdomain}.domain.com") +// r.Host("{subdomain:[a-z]+}.domain.com") // // Variable names must be unique in a given route. They can be retrieved // calling mux.Vars(request). @@ -342,11 +352,11 @@ func (r *Route) Methods(methods ...string) *Route { // // For example: // -// r := mux.NewRouter() -// r.Path("/products/").Handler(ProductsHandler) -// r.Path("/products/{key}").Handler(ProductsHandler) -// r.Path("/articles/{category}/{id:[0-9]+}"). -// Handler(ArticleHandler) +// r := mux.NewRouter().NewRoute() +// r.Path("/products/").Handler(ProductsHandler) +// r.Path("/products/{key}").Handler(ProductsHandler) +// r.Path("/articles/{category}/{id:[0-9]+}"). +// Handler(ArticleHandler) // // Variable names must be unique in a given route. They can be retrieved // calling mux.Vars(request). @@ -377,8 +387,8 @@ func (r *Route) PathPrefix(tpl string) *Route { // It accepts a sequence of key/value pairs. Values may define variables. // For example: // -// r := mux.NewRouter() -// r.Queries("foo", "bar", "id", "{id:[0-9]+}") +// r := mux.NewRouter().NewRoute() +// r.Queries("foo", "bar", "id", "{id:[0-9]+}") // // The above route will only match if the URL contains the defined queries // values, e.g.: ?foo=bar&id=42. @@ -473,11 +483,11 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { // // It will test the inner routes only if the parent route matched. For example: // -// r := mux.NewRouter() -// s := r.Host("www.example.com").Subrouter() -// s.HandleFunc("/products/", ProductsHandler) -// s.HandleFunc("/products/{key}", ProductHandler) -// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) +// r := mux.NewRouter().NewRoute() +// s := r.Host("www.example.com").Subrouter() +// s.HandleFunc("/products/", ProductsHandler) +// s.HandleFunc("/products/{key}", ProductHandler) +// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) // // Here, the routes registered in the subrouter won't be tested if the host // doesn't match. @@ -497,36 +507,36 @@ func (r *Route) Subrouter() *Router { // It accepts a sequence of key/value pairs for the route variables. For // example, given this route: // -// r := mux.NewRouter() -// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Name("article") +// r := mux.NewRouter() +// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Name("article") // // ...a URL for it can be built using: // -// url, err := r.Get("article").URL("category", "technology", "id", "42") +// url, err := r.Get("article").URL("category", "technology", "id", "42") // // ...which will return an url.URL with the following path: // -// "/articles/technology/42" +// "/articles/technology/42" // // This also works for host variables: // -// r := mux.NewRouter() -// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Host("{subdomain}.domain.com"). -// Name("article") +// r := mux.NewRouter() +// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Host("{subdomain}.domain.com"). +// Name("article") // -// // url.String() will be "http://news.domain.com/articles/technology/42" -// url, err := r.Get("article").URL("subdomain", "news", -// "category", "technology", -// "id", "42") +// // url.String() will be "http://news.domain.com/articles/technology/42" +// url, err := r.Get("article").URL("subdomain", "news", +// "category", "technology", +// "id", "42") // // The scheme of the resulting url will be the first argument that was passed to Schemes: // -// // url.String() will be "https://example.com" -// r := mux.NewRouter() -// url, err := r.Host("example.com") -// .Schemes("https", "http").URL() +// // url.String() will be "https://example.com" +// r := mux.NewRouter().NewRoute() +// url, err := r.Host("example.com") +// .Schemes("https", "http").URL() // // All variables defined in the route are required, and their values must // conform to the corresponding patterns. @@ -718,6 +728,25 @@ func (r *Route) GetHostTemplate() (string, error) { return r.regexp.host.template, nil } +// GetVarNames returns the names of all variables added by regexp matchers +// These can be used to know which route variables should be passed into r.URL() +func (r *Route) GetVarNames() ([]string, error) { + if r.err != nil { + return nil, r.err + } + var varNames []string + if r.regexp.host != nil { + varNames = append(varNames, r.regexp.host.varsN...) + } + if r.regexp.path != nil { + varNames = append(varNames, r.regexp.path.varsN...) + } + for _, regx := range r.regexp.queries { + varNames = append(varNames, regx.varsN...) + } + return varNames, nil +} + // prepareVars converts the route variable pairs into a map. If the route has a // BuildVarsFunc, it is invoked. func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { diff --git a/vendor/github.com/itchyny/gojq/.dockerignore b/vendor/github.com/itchyny/gojq/.dockerignore index c8e02dc8..ac001636 100644 --- a/vendor/github.com/itchyny/gojq/.dockerignore +++ b/vendor/github.com/itchyny/gojq/.dockerignore @@ -6,4 +6,12 @@ *.exe *.test *.out -/.github/ +*.md +*.y +**/*.jq +**/*.json +**/*.yaml +**/*_test.go +.github +_gojq +_tools diff --git a/vendor/github.com/itchyny/gojq/CHANGELOG.md b/vendor/github.com/itchyny/gojq/CHANGELOG.md index cee2ce33..0a09d08d 100644 --- a/vendor/github.com/itchyny/gojq/CHANGELOG.md +++ b/vendor/github.com/itchyny/gojq/CHANGELOG.md @@ -1,4 +1,73 @@ # Changelog +## [v0.12.14](https://github.com/itchyny/gojq/compare/v0.12.13..v0.12.14) (2023-12-01) +* implement `abs`, `pick`, and `debug/1` functions +* implement `--raw-output0` option, and remove `--nul-output` (`-0`) option +* fix string multiplication by zero to emit an empty string +* fix zero divided by zero to emit an error, not `nan` +* fix modulo operator to emit `nan` if either side is `nan` +* fix `implode` function to emit replacement characters on invalid code points +* fix `stderr` function to output strings in raw format +* fix `error` function to throw an error even for `null` +* fix `walk` function on multiple outputs arguments +* fix `--from-file` option to work with `--arg` and `--argjson` options +* fix the default module search path `../lib` relative to the executable +* improve query parser to support comment continuation with backslash +* improve `modulemeta` function to include defined function names in the module +* improve search path of `import` and `include` directives to support `$ORIGIN` expansion +* remove deprecated `leaf_paths` function + +## [v0.12.13](https://github.com/itchyny/gojq/compare/v0.12.12..v0.12.13) (2023-06-01) +* implement `@urid` format string to decode URI values +* fix functions returning arrays not to emit nil slices (`flatten`, `group_by`, + `unique`, `unique_by`, `nth`, `indices`, `path`, and `modulemeta.deps`) + +## [v0.12.12](https://github.com/itchyny/gojq/compare/v0.12.11..v0.12.12) (2023-03-01) +* fix assignment operator (`=`) with overlapping paths and multiple values (`[[]] | .. = ..`) +* fix crash on multiplying large numbers to an empty string (`9223372036854775807 * ""`) +* improve zsh completion file + +## [v0.12.11](https://github.com/itchyny/gojq/compare/v0.12.10..v0.12.11) (2022-12-24) +* fix crash on assignment operator (`=`) with multiple values (`. = (0,0)`) +* fix `isnormal` and `normals` functions against subnormal numbers + +## [v0.12.10](https://github.com/itchyny/gojq/compare/v0.12.9..v0.12.10) (2022-12-01) +* fix `break` in `try`-`catch` query (`label $x | try break $x catch .`) +* fix path value validation for `getpath` function (`path(getpath([[0]][0]))`) +* fix path value validation for custom iterator functions +* fix `walk` function with argument emitting multiple values (`[1],{x:1} | walk(.,0)`) +* fix `@csv`, `@tsv`, `@sh` to escape the null character (`["\u0000"] | @csv,@tsv,@sh`) +* improve performance of assignment operator (`=`), update-assignment operator (`|=`), + `map_values`, `del`, `delpaths`, `walk`, `ascii_downcase`, and `ascii_upcase` functions + +## [v0.12.9](https://github.com/itchyny/gojq/compare/v0.12.8..v0.12.9) (2022-09-01) +* fix `fromjson` to emit error on unexpected trailing string +* fix path analyzer on variable argument evaluation (`def f($x): .y; path(f(.x))`) +* fix raw input option `--raw-input` (`-R`) to keep carriage returns and support 64KiB+ lines + +## [v0.12.8](https://github.com/itchyny/gojq/compare/v0.12.7..v0.12.8) (2022-06-01) +* implement `gojq.Compare` for comparing values in custom internal functions +* implement `gojq.TypeOf` for obtaining type name of values in custom internal functions +* implement `gojq.Preview` for previewing values for error messages of custom internal functions +* fix query lexer to parse string literals as JSON to support surrogate pairs (`"\ud83d\ude04"`) +* fix priority bug of declared and builtin functions (`def empty: .; null | select(.)`) +* fix string indexing by index out of bounds to emit `null` (`"abc" | .[3]`) +* fix array binding pattern not to match against strings (`"abc" as [$a] ?// $a | $a`) +* fix `sub` and `gsub` functions to emit results in the same order of jq +* fix `fromjson` to keep integer precision (`"10000000000000000" | fromjson + 1`) +* fix stream option to raise error against incomplete JSON input +* improve array updating index and string repetition to increase limitations +* improve `mktime` to support nanoseconds, just like `gmtime` and `now` +* improve query lexer to report unterminated string literals +* improve performance of string indexing and slicing by reducing allocations +* improve performance of object and array indexing, slicing, and iteration, + by validating path values by comparing data addresses. This change improves jq + compatibility of path value validation (`{} | {}.x = 0`, `[0] | [.[]][] = 1`). + Also optimize constant indexing and slicing by specialized instruction +* improve performance of `add` (on array of strings), `flatten`, `min`, `max`, + `sort`, `unique`, `join`, `to_entries`, `from_entries`, `indices`, `index`, + `rindex`, `startswith`, `endswith`, `ltrimstr`, `rtrimstr`, `explode`, + `capture`, `sub`, and `gsub` functions + ## [v0.12.7](https://github.com/itchyny/gojq/compare/v0.12.6..v0.12.7) (2022-03-01) * fix precedence of try expression against operators (`try 0 * error(0)`) * fix iterator suffix with optional operator (`0 | .x[]?`) @@ -187,7 +256,7 @@ ## [v0.7.0](https://github.com/itchyny/gojq/compare/v0.6.0..v0.7.0) (2019-12-22) * implement YAML input (`--yaml-input`) and output (`--yaml-output`) * fix pipe in object value -* fix precedence of if, try, reduce and foreach expressions +* fix precedence of `if`, `try`, `reduce` and `foreach` expressions * release from GitHub Actions ## [v0.6.0](https://github.com/itchyny/gojq/compare/v0.5.0..v0.6.0) (2019-08-26) diff --git a/vendor/github.com/itchyny/gojq/Dockerfile b/vendor/github.com/itchyny/gojq/Dockerfile index 7beaf4ed..75f011ec 100644 --- a/vendor/github.com/itchyny/gojq/Dockerfile +++ b/vendor/github.com/itchyny/gojq/Dockerfile @@ -1,6 +1,8 @@ -FROM golang:1.17 AS builder +FROM golang:1.21 AS builder WORKDIR /app +COPY go.* ./ +RUN go mod download COPY . . ENV CGO_ENABLED 0 RUN make build diff --git a/vendor/github.com/itchyny/gojq/LICENSE b/vendor/github.com/itchyny/gojq/LICENSE index e3fc027c..3f4fcb26 100644 --- a/vendor/github.com/itchyny/gojq/LICENSE +++ b/vendor/github.com/itchyny/gojq/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019-2022 itchyny +Copyright (c) 2019-2023 itchyny Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/itchyny/gojq/Makefile b/vendor/github.com/itchyny/gojq/Makefile index b5de9126..b7cdb400 100644 --- a/vendor/github.com/itchyny/gojq/Makefile +++ b/vendor/github.com/itchyny/gojq/Makefile @@ -1,8 +1,8 @@ BIN := gojq VERSION := $$(make -s show-version) VERSION_PATH := cli -CURRENT_REVISION := $(shell git rev-parse --short HEAD) -BUILD_LDFLAGS := "-s -w -X github.com/itchyny/$(BIN)/cli.revision=$(CURRENT_REVISION)" +CURRENT_REVISION = $(shell git rev-parse --short HEAD) +BUILD_LDFLAGS = "-s -w -X github.com/itchyny/$(BIN)/cli.revision=$(CURRENT_REVISION)" GOBIN ?= $(shell go env GOPATH)/bin SHELL := /bin/bash @@ -19,7 +19,7 @@ build-dev: parser.go builtin.go .PHONY: build-debug build-debug: parser.go builtin.go - go build -tags debug -ldflags=$(BUILD_LDFLAGS) -o $(BIN) ./cmd/$(BIN) + go build -tags gojq_debug -ldflags=$(BUILD_LDFLAGS) -o $(BIN) ./cmd/$(BIN) builtin.go: builtin.jq parser.go.y parser.go query.go operator.go _tools/* GOOS= GOARCH= go generate @@ -33,26 +33,26 @@ $(GOBIN)/goyacc: .PHONY: install install: - go install -ldflags=$(BUILD_LDFLAGS) ./... + go install -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) .PHONY: install-dev install-dev: parser.go builtin.go - go install -ldflags=$(BUILD_LDFLAGS) ./... + go install -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) .PHONY: install-debug install-debug: parser.go builtin.go - go install -tags debug -ldflags=$(BUILD_LDFLAGS) ./... + go install -tags gojq_debug -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) .PHONY: show-version show-version: $(GOBIN)/gobump - @gobump show -r $(VERSION_PATH) + @gobump show -r "$(VERSION_PATH)" $(GOBIN)/gobump: @go install github.com/x-motemen/gobump/cmd/gobump@latest .PHONY: cross cross: $(GOBIN)/goxz CREDITS - goxz -n $(BIN) -pv=v$(VERSION) -include _$(BIN) -arch=amd64,arm64 \ + goxz -n $(BIN) -pv=v$(VERSION) -include _$(BIN) \ -build-ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) $(GOBIN)/goxz: @@ -72,7 +72,7 @@ test: build .PHONY: lint lint: $(GOBIN)/staticcheck go vet ./... - staticcheck -checks all,-ST1000 -tags debug ./... + staticcheck -checks all -tags gojq_debug ./... $(GOBIN)/staticcheck: go install honnef.co/go/tools/cmd/staticcheck@latest @@ -89,27 +89,15 @@ clean: .PHONY: update update: export GOPROXY=direct update: - rm -f go.sum && go get -u -d ./... && go get -d github.com/mattn/go-runewidth@v0.0.9 && go mod tidy - sed -i.bak '/require (/,/)/d' go.dev.mod && rm -f go.dev.{sum,mod.bak} + go get -u -d ./... && go mod tidy + go mod edit -modfile=go.dev.mod -droprequire=github.com/itchyny/{astgen,timefmt}-go go get -u -d -modfile=go.dev.mod github.com/itchyny/{astgen,timefmt}-go && go generate .PHONY: bump bump: $(GOBIN)/gobump -ifneq ($(shell git status --porcelain),) - $(error git workspace is dirty) -endif -ifneq ($(shell git rev-parse --abbrev-ref HEAD),main) - $(error current branch is not main) -endif + test -z "$$(git status --porcelain || echo .)" + test "$$(git branch --show-current)" = "main" @gobump up -w "$(VERSION_PATH)" git commit -am "bump up version to $(VERSION)" git tag "v$(VERSION)" - git push origin main - git push origin "refs/tags/v$(VERSION)" - -.PHONY: upload -upload: $(GOBIN)/ghr - ghr "v$(VERSION)" goxz - -$(GOBIN)/ghr: - go install github.com/tcnksm/ghr@latest + git push --atomic origin main tag "v$(VERSION)" diff --git a/vendor/github.com/itchyny/gojq/README.md b/vendor/github.com/itchyny/gojq/README.md index baba061f..11b4cd17 100644 --- a/vendor/github.com/itchyny/gojq/README.md +++ b/vendor/github.com/itchyny/gojq/README.md @@ -1,11 +1,11 @@ # gojq -[![CI Status](https://github.com/itchyny/gojq/workflows/CI/badge.svg)](https://github.com/itchyny/gojq/actions) +[![CI Status](https://github.com/itchyny/gojq/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/itchyny/gojq/actions?query=branch:main) [![Go Report Card](https://goreportcard.com/badge/github.com/itchyny/gojq)](https://goreportcard.com/report/github.com/itchyny/gojq) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchyny/gojq/blob/main/LICENSE) [![release](https://img.shields.io/github/release/itchyny/gojq/all.svg)](https://github.com/itchyny/gojq/releases) [![pkg.go.dev](https://pkg.go.dev/badge/github.com/itchyny/gojq)](https://pkg.go.dev/github.com/itchyny/gojq) -### Pure Go implementation of [jq](https://github.com/stedolan/jq) +### Pure Go implementation of [jq](https://github.com/jqlang/jq) This is an implementation of jq command written in Go language. You can also embed gojq as a library to your Go products. @@ -75,12 +75,11 @@ docker run -i --rm ghcr.io/itchyny/gojq ## Difference to jq - gojq is purely implemented with Go language and is completely portable. jq depends on the C standard library so the availability of math functions depends on the library. jq also depends on the regular expression library and it makes build scripts complex. - gojq implements nice error messages for invalid query and JSON input. The error message of jq is sometimes difficult to tell where to fix the query. -- gojq does not keep the order of object keys. I understand this might cause problems for some scripts but basically, we should not rely on the order of object keys. Due to this limitation, gojq does not have `keys_unsorted` function and `--sort-keys` (`-S`) option. I would implement when ordered map is implemented in the standard library of Go but I'm less motivated. Also, gojq assumes only valid JSON input while jq deals with some JSON extensions; `NaN`, `Infinity` and `[000]`. -- gojq supports arbitrary-precision integer calculation while jq does not. This is important to keep the precision of numeric IDs or nanosecond values. You can also use gojq to solve some mathematical problems which require big integers. Note that mathematical functions convert integers to floating-point numbers; only addition, subtraction, multiplication, modulo operation, and division (when divisible) keep integer precisions. When you want to calculate floor division of big integers, use `def intdiv($x; $y): ($x - $x % $y) / $y;`, instead of `$x / $y`. -- gojq fixes various bugs of jq. gojq correctly deletes elements of arrays by `|= empty` ([jq#2051](https://github.com/stedolan/jq/issues/2051)). gojq fixes `try`/`catch` handling ([jq#1859](https://github.com/stedolan/jq/issues/1859), [jq#1885](https://github.com/stedolan/jq/issues/1885), [jq#2140](https://github.com/stedolan/jq/issues/2140)). gojq fixes `nth/2` to output nothing when the count is equal to or larger than the stream size ([jq#1867](https://github.com/stedolan/jq/issues/1867)). gojq consistently counts by characters (not by bytes) in `index`, `rindex`, and `indices` functions; `"12345" | .[index("3"):]` results in `"345"` ([jq#1430](https://github.com/stedolan/jq/issues/1430), [jq#1624](https://github.com/stedolan/jq/issues/1624)), and supports string indexing; `"abcde"[2]` ([jq#1520](https://github.com/stedolan/jq/issues/1520)). gojq accepts indexing query `.e0` ([jq#1526](https://github.com/stedolan/jq/issues/1526), [jq#1651](https://github.com/stedolan/jq/issues/1651)), and allows `gsub` to handle patterns including `"^"` ([jq#2148](https://github.com/stedolan/jq/issues/2148)). gojq improves variable lexer to allow using keywords for variable names, especially in binding patterns, also disallows spaces after `$` ([jq#526](https://github.com/stedolan/jq/issues/526)). gojq fixes handling files with no newline characters at the end ([jq#2374](https://github.com/stedolan/jq/issues/2374)). -- gojq implements `@uri` to escape all the reserved characters defined in RFC 3986, Sec. 2.2 ([jq#1506](https://github.com/stedolan/jq/issues/1506)), and fixes `@base64d` to allow binary string as the decoded string ([jq#1931](https://github.com/stedolan/jq/issues/1931)). gojq improves time formatting and parsing, deals with `%f` in `strftime` and `strptime` ([jq#1409](https://github.com/stedolan/jq/issues/1409)), parses timezone offsets with `fromdate` and `fromdateiso8601` ([jq#1053](https://github.com/stedolan/jq/issues/1053)), supports timezone name/offset with `%Z`/`%z` in `strptime` ([jq#929](https://github.com/stedolan/jq/issues/929), [jq#2195](https://github.com/stedolan/jq/issues/2195)), and looks up correct timezone during daylight saving time on formatting with `%Z` ([jq#1912](https://github.com/stedolan/jq/issues/1912)). -- gojq does not support some functions intentionally; `get_jq_origin`, `get_prog_origin`, `get_search_list` (unstable, not listed in jq document), `input_line_number`, `$__loc__` (performance issue), `recurse_down` (deprecated in jq). gojq does not support some flags; `--ascii-output, -a` (performance issue), `--seq` (not used commonly), `--sort-keys, -S` (sorts by default because `map[string]interface{}` does not keep the order), `--unbuffered` (unbuffered by default). gojq normalizes floating-point numbers to fit to double-precision floating-point numbers. gojq does not support some regular expression flags (regular expression engine differences). gojq does not support BOM (`encoding/json` does not support this). gojq disallows using keywords for function names (declaration of `def true: .;` is a confusing query). -- gojq supports reading from YAML input (`--yaml-input`) while jq does not. gojq also supports YAML output (`--yaml-output`). +- gojq does not keep the order of object keys. I understand this might cause problems for some scripts but basically, we should not rely on the order of object keys. Due to this limitation, gojq does not have `keys_unsorted` function and `--sort-keys` (`-S`) option. I would implement when ordered map is implemented in the standard library of Go but I'm less motivated. +- gojq supports arbitrary-precision integer calculation while jq does not; jq loses the precision of large integers when calculation is involved. Note that even with gojq, all mathematical functions, including `floor` and `round`, convert integers to floating-point numbers; only addition, subtraction, multiplication, modulo, and division operators (when divisible) keep the integer precision. To calculate floor division of integers without losing the precision, use `def idivide($n): (. - . % $n) / $n;`. To round down floating-point numbers to integers, use `def ifloor: floor | tostring | tonumber;`, but note that this function does not work with large floating-point numbers and also loses the precision of large integers. +- gojq behaves differently than jq in some features, hoping that jq will fix the behaviors in the future. gojq consistently counts by characters (not by bytes) in `index`, `rindex`, and `indices` functions; `"12345" | .[index("3"):]` results in `"345"` ([jq#1430](https://github.com/jqlang/jq/issues/1430), [jq#1624](https://github.com/jqlang/jq/issues/1624)). gojq supports string indexing; `"abcde"[2]` ([jq#1520](https://github.com/jqlang/jq/issues/1520)). gojq fixes handling files with no newline characters at the end ([jq#2374](https://github.com/jqlang/jq/issues/2374)). gojq consistently truncates down floating-point number indices both in indexing (`[0] | .[0.5]` results in `0`), and slicing (`[0,1,2] | .[0.5:1.5]` results in `[0]`). gojq parses unary operators with higher precedence than variable binding (`[-1 as $x | 1,$x]` results in `[1,-1]` not `[-1,-1]`). gojq fixes `@base64d` to allow binary string as the decoded string ([jq#1931](https://github.com/jqlang/jq/issues/1931)). gojq improves time formatting and parsing; deals with `%f` in `strftime` and `strptime` ([jq#1409](https://github.com/jqlang/jq/issues/1409)), parses timezone offsets with `fromdate` and `fromdateiso8601` ([jq#1053](https://github.com/jqlang/jq/issues/1053)), supports timezone name/offset with `%Z`/`%z` in `strptime` ([jq#929](https://github.com/jqlang/jq/issues/929), [jq#2195](https://github.com/jqlang/jq/issues/2195)), and looks up correct timezone during daylight saving time on formatting with `%Z` ([jq#1912](https://github.com/jqlang/jq/issues/1912)). gojq supports nanoseconds in date and time functions. +- gojq does not support some functions intentionally; `get_jq_origin`, `get_prog_origin`, `get_search_list` (unstable, not listed in jq document), `input_line_number`, `$__loc__` (performance issue), `recurse_down` (deprecated in jq). gojq does not support some flags; `--ascii-output, -a` (performance issue), `--seq` (not used commonly), `--sort-keys, -S` (sorts by default because `map[string]any` does not keep the order), `--unbuffered` (unbuffered by default). gojq does not parse JSON extensions supported by jq; `NaN`, `Infinity`, and `[000]`. gojq normalizes floating-point numbers to fit to double-precision floating-point numbers. gojq does not support or behaves differently with some regular expression metacharacters and flags (regular expression engine differences). gojq does not support BOM (`encoding/json` does not support this). gojq disallows using keywords for function names (`def true: .; true` is a confusing query), and module name prefixes in function declarations (using module prefixes like `def m::f: .;` is undocumented). +- gojq supports reading from YAML input (`--yaml-input`) while jq does not. gojq also supports YAML output (`--yaml-output`). gojq supports `@urid` format string ([jq#798](https://github.com/jqlang/jq/issues/798), [jq#2261](https://github.com/jqlang/jq/issues/2261)). ### Color configuration The gojq command automatically disables coloring output when the output is not a tty. @@ -109,7 +108,7 @@ func main() { if err != nil { log.Fatalln(err) } - input := map[string]interface{}{"foo": []interface{}{1, 2, 3}} + input := map[string]any{"foo": []any{1, 2, 3}} iter := query.Run(input) // or query.RunWithContext for { v, ok := iter.Next() @@ -127,10 +126,10 @@ func main() { - Firstly, use [`gojq.Parse(string) (*Query, error)`](https://pkg.go.dev/github.com/itchyny/gojq#Parse) to get the query from a string. - Secondly, get the result iterator - using [`query.Run`](https://pkg.go.dev/github.com/itchyny/gojq#Query.Run) or [`query.RunWithContext`](https://pkg.go.dev/github.com/itchyny/gojq#Query.RunWithContext) - - or alternatively, compile the query using [`gojq.Compile`](https://pkg.go.dev/github.com/itchyny/gojq#Compile) and then [`code.Run`](https://pkg.go.dev/github.com/itchyny/gojq#Code.Run) or [`code.RunWithContext`](https://pkg.go.dev/github.com/itchyny/gojq#Code.RunWithContext). You can reuse the `*Code` against multiple inputs to avoid compilation of the same query. - - In either case, you cannot use custom type values as the query input. The type should be `[]interface{}` for an array and `map[string]interface{}` for a map (just like decoded to an `interface{}` using the [encoding/json](https://golang.org/pkg/encoding/json/) package). You can't use `[]int` or `map[string]string`, for example. If you want to query your custom struct, marshal to JSON, unmarshal to `interface{}` and use it as the query input. -- Thirdly, iterate through the results using [`iter.Next() (interface{}, bool)`](https://pkg.go.dev/github.com/itchyny/gojq#Iter). The iterator can emit an error so make sure to handle it. The method returns `true` with results, and `false` when the iterator terminates. - - The return type is not `(interface{}, error)` because iterators can emit multiple errors and you can continue after an error. It is difficult for the iterator to tell the termination in this situation. + - or alternatively, compile the query using [`gojq.Compile`](https://pkg.go.dev/github.com/itchyny/gojq#Compile) and then [`code.Run`](https://pkg.go.dev/github.com/itchyny/gojq#Code.Run) or [`code.RunWithContext`](https://pkg.go.dev/github.com/itchyny/gojq#Code.RunWithContext). You can reuse the `*Code` against multiple inputs to avoid compilation of the same query. But for arguments of `code.Run`, do not give values sharing same data between multiple calls. + - In either case, you cannot use custom type values as the query input. The type should be `[]any` for an array and `map[string]any` for a map (just like decoded to an `any` using the [encoding/json](https://golang.org/pkg/encoding/json/) package). You can't use `[]int` or `map[string]string`, for example. If you want to query your custom struct, marshal to JSON, unmarshal to `any` and use it as the query input. +- Thirdly, iterate through the results using [`iter.Next() (any, bool)`](https://pkg.go.dev/github.com/itchyny/gojq#Iter). The iterator can emit an error so make sure to handle it. The method returns `true` with results, and `false` when the iterator terminates. + - The return type is not `(any, error)` because iterators can emit multiple errors and you can continue after an error. It is difficult for the iterator to tell the termination in this situation. - Note that the result iterator may emit infinite number of values; `repeat(0)` and `range(infinite)`. It may stuck with no output value; `def f: f; f`. Use `RunWithContext` when you want to limit the execution time. [`gojq.Compile`](https://pkg.go.dev/github.com/itchyny/gojq#Compile) allows to configure the following compiler options. diff --git a/vendor/github.com/itchyny/gojq/_gojq b/vendor/github.com/itchyny/gojq/_gojq index 4c94718a..01e4c4f7 100644 --- a/vendor/github.com/itchyny/gojq/_gojq +++ b/vendor/github.com/itchyny/gojq/_gojq @@ -2,31 +2,42 @@ _gojq() { - _arguments -C \ - '(-c --compact-output)'{-c,--compact-output}'[compact output]' \ - '(-r --raw-output)'{-r,--raw-output}'[output raw strings]' \ - '(-j --join-output)'{-j,--join-output}'[stop printing a newline after each output]' \ - '(-0 --nul-output)'{-0,--nul-output}'[print NUL after each output]' \ - '(-C --color-output)'{-C,--color-output}'[colorize output even if piped]' \ - '(-M --monochrome-output)'{-M,--monochrome-output}'[stop colorizing output]' \ - '(--yaml-output)'--yaml-output'[output by YAML]' \ - '(--indent)'--indent'[number of spaces for indentation]:indentation count' \ - '(--tab)'--tab'[use tabs for indentation]' \ + _arguments -s -S \ + '(-r --raw-output --raw-output0 -j --join-output)'{-r,--raw-output}'[output raw strings]' \ + '(-r --raw-output -j --join-output)--raw-output0[implies -r with NUL character delimiter]' \ + '(-r --raw-output --raw-output0 -j --join-output)'{-j,--join-output}'[implies -r with no newline delimiter]' \ + '(-c --compact-output --indent --tab --yaml-output)'{-c,--compact-output}'[output without pretty-printing]' \ + '(-c --compact-output --tab --yaml-output)--indent=[number of spaces for indentation]:indentation count:(2 4 8)' \ + '(-c --compact-output --indent --yaml-output)--tab[use tabs for indentation]' \ + '(-c --compact-output --indent --tab )--yaml-output[output in YAML format]' \ + '(-C --color-output -M --monochrome-output)'{-C,--color-output}'[output with colors even if piped]' \ + '(-C --color-output -M --monochrome-output)'{-M,--monochrome-output}'[output without colors]' \ '(-n --null-input)'{-n,--null-input}'[use null as input value]' \ - '(-R --raw-input)'{-R,--raw-input}'[read input as raw strings]' \ + '(-R --raw-input --stream --yaml-input)'{-R,--raw-input}'[read input as raw strings]' \ + '(-R --raw-input --yaml-input)--stream[parse input in stream fashion]' \ + '(-R --raw-input --stream )--yaml-input[read input as YAML format]' \ '(-s --slurp)'{-s,--slurp}'[read all inputs into an array]' \ - '(--stream)'--stream'[parse input in stream fashion]' \ - '(--yaml-input)'--yaml-input'[read input as YAML]' \ - '(-f --from-file)'{-f,--from-file}'[load query from file]:filename of jq query:_files' \ - '(-L)'-L'[directory to search modules from]:module directory:_directories' \ - '(--arg)'--arg'[set variable to string value]:variable name:' \ - '(--argjson)'--argjson'[set variable to JSON value]:variable name:' \ - '(--slurpfile)'--slurpfile'[set variable to the JSON contents of the file]:variable name:' \ - '(--rawfile)'--rawfile'[set variable to the contents of the file]:variable name:' \ - '(--args)'--args'[consume remaining arguments as positional string values]' \ - '(--jsonargs)'--jsonargs'[consume remaining arguments as positional JSON values]' \ + '(-f --from-file 1)'{-f,--from-file}'[load query from file]:filename of jq query:_files' \ + '*-L=[directory to search modules from]:module directory:_directories' \ + '*--arg[set a string value to a variable]:variable name: :string value' \ + '*--argjson[set a JSON value to a variable]:variable name: :JSON value' \ + '*--slurpfile[set the JSON contents of a file to a variable]:variable name: :JSON file:_files' \ + '*--rawfile[set the contents of a file to a variable]:variable name: :file:_files' \ + '*--args[consume remaining arguments as positional string values]' \ + '*--jsonargs[consume remaining arguments as positional JSON values]' \ '(-e --exit-status)'{-e,--exit-status}'[exit 1 when the last value is false or null]' \ - '(-v --version)'{-v,--version}'[print version]' \ - '(-h --help)'{-h,--help}'[print help]' \ - && ret=0 + '(- 1 *)'{-v,--version}'[display version information]' \ + '(- 1 *)'{-h,--help}'[display help information]' \ + '1: :_guard "^-([[:alpha:]0]#|-*)" "jq query"' \ + '*: :_gojq_args' +} + +_gojq_args() { + if (($words[(I)--args] > $words[(I)--jsonargs])); then + _message 'string value' + elif (($words[(I)--args] < $words[(I)--jsonargs])); then + _message 'JSON value' + else + _arguments '*:input file:_files' + fi } diff --git a/vendor/github.com/itchyny/gojq/builtin.go b/vendor/github.com/itchyny/gojq/builtin.go index 65f9e234..7da4a93e 100644 --- a/vendor/github.com/itchyny/gojq/builtin.go +++ b/vendor/github.com/itchyny/gojq/builtin.go @@ -4,82 +4,66 @@ package gojq func init() { builtinFuncDefs = map[string][]*FuncDef{ - "IN": []*FuncDef{&FuncDef{Name: "IN", Args: []string{"s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Left: &Query{Func: "s"}, Op: OpEq, Right: &Query{Func: "."}}, &Query{Func: "."}}}}}}, &FuncDef{Name: "IN", Args: []string{"src", "s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Left: &Query{Func: "src"}, Op: OpEq, Right: &Query{Func: "s"}}, &Query{Func: "."}}}}}}}, - "INDEX": []*FuncDef{&FuncDef{Name: "INDEX", Args: []string{"stream", "idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "stream"}}, Pattern: &Pattern{Name: "$row"}, Start: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Left: &Query{Func: "$row"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "idx_expr"}, Op: OpPipe, Right: &Query{Func: "tostring"}}}}}}, Op: OpAssign, Right: &Query{Func: "$row"}}}}}}, &FuncDef{Name: "INDEX", Args: []string{"idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "INDEX", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "idx_expr"}}}}}}}, - "JOIN": []*FuncDef{&FuncDef{Name: "JOIN", Args: []string{"$idx", "idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}}}}}}, &FuncDef{Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr"}, Body: &Query{Left: &Query{Func: "stream"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}}}, &FuncDef{Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr", "join_expr"}, Body: &Query{Left: &Query{Func: "stream"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "join_expr"}}}}}, - "all": []*FuncDef{&FuncDef{Name: "all", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "."}}}}}}, &FuncDef{Name: "all", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "y"}}}}}}, &FuncDef{Name: "all", Args: []string{"g", "y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{&Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "y"}, Op: OpAnd, Right: &Query{Func: "empty"}}}}}}}}}, - "any": []*FuncDef{&FuncDef{Name: "any", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "."}}}}}}, &FuncDef{Name: "any", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "y"}}}}}}, &FuncDef{Name: "any", Args: []string{"g", "y"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{&Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "y"}, Op: OpOr, Right: &Query{Func: "empty"}}}}}}}, Op: OpPipe, Right: &Query{Func: "not"}}}}, - "arrays": []*FuncDef{&FuncDef{Name: "arrays", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}}}}}}}, - "ascii_downcase": []*FuncDef{&FuncDef{Name: "ascii_downcase", Body: &Query{Left: &Query{Func: "explode"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeNumber, Number: "65"}}, Op: OpLe, Right: &Query{Func: "."}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "90"}}}}, Then: &Query{Left: &Query{Func: "."}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "32"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "implode"}}}}}, - "ascii_upcase": []*FuncDef{&FuncDef{Name: "ascii_upcase", Body: &Query{Left: &Query{Func: "explode"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeNumber, Number: "97"}}, Op: OpLe, Right: &Query{Func: "."}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "122"}}}}, Then: &Query{Left: &Query{Func: "."}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "32"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "implode"}}}}}, - "assign": []*FuncDef{&FuncDef{Name: "_assign", Args: []string{"ps", "$v"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Func: "ps"}}}}, Pattern: &Pattern{Name: "$p"}, Start: &Query{Func: "."}, Update: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Func: "$p"}, &Query{Func: "$v"}}}}}}}}}}, - "booleans": []*FuncDef{&FuncDef{Name: "booleans", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "boolean"}}}}}}}}}}, - "capture": []*FuncDef{&FuncDef{Name: "capture", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "capture", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "capture", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Op: OpNe, Right: &Query{Func: "null"}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{KeyQuery: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "add"}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}}}}}}, - "combinations": []*FuncDef{&FuncDef{Name: "combinations", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "combinations"}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$y"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "$x"}}}}, Op: OpAdd, Right: &Query{Func: "$y"}}}}}}}}}}}}}}}}}, &FuncDef{Name: "combinations", Args: []string{"n"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$dot"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "range", Args: []*Query{&Query{Func: "n"}}}}}, Op: OpPipe, Right: &Query{Func: "$dot"}}}}}, Op: OpPipe, Right: &Query{Func: "combinations"}}}}}}}}}, - "del": []*FuncDef{&FuncDef{Name: "del", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "delpaths", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Func: "f"}}}}}}}}}}}}}}, - "endswith": []*FuncDef{&FuncDef{Name: "endswith", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Then: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}}}}}, IsSlice: true}}}, Op: OpEq, Right: &Query{Func: "$x"}}, Else: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "endswith"}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "endswith"}}}}}}}}}}}}, - "finites": []*FuncDef{&FuncDef{Name: "finites", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "isfinite"}}}}}}}, - "first": []*FuncDef{&FuncDef{Name: "first", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}, &FuncDef{Name: "first", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}}}}, - "flatten": []*FuncDef{&FuncDef{Name: "_flatten", Args: []string{"$x"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "$x"}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_flatten", Args: []*Query{&Query{Left: &Query{Func: "$x"}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "."}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "add"}}}, &FuncDef{Name: "flatten", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$x"}, Op: OpLt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "flatten depth must not be negative"}}}}}}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_flatten", Args: []*Query{&Query{Func: "$x"}}}}}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}}, &FuncDef{Name: "flatten", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_flatten", Args: []*Query{&Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}, - "from_entries": []*FuncDef{&FuncDef{Name: "from_entries", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{KeyQuery: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "key"}}}, Op: OpAlt, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Key"}}}, Op: OpAlt, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Name"}}}}}}, Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "value"}}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "value"}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Value"}}}}}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "add"}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}}}}}, - "fromdate": []*FuncDef{&FuncDef{Name: "fromdate", Body: &Query{Func: "fromdateiso8601"}}}, - "fromdateiso8601": []*FuncDef{&FuncDef{Name: "fromdateiso8601", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strptime", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%S%z"}}}}}}}, Op: OpPipe, Right: &Query{Func: "mktime"}}}}, - "fromstream": []*FuncDef{&FuncDef{Name: "fromstream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "x", Val: &ObjectVal{Queries: []*Query{&Query{Func: "null"}}}}, &ObjectKeyVal{Key: "e", Val: &ObjectVal{Queries: []*Query{&Query{Func: "false"}}}}}}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$init"}}, Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}, Pattern: &Pattern{Name: "$i"}, Start: &Query{Func: "$init"}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Func: "$init"}, Else: &Query{Func: "."}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$i"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "2"}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "x"}}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "x"}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}}, - "group_by": []*FuncDef{&FuncDef{Name: "group_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_group_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, - "gsub": []*FuncDef{&FuncDef{Name: "gsub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "str"}, &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, &FuncDef{Name: "gsub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "str"}, &Query{Left: &Query{Func: "$flags"}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}}}, - "in": []*FuncDef{&FuncDef{Name: "in", Args: []string{"xs"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Func: "xs"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{&Query{Func: "$x"}}}}}}}}}}}}}, - "index": []*FuncDef{&FuncDef{Name: "index", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_lindex", Args: []*Query{&Query{Func: "$x"}}}}}}}, - "indices": []*FuncDef{&FuncDef{Name: "indices", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_indices", Args: []*Query{&Query{Func: "$x"}}}}}}}, - "inputs": []*FuncDef{&FuncDef{Name: "inputs", Body: &Query{Term: &Term{Type: TermTypeTry, Try: &Try{Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{&Query{Func: "input"}}}}}, Catch: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "break"}}}}, Then: &Query{Func: "empty"}, Else: &Query{Func: "error"}}}}}}}}}, - "inside": []*FuncDef{&FuncDef{Name: "inside", Args: []string{"xs"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Func: "xs"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "contains", Args: []*Query{&Query{Func: "$x"}}}}}}}}}}}}}, - "isempty": []*FuncDef{&FuncDef{Name: "isempty", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "false"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}, Op: OpComma, Right: &Query{Func: "true"}}}}}}}, - "iterables": []*FuncDef{&FuncDef{Name: "iterables", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpOr, Right: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}}}}}}}, - "join": []*FuncDef{&FuncDef{Name: "join", Args: []string{"$x"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_join", Args: []*Query{&Query{Func: "$x"}}}}}}}}, - "last": []*FuncDef{&FuncDef{Name: "last", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}, &FuncDef{Name: "last", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Func: "null"}, Update: &Query{Func: "$item"}}}}}}, - "leaf_paths": []*FuncDef{&FuncDef{Name: "leaf_paths", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "paths", Args: []*Query{&Query{Func: "scalars"}}}}}}}, - "limit": []*FuncDef{&FuncDef{Name: "limit", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Func: "$n"}, Update: &Query{Left: &Query{Func: "."}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}, Extract: &Query{Left: &Query{Func: "$item"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Else: &Query{Func: "empty"}}}}}}}}}}}, Elif: []*IfElif{&IfElif{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Func: "empty"}}}, Else: &Query{Func: "g"}}}}}}, - "ltrimstr": []*FuncDef{&FuncDef{Name: "ltrimstr", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "startswith", Args: []*Query{&Query{Func: "$x"}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}, IsSlice: true}}}}}}}}, - "map": []*FuncDef{&FuncDef{Name: "map", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}}}}, - "map_values": []*FuncDef{&FuncDef{Name: "map_values", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpModify, Right: &Query{Func: "f"}}}}, - "match": []*FuncDef{&FuncDef{Name: "match", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "match", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}, &Query{Func: "false"}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}}, - "max": []*FuncDef{&FuncDef{Name: "max", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "max_by", Args: []*Query{&Query{Func: "."}}}}}}}, - "max_by": []*FuncDef{&FuncDef{Name: "max_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_max_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, - "min": []*FuncDef{&FuncDef{Name: "min", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "min_by", Args: []*Query{&Query{Func: "."}}}}}}}, - "min_by": []*FuncDef{&FuncDef{Name: "min_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_min_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, - "modify": []*FuncDef{&FuncDef{Name: "_modify", Args: []string{"ps", "f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Func: "ps"}}}}, Pattern: &Pattern{Name: "$p"}, Start: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}, Update: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Op: OpAdd, Right: &Query{Func: "$p"}}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$q"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Func: "$q"}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{&Query{Func: "$q"}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}}}}}}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "$p"}}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "delpaths", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}}}}}}}}}, - "normals": []*FuncDef{&FuncDef{Name: "normals", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "isnormal"}}}}}}}, - "not": []*FuncDef{&FuncDef{Name: "not", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "."}, Then: &Query{Func: "false"}, Else: &Query{Func: "true"}}}}}}, - "nth": []*FuncDef{&FuncDef{Name: "nth", Args: []string{"$n"}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Func: "$n"}}}}}, &FuncDef{Name: "nth", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpLt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "nth doesn't support negative indices"}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Func: "$n"}, Update: &Query{Left: &Query{Func: "."}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}, Extract: &Query{Left: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpLt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Op: OpOr, Right: &Query{Func: "empty"}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "$item"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}}}}}}}}}}, - "nulls": []*FuncDef{&FuncDef{Name: "nulls", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Func: "null"}}}}}}}}, - "numbers": []*FuncDef{&FuncDef{Name: "numbers", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "number"}}}}}}}}}}, - "objects": []*FuncDef{&FuncDef{Name: "objects", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}}}}}, - "paths": []*FuncDef{&FuncDef{Name: "paths", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "recurse", Args: []*Query{&Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpOr, Right: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "length"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}}, &FuncDef{Name: "paths", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Func: "paths"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$p"}}, Body: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{&Query{Func: "$p"}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}}}}}}}}}}}}}}}}}, - "range": []*FuncDef{&FuncDef{Name: "range", Args: []string{"$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{&Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, &Query{Func: "$end"}, &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, &FuncDef{Name: "range", Args: []string{"$start", "$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{&Query{Func: "$start"}, &Query{Func: "$end"}, &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, &FuncDef{Name: "range", Args: []string{"$start", "$end", "$step"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{&Query{Func: "$start"}, &Query{Func: "$end"}, &Query{Func: "$step"}}}}}}}, - "recurse": []*FuncDef{&FuncDef{Name: "recurse", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "recurse", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Optional: true}}}}}}}}}, &FuncDef{Name: "recurse", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "r", Body: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Func: "r"}}}}}}}, Func: "r"}}, &FuncDef{Name: "recurse", Args: []string{"f", "cond"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "r", Body: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "cond"}}}}}, Op: OpPipe, Right: &Query{Func: "r"}}}}}}}}, Func: "r"}}}, - "repeat": []*FuncDef{&FuncDef{Name: "repeat", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_repeat", Body: &Query{Left: &Query{Func: "f"}, Op: OpComma, Right: &Query{Func: "_repeat"}}}}, Func: "_repeat"}}}, - "rindex": []*FuncDef{&FuncDef{Name: "rindex", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_rindex", Args: []*Query{&Query{Func: "$x"}}}}}}}, - "rtrimstr": []*FuncDef{&FuncDef{Name: "rtrimstr", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "endswith", Args: []*Query{&Query{Func: "$x"}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{End: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}}}}}, IsSlice: true}}}}}}}}, - "scalars": []*FuncDef{&FuncDef{Name: "scalars", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}}}}}}}, - "scan": []*FuncDef{&FuncDef{Name: "scan", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "scan", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "scan", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Left: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}, Op: OpAdd, Right: &Query{Func: "$flags"}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Index: &Index{Name: "string"}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}, - "select": []*FuncDef{&FuncDef{Name: "select", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "f"}, Then: &Query{Func: "."}, Else: &Query{Func: "empty"}}}}}}, - "sort": []*FuncDef{&FuncDef{Name: "sort", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sort_by", Args: []*Query{&Query{Func: "."}}}}}}}, - "sort_by": []*FuncDef{&FuncDef{Name: "sort_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_sort_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, - "splits": []*FuncDef{&FuncDef{Name: "splits", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "splits", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "split", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}}, - "startswith": []*FuncDef{&FuncDef{Name: "startswith", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Then: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{End: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}, IsSlice: true}}}, Op: OpEq, Right: &Query{Func: "$x"}}, Else: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "startswith"}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "startswith"}}}}}}}}}}}}, - "strings": []*FuncDef{&FuncDef{Name: "strings", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}}}}}}, - "sub": []*FuncDef{&FuncDef{Name: "sub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "str"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "sub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$in"}}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_sub", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, Then: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$r"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "captures"}}, &Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Op: OpNe, Right: &Query{Func: "null"}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{KeyQuery: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "add"}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "string", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "string"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$in"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}, End: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}, IsSlice: true}}}}}}, Op: OpAdd, Right: &Query{Func: "str"}}}}}}}, &ObjectKeyVal{Key: "offset", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "length"}}}}}}}}}}}, &ObjectKeyVal{Key: "matches", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "matches"}}, &Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}}}}}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$in"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}}}}}}}, Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "string", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{}}}}}}, &ObjectKeyVal{Key: "offset", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, &ObjectKeyVal{Key: "matches", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}, - "test": []*FuncDef{&FuncDef{Name: "test", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "test", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "test", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}, &Query{Func: "true"}}}}}}}, - "to_entries": []*FuncDef{&FuncDef{Name: "to_entries", Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "keys"}, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$k"}}, Body: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "key", Val: &ObjectVal{Queries: []*Query{&Query{Func: "$k"}}}}, &ObjectKeyVal{Key: "value", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Func: "$k"}}}}}}}}}}}}}}}}}}}}}, - "todate": []*FuncDef{&FuncDef{Name: "todate", Body: &Query{Func: "todateiso8601"}}}, - "todateiso8601": []*FuncDef{&FuncDef{Name: "todateiso8601", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strftime", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%SZ"}}}}}}}}}, - "tostream": []*FuncDef{&FuncDef{Name: "tostream", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{FuncDefs: []*FuncDef{&FuncDef{Name: "r", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Optional: true}}}}, Op: OpPipe, Right: &Query{Func: "r"}}}}, Op: OpComma, Right: &Query{Func: "."}}}}, Func: "r"}}}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$p"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{&Query{Func: "$p"}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Optional: true}}}}}}}, Pattern: &Pattern{Name: "$q"}, Start: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "$p"}, Op: OpComma, Right: &Query{Func: "."}}}}}, Update: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "$p"}, Op: OpAdd, Right: &Query{Func: "$q"}}}}}}}}}}}}}}}}, - "truncate_stream": []*FuncDef{&FuncDef{Name: "truncate_stream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$n"}}, Body: &Query{Left: &Query{Func: "null"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$input"}}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Op: OpPipe, Right: &Query{Func: "length"}}}}, Op: OpGt, Right: &Query{Func: "$n"}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$input"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, &Suffix{Index: &Index{Start: &Query{Func: "$n"}, IsSlice: true}}}}}}}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}}}}}}, - "unique": []*FuncDef{&FuncDef{Name: "unique", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "unique_by", Args: []*Query{&Query{Func: "."}}}}}}}, - "unique_by": []*FuncDef{&FuncDef{Name: "unique_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_unique_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, - "until": []*FuncDef{&FuncDef{Name: "until", Args: []string{"cond", "next"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_until", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "cond"}, Then: &Query{Func: "."}, Else: &Query{Left: &Query{Func: "next"}, Op: OpPipe, Right: &Query{Func: "_until"}}}}}}}, Func: "_until"}}}, - "values": []*FuncDef{&FuncDef{Name: "values", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Func: "null"}}}}}}}}, - "walk": []*FuncDef{&FuncDef{Name: "walk", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_walk", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpOr, Right: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map_values", Args: []*Query{&Query{Func: "_walk"}}}}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}, Func: "_walk"}}}, - "while": []*FuncDef{&FuncDef{Name: "while", Args: []string{"cond", "update"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_while", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "cond"}, Then: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "update"}, Op: OpPipe, Right: &Query{Func: "_while"}}}}}, Else: &Query{Func: "empty"}}}}}}, Func: "_while"}}}, - "with_entries": []*FuncDef{&FuncDef{Name: "with_entries", Args: []string{"f"}, Body: &Query{Left: &Query{Func: "to_entries"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Func: "f"}}}}}, Op: OpPipe, Right: &Query{Func: "from_entries"}}}}}, + "IN": {{Name: "IN", Args: []string{"s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Left: &Query{Func: "s"}, Op: OpEq, Right: &Query{Func: "."}}, {Func: "."}}}}}}, {Name: "IN", Args: []string{"src", "s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Left: &Query{Func: "src"}, Op: OpEq, Right: &Query{Func: "s"}}, {Func: "."}}}}}}}, + "INDEX": {{Name: "INDEX", Args: []string{"stream", "idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "stream"}}, Pattern: &Pattern{Name: "$row"}, Start: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Left: &Query{Func: "$row"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "idx_expr"}, Op: OpPipe, Right: &Query{Func: "tostring"}}}}}}, Op: OpAssign, Right: &Query{Func: "$row"}}}}}}, {Name: "INDEX", Args: []string{"idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "INDEX", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, {Func: "idx_expr"}}}}}}}, + "JOIN": {{Name: "JOIN", Args: []string{"$idx", "idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}}}}}}, {Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr"}, Body: &Query{Left: &Query{Func: "stream"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}}}, {Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr", "join_expr"}, Body: &Query{Left: &Query{Func: "stream"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "join_expr"}}}}}, + "_assign": {}, + "_modify": {}, + "all": {{Name: "all", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{{Func: "."}}}}}}, {Name: "all", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, {Func: "y"}}}}}}, {Name: "all", Args: []string{"g", "y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "y"}, Op: OpPipe, Right: &Query{Func: "not"}}}}}}}}}}}}}, + "any": {{Name: "any", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Func: "."}}}}}}, {Name: "any", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, {Func: "y"}}}}}}, {Name: "any", Args: []string{"g", "y"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Func: "y"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "not"}}}}, + "arrays": {{Name: "arrays", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}}}}}}}, + "booleans": {{Name: "booleans", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "boolean"}}}}}}}}}}, + "capture": {{Name: "capture", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "capture", Args: []*Query{{Func: "$re"}, {Func: "null"}}}}}}, {Name: "capture", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Func: "$re"}, {Func: "$flags"}}}}}, Op: OpPipe, Right: &Query{Func: "_capture"}}}}, + "combinations": {{Name: "combinations", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, SuffixList: []*Suffix{{Iter: true}, {Bind: &Bind{Patterns: []*Pattern{{Name: "$x"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "$x"}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}, Op: OpPipe, Right: &Query{Func: "combinations"}}}}}}}}}}}}}}, {Name: "combinations", Args: []string{"n"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "limit", Args: []*Query{{Func: "n"}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{{Func: "."}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "combinations"}}}}, + "debug": {{Name: "debug", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "debug"}, Op: OpPipe, Right: &Query{Func: "empty"}}}}}, Op: OpComma, Right: &Query{Func: "."}}}}, + "del": {{Name: "del", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "delpaths", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Func: "f"}}}}}}}}}}}}}}, + "finites": {{Name: "finites", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Func: "isfinite"}}}}}}}, + "first": {{Name: "first", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}, {Name: "first", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}}}}, + "fromdate": {{Name: "fromdate", Body: &Query{Func: "fromdateiso8601"}}}, + "fromdateiso8601": {{Name: "fromdateiso8601", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strptime", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%S%z"}}}}}}}, Op: OpPipe, Right: &Query{Func: "mktime"}}}}, + "fromstream": {{Name: "fromstream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "x", Val: &ObjectVal{Queries: []*Query{{Func: "null"}}}}, {Key: "e", Val: &ObjectVal{Queries: []*Query{{Func: "false"}}}}}}, SuffixList: []*Suffix{{Bind: &Bind{Patterns: []*Pattern{{Name: "$init"}}, Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}, Pattern: &Pattern{Name: "$i"}, Start: &Query{Func: "$init"}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Func: "$init"}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$i"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "2"}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "x"}}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "x"}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}}, + "group_by": {{Name: "group_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_group_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, + "gsub": {{Name: "gsub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Func: "$re"}, {Func: "str"}, {Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, {Name: "gsub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Func: "$re"}, {Func: "str"}, {Left: &Query{Func: "$flags"}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}}}, + "in": {{Name: "in", Args: []string{"xs"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Bind: &Bind{Patterns: []*Pattern{{Name: "$x"}}, Body: &Query{Left: &Query{Func: "xs"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{{Func: "$x"}}}}}}}}}}}}}, + "inputs": {{Name: "inputs", Body: &Query{Term: &Term{Type: TermTypeTry, Try: &Try{Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{{Func: "input"}}}}}, Catch: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "break"}}}}, Then: &Query{Func: "empty"}, Else: &Query{Func: "error"}}}}}}}}}, + "inside": {{Name: "inside", Args: []string{"xs"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Bind: &Bind{Patterns: []*Pattern{{Name: "$x"}}, Body: &Query{Left: &Query{Func: "xs"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "contains", Args: []*Query{{Func: "$x"}}}}}}}}}}}}}, + "isempty": {{Name: "isempty", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "false"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}, Op: OpComma, Right: &Query{Func: "true"}}}}}}}, + "iterables": {{Name: "iterables", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpOr, Right: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}}}}}}}, + "last": {{Name: "last", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}, {Name: "last", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Func: "null"}, Update: &Query{Func: "$item"}}}}}}, + "limit": {{Name: "limit", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Func: "$n"}, Update: &Query{Left: &Query{Func: "."}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}, Extract: &Query{Left: &Query{Func: "$item"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Else: &Query{Func: "empty"}}}}}}}}}}}, Elif: []*IfElif{{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Func: "empty"}}}, Else: &Query{Func: "g"}}}}}}, + "map": {{Name: "map", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}}}}, + "map_values": {{Name: "map_values", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, Op: OpModify, Right: &Query{Func: "f"}}}}, + "match": {{Name: "match", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Func: "$re"}, {Func: "null"}}}}}}, {Name: "match", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{{Func: "$re"}, {Func: "$flags"}, {Func: "false"}}}, SuffixList: []*Suffix{{Iter: true}}}}}}, + "max_by": {{Name: "max_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_max_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, + "min_by": {{Name: "min_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_min_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, + "normals": {{Name: "normals", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Func: "isnormal"}}}}}}}, + "not": {{Name: "not", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "."}, Then: &Query{Func: "false"}, Else: &Query{Func: "true"}}}}}}, + "nth": {{Name: "nth", Args: []string{"$n"}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Func: "$n"}}}}}, {Name: "nth", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpLt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "nth doesn't support negative indices"}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Left: &Query{Func: "$n"}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}, Update: &Query{Left: &Query{Func: "."}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Left: &Query{Func: "$item"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}}}, + "nulls": {{Name: "nulls", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Func: "null"}}}}}}}}, + "numbers": {{Name: "numbers", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "number"}}}}}}}}}}, + "objects": {{Name: "objects", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}}}}}, + "paths": {{Name: "paths", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Func: ".."}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}}}}, {Name: "paths", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Left: &Query{Func: ".."}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Func: "f"}}}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}}}}}, + "pick": {{Name: "pick", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Bind: &Bind{Patterns: []*Pattern{{Name: "$v"}}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Func: "f"}}}}, Pattern: &Pattern{Name: "$p"}, Start: &Query{Func: "null"}, Update: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Func: "$p"}, {Left: &Query{Func: "$v"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{{Func: "$p"}}}}}}}}}}}}}}}}}}}}, + "range": {{Name: "range", Args: []string{"$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{{Term: &Term{Type: TermTypeNumber, Number: "0"}}, {Func: "$end"}, {Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, {Name: "range", Args: []string{"$start", "$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{{Func: "$start"}, {Func: "$end"}, {Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, {Name: "range", Args: []string{"$start", "$end", "$step"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{{Func: "$start"}, {Func: "$end"}, {Func: "$step"}}}}}}}, + "recurse": {{Name: "recurse", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "recurse", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}}}}}}, {Name: "recurse", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "r", Body: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Func: "r"}}}}}}}, Func: "r"}}, {Name: "recurse", Args: []string{"f", "cond"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "r", Body: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Func: "cond"}}}}}, Op: OpPipe, Right: &Query{Func: "r"}}}}}}}}, Func: "r"}}}, + "repeat": {{Name: "repeat", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_repeat", Body: &Query{Left: &Query{Func: "f"}, Op: OpComma, Right: &Query{Func: "_repeat"}}}}, Func: "_repeat"}}}, + "scalars": {{Name: "scalars", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}}}}}}}, + "scan": {{Name: "scan", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "scan", Args: []*Query{{Func: "$re"}, {Func: "null"}}}}}}, {Name: "scan", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Func: "$re"}, {Left: &Query{Func: "$flags"}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}}}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}, Else: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}, SuffixList: []*Suffix{{Iter: true}, {Index: &Index{Name: "string"}}}}}}}}}}}}}}, + "select": {{Name: "select", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "f"}, Then: &Query{Func: "."}, Else: &Query{Func: "empty"}}}}}}, + "sort_by": {{Name: "sort_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_sort_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, + "splits": {{Name: "splits", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{{Func: "$re"}, {Func: "null"}}}}}}, {Name: "splits", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "split", Args: []*Query{{Func: "$re"}, {Func: "$flags"}}}, SuffixList: []*Suffix{{Iter: true}}}}}}, + "strings": {{Name: "strings", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}}}}}}, + "sub": {{Name: "sub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Func: "$re"}, {Func: "str"}, {Func: "null"}}}}}}, {Name: "sub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Bind: &Bind{Patterns: []*Pattern{{Name: "$str"}}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_sub", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}}}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{{Index: &Index{End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}, {Bind: &Bind{Patterns: []*Pattern{{Name: "$r"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "string", Val: &ObjectVal{Queries: []*Query{{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$r"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "_capture"}, Op: OpPipe, Right: &Query{Func: "str"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{{Index: &Index{Name: "offset"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{{Index: &Index{Name: "length"}}}}}}, End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}, {Key: "offset", Val: &ObjectVal{Queries: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{{Index: &Index{Name: "offset"}}}}}}}}, {Key: "matches", Val: &ObjectVal{Queries: []*Query{{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{{Index: &Index{End: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}, IsSlice: true}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}}}}, Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "string", Val: &ObjectVal{Queries: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{}}}}}}, {Key: "matches", Val: &ObjectVal{Queries: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Func: "$re"}, {Func: "$flags"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}, + "test": {{Name: "test", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "test", Args: []*Query{{Func: "$re"}, {Func: "null"}}}}}}, {Name: "test", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{{Func: "$re"}, {Func: "$flags"}, {Func: "true"}}}}}}}, + "todate": {{Name: "todate", Body: &Query{Func: "todateiso8601"}}}, + "todateiso8601": {{Name: "todateiso8601", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strftime", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%SZ"}}}}}}}}}, + "tostream": {{Name: "tostream", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{FuncDefs: []*FuncDef{{Name: "r", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}, Op: OpPipe, Right: &Query{Func: "r"}}}}, Op: OpComma, Right: &Query{Func: "."}}}}, Func: "r"}}}, SuffixList: []*Suffix{{Bind: &Bind{Patterns: []*Pattern{{Name: "$p"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{{Func: "$p"}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}}}}, Pattern: &Pattern{Name: "$q"}, Start: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "$p"}, Op: OpComma, Right: &Query{Func: "."}}}}}, Update: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "$p"}, Op: OpAdd, Right: &Query{Func: "$q"}}}}}}}}}}}}}}}}, + "truncate_stream": {{Name: "truncate_stream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Bind: &Bind{Patterns: []*Pattern{{Name: "$n"}}, Body: &Query{Left: &Query{Func: "null"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpGt, Right: &Query{Func: "$n"}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Op: OpModify, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Func: "$n"}, IsSlice: true}}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}, + "unique_by": {{Name: "unique_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_unique_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, + "until": {{Name: "until", Args: []string{"cond", "next"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_until", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "cond"}, Then: &Query{Func: "."}, Else: &Query{Left: &Query{Func: "next"}, Op: OpPipe, Right: &Query{Func: "_until"}}}}}}}, Func: "_until"}}}, + "values": {{Name: "values", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Func: "null"}}}}}}}}, + "walk": {{Name: "walk", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_walk", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Func: "_walk"}}}}}, Elif: []*IfElif{{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map_values", Args: []*Query{{Func: "_walk"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}, Func: "_walk"}}}, + "while": {{Name: "while", Args: []string{"cond", "update"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_while", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "cond"}, Then: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "update"}, Op: OpPipe, Right: &Query{Func: "_while"}}}}}, Else: &Query{Func: "empty"}}}}}}, Func: "_while"}}}, + "with_entries": {{Name: "with_entries", Args: []string{"f"}, Body: &Query{Left: &Query{Func: "to_entries"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Func: "f"}}}}}, Op: OpPipe, Right: &Query{Func: "from_entries"}}}}}, } } diff --git a/vendor/github.com/itchyny/gojq/builtin.jq b/vendor/github.com/itchyny/gojq/builtin.jq index b6a9e1d8..24c97290 100644 --- a/vendor/github.com/itchyny/gojq/builtin.jq +++ b/vendor/github.com/itchyny/gojq/builtin.jq @@ -1,10 +1,6 @@ def not: if . then false else true end; def in(xs): . as $x | xs | has($x); def map(f): [.[] | f]; -def to_entries: [keys[] as $k | {key: $k, value: .[$k]}]; -def from_entries: - map({ (.key // .Key // .name // .Name): (if has("value") then .value else .Value end) }) - | add // {}; def with_entries(f): to_entries | map(f) | from_entries; def select(f): if f then . else empty end; def recurse: recurse(.[]?); @@ -24,21 +20,10 @@ def range($end): _range(0; $end; 1); def range($start; $end): _range($start; $end; 1); def range($start; $end; $step): _range($start; $end; $step); -def _flatten($x): - map(if type == "array" and $x != 0 then _flatten($x - 1) else [.] end) | add; -def flatten($x): - if $x < 0 - then error("flatten depth must not be negative") - else _flatten($x) // [] end; -def flatten: _flatten(-1) // []; -def min: min_by(.); def min_by(f): _min_by(map([f])); -def max: max_by(.); def max_by(f): _max_by(map([f])); -def sort: sort_by(.); def sort_by(f): _sort_by(map([f])); def group_by(f): _group_by(map([f])); -def unique: unique_by(.); def unique_by(f): _unique_by(map([f])); def arrays: select(type == "array"); @@ -52,57 +37,22 @@ def strings: select(type == "string"); def nulls: select(. == null); def values: select(. != null); def scalars: select(type | . != "array" and . != "object"); -def leaf_paths: paths(scalars); -def indices($x): _indices($x); -def index($x): _lindex($x); -def rindex($x): _rindex($x); def inside(xs): . as $x | xs | contains($x); -def startswith($x): - if type == "string" then - if $x|type == "string" then - .[:$x | length] == $x - else - $x | _type_error("startswith") - end - else - _type_error("startswith") - end; -def endswith($x): - if type == "string" then - if $x|type == "string" then - .[- ($x | length):] == $x - else - $x | _type_error("endswith") - end - else - _type_error("endswith") - end; -def ltrimstr($x): - if type == "string" and ($x|type == "string") and startswith($x) then - .[$x | length:] - end; -def rtrimstr($x): - if type == "string" and ($x|type == "string") and endswith($x) then - .[:- ($x | length)] - end; - def combinations: if length == 0 then [] else - .[0][] as $x | .[1:] | combinations as $y | [$x] + $y + .[0][] as $x | [$x] + (.[1:] | combinations) end; -def combinations(n): - . as $dot | [range(n) | $dot] | combinations; -def join($x): - if type != "array" then [.[]] end | _join($x); -def ascii_downcase: - explode | map(if 65 <= . and . <= 90 then . + 32 end) | implode; -def ascii_upcase: - explode | map(if 97 <= . and . <= 122 then . - 32 end) | implode; +def combinations(n): [limit(n; repeat(.))] | combinations; def walk(f): - def _walk: if type | . == "array" or . == "object" then map_values(_walk) end | f; + def _walk: + if type == "array" then + map(_walk) + elif type == "object" then + map_values(_walk) + end | f; _walk; def first: .[0]; @@ -110,17 +60,20 @@ def first(g): label $out | g | ., break $out; def last: .[-1]; def last(g): reduce g as $item (null; $item); def isempty(g): label $out | (g | false, break $out), true; -def all: all(.[]; .); +def all: all(.); def all(y): all(.[]; y); -def all(g; y): isempty(g|y and empty); -def any: any(.[]; .); +def all(g; y): isempty(g | select(y | not)); +def any: any(.); def any(y): any(.[]; y); -def any(g; y): isempty(g|y or empty) | not; +def any(g; y): isempty(g | select(y)) | not; def limit($n; g): if $n > 0 then - label $out - | foreach g as $item - ($n; .-1; $item, if . <= 0 then break $out else empty end) + label $out | + foreach g as $item ( + $n; + . - 1; + $item, if . <= 0 then break $out else empty end + ) elif $n == 0 then empty else @@ -131,41 +84,41 @@ def nth($n; g): if $n < 0 then error("nth doesn't support negative indices") else - label $out - | foreach g as $item - ($n; .-1; . < 0 or empty | $item, break $out) + label $out | + foreach g as $item ( + $n + 1; + . - 1; + if . <= 0 then $item, break $out else empty end + ) end; def truncate_stream(f): - . as $n | null | f | . as $input - | if (.[0] | length) > $n then setpath([0]; $input[0][$n:]) else empty end; + . as $n | null | f | + if .[0] | length > $n then .[0] |= .[$n:] else empty end; def fromstream(f): - { x: null, e: false } as $init - | foreach f as $i - ( $init; - if .e then $init else . end - | if $i | length == 2 - then setpath(["e"]; $i[0] | length==0) | setpath(["x"] + $i[0]; $i[1]) - else setpath(["e"]; $i[0] | length==1) end; - if .e then .x else empty end); + { x: null, e: false } as $init | + foreach f as $i ( + $init; + if .e then $init end | + if $i | length == 2 then + setpath(["e"]; $i[0] | length == 0) | + setpath(["x"] + $i[0]; $i[1]) + else + setpath(["e"]; $i[0] | length == 1) + end; + if .e then .x else empty end + ); def tostream: - path(def r: (.[]? | r), .; r) as $p - | getpath($p) - | reduce path(.[]?) as $q ([$p, .]; [$p + $q]); + path(def r: (.[]? | r), .; r) as $p | + getpath($p) | + reduce path(.[]?) as $q ([$p, .]; [$p + $q]); -def _assign(ps; $v): - reduce path(ps) as $p (.; setpath($p; $v)); -def _modify(ps; f): - reduce path(ps) as $p - ([., []]; label $out | (([0] + $p) as $q | setpath($q; getpath($q) | f) | ., break $out), setpath([1]; .[1] + [$p])) - | . as $x | $x[0] | delpaths($x[1]); def map_values(f): .[] |= f; def del(f): delpaths([path(f)]); -def paths: - path(recurse(if type | . == "array" or . == "object" then .[] else empty end)) - | select(length > 0); -def paths(f): - . as $x | paths | select(. as $p | $x | getpath($p) | f); +def paths: path(..) | select(. != []); +def paths(f): path(.. | select(f)) | select(. != []); +def pick(f): . as $v | + reduce path(f) as $p (null; setpath($p; $v | getpath($p))); def fromdateiso8601: strptime("%Y-%m-%dT%H:%M:%S%z") | mktime; def todateiso8601: strftime("%Y-%m-%dT%H:%M:%SZ"); @@ -173,42 +126,41 @@ def fromdate: fromdateiso8601; def todate: todateiso8601; def match($re): match($re; null); -def match($re; $flags): _match($re; $flags; false) | .[]; +def match($re; $flags): _match($re; $flags; false)[]; def test($re): test($re; null); def test($re; $flags): _match($re; $flags; true); def capture($re): capture($re; null); -def capture($re; $flags): - match($re; $flags) - | [.captures[] | select(.name != null) | { (.name): .string }] - | add // {}; +def capture($re; $flags): match($re; $flags) | _capture; def scan($re): scan($re; null); def scan($re; $flags): - match($re; "g" + $flags) - | if .captures|length > 0 then [.captures[].string] else .string end; + match($re; $flags + "g") | + if .captures == [] then + .string + else + [.captures[].string] + end; def splits($re): splits($re; null); -def splits($re; $flags): split($re; $flags) | .[]; +def splits($re; $flags): split($re; $flags)[]; def sub($re; str): sub($re; str; null); def sub($re; str; $flags): - . as $in - | def _sub: - if .matches|length > 0 - then - . as $x | .matches[0] as $r - | [$r.captures[] | select(.name != null) | { (.name): .string }] - | add // {} - | { - string: ($x.string + $in[$x.offset:$r.offset] + str), - offset: ($r.offset + $r.length), - matches: $x.matches[1:] - } - | _sub - else - .string + $in[.offset:] - end; - { string: "", offset: 0, matches: [match($re; $flags)] } | _sub; + . as $str | + def _sub: + if .matches == [] then + $str[:.offset] + .string + else + .matches[-1] as $r | + { + string: (($r | _capture | str) + $str[$r.offset+$r.length:.offset] + .string), + offset: $r.offset, + matches: .matches[:-1], + } | + _sub + end; + { string: "", matches: [match($re; $flags)] } | _sub; def gsub($re; str): sub($re; str; "g"); def gsub($re; str; $flags): sub($re; str; $flags + "g"); +def debug(f): (f | debug | empty), .; def inputs: try repeat(input) @@ -216,8 +168,9 @@ def inputs: if . == "break" then empty else error end; def INDEX(stream; idx_expr): - reduce stream as $row ({}; .[$row|idx_expr|tostring] = $row); -def INDEX(idx_expr): INDEX(.[]; idx_expr); + reduce stream as $row ({}; .[$row | idx_expr | tostring] = $row); +def INDEX(idx_expr): + INDEX(.[]; idx_expr); def JOIN($idx; idx_expr): [.[] | [., $idx[idx_expr]]]; def JOIN($idx; stream; idx_expr): diff --git a/vendor/github.com/itchyny/gojq/code.go b/vendor/github.com/itchyny/gojq/code.go index f1935d84..33505bde 100644 --- a/vendor/github.com/itchyny/gojq/code.go +++ b/vendor/github.com/itchyny/gojq/code.go @@ -1,7 +1,7 @@ package gojq type code struct { - v interface{} + v any op opcode } @@ -25,13 +25,15 @@ const ( opbacktrack opjump opjumpifnot + opindex + opindexarray opcall opcallrec oppushpc opcallpc opscope opret - opeach + opiter opexpbegin opexpend oppathbegin @@ -74,6 +76,10 @@ func (op opcode) String() string { return "jump" case opjumpifnot: return "jumpifnot" + case opindex: + return "index" + case opindexarray: + return "indexarray" case opcall: return "call" case opcallrec: @@ -86,8 +92,8 @@ func (op opcode) String() string { return "scope" case opret: return "ret" - case opeach: - return "each" + case opiter: + return "iter" case opexpbegin: return "expbegin" case opexpend: diff --git a/vendor/github.com/itchyny/gojq/compare.go b/vendor/github.com/itchyny/gojq/compare.go index 9f0d5338..e70c1fbb 100644 --- a/vendor/github.com/itchyny/gojq/compare.go +++ b/vendor/github.com/itchyny/gojq/compare.go @@ -5,19 +5,17 @@ import ( "math/big" ) -func compare(l, r interface{}) int { +// Compare l and r, and returns jq-flavored comparison value. +// The result will be 0 if l == r, -1 if l < r, and +1 if l > r. +// This comparison is used by built-in operators and functions. +func Compare(l, r any) int { + return compare(l, r) +} + +func compare(l, r any) int { return binopTypeSwitch(l, r, - func(l, r int) interface{} { - switch { - case l < r: - return -1 - case l == r: - return 0 - default: - return 1 - } - }, - func(l, r float64) interface{} { + compareInt, + func(l, r float64) any { switch { case l < r || math.IsNaN(l): return -1 @@ -27,10 +25,10 @@ func compare(l, r interface{}) int { return 1 } }, - func(l, r *big.Int) interface{} { + func(l, r *big.Int) any { return l.Cmp(r) }, - func(l, r string) interface{} { + func(l, r string) any { switch { case l < r: return -1 @@ -40,66 +38,63 @@ func compare(l, r interface{}) int { return 1 } }, - func(l, r []interface{}) interface{} { - for i := 0; ; i++ { - if i >= len(l) { - if i >= len(r) { - return 0 - } - return -1 - } - if i >= len(r) { - return 1 - } + func(l, r []any) any { + n := len(l) + if len(r) < n { + n = len(r) + } + for i := 0; i < n; i++ { if cmp := compare(l[i], r[i]); cmp != 0 { return cmp } } + return compareInt(len(l), len(r)) }, - func(l, r map[string]interface{}) interface{} { + func(l, r map[string]any) any { lk, rk := funcKeys(l), funcKeys(r) if cmp := compare(lk, rk); cmp != 0 { return cmp } - for _, k := range lk.([]interface{}) { + for _, k := range lk.([]any) { if cmp := compare(l[k.(string)], r[k.(string)]); cmp != 0 { return cmp } } return 0 }, - func(l, r interface{}) interface{} { - ln, rn := getTypeOrdNum(l), getTypeOrdNum(r) - switch { - case ln < rn: - return -1 - case ln == rn: - return 0 - default: - return 1 - } + func(l, r any) any { + return compareInt(typeIndex(l), typeIndex(r)) }, ).(int) } -func getTypeOrdNum(v interface{}) int { +func compareInt(l, r int) any { + switch { + case l < r: + return -1 + case l == r: + return 0 + default: + return 1 + } +} + +func typeIndex(v any) int { switch v := v.(type) { - case nil: + default: return 0 case bool: - if v { - return 2 + if !v { + return 1 } - return 1 + return 2 case int, float64, *big.Int: return 3 case string: return 4 - case []interface{}: + case []any: return 5 - case map[string]interface{}: + case map[string]any: return 6 - default: - return -1 } } diff --git a/vendor/github.com/itchyny/gojq/compiler.go b/vendor/github.com/itchyny/gojq/compiler.go index 00daa04c..67ed288a 100644 --- a/vendor/github.com/itchyny/gojq/compiler.go +++ b/vendor/github.com/itchyny/gojq/compiler.go @@ -2,7 +2,6 @@ package gojq import ( "context" - "encoding/json" "errors" "fmt" "sort" @@ -18,6 +17,7 @@ type compiler struct { inputIter Iter codes []*code codeinfos []codeinfo + builtinScope *scopeinfo scopes []*scopeinfo scopecnt int } @@ -30,16 +30,17 @@ type Code struct { } // Run runs the code with the variable values (which should be in the -// same order as the given variables using WithVariables) and returns +// same order as the given variables using [WithVariables]) and returns // a result iterator. // -// It is safe to call this method of a *Code in multiple goroutines. -func (c *Code) Run(v interface{}, values ...interface{}) Iter { +// It is safe to call this method in goroutines, to reuse a compiled [*Code]. +// But for arguments, do not give values sharing same data between goroutines. +func (c *Code) Run(v any, values ...any) Iter { return c.RunWithContext(context.Background(), v, values...) } // RunWithContext runs the code with context. -func (c *Code) RunWithContext(ctx context.Context, v interface{}, values ...interface{}) Iter { +func (c *Code) RunWithContext(ctx context.Context, v any, values ...any) Iter { if len(values) > len(c.variables) { return NewIter(&tooManyVariableValuesError{}) } else if len(values) < len(c.variables) { @@ -51,16 +52,6 @@ func (c *Code) RunWithContext(ctx context.Context, v interface{}, values ...inte return newEnv(ctx).execute(c, normalizeNumbers(v), values...) } -// ModuleLoader is an interface for loading modules. -// -// Implement following optional methods. Use NewModuleLoader to load local modules. -// LoadModule(string) (*Query, error) -// LoadModuleWithMeta(string, map[string]interface{}) (*Query, error) -// LoadInitModules() ([]*Query, error) -// LoadJSON(string) (interface{}, error) -// LoadJSONWithMeta(string, map[string]interface{}) (interface{}, error) -type ModuleLoader interface{} - type scopeinfo struct { variables []*varinfo funcs []*funcinfo @@ -87,6 +78,7 @@ func Compile(q *Query, options ...CompilerOption) (*Code, error) { for _, opt := range options { opt(c) } + c.builtinScope = c.newScope() scope := c.newScope() c.scopes = []*scopeinfo{scope} setscope := c.lazy(func() *code { @@ -152,15 +144,15 @@ func (c *compiler) compileImport(i *Import) error { return fmt.Errorf("cannot load module: %q", path) } if strings.HasPrefix(alias, "$") { - var vals interface{} + var vals any if moduleLoader, ok := c.moduleLoader.(interface { - LoadJSONWithMeta(string, map[string]interface{}) (interface{}, error) + LoadJSONWithMeta(string, map[string]any) (any, error) }); ok { if vals, err = moduleLoader.LoadJSONWithMeta(path, i.Meta.ToValue()); err != nil { return err } } else if moduleLoader, ok := c.moduleLoader.(interface { - LoadJSON(string) (interface{}, error) + LoadJSON(string) (any, error) }); ok { if vals, err = moduleLoader.LoadJSON(path); err != nil { return err @@ -177,7 +169,7 @@ func (c *compiler) compileImport(i *Import) error { } var q *Query if moduleLoader, ok := c.moduleLoader.(interface { - LoadModuleWithMeta(string, map[string]interface{}) (*Query, error) + LoadModuleWithMeta(string, map[string]any) (*Query, error) }); ok { if q, err = moduleLoader.LoadModuleWithMeta(path, i.Meta.ToValue()); err != nil { return err @@ -190,8 +182,11 @@ func (c *compiler) compileImport(i *Import) error { } } c.appendCodeInfo("module " + path) - defer c.appendCodeInfo("end of module " + path) - return c.compileModule(q, alias) + if err = c.compileModule(q, alias); err != nil { + return err + } + c.appendCodeInfo("end of module " + path) + return nil } func (c *compiler) compileModule(q *Query, alias string) error { @@ -274,6 +269,31 @@ func (c *compiler) lookupFuncOrVariable(name string) (*funcinfo, *varinfo) { return nil, nil } +func (c *compiler) lookupBuiltin(name string, argcnt int) *funcinfo { + s := c.builtinScope + for i := len(s.funcs) - 1; i >= 0; i-- { + if f := s.funcs[i]; f.name == name && f.argcnt == argcnt { + return f + } + } + return nil +} + +func (c *compiler) appendBuiltin(name string, argcnt int) func() { + setjump := c.lazy(func() *code { + return &code{op: opjump, v: len(c.codes)} + }) + c.appendCodeInfo(name) + c.builtinScope.funcs = append( + c.builtinScope.funcs, + &funcinfo{name, len(c.codes), argcnt}, + ) + return func() { + setjump() + c.appendCodeInfo("end of " + name) + } +} + func (c *compiler) newScope() *scopeinfo { i := c.scopecnt // do not use len(c.scopes) because it pops c.scopecnt++ @@ -294,29 +314,22 @@ func (c *compiler) newScopeDepth() func() { func (c *compiler) compileFuncDef(e *FuncDef, builtin bool) error { var scope *scopeinfo if builtin { - scope = c.scopes[0] - for i := len(scope.funcs) - 1; i >= 0; i-- { - if f := scope.funcs[i]; f.name == e.Name && f.argcnt == len(e.Args) { - return nil - } - } + scope = c.builtinScope } else { scope = c.scopes[len(c.scopes)-1] } defer c.lazy(func() *code { - return &code{op: opjump, v: c.pc()} + return &code{op: opjump, v: len(c.codes)} })() c.appendCodeInfo(e.Name) - defer c.appendCodeInfo("end of " + e.Name) - pc := c.pc() - scope.funcs = append(scope.funcs, &funcinfo{e.Name, pc, len(e.Args)}) + scope.funcs = append(scope.funcs, &funcinfo{e.Name, len(c.codes), len(e.Args)}) defer func(scopes []*scopeinfo, variables []string) { c.scopes, c.variables = scopes, variables }(c.scopes, c.variables) c.variables = c.variables[len(c.variables):] scope = c.newScope() if builtin { - c.scopes = []*scopeinfo{c.scopes[0], scope} + c.scopes = []*scopeinfo{c.builtinScope, scope} } else { c.scopes = append(c.scopes, scope) } @@ -344,14 +357,20 @@ func (c *compiler) compileFuncDef(e *FuncDef, builtin bool) error { } for _, w := range vis { c.append(&code{op: opload, v: v}) + c.append(&code{op: opexpbegin}) c.append(&code{op: opload, v: w.index}) c.append(&code{op: opcallpc}) c.appendCodeInfo(w.name) c.append(&code{op: opstore, v: c.pushVariable(w.name)}) + c.append(&code{op: opexpend}) } c.append(&code{op: opload, v: v}) } - return c.compile(e.Body) + if err := c.compile(e.Body); err != nil { + return err + } + c.appendCodeInfo("end of " + e.Name) + return nil } func (c *compiler) compileQuery(e *Query) error { @@ -425,15 +444,15 @@ func (c *compiler) compileQuery(e *Query) error { func (c *compiler) compileComma(l, r *Query) error { setfork := c.lazy(func() *code { - return &code{op: opfork, v: c.pc() + 1} + return &code{op: opfork, v: len(c.codes)} }) if err := c.compileQuery(l); err != nil { return err } - setfork() defer c.lazy(func() *code { - return &code{op: opjump, v: c.pc()} + return &code{op: opjump, v: len(c.codes)} })() + setfork() return c.compileQuery(r) } @@ -442,23 +461,23 @@ func (c *compiler) compileAlt(l, r *Query) error { found := c.newVariable() c.append(&code{op: opstore, v: found}) setfork := c.lazy(func() *code { - return &code{op: opfork, v: c.pc()} // opload found + return &code{op: opfork, v: len(c.codes)} // opload found }) if err := c.compileQuery(l); err != nil { return err } c.append(&code{op: opdup}) - c.append(&code{op: opjumpifnot, v: c.pc() + 4}) // oppop - c.append(&code{op: oppush, v: true}) // found some value + c.append(&code{op: opjumpifnot, v: len(c.codes) + 4}) // oppop + c.append(&code{op: oppush, v: true}) // found some value c.append(&code{op: opstore, v: found}) defer c.lazy(func() *code { - return &code{op: opjump, v: c.pc()} // ret + return &code{op: opjump, v: len(c.codes)} })() c.append(&code{op: oppop}) c.append(&code{op: opbacktrack}) setfork() c.append(&code{op: opload, v: found}) - c.append(&code{op: opjumpifnot, v: c.pc() + 3}) + c.append(&code{op: opjumpifnot, v: len(c.codes) + 3}) c.append(&code{op: opbacktrack}) // if found, backtrack c.append(&code{op: oppop}) return c.compileQuery(r) @@ -467,8 +486,9 @@ func (c *compiler) compileAlt(l, r *Query) error { func (c *compiler) compileQueryUpdate(l, r *Query, op Operator) error { switch op { case OpAssign: - // .foo.bar = f => setpath(["foo", "bar"]; f) - if xs := l.toIndices(); xs != nil { + // optimize assignment operator with constant indexing and slicing + // .foo.[0].[1:2] = f => setpath(["foo",0,{"start":1,"end":2}]; f) + if xs := l.toIndices(nil); xs != nil { // ref: compileCall v := c.newVariable() c.append(&code{op: opstore, v: v}) @@ -478,7 +498,7 @@ func (c *compiler) compileQueryUpdate(l, r *Query, op Operator) error { } c.append(&code{op: oppush, v: xs}) c.append(&code{op: opload, v: v}) - c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["setpath"].callback, 2, "setpath"}}) + c.append(&code{op: opcall, v: [3]any{internalFuncs["setpath"].callback, 2, "setpath"}}) return nil } fallthrough @@ -517,7 +537,12 @@ func (c *compiler) compileQueryUpdate(l, r *Query, op Operator) error { } } -func (c *compiler) compileBind(b *Bind) error { +func (c *compiler) compileBind(e *Term, b *Bind) error { + c.append(&code{op: opdup}) + c.append(&code{op: opexpbegin}) + if err := c.compileTerm(e); err != nil { + return err + } var pc int var vs [][2]int for i, p := range b.Patterns { @@ -534,97 +559,86 @@ func (c *compiler) compileBind(b *Bind) error { c.append(&code{op: opstore, v: v}) } } - vs, err = c.compilePattern(p) - if err != nil { + if vs, err = c.compilePattern(vs[:0], p); err != nil { return err } if i < len(b.Patterns)-1 { defer c.lazy(func() *code { return &code{op: opjump, v: pc} })() - pcc = c.pc() + pcc = len(c.codes) } } if len(b.Patterns) > 1 { - pc = c.pc() + pc = len(c.codes) } if len(b.Patterns) == 1 && c.codes[len(c.codes)-2].op == opexpbegin { c.codes[len(c.codes)-2].op = opnop } else { - c.append(&code{op: opexpend}) // ref: compileTermSuffix + c.append(&code{op: opexpend}) } return c.compileQuery(b.Body) } -func (c *compiler) compilePattern(p *Pattern) ([][2]int, error) { +func (c *compiler) compilePattern(vs [][2]int, p *Pattern) ([][2]int, error) { + var err error c.appendCodeInfo(p) if p.Name != "" { v := c.pushVariable(p.Name) c.append(&code{op: opstore, v: v}) - return [][2]int{v}, nil + return append(vs, v), nil } else if len(p.Array) > 0 { - var vs [][2]int v := c.newVariable() c.append(&code{op: opstore, v: v}) for i, p := range p.Array { - c.append(&code{op: oppush, v: i}) c.append(&code{op: opload, v: v}) - c.append(&code{op: opload, v: v}) - // ref: compileCall - c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["_index"].callback, 2, "_index"}}) - ns, err := c.compilePattern(p) - if err != nil { + c.append(&code{op: opindexarray, v: i}) + if vs, err = c.compilePattern(vs, p); err != nil { return nil, err } - vs = append(vs, ns...) } return vs, nil } else if len(p.Object) > 0 { - var vs [][2]int v := c.newVariable() c.append(&code{op: opstore, v: v}) for _, kv := range p.Object { var key, name string - if kv.KeyOnly != "" { - key, name = kv.KeyOnly[1:], kv.KeyOnly - c.append(&code{op: oppush, v: key}) - } else if kv.Key != "" { - key = kv.Key - if key != "" && key[0] == '$' { + c.append(&code{op: opload, v: v}) + if key = kv.Key; key != "" { + if key[0] == '$' { key, name = key[1:], key } - c.append(&code{op: oppush, v: key}) } else if kv.KeyString != nil { - c.append(&code{op: opload, v: v}) - if err := c.compileString(kv.KeyString, nil); err != nil { - return nil, err + if key = kv.KeyString.Str; key == "" { + if err := c.compileString(kv.KeyString, nil); err != nil { + return nil, err + } } } else if kv.KeyQuery != nil { - c.append(&code{op: opload, v: v}) if err := c.compileQuery(kv.KeyQuery); err != nil { return nil, err } } - c.append(&code{op: opload, v: v}) - c.append(&code{op: opload, v: v}) - // ref: compileCall - c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["_index"].callback, 2, "_index"}}) + if key != "" { + c.append(&code{op: opindex, v: key}) + } else { + c.append(&code{op: opload, v: v}) + c.append(&code{op: oppush, v: nil}) + // ref: compileCall + c.append(&code{op: opcall, v: [3]any{internalFuncs["_index"].callback, 2, "_index"}}) + } if name != "" { if kv.Val != nil { c.append(&code{op: opdup}) } - ns, err := c.compilePattern(&Pattern{Name: name}) - if err != nil { + if vs, err = c.compilePattern(vs, &Pattern{Name: name}); err != nil { return nil, err } - vs = append(vs, ns...) } if kv.Val != nil { - ns, err := c.compilePattern(kv.Val) - if err != nil { + if vs, err = c.compilePattern(vs, kv.Val); err != nil { return nil, err } - vs = append(vs, ns...) } } return vs, nil @@ -650,17 +664,17 @@ func (c *compiler) compileIf(e *If) error { } pcc := len(c.codes) setjumpifnot := c.lazy(func() *code { - return &code{op: opjumpifnot, v: c.pc() + 1} // if falsy, skip then clause + return &code{op: opjumpifnot, v: len(c.codes)} // skip then clause }) f = c.newScopeDepth() if err := c.compileQuery(e.Then); err != nil { return err } f() - setjumpifnot() defer c.lazy(func() *code { - return &code{op: opjump, v: c.pc()} // jump to ret after else clause + return &code{op: opjump, v: len(c.codes)} })() + setjumpifnot() if len(e.Elif) > 0 { return c.compileIf(&If{e.Elif[0].Cond, e.Elif[0].Then, e.Elif[1:], e.Else}) } @@ -686,7 +700,7 @@ func (c *compiler) compileIf(e *If) error { func (c *compiler) compileTry(e *Try) error { c.appendCodeInfo(e) setforktrybegin := c.lazy(func() *code { - return &code{op: opforktrybegin, v: c.pc()} + return &code{op: opforktrybegin, v: len(c.codes)} }) f := c.newScopeDepth() if err := c.compileQuery(e.Body); err != nil { @@ -695,7 +709,7 @@ func (c *compiler) compileTry(e *Try) error { f() c.append(&code{op: opforktryend}) defer c.lazy(func() *code { - return &code{op: opjump, v: c.pc()} + return &code{op: opjump, v: len(c.codes)} })() setforktrybegin() if e.Catch != nil { @@ -709,9 +723,9 @@ func (c *compiler) compileTry(e *Try) error { func (c *compiler) compileReduce(e *Reduce) error { c.appendCodeInfo(e) defer c.newScopeDepth()() - defer c.lazy(func() *code { - return &code{op: opfork, v: c.pc() - 2} - })() + setfork := c.lazy(func() *code { + return &code{op: opfork, v: len(c.codes)} + }) c.append(&code{op: opdup}) v := c.newVariable() f := c.newScopeDepth() @@ -723,7 +737,7 @@ func (c *compiler) compileReduce(e *Reduce) error { if err := c.compileTerm(e.Term); err != nil { return err } - if _, err := c.compilePattern(e.Pattern); err != nil { + if _, err := c.compilePattern(nil, e.Pattern); err != nil { return err } c.append(&code{op: opload, v: v}) @@ -734,6 +748,7 @@ func (c *compiler) compileReduce(e *Reduce) error { f() c.append(&code{op: opstore, v: v}) c.append(&code{op: opbacktrack}) + setfork() c.append(&code{op: oppop}) c.append(&code{op: opload, v: v}) return nil @@ -753,7 +768,7 @@ func (c *compiler) compileForeach(e *Foreach) error { if err := c.compileTerm(e.Term); err != nil { return err } - if _, err := c.compilePattern(e.Pattern); err != nil { + if _, err := c.compilePattern(nil, e.Pattern); err != nil { return err } c.append(&code{op: opload, v: v}) @@ -774,9 +789,7 @@ func (c *compiler) compileForeach(e *Foreach) error { func (c *compiler) compileLabel(e *Label) error { c.appendCodeInfo(e) v := c.pushVariable("$%" + e.Ident[1:]) - defer c.lazy(func() *code { - return &code{op: opforklabel, v: v} - })() + c.append(&code{op: opforklabel, v: v}) return c.compileQuery(e.Body) } @@ -787,21 +800,21 @@ func (c *compiler) compileBreak(label string) error { } c.append(&code{op: oppop}) c.append(&code{op: opload, v: v}) - c.append(&code{op: opcall, v: [3]interface{}{ - func(v interface{}, _ []interface{}) interface{} { - return &breakError{label, v} - }, - 0, - "_break", - }}) + c.append(&code{op: opcall, v: [3]any{funcBreak(label), 0, "_break"}}) return nil } +func funcBreak(label string) func(any, []any) any { + return func(v any, _ []any) any { + return &breakError{label, v} + } +} + func (c *compiler) compileTerm(e *Term) error { if len(e.SuffixList) > 0 { s := e.SuffixList[len(e.SuffixList)-1] t := *e // clone without changing e - (&t).SuffixList = t.SuffixList[:len(e.SuffixList)-1] + t.SuffixList = t.SuffixList[:len(e.SuffixList)-1] return c.compileTermSuffix(&t, s) } switch e.Type { @@ -827,11 +840,7 @@ func (c *compiler) compileTerm(e *Term) error { case TermTypeArray: return c.compileArray(e.Array) case TermTypeNumber: - v := normalizeNumber(json.Number(e.Number)) - if err, ok := v.(error); ok { - return err - } - c.append(&code{op: opconst, v: v}) + c.append(&code{op: opconst, v: toNumber(e.Number)}) return nil case TermTypeUnary: return c.compileUnary(e.Unary) @@ -860,10 +869,15 @@ func (c *compiler) compileTerm(e *Term) error { } func (c *compiler) compileIndex(e *Term, x *Index) error { - c.appendCodeInfo(x) - if x.Name != "" { - return c.compileCall("_index", []*Query{{Term: e}, {Term: &Term{Type: TermTypeString, Str: &String{Str: x.Name}}}}) + if k := x.toIndexKey(); k != nil { + if err := c.compileTerm(e); err != nil { + return err + } + c.appendCodeInfo(x) + c.append(&code{op: opindex, v: k}) + return nil } + c.appendCodeInfo(x) if x.Str != nil { return c.compileCall("_index", []*Query{{Term: e}, {Term: &Term{Type: TermTypeString, Str: x.Str}}}) } @@ -880,12 +894,11 @@ func (c *compiler) compileIndex(e *Term, x *Index) error { } func (c *compiler) compileFunc(e *Func) error { - name := e.Name if len(e.Args) == 0 { - if f, v := c.lookupFuncOrVariable(name); f != nil { + if f, v := c.lookupFuncOrVariable(e.Name); f != nil { return c.compileCallPc(f, e.Args) } else if v != nil { - if name[0] == '$' { + if e.Name[0] == '$' { c.append(&code{op: oppop}) c.append(&code{op: opload, v: v.index}) } else { @@ -893,8 +906,8 @@ func (c *compiler) compileFunc(e *Func) error { c.append(&code{op: opcallpc}) } return nil - } else if name == "$ENV" || name == "env" { - env := make(map[string]interface{}) + } else if e.Name == "$ENV" || e.Name == "env" { + env := make(map[string]any) if c.environLoader != nil { for _, kv := range c.environLoader() { if i := strings.IndexByte(kv, '='); i > 0 { @@ -904,36 +917,42 @@ func (c *compiler) compileFunc(e *Func) error { } c.append(&code{op: opconst, v: env}) return nil - } else if name[0] == '$' { - return &variableNotFoundError{name} + } else if e.Name[0] == '$' { + return &variableNotFoundError{e.Name} } } else { for i := len(c.scopes) - 1; i >= 0; i-- { s := c.scopes[i] for j := len(s.funcs) - 1; j >= 0; j-- { - if f := s.funcs[j]; f.name == name && f.argcnt == len(e.Args) { + if f := s.funcs[j]; f.name == e.Name && f.argcnt == len(e.Args) { return c.compileCallPc(f, e.Args) } } } } - if name[0] == '_' { - name = name[1:] + if f := c.lookupBuiltin(e.Name, len(e.Args)); f != nil { + return c.compileCallPc(f, e.Args) } - if fds, ok := builtinFuncDefs[name]; ok { + if fds, ok := builtinFuncDefs[e.Name]; ok { for _, fd := range fds { if len(fd.Args) == len(e.Args) { if err := c.compileFuncDef(fd, true); err != nil { return err } + break } } - s := c.scopes[0] - for i := len(s.funcs) - 1; i >= 0; i-- { - if f := s.funcs[i]; f.name == e.Name && f.argcnt == len(e.Args) { - return c.compileCallPc(f, e.Args) + if len(fds) == 0 { + switch e.Name { + case "_assign": + c.compileAssign() + case "_modify": + c.compileModify() } } + if f := c.lookupBuiltin(e.Name, len(e.Args)); f != nil { + return c.compileCallPc(f, e.Args) + } } if fn, ok := internalFuncs[e.Name]; ok && fn.accept(len(e.Args)) { switch e.Name { @@ -949,27 +968,27 @@ func (c *compiler) compileFunc(e *Func) error { return nil case "builtins": return c.compileCallInternal( - [3]interface{}{c.funcBuiltins, 0, e.Name}, + [3]any{c.funcBuiltins, 0, e.Name}, e.Args, true, - false, + -1, ) case "input": if c.inputIter == nil { return &inputNotAllowedError{} } return c.compileCallInternal( - [3]interface{}{c.funcInput, 0, e.Name}, + [3]any{c.funcInput, 0, e.Name}, e.Args, true, - false, + -1, ) case "modulemeta": return c.compileCallInternal( - [3]interface{}{c.funcModulemeta, 0, e.Name}, + [3]any{c.funcModulemeta, 0, e.Name}, e.Args, true, - false, + -1, ) default: return c.compileCall(e.Name, e.Args) @@ -977,22 +996,130 @@ func (c *compiler) compileFunc(e *Func) error { } if fn, ok := c.customFuncs[e.Name]; ok && fn.accept(len(e.Args)) { if err := c.compileCallInternal( - [3]interface{}{fn.callback, len(e.Args), e.Name}, + [3]any{fn.callback, len(e.Args), e.Name}, e.Args, true, - false, + -1, ); err != nil { return err } if fn.iter { - c.append(&code{op: opeach}) + c.append(&code{op: opiter}) } return nil } return &funcNotFoundError{e} } -func (c *compiler) funcBuiltins(interface{}, []interface{}) interface{} { +// Appends the compiled code for the assignment operator (`=`) to maximize +// performance. Originally the operator was implemented as follows. +// +// def _assign(p; $x): reduce path(p) as $q (.; setpath($q; $x)); +// +// To overcome the difficulty of reducing allocations on `setpath`, we use the +// `allocator` type and track the allocated addresses during the reduction. +func (c *compiler) compileAssign() { + defer c.appendBuiltin("_assign", 2)() + scope := c.newScope() + v, p := [2]int{scope.id, 0}, [2]int{scope.id, 1} + x, a := [2]int{scope.id, 2}, [2]int{scope.id, 3} + // Cannot reuse v, p due to backtracking in x. + w, q := [2]int{scope.id, 4}, [2]int{scope.id, 5} + c.appends( + &code{op: opscope, v: [3]int{scope.id, 6, 2}}, + &code{op: opstore, v: v}, // def _assign(p; $x): + &code{op: opstore, v: p}, + &code{op: opstore, v: x}, + &code{op: opload, v: v}, + &code{op: opexpbegin}, + &code{op: opload, v: x}, + &code{op: opcallpc}, + &code{op: opstore, v: x}, + &code{op: opexpend}, + &code{op: oppush, v: nil}, + &code{op: opcall, v: [3]any{funcAllocator, 0, "_allocator"}}, + &code{op: opstore, v: a}, + &code{op: opload, v: v}, + &code{op: opfork, v: len(c.codes) + 30}, // reduce [L1] + &code{op: opdup}, + &code{op: opstore, v: w}, + &code{op: oppathbegin}, // path(p) + &code{op: opload, v: p}, + &code{op: opcallpc}, + &code{op: opload, v: w}, + &code{op: oppathend}, + &code{op: opstore, v: q}, // as $q (.; + &code{op: opload, v: a}, // setpath($q; $x) + &code{op: opload, v: x}, + &code{op: opload, v: q}, + &code{op: opload, v: w}, + &code{op: opcall, v: [3]any{funcSetpathWithAllocator, 3, "_setpath"}}, + &code{op: opstore, v: w}, + &code{op: opbacktrack}, // ); + &code{op: oppop}, // [L1] + &code{op: opload, v: w}, + &code{op: opret}, + ) +} + +// Appends the compiled code for the update-assignment operator (`|=`) to +// maximize performance. We use the `allocator` type, just like `_assign/2`. +func (c *compiler) compileModify() { + defer c.appendBuiltin("_modify", 2)() + scope := c.newScope() + v, p := [2]int{scope.id, 0}, [2]int{scope.id, 1} + f, d := [2]int{scope.id, 2}, [2]int{scope.id, 3} + a, l := [2]int{scope.id, 4}, [2]int{scope.id, 5} + c.appends( + &code{op: opscope, v: [3]int{scope.id, 6, 2}}, + &code{op: opstore, v: v}, // def _modify(p; f): + &code{op: opstore, v: p}, + &code{op: opstore, v: f}, + &code{op: oppush, v: []any{}}, + &code{op: opstore, v: d}, + &code{op: oppush, v: nil}, + &code{op: opcall, v: [3]any{funcAllocator, 0, "_allocator"}}, + &code{op: opstore, v: a}, + &code{op: opload, v: v}, + &code{op: opfork, v: len(c.codes) + 39}, // reduce [L1] + &code{op: oppathbegin}, // path(p) + &code{op: opload, v: p}, + &code{op: opcallpc}, + &code{op: opload, v: v}, + &code{op: oppathend}, + &code{op: opstore, v: p}, // as $p (.; + &code{op: opforklabel, v: l}, // label $l | + &code{op: opload, v: v}, // + &code{op: opfork, v: len(c.codes) + 36}, // [L2] + &code{op: oppop}, // (getpath($p) | + &code{op: opload, v: a}, + &code{op: opload, v: p}, + &code{op: opload, v: v}, + &code{op: opcall, v: [3]any{internalFuncs["getpath"].callback, 1, "getpath"}}, + &code{op: opload, v: f}, // f) + &code{op: opcallpc}, + &code{op: opload, v: p}, // setpath($p; ...) + &code{op: opload, v: v}, + &code{op: opcall, v: [3]any{funcSetpathWithAllocator, 3, "_setpath"}}, + &code{op: opstore, v: v}, + &code{op: opload, v: v}, // ., break $l + &code{op: opfork, v: len(c.codes) + 34}, // [L4] + &code{op: opjump, v: len(c.codes) + 38}, // [L3] + &code{op: opload, v: l}, // [L4] + &code{op: opcall, v: [3]any{funcBreak(""), 0, "_break"}}, + &code{op: opload, v: p}, // append $p to $d [L2] + &code{op: opappend, v: d}, // + &code{op: opbacktrack}, // ) | [L3] + &code{op: oppop}, // delpaths($d); [L1] + &code{op: opload, v: a}, + &code{op: opload, v: d}, + &code{op: opload, v: v}, + &code{op: opcall, v: [3]any{funcDelpathsWithAllocator, 2, "_delpaths"}}, + &code{op: opret}, + ) +} + +func (c *compiler) funcBuiltins(any, []any) any { type funcNameArity struct { name string arity int @@ -1027,14 +1154,14 @@ func (c *compiler) funcBuiltins(interface{}, []interface{}) interface{} { return xs[i].name < xs[j].name || xs[i].name == xs[j].name && xs[i].arity < xs[j].arity }) - ys := make([]interface{}, len(xs)) + ys := make([]any, len(xs)) for i, x := range xs { ys[i] = x.name + "/" + strconv.Itoa(x.arity) } return ys } -func (c *compiler) funcInput(interface{}, []interface{}) interface{} { +func (c *compiler) funcInput(any, []any) any { v, ok := c.inputIter.Next() if !ok { return errors.New("break") @@ -1042,10 +1169,10 @@ func (c *compiler) funcInput(interface{}, []interface{}) interface{} { return normalizeNumbers(v) } -func (c *compiler) funcModulemeta(v interface{}, _ []interface{}) interface{} { +func (c *compiler) funcModulemeta(v any, _ []any) any { s, ok := v.(string) if !ok { - return &funcTypeError{"modulemeta", v} + return &func0TypeError{"modulemeta", v} } if c.moduleLoader == nil { return fmt.Errorf("cannot load module: %q", s) @@ -1053,7 +1180,7 @@ func (c *compiler) funcModulemeta(v interface{}, _ []interface{}) interface{} { var q *Query var err error if moduleLoader, ok := c.moduleLoader.(interface { - LoadModuleWithMeta(string, map[string]interface{}) (*Query, error) + LoadModuleWithMeta(string, map[string]any) (*Query, error) }); ok { if q, err = moduleLoader.LoadModuleWithMeta(s, nil); err != nil { return err @@ -1067,43 +1194,60 @@ func (c *compiler) funcModulemeta(v interface{}, _ []interface{}) interface{} { } meta := q.Meta.ToValue() if meta == nil { - meta = make(map[string]interface{}) + meta = make(map[string]any) } - var deps []interface{} - for _, i := range q.Imports { + meta["defs"] = listModuleDefs(q) + meta["deps"] = listModuleDeps(q) + return meta +} + +func listModuleDefs(q *Query) []any { + type funcNameArity struct { + name string + arity int + } + var xs []*funcNameArity + for _, fd := range q.FuncDefs { + if fd.Name[0] != '_' { + xs = append(xs, &funcNameArity{fd.Name, len(fd.Args)}) + } + } + sort.Slice(xs, func(i, j int) bool { + return xs[i].name < xs[j].name || + xs[i].name == xs[j].name && xs[i].arity < xs[j].arity + }) + defs := make([]any, len(xs)) + for i, x := range xs { + defs[i] = x.name + "/" + strconv.Itoa(x.arity) + } + return defs +} + +func listModuleDeps(q *Query) []any { + deps := make([]any, len(q.Imports)) + for j, i := range q.Imports { v := i.Meta.ToValue() if v == nil { - v = make(map[string]interface{}) - } else { - for k := range v { - // dirty hack to remove the internal fields - if strings.HasPrefix(k, "$$") { - delete(v, k) - } - } + v = make(map[string]any) } - if i.ImportPath == "" { - v["relpath"] = i.IncludePath - } else { - v["relpath"] = i.ImportPath - } - if err != nil { - return err + relpath := i.ImportPath + if relpath == "" { + relpath = i.IncludePath } + v["relpath"] = relpath if i.ImportAlias != "" { v["as"] = strings.TrimPrefix(i.ImportAlias, "$") } v["is_data"] = strings.HasPrefix(i.ImportAlias, "$") - deps = append(deps, v) + deps[j] = v } - meta["deps"] = deps - return meta + return deps } func (c *compiler) compileObject(e *Object) error { c.appendCodeInfo(e) if len(e.KeyVals) == 0 { - c.append(&code{op: opconst, v: map[string]interface{}{}}) + c.append(&code{op: opconst, v: map[string]any{}}) return nil } defer c.newScopeDepth()() @@ -1128,7 +1272,7 @@ func (c *compiler) compileObject(e *Object) error { return nil } } - w := make(map[string]interface{}, l) + w := make(map[string]any, l) for i := 0; i < l; i++ { w[c.codes[pc+i*3].v.(string)] = c.codes[pc+i*3+2].v } @@ -1138,61 +1282,57 @@ func (c *compiler) compileObject(e *Object) error { } func (c *compiler) compileObjectKeyVal(v [2]int, kv *ObjectKeyVal) error { - if kv.KeyOnly != "" { - if kv.KeyOnly[0] == '$' { - c.append(&code{op: oppush, v: kv.KeyOnly[1:]}) - c.append(&code{op: opload, v: v}) - return c.compileFunc(&Func{Name: kv.KeyOnly}) - } - c.append(&code{op: oppush, v: kv.KeyOnly}) - c.append(&code{op: opload, v: v}) - return c.compileIndex(&Term{Type: TermTypeIdentity}, &Index{Name: kv.KeyOnly}) - } else if kv.KeyOnlyString != nil { - c.append(&code{op: opload, v: v}) - if err := c.compileString(kv.KeyOnlyString, nil); err != nil { - return err - } - c.append(&code{op: opdup}) - c.append(&code{op: opload, v: v}) - c.append(&code{op: opload, v: v}) - // ref: compileCall - c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["_index"].callback, 2, "_index"}}) - return nil - } else { - if kv.KeyQuery != nil { - c.append(&code{op: opload, v: v}) - f := c.newScopeDepth() - if err := c.compileQuery(kv.KeyQuery); err != nil { - return err + if key := kv.Key; key != "" { + if key[0] == '$' { + if kv.Val == nil { // {$foo} == {foo:$foo} + c.append(&code{op: oppush, v: key[1:]}) } - f() - } else if kv.KeyString != nil { c.append(&code{op: opload, v: v}) - if err := c.compileString(kv.KeyString, nil); err != nil { + if err := c.compileFunc(&Func{Name: key}); err != nil { return err } - if d := c.codes[len(c.codes)-1]; d.op == opconst { - c.codes[len(c.codes)-2] = &code{op: oppush, v: d.v} - c.codes = c.codes[:len(c.codes)-1] + } else { + c.append(&code{op: oppush, v: key}) + if kv.Val == nil { // {foo} == {foo:.foo} + c.append(&code{op: opload, v: v}) + c.append(&code{op: opindex, v: key}) + } + } + } else if key := kv.KeyString; key != nil { + if key.Queries == nil { + c.append(&code{op: oppush, v: key.Str}) + if kv.Val == nil { // {"foo"} == {"foo":.["foo"]} + c.append(&code{op: opload, v: v}) + c.append(&code{op: opindex, v: key.Str}) } - } else if kv.Key[0] == '$' { + } else { c.append(&code{op: opload, v: v}) - if err := c.compileFunc(&Func{Name: kv.Key}); err != nil { + if err := c.compileString(key, nil); err != nil { return err } - } else { - c.append(&code{op: oppush, v: kv.Key}) + if kv.Val == nil { + c.append(&code{op: opdup}) + c.append(&code{op: opload, v: v}) + c.append(&code{op: oppush, v: nil}) + // ref: compileCall + c.append(&code{op: opcall, v: [3]any{internalFuncs["_index"].callback, 2, "_index"}}) + } } + } else if kv.KeyQuery != nil { c.append(&code{op: opload, v: v}) - return c.compileObjectVal(kv.Val) - } -} - -func (c *compiler) compileObjectVal(e *ObjectVal) error { - for _, e := range e.Queries { - if err := c.compileQuery(e); err != nil { + f := c.newScopeDepth() + if err := c.compileQuery(kv.KeyQuery); err != nil { return err } + f() + } + if kv.Val != nil { + c.append(&code{op: opload, v: v}) + for _, e := range kv.Val.Queries { + if err := c.compileQuery(e); err != nil { + return err + } + } } return nil } @@ -1200,25 +1340,23 @@ func (c *compiler) compileObjectVal(e *ObjectVal) error { func (c *compiler) compileArray(e *Array) error { c.appendCodeInfo(e) if e.Query == nil { - c.append(&code{op: opconst, v: []interface{}{}}) + c.append(&code{op: opconst, v: []any{}}) return nil } - c.append(&code{op: oppush, v: []interface{}{}}) + c.append(&code{op: oppush, v: []any{}}) arr := c.newVariable() c.append(&code{op: opstore, v: arr}) pc := len(c.codes) - c.append(&code{op: opfork}) - defer func() { - if pc < len(c.codes) { - c.codes[pc].v = c.pc() - 2 - } - }() + setfork := c.lazy(func() *code { + return &code{op: opfork, v: len(c.codes)} + }) defer c.newScopeDepth()() if err := c.compileQuery(e.Query); err != nil { return err } c.append(&code{op: opappend, v: arr}) c.append(&code{op: opbacktrack}) + setfork() c.append(&code{op: oppop}) c.append(&code{op: opload, v: arr}) if e.Query.Op == OpPipe { @@ -1236,7 +1374,7 @@ func (c *compiler) compileArray(e *Array) error { return nil } } - v := make([]interface{}, l) + v := make([]any, l) for i := 0; i < l; i++ { v[i] = c.codes[pc+i*2+l].v } @@ -1247,6 +1385,10 @@ func (c *compiler) compileArray(e *Array) error { func (c *compiler) compileUnary(e *Unary) error { c.appendCodeInfo(e) + if v := e.toNumber(); v != nil { + c.append(&code{op: opconst, v: v}) + return nil + } if err := c.compileTerm(e.Term); err != nil { return err } @@ -1260,12 +1402,12 @@ func (c *compiler) compileUnary(e *Unary) error { } } -func (c *compiler) compileFormat(fmt string, str *String) error { - f := formatToFunc(fmt) +func (c *compiler) compileFormat(format string, str *String) error { + f := formatToFunc(format) if f == nil { f = &Func{ Name: "format", - Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: fmt[1:]}}}}, + Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: format[1:]}}}}, } } if str == nil { @@ -1274,8 +1416,8 @@ func (c *compiler) compileFormat(fmt string, str *String) error { return c.compileString(str, f) } -func formatToFunc(fmt string) *Func { - switch fmt { +func formatToFunc(format string) *Func { + switch format { case "@text": return &Func{Name: "tostring"} case "@json": @@ -1284,6 +1426,8 @@ func formatToFunc(fmt string) *Func { return &Func{Name: "_tohtml"} case "@uri": return &Func{Name: "_touri"} + case "@urid": + return &Func{Name: "_tourid"} case "@csv": return &Func{Name: "_tocsv"} case "@tsv": @@ -1328,14 +1472,14 @@ func (c *compiler) compileTermSuffix(e *Term, s *Suffix) error { if err := c.compileTerm(e); err != nil { return err } - c.append(&code{op: opeach}) + c.append(&code{op: opiter}) return nil } else if s.Optional { if len(e.SuffixList) > 0 { - if u, ok := e.SuffixList[len(e.SuffixList)-1].toTerm(); ok { - t := *e // clone without changing e - (&t).SuffixList = t.SuffixList[:len(e.SuffixList)-1] - if err := c.compileTerm(&t); err != nil { + if u := e.SuffixList[len(e.SuffixList)-1].toTerm(); u != nil { + // no need to clone (ref: compileTerm) + e.SuffixList = e.SuffixList[:len(e.SuffixList)-1] + if err := c.compileTerm(e); err != nil { return err } e = u @@ -1343,12 +1487,7 @@ func (c *compiler) compileTermSuffix(e *Term, s *Suffix) error { } return c.compileTry(&Try{Body: &Query{Term: e}}) } else if s.Bind != nil { - c.append(&code{op: opdup}) - c.append(&code{op: opexpbegin}) - if err := c.compileTerm(e); err != nil { - return err - } - return c.compileBind(s.Bind) + return c.compileBind(e, s.Bind) } else { return fmt.Errorf("invalid suffix: %s", s) } @@ -1356,46 +1495,56 @@ func (c *compiler) compileTermSuffix(e *Term, s *Suffix) error { func (c *compiler) compileCall(name string, args []*Query) error { fn := internalFuncs[name] + var indexing int + switch name { + case "_index", "_slice": + indexing = 1 + case "getpath": + indexing = 0 + default: + indexing = -1 + } if err := c.compileCallInternal( - [3]interface{}{fn.callback, len(args), name}, + [3]any{fn.callback, len(args), name}, args, true, - name == "_index" || name == "_slice", + indexing, ); err != nil { return err } if fn.iter { - c.append(&code{op: opeach}) + c.append(&code{op: opiter}) } return nil } func (c *compiler) compileCallPc(fn *funcinfo, args []*Query) error { - return c.compileCallInternal(fn.pc, args, false, false) + return c.compileCallInternal(fn.pc, args, false, -1) } func (c *compiler) compileCallInternal( - fn interface{}, args []*Query, internal, indexing bool) error { + fn any, args []*Query, internal bool, indexing int, +) error { if len(args) == 0 { c.append(&code{op: opcall, v: fn}) return nil } - idx := c.newVariable() - c.append(&code{op: opstore, v: idx}) - if indexing && len(args) > 1 { + v := c.newVariable() + c.append(&code{op: opstore, v: v}) + if indexing >= 0 { c.append(&code{op: opexpbegin}) } for i := len(args) - 1; i >= 0; i-- { - pc := c.pc() + 1 // skip opjump (ref: compileFuncDef) + pc := len(c.codes) + 1 // skip opjump (ref: compileFuncDef) name := "lambda:" + strconv.Itoa(pc) if err := c.compileFuncDef(&FuncDef{Name: name, Body: args[i]}, false); err != nil { return err } if internal { - switch c.pc() - pc { + switch len(c.codes) - pc { case 2: // optimize identity argument (opscope, opret) j := len(c.codes) - 3 - c.codes[j] = &code{op: opload, v: idx} + c.codes[j] = &code{op: opload, v: v} c.codes = c.codes[:j+1] s := c.scopes[len(c.scopes)-1] s.funcs = s.funcs[:len(s.funcs)-1] @@ -1406,7 +1555,7 @@ func (c *compiler) compileCallInternal( c.codes[j] = &code{op: oppush, v: c.codes[j+2].v} c.codes = c.codes[:j+1] } else { - c.codes[j] = &code{op: opload, v: idx} + c.codes[j] = &code{op: opload, v: v} c.codes[j+1] = c.codes[j+2] c.codes = c.codes[:j+2] } @@ -1414,14 +1563,14 @@ func (c *compiler) compileCallInternal( s.funcs = s.funcs[:len(s.funcs)-1] c.deleteCodeInfo(name) default: - c.append(&code{op: opload, v: idx}) + c.append(&code{op: opload, v: v}) c.append(&code{op: oppushpc, v: pc}) c.append(&code{op: opcallpc}) } } else { c.append(&code{op: oppushpc, v: pc}) } - if indexing && i == 1 { + if i == indexing { if c.codes[len(c.codes)-2].op == opexpbegin { c.codes[len(c.codes)-2] = c.codes[len(c.codes)-1] c.codes = c.codes[:len(c.codes)-1] @@ -1430,7 +1579,11 @@ func (c *compiler) compileCallInternal( } } } - c.append(&code{op: opload, v: idx}) + if indexing > 0 { + c.append(&code{op: oppush, v: nil}) + } else { + c.append(&code{op: opload, v: v}) + } c.append(&code{op: opcall, v: fn}) return nil } @@ -1439,8 +1592,8 @@ func (c *compiler) append(code *code) { c.codes = append(c.codes, code) } -func (c *compiler) pc() int { - return len(c.codes) +func (c *compiler) appends(codes ...*code) { + c.codes = append(c.codes, codes...) } func (c *compiler) lazy(f func() *code) func() { diff --git a/vendor/github.com/itchyny/gojq/debug.go b/vendor/github.com/itchyny/gojq/debug.go index 4a346fb2..ad3d7216 100644 --- a/vendor/github.com/itchyny/gojq/debug.go +++ b/vendor/github.com/itchyny/gojq/debug.go @@ -1,5 +1,5 @@ -//go:build debug -// +build debug +//go:build gojq_debug +// +build gojq_debug package gojq @@ -32,7 +32,7 @@ type codeinfo struct { pc int } -func (c *compiler) appendCodeInfo(x interface{}) { +func (c *compiler) appendCodeInfo(x any) { if !debug { return } @@ -47,7 +47,7 @@ func (c *compiler) appendCodeInfo(x interface{}) { if c.codes[len(c.codes)-1] != nil && c.codes[len(c.codes)-1].op == opret && strings.HasPrefix(name, "end of ") { diff = -1 } - c.codeinfos = append(c.codeinfos, codeinfo{name, c.pc() + diff}) + c.codeinfos = append(c.codeinfos, codeinfo{name, len(c.codes) + diff}) } func (c *compiler) deleteCodeInfo(name string) { @@ -182,7 +182,7 @@ func debugOperand(c *code) string { switch v := c.v.(type) { case int: return strconv.Itoa(v) - case [3]interface{}: + case [3]any: return fmt.Sprintf("%s/%d", v[2], v[1]) default: panic(c) @@ -192,17 +192,21 @@ func debugOperand(c *code) string { } } -func debugValue(v interface{}) string { +func debugValue(v any) string { switch v := v.(type) { case Iter: return fmt.Sprintf("gojq.Iter(%#v)", v) + case []pathValue: + return fmt.Sprintf("[]gojq.pathValue(%v)", v) case [2]int: return fmt.Sprintf("[%d,%d]", v[0], v[1]) case [3]int: return fmt.Sprintf("[%d,%d,%d]", v[0], v[1], v[2]) - case [3]interface{}: + case [3]any: return fmt.Sprintf("[%v,%v,%v]", v[0], v[1], v[2]) + case allocator: + return fmt.Sprintf("%v", v) default: - return previewValue(v) + return Preview(v) } } diff --git a/vendor/github.com/itchyny/gojq/deepequal.go b/vendor/github.com/itchyny/gojq/deepequal.go deleted file mode 100644 index 37c56e42..00000000 --- a/vendor/github.com/itchyny/gojq/deepequal.go +++ /dev/null @@ -1,48 +0,0 @@ -package gojq - -import ( - "math" - "math/big" -) - -func deepEqual(l, r interface{}) bool { - return binopTypeSwitch(l, r, - func(l, r int) interface{} { - return l == r - }, - func(l, r float64) interface{} { - return l == r || math.IsNaN(l) && math.IsNaN(r) - }, - func(l, r *big.Int) interface{} { - return l.Cmp(r) == 0 - }, - func(l, r string) interface{} { - return l == r - }, - func(l, r []interface{}) interface{} { - if len(l) != len(r) { - return false - } - for i, v := range l { - if !deepEqual(v, r[i]) { - return false - } - } - return true - }, - func(l, r map[string]interface{}) interface{} { - if len(l) != len(r) { - return false - } - for k, v := range l { - if !deepEqual(v, r[k]) { - return false - } - } - return true - }, - func(l, r interface{}) interface{} { - return l == r - }, - ).(bool) -} diff --git a/vendor/github.com/itchyny/gojq/encoder.go b/vendor/github.com/itchyny/gojq/encoder.go index c7905f96..3233e8a9 100644 --- a/vendor/github.com/itchyny/gojq/encoder.go +++ b/vendor/github.com/itchyny/gojq/encoder.go @@ -14,25 +14,29 @@ import ( // Marshal returns the jq-flavored JSON encoding of v. // -// This method only accepts limited types (nil, bool, int, float64, *big.Int, -// string, []interface{} and map[string]interface{}) because these are the -// possible types a gojq iterator can emit. This method marshals NaN to null, -// truncates infinities to (+|-) math.MaxFloat64, uses \b and \f in strings, -// and does not escape '<' and '>' for embedding in HTML. These behaviors are -// based on the marshaler of jq command and different from Go standard library -// method json.Marshal. -func Marshal(v interface{}) ([]byte, error) { +// This method accepts only limited types (nil, bool, int, float64, *big.Int, +// string, []any, and map[string]any) because these are the possible types a +// gojq iterator can emit. This method marshals NaN to null, truncates +// infinities to (+|-) math.MaxFloat64, uses \b and \f in strings, and does not +// escape '<', '>', '&', '\u2028', and '\u2029'. These behaviors are based on +// the marshaler of jq command, and different from json.Marshal in the Go +// standard library. Note that the result is not safe to embed in HTML. +func Marshal(v any) ([]byte, error) { var b bytes.Buffer (&encoder{w: &b}).encode(v) return b.Bytes(), nil } -func jsonMarshal(v interface{}) string { +func jsonMarshal(v any) string { var sb strings.Builder (&encoder{w: &sb}).encode(v) return sb.String() } +func jsonEncodeString(sb *strings.Builder, v string) { + (&encoder{w: sb}).encodeString(v) +} + type encoder struct { w interface { io.Writer @@ -42,7 +46,7 @@ type encoder struct { buf [64]byte } -func (e *encoder) encode(v interface{}) { +func (e *encoder) encode(v any) { switch v := v.(type) { case nil: e.w.WriteString("null") @@ -60,12 +64,12 @@ func (e *encoder) encode(v interface{}) { e.w.Write(v.Append(e.buf[:0], 10)) case string: e.encodeString(v) - case []interface{}: + case []any: e.encodeArray(v) - case map[string]interface{}: - e.encodeMap(v) + case map[string]any: + e.encodeObject(v) default: - panic(fmt.Sprintf("invalid value: %v", v)) + panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v)) } } @@ -80,12 +84,12 @@ func (e *encoder) encodeFloat64(f float64) { } else if f <= -math.MaxFloat64 { f = -math.MaxFloat64 } - fmt := byte('f') + format := byte('f') if x := math.Abs(f); x != 0 && x < 1e-6 || x >= 1e21 { - fmt = 'e' + format = 'e' } - buf := strconv.AppendFloat(e.buf[:0], f, fmt, -1, 64) - if fmt == 'e' { + buf := strconv.AppendFloat(e.buf[:0], f, format, -1, 64) + if format == 'e' { // clean up e-09 to e-9 if n := len(buf); n >= 4 && buf[n-4] == 'e' && buf[n-3] == '-' && buf[n-2] == '0' { buf[n-2] = buf[n-1] @@ -101,30 +105,31 @@ func (e *encoder) encodeString(s string) { start := 0 for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { - if ']' <= b && b <= '~' || '#' <= b && b <= '[' || b == ' ' || b == '!' { + if ' ' <= b && b <= '~' && b != '"' && b != '\\' { i++ continue } if start < i { e.w.WriteString(s[start:i]) } - e.w.WriteByte('\\') switch b { - case '\\', '"': - e.w.WriteByte(b) + case '"': + e.w.WriteString(`\"`) + case '\\': + e.w.WriteString(`\\`) case '\b': - e.w.WriteByte('b') + e.w.WriteString(`\b`) case '\f': - e.w.WriteByte('f') + e.w.WriteString(`\f`) case '\n': - e.w.WriteByte('n') + e.w.WriteString(`\n`) case '\r': - e.w.WriteByte('r') + e.w.WriteString(`\r`) case '\t': - e.w.WriteByte('t') + e.w.WriteString(`\t`) default: const hex = "0123456789abcdef" - e.w.WriteString("u00") + e.w.WriteString(`\u00`) e.w.WriteByte(hex[b>>4]) e.w.WriteByte(hex[b&0xF]) } @@ -150,7 +155,7 @@ func (e *encoder) encodeString(s string) { e.w.WriteByte('"') } -func (e *encoder) encodeArray(vs []interface{}) { +func (e *encoder) encodeArray(vs []any) { e.w.WriteByte('[') for i, v := range vs { if i > 0 { @@ -161,11 +166,11 @@ func (e *encoder) encodeArray(vs []interface{}) { e.w.WriteByte(']') } -func (e *encoder) encodeMap(vs map[string]interface{}) { +func (e *encoder) encodeObject(vs map[string]any) { e.w.WriteByte('{') type keyVal struct { key string - val interface{} + val any } kvs := make([]keyVal, len(vs)) var i int diff --git a/vendor/github.com/itchyny/gojq/env.go b/vendor/github.com/itchyny/gojq/env.go index dd1859c4..bf058eda 100644 --- a/vendor/github.com/itchyny/gojq/env.go +++ b/vendor/github.com/itchyny/gojq/env.go @@ -7,7 +7,7 @@ type env struct { stack *stack paths *stack scopes *scopeStack - values []interface{} + values []any codes []*code codeinfos []codeinfo forks []fork @@ -15,7 +15,7 @@ type env struct { offset int expdepth int label int - args [32]interface{} // len(env.args) > maxarity + args [32]any // len(env.args) > maxarity ctx context.Context } diff --git a/vendor/github.com/itchyny/gojq/error.go b/vendor/github.com/itchyny/gojq/error.go index c13e9777..6657a8c2 100644 --- a/vendor/github.com/itchyny/gojq/error.go +++ b/vendor/github.com/itchyny/gojq/error.go @@ -1,23 +1,18 @@ package gojq -import ( - "fmt" - "math/big" - "strconv" - "strings" -) +import "strconv" // ValueError is an interface for errors with a value for internal function. // Return an error implementing this interface when you want to catch error // values (not error messages) by try-catch, just like built-in error function. -// Refer to WithFunction to add a custom internal function. +// Refer to [WithFunction] to add a custom internal function. type ValueError interface { error - Value() interface{} + Value() any } type expectedObjectError struct { - v interface{} + v any } func (err *expectedObjectError) Error() string { @@ -25,39 +20,39 @@ func (err *expectedObjectError) Error() string { } type expectedArrayError struct { - v interface{} + v any } func (err *expectedArrayError) Error() string { return "expected an array but got: " + typeErrorPreview(err.v) } -type expectedStringError struct { - v interface{} +type iteratorError struct { + v any } -func (err *expectedStringError) Error() string { - return "expected a string but got: " + typeErrorPreview(err.v) +func (err *iteratorError) Error() string { + return "cannot iterate over: " + typeErrorPreview(err.v) } -type iteratorError struct { - v interface{} +type arrayIndexNegativeError struct { + v int } -func (err *iteratorError) Error() string { - return "cannot iterate over: " + typeErrorPreview(err.v) +func (err *arrayIndexNegativeError) Error() string { + return "array index should not be negative: " + Preview(err.v) } type arrayIndexTooLargeError struct { - v interface{} + v any } func (err *arrayIndexTooLargeError) Error() string { - return "array index too large: " + previewValue(err.v) + return "array index too large: " + Preview(err.v) } type objectKeyNotStringError struct { - v interface{} + v any } func (err *objectKeyNotStringError) Error() string { @@ -65,28 +60,33 @@ func (err *objectKeyNotStringError) Error() string { } type arrayIndexNotNumberError struct { - v interface{} + v any } func (err *arrayIndexNotNumberError) Error() string { return "expected a number for indexing an array but got: " + typeErrorPreview(err.v) } +type stringIndexNotNumberError struct { + v any +} + +func (err *stringIndexNotNumberError) Error() string { + return "expected a number for indexing a string but got: " + typeErrorPreview(err.v) +} + type expectedStartEndError struct { - v interface{} + v any } func (err *expectedStartEndError) Error() string { return `expected "start" and "end" for slicing but got: ` + typeErrorPreview(err.v) } -type lengthMismatchError struct { - name string - v, x []interface{} -} +type lengthMismatchError struct{} func (err *lengthMismatchError) Error() string { - return "length mismatch in " + err.name + ": " + typeErrorPreview(err.v) + ", " + typeErrorPreview(err.x) + return "length mismatch" } type inputNotAllowedError struct{} @@ -103,19 +103,66 @@ func (err *funcNotFoundError) Error() string { return "function not defined: " + err.f.Name + "/" + strconv.Itoa(len(err.f.Args)) } -type funcTypeError struct { +type func0TypeError struct { name string - v interface{} + v any } -func (err *funcTypeError) Error() string { +func (err *func0TypeError) Error() string { return err.name + " cannot be applied to: " + typeErrorPreview(err.v) } +type func1TypeError struct { + name string + v, w any +} + +func (err *func1TypeError) Error() string { + return err.name + "(" + Preview(err.w) + ") cannot be applied to: " + typeErrorPreview(err.v) +} + +type func2TypeError struct { + name string + v, w, x any +} + +func (err *func2TypeError) Error() string { + return err.name + "(" + Preview(err.w) + "; " + Preview(err.x) + ") cannot be applied to: " + typeErrorPreview(err.v) +} + +type func0WrapError struct { + name string + v any + err error +} + +func (err *func0WrapError) Error() string { + return err.name + " cannot be applied to " + Preview(err.v) + ": " + err.err.Error() +} + +type func1WrapError struct { + name string + v, w any + err error +} + +func (err *func1WrapError) Error() string { + return err.name + "(" + Preview(err.w) + ") cannot be applied to " + Preview(err.v) + ": " + err.err.Error() +} + +type func2WrapError struct { + name string + v, w, x any + err error +} + +func (err *func2WrapError) Error() string { + return err.name + "(" + Preview(err.w) + "; " + Preview(err.x) + ") cannot be applied to " + Preview(err.v) + ": " + err.err.Error() +} + type exitCodeError struct { - value interface{} + value any code int - halt bool } func (err *exitCodeError) Error() string { @@ -125,11 +172,7 @@ func (err *exitCodeError) Error() string { return "error: " + jsonMarshal(err.value) } -func (err *exitCodeError) IsEmptyError() bool { - return err.value == nil -} - -func (err *exitCodeError) Value() interface{} { +func (err *exitCodeError) Value() any { return err.value } @@ -137,29 +180,53 @@ func (err *exitCodeError) ExitCode() int { return err.code } -func (err *exitCodeError) IsHaltError() bool { - return err.halt +type haltError exitCodeError + +func (err *haltError) Error() string { + return (*exitCodeError)(err).Error() +} + +func (err *haltError) IsEmptyError() bool { + return err.value == nil +} + +func (err *haltError) Value() any { + return (*exitCodeError)(err).Value() +} + +func (err *haltError) ExitCode() int { + return (*exitCodeError)(err).ExitCode() } -type funcContainsError struct { - l, r interface{} +func (err *haltError) IsHaltError() bool { + return true } -func (err *funcContainsError) Error() string { - return "cannot check contains(" + previewValue(err.r) + "): " + typeErrorPreview(err.l) +type flattenDepthError struct { + v float64 } -type hasKeyTypeError struct { - l, r interface{} +func (err *flattenDepthError) Error() string { + return "flatten depth should not be negative: " + Preview(err.v) } -func (err *hasKeyTypeError) Error() string { - return "cannot check whether " + typeErrorPreview(err.l) + " has a key: " + typeErrorPreview(err.r) +type joinTypeError struct { + v any +} + +func (err *joinTypeError) Error() string { + return "join cannot be applied to an array including: " + typeErrorPreview(err.v) +} + +type timeArrayError struct{} + +func (err *timeArrayError) Error() string { + return "expected an array of 8 numbers" } type unaryTypeError struct { name string - v interface{} + v any } func (err *unaryTypeError) Error() string { @@ -168,7 +235,7 @@ func (err *unaryTypeError) Error() string { type binopTypeError struct { name string - l, r interface{} + l, r any } func (err *binopTypeError) Error() string { @@ -176,7 +243,7 @@ func (err *binopTypeError) Error() string { } type zeroDivisionError struct { - l, r interface{} + l, r any } func (err *zeroDivisionError) Error() string { @@ -184,11 +251,11 @@ func (err *zeroDivisionError) Error() string { } type zeroModuloError struct { - l, r interface{} + l, r any } func (err *zeroModuloError) Error() string { - return "cannot modulo " + typeErrorPreview(err.l) + " by: " + typeErrorPreview(err.r) + "" + return "cannot modulo " + typeErrorPreview(err.l) + " by: " + typeErrorPreview(err.r) } type formatNotFoundError struct { @@ -199,21 +266,13 @@ func (err *formatNotFoundError) Error() string { return "format not defined: " + err.n } -type formatCsvTsvRowError struct { +type formatRowError struct { typ string - v interface{} -} - -func (err *formatCsvTsvRowError) Error() string { - return "invalid " + err.typ + " row: " + typeErrorPreview(err.v) -} - -type formatShError struct { - v interface{} + v any } -func (err *formatShError) Error() string { - return "cannot escape for shell: " + typeErrorPreview(err.v) +func (err *formatRowError) Error() string { + return "@" + err.typ + " cannot format an array including: " + typeErrorPreview(err.v) } type tooManyVariableValuesError struct{} @@ -248,7 +307,7 @@ func (err *variableNameError) Error() string { type breakError struct { n string - v interface{} + v any } func (err *breakError) Error() string { @@ -268,7 +327,7 @@ func (err *tryEndError) Error() string { } type invalidPathError struct { - v interface{} + v any } func (err *invalidPathError) Error() string { @@ -276,32 +335,24 @@ func (err *invalidPathError) Error() string { } type invalidPathIterError struct { - v interface{} + v any } func (err *invalidPathIterError) Error() string { return "invalid path on iterating against: " + typeErrorPreview(err.v) } -type getpathError struct { - v, path interface{} -} - -func (err *getpathError) Error() string { - return "cannot getpath with " + previewValue(err.path) + " against: " + typeErrorPreview(err.v) + "" -} - type queryParseError struct { - typ, fname, contents string - err error + fname, contents string + err error } -func (err *queryParseError) QueryParseError() (string, string, string, error) { - return err.typ, err.fname, err.contents, err.err +func (err *queryParseError) QueryParseError() (string, string, error) { + return err.fname, err.contents, err.err } func (err *queryParseError) Error() string { - return "invalid " + err.typ + ": " + err.fname + ": " + err.err.Error() + return "invalid query: " + err.fname + ": " + err.err.Error() } type jsonParseError struct { @@ -317,113 +368,13 @@ func (err *jsonParseError) Error() string { return "invalid json: " + err.fname + ": " + err.err.Error() } -func typeErrorPreview(v interface{}) string { - if _, ok := v.(Iter); ok { - return "gojq.Iter" - } - p := preview(v) - if p != "" { - p = " (" + p + ")" - } - return typeof(v) + p -} - -func typeof(v interface{}) string { - switch v := v.(type) { +func typeErrorPreview(v any) string { + switch v.(type) { case nil: return "null" - case bool: - return "boolean" - case int, float64, *big.Int: - return "number" - case string: - return "string" - case []interface{}: - return "array" - case map[string]interface{}: - return "object" + case Iter: + return "gojq.Iter" default: - panic(fmt.Sprintf("invalid value: %v", v)) - } -} - -type limitedWriter struct { - buf []byte - off int -} - -func (w *limitedWriter) Write(bs []byte) (int, error) { - n := copy(w.buf[w.off:], bs) - if w.off += n; w.off == len(w.buf) { - panic(nil) - } - return n, nil -} - -func (w *limitedWriter) WriteByte(b byte) error { - w.buf[w.off] = b - if w.off++; w.off == len(w.buf) { - panic(nil) - } - return nil -} - -func (w *limitedWriter) WriteString(s string) (int, error) { - n := copy(w.buf[w.off:], s) - if w.off += n; w.off == len(w.buf) { - panic(nil) - } - return n, nil -} - -func (w *limitedWriter) String() string { - return string(w.buf[:w.off]) -} - -func jsonLimitedMarshal(v interface{}, n int) (s string) { - w := &limitedWriter{buf: make([]byte, n)} - defer func() { - recover() - s = w.String() - }() - (&encoder{w: w}).encode(v) - return -} - -func preview(v interface{}) string { - if v == nil { - return "" - } - s := jsonLimitedMarshal(v, 32) - if l := 30; len(s) > l { - var trailing string - switch v.(type) { - case string: - trailing = ` ..."` - case []interface{}: - trailing = " ...]" - case map[string]interface{}: - trailing = " ...}" - default: - trailing = " ..." - } - var sb strings.Builder - sb.Grow(l + 5) - for _, c := range s { - sb.WriteRune(c) - if sb.Len() >= l-len(trailing) { - sb.WriteString(trailing) - break - } - } - s = sb.String() - } - return s -} - -func previewValue(v interface{}) string { - if v == nil { - return "null" + return TypeOf(v) + " (" + Preview(v) + ")" } - return preview(v) } diff --git a/vendor/github.com/itchyny/gojq/execute.go b/vendor/github.com/itchyny/gojq/execute.go index d861c628..91856e74 100644 --- a/vendor/github.com/itchyny/gojq/execute.go +++ b/vendor/github.com/itchyny/gojq/execute.go @@ -2,11 +2,12 @@ package gojq import ( "context" - "fmt" + "math" + "reflect" "sort" ) -func (env *env) execute(bc *Code, v interface{}, vars ...interface{}) Iter { +func (env *env) execute(bc *Code, v any, vars ...any) Iter { env.codes = bc.codes env.codeinfos = bc.codeinfos env.push(v) @@ -17,7 +18,7 @@ func (env *env) execute(bc *Code, v interface{}, vars ...interface{}) Iter { return env } -func (env *env) Next() (interface{}, bool) { +func (env *env) Next() (any, bool) { var err error pc, callpc, index := env.pc, len(env.codes)-1, -1 backtrack, hasCtx := env.backtrack, env.ctx != context.Background() @@ -42,9 +43,9 @@ loop: case oppop: env.pop() case opdup: - x := env.pop() - env.push(x) - env.push(x) + v := env.pop() + env.push(v) + env.push(v) case opconst: env.pop() env.push(code.v) @@ -57,7 +58,7 @@ loop: break loop } n := code.v.(int) - m := make(map[string]interface{}, n) + m := make(map[string]any, n) for i := 0; i < n; i++ { v, k := env.pop(), env.pop() s, ok := k.(string) @@ -70,7 +71,7 @@ loop: env.push(m) case opappend: i := env.index(code.v.([2]int)) - env.values[i] = append(env.values[i].([]interface{}), env.pop()) + env.values[i] = append(env.values[i].([]any), env.pop()) case opfork: if backtrack { if err != nil { @@ -78,47 +79,38 @@ loop: } pc, backtrack = code.v.(int), false goto loop - } else { - env.pushfork(pc) } + env.pushfork(pc) case opforktrybegin: if backtrack { if err == nil { break loop } - switch er := err.(type) { + switch e := err.(type) { case *tryEndError: - err = er.err + err = e.err + break loop + case *breakError, *haltError: break loop case ValueError: - if er, ok := er.(*exitCodeError); ok && er.halt { - break loop - } - if v := er.Value(); v != nil { - env.pop() - env.push(v) - } else { - err = nil - break loop - } + env.pop() + env.push(e.Value()) default: env.pop() env.push(err.Error()) } pc, backtrack, err = code.v.(int), false, nil goto loop - } else { - env.pushfork(pc) } + env.pushfork(pc) case opforktryend: if backtrack { if err != nil { err = &tryEndError{err} } break loop - } else { - env.pushfork(pc) } + env.pushfork(pc) case opforkalt: if backtrack { if err == nil { @@ -126,9 +118,8 @@ loop: } pc, backtrack, err = code.v.(int), false, nil goto loop - } else { - env.pushfork(pc) } + env.pushfork(pc) case opforklabel: if backtrack { label := env.pop() @@ -136,13 +127,12 @@ loop: err = nil } break loop - } else { - env.push(env.label) - env.pushfork(pc) - env.pop() - env.values[env.index(code.v.([2]int))] = env.label - env.label++ } + env.push(env.label) + env.pushfork(pc) + env.pop() + env.values[env.index(code.v.([2]int))] = env.label + env.label++ case opbacktrack: break loop case opjump: @@ -153,6 +143,30 @@ loop: pc = code.v.(int) goto loop } + case opindex, opindexarray: + if backtrack { + break loop + } + p, v := code.v, env.pop() + if code.op == opindexarray && v != nil { + if _, ok := v.([]any); !ok { + err = &expectedArrayError{v} + break loop + } + } + w := funcIndex2(nil, v, p) + if e, ok := w.(error); ok { + err = e + break loop + } + env.push(w) + if !env.paths.empty() && env.expdepth == 0 { + if !env.pathIntact(v) { + err = &invalidPathError{v} + break loop + } + env.paths.push(pathValue{path: p, value: w}) + } case opcall: if backtrack { break loop @@ -161,28 +175,43 @@ loop: case int: pc, callpc, index = v, pc, env.scopes.index goto loop - case [3]interface{}: + case [3]any: argcnt := v[1].(int) x, args := env.pop(), env.args[:argcnt] for i := 0; i < argcnt; i++ { args[i] = env.pop() } - w := v[0].(func(interface{}, []interface{}) interface{})(x, args) + w := v[0].(func(any, []any) any)(x, args) if e, ok := w.(error); ok { - if er, ok := e.(*exitCodeError); !ok || er.value != nil || er.halt { - err = e - } + err = e break loop } env.push(w) - if !env.paths.empty() { - var ps []interface{} - ps, err = env.pathEntries(v[2].(string), x, args) - if err != nil { - break loop - } - for _, p := range ps { - env.paths.push(pathValue{path: p, value: w}) + if !env.paths.empty() && env.expdepth == 0 { + switch v[2].(string) { + case "_index": + if x = args[0]; !env.pathIntact(x) { + err = &invalidPathError{x} + break loop + } + env.paths.push(pathValue{path: args[1], value: w}) + case "_slice": + if x = args[0]; !env.pathIntact(x) { + err = &invalidPathError{x} + break loop + } + env.paths.push(pathValue{ + path: map[string]any{"start": args[2], "end": args[1]}, + value: w, + }) + case "getpath": + if !env.pathIntact(x) { + err = &invalidPathError{x} + break loop + } + for _, p := range args[0].([]any) { + env.paths.push(pathValue{path: p, value: w}) + } } } default: @@ -199,7 +228,7 @@ loop: goto loop case opscope: xs := code.v.([3]int) - var saveindex, outerindex, limit int + var saveindex, outerindex int if index == env.scopes.index { if callpc >= 0 { saveindex = index @@ -207,7 +236,7 @@ loop: callpc, saveindex = env.popscope() } } else { - env.scopes.save(&saveindex, &limit) + saveindex, _ = env.scopes.save() env.scopes.index = index } if outerindex = index; outerindex >= 0 { @@ -218,7 +247,7 @@ loop: env.scopes.push(scope{xs[0], env.offset, callpc, saveindex, outerindex}) env.offset += xs[1] if env.offset > len(env.values) { - vs := make([]interface{}, env.offset*2) + vs := make([]any, env.offset*2) copy(vs, env.values) env.values = vs } @@ -230,7 +259,7 @@ loop: if env.scopes.empty() { return env.pop(), true } - case opeach: + case opiter: if err != nil { break loop } @@ -239,9 +268,8 @@ loop: switch v := env.pop().(type) { case []pathValue: xs = v - case []interface{}: - if !env.paths.empty() && env.expdepth == 0 && - !deepEqual(v, env.paths.top().(pathValue).value) { + case []any: + if !env.paths.empty() && env.expdepth == 0 && !env.pathIntact(v) { err = &invalidPathIterError{v} break loop } @@ -252,9 +280,8 @@ loop: for i, v := range v { xs[i] = pathValue{path: i, value: v} } - case map[string]interface{}: - if !env.paths.empty() && env.expdepth == 0 && - !deepEqual(v, env.paths.top().(pathValue).value) { + case map[string]any: + if !env.paths.empty() && env.expdepth == 0 && !env.pathIntact(v) { err = &invalidPathIterError{v} break loop } @@ -271,10 +298,6 @@ loop: return xs[i].path.(string) < xs[j].path.(string) }) case Iter: - if !env.paths.empty() && env.expdepth == 0 { - err = &invalidPathIterError{v} - break loop - } if w, ok := v.Next(); ok { env.push(v) env.pushfork(pc) @@ -289,6 +312,7 @@ loop: break loop default: err = &iteratorError{v} + env.push(emptyIter{}) break loop } if len(xs) > 1 { @@ -312,18 +336,13 @@ loop: if backtrack { break loop } - if env.expdepth > 0 { - panic(fmt.Sprintf("unexpected expdepth: %d", env.expdepth)) - } env.pop() - x := env.pop() - if deepEqual(x, env.paths.top().(pathValue).value) { - env.push(env.poppaths()) - env.expdepth = env.paths.pop().(int) - } else { - err = &invalidPathError{x} + if v := env.pop(); !env.pathIntact(v) { + err = &invalidPathError{v} break loop } + env.push(env.poppaths()) + env.expdepth = env.paths.pop().(int) default: panic(code.op) } @@ -338,11 +357,11 @@ loop: return nil, false } -func (env *env) push(v interface{}) { +func (env *env) push(v any) { env.stack.push(v) } -func (env *env) pop() interface{} { +func (env *env) pop() any { return env.stack.pop() } @@ -357,9 +376,9 @@ func (env *env) popscope() (int, int) { func (env *env) pushfork(pc int) { f := fork{pc: pc, expdepth: env.expdepth} - env.stack.save(&f.stackindex, &f.stacklimit) - env.scopes.save(&f.scopeindex, &f.scopelimit) - env.paths.save(&f.pathindex, &f.pathlimit) + f.stackindex, f.stacklimit = env.stack.save() + f.scopeindex, f.scopelimit = env.scopes.save() + f.pathindex, f.pathlimit = env.paths.save() env.forks = append(env.forks, f) env.debugForks(pc, ">>>") } @@ -386,39 +405,28 @@ func (env *env) index(v [2]int) int { } type pathValue struct { - path, value interface{} + path, value any } -func (env *env) pathEntries(name string, x interface{}, args []interface{}) ([]interface{}, error) { - switch name { - case "_index": - if env.expdepth > 0 { - return nil, nil - } else if !deepEqual(args[0], env.paths.top().(pathValue).value) { - return nil, &invalidPathError{x} - } - return []interface{}{args[1]}, nil - case "_slice": - if env.expdepth > 0 { - return nil, nil - } else if !deepEqual(args[0], env.paths.top().(pathValue).value) { - return nil, &invalidPathError{x} +func (env *env) pathIntact(v any) bool { + w := env.paths.top().(pathValue).value + switch v := v.(type) { + case []any, map[string]any: + switch w.(type) { + case []any, map[string]any: + v, w := reflect.ValueOf(v), reflect.ValueOf(w) + return v.Pointer() == w.Pointer() && v.Len() == w.Len() } - return []interface{}{map[string]interface{}{"start": args[2], "end": args[1]}}, nil - case "getpath": - if env.expdepth > 0 { - return nil, nil - } else if !deepEqual(x, env.paths.top().(pathValue).value) { - return nil, &invalidPathError{x} + case float64: + if w, ok := w.(float64); ok { + return v == w || math.IsNaN(v) && math.IsNaN(w) } - return args[0].([]interface{}), nil - default: - return nil, nil } + return v == w } -func (env *env) poppaths() []interface{} { - var xs []interface{} +func (env *env) poppaths() []any { + xs := []any{} for { p := env.paths.pop().(pathValue) if p.path == nil { diff --git a/vendor/github.com/itchyny/gojq/func.go b/vendor/github.com/itchyny/gojq/func.go index ca4caa69..d275197e 100644 --- a/vendor/github.com/itchyny/gojq/func.go +++ b/vendor/github.com/itchyny/gojq/func.go @@ -3,10 +3,13 @@ package gojq import ( "encoding/base64" "encoding/json" + "errors" "fmt" + "io" "math" "math/big" "net/url" + "reflect" "regexp" "sort" "strconv" @@ -30,7 +33,7 @@ const ( type function struct { argcount int iter bool - callback func(interface{}, []interface{}) interface{} + callback func(any, []any) any } func (fn function) accept(cnt int) bool { @@ -47,34 +50,44 @@ func init() { "builtins": argFunc0(nil), "input": argFunc0(nil), "modulemeta": argFunc0(nil), + "abs": argFunc0(funcAbs), "length": argFunc0(funcLength), "utf8bytelength": argFunc0(funcUtf8ByteLength), "keys": argFunc0(funcKeys), "has": argFunc1(funcHas), + "to_entries": argFunc0(funcToEntries), + "from_entries": argFunc0(funcFromEntries), "add": argFunc0(funcAdd), "tonumber": argFunc0(funcToNumber), "tostring": argFunc0(funcToString), "type": argFunc0(funcType), "reverse": argFunc0(funcReverse), "contains": argFunc1(funcContains), + "indices": argFunc1(funcIndices), + "index": argFunc1(funcIndex), + "rindex": argFunc1(funcRindex), + "startswith": argFunc1(funcStartsWith), + "endswith": argFunc1(funcEndsWith), + "ltrimstr": argFunc1(funcLtrimstr), + "rtrimstr": argFunc1(funcRtrimstr), "explode": argFunc0(funcExplode), "implode": argFunc0(funcImplode), "split": {argcount1 | argcount2, false, funcSplit}, + "ascii_downcase": argFunc0(funcASCIIDowncase), + "ascii_upcase": argFunc0(funcASCIIUpcase), "tojson": argFunc0(funcToJSON), "fromjson": argFunc0(funcFromJSON), "format": argFunc1(funcFormat), "_tohtml": argFunc0(funcToHTML), "_touri": argFunc0(funcToURI), + "_tourid": argFunc0(funcToURId), "_tocsv": argFunc0(funcToCSV), "_totsv": argFunc0(funcToTSV), "_tosh": argFunc0(funcToSh), "_tobase64": argFunc0(funcToBase64), "_tobase64d": argFunc0(funcToBase64d), - "_index": argFunc2(funcIndex), + "_index": argFunc2(funcIndex2), "_slice": argFunc3(funcSlice), - "_indices": argFunc1(funcIndices), - "_lindex": argFunc1(funcLindex), - "_rindex": argFunc1(funcRindex), "_plus": argFunc0(funcOpPlus), "_negate": argFunc0(funcOpNegate), "_add": argFunc2(funcOpAdd), @@ -89,13 +102,18 @@ func init() { "_less": argFunc2(funcOpLt), "_greatereq": argFunc2(funcOpGe), "_lesseq": argFunc2(funcOpLe), + "flatten": {argcount0 | argcount1, false, funcFlatten}, "_range": {argcount3, true, funcRange}, + "min": argFunc0(funcMin), "_min_by": argFunc1(funcMinBy), + "max": argFunc0(funcMax), "_max_by": argFunc1(funcMaxBy), + "sort": argFunc0(funcSort), "_sort_by": argFunc1(funcSortBy), "_group_by": argFunc1(funcGroupBy), + "unique": argFunc0(funcUnique), "_unique_by": argFunc1(funcUniqueBy), - "_join": argFunc1(funcJoin), + "join": argFunc1(funcJoin), "sin": mathFunc("sin", math.Sin), "cos": mathFunc("cos", math.Cos), "tan": mathFunc("tan", math.Tan), @@ -176,95 +194,89 @@ func init() { "strptime": argFunc1(funcStrptime), "now": argFunc0(funcNow), "_match": argFunc3(funcMatch), + "_capture": argFunc0(funcCapture), "error": {argcount0 | argcount1, false, funcError}, "halt": argFunc0(funcHalt), "halt_error": {argcount0 | argcount1, false, funcHaltError}, - "_type_error": argFunc1(internalfuncTypeError), } } -func argFunc0(fn func(interface{}) interface{}) function { +func argFunc0(f func(any) any) function { return function{ - argcount0, false, func(v interface{}, _ []interface{}) interface{} { - return fn(v) + argcount0, false, func(v any, _ []any) any { + return f(v) }, } } -func argFunc1(fn func(_, _ interface{}) interface{}) function { +func argFunc1(f func(_, _ any) any) function { return function{ - argcount1, false, func(v interface{}, args []interface{}) interface{} { - return fn(v, args[0]) + argcount1, false, func(v any, args []any) any { + return f(v, args[0]) }, } } -func argFunc2(fn func(_, _, _ interface{}) interface{}) function { +func argFunc2(f func(_, _, _ any) any) function { return function{ - argcount2, false, func(v interface{}, args []interface{}) interface{} { - return fn(v, args[0], args[1]) + argcount2, false, func(v any, args []any) any { + return f(v, args[0], args[1]) }, } } -func argFunc3(fn func(_, _, _, _ interface{}) interface{}) function { +func argFunc3(f func(_, _, _, _ any) any) function { return function{ - argcount3, false, func(v interface{}, args []interface{}) interface{} { - return fn(v, args[0], args[1], args[2]) + argcount3, false, func(v any, args []any) any { + return f(v, args[0], args[1], args[2]) }, } } func mathFunc(name string, f func(float64) float64) function { - return argFunc0(func(v interface{}) interface{} { + return argFunc0(func(v any) any { x, ok := toFloat(v) if !ok { - return &funcTypeError{name, v} + return &func0TypeError{name, v} } return f(x) }) } func mathFunc2(name string, f func(_, _ float64) float64) function { - return argFunc2(func(_, x, y interface{}) interface{} { + return argFunc2(func(_, x, y any) any { l, ok := toFloat(x) if !ok { - return &funcTypeError{name, x} + return &func0TypeError{name, x} } r, ok := toFloat(y) if !ok { - return &funcTypeError{name, y} + return &func0TypeError{name, y} } return f(l, r) }) } func mathFunc3(name string, f func(_, _, _ float64) float64) function { - return argFunc3(func(_, a, b, c interface{}) interface{} { + return argFunc3(func(_, a, b, c any) any { x, ok := toFloat(a) if !ok { - return &funcTypeError{name, a} + return &func0TypeError{name, a} } y, ok := toFloat(b) if !ok { - return &funcTypeError{name, b} + return &func0TypeError{name, b} } z, ok := toFloat(c) if !ok { - return &funcTypeError{name, c} + return &func0TypeError{name, c} } return f(x, y, z) }) } -func funcLength(v interface{}) interface{} { +func funcAbs(v any) any { switch v := v.(type) { - case []interface{}: - return len(v) - case map[string]interface{}: - return len(v) - case string: - return len([]rune(v)) case int: if v >= 0 { return v @@ -277,266 +289,467 @@ func funcLength(v interface{}) interface{} { return v } return new(big.Int).Abs(v) - case nil: - return 0 default: - return &funcTypeError{"length", v} + return &func0TypeError{"abs", v} } } -func funcUtf8ByteLength(v interface{}) interface{} { +func funcLength(v any) any { switch v := v.(type) { + case nil: + return 0 + case int: + if v >= 0 { + return v + } + return -v + case float64: + return math.Abs(v) + case *big.Int: + if v.Sign() >= 0 { + return v + } + return new(big.Int).Abs(v) case string: + return len([]rune(v)) + case []any: + return len(v) + case map[string]any: return len(v) default: - return &funcTypeError{"utf8bytelength", v} + return &func0TypeError{"length", v} } } -func funcKeys(v interface{}) interface{} { +func funcUtf8ByteLength(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"utf8bytelength", v} + } + return len(s) +} + +func funcKeys(v any) any { switch v := v.(type) { - case []interface{}: - w := make([]interface{}, len(v)) + case []any: + w := make([]any, len(v)) for i := range v { w[i] = i } return w - case map[string]interface{}: - w := make([]string, len(v)) - var i int - for k := range v { + case map[string]any: + w := make([]any, len(v)) + for i, k := range keys(v) { w[i] = k - i++ - } - sort.Strings(w) - u := make([]interface{}, len(v)) - for i, x := range w { - u[i] = x } - return u + return w default: - return &funcTypeError{"keys", v} + return &func0TypeError{"keys", v} } } -func funcHas(v, x interface{}) interface{} { +func keys(v map[string]any) []string { + w := make([]string, len(v)) + var i int + for k := range v { + w[i] = k + i++ + } + sort.Strings(w) + return w +} + +func values(v any) ([]any, bool) { switch v := v.(type) { - case []interface{}: + case []any: + return v, true + case map[string]any: + vs := make([]any, len(v)) + for i, k := range keys(v) { + vs[i] = v[k] + } + return vs, true + default: + return nil, false + } +} + +func funcHas(v, x any) any { + switch v := v.(type) { + case []any: if x, ok := toInt(x); ok { return 0 <= x && x < len(v) } - return &hasKeyTypeError{v, x} - case map[string]interface{}: - switch x := x.(type) { - case string: + case map[string]any: + if x, ok := x.(string); ok { _, ok := v[x] return ok - default: - return &hasKeyTypeError{v, x} } case nil: return false - default: - return &hasKeyTypeError{v, x} } + return &func1TypeError{"has", v, x} } -func funcAdd(v interface{}) interface{} { - if vs, ok := v.(map[string]interface{}); ok { - xs := make([]string, len(vs)) - var i int - for k := range vs { - xs[i] = k - i++ +func funcToEntries(v any) any { + switch v := v.(type) { + case []any: + w := make([]any, len(v)) + for i, x := range v { + w[i] = map[string]any{"key": i, "value": x} } - sort.Strings(xs) - us := make([]interface{}, len(vs)) - for i, x := range xs { - us[i] = vs[x] + return w + case map[string]any: + w := make([]any, len(v)) + for i, k := range keys(v) { + w[i] = map[string]any{"key": k, "value": v[k]} } - v = us + return w + default: + return &func0TypeError{"to_entries", v} } - vs, ok := v.([]interface{}) +} + +func funcFromEntries(v any) any { + vs, ok := v.([]any) if !ok { - return &funcTypeError{"add", v} + return &func0TypeError{"from_entries", v} + } + w := make(map[string]any, len(vs)) + for _, v := range vs { + switch v := v.(type) { + case map[string]any: + var ( + key string + value any + ok bool + ) + for _, k := range [4]string{"key", "Key", "name", "Name"} { + if k := v[k]; k != nil && k != false { + if key, ok = k.(string); !ok { + return &func0WrapError{"from_entries", vs, &objectKeyNotStringError{k}} + } + break + } + } + if !ok { + return &func0WrapError{"from_entries", vs, &objectKeyNotStringError{nil}} + } + for _, k := range [2]string{"value", "Value"} { + if value, ok = v[k]; ok { + break + } + } + w[key] = value + default: + return &func0TypeError{"from_entries", v} + } + } + return w +} + +func funcAdd(v any) any { + vs, ok := values(v) + if !ok { + return &func0TypeError{"add", v} } v = nil for _, x := range vs { - switch y := x.(type) { - case map[string]interface{}: + switch x := x.(type) { + case nil: + continue + case string: switch w := v.(type) { case nil: - m := make(map[string]interface{}, len(y)) - for k, e := range y { - m[k] = e - } - v = m + var sb strings.Builder + sb.WriteString(x) + v = &sb continue - case map[string]interface{}: - for k, e := range y { - w[k] = e - } + case *strings.Builder: + w.WriteString(x) continue } - case []interface{}: + case []any: switch w := v.(type) { case nil: - s := make([]interface{}, len(y)) - copy(s, y) + s := make([]any, len(x)) + copy(s, x) v = s continue - case []interface{}: - v = append(w, y...) + case []any: + v = append(w, x...) + continue + } + case map[string]any: + switch w := v.(type) { + case nil: + m := make(map[string]any, len(x)) + for k, e := range x { + m[k] = e + } + v = m + continue + case map[string]any: + for k, e := range x { + w[k] = e + } continue } } + if sb, ok := v.(*strings.Builder); ok { + v = sb.String() + } v = funcOpAdd(nil, v, x) if err, ok := v.(error); ok { return err } } + if sb, ok := v.(*strings.Builder); ok { + v = sb.String() + } return v } -func funcToNumber(v interface{}) interface{} { +func funcToNumber(v any) any { switch v := v.(type) { case int, float64, *big.Int: return v case string: if !newLexer(v).validNumber() { - return fmt.Errorf("invalid number: %q", v) + return &func0WrapError{"tonumber", v, errors.New("invalid number")} } - return normalizeNumber(json.Number(v)) + return toNumber(v) default: - return &funcTypeError{"tonumber", v} + return &func0TypeError{"tonumber", v} } } -func funcToString(v interface{}) interface{} { +func toNumber(v string) any { + return normalizeNumber(json.Number(v)) +} + +func funcToString(v any) any { if s, ok := v.(string); ok { return s } return funcToJSON(v) } -func funcType(v interface{}) interface{} { - return typeof(v) +func funcType(v any) any { + return TypeOf(v) } -func funcReverse(v interface{}) interface{} { - vs, ok := v.([]interface{}) +func funcReverse(v any) any { + vs, ok := v.([]any) if !ok { - return &expectedArrayError{v} + return &func0TypeError{"reverse", v} } - ws := make([]interface{}, len(vs)) + ws := make([]any, len(vs)) for i, v := range vs { ws[len(ws)-i-1] = v } return ws } -func funcContains(v, x interface{}) interface{} { - switch v := v.(type) { - case nil: - if x == nil { - return true - } - case bool: - switch x := x.(type) { - case bool: - if v == x { - return true - } - } - } +func funcContains(v, x any) any { return binopTypeSwitch(v, x, - func(l, r int) interface{} { return l == r }, - func(l, r float64) interface{} { return l == r }, - func(l, r *big.Int) interface{} { return l.Cmp(r) == 0 }, - func(l, r string) interface{} { return strings.Contains(l, r) }, - func(l, r []interface{}) interface{} { - for _, x := range r { - var found bool - for _, y := range l { - if funcContains(y, x) == true { - found = true - break + func(l, r int) any { return l == r }, + func(l, r float64) any { return l == r }, + func(l, r *big.Int) any { return l.Cmp(r) == 0 }, + func(l, r string) any { return strings.Contains(l, r) }, + func(l, r []any) any { + R: + for _, r := range r { + for _, l := range l { + if funcContains(l, r) == true { + continue R } } - if !found { - return false - } + return false } return true }, - func(l, r map[string]interface{}) interface{} { - for k, rk := range r { - lk, ok := l[k] - if !ok { - return false - } - c := funcContains(lk, rk) - if _, ok := c.(error); ok { - return false - } - if c == false { + func(l, r map[string]any) any { + if len(l) < len(r) { + return false + } + for k, r := range r { + if l, ok := l[k]; !ok || funcContains(l, r) != true { return false } } return true }, - func(l, r interface{}) interface{} { return &funcContainsError{l, r} }, + func(l, r any) any { + if l == r { + return true + } + return &func1TypeError{"contains", l, r} + }, ) } -func funcExplode(v interface{}) interface{} { +func funcIndices(v, x any) any { + return indexFunc("indices", v, x, indices) +} + +func indices(vs, xs []any) any { + rs := []any{} + if len(xs) == 0 { + return rs + } + for i := 0; i <= len(vs)-len(xs); i++ { + if compare(vs[i:i+len(xs)], xs) == 0 { + rs = append(rs, i) + } + } + return rs +} + +func funcIndex(v, x any) any { + return indexFunc("index", v, x, func(vs, xs []any) any { + if len(xs) == 0 { + return nil + } + for i := 0; i <= len(vs)-len(xs); i++ { + if compare(vs[i:i+len(xs)], xs) == 0 { + return i + } + } + return nil + }) +} + +func funcRindex(v, x any) any { + return indexFunc("rindex", v, x, func(vs, xs []any) any { + if len(xs) == 0 { + return nil + } + for i := len(vs) - len(xs); i >= 0; i-- { + if compare(vs[i:i+len(xs)], xs) == 0 { + return i + } + } + return nil + }) +} + +func indexFunc(name string, v, x any, f func(_, _ []any) any) any { switch v := v.(type) { + case nil: + return nil + case []any: + switch x := x.(type) { + case []any: + return f(v, x) + default: + return f(v, []any{x}) + } case string: - return explode(v) + if x, ok := x.(string); ok { + return f(explode(v), explode(x)) + } + return &func1TypeError{name, v, x} default: - return &funcTypeError{"explode", v} + return &func1TypeError{name, v, x} } } -func explode(s string) []interface{} { - rs := []int32(s) - xs := make([]interface{}, len(rs)) - for i, r := range rs { - xs[i] = int(r) +func funcStartsWith(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"startswith", v, x} } - return xs + t, ok := x.(string) + if !ok { + return &func1TypeError{"startswith", v, x} + } + return strings.HasPrefix(s, t) } -func funcImplode(v interface{}) interface{} { - switch v := v.(type) { - case []interface{}: - return implode(v) - default: - return &funcTypeError{"implode", v} +func funcEndsWith(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"endswith", v, x} + } + t, ok := x.(string) + if !ok { + return &func1TypeError{"endswith", v, x} } + return strings.HasSuffix(s, t) } -func implode(v []interface{}) interface{} { +func funcLtrimstr(v, x any) any { + s, ok := v.(string) + if !ok { + return v + } + t, ok := x.(string) + if !ok { + return v + } + return strings.TrimPrefix(s, t) +} + +func funcRtrimstr(v, x any) any { + s, ok := v.(string) + if !ok { + return v + } + t, ok := x.(string) + if !ok { + return v + } + return strings.TrimSuffix(s, t) +} + +func funcExplode(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"explode", v} + } + return explode(s) +} + +func explode(s string) []any { + xs := make([]any, len([]rune(s))) + var i int + for _, r := range s { + xs[i] = int(r) + i++ + } + return xs +} + +func funcImplode(v any) any { + vs, ok := v.([]any) + if !ok { + return &func0TypeError{"implode", v} + } var sb strings.Builder - sb.Grow(len(v)) - for _, r := range v { - if r, ok := toInt(r); ok && 0 <= r && r <= utf8.MaxRune { - sb.WriteRune(rune(r)) + sb.Grow(len(vs)) + for _, v := range vs { + if r, ok := toInt(v); ok { + if 0 <= r && r <= utf8.MaxRune { + sb.WriteRune(rune(r)) + } else { + sb.WriteRune(utf8.RuneError) + } } else { - return &funcTypeError{"implode", v} + return &func0TypeError{"implode", vs} } } return sb.String() } -func funcSplit(v interface{}, args []interface{}) interface{} { +func funcSplit(v any, args []any) any { s, ok := v.(string) if !ok { - return &funcTypeError{"split", v} + return &func0TypeError{"split", v} } x, ok := args[0].(string) if !ok { - return &funcTypeError{"split", x} + return &func0TypeError{"split", x} } var ss []string if len(args) == 1 { @@ -546,7 +759,7 @@ func funcSplit(v interface{}, args []interface{}) interface{} { if args[1] != nil { v, ok := args[1].(string) if !ok { - return &funcTypeError{"split", args[1]} + return &func0TypeError{"split", args[1]} } flags = v } @@ -556,43 +769,71 @@ func funcSplit(v interface{}, args []interface{}) interface{} { } ss = r.Split(s, -1) } - xs := make([]interface{}, len(ss)) + xs := make([]any, len(ss)) for i, s := range ss { xs[i] = s } return xs } -func funcToJSON(v interface{}) interface{} { - return jsonMarshal(v) +func funcASCIIDowncase(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"ascii_downcase", v} + } + return strings.Map(func(r rune) rune { + if 'A' <= r && r <= 'Z' { + return r + ('a' - 'A') + } + return r + }, s) } -func funcFromJSON(v interface{}) interface{} { - switch v := v.(type) { - case string: - var w interface{} - err := json.Unmarshal([]byte(v), &w) - if err != nil { - return err +func funcASCIIUpcase(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"ascii_upcase", v} + } + return strings.Map(func(r rune) rune { + if 'a' <= r && r <= 'z' { + return r - ('a' - 'A') } - return w - default: - return &funcTypeError{"fromjson", v} + return r + }, s) +} + +func funcToJSON(v any) any { + return jsonMarshal(v) +} + +func funcFromJSON(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"fromjson", v} } + var w any + dec := json.NewDecoder(strings.NewReader(s)) + dec.UseNumber() + if err := dec.Decode(&w); err != nil { + return &func0WrapError{"fromjson", v, err} + } + if _, err := dec.Token(); err != io.EOF { + return &func0TypeError{"fromjson", v} + } + return normalizeNumbers(w) } -func funcFormat(v, x interface{}) interface{} { - switch x := x.(type) { - case string: - fmt := "@" + x - f := formatToFunc(fmt) - if f == nil { - return &formatNotFoundError{fmt} - } - return internalFuncs[f.Name].callback(v, nil) - default: - return &funcTypeError{"format", x} +func funcFormat(v, x any) any { + s, ok := x.(string) + if !ok { + return &func0TypeError{"format", x} + } + format := "@" + s + f := formatToFunc(format) + if f == nil { + return &formatNotFoundError{format} } + return internalFuncs[f.Name].callback(v, nil) } var htmlEscaper = strings.NewReplacer( @@ -603,7 +844,7 @@ var htmlEscaper = strings.NewReplacer( `"`, """, ) -func funcToHTML(v interface{}) interface{} { +func funcToHTML(v any) any { switch x := funcToString(v).(type) { case string: return htmlEscaper.Replace(x) @@ -612,7 +853,7 @@ func funcToHTML(v interface{}) interface{} { } } -func funcToURI(v interface{}) interface{} { +func funcToURI(v any) any { switch x := funcToString(v).(type) { case string: return url.QueryEscape(x) @@ -621,9 +862,27 @@ func funcToURI(v interface{}) interface{} { } } -func funcToCSV(v interface{}) interface{} { - return funcToCSVTSV("csv", v, ",", func(s string) string { - return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` +func funcToURId(v any) any { + switch x := funcToString(v).(type) { + case string: + x, err := url.QueryUnescape(x) + if err != nil { + return &func0WrapError{"@urid", v, err} + } + return x + default: + return x + } +} + +var csvEscaper = strings.NewReplacer( + `"`, `""`, + "\x00", `\0`, +) + +func funcToCSV(v any) any { + return formatJoin("csv", v, ",", func(s string) string { + return `"` + csvEscaper.Replace(s) + `"` }) } @@ -632,72 +891,49 @@ var tsvEscaper = strings.NewReplacer( "\r", `\r`, "\n", `\n`, "\\", `\\`, + "\x00", `\0`, ) -func funcToTSV(v interface{}) interface{} { - return funcToCSVTSV("tsv", v, "\t", func(s string) string { - return tsvEscaper.Replace(s) - }) +func funcToTSV(v any) any { + return formatJoin("tsv", v, "\t", tsvEscaper.Replace) } -func funcToCSVTSV(typ string, v interface{}, sep string, escape func(string) string) interface{} { - switch xs := v.(type) { - case []interface{}: - ys := make([]string, len(xs)) - for i, x := range xs { - y, err := toCSVTSV(typ, x, escape) - if err != nil { - return err - } - ys[i] = y - } - return strings.Join(ys, sep) - default: - return &expectedArrayError{v} - } -} +var shEscaper = strings.NewReplacer( + "'", `'\''`, + "\x00", `\0`, +) -func toCSVTSV(typ string, v interface{}, escape func(string) string) (string, error) { - switch v := v.(type) { - case map[string]interface{}, []interface{}: - return "", &formatCsvTsvRowError{typ, v} - case string: - return escape(v), nil - default: - if s := jsonMarshal(v); s != "null" { - return s, nil - } - return "", nil +func funcToSh(v any) any { + if _, ok := v.([]any); !ok { + v = []any{v} } + return formatJoin("sh", v, " ", func(s string) string { + return "'" + shEscaper.Replace(s) + "'" + }) } -func funcToSh(v interface{}) interface{} { - var xs []interface{} - if w, ok := v.([]interface{}); ok { - xs = w - } else { - xs = []interface{}{v} +func formatJoin(typ string, v any, sep string, escape func(string) string) any { + vs, ok := v.([]any) + if !ok { + return &func0TypeError{"@" + typ, v} } - var s strings.Builder - for i, x := range xs { - if i > 0 { - s.WriteByte(' ') - } - switch x := x.(type) { - case map[string]interface{}, []interface{}: - return &formatShError{x} + ss := make([]string, len(vs)) + for i, v := range vs { + switch v := v.(type) { + case []any, map[string]any: + return &formatRowError{typ, v} case string: - s.WriteByte('\'') - s.WriteString(strings.ReplaceAll(x, "'", `'\''`)) - s.WriteByte('\'') + ss[i] = escape(v) default: - s.WriteString(jsonMarshal(x)) + if s := jsonMarshal(v); s != "null" || typ == "sh" { + ss[i] = s + } } } - return s.String() + return strings.Join(ss, sep) } -func funcToBase64(v interface{}) interface{} { +func funcToBase64(v any) any { switch x := funcToString(v).(type) { case string: return base64.StdEncoding.EncodeToString([]byte(x)) @@ -706,7 +942,7 @@ func funcToBase64(v interface{}) interface{} { } } -func funcToBase64d(v interface{}) interface{} { +func funcToBase64d(v any) any { switch x := funcToString(v).(type) { case string: if i := strings.IndexRune(x, base64.StdPadding); i >= 0 { @@ -714,7 +950,7 @@ func funcToBase64d(v interface{}) interface{} { } y, err := base64.RawStdEncoding.DecodeString(x) if err != nil { - return err + return &func0WrapError{"@base64d", v, err} } return string(y) default: @@ -722,48 +958,39 @@ func funcToBase64d(v interface{}) interface{} { } } -func funcIndex(_, v, x interface{}) interface{} { +func funcIndex2(_, v, x any) any { switch x := x.(type) { case string: switch v := v.(type) { case nil: return nil - case map[string]interface{}: + case map[string]any: return v[x] default: return &expectedObjectError{v} } case int, float64, *big.Int: - idx, _ := toInt(x) + i, _ := toInt(x) switch v := v.(type) { case nil: return nil - case []interface{}: - return funcIndexSlice(nil, nil, &idx, v) + case []any: + return index(v, i) case string: - switch v := funcIndexSlice(nil, nil, &idx, explode(v)).(type) { - case []interface{}: - return implode(v) - case int: - return implode([]interface{}{v}) - case nil: - return "" - default: - panic(v) - } + return indexString(v, i) default: return &expectedArrayError{v} } - case []interface{}: + case []any: switch v := v.(type) { case nil: return nil - case []interface{}: + case []any: return indices(v, x) default: return &expectedArrayError{v} } - case map[string]interface{}: + case map[string]any: if v == nil { return nil } @@ -777,192 +1004,163 @@ func funcIndex(_, v, x interface{}) interface{} { } return funcSlice(nil, v, end, start) default: - return &objectKeyNotStringError{x} + switch v.(type) { + case []any: + return &arrayIndexNotNumberError{x} + case string: + return &stringIndexNotNumberError{x} + default: + return &objectKeyNotStringError{x} + } } } -func indices(vs, xs []interface{}) interface{} { - var rs []interface{} - if len(xs) == 0 { - return rs +func index(vs []any, i int) any { + i = clampIndex(i, -1, len(vs)) + if 0 <= i && i < len(vs) { + return vs[i] } - for i := 0; i < len(vs) && i < len(vs)-len(xs)+1; i++ { - var neq bool - for j, y := range xs { - if neq = compare(vs[i+j], y) != 0; neq { - break + return nil +} + +func indexString(s string, i int) any { + l := len([]rune(s)) + i = clampIndex(i, -1, l) + if 0 <= i && i < l { + for _, r := range s { + if i--; i < 0 { + return string(r) } } - if !neq { - rs = append(rs, i) - } } - return rs + return nil } -func funcSlice(_, v, end, start interface{}) (r interface{}) { - if w, ok := v.(string); ok { - v = explode(w) - defer func() { - switch s := r.(type) { - case []interface{}: - r = implode(s) - case int: - r = implode([]interface{}{s}) - case nil: - r = "" - case error: - default: - panic(r) - } - }() - } +func funcSlice(_, v, e, s any) (r any) { switch v := v.(type) { case nil: return nil - case []interface{}: - if start != nil { - if start, ok := toInt(start); ok { - if end != nil { - if end, ok := toInt(end); ok { - return funcIndexSlice(&start, &end, nil, v) - } - return &arrayIndexNotNumberError{end} - } - return funcIndexSlice(&start, nil, nil, v) - } - return &arrayIndexNotNumberError{start} - } - if end != nil { - if end, ok := toInt(end); ok { - return funcIndexSlice(nil, &end, nil, v) - } - return &arrayIndexNotNumberError{end} - } - return v + case []any: + return slice(v, e, s) + case string: + return sliceString(v, e, s) default: return &expectedArrayError{v} } } -func funcIndexSlice(start, end, index *int, a []interface{}) interface{} { - aa := a - if index != nil { - i := toIndex(aa, *index) - if i < 0 { - return nil - } - return a[i] - } - if end != nil { - i := toIndex(aa, *end) - if i == -1 { - i = len(a) - } else if i == -2 { - i = 0 +func slice(vs []any, e, s any) any { + var start, end int + if s != nil { + if i, ok := toInt(s); ok { + start = clampIndex(i, 0, len(vs)) + } else { + return &arrayIndexNotNumberError{s} } - a = a[:i] } - if start != nil { - i := toIndex(aa, *start) - if i == -1 || len(a) < i { - i = len(a) - } else if i == -2 { - i = 0 + if e != nil { + if i, ok := toInt(e); ok { + end = clampIndex(i, start, len(vs)) + } else { + return &arrayIndexNotNumberError{e} } - a = a[i:] + } else { + end = len(vs) } - return a + return vs[start:end] } -func toIndex(a []interface{}, i int) int { - l := len(a) - switch { - case i < -l: - return -2 - case i < 0: - return l + i - case i < l: - return i - default: - return -1 +func sliceString(v string, e, s any) any { + var start, end int + l := len([]rune(v)) + if s != nil { + if i, ok := toInt(s); ok { + start = clampIndex(i, 0, l) + } else { + return &stringIndexNotNumberError{s} + } } -} - -func funcIndices(v, x interface{}) interface{} { - return indexFunc(v, x, indices) -} - -func funcLindex(v, x interface{}) interface{} { - return indexFunc(v, x, func(vs, xs []interface{}) interface{} { - if len(xs) == 0 { - return nil + if e != nil { + if i, ok := toInt(e); ok { + end = clampIndex(i, start, l) + } else { + return &stringIndexNotNumberError{e} } - for i := 0; i < len(vs) && i < len(vs)-len(xs)+1; i++ { - var neq bool - for j, y := range xs { - if neq = compare(vs[i+j], y) != 0; neq { - break - } + } else { + end = l + } + if start < l { + for i := range v { + if start--; start < 0 { + start = i + break } - if !neq { - return i + } + } else { + start = len(v) + } + if end < l { + for i := range v { + if end--; end < 0 { + end = i + break } } - return nil - }) + } else { + end = len(v) + } + return v[start:end] } -func funcRindex(v, x interface{}) interface{} { - return indexFunc(v, x, func(vs, xs []interface{}) interface{} { - if len(xs) == 0 { - return nil - } - i := len(vs) - 1 - if j := len(vs) - len(xs); j < i { - i = j - } - for ; i >= 0; i-- { - var neq bool - for j, y := range xs { - if neq = compare(vs[i+j], y) != 0; neq { - break - } - } - if !neq { - return i - } - } - return nil - }) +func clampIndex(i, min, max int) int { + if i < 0 { + i += max + } + if i < min { + return min + } else if i < max { + return i + } else { + return max + } } -func indexFunc(v, x interface{}, f func(_, _ []interface{}) interface{}) interface{} { - switch v := v.(type) { - case nil: - return nil - case []interface{}: - switch x := x.(type) { - case []interface{}: - return f(v, x) - default: - return f(v, []interface{}{x}) +func funcFlatten(v any, args []any) any { + vs, ok := values(v) + if !ok { + return &func0TypeError{"flatten", v} + } + var depth float64 + if len(args) == 0 { + depth = -1 + } else { + depth, ok = toFloat(args[0]) + if !ok { + return &func0TypeError{"flatten", args[0]} } - case string: - if x, ok := x.(string); ok { - return f(explode(v), explode(x)) + if depth < 0 { + return &flattenDepthError{depth} + } + } + return flatten([]any{}, vs, depth) +} + +func flatten(xs, vs []any, depth float64) []any { + for _, v := range vs { + if vs, ok := v.([]any); ok && depth != 0 { + xs = flatten(xs, vs, depth-1) + } else { + xs = append(xs, v) } - return &expectedStringError{x} - default: - return &expectedArrayError{v} } + return xs } type rangeIter struct { - value, end, step interface{} + value, end, step any } -func (iter *rangeIter) Next() (interface{}, bool) { +func (iter *rangeIter) Next() (any, bool) { if compare(iter.step, 0)*compare(iter.value, iter.end) >= 0 { return nil, false } @@ -971,54 +1169,70 @@ func (iter *rangeIter) Next() (interface{}, bool) { return v, true } -func funcRange(_ interface{}, xs []interface{}) interface{} { +func funcRange(_ any, xs []any) any { for _, x := range xs { switch x.(type) { case int, float64, *big.Int: default: - return &funcTypeError{"range", x} + return &func0TypeError{"range", x} } } return &rangeIter{xs[0], xs[1], xs[2]} } -func funcMinBy(v, x interface{}) interface{} { - vs, ok := v.([]interface{}) +func funcMin(v any) any { + vs, ok := v.([]any) if !ok { - return &expectedArrayError{v} + return &func0TypeError{"min", v} + } + return minMaxBy(vs, vs, true) +} + +func funcMinBy(v, x any) any { + vs, ok := v.([]any) + if !ok { + return &func1TypeError{"min_by", v, x} } - xs, ok := x.([]interface{}) + xs, ok := x.([]any) if !ok { - return &expectedArrayError{x} + return &func1TypeError{"min_by", v, x} } if len(vs) != len(xs) { - return &lengthMismatchError{"min_by", vs, xs} + return &func1WrapError{"min_by", v, x, &lengthMismatchError{}} } - return funcMinMaxBy(vs, xs, true) + return minMaxBy(vs, xs, true) } -func funcMaxBy(v, x interface{}) interface{} { - vs, ok := v.([]interface{}) +func funcMax(v any) any { + vs, ok := v.([]any) if !ok { - return &expectedArrayError{v} + return &func0TypeError{"max", v} + } + return minMaxBy(vs, vs, false) +} + +func funcMaxBy(v, x any) any { + vs, ok := v.([]any) + if !ok { + return &func1TypeError{"max_by", v, x} } - xs, ok := x.([]interface{}) + xs, ok := x.([]any) if !ok { - return &expectedArrayError{x} + return &func1TypeError{"max_by", v, x} } if len(vs) != len(xs) { - return &lengthMismatchError{"max_by", vs, xs} + return &func1WrapError{"max_by", v, x, &lengthMismatchError{}} } - return funcMinMaxBy(vs, xs, false) + return minMaxBy(vs, xs, false) } -func funcMinMaxBy(vs, xs []interface{}, isMin bool) interface{} { +func minMaxBy(vs, xs []any, isMin bool) any { if len(vs) == 0 { return nil } i, j, x := 0, 0, xs[0] for i++; i < len(xs); i++ { - if (compare(x, xs[i]) > 0) == isMin { + if compare(x, xs[i]) > 0 == isMin { j, x = i, xs[i] } } @@ -1026,45 +1240,86 @@ func funcMinMaxBy(vs, xs []interface{}, isMin bool) interface{} { } type sortItem struct { - value, key interface{} + value, key any +} + +func sortItems(name string, v, x any) ([]*sortItem, error) { + vs, ok := v.([]any) + if !ok { + if strings.HasSuffix(name, "_by") { + return nil, &func1TypeError{name, v, x} + } + return nil, &func0TypeError{name, v} + } + xs, ok := x.([]any) + if !ok { + return nil, &func1TypeError{name, v, x} + } + if len(vs) != len(xs) { + return nil, &func1WrapError{name, v, x, &lengthMismatchError{}} + } + items := make([]*sortItem, len(vs)) + for i, v := range vs { + items[i] = &sortItem{v, xs[i]} + } + sort.SliceStable(items, func(i, j int) bool { + return compare(items[i].key, items[j].key) < 0 + }) + return items, nil +} + +func funcSort(v any) any { + return sortBy("sort", v, v) +} + +func funcSortBy(v, x any) any { + return sortBy("sort_by", v, x) } -func funcSortBy(v, x interface{}) interface{} { - items, err := sortItems("sort_by", v, x) +func sortBy(name string, v, x any) any { + items, err := sortItems(name, v, x) if err != nil { return err } - rs := make([]interface{}, len(items)) + rs := make([]any, len(items)) for i, x := range items { rs[i] = x.value } return rs } -func funcGroupBy(v, x interface{}) interface{} { +func funcGroupBy(v, x any) any { items, err := sortItems("group_by", v, x) if err != nil { return err } - var rs []interface{} - var last interface{} + rs := []any{} + var last any for i, r := range items { if i == 0 || compare(last, r.key) != 0 { - rs, last = append(rs, []interface{}{r.value}), r.key + rs, last = append(rs, []any{r.value}), r.key } else { - rs[len(rs)-1] = append(rs[len(rs)-1].([]interface{}), r.value) + rs[len(rs)-1] = append(rs[len(rs)-1].([]any), r.value) } } return rs } -func funcUniqueBy(v, x interface{}) interface{} { - items, err := sortItems("unique_by", v, x) +func funcUnique(v any) any { + return uniqueBy("unique", v, v) +} + +func funcUniqueBy(v, x any) any { + return uniqueBy("unique_by", v, x) +} + +func uniqueBy(name string, v, x any) any { + items, err := sortItems(name, v, x) if err != nil { return err } - var rs []interface{} - var last interface{} + rs := []any{} + var last any for i, r := range items { if i == 0 || compare(last, r.key) != 0 { rs, last = append(rs, r.value), r.key @@ -1073,61 +1328,39 @@ func funcUniqueBy(v, x interface{}) interface{} { return rs } -func funcJoin(v, x interface{}) interface{} { - vs, ok := v.([]interface{}) +func funcJoin(v, x any) any { + vs, ok := values(v) if !ok { - return &expectedArrayError{v} + return &func1TypeError{"join", v, x} } if len(vs) == 0 { return "" } sep, ok := x.(string) if len(vs) > 1 && !ok { - return &funcTypeError{"join", x} + return &func1TypeError{"join", v, x} } ss := make([]string, len(vs)) - for i, e := range vs { - switch e := e.(type) { + for i, v := range vs { + switch v := v.(type) { case nil: case string: - ss[i] = e + ss[i] = v case bool: - if e { + if v { ss[i] = "true" } else { ss[i] = "false" } case int, float64, *big.Int: - ss[i] = jsonMarshal(e) + ss[i] = jsonMarshal(v) default: - return &unaryTypeError{"join", e} + return &joinTypeError{v} } } return strings.Join(ss, sep) } -func sortItems(name string, v, x interface{}) ([]*sortItem, error) { - vs, ok := v.([]interface{}) - if !ok { - return nil, &expectedArrayError{v} - } - xs, ok := x.([]interface{}) - if !ok { - return nil, &expectedArrayError{x} - } - if len(vs) != len(xs) { - return nil, &lengthMismatchError{name, vs, xs} - } - items := make([]*sortItem, len(vs)) - for i, v := range vs { - items[i] = &sortItem{v, xs[i]} - } - sort.SliceStable(items, func(i, j int) bool { - return compare(items[i].key, items[j].key) < 0 - }) - return items, nil -} - func funcSignificand(v float64) float64 { if math.IsNaN(v) || math.IsInf(v, 0) || v == 0.0 { return v @@ -1139,22 +1372,22 @@ func funcExp10(v float64) float64 { return math.Pow(10, v) } -func funcFrexp(v interface{}) interface{} { +func funcFrexp(v any) any { x, ok := toFloat(v) if !ok { - return &funcTypeError{"frexp", v} + return &func0TypeError{"frexp", v} } f, e := math.Frexp(x) - return []interface{}{f, e} + return []any{f, e} } -func funcModf(v interface{}) interface{} { +func funcModf(v any) any { x, ok := toFloat(v) if !ok { - return &funcTypeError{"modf", v} + return &func0TypeError{"modf", v} } i, f := math.Modf(x) - return []interface{}{f, i} + return []any{f, i} } func funcLgamma(v float64) float64 { @@ -1190,237 +1423,169 @@ func funcYn(l, r float64) float64 { return math.Yn(int(l), r) } -func funcInfinite(interface{}) interface{} { +func funcInfinite(any) any { return math.Inf(1) } -func funcIsfinite(v interface{}) interface{} { +func funcIsfinite(v any) any { x, ok := toFloat(v) return ok && !math.IsInf(x, 0) } -func funcIsinfinite(v interface{}) interface{} { +func funcIsinfinite(v any) any { x, ok := toFloat(v) return ok && math.IsInf(x, 0) } -func funcNan(interface{}) interface{} { +func funcNan(any) any { return math.NaN() } -func funcIsnan(v interface{}) interface{} { +func funcIsnan(v any) any { x, ok := toFloat(v) if !ok { if v == nil { return false } - return &funcTypeError{"isnan", v} + return &func0TypeError{"isnan", v} } return math.IsNaN(x) } -func funcIsnormal(v interface{}) interface{} { - x, ok := toFloat(v) - return ok && !math.IsNaN(x) && !math.IsInf(x, 0) && x != 0.0 +func funcIsnormal(v any) any { + if v, ok := toFloat(v); ok { + e := math.Float64bits(v) & 0x7ff0000000000000 >> 52 + return 0 < e && e < 0x7ff + } + return false } -func funcSetpath(v, p, w interface{}) interface{} { - path, ok := p.([]interface{}) - if !ok { - return &funcTypeError{"setpath", p} +// An `allocator` creates new maps and slices, stores the allocated addresses. +// This allocator is used to reduce allocations on assignment operator (`=`), +// update-assignment operator (`|=`), and the `map_values`, `del`, `delpaths` +// functions. +type allocator map[uintptr]struct{} + +func funcAllocator(any, []any) any { + return allocator{} +} + +func (a allocator) allocated(v any) bool { + _, ok := a[reflect.ValueOf(v).Pointer()] + return ok +} + +func (a allocator) makeObject(l int) map[string]any { + v := make(map[string]any, l) + if a != nil { + a[reflect.ValueOf(v).Pointer()] = struct{}{} } - var err error - if v, err = updatePaths(v, path, w, false); err != nil { - if err, ok := err.(*funcTypeError); ok { - err.name = "setpath" - } - return err + return v +} + +func (a allocator) makeArray(l, c int) []any { + if c < l { + c = l + } + v := make([]any, l, c) + if a != nil { + a[reflect.ValueOf(v).Pointer()] = struct{}{} } return v } -func funcDelpaths(v, p interface{}) interface{} { - paths, ok := p.([]interface{}) +func funcSetpath(v, p, n any) any { + // There is no need to use an allocator on a single update. + return setpath(v, p, n, nil) +} + +// Used in compiler#compileAssign and compiler#compileModify. +func funcSetpathWithAllocator(v any, args []any) any { + return setpath(v, args[0], args[1], args[2].(allocator)) +} + +func setpath(v, p, n any, a allocator) any { + path, ok := p.([]any) + if !ok { + return &func1TypeError{"setpath", v, p} + } + u, err := update(v, path, n, a) + if err != nil { + return &func2WrapError{"setpath", v, p, n, err} + } + return u +} + +func funcDelpaths(v, p any) any { + return delpaths(v, p, allocator{}) +} + +// Used in compiler#compileAssign and compiler#compileModify. +func funcDelpathsWithAllocator(v any, args []any) any { + return delpaths(v, args[0], args[1].(allocator)) +} + +func delpaths(v, p any, a allocator) any { + paths, ok := p.([]any) if !ok { - return &funcTypeError{"delpaths", p} + return &func1TypeError{"delpaths", v, p} + } + if len(paths) == 0 { + return v } // Fills the paths with an empty value and then delete them. We cannot delete // in each loop because array indices should not change. For example, // jq -n "[0, 1, 2, 3] | delpaths([[1], [2]])" #=> [0, 3]. var empty struct{} var err error - for _, p := range paths { - path, ok := p.([]interface{}) + u := v + for _, q := range paths { + path, ok := q.([]any) if !ok { - return &funcTypeError{"delpaths", p} + return &func1WrapError{"delpaths", v, p, &expectedArrayError{q}} } - if v, err = updatePaths(v, path, empty, true); err != nil { - return err + u, err = update(u, path, empty, a) + if err != nil { + return &func1WrapError{"delpaths", v, p, err} } } - return deleteEmpty(v) + return deleteEmpty(u) } -func updatePaths(v interface{}, path []interface{}, w interface{}, delpaths bool) (interface{}, error) { +func update(v any, path []any, n any, a allocator) (any, error) { if len(path) == 0 { - return w, nil + return n, nil } - switch x := path[0].(type) { + switch p := path[0].(type) { case string: - if v == nil { - if delpaths { - return v, nil - } - v = make(map[string]interface{}) - } - switch uu := v.(type) { - case map[string]interface{}: - if _, ok := uu[x]; !ok && delpaths { - return v, nil - } - u, err := updatePaths(uu[x], path[1:], w, delpaths) - if err != nil { - return nil, err - } - vs := make(map[string]interface{}, len(uu)) - for k, v := range uu { - vs[k] = v - } - vs[x] = u - return vs, nil + switch v := v.(type) { + case nil: + return updateObject(nil, p, path[1:], n, a) + case map[string]any: + return updateObject(v, p, path[1:], n, a) case struct{}: return v, nil default: return nil, &expectedObjectError{v} } case int, float64, *big.Int: - if v == nil { - if delpaths { - return v, nil - } - v = []interface{}{} - } - switch uu := v.(type) { - case []interface{}: - y, _ := toInt(x) - l := len(uu) - var copied bool - if copied = y >= l; copied { - if delpaths { - return v, nil - } - if y > 0x3ffffff { - return nil, &arrayIndexTooLargeError{y} - } - l = y + 1 - ys := make([]interface{}, l) - copy(ys, uu) - uu = ys - } else if y < -l { - if delpaths { - return v, nil - } - return nil, &funcTypeError{v: y} - } else if y < 0 { - y += l - } - u, err := updatePaths(uu[y], path[1:], w, delpaths) - if err != nil { - return nil, err - } - if copied { - uu[y] = u - return uu, nil - } - vs := make([]interface{}, l) - copy(vs, uu) - vs[y] = u - return vs, nil + i, _ := toInt(p) + switch v := v.(type) { + case nil: + return updateArrayIndex(nil, i, path[1:], n, a) + case []any: + return updateArrayIndex(v, i, path[1:], n, a) case struct{}: return v, nil default: return nil, &expectedArrayError{v} } - case map[string]interface{}: - if len(x) == 0 { - switch v.(type) { - case []interface{}: - return nil, &arrayIndexNotNumberError{x} - default: - return nil, &objectKeyNotStringError{x} - } - } - if v == nil { - v = []interface{}{} - } - switch uu := v.(type) { - case []interface{}: - var start, end int - if x, ok := toInt(x["start"]); ok { - x := toIndex(uu, x) - if x > len(uu) || x == -1 { - start = len(uu) - } else if x == -2 { - start = 0 - } else { - start = x - } - } - if x, ok := toInt(x["end"]); ok { - x := toIndex(uu, x) - if x == -1 { - end = len(uu) - } else if x < start { - end = start - } else { - end = x - } - } else { - end = len(uu) - } - if delpaths { - if start >= end { - return uu, nil - } - if len(path) > 1 { - u, err := updatePaths(uu[start:end], path[1:], w, delpaths) - if err != nil { - return nil, err - } - switch us := u.(type) { - case []interface{}: - vs := make([]interface{}, len(uu)) - copy(vs, uu) - copy(vs[start:end], us) - return vs, nil - default: - return nil, &expectedArrayError{u} - } - } - vs := make([]interface{}, len(uu)) - copy(vs, uu) - for y := start; y < end; y++ { - vs[y] = w - } - return vs, nil - } - if len(path) > 1 { - u, err := updatePaths(uu[start:end], path[1:], w, delpaths) - if err != nil { - return nil, err - } - w = u - } - switch v := w.(type) { - case []interface{}: - vs := make([]interface{}, start+len(v)+len(uu)-end) - copy(vs, uu[:start]) - copy(vs[start:], v) - copy(vs[start+len(v):], uu[end:]) - return vs, nil - default: - return nil, &expectedArrayError{v} - } + case map[string]any: + switch v := v.(type) { + case nil: + return updateArraySlice(nil, p, path[1:], n, a) + case []any: + return updateArraySlice(v, p, path[1:], n, a) case struct{}: return v, nil default: @@ -1428,73 +1593,218 @@ func updatePaths(v interface{}, path []interface{}, w interface{}, delpaths bool } default: switch v.(type) { - case []interface{}: - return nil, &arrayIndexNotNumberError{x} + case []any: + return nil, &arrayIndexNotNumberError{p} default: - return nil, &objectKeyNotStringError{x} + return nil, &objectKeyNotStringError{p} + } + } +} + +func updateObject(v map[string]any, k string, path []any, n any, a allocator) (any, error) { + x, ok := v[k] + if !ok && n == struct{}{} { + return v, nil + } + u, err := update(x, path, n, a) + if err != nil { + return nil, err + } + if a.allocated(v) { + v[k] = u + return v, nil + } + w := a.makeObject(len(v) + 1) + for k, v := range v { + w[k] = v + } + w[k] = u + return w, nil +} + +func updateArrayIndex(v []any, i int, path []any, n any, a allocator) (any, error) { + var x any + if j := clampIndex(i, -1, len(v)); j < 0 { + if n == struct{}{} { + return v, nil + } + return nil, &arrayIndexNegativeError{i} + } else if j < len(v) { + i = j + x = v[i] + } else { + if n == struct{}{} { + return v, nil + } + if i >= 0x8000000 { + return nil, &arrayIndexTooLargeError{i} + } + } + u, err := update(x, path, n, a) + if err != nil { + return nil, err + } + l, c := len(v), cap(v) + if a.allocated(v) { + if i < c { + if i >= l { + v = v[:i+1] + } + v[i] = u + return v, nil + } + c *= 2 + } + if i >= l { + l = i + 1 + } + w := a.makeArray(l, c) + copy(w, v) + w[i] = u + return w, nil +} + +func updateArraySlice(v []any, m map[string]any, path []any, n any, a allocator) (any, error) { + s, ok := m["start"] + if !ok { + return nil, &expectedStartEndError{m} + } + e, ok := m["end"] + if !ok { + return nil, &expectedStartEndError{m} + } + var start, end int + if i, ok := toInt(s); ok { + start = clampIndex(i, 0, len(v)) + } + if i, ok := toInt(e); ok { + end = clampIndex(i, start, len(v)) + } else { + end = len(v) + } + if start == end && n == struct{}{} { + return v, nil + } + u, err := update(v[start:end], path, n, a) + if err != nil { + return nil, err + } + switch u := u.(type) { + case []any: + var w []any + if len(u) == end-start && a.allocated(v) { + w = v + } else { + w = a.makeArray(len(v)-(end-start)+len(u), 0) + copy(w, v[:start]) + copy(w[start+len(u):], v[end:]) + } + copy(w[start:], u) + return w, nil + case struct{}: + var w []any + if a.allocated(v) { + w = v + } else { + w = a.makeArray(len(v), 0) + copy(w, v) + } + for i := start; i < end; i++ { + w[i] = u + } + return w, nil + default: + return nil, &expectedArrayError{u} + } +} + +func deleteEmpty(v any) any { + switch v := v.(type) { + case struct{}: + return nil + case map[string]any: + for k, w := range v { + if w == struct{}{} { + delete(v, k) + } else { + v[k] = deleteEmpty(w) + } + } + return v + case []any: + var j int + for _, w := range v { + if w != struct{}{} { + v[j] = deleteEmpty(w) + j++ + } } + for i := j; i < len(v); i++ { + v[i] = nil + } + return v[:j] + default: + return v } } -func funcGetpath(v, p interface{}) interface{} { - keys, ok := p.([]interface{}) +func funcGetpath(v, p any) any { + path, ok := p.([]any) if !ok { - return &funcTypeError{"getpath", p} + return &func1TypeError{"getpath", v, p} } u := v - for _, x := range keys { + for _, x := range path { switch v.(type) { - case map[string]interface{}: - case []interface{}: - case nil: + case nil, []any, map[string]any: + v = funcIndex2(nil, v, x) + if err, ok := v.(error); ok { + return &func1WrapError{"getpath", u, p, err} + } default: - return &getpathError{u, p} - } - v = funcIndex(nil, v, x) - if _, ok := v.(error); ok { - return &getpathError{u, p} + return &func1TypeError{"getpath", u, p} } } return v } -func funcTranspose(v interface{}) interface{} { - vss, ok := v.([]interface{}) +func funcTranspose(v any) any { + vss, ok := v.([]any) if !ok { - return &funcTypeError{"transpose", v} + return &func0TypeError{"transpose", v} } if len(vss) == 0 { - return []interface{}{} + return []any{} } var l int for _, vs := range vss { - vs, ok := vs.([]interface{}) + vs, ok := vs.([]any) if !ok { - return &funcTypeError{"transpose", v} + return &func0TypeError{"transpose", v} } if k := len(vs); l < k { l = k } } - wss := make([][]interface{}, l) - xs := make([]interface{}, l) + wss := make([][]any, l) + xs := make([]any, l) for i, k := 0, len(vss); i < l; i++ { - s := make([]interface{}, k) + s := make([]any, k) wss[i] = s xs[i] = s } for i, vs := range vss { - for j, v := range vs.([]interface{}) { + for j, v := range vs.([]any) { wss[j][i] = v } } return xs } -func funcBsearch(v, t interface{}) interface{} { - vs, ok := v.([]interface{}) +func funcBsearch(v, t any) any { + vs, ok := v.([]any) if !ok { - return &funcTypeError{"bsearch", v} + return &func1TypeError{"bsearch", v, t} } i := sort.Search(len(vs), func(i int) bool { return compare(vs[i], t) >= 0 @@ -1505,23 +1815,23 @@ func funcBsearch(v, t interface{}) interface{} { return -i - 1 } -func funcGmtime(v interface{}) interface{} { +func funcGmtime(v any) any { if v, ok := toFloat(v); ok { return epochToArray(v, time.UTC) } - return &funcTypeError{"gmtime", v} + return &func0TypeError{"gmtime", v} } -func funcLocaltime(v interface{}) interface{} { +func funcLocaltime(v any) any { if v, ok := toFloat(v); ok { return epochToArray(v, time.Local) } - return &funcTypeError{"localtime", v} + return &func0TypeError{"localtime", v} } -func epochToArray(v float64, loc *time.Location) []interface{} { +func epochToArray(v float64, loc *time.Location) []any { t := time.Unix(int64(v), int64((v-math.Floor(v))*1e9)).In(loc) - return []interface{}{ + return []any{ t.Year(), int(t.Month()) - 1, t.Day(), @@ -1533,130 +1843,143 @@ func epochToArray(v float64, loc *time.Location) []interface{} { } } -func funcMktime(v interface{}) interface{} { - if a, ok := v.([]interface{}); ok { - t, err := arrayToTime("mktime", a, time.UTC) - if err != nil { - return err - } - return float64(t.Unix()) +func funcMktime(v any) any { + a, ok := v.([]any) + if !ok { + return &func0TypeError{"mktime", v} } - return &funcTypeError{"mktime", v} + t, err := arrayToTime(a, time.UTC) + if err != nil { + return &func0WrapError{"mktime", v, err} + } + return timeToEpoch(t) +} + +func timeToEpoch(t time.Time) float64 { + return float64(t.Unix()) + float64(t.Nanosecond())/1e9 } -func funcStrftime(v, x interface{}) interface{} { +func funcStrftime(v, x any) any { if w, ok := toFloat(v); ok { v = epochToArray(w, time.UTC) } - if a, ok := v.([]interface{}); ok { - if format, ok := x.(string); ok { - t, err := arrayToTime("strftime", a, time.UTC) - if err != nil { - return err - } - return timefmt.Format(t, format) - } - return &funcTypeError{"strftime", x} + a, ok := v.([]any) + if !ok { + return &func1TypeError{"strftime", v, x} + } + format, ok := x.(string) + if !ok { + return &func1TypeError{"strftime", v, x} + } + t, err := arrayToTime(a, time.UTC) + if err != nil { + return &func1WrapError{"strftime", v, x, err} } - return &funcTypeError{"strftime", v} + return timefmt.Format(t, format) } -func funcStrflocaltime(v, x interface{}) interface{} { +func funcStrflocaltime(v, x any) any { if w, ok := toFloat(v); ok { v = epochToArray(w, time.Local) } - if a, ok := v.([]interface{}); ok { - if format, ok := x.(string); ok { - t, err := arrayToTime("strflocaltime", a, time.Local) - if err != nil { - return err - } - return timefmt.Format(t, format) - } - return &funcTypeError{"strflocaltime", x} + a, ok := v.([]any) + if !ok { + return &func1TypeError{"strflocaltime", v, x} + } + format, ok := x.(string) + if !ok { + return &func1TypeError{"strflocaltime", v, x} + } + t, err := arrayToTime(a, time.Local) + if err != nil { + return &func1WrapError{"strflocaltime", v, x, err} } - return &funcTypeError{"strflocaltime", v} + return timefmt.Format(t, format) } -func funcStrptime(v, x interface{}) interface{} { - if v, ok := v.(string); ok { - if format, ok := x.(string); ok { - t, err := timefmt.Parse(v, format) - if err != nil { - return err - } - var s time.Time - if t == s { - return &funcTypeError{"strptime", v} - } - return epochToArray(float64(t.Unix())+float64(t.Nanosecond())/1e9, time.UTC) - } - return &funcTypeError{"strptime", x} +func funcStrptime(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"strptime", v, x} + } + format, ok := x.(string) + if !ok { + return &func1TypeError{"strptime", v, x} + } + t, err := timefmt.Parse(s, format) + if err != nil { + return &func1WrapError{"strptime", v, x, err} } - return &funcTypeError{"strptime", v} + var u time.Time + if t == u { + return &func1TypeError{"strptime", v, x} + } + return epochToArray(timeToEpoch(t), time.UTC) } -func arrayToTime(name string, a []interface{}, loc *time.Location) (time.Time, error) { +func arrayToTime(a []any, loc *time.Location) (time.Time, error) { var t time.Time if len(a) != 8 { - return t, &funcTypeError{name, a} + return t, &timeArrayError{} } var y, m, d, h, min, sec, nsec int - if x, ok := toInt(a[0]); ok { - y = x - } else { - return t, &funcTypeError{name, a} + var ok bool + if y, ok = toInt(a[0]); !ok { + return t, &timeArrayError{} } - if x, ok := toInt(a[1]); ok { - m = x + 1 + if m, ok = toInt(a[1]); ok { + m++ } else { - return t, &funcTypeError{name, a} + return t, &timeArrayError{} } - if x, ok := toInt(a[2]); ok { - d = x - } else { - return t, &funcTypeError{name, a} + if d, ok = toInt(a[2]); !ok { + return t, &timeArrayError{} } - if x, ok := toInt(a[3]); ok { - h = x - } else { - return t, &funcTypeError{name, a} + if h, ok = toInt(a[3]); !ok { + return t, &timeArrayError{} } - if x, ok := toInt(a[4]); ok { - min = x - } else { - return t, &funcTypeError{name, a} + if min, ok = toInt(a[4]); !ok { + return t, &timeArrayError{} } if x, ok := toFloat(a[5]); ok { sec = int(x) nsec = int((x - math.Floor(x)) * 1e9) } else { - return t, &funcTypeError{name, a} + return t, &timeArrayError{} + } + if _, ok = toFloat(a[6]); !ok { + return t, &timeArrayError{} + } + if _, ok = toFloat(a[7]); !ok { + return t, &timeArrayError{} } return time.Date(y, time.Month(m), d, h, min, sec, nsec, loc), nil } -func funcNow(interface{}) interface{} { - t := time.Now() - return float64(t.Unix()) + float64(t.Nanosecond())/1e9 +func funcNow(any) any { + return timeToEpoch(time.Now()) } -func funcMatch(v, re, fs, testing interface{}) interface{} { +func funcMatch(v, re, fs, testing any) any { + name := "match" + if testing == true { + name = "test" + } var flags string if fs != nil { v, ok := fs.(string) if !ok { - return &funcTypeError{"match", fs} + return &func2TypeError{name, v, re, fs} } flags = v } s, ok := v.(string) if !ok { - return &funcTypeError{"match", v} + return &func2TypeError{name, v, re, fs} } restr, ok := re.(string) if !ok { - return &funcTypeError{"match", v} + return &func2TypeError{name, v, re, fs} } r, err := compileRegexp(restr, flags) if err != nil { @@ -1674,16 +1997,16 @@ func funcMatch(v, re, fs, testing interface{}) interface{} { xs = [][]int{got} } } - res, names := make([]interface{}, len(xs)), r.SubexpNames() + res, names := make([]any, len(xs)), r.SubexpNames() for i, x := range xs { - captures := make([]interface{}, (len(x)-2)/2) + captures := make([]any, (len(x)-2)/2) for j := 1; j < len(x)/2; j++ { - var name interface{} + var name any if n := names[j]; n != "" { name = n } if x[j*2] < 0 { - captures[j-1] = map[string]interface{}{ + captures[j-1] = map[string]any{ "name": name, "offset": -1, "length": 0, @@ -1691,14 +2014,14 @@ func funcMatch(v, re, fs, testing interface{}) interface{} { } continue } - captures[j-1] = map[string]interface{}{ + captures[j-1] = map[string]any{ "name": name, "offset": len([]rune(s[:x[j*2]])), "length": len([]rune(s[:x[j*2+1]])) - len([]rune(s[:x[j*2]])), "string": s[x[j*2]:x[j*2+1]], } } - res[i] = map[string]interface{}{ + res[i] = map[string]any{ "offset": len([]rune(s[:x[0]])), "length": len([]rune(s[:x[1]])) - len([]rune(s[:x[0]])), "string": s[x[0]:x[1]], @@ -1728,40 +2051,50 @@ func compileRegexp(re, flags string) (*regexp.Regexp, error) { return r, nil } -func funcError(v interface{}, args []interface{}) interface{} { +func funcCapture(v any) any { + vs, ok := v.(map[string]any) + if !ok { + return &expectedObjectError{v} + } + v = vs["captures"] + captures, ok := v.([]any) + if !ok { + return &expectedArrayError{v} + } + w := make(map[string]any, len(captures)) + for _, capture := range captures { + if capture, ok := capture.(map[string]any); ok { + if name, ok := capture["name"].(string); ok { + w[name] = capture["string"] + } + } + } + return w +} + +func funcError(v any, args []any) any { if len(args) > 0 { v = args[0] } - code := 5 - if v == nil { - code = 0 - } - return &exitCodeError{v, code, false} + return &exitCodeError{v, 5} } -func funcHalt(interface{}) interface{} { - return &exitCodeError{nil, 0, true} +func funcHalt(any) any { + return &haltError{nil, 0} } -func funcHaltError(v interface{}, args []interface{}) interface{} { +func funcHaltError(v any, args []any) any { code := 5 if len(args) > 0 { var ok bool if code, ok = toInt(args[0]); !ok { - return &funcTypeError{"halt_error", args[0]} + return &func0TypeError{"halt_error", args[0]} } } - return &exitCodeError{v, code, true} -} - -func internalfuncTypeError(v, x interface{}) interface{} { - if x, ok := x.(string); ok { - return &funcTypeError{x, v} - } - return &funcTypeError{"_type_error", v} + return &haltError{v, code} } -func toInt(x interface{}) (int, bool) { +func toInt(x any) (int, bool) { switch x := x.(type) { case int: return x, true @@ -1769,30 +2102,30 @@ func toInt(x interface{}) (int, bool) { return floatToInt(x), true case *big.Int: if x.IsInt64() { - if i := x.Int64(); minInt <= i && i <= maxInt { + if i := x.Int64(); math.MinInt <= i && i <= math.MaxInt { return int(i), true } } if x.Sign() > 0 { - return maxInt, true + return math.MaxInt, true } - return minInt, true + return math.MinInt, true default: return 0, false } } func floatToInt(x float64) int { - if minInt <= x && x <= maxInt { + if math.MinInt <= x && x <= math.MaxInt { return int(x) } if x > 0 { - return maxInt + return math.MaxInt } - return minInt + return math.MinInt } -func toFloat(x interface{}) (float64, bool) { +func toFloat(x any) (float64, bool) { switch x := x.(type) { case int: return float64(x), true diff --git a/vendor/github.com/itchyny/gojq/go.dev.mod b/vendor/github.com/itchyny/gojq/go.dev.mod index 46e652e2..d60fa625 100644 --- a/vendor/github.com/itchyny/gojq/go.dev.mod +++ b/vendor/github.com/itchyny/gojq/go.dev.mod @@ -1,8 +1,8 @@ module github.com/itchyny/gojq -go 1.16 +go 1.19 require ( - github.com/itchyny/astgen-go v0.0.0-20210914105503-cc8fccf6f972 // indirect - github.com/itchyny/timefmt-go v0.1.3 // indirect + github.com/itchyny/astgen-go v0.0.0-20231113225122-e1c22b9aaf7b // indirect + github.com/itchyny/timefmt-go v0.1.5 // indirect ) diff --git a/vendor/github.com/itchyny/gojq/go.dev.sum b/vendor/github.com/itchyny/gojq/go.dev.sum index 467aa0b9..5c52eab2 100644 --- a/vendor/github.com/itchyny/gojq/go.dev.sum +++ b/vendor/github.com/itchyny/gojq/go.dev.sum @@ -1,4 +1,4 @@ -github.com/itchyny/astgen-go v0.0.0-20210914105503-cc8fccf6f972 h1:XYWolmPDLTY9B1O5o/Ad811/mtVkaHWMiZdbPLm/nDA= -github.com/itchyny/astgen-go v0.0.0-20210914105503-cc8fccf6f972/go.mod h1:jTXcxGeQMJfFN3wWjtzb4aAaWDDN+QbezE0HjH1XfNk= -github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= -github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= +github.com/itchyny/astgen-go v0.0.0-20231113225122-e1c22b9aaf7b h1:72fDU7wad+r3iQObaxhlXVIpAIMRUIUMrNa3go1vb8s= +github.com/itchyny/astgen-go v0.0.0-20231113225122-e1c22b9aaf7b/go.mod h1:Zp6xzEWVc2pQ/ObfLD6t/M6gDegsJWKdGKJSiT7qlu0= +github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= +github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= diff --git a/vendor/github.com/itchyny/gojq/gojq.go b/vendor/github.com/itchyny/gojq/gojq.go index 8f53b356..e078c809 100644 --- a/vendor/github.com/itchyny/gojq/gojq.go +++ b/vendor/github.com/itchyny/gojq/gojq.go @@ -1,5 +1,5 @@ -// Package gojq provides the parser and interpreter of gojq. +// Package gojq provides the parser and the interpreter of gojq. +// Please refer to [Usage as a library] for introduction. // -// Please refer to https://github.com/itchyny/gojq#usage-as-a-library for -// introduction of the usage as a library. +// [Usage as a library]: https://github.com/itchyny/gojq#usage-as-a-library package gojq diff --git a/vendor/github.com/itchyny/gojq/iter.go b/vendor/github.com/itchyny/gojq/iter.go index 0cee25ba..d0bed960 100644 --- a/vendor/github.com/itchyny/gojq/iter.go +++ b/vendor/github.com/itchyny/gojq/iter.go @@ -2,11 +2,11 @@ package gojq // Iter is an interface for an iterator. type Iter interface { - Next() (interface{}, bool) + Next() (any, bool) } -// NewIter creates a new Iter from values. -func NewIter(values ...interface{}) Iter { +// NewIter creates a new [Iter] from values. +func NewIter(values ...any) Iter { switch len(values) { case 0: return emptyIter{} @@ -20,16 +20,16 @@ func NewIter(values ...interface{}) Iter { type emptyIter struct{} -func (emptyIter) Next() (interface{}, bool) { +func (emptyIter) Next() (any, bool) { return nil, false } type unitIter struct { - value interface{} + value any done bool } -func (iter *unitIter) Next() (interface{}, bool) { +func (iter *unitIter) Next() (any, bool) { if iter.done { return nil, false } @@ -37,9 +37,9 @@ func (iter *unitIter) Next() (interface{}, bool) { return iter.value, true } -type sliceIter []interface{} +type sliceIter []any -func (iter *sliceIter) Next() (interface{}, bool) { +func (iter *sliceIter) Next() (any, bool) { if len(*iter) == 0 { return nil, false } diff --git a/vendor/github.com/itchyny/gojq/lexer.go b/vendor/github.com/itchyny/gojq/lexer.go index d36a6838..35cc8305 100644 --- a/vendor/github.com/itchyny/gojq/lexer.go +++ b/vendor/github.com/itchyny/gojq/lexer.go @@ -1,8 +1,7 @@ package gojq import ( - "strconv" - "strings" + "encoding/json" "unicode/utf8" ) @@ -235,9 +234,9 @@ func (l *lexer) Lex(lval *yySymType) (tokenType int) { return tok default: if ch >= utf8.RuneSelf { - r, _ := utf8.DecodeRuneInString(l.source[l.offset-1:]) + r, size := utf8.DecodeRuneInString(l.source[l.offset-1:]) + l.offset += size l.token = string(r) - l.offset += len(l.token) } } return int(ch) @@ -248,15 +247,9 @@ func (l *lexer) next() (byte, bool) { ch := l.source[l.offset] l.offset++ if ch == '#' { - if len(l.source) == l.offset { + if l.skipComment() { return 0, true } - for !isNewLine(l.source[l.offset]) { - l.offset++ - if len(l.source) == l.offset { - return 0, true - } - } } else if !isWhite(ch) { return ch, false } else if len(l.source) == l.offset { @@ -265,6 +258,28 @@ func (l *lexer) next() (byte, bool) { } } +func (l *lexer) skipComment() bool { + for { + switch l.peek() { + case 0: + return true + case '\\': + switch l.offset++; l.peek() { + case '\\', '\n': + l.offset++ + case '\r': + if l.offset++; l.peek() == '\n' { + l.offset++ + } + } + case '\n', '\r': + return false + default: + l.offset++ + } + } +} + func (l *lexer) peek() byte { if len(l.source) == l.offset { return 0 @@ -381,82 +396,129 @@ func (l *lexer) validNumber() bool { } func (l *lexer) scanString(start int) (int, string) { - var quote, newline bool + var decode bool + var controls int unquote := func(src string, quote bool) (string, error) { - if quote { - src = "\"" + src + "\"" + if !decode { + if quote { + return src, nil + } + return src[1 : len(src)-1], nil + } + var buf []byte + if !quote && controls == 0 { + buf = []byte(src) + } else { + buf = quoteAndEscape(src, quote, controls) } - if newline { - src = strings.ReplaceAll(src, "\n", "\\n") + if err := json.Unmarshal(buf, &src); err != nil { + return "", err } - return strconv.Unquote(src) + return src, nil } - for i, m := l.offset, len(l.source); i < m; i++ { + for i := l.offset; i < len(l.source); i++ { ch := l.source[i] switch ch { case '\\': - quote = !quote - case '\n': - newline = true - case '"': - if !quote { - if !l.inString { - l.offset = i + 1 - l.token = l.source[start:l.offset] - str, err := unquote(l.token, false) - if err != nil { - return tokInvalid, "" + if i++; i >= len(l.source) { + break + } + switch l.source[i] { + case 'u': + for j := 1; j <= 4; j++ { + if i+j >= len(l.source) || !isHex(l.source[i+j]) { + l.offset = i + j + l.token = l.source[i-1 : l.offset] + return tokInvalidEscapeSequence, "" } - return tokString, str } - if i > l.offset { - l.offset = i - l.token = l.source[start:l.offset] - str, err := unquote(l.token, true) - if err != nil { - return tokInvalid, "" - } - return tokString, str + i += 4 + fallthrough + case '"', '/', '\\', 'b', 'f', 'n', 'r', 't': + decode = true + case '(': + if !l.inString { + l.inString = true + return tokStringStart, "" } - l.inString = false - l.offset = i + 1 - return tokStringEnd, "" - } - quote = false - case '(': - if quote { - if l.inString { - if i > l.offset+1 { - l.offset = i - 1 - l.token = l.source[start:l.offset] - str, err := unquote(l.token, true) - if err != nil { - return tokInvalid, "" - } - return tokString, str - } - l.offset = i + 1 + if i == l.offset+1 { + l.offset += 2 l.inString = false return tokStringQuery, "" } - l.inString = true - return tokStringStart, "" + l.offset = i - 1 + l.token = l.source[start:l.offset] + str, err := unquote(l.token, true) + if err != nil { + return tokInvalid, "" + } + return tokString, str + default: + l.offset = i + 1 + l.token = l.source[l.offset-2 : l.offset] + return tokInvalidEscapeSequence, "" } - default: - if quote { - if !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || - '0' <= ch && ch <= '9' || ch == '\'' || ch == '"') { - l.offset = i + 1 - l.token = l.source[l.offset-2 : l.offset] + case '"': + if !l.inString { + l.offset = i + 1 + l.token = l.source[start:l.offset] + str, err := unquote(l.token, false) + if err != nil { return tokInvalid, "" } - quote = false + return tokString, str + } + if i > l.offset { + l.offset = i + l.token = l.source[start:l.offset] + str, err := unquote(l.token, true) + if err != nil { + return tokInvalid, "" + } + return tokString, str + } + l.inString = false + l.offset = i + 1 + return tokStringEnd, "" + default: + if !decode { + decode = ch > '~' + } + if ch < ' ' { // ref: unquoteBytes in encoding/json + controls++ } } } l.offset = len(l.source) - l.token = l.source[start:l.offset] - return tokInvalid, "" + l.token = "" + return tokUnterminatedString, "" +} + +func quoteAndEscape(src string, quote bool, controls int) []byte { + size := len(src) + controls*5 + if quote { + size += 2 + } + buf := make([]byte, size) + var j int + if quote { + buf[0] = '"' + buf[len(buf)-1] = '"' + j++ + } + for i := 0; i < len(src); i++ { + if ch := src[i]; ch < ' ' { + const hex = "0123456789abcdef" + copy(buf[j:], `\u00`) + buf[j+4] = hex[ch>>4] + buf[j+5] = hex[ch&0xF] + j += 6 + } else { + buf[j] = ch + j++ + } + } + return buf } type parseError struct { @@ -466,24 +528,18 @@ type parseError struct { } func (err *parseError) Error() string { - var message string - prefix := "unexpected" - switch { - case err.tokenType == eof: - message = "" - case err.tokenType == tokInvalid: - prefix = "invalid" - fallthrough - case err.tokenType >= utf8.RuneSelf: - if strings.HasPrefix(err.token, "\"") { - message = err.token - } else { - message = "\"" + err.token + "\"" - } + switch err.tokenType { + case eof: + return "unexpected EOF" + case tokInvalid: + return "invalid token " + jsonMarshal(err.token) + case tokInvalidEscapeSequence: + return `invalid escape sequence "` + err.token + `" in string literal` + case tokUnterminatedString: + return "unterminated string literal" default: - message = strconv.Quote(string(rune(err.tokenType))) + return "unexpected token " + jsonMarshal(err.token) } - return prefix + " token " + message } func (err *parseError) Token() (string, int) { @@ -492,12 +548,7 @@ func (err *parseError) Token() (string, int) { func (l *lexer) Error(string) { offset, token := l.offset, l.token - switch { - case l.tokenType == eof: - offset++ - case l.tokenType >= utf8.RuneSelf: - offset -= len(token) - 1 - default: + if l.tokenType != eof && l.tokenType < utf8.RuneSelf { token = string(rune(l.tokenType)) } l.err = &parseError{offset, token, l.tokenType} @@ -518,15 +569,12 @@ func isIdent(ch byte, tail bool) bool { tail && isNumber(ch) } -func isNumber(ch byte) bool { - return '0' <= ch && ch <= '9' +func isHex(ch byte) bool { + return 'a' <= ch && ch <= 'f' || + 'A' <= ch && ch <= 'F' || + isNumber(ch) } -func isNewLine(ch byte) bool { - switch ch { - case '\n', '\r': - return true - default: - return false - } +func isNumber(ch byte) bool { + return '0' <= ch && ch <= '9' } diff --git a/vendor/github.com/itchyny/gojq/math.go b/vendor/github.com/itchyny/gojq/math.go deleted file mode 100644 index 55d64765..00000000 --- a/vendor/github.com/itchyny/gojq/math.go +++ /dev/null @@ -1,10 +0,0 @@ -package gojq - -import "math/bits" - -const ( - maxInt = 1<<(bits.UintSize-1) - 1 // math.MaxInt64 or math.MaxInt32 - minInt = -maxInt - 1 // math.MinInt64 or math.MinInt32 - maxHalfInt = 1<<(bits.UintSize/2-1) - 1 // math.MaxInt32 or math.MaxInt16 - minHalfInt = -maxHalfInt - 1 // math.MinInt32 or math.MinInt16 -) diff --git a/vendor/github.com/itchyny/gojq/module_loader.go b/vendor/github.com/itchyny/gojq/module_loader.go index b6cebca9..0a73ba05 100644 --- a/vendor/github.com/itchyny/gojq/module_loader.go +++ b/vendor/github.com/itchyny/gojq/module_loader.go @@ -9,13 +9,32 @@ import ( "strings" ) -type moduleLoader struct { - paths []string -} +// ModuleLoader is the interface for loading modules. +// +// Implement following optional methods. Use [NewModuleLoader] to load local modules. +// +// LoadInitModules() ([]*Query, error) +// LoadModule(string) (*Query, error) +// LoadModuleWithMeta(string, map[string]any) (*Query, error) +// LoadJSON(string) (any, error) +// LoadJSONWithMeta(string, map[string]any) (any, error) +type ModuleLoader any -// NewModuleLoader creates a new ModuleLoader reading local modules in the paths. +// NewModuleLoader creates a new [ModuleLoader] loading local modules in the paths. +// Note that user can load modules outside the paths using "search" path of metadata. +// Empty paths are ignored, so specify "." for the current working directory. func NewModuleLoader(paths []string) ModuleLoader { - return &moduleLoader{expandHomeDir(paths)} + ps := make([]string, 0, len(paths)) + for _, path := range paths { + if path = resolvePath(path, ""); path != "" { + ps = append(ps, path) + } + } + return &moduleLoader{ps} +} + +type moduleLoader struct { + paths []string } func (l *moduleLoader) LoadInitModules() ([]*Query, error) { @@ -38,16 +57,16 @@ func (l *moduleLoader) LoadInitModules() ([]*Query, error) { if err != nil { return nil, err } - q, err := parseModule(path, string(cnt)) + q, err := parseModule(string(cnt), filepath.Dir(path)) if err != nil { - return nil, &queryParseError{"query in module", path, string(cnt), err} + return nil, &queryParseError{path, string(cnt), err} } qs = append(qs, q) } return qs, nil } -func (l *moduleLoader) LoadModuleWithMeta(name string, meta map[string]interface{}) (*Query, error) { +func (l *moduleLoader) LoadModuleWithMeta(name string, meta map[string]any) (*Query, error) { path, err := l.lookupModule(name, ".jq", meta) if err != nil { return nil, err @@ -56,14 +75,14 @@ func (l *moduleLoader) LoadModuleWithMeta(name string, meta map[string]interface if err != nil { return nil, err } - q, err := parseModule(path, string(cnt)) + q, err := parseModule(string(cnt), filepath.Dir(path)) if err != nil { - return nil, &queryParseError{"query in module", path, string(cnt), err} + return nil, &queryParseError{path, string(cnt), err} } return q, nil } -func (l *moduleLoader) LoadJSONWithMeta(name string, meta map[string]interface{}) (interface{}, error) { +func (l *moduleLoader) LoadJSONWithMeta(name string, meta map[string]any) (any, error) { path, err := l.lookupModule(name, ".json", meta) if err != nil { return nil, err @@ -73,11 +92,11 @@ func (l *moduleLoader) LoadJSONWithMeta(name string, meta map[string]interface{} return nil, err } defer f.Close() - var vals []interface{} + vals := []any{} dec := json.NewDecoder(f) dec.UseNumber() for { - var val interface{} + var val any if err := dec.Decode(&val); err != nil { if err == io.EOF { break @@ -96,17 +115,17 @@ func (l *moduleLoader) LoadJSONWithMeta(name string, meta map[string]interface{} return vals, nil } -func (l *moduleLoader) lookupModule(name, extension string, meta map[string]interface{}) (string, error) { +func (l *moduleLoader) lookupModule(name, extension string, meta map[string]any) (string, error) { paths := l.paths - if path := searchPath(meta); path != "" { + if path, ok := meta["search"].(string); ok { paths = append([]string{path}, paths...) } for _, base := range paths { - path := filepath.Clean(filepath.Join(base, name+extension)) + path := filepath.Join(base, name+extension) if _, err := os.Stat(path); err == nil { return path, err } - path = filepath.Clean(filepath.Join(base, name, filepath.Base(name)+extension)) + path = filepath.Join(base, name, filepath.Base(name)+extension) if _, err := os.Stat(path); err == nil { return path, err } @@ -114,66 +133,50 @@ func (l *moduleLoader) lookupModule(name, extension string, meta map[string]inte return "", fmt.Errorf("module not found: %q", name) } -// This is a dirty hack to implement the "search" field. -func parseModule(path, cnt string) (*Query, error) { +func parseModule(cnt, dir string) (*Query, error) { q, err := Parse(cnt) if err != nil { return nil, err } for _, i := range q.Imports { - if i.Meta == nil { - continue + if i.Meta != nil { + for _, e := range i.Meta.KeyVals { + if e.Key == "search" || e.KeyString == "search" { + if path, ok := e.Val.toString(); ok { + if path = resolvePath(path, dir); path != "" { + e.Val.Str = path + } else { + e.Val.Null = true + } + } + } + } } - i.Meta.KeyVals = append( - i.Meta.KeyVals, - &ConstObjectKeyVal{ - Key: "$$path", - Val: &ConstTerm{Str: path}, - }, - ) } return q, nil } -func searchPath(meta map[string]interface{}) string { - x, ok := meta["search"] - if !ok { - return "" - } - s, ok := x.(string) - if !ok { - return "" - } - if filepath.IsAbs(s) { - return s - } - if strings.HasPrefix(s, "~") { - if homeDir, err := os.UserHomeDir(); err == nil { - return filepath.Join(homeDir, s[1:]) +func resolvePath(path, dir string) string { + switch { + case filepath.IsAbs(path): + return path + case strings.HasPrefix(path, "~/"): + dir, err := os.UserHomeDir() + if err != nil { + return "" } - } - var path string - if x, ok := meta["$$path"]; ok { - path, _ = x.(string) - } - if path == "" { - return s - } - return filepath.Join(filepath.Dir(path), s) -} - -func expandHomeDir(paths []string) []string { - var homeDir string - var err error - for i, path := range paths { - if strings.HasPrefix(path, "~") { - if homeDir == "" && err == nil { - homeDir, err = os.UserHomeDir() - } - if homeDir != "" { - paths[i] = filepath.Join(homeDir, path[1:]) - } + return filepath.Join(dir, path[2:]) + case strings.HasPrefix(path, "$ORIGIN/"): + exe, err := os.Executable() + if err != nil { + return "" + } + exe, err = filepath.EvalSymlinks(exe) + if err != nil { + return "" } + return filepath.Join(filepath.Dir(exe), path[8:]) + default: + return filepath.Join(dir, path) } - return paths } diff --git a/vendor/github.com/itchyny/gojq/normalize.go b/vendor/github.com/itchyny/gojq/normalize.go index afca122b..2bfcd215 100644 --- a/vendor/github.com/itchyny/gojq/normalize.go +++ b/vendor/github.com/itchyny/gojq/normalize.go @@ -7,8 +7,8 @@ import ( "strings" ) -func normalizeNumber(v json.Number) interface{} { - if i, err := v.Int64(); err == nil && minInt <= i && i <= maxInt { +func normalizeNumber(v json.Number) any { + if i, err := v.Int64(); err == nil && math.MinInt <= i && i <= math.MaxInt { return int(i) } if strings.ContainsAny(v.String(), ".eE") { @@ -25,22 +25,22 @@ func normalizeNumber(v json.Number) interface{} { return math.Inf(1) } -func normalizeNumbers(v interface{}) interface{} { +func normalizeNumbers(v any) any { switch v := v.(type) { case json.Number: return normalizeNumber(v) case *big.Int: if v.IsInt64() { - if i := v.Int64(); minInt <= i && i <= maxInt { + if i := v.Int64(); math.MinInt <= i && i <= math.MaxInt { return int(i) } } return v case int64: - if v > maxInt || v < minInt { - return new(big.Int).SetInt64(v) + if math.MinInt <= v && v <= math.MaxInt { + return int(v) } - return int(v) + return big.NewInt(v) case int32: return int(v) case int16: @@ -48,68 +48,36 @@ func normalizeNumbers(v interface{}) interface{} { case int8: return int(v) case uint: - if v > maxInt { - return new(big.Int).SetUint64(uint64(v)) + if v <= math.MaxInt { + return int(v) } - return int(v) + return new(big.Int).SetUint64(uint64(v)) case uint64: - if v > maxInt { - return new(big.Int).SetUint64(v) + if v <= math.MaxInt { + return int(v) } - return int(v) + return new(big.Int).SetUint64(v) case uint32: - if uint64(v) > maxInt { - return new(big.Int).SetUint64(uint64(v)) + if uint64(v) <= math.MaxInt { + return int(v) } - return int(v) + return new(big.Int).SetUint64(uint64(v)) case uint16: return int(v) case uint8: return int(v) case float32: return float64(v) - case map[string]interface{}: - for k, x := range v { - v[k] = normalizeNumbers(x) - } - return v - case []interface{}: + case []any: for i, x := range v { v[i] = normalizeNumbers(x) } return v - default: - return v - } -} - -// It's ok to delete destructively because this function is used right after -// updatePaths, where it shallow-copies maps or slices on updates. -func deleteEmpty(v interface{}) interface{} { - switch v := v.(type) { - case struct{}: - return nil - case map[string]interface{}: - for k, w := range v { - if w == struct{}{} { - delete(v, k) - } else { - v[k] = deleteEmpty(w) - } + case map[string]any: + for k, x := range v { + v[k] = normalizeNumbers(x) } return v - case []interface{}: - var j int - for _, w := range v { - if w != struct{}{} { - v[j] = deleteEmpty(w) - j++ - } - } - for i := j; i < len(v); i++ { - v[i] = nil - } - return v[:j] default: return v } diff --git a/vendor/github.com/itchyny/gojq/operator.go b/vendor/github.com/itchyny/gojq/operator.go index 80e13ef9..513dbfad 100644 --- a/vendor/github.com/itchyny/gojq/operator.go +++ b/vendor/github.com/itchyny/gojq/operator.go @@ -37,7 +37,7 @@ const ( OpUpdateAlt ) -// String implements Stringer. +// String implements [fmt.Stringer]. func (op Operator) String() string { switch op { case OpPipe: @@ -93,7 +93,7 @@ func (op Operator) String() string { } } -// GoString implements GoStringer. +// GoString implements [fmt.GoStringer]. func (op Operator) GoString() (str string) { defer func() { str = "gojq." + str }() switch op { @@ -208,23 +208,19 @@ func (op Operator) getFunc() string { } func binopTypeSwitch( - l, r interface{}, - callbackInts func(_, _ int) interface{}, - callbackFloats func(_, _ float64) interface{}, - callbackBigInts func(_, _ *big.Int) interface{}, - callbackStrings func(_, _ string) interface{}, - callbackArrays func(_, _ []interface{}) interface{}, - callbackMaps func(_, _ map[string]interface{}) interface{}, - fallback func(_, _ interface{}) interface{}) interface{} { + l, r any, + callbackInts func(_, _ int) any, + callbackFloats func(_, _ float64) any, + callbackBigInts func(_, _ *big.Int) any, + callbackStrings func(_, _ string) any, + callbackArrays func(_, _ []any) any, + callbackMaps func(_, _ map[string]any) any, + fallback func(_, _ any) any) any { switch l := l.(type) { case int: switch r := r.(type) { case int: - if minHalfInt <= l && l <= maxHalfInt && - minHalfInt <= r && r <= maxHalfInt { - return callbackInts(l, r) - } - return callbackBigInts(big.NewInt(int64(l)), big.NewInt(int64(r))) + return callbackInts(l, r) case float64: return callbackFloats(float64(l), r) case *big.Int: @@ -261,16 +257,16 @@ func binopTypeSwitch( default: return fallback(l, r) } - case []interface{}: + case []any: switch r := r.(type) { - case []interface{}: + case []any: return callbackArrays(l, r) default: return fallback(l, r) } - case map[string]interface{}: + case map[string]any: switch r := r.(type) { - case map[string]interface{}: + case map[string]any: return callbackMaps(l, r) default: return fallback(l, r) @@ -280,7 +276,7 @@ func binopTypeSwitch( } } -func funcOpPlus(v interface{}) interface{} { +func funcOpPlus(v any) any { switch v := v.(type) { case int: return v @@ -293,7 +289,7 @@ func funcOpPlus(v interface{}) interface{} { } } -func funcOpNegate(v interface{}) interface{} { +func funcOpNegate(v any) any { switch v := v.(type) { case int: return -v @@ -306,28 +302,38 @@ func funcOpNegate(v interface{}) interface{} { } } -func funcOpAdd(_, l, r interface{}) interface{} { - if l == nil { - return r - } else if r == nil { - return l - } +func funcOpAdd(_, l, r any) any { return binopTypeSwitch(l, r, - func(l, r int) interface{} { return l + r }, - func(l, r float64) interface{} { return l + r }, - func(l, r *big.Int) interface{} { return new(big.Int).Add(l, r) }, - func(l, r string) interface{} { return l + r }, - func(l, r []interface{}) interface{} { + func(l, r int) any { + if v := l + r; (v >= l) == (r >= 0) { + return v + } + x, y := big.NewInt(int64(l)), big.NewInt(int64(r)) + return x.Add(x, y) + }, + func(l, r float64) any { return l + r }, + func(l, r *big.Int) any { return new(big.Int).Add(l, r) }, + func(l, r string) any { return l + r }, + func(l, r []any) any { + if len(l) == 0 { + return r + } if len(r) == 0 { return l - } else if len(l) == 0 { - return r } - v := make([]interface{}, 0, len(l)+len(r)) - return append(append(v, l...), r...) + v := make([]any, len(l)+len(r)) + copy(v, l) + copy(v[len(l):], r) + return v }, - func(l, r map[string]interface{}) interface{} { - m := make(map[string]interface{}, len(l)+len(r)) + func(l, r map[string]any) any { + if len(l) == 0 { + return r + } + if len(r) == 0 { + return l + } + m := make(map[string]any, len(l)+len(r)) for k, v := range l { m[k] = v } @@ -336,63 +342,71 @@ func funcOpAdd(_, l, r interface{}) interface{} { } return m }, - func(l, r interface{}) interface{} { return &binopTypeError{"add", l, r} }, + func(l, r any) any { + if l == nil { + return r + } + if r == nil { + return l + } + return &binopTypeError{"add", l, r} + }, ) } -func funcOpSub(_, l, r interface{}) interface{} { +func funcOpSub(_, l, r any) any { return binopTypeSwitch(l, r, - func(l, r int) interface{} { return l - r }, - func(l, r float64) interface{} { return l - r }, - func(l, r *big.Int) interface{} { return new(big.Int).Sub(l, r) }, - func(l, r string) interface{} { return &binopTypeError{"subtract", l, r} }, - func(l, r []interface{}) interface{} { - a := make([]interface{}, 0, len(l)) - for _, v := range l { - var found bool - for _, w := range r { - if compare(v, w) == 0 { - found = true - break + func(l, r int) any { + if v := l - r; (v <= l) == (r >= 0) { + return v + } + x, y := big.NewInt(int64(l)), big.NewInt(int64(r)) + return x.Sub(x, y) + }, + func(l, r float64) any { return l - r }, + func(l, r *big.Int) any { return new(big.Int).Sub(l, r) }, + func(l, r string) any { return &binopTypeError{"subtract", l, r} }, + func(l, r []any) any { + v := make([]any, 0, len(l)) + L: + for _, l := range l { + for _, r := range r { + if compare(l, r) == 0 { + continue L } } - if !found { - a = append(a, v) - } + v = append(v, l) } - return a + return v }, - func(l, r map[string]interface{}) interface{} { return &binopTypeError{"subtract", l, r} }, - func(l, r interface{}) interface{} { return &binopTypeError{"subtract", l, r} }, + func(l, r map[string]any) any { return &binopTypeError{"subtract", l, r} }, + func(l, r any) any { return &binopTypeError{"subtract", l, r} }, ) } -func funcOpMul(_, l, r interface{}) interface{} { +func funcOpMul(_, l, r any) any { return binopTypeSwitch(l, r, - func(l, r int) interface{} { return l * r }, - func(l, r float64) interface{} { return l * r }, - func(l, r *big.Int) interface{} { return new(big.Int).Mul(l, r) }, - func(l, r string) interface{} { return &binopTypeError{"multiply", l, r} }, - func(l, r []interface{}) interface{} { return &binopTypeError{"multiply", l, r} }, - deepMergeObjects, - func(l, r interface{}) interface{} { - multiplyString := func(s string, cnt float64) interface{} { - if cnt <= 0.0 || cnt > float64(maxHalfInt/(16*(len(s)+1))) || math.IsNaN(cnt) { - return nil - } - if cnt < 1.0 { - return s - } - return strings.Repeat(s, int(cnt)) + func(l, r int) any { + if v := l * r; r == 0 || v/r == l { + return v } + x, y := big.NewInt(int64(l)), big.NewInt(int64(r)) + return x.Mul(x, y) + }, + func(l, r float64) any { return l * r }, + func(l, r *big.Int) any { return new(big.Int).Mul(l, r) }, + func(l, r string) any { return &binopTypeError{"multiply", l, r} }, + func(l, r []any) any { return &binopTypeError{"multiply", l, r} }, + deepMergeObjects, + func(l, r any) any { if l, ok := l.(string); ok { - if f, ok := toFloat(r); ok { - return multiplyString(l, f) + if r, ok := toFloat(r); ok { + return repeatString(l, r) } } if r, ok := r.(string); ok { - if f, ok := toFloat(l); ok { - return multiplyString(r, f) + if l, ok := toFloat(l); ok { + return repeatString(r, l) } } return &binopTypeError{"multiply", l, r} @@ -400,15 +414,15 @@ func funcOpMul(_, l, r interface{}) interface{} { ) } -func deepMergeObjects(l, r map[string]interface{}) interface{} { - m := make(map[string]interface{}, len(l)+len(r)) +func deepMergeObjects(l, r map[string]any) any { + m := make(map[string]any, len(l)+len(r)) for k, v := range l { m[k] = v } for k, v := range r { if mk, ok := m[k]; ok { - if mk, ok := mk.(map[string]interface{}); ok { - if w, ok := v.(map[string]interface{}); ok { + if mk, ok := mk.(map[string]any); ok { + if w, ok := v.(map[string]any); ok { v = deepMergeObjects(mk, w) } } @@ -418,13 +432,20 @@ func deepMergeObjects(l, r map[string]interface{}) interface{} { return m } -func funcOpDiv(_, l, r interface{}) interface{} { +func repeatString(s string, n float64) any { + if n < 0.0 || len(s) > 0 && n > float64(0x10000000/len(s)) || math.IsNaN(n) { + return nil + } + if s == "" { + return "" + } + return strings.Repeat(s, int(n)) +} + +func funcOpDiv(_, l, r any) any { return binopTypeSwitch(l, r, - func(l, r int) interface{} { + func(l, r int) any { if r == 0 { - if l == 0 { - return math.NaN() - } return &zeroDivisionError{l, r} } if l%r == 0 { @@ -432,20 +453,14 @@ func funcOpDiv(_, l, r interface{}) interface{} { } return float64(l) / float64(r) }, - func(l, r float64) interface{} { + func(l, r float64) any { if r == 0.0 { - if l == 0.0 { - return math.NaN() - } return &zeroDivisionError{l, r} } return l / r }, - func(l, r *big.Int) interface{} { + func(l, r *big.Int) any { if r.Sign() == 0 { - if l.Sign() == 0 { - return math.NaN() - } return &zeroDivisionError{l, r} } d, m := new(big.Int).DivMod(l, r, new(big.Int)) @@ -454,78 +469,81 @@ func funcOpDiv(_, l, r interface{}) interface{} { } return bigToFloat(l) / bigToFloat(r) }, - func(l, r string) interface{} { + func(l, r string) any { if l == "" { - return []interface{}{} + return []any{} } xs := strings.Split(l, r) - vs := make([]interface{}, len(xs)) + vs := make([]any, len(xs)) for i, x := range xs { vs[i] = x } return vs }, - func(l, r []interface{}) interface{} { return &binopTypeError{"divide", l, r} }, - func(l, r map[string]interface{}) interface{} { return &binopTypeError{"divide", l, r} }, - func(l, r interface{}) interface{} { return &binopTypeError{"divide", l, r} }, + func(l, r []any) any { return &binopTypeError{"divide", l, r} }, + func(l, r map[string]any) any { return &binopTypeError{"divide", l, r} }, + func(l, r any) any { return &binopTypeError{"divide", l, r} }, ) } -func funcOpMod(_, l, r interface{}) interface{} { +func funcOpMod(_, l, r any) any { return binopTypeSwitch(l, r, - func(l, r int) interface{} { + func(l, r int) any { if r == 0 { return &zeroModuloError{l, r} } return l % r }, - func(l, r float64) interface{} { + func(l, r float64) any { ri := floatToInt(r) if ri == 0 { return &zeroModuloError{l, r} } + if math.IsNaN(l) || math.IsNaN(r) { + return math.NaN() + } return floatToInt(l) % ri }, - func(l, r *big.Int) interface{} { + func(l, r *big.Int) any { if r.Sign() == 0 { return &zeroModuloError{l, r} } return new(big.Int).Rem(l, r) }, - func(l, r string) interface{} { return &binopTypeError{"modulo", l, r} }, - func(l, r []interface{}) interface{} { return &binopTypeError{"modulo", l, r} }, - func(l, r map[string]interface{}) interface{} { return &binopTypeError{"modulo", l, r} }, - func(l, r interface{}) interface{} { return &binopTypeError{"modulo", l, r} }, + func(l, r string) any { return &binopTypeError{"modulo", l, r} }, + func(l, r []any) any { return &binopTypeError{"modulo", l, r} }, + func(l, r map[string]any) any { return &binopTypeError{"modulo", l, r} }, + func(l, r any) any { return &binopTypeError{"modulo", l, r} }, ) } -func funcOpAlt(_, l, r interface{}) interface{} { +func funcOpAlt(_, l, r any) any { if l == nil || l == false { return r } return l } -func funcOpEq(_, l, r interface{}) interface{} { +func funcOpEq(_, l, r any) any { return compare(l, r) == 0 } -func funcOpNe(_, l, r interface{}) interface{} { +func funcOpNe(_, l, r any) any { return compare(l, r) != 0 } -func funcOpGt(_, l, r interface{}) interface{} { +func funcOpGt(_, l, r any) any { return compare(l, r) > 0 } -func funcOpLt(_, l, r interface{}) interface{} { +func funcOpLt(_, l, r any) any { return compare(l, r) < 0 } -func funcOpGe(_, l, r interface{}) interface{} { +func funcOpGe(_, l, r any) any { return compare(l, r) >= 0 } -func funcOpLe(_, l, r interface{}) interface{} { +func funcOpLe(_, l, r any) any { return compare(l, r) <= 0 } diff --git a/vendor/github.com/itchyny/gojq/option.go b/vendor/github.com/itchyny/gojq/option.go index 5eb771cc..f1a110fa 100644 --- a/vendor/github.com/itchyny/gojq/option.go +++ b/vendor/github.com/itchyny/gojq/option.go @@ -6,7 +6,7 @@ import "fmt" type CompilerOption func(*compiler) // WithModuleLoader is a compiler option for module loader. -// If you want to load modules from the filesystem, use NewModuleLoader. +// If you want to load modules from the filesystem, use [NewModuleLoader]. func WithModuleLoader(moduleLoader ModuleLoader) CompilerOption { return func(c *compiler) { c.moduleLoader = moduleLoader @@ -15,7 +15,7 @@ func WithModuleLoader(moduleLoader ModuleLoader) CompilerOption { // WithEnvironLoader is a compiler option for environment variables loader. // The OS environment variables are not accessible by default due to security -// reason. You can pass os.Environ if you allow to access it. +// reasons. You can specify [os.Environ] as argument if you allow to access. func WithEnvironLoader(environLoader func() []string) CompilerOption { return func(c *compiler) { c.environLoader = environLoader @@ -23,7 +23,7 @@ func WithEnvironLoader(environLoader func() []string) CompilerOption { } // WithVariables is a compiler option for variable names. The variables can be -// used in the query. You have to give the values to code.Run in the same order. +// used in the query. You have to give the values to [*Code.Run] in the same order. func WithVariables(variables []string) CompilerOption { return func(c *compiler) { c.variables = variables @@ -35,31 +35,28 @@ func WithVariables(variables []string) CompilerOption { // values should satisfy 0 <= minarity <= maxarity <= 30, otherwise panics. // On handling numbers, you should take account to int, float64 and *big.Int. // These are the number types you are allowed to return, so do not return int64. -// Refer to ValueError to return a value error just like built-in error function. -// If you want to emit multiple values, call the empty function, accept a filter -// for its argument, or call another built-in function, then use LoadInitModules -// of the module loader. -func WithFunction(name string, minarity, maxarity int, - f func(interface{}, []interface{}) interface{}) CompilerOption { +// Refer to [ValueError] to return a value error just like built-in error +// function. If you want to emit multiple values, call the empty function, +// accept a filter for its argument, or call another built-in function, then +// use LoadInitModules of the module loader. +func WithFunction(name string, minarity, maxarity int, f func(any, []any) any) CompilerOption { return withFunction(name, minarity, maxarity, false, f) } // WithIterFunction is a compiler option for adding a custom iterator function. -// This is like the WithFunction option, but you can add a function which +// This is like the [WithFunction] option, but you can add a function which // returns an Iter to emit multiple values. You cannot define both iterator and // non-iterator functions of the same name (with possibly different arities). -// See also NewIter, which can be used to convert values or an error to an Iter. -func WithIterFunction(name string, minarity, maxarity int, - f func(interface{}, []interface{}) Iter) CompilerOption { +// See also [NewIter], which can be used to convert values or an error to an Iter. +func WithIterFunction(name string, minarity, maxarity int, f func(any, []any) Iter) CompilerOption { return withFunction(name, minarity, maxarity, true, - func(v interface{}, args []interface{}) interface{} { + func(v any, args []any) any { return f(v, args) }, ) } -func withFunction(name string, minarity, maxarity int, iter bool, - f func(interface{}, []interface{}) interface{}) CompilerOption { +func withFunction(name string, minarity, maxarity int, iter bool, f func(any, []any) any) CompilerOption { if !(0 <= minarity && minarity <= maxarity && maxarity <= 30) { panic(fmt.Sprintf("invalid arity for %q: %d, %d", name, minarity, maxarity)) } @@ -74,7 +71,7 @@ func withFunction(name string, minarity, maxarity int, iter bool, } c.customFuncs[name] = function{ argcount | fn.argcount, iter, - func(x interface{}, xs []interface{}) interface{} { + func(x any, xs []any) any { if argcount&(1< 0 { @@ -30,10 +35,10 @@ func prependFuncDef(xs []*FuncDef, x *FuncDef) []*FuncDef { return xs } -//line parser.go.y:28 +//line parser.go.y:33 type yySymType struct { yys int - value interface{} + value any token string operator Operator } @@ -61,24 +66,26 @@ const tokModuleVariable = 57365 const tokIndex = 57366 const tokNumber = 57367 const tokFormat = 57368 -const tokInvalid = 57369 -const tokString = 57370 -const tokStringStart = 57371 -const tokStringQuery = 57372 -const tokStringEnd = 57373 -const tokIf = 57374 -const tokThen = 57375 -const tokElif = 57376 -const tokElse = 57377 -const tokEnd = 57378 -const tokTry = 57379 -const tokCatch = 57380 -const tokReduce = 57381 -const tokForeach = 57382 -const tokRecurse = 57383 -const tokFuncDefPost = 57384 -const tokTermPost = 57385 -const tokEmptyCatch = 57386 +const tokString = 57369 +const tokStringStart = 57370 +const tokStringQuery = 57371 +const tokStringEnd = 57372 +const tokIf = 57373 +const tokThen = 57374 +const tokElif = 57375 +const tokElse = 57376 +const tokEnd = 57377 +const tokTry = 57378 +const tokCatch = 57379 +const tokReduce = 57380 +const tokForeach = 57381 +const tokRecurse = 57382 +const tokFuncDefPost = 57383 +const tokTermPost = 57384 +const tokEmptyCatch = 57385 +const tokInvalid = 57386 +const tokInvalidEscapeSequence = 57387 +const tokUnterminatedString = 57388 var yyToknames = [...]string{ "$end", @@ -107,7 +114,6 @@ var yyToknames = [...]string{ "tokIndex", "tokNumber", "tokFormat", - "tokInvalid", "tokString", "tokStringStart", "tokStringQuery", @@ -125,6 +131,9 @@ var yyToknames = [...]string{ "tokFuncDefPost", "tokTermPost", "tokEmptyCatch", + "tokInvalid", + "tokInvalidEscapeSequence", + "tokUnterminatedString", "'|'", "','", "'+'", @@ -150,15 +159,15 @@ const yyEofCode = 1 const yyErrCode = 2 const yyInitialStackSize = 16 -//line parser.go.y:687 +//line parser.go.y:693 //line yacctab:1 -var yyExca = [...]int{ +var yyExca = [...]int16{ -1, 1, 1, -1, -2, 0, -1, 97, - 53, 0, + 55, 0, -2, 104, -1, 130, 5, 0, @@ -167,167 +176,170 @@ var yyExca = [...]int{ 9, 0, -2, 35, -1, 194, - 56, 114, + 58, 114, -2, 54, } const yyPrivate = 57344 -const yyLast = 1094 +const yyLast = 1127 -var yyAct = [...]int{ +var yyAct = [...]int16{ 86, 214, 174, 112, 12, 203, 9, 175, 111, 31, 190, 6, 156, 140, 117, 47, 95, 97, 93, 94, - 89, 227, 49, 75, 76, 7, 77, 78, 79, 240, - 235, 103, 239, 106, 164, 123, 226, 119, 107, 108, - 105, 234, 102, 75, 76, 113, 77, 78, 79, 163, - 122, 104, 211, 75, 76, 210, 77, 78, 79, 158, - 159, 264, 259, 243, 72, 74, 80, 81, 82, 83, - 84, 229, 73, 127, 275, 128, 129, 130, 131, 132, - 133, 134, 135, 136, 137, 138, 80, 81, 82, 83, - 84, 228, 73, 147, 72, 74, 80, 81, 82, 83, - 84, 145, 73, 141, 278, 161, 246, 277, 157, 225, - 166, 165, 144, 126, 125, 167, 88, 42, 43, 245, - 124, 258, 224, 206, 179, 180, 181, 44, 183, 184, - 73, 242, 177, 154, 153, 178, 142, 186, 49, 173, - 267, 100, 143, 92, 91, 90, 92, 191, 99, 197, - 150, 120, 200, 192, 201, 202, 188, 256, 257, 207, - 88, 182, 98, 198, 199, 209, 219, 7, 216, 101, - 215, 215, 218, 213, 113, 155, 185, 75, 76, 3, - 77, 78, 79, 42, 43, 221, 222, 28, 91, 90, - 92, 179, 180, 181, 230, 204, 205, 232, 8, 177, - 223, 27, 178, 80, 81, 82, 83, 84, 220, 73, - 85, 157, 241, 176, 46, 149, 237, 110, 72, 74, - 80, 81, 82, 83, 84, 88, 73, 152, 182, 196, - 79, 191, 195, 255, 7, 253, 254, 192, 248, 247, - 236, 160, 249, 250, 96, 262, 260, 261, 215, 263, - 11, 121, 189, 91, 90, 92, 11, 268, 269, 187, - 270, 82, 83, 84, 139, 73, 272, 273, 80, 81, - 82, 83, 84, 208, 73, 279, 10, 5, 271, 280, - 51, 52, 4, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 115, 116, 170, 2, 171, 169, 1, - 0, 42, 43, 0, 0, 63, 64, 65, 66, 67, - 68, 69, 70, 71, 0, 0, 20, 0, 17, 37, - 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, - 114, 42, 43, 0, 212, 15, 0, 0, 0, 0, - 16, 0, 13, 14, 22, 0, 0, 0, 0, 0, - 33, 34, 0, 0, 0, 21, 0, 36, 0, 148, - 32, 0, 146, 35, 51, 52, 0, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 115, 116, 0, + 89, 141, 49, 7, 179, 180, 181, 240, 246, 264, + 239, 103, 177, 106, 178, 227, 164, 119, 107, 108, + 105, 245, 102, 75, 76, 113, 77, 78, 79, 123, + 226, 163, 211, 225, 259, 210, 142, 179, 180, 181, + 158, 159, 143, 182, 122, 177, 224, 178, 219, 7, + 235, 234, 104, 127, 243, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 72, 74, 80, 81, + 82, 83, 84, 147, 73, 88, 182, 196, 73, 229, + 195, 145, 7, 150, 228, 161, 166, 165, 157, 126, + 125, 124, 144, 88, 258, 167, 80, 81, 82, 83, + 84, 206, 73, 44, 242, 91, 90, 92, 183, 184, + 82, 83, 84, 154, 73, 153, 267, 186, 49, 173, + 42, 43, 100, 91, 90, 92, 99, 191, 120, 197, + 256, 257, 200, 192, 201, 202, 188, 75, 76, 207, + 77, 78, 79, 198, 199, 209, 42, 43, 216, 92, + 215, 215, 218, 213, 113, 98, 75, 76, 185, 77, + 78, 79, 204, 205, 101, 221, 222, 170, 155, 171, + 169, 3, 28, 27, 230, 96, 220, 232, 176, 46, + 223, 11, 80, 81, 82, 83, 84, 11, 73, 78, + 79, 157, 241, 110, 8, 152, 237, 255, 236, 72, + 74, 80, 81, 82, 83, 84, 85, 73, 79, 278, + 160, 191, 277, 121, 189, 253, 254, 192, 248, 247, + 187, 139, 249, 250, 208, 262, 260, 261, 215, 263, + 80, 81, 82, 83, 84, 149, 73, 268, 269, 10, + 270, 5, 4, 2, 1, 88, 272, 273, 80, 81, + 82, 83, 84, 0, 73, 279, 0, 0, 271, 280, + 51, 52, 0, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 115, 116, 91, 90, 92, 0, 0, + 42, 43, 0, 87, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 88, 0, 20, 0, 17, 37, 24, + 25, 26, 38, 40, 39, 41, 23, 29, 30, 42, + 43, 0, 114, 15, 0, 0, 212, 0, 16, 0, + 13, 14, 22, 91, 90, 92, 0, 0, 0, 0, + 0, 33, 34, 0, 0, 0, 21, 0, 36, 0, + 148, 32, 0, 146, 35, 51, 52, 0, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 115, 116, 0, 0, 0, 0, 0, 42, 43, 0, 0, 63, 64, 65, 66, 67, 68, 69, 70, 71, 18, 19, 20, 0, 17, 37, 24, 25, 26, 38, 40, 39, - 41, 23, 29, 30, 114, 42, 43, 0, 109, 15, - 0, 0, 0, 0, 16, 0, 13, 14, 22, 0, + 41, 23, 29, 30, 42, 43, 0, 114, 15, 0, + 0, 109, 0, 16, 0, 13, 14, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 34, 0, 0, + 0, 21, 0, 36, 0, 0, 32, 0, 20, 35, + 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, + 29, 30, 42, 43, 0, 0, 15, 0, 0, 0, + 0, 16, 0, 13, 14, 22, 0, 0, 0, 0, 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, - 0, 36, 0, 0, 32, 0, 20, 35, 17, 37, - 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, - 0, 42, 43, 0, 0, 15, 0, 0, 0, 0, - 16, 0, 13, 14, 22, 0, 87, 0, 0, 0, - 33, 34, 0, 0, 0, 21, 88, 36, 0, 0, - 32, 0, 231, 35, 20, 0, 17, 37, 24, 25, - 26, 38, 40, 39, 41, 23, 29, 30, 0, 42, - 43, 0, 0, 15, 91, 90, 92, 0, 16, 0, - 13, 14, 22, 0, 0, 0, 0, 0, 33, 34, - 0, 0, 0, 21, 0, 36, 0, 0, 32, 0, - 118, 35, 20, 0, 17, 37, 24, 25, 26, 38, - 40, 39, 41, 23, 29, 30, 0, 42, 43, 0, - 0, 15, 0, 77, 78, 79, 16, 0, 13, 14, - 22, 0, 0, 0, 0, 0, 33, 34, 0, 0, - 0, 21, 0, 36, 0, 0, 32, 51, 52, 35, - 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, - 48, 0, 0, 80, 81, 82, 83, 84, 50, 73, - 0, 0, 63, 64, 65, 66, 67, 68, 69, 70, + 0, 36, 0, 0, 32, 0, 231, 35, 20, 0, + 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, + 29, 30, 42, 43, 0, 0, 15, 0, 0, 0, + 0, 16, 0, 13, 14, 22, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, + 0, 36, 0, 0, 32, 0, 118, 35, 20, 0, + 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, + 29, 30, 42, 43, 0, 0, 15, 0, 77, 78, + 79, 16, 0, 13, 14, 22, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, + 0, 36, 0, 0, 32, 51, 52, 35, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 48, 0, + 80, 81, 82, 83, 84, 50, 73, 0, 0, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 51, 52, + 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 48, 0, 0, 0, 0, 0, 0, 50, 0, + 0, 172, 63, 64, 65, 66, 67, 68, 69, 70, 71, 51, 52, 0, 53, 54, 55, 56, 57, 58, - 59, 60, 61, 62, 48, 0, 0, 0, 0, 0, - 0, 172, 50, 0, 0, 0, 63, 64, 65, 66, - 67, 68, 69, 70, 71, 51, 52, 0, 53, 54, - 55, 56, 57, 58, 59, 60, 61, 62, 115, 194, - 78, 79, 0, 0, 0, 45, 42, 43, 0, 0, - 63, 64, 65, 66, 67, 68, 69, 70, 71, 37, - 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, - 0, 42, 43, 75, 76, 193, 77, 78, 79, 80, - 81, 82, 83, 84, 22, 73, 0, 0, 0, 0, - 33, 34, 0, 0, 0, 21, 0, 36, 0, 0, - 32, 75, 76, 35, 77, 78, 79, 0, 0, 0, - 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, - 84, 0, 73, 0, 0, 0, 75, 76, 252, 77, - 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, - 73, 0, 0, 0, 75, 76, 233, 77, 78, 79, - 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, - 81, 82, 83, 84, 0, 73, 0, 0, 0, 75, - 76, 168, 77, 78, 79, 0, 0, 0, 0, 0, + 59, 60, 61, 62, 115, 194, 0, 0, 0, 0, + 0, 42, 43, 0, 45, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 37, 24, 25, 26, 38, 40, + 39, 41, 23, 29, 30, 42, 43, 75, 76, 0, + 77, 78, 79, 193, 0, 0, 0, 0, 22, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 34, 0, + 0, 0, 21, 0, 36, 0, 0, 32, 75, 76, + 35, 77, 78, 79, 0, 0, 0, 0, 0, 0, + 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, + 0, 0, 75, 76, 252, 77, 78, 79, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, + 0, 0, 0, 75, 76, 233, 77, 78, 79, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, - 83, 84, 0, 73, 0, 0, 75, 76, 281, 77, - 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, + 83, 84, 0, 73, 0, 0, 0, 75, 76, 168, + 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, + 82, 83, 84, 0, 73, 0, 0, 75, 76, 281, + 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, 276, 77, 78, 79, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, - 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, - 251, 77, 78, 79, 0, 0, 0, 0, 0, 0, - 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, - 73, 0, 0, 75, 76, 244, 77, 78, 79, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, - 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, - 75, 76, 217, 77, 78, 79, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, + 0, 75, 76, 251, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, - 84, 0, 73, 0, 0, 75, 76, 162, 77, 78, + 84, 0, 73, 0, 0, 75, 76, 244, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, - 0, 266, 75, 76, 0, 77, 78, 79, 0, 0, - 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, - 82, 83, 84, 0, 73, 0, 265, 75, 76, 0, - 77, 78, 79, 0, 0, 0, 75, 76, 0, 77, - 78, 79, 0, 72, 74, 80, 81, 82, 83, 84, - 0, 73, 0, 238, 75, 76, 274, 77, 78, 79, - 0, 0, 0, 0, 0, 151, 0, 0, 72, 74, - 80, 81, 82, 83, 84, 0, 73, 72, 74, 80, - 81, 82, 83, 84, 0, 73, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, - 83, 84, 0, 73, + 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, + 84, 0, 73, 0, 0, 75, 76, 217, 77, 78, + 79, 0, 0, 0, 0, 0, 0, 0, 72, 74, + 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, + 76, 162, 77, 78, 79, 0, 0, 0, 0, 0, + 75, 76, 0, 77, 78, 79, 0, 0, 72, 74, + 80, 81, 82, 83, 84, 0, 73, 0, 275, 75, + 76, 0, 77, 78, 79, 0, 0, 0, 0, 0, + 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, + 73, 0, 266, 72, 74, 80, 81, 82, 83, 84, + 0, 73, 0, 265, 75, 76, 0, 77, 78, 79, + 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, + 73, 0, 238, 0, 0, 0, 75, 76, 0, 77, + 78, 79, 274, 0, 0, 75, 76, 0, 77, 78, + 79, 0, 0, 0, 0, 0, 0, 72, 74, 80, + 81, 82, 83, 84, 151, 73, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, + 74, 80, 81, 82, 83, 84, 0, 73, 72, 74, + 80, 81, 82, 83, 84, 0, 73, } -var yyPact = [...]int{ - 169, -1000, -1000, -35, -1000, 387, 72, 614, -1000, 1040, - -1000, 529, 462, 673, 673, 529, 529, 141, 120, 113, - 149, 89, -1000, -1000, -1000, -1000, -1000, -6, -1000, -1000, - 155, -1000, 529, 673, 673, 357, 481, 130, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -11, -1000, 64, 58, - 57, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, +var yyPact = [...]int16{ + 181, -1000, -1000, -39, -1000, 387, 66, 621, -1000, 1071, + -1000, 535, 289, 678, 678, 535, 535, 154, 119, 115, + 164, 113, -1000, -1000, -1000, -1000, -1000, 13, -1000, -1000, + 139, -1000, 535, 678, 678, 358, 485, 127, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 1, -1000, 53, 52, + 51, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 529, -1000, 529, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 529, -1000, 1040, 82, -1000, -1000, - -1000, 89, 303, 201, 136, 1022, 529, 96, 88, 161, - -35, 3, -1000, -1000, 529, -1000, 909, 92, 92, -1000, - -12, -1000, 55, 54, 529, -1000, -1000, -1000, -1000, 752, - -1000, 267, -1000, 580, 174, 174, 174, 1040, 39, 39, - 556, 662, 221, 156, 212, 212, 77, 77, 77, 131, - -1000, -1000, 82, 648, -1000, -1000, -1000, 173, 529, 82, - 82, 529, -1000, 529, 529, 175, 68, -1000, 529, 175, - -3, 1040, -1000, -1000, 273, 673, 673, 884, -1000, -1000, - -1000, 529, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 107, -1000, -1000, 529, 82, 63, -1000, -25, - -1000, 35, 15, 529, -1000, -1000, 433, 727, -16, -27, - 1040, -1000, 1040, -35, -1000, -1000, -1000, 988, -26, -1000, - -1000, 529, -1000, -1000, 86, 92, 86, 7, 857, -1000, - 60, -1000, 1040, -1000, -1000, 82, -1000, 648, 82, 82, - 832, -1000, 699, -1000, 529, 529, 123, 66, -1000, 6, - 175, 1040, 673, 673, -1000, -1000, 174, -1000, -1000, -1000, - -1000, 5, -1000, 961, 936, 104, 529, 529, -1000, 529, - -1000, 92, 86, -1000, 82, 529, 529, -1000, 1013, 1040, - 19, -1000, 805, 49, 529, -1000, -1000, -1000, 529, 1040, - 780, -1000, + -1000, -1000, 535, -1000, 535, 535, 535, 535, 535, 535, + 535, 535, 535, 535, 535, -1000, 1071, 0, -1000, -1000, + -1000, 113, 302, 241, 89, 1062, 535, 98, 86, 174, + -39, 2, -1000, -1000, 535, -1000, 921, 71, 71, -1000, + -12, -1000, 49, 48, 535, -1000, -1000, -1000, -1000, 758, + -1000, 160, -1000, 588, 40, 40, 40, 1071, 153, 153, + 561, 201, 219, 67, 79, 79, 43, 43, 43, 131, + -1000, -1000, 0, 654, -1000, -1000, -1000, 39, 535, 0, + 0, 535, -1000, 535, 535, 162, 64, -1000, 535, 162, + -5, 1071, -1000, -1000, 273, 678, 678, 897, -1000, -1000, + -1000, 535, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 7, -1000, -1000, 535, 0, 5, -1000, -13, + -1000, 46, 41, 535, -1000, -1000, 435, 734, 12, 11, + 1071, -1000, 1071, -39, -1000, -1000, -1000, 1005, -30, -1000, + -1000, 535, -1000, -1000, 77, 71, 77, 16, 867, -1000, + -20, -1000, 1071, -1000, -1000, 0, -1000, 654, 0, 0, + 843, -1000, 703, -1000, 535, 535, 117, 57, -1000, -4, + 162, 1071, 678, 678, -1000, -1000, 40, -1000, -1000, -1000, + -1000, -29, -1000, 986, 975, 101, 535, 535, -1000, 535, + -1000, 71, 77, -1000, 0, 535, 535, -1000, 1040, 1071, + 951, -1000, 813, 172, 535, -1000, -1000, -1000, 535, 1071, + 789, -1000, } -var yyPgo = [...]int{ - 0, 299, 296, 282, 277, 276, 12, 198, 244, 273, - 0, 264, 13, 259, 252, 10, 4, 9, 251, 20, - 241, 240, 233, 227, 217, 8, 1, 2, 7, 214, - 15, 213, 208, 5, 201, 187, 14, 3, +var yyPgo = [...]int16{ + 0, 264, 263, 262, 261, 259, 12, 214, 195, 244, + 0, 241, 13, 240, 234, 10, 4, 9, 233, 20, + 230, 218, 217, 215, 213, 8, 1, 2, 7, 199, + 15, 198, 196, 5, 193, 192, 14, 3, } -var yyR1 = [...]int{ +var yyR1 = [...]int8{ 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 33, 33, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, @@ -346,7 +358,7 @@ var yyR1 = [...]int{ 36, 36, 36, 36, 36, 36, 36, 36, } -var yyR2 = [...]int{ +var yyR2 = [...]int8{ 0, 2, 0, 3, 2, 2, 0, 2, 6, 4, 0, 1, 0, 2, 5, 8, 1, 3, 1, 1, 2, 3, 5, 9, 9, 11, 7, 3, 4, 2, @@ -365,39 +377,39 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, } -var yyChk = [...]int{ - -1000, -1, -2, 10, -3, -4, -28, 60, -7, -10, - -5, -8, -16, 39, 40, 32, 37, 15, 11, 12, - 13, 52, 41, 24, 17, 18, 19, -34, -35, 25, - 26, -17, 57, 47, 48, 60, 54, 16, 20, 22, - 21, 23, 28, 29, 55, 61, -29, -30, 20, -36, - 28, 7, 8, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 45, 53, 46, 4, 5, 7, 8, 9, - 47, 48, 49, 50, 51, -7, -10, 14, 24, -19, - 53, 52, 54, -16, -16, -10, -8, -10, 21, 28, - 28, 20, -19, -17, 57, -17, -10, -16, -16, 61, - -24, -25, -37, -17, 57, 20, 21, -36, 59, -10, - 21, -18, 61, 46, 56, 56, 56, -10, -10, -10, +var yyChk = [...]int16{ + -1000, -1, -2, 10, -3, -4, -28, 62, -7, -10, + -5, -8, -16, 38, 39, 31, 36, 15, 11, 12, + 13, 54, 40, 24, 17, 18, 19, -34, -35, 25, + 26, -17, 59, 49, 50, 62, 56, 16, 20, 22, + 21, 23, 27, 28, 57, 63, -29, -30, 20, -36, + 27, 7, 8, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 47, 55, 48, 4, 5, 7, 8, 9, + 49, 50, 51, 52, 53, -7, -10, 14, 24, -19, + 55, 54, 56, -16, -16, -10, -8, -10, 21, 27, + 27, 20, -19, -17, 59, -17, -10, -16, -16, 63, + -24, -25, -37, -17, 59, 20, 21, -36, 61, -10, + 21, -18, 63, 48, 58, 58, 58, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -11, - -12, 21, 54, 60, -19, -17, 59, -10, 56, 14, - 14, 33, -23, 38, 45, 14, -6, -28, 56, 57, - -20, -10, 58, 61, 46, 56, 56, -10, 59, 31, - 28, 30, 61, -30, -27, -28, -31, 25, 28, 17, - 18, 19, 54, -27, -27, 45, 6, -13, -12, -14, - -15, -37, -17, 57, 21, 59, 56, -10, -12, -12, - -10, -10, -10, -33, 20, 21, 55, -10, -9, -33, - 58, 55, 61, -25, -26, -16, -26, 58, -10, 59, - -32, -27, -10, -12, 59, 46, 61, 46, 56, 56, - -10, 59, -10, 59, 57, 57, -21, -6, 55, 58, - 55, -10, 45, 56, 58, 59, 46, -12, -15, -12, - -12, 58, 59, -10, -10, -22, 34, 35, 55, 56, - -33, -16, -26, -27, 56, 55, 55, 36, -10, -10, - -10, -12, -10, -10, 33, 55, 58, 58, 55, -10, - -10, 58, + -12, 21, 56, 62, -19, -17, 61, -10, 58, 14, + 14, 32, -23, 37, 47, 14, -6, -28, 58, 59, + -20, -10, 60, 63, 48, 58, 58, -10, 61, 30, + 27, 29, 63, -30, -27, -28, -31, 25, 27, 17, + 18, 19, 56, -27, -27, 47, 6, -13, -12, -14, + -15, -37, -17, 59, 21, 61, 58, -10, -12, -12, + -10, -10, -10, -33, 20, 21, 57, -10, -9, -33, + 60, 57, 63, -25, -26, -16, -26, 60, -10, 61, + -32, -27, -10, -12, 61, 48, 63, 48, 58, 58, + -10, 61, -10, 61, 59, 59, -21, -6, 57, 60, + 57, -10, 47, 58, 60, 61, 48, -12, -15, -12, + -12, 60, 61, -10, -10, -22, 33, 34, 57, 58, + -33, -16, -26, -27, 58, 57, 57, 35, -10, -10, + -10, -12, -10, -10, 32, 57, 60, 60, 57, -10, + -10, 60, } -var yyDef = [...]int{ +var yyDef = [...]int16{ 2, -2, 6, 0, 1, 12, 0, 0, 4, 5, 7, 12, 41, 0, 0, 0, 0, 0, 0, 0, 0, 55, 56, 57, 60, 61, 62, 63, 65, 66, @@ -429,31 +441,31 @@ var yyDef = [...]int{ 0, 25, } -var yyTok1 = [...]int{ +var yyTok1 = [...]int8{ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 51, 3, 3, - 57, 58, 49, 47, 46, 48, 52, 50, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 56, 55, - 3, 3, 3, 53, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 53, 3, 3, + 59, 60, 51, 49, 48, 50, 54, 52, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 58, 57, + 3, 3, 3, 55, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 54, 3, 59, 3, 3, 3, 3, 3, 3, + 3, 56, 3, 61, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 60, 45, 61, + 3, 3, 3, 62, 47, 63, } -var yyTok2 = [...]int{ +var yyTok2 = [...]int8{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, + 42, 43, 44, 45, 46, } -var yyTok3 = [...]int{ +var yyTok3 = [...]int8{ 0, } @@ -535,9 +547,9 @@ func yyErrorMessage(state, lookAhead int) string { expected := make([]int, 0, 4) // Look for shiftable tokens. - base := yyPact[state] + base := int(yyPact[state]) for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { - if n := base + tok; n >= 0 && n < yyLast && yyChk[yyAct[n]] == tok { + if n := base + tok; n >= 0 && n < yyLast && int(yyChk[int(yyAct[n])]) == tok { if len(expected) == cap(expected) { return res } @@ -547,13 +559,13 @@ func yyErrorMessage(state, lookAhead int) string { if yyDef[state] == -2 { i := 0 - for yyExca[i] != -1 || yyExca[i+1] != state { + for yyExca[i] != -1 || int(yyExca[i+1]) != state { i += 2 } // Look for tokens that we accept or reduce. for i += 2; yyExca[i] >= 0; i += 2 { - tok := yyExca[i] + tok := int(yyExca[i]) if tok < TOKSTART || yyExca[i+1] == 0 { continue } @@ -584,30 +596,30 @@ func yylex1(lex yyLexer, lval *yySymType) (char, token int) { token = 0 char = lex.Lex(lval) if char <= 0 { - token = yyTok1[0] + token = int(yyTok1[0]) goto out } if char < len(yyTok1) { - token = yyTok1[char] + token = int(yyTok1[char]) goto out } if char >= yyPrivate { if char < yyPrivate+len(yyTok2) { - token = yyTok2[char-yyPrivate] + token = int(yyTok2[char-yyPrivate]) goto out } } for i := 0; i < len(yyTok3); i += 2 { - token = yyTok3[i+0] + token = int(yyTok3[i+0]) if token == char { - token = yyTok3[i+1] + token = int(yyTok3[i+1]) goto out } } out: if token == 0 { - token = yyTok2[1] /* unknown char */ + token = int(yyTok2[1]) /* unknown char */ } if yyDebug >= 3 { __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) @@ -662,7 +674,7 @@ yystack: yyS[yyp].yys = yystate yynewstate: - yyn = yyPact[yystate] + yyn = int(yyPact[yystate]) if yyn <= yyFlag { goto yydefault /* simple state */ } @@ -673,8 +685,8 @@ yynewstate: if yyn < 0 || yyn >= yyLast { goto yydefault } - yyn = yyAct[yyn] - if yyChk[yyn] == yytoken { /* valid shift */ + yyn = int(yyAct[yyn]) + if int(yyChk[yyn]) == yytoken { /* valid shift */ yyrcvr.char = -1 yytoken = -1 yyVAL = yyrcvr.lval @@ -687,7 +699,7 @@ yynewstate: yydefault: /* default state action */ - yyn = yyDef[yystate] + yyn = int(yyDef[yystate]) if yyn == -2 { if yyrcvr.char < 0 { yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) @@ -696,18 +708,18 @@ yydefault: /* look through exception table */ xi := 0 for { - if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { + if yyExca[xi+0] == -1 && int(yyExca[xi+1]) == yystate { break } xi += 2 } for xi += 2; ; xi += 2 { - yyn = yyExca[xi+0] + yyn = int(yyExca[xi+0]) if yyn < 0 || yyn == yytoken { break } } - yyn = yyExca[xi+1] + yyn = int(yyExca[xi+1]) if yyn < 0 { goto ret0 } @@ -729,10 +741,10 @@ yydefault: /* find a state where "error" is a legal shift action */ for yyp >= 0 { - yyn = yyPact[yyS[yyp].yys] + yyErrCode + yyn = int(yyPact[yyS[yyp].yys]) + yyErrCode if yyn >= 0 && yyn < yyLast { - yystate = yyAct[yyn] /* simulate a shift of "error" */ - if yyChk[yystate] == yyErrCode { + yystate = int(yyAct[yyn]) /* simulate a shift of "error" */ + if int(yyChk[yystate]) == yyErrCode { goto yystack } } @@ -768,7 +780,7 @@ yydefault: yypt := yyp _ = yypt // guard against "declared and not used" - yyp -= yyR2[yyn] + yyp -= int(yyR2[yyn]) // yyp is now the index of $0. Perform the default action. Iff the // reduced production is ε, $1 is possibly out of range. if yyp+1 >= len(yyS) { @@ -779,16 +791,16 @@ yydefault: yyVAL = yyS[yyp+1] /* consult goto table to find next state */ - yyn = yyR1[yyn] - yyg := yyPgo[yyn] + yyn = int(yyR1[yyn]) + yyg := int(yyPgo[yyn]) yyj := yyg + yyS[yyp].yys + 1 if yyj >= yyLast { - yystate = yyAct[yyg] + yystate = int(yyAct[yyg]) } else { - yystate = yyAct[yyj] - if yyChk[yystate] != -yyn { - yystate = yyAct[yyg] + yystate = int(yyAct[yyj]) + if int(yyChk[yystate]) != -yyn { + yystate = int(yyAct[yyg]) } } // dummy call; replaced with literal code @@ -796,7 +808,7 @@ yydefault: case 1: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:67 +//line parser.go.y:73 { if yyDollar[1].value != nil { yyDollar[2].value.(*Query).Meta = yyDollar[1].value.(*ConstObject) @@ -805,25 +817,25 @@ yydefault: } case 2: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:74 +//line parser.go.y:80 { yyVAL.value = nil } case 3: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:78 +//line parser.go.y:84 { yyVAL.value = yyDollar[2].value } case 4: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:84 +//line parser.go.y:90 { yyVAL.value = &Query{Imports: yyDollar[1].value.([]*Import), FuncDefs: reverseFuncDef(yyDollar[2].value.([]*FuncDef)), Term: &Term{Type: TermTypeIdentity}} } case 5: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:88 +//line parser.go.y:94 { if yyDollar[1].value != nil { yyDollar[2].value.(*Query).Imports = yyDollar[1].value.([]*Import) @@ -832,144 +844,144 @@ yydefault: } case 6: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:95 +//line parser.go.y:101 { yyVAL.value = []*Import(nil) } case 7: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:99 +//line parser.go.y:105 { yyVAL.value = append(yyDollar[1].value.([]*Import), yyDollar[2].value.(*Import)) } case 8: yyDollar = yyS[yypt-6 : yypt+1] -//line parser.go.y:105 +//line parser.go.y:111 { yyVAL.value = &Import{ImportPath: yyDollar[2].token, ImportAlias: yyDollar[4].token, Meta: yyDollar[5].value.(*ConstObject)} } case 9: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:109 +//line parser.go.y:115 { yyVAL.value = &Import{IncludePath: yyDollar[2].token, Meta: yyDollar[3].value.(*ConstObject)} } case 10: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:115 +//line parser.go.y:121 { yyVAL.value = (*ConstObject)(nil) } case 11: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:118 +//line parser.go.y:124 { } case 12: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:122 +//line parser.go.y:128 { yyVAL.value = []*FuncDef(nil) } case 13: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:126 +//line parser.go.y:132 { yyVAL.value = append(yyDollar[2].value.([]*FuncDef), yyDollar[1].value.(*FuncDef)) } case 14: yyDollar = yyS[yypt-5 : yypt+1] -//line parser.go.y:132 +//line parser.go.y:138 { yyVAL.value = &FuncDef{Name: yyDollar[2].token, Body: yyDollar[4].value.(*Query)} } case 15: yyDollar = yyS[yypt-8 : yypt+1] -//line parser.go.y:136 +//line parser.go.y:142 { yyVAL.value = &FuncDef{yyDollar[2].token, yyDollar[4].value.([]string), yyDollar[7].value.(*Query)} } case 16: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:142 +//line parser.go.y:148 { yyVAL.value = []string{yyDollar[1].token} } case 17: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:146 +//line parser.go.y:152 { yyVAL.value = append(yyDollar[1].value.([]string), yyDollar[3].token) } case 18: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:151 +//line parser.go.y:157 { } case 19: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:152 +//line parser.go.y:158 { } case 20: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:156 +//line parser.go.y:162 { yyDollar[2].value.(*Query).FuncDefs = prependFuncDef(yyDollar[2].value.(*Query).FuncDefs, yyDollar[1].value.(*FuncDef)) yyVAL.value = yyDollar[2].value } case 21: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:161 +//line parser.go.y:167 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpPipe, Right: yyDollar[3].value.(*Query)} } case 22: yyDollar = yyS[yypt-5 : yypt+1] -//line parser.go.y:165 +//line parser.go.y:171 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Bind: &Bind{yyDollar[3].value.([]*Pattern), yyDollar[5].value.(*Query)}}) yyVAL.value = &Query{Term: yyDollar[1].value.(*Term)} } case 23: yyDollar = yyS[yypt-9 : yypt+1] -//line parser.go.y:170 +//line parser.go.y:176 { yyVAL.value = &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{yyDollar[2].value.(*Term), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query)}}} } case 24: yyDollar = yyS[yypt-9 : yypt+1] -//line parser.go.y:174 +//line parser.go.y:180 { yyVAL.value = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{yyDollar[2].value.(*Term), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query), nil}}} } case 25: yyDollar = yyS[yypt-11 : yypt+1] -//line parser.go.y:178 +//line parser.go.y:184 { yyVAL.value = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{yyDollar[2].value.(*Term), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query), yyDollar[10].value.(*Query)}}} } case 26: yyDollar = yyS[yypt-7 : yypt+1] -//line parser.go.y:182 +//line parser.go.y:188 { yyVAL.value = &Query{Term: &Term{Type: TermTypeIf, If: &If{yyDollar[2].value.(*Query), yyDollar[4].value.(*Query), yyDollar[5].value.([]*IfElif), yyDollar[6].value.(*Query)}}} } case 27: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:186 +//line parser.go.y:192 { yyVAL.value = &Query{Term: &Term{Type: TermTypeTry, Try: &Try{yyDollar[2].value.(*Query), yyDollar[3].value.(*Query)}}} } case 28: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:190 +//line parser.go.y:196 { yyVAL.value = &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{yyDollar[2].token, yyDollar[4].value.(*Query)}}} } case 29: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:194 +//line parser.go.y:200 { if t := yyDollar[1].value.(*Query).Term; t != nil { t.SuffixList = append(t.SuffixList, &Suffix{Optional: true}) @@ -979,175 +991,175 @@ yydefault: } case 30: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:202 +//line parser.go.y:208 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpComma, Right: yyDollar[3].value.(*Query)} } case 31: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:206 +//line parser.go.y:212 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} } case 32: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:210 +//line parser.go.y:216 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} } case 33: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:214 +//line parser.go.y:220 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpOr, Right: yyDollar[3].value.(*Query)} } case 34: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:218 +//line parser.go.y:224 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpAnd, Right: yyDollar[3].value.(*Query)} } case 35: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:222 +//line parser.go.y:228 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} } case 36: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:226 +//line parser.go.y:232 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpAdd, Right: yyDollar[3].value.(*Query)} } case 37: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:230 +//line parser.go.y:236 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpSub, Right: yyDollar[3].value.(*Query)} } case 38: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:234 +//line parser.go.y:240 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpMul, Right: yyDollar[3].value.(*Query)} } case 39: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:238 +//line parser.go.y:244 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpDiv, Right: yyDollar[3].value.(*Query)} } case 40: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:242 +//line parser.go.y:248 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpMod, Right: yyDollar[3].value.(*Query)} } case 41: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:246 +//line parser.go.y:252 { yyVAL.value = &Query{Term: yyDollar[1].value.(*Term)} } case 42: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:252 +//line parser.go.y:258 { yyVAL.value = []*Pattern{yyDollar[1].value.(*Pattern)} } case 43: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:256 +//line parser.go.y:262 { yyVAL.value = append(yyDollar[1].value.([]*Pattern), yyDollar[3].value.(*Pattern)) } case 44: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:262 +//line parser.go.y:268 { yyVAL.value = &Pattern{Name: yyDollar[1].token} } case 45: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:266 +//line parser.go.y:272 { yyVAL.value = &Pattern{Array: yyDollar[2].value.([]*Pattern)} } case 46: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:270 +//line parser.go.y:276 { yyVAL.value = &Pattern{Object: yyDollar[2].value.([]*PatternObject)} } case 47: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:276 +//line parser.go.y:282 { yyVAL.value = []*Pattern{yyDollar[1].value.(*Pattern)} } case 48: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:280 +//line parser.go.y:286 { yyVAL.value = append(yyDollar[1].value.([]*Pattern), yyDollar[3].value.(*Pattern)) } case 49: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:286 +//line parser.go.y:292 { yyVAL.value = []*PatternObject{yyDollar[1].value.(*PatternObject)} } case 50: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:290 +//line parser.go.y:296 { yyVAL.value = append(yyDollar[1].value.([]*PatternObject), yyDollar[3].value.(*PatternObject)) } case 51: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:296 +//line parser.go.y:302 { yyVAL.value = &PatternObject{Key: yyDollar[1].token, Val: yyDollar[3].value.(*Pattern)} } case 52: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:300 +//line parser.go.y:306 { yyVAL.value = &PatternObject{KeyString: yyDollar[1].value.(*String), Val: yyDollar[3].value.(*Pattern)} } case 53: yyDollar = yyS[yypt-5 : yypt+1] -//line parser.go.y:304 +//line parser.go.y:310 { yyVAL.value = &PatternObject{KeyQuery: yyDollar[2].value.(*Query), Val: yyDollar[5].value.(*Pattern)} } case 54: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:308 +//line parser.go.y:314 { - yyVAL.value = &PatternObject{KeyOnly: yyDollar[1].token} + yyVAL.value = &PatternObject{Key: yyDollar[1].token} } case 55: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:314 +//line parser.go.y:320 { yyVAL.value = &Term{Type: TermTypeIdentity} } case 56: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:318 +//line parser.go.y:324 { yyVAL.value = &Term{Type: TermTypeRecurse} } case 57: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:322 +//line parser.go.y:328 { yyVAL.value = &Term{Type: TermTypeIndex, Index: &Index{Name: yyDollar[1].token}} } case 58: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:326 +//line parser.go.y:332 { if yyDollar[2].value.(*Suffix).Iter { yyVAL.value = &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{yyDollar[2].value.(*Suffix)}} @@ -1157,569 +1169,569 @@ yydefault: } case 59: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:334 +//line parser.go.y:340 { yyVAL.value = &Term{Type: TermTypeIndex, Index: &Index{Str: yyDollar[2].value.(*String)}} } case 60: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:338 +//line parser.go.y:344 { yyVAL.value = &Term{Type: TermTypeNull} } case 61: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:342 +//line parser.go.y:348 { yyVAL.value = &Term{Type: TermTypeTrue} } case 62: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:346 +//line parser.go.y:352 { yyVAL.value = &Term{Type: TermTypeFalse} } case 63: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:350 +//line parser.go.y:356 { yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token}} } case 64: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:354 +//line parser.go.y:360 { yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token, Args: yyDollar[3].value.([]*Query)}} } case 65: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:358 +//line parser.go.y:364 { yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token}} } case 66: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:362 +//line parser.go.y:368 { yyVAL.value = &Term{Type: TermTypeNumber, Number: yyDollar[1].token} } case 67: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:366 +//line parser.go.y:372 { yyVAL.value = &Term{Type: TermTypeFormat, Format: yyDollar[1].token} } case 68: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:370 +//line parser.go.y:376 { yyVAL.value = &Term{Type: TermTypeFormat, Format: yyDollar[1].token, Str: yyDollar[2].value.(*String)} } case 69: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:374 +//line parser.go.y:380 { yyVAL.value = &Term{Type: TermTypeString, Str: yyDollar[1].value.(*String)} } case 70: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:378 +//line parser.go.y:384 { yyVAL.value = &Term{Type: TermTypeQuery, Query: yyDollar[2].value.(*Query)} } case 71: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:382 +//line parser.go.y:388 { yyVAL.value = &Term{Type: TermTypeUnary, Unary: &Unary{OpAdd, yyDollar[2].value.(*Term)}} } case 72: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:386 +//line parser.go.y:392 { yyVAL.value = &Term{Type: TermTypeUnary, Unary: &Unary{OpSub, yyDollar[2].value.(*Term)}} } case 73: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:390 +//line parser.go.y:396 { yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{}} } case 74: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:394 +//line parser.go.y:400 { yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{yyDollar[2].value.([]*ObjectKeyVal)}} } case 75: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:398 +//line parser.go.y:404 { yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{yyDollar[2].value.([]*ObjectKeyVal)}} } case 76: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:402 +//line parser.go.y:408 { yyVAL.value = &Term{Type: TermTypeArray, Array: &Array{}} } case 77: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:406 +//line parser.go.y:412 { yyVAL.value = &Term{Type: TermTypeArray, Array: &Array{yyDollar[2].value.(*Query)}} } case 78: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:410 +//line parser.go.y:416 { yyVAL.value = &Term{Type: TermTypeBreak, Break: yyDollar[2].token} } case 79: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:414 +//line parser.go.y:420 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Index: &Index{Name: yyDollar[2].token}}) } case 80: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:418 +//line parser.go.y:424 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, yyDollar[2].value.(*Suffix)) } case 81: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:422 +//line parser.go.y:428 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Optional: true}) } case 82: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:426 +//line parser.go.y:432 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, yyDollar[3].value.(*Suffix)) } case 83: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:430 +//line parser.go.y:436 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Index: &Index{Str: yyDollar[3].value.(*String)}}) } case 84: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:436 +//line parser.go.y:442 { yyVAL.value = &String{Str: yyDollar[1].token} } case 85: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:440 +//line parser.go.y:446 { yyVAL.value = &String{Queries: yyDollar[2].value.([]*Query)} } case 86: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:446 +//line parser.go.y:452 { yyVAL.value = []*Query{} } case 87: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:450 +//line parser.go.y:456 { yyVAL.value = append(yyDollar[1].value.([]*Query), &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: yyDollar[2].token}}}) } case 88: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:454 +//line parser.go.y:460 { yylex.(*lexer).inString = true yyVAL.value = append(yyDollar[1].value.([]*Query), &Query{Term: &Term{Type: TermTypeQuery, Query: yyDollar[3].value.(*Query)}}) } case 89: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:460 +//line parser.go.y:466 { } case 90: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:461 +//line parser.go.y:467 { } case 91: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:464 +//line parser.go.y:470 { } case 92: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:465 +//line parser.go.y:471 { } case 93: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:469 +//line parser.go.y:475 { yyVAL.value = &Suffix{Iter: true} } case 94: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:473 +//line parser.go.y:479 { yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query)}} } case 95: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:477 +//line parser.go.y:483 { yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query), IsSlice: true}} } case 96: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:481 +//line parser.go.y:487 { yyVAL.value = &Suffix{Index: &Index{End: yyDollar[3].value.(*Query), IsSlice: true}} } case 97: yyDollar = yyS[yypt-5 : yypt+1] -//line parser.go.y:485 +//line parser.go.y:491 { yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query), End: yyDollar[4].value.(*Query), IsSlice: true}} } case 98: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:491 +//line parser.go.y:497 { yyVAL.value = []*Query{yyDollar[1].value.(*Query)} } case 99: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:495 +//line parser.go.y:501 { yyVAL.value = append(yyDollar[1].value.([]*Query), yyDollar[3].value.(*Query)) } case 100: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:501 +//line parser.go.y:507 { yyVAL.value = []*IfElif(nil) } case 101: yyDollar = yyS[yypt-5 : yypt+1] -//line parser.go.y:505 +//line parser.go.y:511 { yyVAL.value = append(yyDollar[1].value.([]*IfElif), &IfElif{yyDollar[3].value.(*Query), yyDollar[5].value.(*Query)}) } case 102: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:511 +//line parser.go.y:517 { yyVAL.value = (*Query)(nil) } case 103: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:515 +//line parser.go.y:521 { yyVAL.value = yyDollar[2].value } case 104: yyDollar = yyS[yypt-0 : yypt+1] -//line parser.go.y:521 +//line parser.go.y:527 { yyVAL.value = (*Query)(nil) } case 105: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:525 +//line parser.go.y:531 { yyVAL.value = yyDollar[2].value } case 106: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:531 +//line parser.go.y:537 { yyVAL.value = []*ObjectKeyVal{yyDollar[1].value.(*ObjectKeyVal)} } case 107: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:535 +//line parser.go.y:541 { yyVAL.value = append(yyDollar[1].value.([]*ObjectKeyVal), yyDollar[3].value.(*ObjectKeyVal)) } case 108: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:541 +//line parser.go.y:547 { yyVAL.value = &ObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ObjectVal)} } case 109: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:545 +//line parser.go.y:551 { yyVAL.value = &ObjectKeyVal{KeyString: yyDollar[1].value.(*String), Val: yyDollar[3].value.(*ObjectVal)} } case 110: yyDollar = yyS[yypt-5 : yypt+1] -//line parser.go.y:549 +//line parser.go.y:555 { yyVAL.value = &ObjectKeyVal{KeyQuery: yyDollar[2].value.(*Query), Val: yyDollar[5].value.(*ObjectVal)} } case 111: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:553 +//line parser.go.y:559 { - yyVAL.value = &ObjectKeyVal{KeyOnly: yyDollar[1].token} + yyVAL.value = &ObjectKeyVal{Key: yyDollar[1].token} } case 112: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:557 +//line parser.go.y:563 { - yyVAL.value = &ObjectKeyVal{KeyOnlyString: yyDollar[1].value.(*String)} + yyVAL.value = &ObjectKeyVal{KeyString: yyDollar[1].value.(*String)} } case 113: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:562 +//line parser.go.y:568 { } case 114: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:563 +//line parser.go.y:569 { } case 115: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:564 +//line parser.go.y:570 { } case 116: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:568 +//line parser.go.y:574 { yyVAL.value = &ObjectVal{[]*Query{{Term: yyDollar[1].value.(*Term)}}} } case 117: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:572 +//line parser.go.y:578 { yyVAL.value = &ObjectVal{append(yyDollar[1].value.(*ObjectVal).Queries, &Query{Term: yyDollar[3].value.(*Term)})} } case 118: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:578 +//line parser.go.y:584 { yyVAL.value = &ConstTerm{Object: yyDollar[1].value.(*ConstObject)} } case 119: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:582 +//line parser.go.y:588 { yyVAL.value = &ConstTerm{Array: yyDollar[1].value.(*ConstArray)} } case 120: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:586 +//line parser.go.y:592 { yyVAL.value = &ConstTerm{Number: yyDollar[1].token} } case 121: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:590 +//line parser.go.y:596 { yyVAL.value = &ConstTerm{Str: yyDollar[1].token} } case 122: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:594 +//line parser.go.y:600 { yyVAL.value = &ConstTerm{Null: true} } case 123: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:598 +//line parser.go.y:604 { yyVAL.value = &ConstTerm{True: true} } case 124: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:602 +//line parser.go.y:608 { yyVAL.value = &ConstTerm{False: true} } case 125: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:608 +//line parser.go.y:614 { yyVAL.value = &ConstObject{} } case 126: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:612 +//line parser.go.y:618 { yyVAL.value = &ConstObject{yyDollar[2].value.([]*ConstObjectKeyVal)} } case 127: yyDollar = yyS[yypt-4 : yypt+1] -//line parser.go.y:616 +//line parser.go.y:622 { yyVAL.value = &ConstObject{yyDollar[2].value.([]*ConstObjectKeyVal)} } case 128: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:622 +//line parser.go.y:628 { yyVAL.value = []*ConstObjectKeyVal{yyDollar[1].value.(*ConstObjectKeyVal)} } case 129: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:626 +//line parser.go.y:632 { yyVAL.value = append(yyDollar[1].value.([]*ConstObjectKeyVal), yyDollar[3].value.(*ConstObjectKeyVal)) } case 130: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:632 +//line parser.go.y:638 { yyVAL.value = &ConstObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} } case 131: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:636 +//line parser.go.y:642 { yyVAL.value = &ConstObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} } case 132: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:640 +//line parser.go.y:646 { yyVAL.value = &ConstObjectKeyVal{KeyString: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} } case 133: yyDollar = yyS[yypt-2 : yypt+1] -//line parser.go.y:646 +//line parser.go.y:652 { yyVAL.value = &ConstArray{} } case 134: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:650 +//line parser.go.y:656 { yyVAL.value = &ConstArray{yyDollar[2].value.([]*ConstTerm)} } case 135: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:656 +//line parser.go.y:662 { yyVAL.value = []*ConstTerm{yyDollar[1].value.(*ConstTerm)} } case 136: yyDollar = yyS[yypt-3 : yypt+1] -//line parser.go.y:660 +//line parser.go.y:666 { yyVAL.value = append(yyDollar[1].value.([]*ConstTerm), yyDollar[3].value.(*ConstTerm)) } case 137: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:665 +//line parser.go.y:671 { } case 138: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:666 +//line parser.go.y:672 { } case 139: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:667 +//line parser.go.y:673 { } case 140: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:668 +//line parser.go.y:674 { } case 141: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:669 +//line parser.go.y:675 { } case 142: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:670 +//line parser.go.y:676 { } case 143: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:671 +//line parser.go.y:677 { } case 144: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:672 +//line parser.go.y:678 { } case 145: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:673 +//line parser.go.y:679 { } case 146: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:674 +//line parser.go.y:680 { } case 147: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:675 +//line parser.go.y:681 { } case 148: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:676 +//line parser.go.y:682 { } case 149: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:677 +//line parser.go.y:683 { } case 150: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:678 +//line parser.go.y:684 { } case 151: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:679 +//line parser.go.y:685 { } case 152: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:680 +//line parser.go.y:686 { } case 153: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:681 +//line parser.go.y:687 { } case 154: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:682 +//line parser.go.y:688 { } case 155: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:683 +//line parser.go.y:689 { } case 156: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:684 +//line parser.go.y:690 { } case 157: yyDollar = yyS[yypt-1 : yypt+1] -//line parser.go.y:685 +//line parser.go.y:691 { } } diff --git a/vendor/github.com/itchyny/gojq/parser.go.y b/vendor/github.com/itchyny/gojq/parser.go.y index 5e2bc081..380c3cf6 100644 --- a/vendor/github.com/itchyny/gojq/parser.go.y +++ b/vendor/github.com/itchyny/gojq/parser.go.y @@ -1,7 +1,12 @@ %{ package gojq -// Parse parses a query. +// Parse a query string, and returns the query struct. +// +// If parsing failed, the returned error has the method Token() (string, int), +// which reports the invalid token and the byte offset in the query string. The +// token is empty if the error occurred after scanning the entire query string. +// The byte offset is the scanned bytes when the error occurred. func Parse(src string) (*Query, error) { l := newLexer(src) if yyParse(l) > 0 { @@ -26,7 +31,7 @@ func prependFuncDef(xs []*FuncDef, x *FuncDef) []*FuncDef { %} %union { - value interface{} + value any token string operator Operator } @@ -41,11 +46,12 @@ func prependFuncDef(xs []*FuncDef, x *FuncDef) []*FuncDef { %token tokModule tokImport tokInclude tokDef tokAs tokLabel tokBreak %token tokNull tokTrue tokFalse %token tokIdent tokVariable tokModuleIdent tokModuleVariable -%token tokIndex tokNumber tokFormat tokInvalid +%token tokIndex tokNumber tokFormat %token tokString tokStringStart tokStringQuery tokStringEnd %token tokIf tokThen tokElif tokElse tokEnd %token tokTry tokCatch tokReduce tokForeach %token tokRecurse tokFuncDefPost tokTermPost tokEmptyCatch +%token tokInvalid tokInvalidEscapeSequence tokUnterminatedString %nonassoc tokFuncDefPost tokTermPost %right '|' @@ -306,7 +312,7 @@ objectpattern } | tokVariable { - $$ = &PatternObject{KeyOnly: $1} + $$ = &PatternObject{Key: $1} } term @@ -551,11 +557,11 @@ objectkeyval } | objectkey { - $$ = &ObjectKeyVal{KeyOnly: $1} + $$ = &ObjectKeyVal{Key: $1} } | string { - $$ = &ObjectKeyVal{KeyOnlyString: $1.(*String)} + $$ = &ObjectKeyVal{KeyString: $1.(*String)} } objectkey diff --git a/vendor/github.com/itchyny/gojq/preview.go b/vendor/github.com/itchyny/gojq/preview.go new file mode 100644 index 00000000..e082eb56 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/preview.go @@ -0,0 +1,77 @@ +package gojq + +import "unicode/utf8" + +// Preview returns the preview string of v. The preview string is basically the +// same as the jq-flavored JSON encoding returned by [Marshal], but is truncated +// by 30 bytes, and more efficient than truncating the result of [Marshal]. +// +// This method is used by error messages of built-in operators and functions, +// and accepts only limited types (nil, bool, int, float64, *big.Int, string, +// []any, and map[string]any). Note that the maximum width and trailing strings +// on truncation may be changed in the future. +func Preview(v any) string { + bs := jsonLimitedMarshal(v, 32) + if l := 30; len(bs) > l { + var trailing string + switch v.(type) { + case string: + trailing = ` ..."` + case []any: + trailing = " ...]" + case map[string]any: + trailing = " ...}" + default: + trailing = " ..." + } + for len(bs) > l-len(trailing) { + _, size := utf8.DecodeLastRune(bs) + bs = bs[:len(bs)-size] + } + bs = append(bs, trailing...) + } + return string(bs) +} + +func jsonLimitedMarshal(v any, n int) (bs []byte) { + w := &limitedWriter{buf: make([]byte, n)} + defer func() { + _ = recover() + bs = w.Bytes() + }() + (&encoder{w: w}).encode(v) + return +} + +type limitedWriter struct { + buf []byte + off int +} + +func (w *limitedWriter) Write(bs []byte) (int, error) { + n := copy(w.buf[w.off:], bs) + if w.off += n; w.off == len(w.buf) { + panic(struct{}{}) + } + return n, nil +} + +func (w *limitedWriter) WriteByte(b byte) error { + w.buf[w.off] = b + if w.off++; w.off == len(w.buf) { + panic(struct{}{}) + } + return nil +} + +func (w *limitedWriter) WriteString(s string) (int, error) { + n := copy(w.buf[w.off:], s) + if w.off += n; w.off == len(w.buf) { + panic(struct{}{}) + } + return n, nil +} + +func (w *limitedWriter) Bytes() []byte { + return w.buf[:w.off] +} diff --git a/vendor/github.com/itchyny/gojq/query.go b/vendor/github.com/itchyny/gojq/query.go index 98b65027..90c165d2 100644 --- a/vendor/github.com/itchyny/gojq/query.go +++ b/vendor/github.com/itchyny/gojq/query.go @@ -2,8 +2,6 @@ package gojq import ( "context" - "encoding/json" - "strconv" "strings" ) @@ -21,13 +19,14 @@ type Query struct { // Run the query. // -// It is safe to call this method of a *Query in multiple goroutines. -func (e *Query) Run(v interface{}) Iter { +// It is safe to call this method in goroutines, to reuse a parsed [*Query]. +// But for arguments, do not give values sharing same data between goroutines. +func (e *Query) Run(v any) Iter { return e.RunWithContext(context.Background(), v) } // RunWithContext runs the query with context. -func (e *Query) RunWithContext(ctx context.Context, v interface{}) Iter { +func (e *Query) RunWithContext(ctx context.Context, v any) Iter { code, err := Compile(e) if err != nil { return NewIter(err) @@ -93,11 +92,18 @@ func (e *Query) minify() { } } -func (e *Query) toIndices() []interface{} { - if e.FuncDefs != nil || e.Right != nil || e.Term == nil { +func (e *Query) toIndexKey() any { + if e.Term == nil { return nil } - return e.Term.toIndices() + return e.Term.toIndexKey() +} + +func (e *Query) toIndices(xs []any) []any { + if e.Term == nil { + return nil + } + return e.Term.toIndices(xs) } // Import ... @@ -117,12 +123,12 @@ func (e *Import) String() string { func (e *Import) writeTo(s *strings.Builder) { if e.ImportPath != "" { s.WriteString("import ") - s.WriteString(strconv.Quote(e.ImportPath)) + jsonEncodeString(s, e.ImportPath) s.WriteString(" as ") s.WriteString(e.ImportAlias) } else { s.WriteString("include ") - s.WriteString(strconv.Quote(e.IncludePath)) + jsonEncodeString(s, e.IncludePath) } if e.Meta != nil { s.WriteByte(' ') @@ -308,25 +314,48 @@ func (e *Term) toFunc() string { } } -func (e *Term) toIndices() []interface{} { - if e.Index != nil { - xs := e.Index.toIndices() - if xs == nil { +func (e *Term) toIndexKey() any { + switch e.Type { + case TermTypeNumber: + return toNumber(e.Number) + case TermTypeUnary: + return e.Unary.toNumber() + case TermTypeString: + if e.Str.Queries == nil { + return e.Str.Str + } + return nil + default: + return nil + } +} + +func (e *Term) toIndices(xs []any) []any { + switch e.Type { + case TermTypeIndex: + if xs = e.Index.toIndices(xs); xs == nil { return nil } - for _, s := range e.SuffixList { - x := s.toIndices() - if x == nil { - return nil - } - xs = append(xs, x...) + case TermTypeQuery: + if xs = e.Query.toIndices(xs); xs == nil { + return nil } - return xs - } else if e.Query != nil && len(e.SuffixList) == 0 { - return e.Query.toIndices() - } else { + default: return nil } + for _, s := range e.SuffixList { + if xs = s.toIndices(xs); xs == nil { + return nil + } + } + return xs +} + +func (e *Term) toNumber() any { + if e.Type == TermTypeNumber { + return toNumber(e.Number) + } + return nil } // Unary ... @@ -350,6 +379,14 @@ func (e *Unary) minify() { e.Term.minify() } +func (e *Unary) toNumber() any { + v := e.Term.toNumber() + if v != nil && e.Op == OpSub { + v = funcOpNegate(v) + } + return v +} + // Pattern ... type Pattern struct { Name string @@ -393,7 +430,6 @@ type PatternObject struct { KeyString *String KeyQuery *Query Val *Pattern - KeyOnly string } func (e *PatternObject) String() string { @@ -416,9 +452,6 @@ func (e *PatternObject) writeTo(s *strings.Builder) { s.WriteString(": ") e.Val.writeTo(s) } - if e.KeyOnly != "" { - s.WriteString(e.KeyOnly) - } } // Index ... @@ -450,24 +483,22 @@ func (e *Index) writeTo(s *strings.Builder) { func (e *Index) writeSuffixTo(s *strings.Builder) { if e.Name != "" { s.WriteString(e.Name) + } else if e.Str != nil { + e.Str.writeTo(s) } else { - if e.Str != nil { - e.Str.writeTo(s) - } else { - s.WriteByte('[') - if e.IsSlice { - if e.Start != nil { - e.Start.writeTo(s) - } - s.WriteByte(':') - if e.End != nil { - e.End.writeTo(s) - } - } else { + s.WriteByte('[') + if e.IsSlice { + if e.Start != nil { e.Start.writeTo(s) } - s.WriteByte(']') + s.WriteByte(':') + if e.End != nil { + e.End.writeTo(s) + } + } else { + e.Start.writeTo(s) } + s.WriteByte(']') } } @@ -483,11 +514,38 @@ func (e *Index) minify() { } } -func (e *Index) toIndices() []interface{} { - if e.Name == "" { - return nil +func (e *Index) toIndexKey() any { + if e.Name != "" { + return e.Name + } else if e.Str != nil { + if e.Str.Queries == nil { + return e.Str.Str + } + } else if !e.IsSlice { + return e.Start.toIndexKey() + } else { + var start, end any + ok := true + if e.Start != nil { + start = e.Start.toIndexKey() + ok = start != nil + } + if e.End != nil && ok { + end = e.End.toIndexKey() + ok = end != nil + } + if ok { + return map[string]any{"start": start, "end": end} + } + } + return nil +} + +func (e *Index) toIndices(xs []any) []any { + if k := e.toIndexKey(); k != nil { + return append(xs, k) } - return []interface{}{e.Name} + return nil } // Func ... @@ -543,7 +601,7 @@ func (e *String) String() string { func (e *String) writeTo(s *strings.Builder) { if e.Queries == nil { - s.WriteString(strconv.Quote(e.Str)) + jsonEncodeString(s, e.Str) return } s.WriteByte('"') @@ -599,12 +657,10 @@ func (e *Object) minify() { // ObjectKeyVal ... type ObjectKeyVal struct { - Key string - KeyString *String - KeyQuery *Query - Val *ObjectVal - KeyOnly string - KeyOnlyString *String + Key string + KeyString *String + KeyQuery *Query + Val *ObjectVal } func (e *ObjectKeyVal) String() string { @@ -627,11 +683,6 @@ func (e *ObjectKeyVal) writeTo(s *strings.Builder) { s.WriteString(": ") e.Val.writeTo(s) } - if e.KeyOnly != "" { - s.WriteString(e.KeyOnly) - } else if e.KeyOnlyString != nil { - e.KeyOnlyString.writeTo(s) - } } func (e *ObjectKeyVal) minify() { @@ -643,9 +694,6 @@ func (e *ObjectKeyVal) minify() { if e.Val != nil { e.Val.minify() } - if e.KeyOnlyString != nil { - e.KeyOnlyString.minify() - } } // ObjectVal ... @@ -737,21 +785,21 @@ func (e *Suffix) minify() { } } -func (e *Suffix) toTerm() (*Term, bool) { +func (e *Suffix) toTerm() *Term { if e.Index != nil { - return &Term{Type: TermTypeIndex, Index: e.Index}, true + return &Term{Type: TermTypeIndex, Index: e.Index} } else if e.Iter { - return &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}, true + return &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}} } else { - return nil, false + return nil } } -func (e *Suffix) toIndices() []interface{} { +func (e *Suffix) toIndices(xs []any) []any { if e.Index == nil { return nil } - return e.Index.toIndices() + return e.Index.toIndices(xs) } // Bind ... @@ -1005,17 +1053,17 @@ func (e *ConstTerm) writeTo(s *strings.Builder) { } else if e.False { s.WriteString("false") } else { - s.WriteString(strconv.Quote(e.Str)) + jsonEncodeString(s, e.Str) } } -func (e *ConstTerm) toValue() interface{} { +func (e *ConstTerm) toValue() any { if e.Object != nil { return e.Object.ToValue() } else if e.Array != nil { return e.Array.toValue() } else if e.Number != "" { - return normalizeNumber(json.Number(e.Number)) + return toNumber(e.Number) } else if e.Null { return nil } else if e.True { @@ -1027,6 +1075,14 @@ func (e *ConstTerm) toValue() interface{} { } } +func (e *ConstTerm) toString() (string, bool) { + if e.Object != nil || e.Array != nil || + e.Number != "" || e.Null || e.True || e.False { + return "", false + } + return e.Str, true +} + // ConstObject ... type ConstObject struct { KeyVals []*ConstObjectKeyVal @@ -1053,12 +1109,12 @@ func (e *ConstObject) writeTo(s *strings.Builder) { s.WriteString(" }") } -// ToValue converts the object to map[string]interface{}. -func (e *ConstObject) ToValue() map[string]interface{} { +// ToValue converts the object to map[string]any. +func (e *ConstObject) ToValue() map[string]any { if e == nil { return nil } - v := make(map[string]interface{}, len(e.KeyVals)) + v := make(map[string]any, len(e.KeyVals)) for _, e := range e.KeyVals { key := e.Key if key == "" { @@ -1086,7 +1142,7 @@ func (e *ConstObjectKeyVal) writeTo(s *strings.Builder) { if e.Key != "" { s.WriteString(e.Key) } else { - s.WriteString(e.KeyString) + jsonEncodeString(s, e.KeyString) } s.WriteString(": ") e.Val.writeTo(s) @@ -1114,8 +1170,8 @@ func (e *ConstArray) writeTo(s *strings.Builder) { s.WriteByte(']') } -func (e *ConstArray) toValue() []interface{} { - v := make([]interface{}, len(e.Elems)) +func (e *ConstArray) toValue() []any { + v := make([]any, len(e.Elems)) for i, e := range e.Elems { v[i] = e.toValue() } diff --git a/vendor/github.com/itchyny/gojq/release.go b/vendor/github.com/itchyny/gojq/release.go index 196fb772..c34dfb45 100644 --- a/vendor/github.com/itchyny/gojq/release.go +++ b/vendor/github.com/itchyny/gojq/release.go @@ -1,11 +1,11 @@ -//go:build !debug -// +build !debug +//go:build !gojq_debug +// +build !gojq_debug package gojq type codeinfo struct{} -func (c *compiler) appendCodeInfo(interface{}) {} +func (c *compiler) appendCodeInfo(any) {} func (c *compiler) deleteCodeInfo(string) {} diff --git a/vendor/github.com/itchyny/gojq/scope_stack.go b/vendor/github.com/itchyny/gojq/scope_stack.go index 82d620be..e140ca15 100644 --- a/vendor/github.com/itchyny/gojq/scope_stack.go +++ b/vendor/github.com/itchyny/gojq/scope_stack.go @@ -39,11 +39,12 @@ func (s *scopeStack) empty() bool { return s.index < 0 } -func (s *scopeStack) save(index, limit *int) { - *index, *limit = s.index, s.limit +func (s *scopeStack) save() (index, limit int) { + index, limit = s.index, s.limit if s.index > s.limit { s.limit = s.index } + return } func (s *scopeStack) restore(index, limit int) { diff --git a/vendor/github.com/itchyny/gojq/stack.go b/vendor/github.com/itchyny/gojq/stack.go index f629d28e..a0e265c8 100644 --- a/vendor/github.com/itchyny/gojq/stack.go +++ b/vendor/github.com/itchyny/gojq/stack.go @@ -7,7 +7,7 @@ type stack struct { } type block struct { - value interface{} + value any next int } @@ -15,7 +15,7 @@ func newStack() *stack { return &stack{index: -1, limit: -1} } -func (s *stack) push(v interface{}) { +func (s *stack) push(v any) { b := block{v, s.index} i := s.index + 1 if i <= s.limit { @@ -29,13 +29,13 @@ func (s *stack) push(v interface{}) { } } -func (s *stack) pop() interface{} { +func (s *stack) pop() any { b := s.data[s.index] s.index = b.next return b.value } -func (s *stack) top() interface{} { +func (s *stack) top() any { return s.data[s.index].value } @@ -43,11 +43,12 @@ func (s *stack) empty() bool { return s.index < 0 } -func (s *stack) save(index, limit *int) { - *index, *limit = s.index, s.limit +func (s *stack) save() (index, limit int) { + index, limit = s.index, s.limit if s.index > s.limit { s.limit = s.index } + return } func (s *stack) restore(index, limit int) { diff --git a/vendor/github.com/itchyny/gojq/term_type.go b/vendor/github.com/itchyny/gojq/term_type.go index 1670948c..941e7ba9 100644 --- a/vendor/github.com/itchyny/gojq/term_type.go +++ b/vendor/github.com/itchyny/gojq/term_type.go @@ -1,6 +1,6 @@ package gojq -// TermType represents the type of Term. +// TermType represents the type of [Term]. type TermType int // TermType list. @@ -27,7 +27,7 @@ const ( TermTypeQuery ) -// GoString implements GoStringer. +// GoString implements [fmt.GoStringer]. func (termType TermType) GoString() (str string) { defer func() { str = "gojq." + str }() switch termType { diff --git a/vendor/github.com/itchyny/gojq/type.go b/vendor/github.com/itchyny/gojq/type.go new file mode 100644 index 00000000..bb388e20 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/type.go @@ -0,0 +1,29 @@ +package gojq + +import ( + "fmt" + "math/big" +) + +// TypeOf returns the jq-flavored type name of v. +// +// This method is used by built-in type/0 function, and accepts only limited +// types (nil, bool, int, float64, *big.Int, string, []any, and map[string]any). +func TypeOf(v any) string { + switch v.(type) { + case nil: + return "null" + case bool: + return "boolean" + case int, float64, *big.Int: + return "number" + case string: + return "string" + case []any: + return "array" + case map[string]any: + return "object" + default: + panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v)) + } +} diff --git a/vendor/github.com/itchyny/timefmt-go/CHANGELOG.md b/vendor/github.com/itchyny/timefmt-go/CHANGELOG.md index 52b37d05..61a4e9dc 100644 --- a/vendor/github.com/itchyny/timefmt-go/CHANGELOG.md +++ b/vendor/github.com/itchyny/timefmt-go/CHANGELOG.md @@ -1,4 +1,11 @@ # Changelog +## [v0.1.5](https://github.com/itchyny/timefmt-go/compare/v0.1.4..v0.1.5) (2022-12-01) +* support parsing time zone offset with name using both `%z` and `%Z` + +## [v0.1.4](https://github.com/itchyny/timefmt-go/compare/v0.1.3..v0.1.4) (2022-09-01) +* improve documents +* drop support for Go 1.16 + ## [v0.1.3](https://github.com/itchyny/timefmt-go/compare/v0.1.2..v0.1.3) (2021-04-14) * implement `ParseInLocation` for configuring the default location diff --git a/vendor/github.com/itchyny/timefmt-go/LICENSE b/vendor/github.com/itchyny/timefmt-go/LICENSE index 4d650fa8..84d6cb03 100644 --- a/vendor/github.com/itchyny/timefmt-go/LICENSE +++ b/vendor/github.com/itchyny/timefmt-go/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020,2021 itchyny +Copyright (c) 2020-2022 itchyny Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/itchyny/timefmt-go/Makefile b/vendor/github.com/itchyny/timefmt-go/Makefile index bacef7bf..a87cb286 100644 --- a/vendor/github.com/itchyny/timefmt-go/Makefile +++ b/vendor/github.com/itchyny/timefmt-go/Makefile @@ -1,20 +1,19 @@ GOBIN ?= $(shell go env GOPATH)/bin -export GO111MODULE=on .PHONY: all all: test .PHONY: test test: - go test -v ./... + go test -v -race ./... .PHONY: lint -lint: $(GOBIN)/golint +lint: $(GOBIN)/staticcheck go vet ./... - golint -set_exit_status ./... + staticcheck -checks all,-ST1000 ./... -$(GOBIN)/golint: - cd && go get golang.org/x/lint/golint +$(GOBIN)/staticcheck: + go install honnef.co/go/tools/cmd/staticcheck@latest .PHONY: clean clean: diff --git a/vendor/github.com/itchyny/timefmt-go/README.md b/vendor/github.com/itchyny/timefmt-go/README.md index 078b1e1a..f01af961 100644 --- a/vendor/github.com/itchyny/timefmt-go/README.md +++ b/vendor/github.com/itchyny/timefmt-go/README.md @@ -1,7 +1,7 @@ # timefmt-go [![CI Status](https://github.com/itchyny/timefmt-go/workflows/CI/badge.svg)](https://github.com/itchyny/timefmt-go/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/itchyny/timefmt-go)](https://goreportcard.com/report/github.com/itchyny/timefmt-go) -[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchyny/timefmt-go/blob/main/LICENSE) +[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchyny/timefmt-go/blob/main/LICENSE) [![release](https://img.shields.io/github/release/itchyny/timefmt-go/all.svg)](https://github.com/itchyny/timefmt-go/releases) [![pkg.go.dev](https://pkg.go.dev/badge/github.com/itchyny/timefmt-go)](https://pkg.go.dev/github.com/itchyny/timefmt-go) @@ -35,14 +35,15 @@ func main() { Please refer to [`man 3 strftime`](https://linux.die.net/man/3/strftime) and [`man 3 strptime`](https://linux.die.net/man/3/strptime) for formatters. +As an extension, `%f` directive is supported for zero-padded microseconds, which originates from Python. Note that `E` and `O` modifier characters are not supported. ## Comparison to other libraries - This library - provides both formatting and parsing functions in pure Go language, - - depends only on the Go standard libraries not to grows up the module file. + - depends only on the Go standard libraries not to grow up dependency. - `Format` (`strftime`) implements glibc extensions including - - width specifier like `%10A, %10B %2k:%M`, + - width specifier like `%6Y %10B %4Z` (limited to 1024 bytes), - omitting padding modifier like `%-y-%-m-%-d`, - space padding modifier like `%_y-%_m-%_d`, - upper case modifier like `%^a %^b`, diff --git a/vendor/github.com/itchyny/timefmt-go/format.go b/vendor/github.com/itchyny/timefmt-go/format.go index 1d9b3672..eea976ee 100644 --- a/vendor/github.com/itchyny/timefmt-go/format.go +++ b/vendor/github.com/itchyny/timefmt-go/format.go @@ -1,6 +1,7 @@ package timefmt import ( + "math" "strconv" "time" ) @@ -10,8 +11,7 @@ func Format(t time.Time, format string) string { return string(AppendFormat(make([]byte, 0, 64), t, format)) } -// AppendFormat appends formatted time to the bytes. -// You can use this method to reduce allocations. +// AppendFormat appends formatted time string to the buffer. func AppendFormat(buf []byte, t time.Time, format string) []byte { year, month, day := t.Date() hour, min, sec := t.Clock() @@ -74,7 +74,7 @@ func AppendFormat(buf []byte, t time.Time, format string) []byte { b = format[i] if b <= '9' && '0' <= b { width = width*10 + int(b&0x0F) - if width >= int((^uint(0)>>1)/10) { + if width >= math.MaxInt/10 { width = maxWidth } } else { diff --git a/vendor/github.com/itchyny/timefmt-go/parse.go b/vendor/github.com/itchyny/timefmt-go/parse.go index 2d2b5f4d..83b0df2c 100644 --- a/vendor/github.com/itchyny/timefmt-go/parse.go +++ b/vendor/github.com/itchyny/timefmt-go/parse.go @@ -25,7 +25,7 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er } }() var j, century, yday, colons int - var pm bool + var pm, hasZoneName, hasZoneOffset bool var pending string for i, l := 0, len(source); i < len(format); i++ { if b := format[i]; b == '%' { @@ -158,14 +158,14 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er hour, min, sec = t.Clock() month = int(mon) case 'f': - var msec, k, d int - if msec, k, err = parseNumber(source, j, 6, 'f'); err != nil { + var usec, k, d int + if usec, k, err = parseNumber(source, j, 6, 'f'); err != nil { return } - nsec = msec * 1000 for j, d = k, k-j; d < 6; d++ { - nsec *= 10 + usec *= 10 } + nsec = usec * 1000 case 'Z': k := j for ; k < l; k++ { @@ -178,7 +178,14 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er err = fmt.Errorf(`cannot parse %q with "%%Z"`, source[j:k]) return } - loc = t.Location() + if hasZoneOffset { + name, _ := t.Zone() + _, offset := locationZone(loc) + loc = time.FixedZone(name, offset) + } else { + loc = t.Location() + } + hasZoneName = true j = k case 'z': if j >= l { @@ -231,7 +238,12 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er j = k } } - loc, colons = time.FixedZone("", sign*((hour*60+min)*60+sec)), 0 + var name string + if hasZoneName { + name, _ = locationZone(loc) + } + loc, colons = time.FixedZone(name, sign*((hour*60+min)*60+sec)), 0 + hasZoneOffset = true case 'Z': loc, colons, j = time.UTC, 0, j+1 default: @@ -328,6 +340,10 @@ func parse(source, format string, loc, base *time.Location) (t time.Time, err er return time.Date(year, time.Month(month), day, hour, min, sec, nsec, loc), nil } +func locationZone(loc *time.Location) (name string, offset int) { + return time.Date(2000, time.January, 1, 0, 0, 0, 0, loc).Zone() +} + type parseFormatError byte func (err parseFormatError) Error() string { diff --git a/vendor/github.com/itchyny/timefmt-go/timefmt.go b/vendor/github.com/itchyny/timefmt-go/timefmt.go new file mode 100644 index 00000000..45bf6ae9 --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/timefmt.go @@ -0,0 +1,2 @@ +// Package timefmt provides functions for formatting and parsing date time strings. +package timefmt diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE b/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE deleted file mode 100644 index 14127cd8..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -(The MIT License) - -Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md deleted file mode 100644 index 949b77e3..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Windows Terminal Sequences - -This library allow for enabling Windows terminal color support for Go. - -See [Console Virtual Terminal Sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) for details. - -## Usage - -```go -import ( - "syscall" - - sequences "github.com/konsorten/go-windows-terminal-sequences" -) - -func main() { - sequences.EnableVirtualTerminalProcessing(syscall.Stdout, true) -} - -``` - -## Authors - -The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de). - -We thank all the authors who provided code to this library: - -* Felix Kollmann - -## License - -(The MIT License) - -Copyright (c) 2018 marvin + konsorten GmbH (open-source@konsorten.de) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go deleted file mode 100644 index ef18d8f9..00000000 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build windows - -package sequences - -import ( - "syscall" - "unsafe" -) - -var ( - kernel32Dll *syscall.LazyDLL = syscall.NewLazyDLL("Kernel32.dll") - setConsoleMode *syscall.LazyProc = kernel32Dll.NewProc("SetConsoleMode") -) - -func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { - const ENABLE_VIRTUAL_TERMINAL_PROCESSING uint32 = 0x4 - - var mode uint32 - err := syscall.GetConsoleMode(syscall.Stdout, &mode) - if err != nil { - return err - } - - if enable { - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING - } else { - mode &^= ENABLE_VIRTUAL_TERMINAL_PROCESSING - } - - ret, _, err := setConsoleMode.Call(uintptr(unsafe.Pointer(stream)), uintptr(mode)) - if ret == 0 { - return err - } - - return nil -} diff --git a/vendor/github.com/lithammer/fuzzysearch/fuzzy/fuzzy.go b/vendor/github.com/lithammer/fuzzysearch/fuzzy/fuzzy.go index 7ae7091f..88908773 100644 --- a/vendor/github.com/lithammer/fuzzysearch/fuzzy/fuzzy.go +++ b/vendor/github.com/lithammer/fuzzysearch/fuzzy/fuzzy.go @@ -3,7 +3,6 @@ package fuzzy import ( - "bytes" "unicode" "unicode/utf8" @@ -53,9 +52,12 @@ func MatchNormalizedFold(source, target string) bool { } func match(source, target string, transformer transform.Transformer) bool { - source = stringTransform(source, transformer) - target = stringTransform(target, transformer) + sourceT := stringTransform(source, transformer) + targetT := stringTransform(target, transformer) + return matchTransformed(sourceT, targetT) +} +func matchTransformed(source, target string) bool { lenDiff := len(target) - len(source) if lenDiff < 0 { @@ -101,10 +103,13 @@ func FindNormalizedFold(source string, targets []string) []string { } func find(source string, targets []string, transformer transform.Transformer) []string { + sourceT := stringTransform(source, transformer) + var matches []string for _, target := range targets { - if match(source, target, transformer) { + targetT := stringTransform(target, transformer) + if matchTransformed(sourceT, targetT) { matches = append(matches, target) } } @@ -194,10 +199,13 @@ func RankFindNormalizedFold(source string, targets []string) Ranks { } func rankFind(source string, targets []string, transformer transform.Transformer) Ranks { + sourceT := stringTransform(source, transformer) + var r Ranks for index, target := range targets { - if match(source, target, transformer) { + targetT := stringTransform(target, transformer) + if matchTransformed(sourceT, targetT) { distance := LevenshteinDistance(source, target) r = append(r, Rank{source, target, distance, index}) } @@ -251,19 +259,30 @@ func stringTransform(s string, t transform.Transformer) (transformed string) { type unicodeFoldTransformer struct{ transform.NopResetter } func (unicodeFoldTransformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { - runes := bytes.Runes(src) - var lowerRunes []rune - for _, r := range runes { - lowerRunes = append(lowerRunes, unicode.ToLower(r)) - } - - srcBytes := []byte(string(lowerRunes)) - n := copy(dst, srcBytes) - if n < len(srcBytes) { - err = transform.ErrShortDst + // Converting src to a string allocates. + // In theory, it need not; see https://go.dev/issue/27148. + // It is possible to write this loop using utf8.DecodeRune + // and thereby avoid allocations, but it is noticeably slower. + // So just let's wait for the compiler to get smarter. + for _, r := range string(src) { + if r == utf8.RuneError { + // Go spec for ranging over a string says: + // If the iteration encounters an invalid UTF-8 sequence, + // the second value will be 0xFFFD, the Unicode replacement character, + // and the next iteration will advance a single byte in the string. + nSrc++ + } else { + nSrc += utf8.RuneLen(r) + } + r = unicode.ToLower(r) + x := utf8.RuneLen(r) + if x > len(dst[nDst:]) { + err = transform.ErrShortDst + break + } + nDst += utf8.EncodeRune(dst[nDst:], r) } - - return n, n, err + return nDst, nSrc, err } type nopTransformer struct{ transform.NopResetter } diff --git a/vendor/github.com/lithammer/fuzzysearch/fuzzy/levenshtein.go b/vendor/github.com/lithammer/fuzzysearch/fuzzy/levenshtein.go index 4fb5838c..c0fc1910 100644 --- a/vendor/github.com/lithammer/fuzzysearch/fuzzy/levenshtein.go +++ b/vendor/github.com/lithammer/fuzzysearch/fuzzy/levenshtein.go @@ -33,11 +33,13 @@ func LevenshteinDistance(s, t string) int { return column[len(r1)] } -func min(a, b, c int) int { - if a < b && a < c { +func min2(a, b int) int { + if a < b { return a - } else if b < c { - return b } - return c + return b +} + +func min(a, b, c int) int { + return min2(min2(a, b), c) } diff --git a/vendor/github.com/logrusorgru/aurora/.gitignore b/vendor/github.com/logrusorgru/aurora/.gitignore deleted file mode 100644 index dbcb7cc7..00000000 --- a/vendor/github.com/logrusorgru/aurora/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof -*.out - -# coverage - -cover.html - -# benchcmp - -*.cmp - diff --git a/vendor/github.com/logrusorgru/aurora/.travis.yml b/vendor/github.com/logrusorgru/aurora/.travis.yml deleted file mode 100644 index 570e361b..00000000 --- a/vendor/github.com/logrusorgru/aurora/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go -go: - - tip -before_install: - - go get github.com/axw/gocov/gocov - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover -script: - - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/logrusorgru/aurora/AUTHORS.md b/vendor/github.com/logrusorgru/aurora/AUTHORS.md deleted file mode 100644 index 0ee9e3eb..00000000 --- a/vendor/github.com/logrusorgru/aurora/AUTHORS.md +++ /dev/null @@ -1,8 +0,0 @@ -AUTHORS -======= - -- Konstantin Ivanov @logrusorgru -- Mattias Eriksson @snaggen -- Ousmane Traore @otraore -- Simon Legner @simon04 -- Sevenate @sevenate diff --git a/vendor/github.com/logrusorgru/aurora/CHANGELOG.md b/vendor/github.com/logrusorgru/aurora/CHANGELOG.md deleted file mode 100644 index ad0a2025..00000000 --- a/vendor/github.com/logrusorgru/aurora/CHANGELOG.md +++ /dev/null @@ -1,59 +0,0 @@ -Changes -======= - ---- -16:05:02 -Thursday, July 2, 2020 - -Change license from the WTFPL to the Unlicense due to pkg.go.dev restriction. - ---- -15:39:40 -Wednesday, April 17, 2019 - -- Bright background and foreground colors -- 8-bit indexed colors `Index`, `BgIndex` -- 24 grayscale colors `Gray`, `BgGray` -- `Yellow` and `BgYellow` methods, mark Brow and BgBrown as deprecated - Following specifications, correct name of the colors are yellow, but - by historical reason they are called brown. Both, the `Yellow` and the - `Brown` methods (including `Bg+`) represents the same colors. The Brown - are leaved for backward compatibility until Go modules production release. -- Additional formats - + `Faint` that is opposite to the `Bold` - + `DoublyUnderline` - + `Fraktur` - + `Italic` - + `Underline` - + `SlowBlink` with `Blink` alias - + `RapidBlink` - + `Reverse` that is alias for the `Inverse` - + `Conceal` with `Hidden` alias - + `CrossedOut` with `StrikeThrough` alias - + `Framed` - + `Encircled` - + `Overlined` -- Add AUTHORS.md file and change all copyright notices. -- `Reset` method to create clear value. `Reset` method that replaces - `Bleach` method. The `Bleach` method was marked as deprecated. - ---- - -14:25:49 -Friday, August 18, 2017 - -- LICENSE.md changed to LICENSE -- fix email in README.md -- add "no warranty" to README.md -- set proper copyright date - ---- - -16:59:28 -Tuesday, November 8, 2016 - -- Rid out off sync.Pool -- Little optimizations (very little) -- Improved benchmarks - ---- diff --git a/vendor/github.com/logrusorgru/aurora/LICENSE b/vendor/github.com/logrusorgru/aurora/LICENSE deleted file mode 100644 index 68a49daa..00000000 --- a/vendor/github.com/logrusorgru/aurora/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to diff --git a/vendor/github.com/logrusorgru/aurora/README.md b/vendor/github.com/logrusorgru/aurora/README.md deleted file mode 100644 index e0afce1c..00000000 --- a/vendor/github.com/logrusorgru/aurora/README.md +++ /dev/null @@ -1,314 +0,0 @@ -Aurora -====== - -[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/logrusorgru/aurora?tab=doc) -[![Unlicense](https://img.shields.io/badge/license-unlicense-blue.svg)](http://unlicense.org/) -[![Build Status](https://travis-ci.org/logrusorgru/aurora.svg)](https://travis-ci.org/logrusorgru/aurora) -[![Coverage Status](https://coveralls.io/repos/logrusorgru/aurora/badge.svg?branch=master)](https://coveralls.io/r/logrusorgru/aurora?branch=master) -[![GoReportCard](https://goreportcard.com/badge/logrusorgru/aurora)](https://goreportcard.com/report/logrusorgru/aurora) -[![Gitter](https://img.shields.io/badge/chat-on_gitter-46bc99.svg?logo=data:image%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTQiIHdpZHRoPSIxNCI%2BPGcgZmlsbD0iI2ZmZiI%2BPHJlY3QgeD0iMCIgeT0iMyIgd2lkdGg9IjEiIGhlaWdodD0iNSIvPjxyZWN0IHg9IjIiIHk9IjQiIHdpZHRoPSIxIiBoZWlnaHQ9IjciLz48cmVjdCB4PSI0IiB5PSI0IiB3aWR0aD0iMSIgaGVpZ2h0PSI3Ii8%2BPHJlY3QgeD0iNiIgeT0iNCIgd2lkdGg9IjEiIGhlaWdodD0iNCIvPjwvZz48L3N2Zz4%3D&logoWidth=10)](https://gitter.im/logrusorgru/aurora) - -Ultimate ANSI colors for Golang. The package supports Printf/Sprintf etc. - - -![aurora logo](https://github.com/logrusorgru/aurora/blob/master/gopher_aurora.png) - -# TOC - -- [Installation](#installation) -- [Usage](#usage) - + [Simple](#simple) - + [Printf](#printf) - + [aurora.Sprintf](#aurorasprintf) - + [Enable/Disable colors](#enabledisable-colors) -- [Chains](#chains) -- [Colorize](#colorize) -- [Grayscale](#grayscale) -- [8-bit colors](#8-bit-colors) -- [Supported Colors & Formats](#supported-colors--formats) - + [All colors](#all-colors) - + [Standard and bright colors](#standard-and-bright-colors) - + [Formats are likely supported](#formats-are-likely-supported) - + [Formats are likely unsupported](#formats-are-likely-unsupported) -- [Limitations](#limitations) - + [Windows](#windows) - + [TTY](#tty) -- [Licensing](#licensing) - -# Installation - -Get -``` -go get -u github.com/logrusorgru/aurora -``` -Test -``` -go test -cover github.com/logrusorgru/aurora -``` - -# Usage - -### Simple - -```go -package main - -import ( - "fmt" - - . "github.com/logrusorgru/aurora" -) - -func main() { - fmt.Println("Hello,", Magenta("Aurora")) - fmt.Println(Bold(Cyan("Cya!"))) -} - -``` - -![simple png](https://github.com/logrusorgru/aurora/blob/master/simple.png) - -### Printf - -```go -package main - -import ( - "fmt" - - . "github.com/logrusorgru/aurora" -) - -func main() { - fmt.Printf("Got it %d times\n", Green(1240)) - fmt.Printf("PI is %+1.2e\n", Cyan(3.14)) -} - -``` - -![printf png](https://github.com/logrusorgru/aurora/blob/master/printf.png) - -### aurora.Sprintf - -```go -package main - -import ( - "fmt" - - . "github.com/logrusorgru/aurora" -) - -func main() { - fmt.Println(Sprintf(Magenta("Got it %d times"), Green(1240))) -} - -``` - -![sprintf png](https://github.com/logrusorgru/aurora/blob/master/sprintf.png) - -### Enable/Disable colors - -```go -package main - -import ( - "fmt" - "flag" - - "github.com/logrusorgru/aurora" -) - -// colorizer -var au aurora.Aurora - -var colors = flag.Bool("colors", false, "enable or disable colors") - -func init() { - flag.Parse() - au = aurora.NewAurora(*colors) -} - -func main() { - // use colorizer - fmt.Println(au.Green("Hello")) -} - -``` -Without flags: -![disable png](https://github.com/logrusorgru/aurora/blob/master/disable.png) - -With `-colors` flag: -![enable png](https://github.com/logrusorgru/aurora/blob/master/enable.png) - -# Chains - -The following samples are equal - -```go -x := BgMagenta(Bold(Red("x"))) -``` - -```go -x := Red("x").Bold().BgMagenta() -``` - -The second is more readable - -# Colorize - -There is `Colorize` function that allows to choose some colors and -format from a side - -```go - -func getColors() Color { - // some stuff that returns appropriate colors and format -} - -// [...] - -func main() { - fmt.Println(Colorize("Greeting", getColors())) -} - -``` -Less complicated example - -```go -x := Colorize("Greeting", GreenFg|GrayBg|BoldFm) -``` - -Unlike other color functions and methods (such as Red/BgBlue etc) -a `Colorize` clears previous colors - -```go -x := Red("x").Colorize(BgGreen) // will be with green background only -``` - -# Grayscale - -```go -fmt.Println(" ", - Gray(1-1, " 00-23 ").BgGray(24-1), - Gray(4-1, " 03-19 ").BgGray(20-1), - Gray(8-1, " 07-15 ").BgGray(16-1), - Gray(12-1, " 11-11 ").BgGray(12-1), - Gray(16-1, " 15-07 ").BgGray(8-1), - Gray(20-1, " 19-03 ").BgGray(4-1), - Gray(24-1, " 23-00 ").BgGray(1-1), -) -``` - -![grayscale png](https://github.com/logrusorgru/aurora/blob/master/aurora_grayscale.png) - -# 8-bit colors - -Methods `Index` and `BgIndex` implements 8-bit colors. - -| Index/BgIndex | Meaning | Foreground | Background | -| -------------- | --------------- | ---------- | ---------- | -| 0- 7 | standard colors | 30- 37 | 40- 47 | -| 8- 15 | bright colors | 90- 97 | 100-107 | -| 16-231 | 216 colors | 38;5;n | 48;5;n | -| 232-255 | 24 grayscale | 38;5;n | 48;5;n | - - -# Supported colors & formats - -- formats - + bold (1) - + faint (2) - + doubly-underline (21) - + fraktur (20) - + italic (3) - + underline (4) - + slow blink (5) - + rapid blink (6) - + reverse video (7) - + conceal (8) - + crossed out (9) - + framed (51) - + encircled (52) - + overlined (53) -- background and foreground colors, including bright - + black - + red - + green - + yellow (brown) - + blue - + magenta - + cyan - + white - + 24 grayscale colors - + 216 8-bit colors - -### All colors - -![linux png](https://github.com/logrusorgru/aurora/blob/master/aurora_colors_black.png) -![white png](https://github.com/logrusorgru/aurora/blob/master/aurora_colors_white.png) - -### Standard and bright colors - -![linux black standard png](https://github.com/logrusorgru/aurora/blob/master/aurora_black_standard.png) -![linux white standard png](https://github.com/logrusorgru/aurora/blob/master/aurora_white_standard.png) - -### Formats are likely supported - -![formats supported gif](https://github.com/logrusorgru/aurora/blob/master/aurora_formats.gif) - -### Formats are likely unsupported - -![formats rarely supported png](https://github.com/logrusorgru/aurora/blob/master/aurora_rarely_supported.png) - -# Limitations - -There is no way to represent `%T` and `%p` with colors using -a standard approach - -```go -package main - -import ( - "fmt" - - . "github.com/logrusorgru/aurora" -) - -func main() { - r := Red("red") - var i int - fmt.Printf("%T %p\n", r, Green(&i)) -} -``` - -Output will be without colors - -``` -aurora.value %!p(aurora.value={0xc42000a310 768 0}) -``` - -The obvious workaround is `Red(fmt.Sprintf("%T", some))` - -### Windows - -The Aurora provides ANSI colors only, so there is no support for Windows. That said, there are workarounds available. -Check out these comments to learn more: - -- [Using go-colorable](https://github.com/logrusorgru/aurora/issues/2#issuecomment-299014211). -- [Using registry for Windows 10](https://github.com/logrusorgru/aurora/issues/10#issue-476361247). - -### TTY - -The Aurora has no internal TTY detectors by design. Take a look - [this comment](https://github.com/logrusorgru/aurora/issues/2#issuecomment-299030108) if you want turn -on colors for a terminal only, and turn them off for a file. - -### Licensing - -Copyright © 2016-2020 The Aurora Authors. This work is free. -It comes without any warranty, to the extent permitted by applicable -law. You can redistribute it and/or modify it under the terms of the -the Unlicense. See the LICENSE file for more details. - - diff --git a/vendor/github.com/logrusorgru/aurora/aurora.go b/vendor/github.com/logrusorgru/aurora/aurora.go deleted file mode 100644 index 3b302305..00000000 --- a/vendor/github.com/logrusorgru/aurora/aurora.go +++ /dev/null @@ -1,725 +0,0 @@ -// -// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. -// This program is free software. It comes without any warranty, -// to the extent permitted by applicable law. You can redistribute -// it and/or modify it under the terms of the Unlicense. See LICENSE -// file for more details or see below. -// - -// -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. -// -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -// - -// Package aurora implements ANSI-colors -package aurora - -import ( - "fmt" -) - -// An Aurora implements colorizer interface. -// It also can be a non-colorizer -type Aurora interface { - - // Reset wraps given argument returning Value - // without formats and colors. - Reset(arg interface{}) Value - - // - // Formats - // - // - // Bold or increased intensity (1). - Bold(arg interface{}) Value - // Faint, decreased intensity (2). - Faint(arg interface{}) Value - // - // DoublyUnderline or Bold off, double-underline - // per ECMA-48 (21). - DoublyUnderline(arg interface{}) Value - // Fraktur, rarely supported (20). - Fraktur(arg interface{}) Value - // - // Italic, not widely supported, sometimes - // treated as inverse (3). - Italic(arg interface{}) Value - // Underline (4). - Underline(arg interface{}) Value - // - // SlowBlink, blinking less than 150 - // per minute (5). - SlowBlink(arg interface{}) Value - // RapidBlink, blinking 150+ per minute, - // not widely supported (6). - RapidBlink(arg interface{}) Value - // Blink is alias for the SlowBlink. - Blink(arg interface{}) Value - // - // Reverse video, swap foreground and - // background colors (7). - Reverse(arg interface{}) Value - // Inverse is alias for the Reverse - Inverse(arg interface{}) Value - // - // Conceal, hidden, not widely supported (8). - Conceal(arg interface{}) Value - // Hidden is alias for the Conceal - Hidden(arg interface{}) Value - // - // CrossedOut, characters legible, but - // marked for deletion (9). - CrossedOut(arg interface{}) Value - // StrikeThrough is alias for the CrossedOut. - StrikeThrough(arg interface{}) Value - // - // Framed (51). - Framed(arg interface{}) Value - // Encircled (52). - Encircled(arg interface{}) Value - // - // Overlined (53). - Overlined(arg interface{}) Value - - // - // Foreground colors - // - // - // Black foreground color (30) - Black(arg interface{}) Value - // Red foreground color (31) - Red(arg interface{}) Value - // Green foreground color (32) - Green(arg interface{}) Value - // Yellow foreground color (33) - Yellow(arg interface{}) Value - // Brown foreground color (33) - // - // Deprecated: use Yellow instead, following specification - Brown(arg interface{}) Value - // Blue foreground color (34) - Blue(arg interface{}) Value - // Magenta foreground color (35) - Magenta(arg interface{}) Value - // Cyan foreground color (36) - Cyan(arg interface{}) Value - // White foreground color (37) - White(arg interface{}) Value - // - // Bright foreground colors - // - // BrightBlack foreground color (90) - BrightBlack(arg interface{}) Value - // BrightRed foreground color (91) - BrightRed(arg interface{}) Value - // BrightGreen foreground color (92) - BrightGreen(arg interface{}) Value - // BrightYellow foreground color (93) - BrightYellow(arg interface{}) Value - // BrightBlue foreground color (94) - BrightBlue(arg interface{}) Value - // BrightMagenta foreground color (95) - BrightMagenta(arg interface{}) Value - // BrightCyan foreground color (96) - BrightCyan(arg interface{}) Value - // BrightWhite foreground color (97) - BrightWhite(arg interface{}) Value - // - // Other - // - // Index of pre-defined 8-bit foreground color - // from 0 to 255 (38;5;n). - // - // 0- 7: standard colors (as in ESC [ 30–37 m) - // 8- 15: high intensity colors (as in ESC [ 90–97 m) - // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) - // 232-255: grayscale from black to white in 24 steps - // - Index(n uint8, arg interface{}) Value - // Gray from 0 to 23. - Gray(n uint8, arg interface{}) Value - - // - // Background colors - // - // - // BgBlack background color (40) - BgBlack(arg interface{}) Value - // BgRed background color (41) - BgRed(arg interface{}) Value - // BgGreen background color (42) - BgGreen(arg interface{}) Value - // BgYellow background color (43) - BgYellow(arg interface{}) Value - // BgBrown background color (43) - // - // Deprecated: use BgYellow instead, following specification - BgBrown(arg interface{}) Value - // BgBlue background color (44) - BgBlue(arg interface{}) Value - // BgMagenta background color (45) - BgMagenta(arg interface{}) Value - // BgCyan background color (46) - BgCyan(arg interface{}) Value - // BgWhite background color (47) - BgWhite(arg interface{}) Value - // - // Bright background colors - // - // BgBrightBlack background color (100) - BgBrightBlack(arg interface{}) Value - // BgBrightRed background color (101) - BgBrightRed(arg interface{}) Value - // BgBrightGreen background color (102) - BgBrightGreen(arg interface{}) Value - // BgBrightYellow background color (103) - BgBrightYellow(arg interface{}) Value - // BgBrightBlue background color (104) - BgBrightBlue(arg interface{}) Value - // BgBrightMagenta background color (105) - BgBrightMagenta(arg interface{}) Value - // BgBrightCyan background color (106) - BgBrightCyan(arg interface{}) Value - // BgBrightWhite background color (107) - BgBrightWhite(arg interface{}) Value - // - // Other - // - // BgIndex of 8-bit pre-defined background color - // from 0 to 255 (48;5;n). - // - // 0- 7: standard colors (as in ESC [ 40–47 m) - // 8- 15: high intensity colors (as in ESC [100–107 m) - // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) - // 232-255: grayscale from black to white in 24 steps - // - BgIndex(n uint8, arg interface{}) Value - // BgGray from 0 to 23. - BgGray(n uint8, arg interface{}) Value - - // - // Special - // - // Colorize removes existing colors and - // formats of the argument and applies given. - Colorize(arg interface{}, color Color) Value - - // - // Support methods - // - // Sprintf allows to use colored format. - Sprintf(format interface{}, args ...interface{}) string -} - -// NewAurora returns a new Aurora interface that -// will support or not support colors depending -// the enableColors argument -func NewAurora(enableColors bool) Aurora { - if enableColors { - return aurora{} - } - return auroraClear{} -} - -// no colors - -type auroraClear struct{} - -func (auroraClear) Reset(arg interface{}) Value { return valueClear{arg} } - -func (auroraClear) Bold(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Faint(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) DoublyUnderline(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Fraktur(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Italic(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Underline(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) SlowBlink(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) RapidBlink(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Blink(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Reverse(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Inverse(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Conceal(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Hidden(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) CrossedOut(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) StrikeThrough(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Framed(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Encircled(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Overlined(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Black(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Red(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Green(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Yellow(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Brown(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Blue(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Magenta(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Cyan(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) White(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightBlack(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightRed(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightGreen(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightYellow(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightBlue(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightMagenta(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightCyan(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BrightWhite(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Index(_ uint8, arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Gray(_ uint8, arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBlack(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgRed(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgGreen(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgYellow(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrown(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBlue(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgMagenta(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgCyan(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgWhite(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightBlack(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightRed(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightGreen(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightYellow(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightBlue(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightMagenta(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightCyan(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgBrightWhite(arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgIndex(_ uint8, arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) BgGray(_ uint8, arg interface{}) Value { - return valueClear{arg} -} - -func (auroraClear) Colorize(arg interface{}, _ Color) Value { - return valueClear{arg} -} - -func (auroraClear) Sprintf(format interface{}, args ...interface{}) string { - if str, ok := format.(string); ok { - return fmt.Sprintf(str, args...) - } - return fmt.Sprintf(fmt.Sprint(format), args...) -} - -// colorized - -type aurora struct{} - -func (aurora) Reset(arg interface{}) Value { - return Reset(arg) -} - -func (aurora) Bold(arg interface{}) Value { - return Bold(arg) -} - -func (aurora) Faint(arg interface{}) Value { - return Faint(arg) -} - -func (aurora) DoublyUnderline(arg interface{}) Value { - return DoublyUnderline(arg) -} - -func (aurora) Fraktur(arg interface{}) Value { - return Fraktur(arg) -} - -func (aurora) Italic(arg interface{}) Value { - return Italic(arg) -} - -func (aurora) Underline(arg interface{}) Value { - return Underline(arg) -} - -func (aurora) SlowBlink(arg interface{}) Value { - return SlowBlink(arg) -} - -func (aurora) RapidBlink(arg interface{}) Value { - return RapidBlink(arg) -} - -func (aurora) Blink(arg interface{}) Value { - return Blink(arg) -} - -func (aurora) Reverse(arg interface{}) Value { - return Reverse(arg) -} - -func (aurora) Inverse(arg interface{}) Value { - return Inverse(arg) -} - -func (aurora) Conceal(arg interface{}) Value { - return Conceal(arg) -} - -func (aurora) Hidden(arg interface{}) Value { - return Hidden(arg) -} - -func (aurora) CrossedOut(arg interface{}) Value { - return CrossedOut(arg) -} - -func (aurora) StrikeThrough(arg interface{}) Value { - return StrikeThrough(arg) -} - -func (aurora) Framed(arg interface{}) Value { - return Framed(arg) -} - -func (aurora) Encircled(arg interface{}) Value { - return Encircled(arg) -} - -func (aurora) Overlined(arg interface{}) Value { - return Overlined(arg) -} - -func (aurora) Black(arg interface{}) Value { - return Black(arg) -} - -func (aurora) Red(arg interface{}) Value { - return Red(arg) -} - -func (aurora) Green(arg interface{}) Value { - return Green(arg) -} - -func (aurora) Yellow(arg interface{}) Value { - return Yellow(arg) -} - -func (aurora) Brown(arg interface{}) Value { - return Brown(arg) -} - -func (aurora) Blue(arg interface{}) Value { - return Blue(arg) -} - -func (aurora) Magenta(arg interface{}) Value { - return Magenta(arg) -} - -func (aurora) Cyan(arg interface{}) Value { - return Cyan(arg) -} - -func (aurora) White(arg interface{}) Value { - return White(arg) -} - -func (aurora) BrightBlack(arg interface{}) Value { - return BrightBlack(arg) -} - -func (aurora) BrightRed(arg interface{}) Value { - return BrightRed(arg) -} - -func (aurora) BrightGreen(arg interface{}) Value { - return BrightGreen(arg) -} - -func (aurora) BrightYellow(arg interface{}) Value { - return BrightYellow(arg) -} - -func (aurora) BrightBlue(arg interface{}) Value { - return BrightBlue(arg) -} - -func (aurora) BrightMagenta(arg interface{}) Value { - return BrightMagenta(arg) -} - -func (aurora) BrightCyan(arg interface{}) Value { - return BrightCyan(arg) -} - -func (aurora) BrightWhite(arg interface{}) Value { - return BrightWhite(arg) -} - -func (aurora) Index(index uint8, arg interface{}) Value { - return Index(index, arg) -} - -func (aurora) Gray(n uint8, arg interface{}) Value { - return Gray(n, arg) -} - -func (aurora) BgBlack(arg interface{}) Value { - return BgBlack(arg) -} - -func (aurora) BgRed(arg interface{}) Value { - return BgRed(arg) -} - -func (aurora) BgGreen(arg interface{}) Value { - return BgGreen(arg) -} - -func (aurora) BgYellow(arg interface{}) Value { - return BgYellow(arg) -} - -func (aurora) BgBrown(arg interface{}) Value { - return BgBrown(arg) -} - -func (aurora) BgBlue(arg interface{}) Value { - return BgBlue(arg) -} - -func (aurora) BgMagenta(arg interface{}) Value { - return BgMagenta(arg) -} - -func (aurora) BgCyan(arg interface{}) Value { - return BgCyan(arg) -} - -func (aurora) BgWhite(arg interface{}) Value { - return BgWhite(arg) -} - -func (aurora) BgBrightBlack(arg interface{}) Value { - return BgBrightBlack(arg) -} - -func (aurora) BgBrightRed(arg interface{}) Value { - return BgBrightRed(arg) -} - -func (aurora) BgBrightGreen(arg interface{}) Value { - return BgBrightGreen(arg) -} - -func (aurora) BgBrightYellow(arg interface{}) Value { - return BgBrightYellow(arg) -} - -func (aurora) BgBrightBlue(arg interface{}) Value { - return BgBrightBlue(arg) -} - -func (aurora) BgBrightMagenta(arg interface{}) Value { - return BgBrightMagenta(arg) -} - -func (aurora) BgBrightCyan(arg interface{}) Value { - return BgBrightCyan(arg) -} - -func (aurora) BgBrightWhite(arg interface{}) Value { - return BgBrightWhite(arg) -} - -func (aurora) BgIndex(n uint8, arg interface{}) Value { - return BgIndex(n, arg) -} - -func (aurora) BgGray(n uint8, arg interface{}) Value { - return BgGray(n, arg) -} - -func (aurora) Colorize(arg interface{}, color Color) Value { - return Colorize(arg, color) -} - -func (aurora) Sprintf(format interface{}, args ...interface{}) string { - return Sprintf(format, args...) -} diff --git a/vendor/github.com/logrusorgru/aurora/aurora_black_standard.png b/vendor/github.com/logrusorgru/aurora/aurora_black_standard.png deleted file mode 100644 index 83658d60..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/aurora_black_standard.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/aurora_colors_black.png b/vendor/github.com/logrusorgru/aurora/aurora_colors_black.png deleted file mode 100644 index 61b1602a..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/aurora_colors_black.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/aurora_colors_white.png b/vendor/github.com/logrusorgru/aurora/aurora_colors_white.png deleted file mode 100644 index ec42b072..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/aurora_colors_white.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/aurora_formats.gif b/vendor/github.com/logrusorgru/aurora/aurora_formats.gif deleted file mode 100644 index 670dd1c3..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/aurora_formats.gif and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/aurora_grayscale.png b/vendor/github.com/logrusorgru/aurora/aurora_grayscale.png deleted file mode 100644 index 40ecac6a..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/aurora_grayscale.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/aurora_rarely_supported.png b/vendor/github.com/logrusorgru/aurora/aurora_rarely_supported.png deleted file mode 100644 index 51b7b6d2..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/aurora_rarely_supported.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/aurora_white_standard.png b/vendor/github.com/logrusorgru/aurora/aurora_white_standard.png deleted file mode 100644 index 1fe99d78..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/aurora_white_standard.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/color.go b/vendor/github.com/logrusorgru/aurora/color.go deleted file mode 100644 index 486fb674..00000000 --- a/vendor/github.com/logrusorgru/aurora/color.go +++ /dev/null @@ -1,398 +0,0 @@ -// -// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. -// This program is free software. It comes without any warranty, -// to the extent permitted by applicable law. You can redistribute -// it and/or modify it under the terms of the Unlicense. See LICENSE -// file for more details or see below. -// - -// -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. -// -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -// - -package aurora - -// A Color type is a color. It can contain -// one background color, one foreground color -// and a format, including ideogram related -// formats. -type Color uint - -/* - - Developer note. - - The int type is architecture depended and can be - represented as int32 or int64. - - Thus, we can use 32-bits only to be fast and - cross-platform. - - All supported formats requires 14 bits. It is - first 14 bits. - - A foreground color requires 8 bit + 1 bit (presence flag). - And the same for background color. - - The Color representations - - [ bg 8 bit ] [fg 8 bit ] [ fg/bg 2 bits ] [ fm 14 bits ] - - https://play.golang.org/p/fq2zcNstFoF - -*/ - -// Special formats -const ( - BoldFm Color = 1 << iota // 1 - FaintFm // 2 - ItalicFm // 3 - UnderlineFm // 4 - SlowBlinkFm // 5 - RapidBlinkFm // 6 - ReverseFm // 7 - ConcealFm // 8 - CrossedOutFm // 9 - - FrakturFm // 20 - DoublyUnderlineFm // 21 or bold off for some systems - - FramedFm // 51 - EncircledFm // 52 - OverlinedFm // 53 - - InverseFm = ReverseFm // alias to ReverseFm - BlinkFm = SlowBlinkFm // alias to SlowBlinkFm - HiddenFm = ConcealFm // alias to ConcealFm - StrikeThroughFm = CrossedOutFm // alias to CrossedOutFm - - maskFm = BoldFm | FaintFm | - ItalicFm | UnderlineFm | - SlowBlinkFm | RapidBlinkFm | - ReverseFm | - ConcealFm | CrossedOutFm | - - FrakturFm | DoublyUnderlineFm | - - FramedFm | EncircledFm | OverlinedFm - - flagFg Color = 1 << 14 // presence flag (14th bit) - flagBg Color = 1 << 15 // presence flag (15th bit) - - shiftFg = 16 // shift for foreground (starting from 16th bit) - shiftBg = 24 // shift for background (starting from 24th bit) -) - -// Foreground colors and related formats -const ( - - // 8 bits - - // [ 0; 7] - 30-37 - // [ 8; 15] - 90-97 - // [ 16; 231] - RGB - // [232; 255] - grayscale - - BlackFg Color = (iota << shiftFg) | flagFg // 30, 90 - RedFg // 31, 91 - GreenFg // 32, 92 - YellowFg // 33, 93 - BlueFg // 34, 94 - MagentaFg // 35, 95 - CyanFg // 36, 96 - WhiteFg // 37, 97 - - BrightFg Color = ((1 << 3) << shiftFg) | flagFg // -> 90 - - // the BrightFg itself doesn't represent - // a color, thus it has not flagFg - - // 5 bits - - // BrownFg represents brown foreground color. - // - // Deprecated: use YellowFg instead, following specifications - BrownFg = YellowFg - - // - maskFg = (0xff << shiftFg) | flagFg -) - -// Background colors and related formats -const ( - - // 8 bits - - // [ 0; 7] - 40-47 - // [ 8; 15] - 100-107 - // [ 16; 231] - RGB - // [232; 255] - grayscale - - BlackBg Color = (iota << shiftBg) | flagBg // 40, 100 - RedBg // 41, 101 - GreenBg // 42, 102 - YellowBg // 43, 103 - BlueBg // 44, 104 - MagentaBg // 45, 105 - CyanBg // 46, 106 - WhiteBg // 47, 107 - - BrightBg Color = ((1 << 3) << shiftBg) | flagBg // -> 100 - - // the BrightBg itself doesn't represent - // a color, thus it has not flagBg - - // 5 bits - - // BrownBg represents brown foreground color. - // - // Deprecated: use YellowBg instead, following specifications - BrownBg = YellowBg - - // - maskBg = (0xff << shiftBg) | flagBg -) - -const ( - availFlags = "-+# 0" - esc = "\033[" - clear = esc + "0m" -) - -// IsValid returns true always -// -// Deprecated: don't use this method anymore -func (c Color) IsValid() bool { - return true -} - -// Nos returns string like 1;7;31;45. It -// may be an empty string for empty color. -// If the zero is true, then the string -// is prepended with 0; -func (c Color) Nos(zero bool) string { - return string(c.appendNos(make([]byte, 0, 59), zero)) -} - -func appendCond(bs []byte, cond, semi bool, vals ...byte) []byte { - if !cond { - return bs - } - return appendSemi(bs, semi, vals...) -} - -// if the semi is true, then prepend with semicolon -func appendSemi(bs []byte, semi bool, vals ...byte) []byte { - if semi { - bs = append(bs, ';') - } - return append(bs, vals...) -} - -func itoa(t byte) string { - var ( - a [3]byte - j = 2 - ) - for i := 0; i < 3; i, j = i+1, j-1 { - a[j] = '0' + t%10 - if t = t / 10; t == 0 { - break - } - } - return string(a[j:]) -} - -func (c Color) appendFg(bs []byte, zero bool) []byte { - - if zero || c&maskFm != 0 { - bs = append(bs, ';') - } - - // 0- 7 : 30-37 - // 8-15 : 90-97 - // > 15 : 38;5;val - - switch fg := (c & maskFg) >> shiftFg; { - case fg <= 7: - // '3' and the value itself - bs = append(bs, '3', '0'+byte(fg)) - case fg <= 15: - // '9' and the value itself - bs = append(bs, '9', '0'+byte(fg&^0x08)) // clear bright flag - default: - bs = append(bs, '3', '8', ';', '5', ';') - bs = append(bs, itoa(byte(fg))...) - } - return bs -} - -func (c Color) appendBg(bs []byte, zero bool) []byte { - - if zero || c&(maskFm|maskFg) != 0 { - bs = append(bs, ';') - } - - // 0- 7 : 40- 47 - // 8-15 : 100-107 - // > 15 : 48;5;val - - switch fg := (c & maskBg) >> shiftBg; { - case fg <= 7: - // '3' and the value itself - bs = append(bs, '4', '0'+byte(fg)) - case fg <= 15: - // '1', '0' and the value itself - bs = append(bs, '1', '0', '0'+byte(fg&^0x08)) // clear bright flag - default: - bs = append(bs, '4', '8', ';', '5', ';') - bs = append(bs, itoa(byte(fg))...) - } - return bs -} - -func (c Color) appendFm9(bs []byte, zero bool) []byte { - - bs = appendCond(bs, c&ItalicFm != 0, - zero || c&(BoldFm|FaintFm) != 0, - '3') - bs = appendCond(bs, c&UnderlineFm != 0, - zero || c&(BoldFm|FaintFm|ItalicFm) != 0, - '4') - // don't combine slow and rapid blink using only - // on of them, preferring slow blink - if c&SlowBlinkFm != 0 { - bs = appendSemi(bs, - zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0, - '5') - } else if c&RapidBlinkFm != 0 { - bs = appendSemi(bs, - zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0, - '6') - } - - // including 1-2 - const mask6i = BoldFm | FaintFm | - ItalicFm | UnderlineFm | - SlowBlinkFm | RapidBlinkFm - - bs = appendCond(bs, c&ReverseFm != 0, - zero || c&(mask6i) != 0, - '7') - bs = appendCond(bs, c&ConcealFm != 0, - zero || c&(mask6i|ReverseFm) != 0, - '8') - bs = appendCond(bs, c&CrossedOutFm != 0, - zero || c&(mask6i|ReverseFm|ConcealFm) != 0, - '9') - - return bs -} - -// append 1;3;38;5;216 like string that represents ANSI -// color of the Color; the zero argument requires -// appending of '0' before to reset previous format -// and colors -func (c Color) appendNos(bs []byte, zero bool) []byte { - - if zero { - bs = append(bs, '0') // reset previous - } - - // formats - // - - if c&maskFm != 0 { - - // 1-2 - - // don't combine bold and faint using only on of them, preferring bold - - if c&BoldFm != 0 { - bs = appendSemi(bs, zero, '1') - } else if c&FaintFm != 0 { - bs = appendSemi(bs, zero, '2') - } - - // 3-9 - - const mask9 = ItalicFm | UnderlineFm | - SlowBlinkFm | RapidBlinkFm | - ReverseFm | ConcealFm | CrossedOutFm - - if c&mask9 != 0 { - bs = c.appendFm9(bs, zero) - } - - // 20-21 - - const ( - mask21 = FrakturFm | DoublyUnderlineFm - mask9i = BoldFm | FaintFm | mask9 - ) - - if c&mask21 != 0 { - bs = appendCond(bs, c&FrakturFm != 0, - zero || c&mask9i != 0, - '2', '0') - bs = appendCond(bs, c&DoublyUnderlineFm != 0, - zero || c&(mask9i|FrakturFm) != 0, - '2', '1') - } - - // 50-53 - - const ( - mask53 = FramedFm | EncircledFm | OverlinedFm - mask21i = mask9i | mask21 - ) - - if c&mask53 != 0 { - bs = appendCond(bs, c&FramedFm != 0, - zero || c&mask21i != 0, - '5', '1') - bs = appendCond(bs, c&EncircledFm != 0, - zero || c&(mask21i|FramedFm) != 0, - '5', '2') - bs = appendCond(bs, c&OverlinedFm != 0, - zero || c&(mask21i|FramedFm|EncircledFm) != 0, - '5', '3') - } - - } - - // foreground - if c&maskFg != 0 { - bs = c.appendFg(bs, zero) - } - - // background - if c&maskBg != 0 { - bs = c.appendBg(bs, zero) - } - - return bs -} diff --git a/vendor/github.com/logrusorgru/aurora/disable.png b/vendor/github.com/logrusorgru/aurora/disable.png deleted file mode 100644 index 0dd1d63f..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/disable.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/enable.png b/vendor/github.com/logrusorgru/aurora/enable.png deleted file mode 100644 index a488367c..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/enable.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/gopher_aurora.png b/vendor/github.com/logrusorgru/aurora/gopher_aurora.png deleted file mode 100644 index 8f61bb20..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/gopher_aurora.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/printf.png b/vendor/github.com/logrusorgru/aurora/printf.png deleted file mode 100644 index 5978844a..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/printf.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/simple.png b/vendor/github.com/logrusorgru/aurora/simple.png deleted file mode 100644 index 50edf042..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/simple.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/sprintf.go b/vendor/github.com/logrusorgru/aurora/sprintf.go deleted file mode 100644 index b92d5933..00000000 --- a/vendor/github.com/logrusorgru/aurora/sprintf.go +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. -// This program is free software. It comes without any warranty, -// to the extent permitted by applicable law. You can redistribute -// it and/or modify it under the terms of the Unlicense. See LICENSE -// file for more details or see below. -// - -// -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. -// -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -// - -package aurora - -import ( - "fmt" -) - -// Sprintf allows to use Value as format. For example -// -// v := Sprintf(Red("total: +3.5f points"), Blue(3.14)) -// -// In this case "total:" and "points" will be red, but -// 3.14 will be blue. But, in another example -// -// v := Sprintf(Red("total: +3.5f points"), 3.14) -// -// full string will be red. And no way to clear 3.14 to -// default format and color -func Sprintf(format interface{}, args ...interface{}) string { - switch ft := format.(type) { - case string: - return fmt.Sprintf(ft, args...) - case Value: - for i, v := range args { - if val, ok := v.(Value); ok { - args[i] = val.setTail(ft.Color()) - continue - } - } - return fmt.Sprintf(ft.String(), args...) - } - // unknown type of format (we hope it's a string) - return fmt.Sprintf(fmt.Sprint(format), args...) -} diff --git a/vendor/github.com/logrusorgru/aurora/sprintf.png b/vendor/github.com/logrusorgru/aurora/sprintf.png deleted file mode 100644 index df2b2cc0..00000000 Binary files a/vendor/github.com/logrusorgru/aurora/sprintf.png and /dev/null differ diff --git a/vendor/github.com/logrusorgru/aurora/value.go b/vendor/github.com/logrusorgru/aurora/value.go deleted file mode 100644 index feda25af..00000000 --- a/vendor/github.com/logrusorgru/aurora/value.go +++ /dev/null @@ -1,745 +0,0 @@ -// -// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. -// This program is free software. It comes without any warranty, -// to the extent permitted by applicable law. You can redistribute -// it and/or modify it under the terms of the Unlicense. See LICENSE -// file for more details or see below. -// - -// -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. -// -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -// - -package aurora - -import ( - "fmt" - "strconv" - "unicode/utf8" -) - -// A Value represents any printable value -// with it's color -type Value interface { - // String returns string with colors. If there are any color - // or format the string will be terminated with \033[0m - fmt.Stringer - // Format implements fmt.Formatter interface - fmt.Formatter - // Color returns value's color - Color() Color - // Value returns value's value (welcome to the tautology club) - Value() interface{} - - // internals - tail() Color - setTail(Color) Value - - // Bleach returns copy of original value without colors - // - // Deprecated: use Reset instead. - Bleach() Value - // Reset colors and formats - Reset() Value - - // - // Formats - // - // - // Bold or increased intensity (1). - Bold() Value - // Faint, decreased intensity, reset the Bold (2). - Faint() Value - // - // DoublyUnderline or Bold off, double-underline - // per ECMA-48 (21). It depends. - DoublyUnderline() Value - // Fraktur, rarely supported (20). - Fraktur() Value - // - // Italic, not widely supported, sometimes - // treated as inverse (3). - Italic() Value - // Underline (4). - Underline() Value - // - // SlowBlink, blinking less than 150 - // per minute (5). - SlowBlink() Value - // RapidBlink, blinking 150+ per minute, - // not widely supported (6). - RapidBlink() Value - // Blink is alias for the SlowBlink. - Blink() Value - // - // Reverse video, swap foreground and - // background colors (7). - Reverse() Value - // Inverse is alias for the Reverse - Inverse() Value - // - // Conceal, hidden, not widely supported (8). - Conceal() Value - // Hidden is alias for the Conceal - Hidden() Value - // - // CrossedOut, characters legible, but - // marked for deletion (9). - CrossedOut() Value - // StrikeThrough is alias for the CrossedOut. - StrikeThrough() Value - // - // Framed (51). - Framed() Value - // Encircled (52). - Encircled() Value - // - // Overlined (53). - Overlined() Value - - // - // Foreground colors - // - // - // Black foreground color (30) - Black() Value - // Red foreground color (31) - Red() Value - // Green foreground color (32) - Green() Value - // Yellow foreground color (33) - Yellow() Value - // Brown foreground color (33) - // - // Deprecated: use Yellow instead, following specification - Brown() Value - // Blue foreground color (34) - Blue() Value - // Magenta foreground color (35) - Magenta() Value - // Cyan foreground color (36) - Cyan() Value - // White foreground color (37) - White() Value - // - // Bright foreground colors - // - // BrightBlack foreground color (90) - BrightBlack() Value - // BrightRed foreground color (91) - BrightRed() Value - // BrightGreen foreground color (92) - BrightGreen() Value - // BrightYellow foreground color (93) - BrightYellow() Value - // BrightBlue foreground color (94) - BrightBlue() Value - // BrightMagenta foreground color (95) - BrightMagenta() Value - // BrightCyan foreground color (96) - BrightCyan() Value - // BrightWhite foreground color (97) - BrightWhite() Value - // - // Other - // - // Index of pre-defined 8-bit foreground color - // from 0 to 255 (38;5;n). - // - // 0- 7: standard colors (as in ESC [ 30–37 m) - // 8- 15: high intensity colors (as in ESC [ 90–97 m) - // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) - // 232-255: grayscale from black to white in 24 steps - // - Index(n uint8) Value - // Gray from 0 to 24. - Gray(n uint8) Value - - // - // Background colors - // - // - // BgBlack background color (40) - BgBlack() Value - // BgRed background color (41) - BgRed() Value - // BgGreen background color (42) - BgGreen() Value - // BgYellow background color (43) - BgYellow() Value - // BgBrown background color (43) - // - // Deprecated: use BgYellow instead, following specification - BgBrown() Value - // BgBlue background color (44) - BgBlue() Value - // BgMagenta background color (45) - BgMagenta() Value - // BgCyan background color (46) - BgCyan() Value - // BgWhite background color (47) - BgWhite() Value - // - // Bright background colors - // - // BgBrightBlack background color (100) - BgBrightBlack() Value - // BgBrightRed background color (101) - BgBrightRed() Value - // BgBrightGreen background color (102) - BgBrightGreen() Value - // BgBrightYellow background color (103) - BgBrightYellow() Value - // BgBrightBlue background color (104) - BgBrightBlue() Value - // BgBrightMagenta background color (105) - BgBrightMagenta() Value - // BgBrightCyan background color (106) - BgBrightCyan() Value - // BgBrightWhite background color (107) - BgBrightWhite() Value - // - // Other - // - // BgIndex of 8-bit pre-defined background color - // from 0 to 255 (48;5;n). - // - // 0- 7: standard colors (as in ESC [ 40–47 m) - // 8- 15: high intensity colors (as in ESC [100–107 m) - // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) - // 232-255: grayscale from black to white in 24 steps - // - BgIndex(n uint8) Value - // BgGray from 0 to 24. - BgGray(n uint8) Value - - // - // Special - // - // Colorize removes existing colors and - // formats of the argument and applies given. - Colorize(color Color) Value -} - -// Value without colors - -type valueClear struct { - value interface{} -} - -func (vc valueClear) String() string { return fmt.Sprint(vc.value) } - -func (vc valueClear) Color() Color { return 0 } -func (vc valueClear) Value() interface{} { return vc.value } - -func (vc valueClear) tail() Color { return 0 } -func (vc valueClear) setTail(Color) Value { return vc } - -func (vc valueClear) Bleach() Value { return vc } -func (vc valueClear) Reset() Value { return vc } - -func (vc valueClear) Bold() Value { return vc } -func (vc valueClear) Faint() Value { return vc } -func (vc valueClear) DoublyUnderline() Value { return vc } -func (vc valueClear) Fraktur() Value { return vc } -func (vc valueClear) Italic() Value { return vc } -func (vc valueClear) Underline() Value { return vc } -func (vc valueClear) SlowBlink() Value { return vc } -func (vc valueClear) RapidBlink() Value { return vc } -func (vc valueClear) Blink() Value { return vc } -func (vc valueClear) Reverse() Value { return vc } -func (vc valueClear) Inverse() Value { return vc } -func (vc valueClear) Conceal() Value { return vc } -func (vc valueClear) Hidden() Value { return vc } -func (vc valueClear) CrossedOut() Value { return vc } -func (vc valueClear) StrikeThrough() Value { return vc } -func (vc valueClear) Framed() Value { return vc } -func (vc valueClear) Encircled() Value { return vc } -func (vc valueClear) Overlined() Value { return vc } - -func (vc valueClear) Black() Value { return vc } -func (vc valueClear) Red() Value { return vc } -func (vc valueClear) Green() Value { return vc } -func (vc valueClear) Yellow() Value { return vc } -func (vc valueClear) Brown() Value { return vc } -func (vc valueClear) Blue() Value { return vc } -func (vc valueClear) Magenta() Value { return vc } -func (vc valueClear) Cyan() Value { return vc } -func (vc valueClear) White() Value { return vc } -func (vc valueClear) BrightBlack() Value { return vc } -func (vc valueClear) BrightRed() Value { return vc } -func (vc valueClear) BrightGreen() Value { return vc } -func (vc valueClear) BrightYellow() Value { return vc } -func (vc valueClear) BrightBlue() Value { return vc } -func (vc valueClear) BrightMagenta() Value { return vc } -func (vc valueClear) BrightCyan() Value { return vc } -func (vc valueClear) BrightWhite() Value { return vc } -func (vc valueClear) Index(uint8) Value { return vc } -func (vc valueClear) Gray(uint8) Value { return vc } - -func (vc valueClear) BgBlack() Value { return vc } -func (vc valueClear) BgRed() Value { return vc } -func (vc valueClear) BgGreen() Value { return vc } -func (vc valueClear) BgYellow() Value { return vc } -func (vc valueClear) BgBrown() Value { return vc } -func (vc valueClear) BgBlue() Value { return vc } -func (vc valueClear) BgMagenta() Value { return vc } -func (vc valueClear) BgCyan() Value { return vc } -func (vc valueClear) BgWhite() Value { return vc } -func (vc valueClear) BgBrightBlack() Value { return vc } -func (vc valueClear) BgBrightRed() Value { return vc } -func (vc valueClear) BgBrightGreen() Value { return vc } -func (vc valueClear) BgBrightYellow() Value { return vc } -func (vc valueClear) BgBrightBlue() Value { return vc } -func (vc valueClear) BgBrightMagenta() Value { return vc } -func (vc valueClear) BgBrightCyan() Value { return vc } -func (vc valueClear) BgBrightWhite() Value { return vc } -func (vc valueClear) BgIndex(uint8) Value { return vc } -func (vc valueClear) BgGray(uint8) Value { return vc } -func (vc valueClear) Colorize(Color) Value { return vc } - -func (vc valueClear) Format(s fmt.State, verb rune) { - // it's enough for many cases (%-+020.10f) - // % - 1 - // availFlags - 3 (5) - // width - 2 - // prec - 3 (.23) - // verb - 1 - // -------------- - // 10 - format := make([]byte, 1, 10) - format[0] = '%' - var f byte - for i := 0; i < len(availFlags); i++ { - if f = availFlags[i]; s.Flag(int(f)) { - format = append(format, f) - } - } - var width, prec int - var ok bool - if width, ok = s.Width(); ok { - format = strconv.AppendInt(format, int64(width), 10) - } - if prec, ok = s.Precision(); ok { - format = append(format, '.') - format = strconv.AppendInt(format, int64(prec), 10) - } - if verb > utf8.RuneSelf { - format = append(format, string(verb)...) - } else { - format = append(format, byte(verb)) - } - fmt.Fprintf(s, string(format), vc.value) -} - -// Value within colors - -type value struct { - value interface{} // value as it - color Color // this color - tailColor Color // tail color -} - -func (v value) String() string { - if v.color != 0 { - if v.tailColor != 0 { - return esc + v.color.Nos(true) + "m" + - fmt.Sprint(v.value) + - esc + v.tailColor.Nos(true) + "m" - } - return esc + v.color.Nos(false) + "m" + fmt.Sprint(v.value) + clear - } - return fmt.Sprint(v.value) -} - -func (v value) Color() Color { return v.color } - -func (v value) Bleach() Value { - v.color, v.tailColor = 0, 0 - return v -} - -func (v value) Reset() Value { - v.color, v.tailColor = 0, 0 - return v -} - -func (v value) tail() Color { return v.tailColor } - -func (v value) setTail(t Color) Value { - v.tailColor = t - return v -} - -func (v value) Value() interface{} { return v.value } - -func (v value) Format(s fmt.State, verb rune) { - - // it's enough for many cases (%-+020.10f) - // % - 1 - // availFlags - 3 (5) - // width - 2 - // prec - 3 (.23) - // verb - 1 - // -------------- - // 10 - // + - // \033[ 5 - // 0;1;3;4;5;7;8;9;20;21;51;52;53 30 - // 38;5;216 8 - // 48;5;216 8 - // m 1 - // + - // \033[0m 7 - // - // x2 (possible tail color) - // - // 10 + 59 * 2 = 128 - - format := make([]byte, 0, 128) - if v.color != 0 { - format = append(format, esc...) - format = v.color.appendNos(format, v.tailColor != 0) - format = append(format, 'm') - } - format = append(format, '%') - var f byte - for i := 0; i < len(availFlags); i++ { - if f = availFlags[i]; s.Flag(int(f)) { - format = append(format, f) - } - } - var width, prec int - var ok bool - if width, ok = s.Width(); ok { - format = strconv.AppendInt(format, int64(width), 10) - } - if prec, ok = s.Precision(); ok { - format = append(format, '.') - format = strconv.AppendInt(format, int64(prec), 10) - } - if verb > utf8.RuneSelf { - format = append(format, string(verb)...) - } else { - format = append(format, byte(verb)) - } - if v.color != 0 { - if v.tailColor != 0 { - // set next (previous) format clearing current one - format = append(format, esc...) - format = v.tailColor.appendNos(format, true) - format = append(format, 'm') - } else { - format = append(format, clear...) // just clear - } - } - fmt.Fprintf(s, string(format), v.value) -} - -func (v value) Bold() Value { - v.color = (v.color &^ FaintFm) | BoldFm - return v -} - -func (v value) Faint() Value { - v.color = (v.color &^ BoldFm) | FaintFm - return v -} - -func (v value) DoublyUnderline() Value { - v.color |= DoublyUnderlineFm - return v -} - -func (v value) Fraktur() Value { - v.color |= FrakturFm - return v -} - -func (v value) Italic() Value { - v.color |= ItalicFm - return v -} - -func (v value) Underline() Value { - v.color |= UnderlineFm - return v -} - -func (v value) SlowBlink() Value { - v.color = (v.color &^ RapidBlinkFm) | SlowBlinkFm - return v -} - -func (v value) RapidBlink() Value { - v.color = (v.color &^ SlowBlinkFm) | RapidBlinkFm - return v -} - -func (v value) Blink() Value { - return v.SlowBlink() -} - -func (v value) Reverse() Value { - v.color |= ReverseFm - return v -} - -func (v value) Inverse() Value { - return v.Reverse() -} - -func (v value) Conceal() Value { - v.color |= ConcealFm - return v -} - -func (v value) Hidden() Value { - return v.Conceal() -} - -func (v value) CrossedOut() Value { - v.color |= CrossedOutFm - return v -} - -func (v value) StrikeThrough() Value { - return v.CrossedOut() -} - -func (v value) Framed() Value { - v.color |= FramedFm - return v -} - -func (v value) Encircled() Value { - v.color |= EncircledFm - return v -} - -func (v value) Overlined() Value { - v.color |= OverlinedFm - return v -} - -func (v value) Black() Value { - v.color = (v.color &^ maskFg) | BlackFg - return v -} - -func (v value) Red() Value { - v.color = (v.color &^ maskFg) | RedFg - return v -} - -func (v value) Green() Value { - v.color = (v.color &^ maskFg) | GreenFg - return v -} - -func (v value) Yellow() Value { - v.color = (v.color &^ maskFg) | YellowFg - return v -} - -func (v value) Brown() Value { - return v.Yellow() -} - -func (v value) Blue() Value { - v.color = (v.color &^ maskFg) | BlueFg - return v -} - -func (v value) Magenta() Value { - v.color = (v.color &^ maskFg) | MagentaFg - return v -} - -func (v value) Cyan() Value { - v.color = (v.color &^ maskFg) | CyanFg - return v -} - -func (v value) White() Value { - v.color = (v.color &^ maskFg) | WhiteFg - return v -} - -func (v value) BrightBlack() Value { - v.color = (v.color &^ maskFg) | BrightFg | BlackFg - return v -} - -func (v value) BrightRed() Value { - v.color = (v.color &^ maskFg) | BrightFg | RedFg - return v -} - -func (v value) BrightGreen() Value { - v.color = (v.color &^ maskFg) | BrightFg | GreenFg - return v -} - -func (v value) BrightYellow() Value { - v.color = (v.color &^ maskFg) | BrightFg | YellowFg - return v -} - -func (v value) BrightBlue() Value { - v.color = (v.color &^ maskFg) | BrightFg | BlueFg - return v -} - -func (v value) BrightMagenta() Value { - v.color = (v.color &^ maskFg) | BrightFg | MagentaFg - return v -} - -func (v value) BrightCyan() Value { - v.color = (v.color &^ maskFg) | BrightFg | CyanFg - return v -} - -func (v value) BrightWhite() Value { - v.color = (v.color &^ maskFg) | BrightFg | WhiteFg - return v -} - -func (v value) Index(n uint8) Value { - v.color = (v.color &^ maskFg) | (Color(n) << shiftFg) | flagFg - return v -} - -func (v value) Gray(n uint8) Value { - if n > 23 { - n = 23 - } - v.color = (v.color &^ maskFg) | (Color(232+n) << shiftFg) | flagFg - return v -} - -func (v value) BgBlack() Value { - v.color = (v.color &^ maskBg) | BlackBg - return v -} - -func (v value) BgRed() Value { - v.color = (v.color &^ maskBg) | RedBg - return v -} - -func (v value) BgGreen() Value { - v.color = (v.color &^ maskBg) | GreenBg - return v -} - -func (v value) BgYellow() Value { - v.color = (v.color &^ maskBg) | YellowBg - return v -} - -func (v value) BgBrown() Value { - return v.BgYellow() -} - -func (v value) BgBlue() Value { - v.color = (v.color &^ maskBg) | BlueBg - return v -} - -func (v value) BgMagenta() Value { - v.color = (v.color &^ maskBg) | MagentaBg - return v -} - -func (v value) BgCyan() Value { - v.color = (v.color &^ maskBg) | CyanBg - return v -} - -func (v value) BgWhite() Value { - v.color = (v.color &^ maskBg) | WhiteBg - return v -} - -func (v value) BgBrightBlack() Value { - v.color = (v.color &^ maskBg) | BrightBg | BlackBg - return v -} - -func (v value) BgBrightRed() Value { - v.color = (v.color &^ maskBg) | BrightBg | RedBg - return v -} - -func (v value) BgBrightGreen() Value { - v.color = (v.color &^ maskBg) | BrightBg | GreenBg - return v -} - -func (v value) BgBrightYellow() Value { - v.color = (v.color &^ maskBg) | BrightBg | YellowBg - return v -} - -func (v value) BgBrightBlue() Value { - v.color = (v.color &^ maskBg) | BrightBg | BlueBg - return v -} - -func (v value) BgBrightMagenta() Value { - v.color = (v.color &^ maskBg) | BrightBg | MagentaBg - return v -} - -func (v value) BgBrightCyan() Value { - v.color = (v.color &^ maskBg) | BrightBg | CyanBg - return v -} - -func (v value) BgBrightWhite() Value { - v.color = (v.color &^ maskBg) | BrightBg | WhiteBg - return v -} - -func (v value) BgIndex(n uint8) Value { - v.color = (v.color &^ maskBg) | (Color(n) << shiftBg) | flagBg - return v -} - -func (v value) BgGray(n uint8) Value { - if n > 23 { - n = 23 - } - v.color = (v.color &^ maskBg) | (Color(232+n) << shiftBg) | flagBg - return v -} - -func (v value) Colorize(color Color) Value { - v.color = color - return v -} diff --git a/vendor/github.com/logrusorgru/aurora/wrap.go b/vendor/github.com/logrusorgru/aurora/wrap.go deleted file mode 100644 index 44e1aa6c..00000000 --- a/vendor/github.com/logrusorgru/aurora/wrap.go +++ /dev/null @@ -1,558 +0,0 @@ -// -// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. -// This program is free software. It comes without any warranty, -// to the extent permitted by applicable law. You can redistribute -// it and/or modify it under the terms of the Unlicense. See LICENSE -// file for more details or see below. -// - -// -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. -// -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -// - -package aurora - -// Colorize wraps given value into Value with -// given colors. For example -// -// s := Colorize("some", BlueFg|GreenBg|BoldFm) -// -// returns a Value with blue foreground, green -// background and bold. Unlike functions like -// Red/BgBlue/Bold etc. This function clears -// all previous colors and formats. Thus -// -// s := Colorize(Red("some"), BgBlue) -// -// clears red color from value -func Colorize(arg interface{}, color Color) Value { - if val, ok := arg.(value); ok { - val.color = color - return val - } - return value{arg, color, 0} -} - -// Reset wraps given argument returning Value -// without formats and colors. -func Reset(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Reset() - } - return value{value: arg} -} - -// -// Formats -// - -// Bold or increased intensity (1). -func Bold(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Bold() - } - return value{value: arg, color: BoldFm} -} - -// Faint decreases intensity (2). -// The Faint rejects the Bold. -func Faint(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Faint() - } - return value{value: arg, color: FaintFm} -} - -// DoublyUnderline or Bold off, double-underline -// per ECMA-48 (21). -func DoublyUnderline(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.DoublyUnderline() - } - return value{value: arg, color: DoublyUnderlineFm} -} - -// Fraktur is rarely supported (20). -func Fraktur(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Fraktur() - } - return value{value: arg, color: FrakturFm} -} - -// Italic is not widely supported, sometimes -// treated as inverse (3). -func Italic(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Italic() - } - return value{value: arg, color: ItalicFm} -} - -// Underline (4). -func Underline(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Underline() - } - return value{value: arg, color: UnderlineFm} -} - -// SlowBlink makes text blink less than -// 150 per minute (5). -func SlowBlink(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.SlowBlink() - } - return value{value: arg, color: SlowBlinkFm} -} - -// RapidBlink makes text blink 150+ per -// minute. It is not widely supported (6). -func RapidBlink(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.RapidBlink() - } - return value{value: arg, color: RapidBlinkFm} -} - -// Blink is alias for the SlowBlink. -func Blink(arg interface{}) Value { - return SlowBlink(arg) -} - -// Reverse video, swap foreground and -// background colors (7). -func Reverse(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Reverse() - } - return value{value: arg, color: ReverseFm} -} - -// Inverse is alias for the Reverse -func Inverse(arg interface{}) Value { - return Reverse(arg) -} - -// Conceal hides text, preserving an ability to select -// the text and copy it. It is not widely supported (8). -func Conceal(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Conceal() - } - return value{value: arg, color: ConcealFm} -} - -// Hidden is alias for the Conceal -func Hidden(arg interface{}) Value { - return Conceal(arg) -} - -// CrossedOut makes characters legible, but -// marked for deletion (9). -func CrossedOut(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.CrossedOut() - } - return value{value: arg, color: CrossedOutFm} -} - -// StrikeThrough is alias for the CrossedOut. -func StrikeThrough(arg interface{}) Value { - return CrossedOut(arg) -} - -// Framed (51). -func Framed(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Framed() - } - return value{value: arg, color: FramedFm} -} - -// Encircled (52). -func Encircled(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Encircled() - } - return value{value: arg, color: EncircledFm} -} - -// Overlined (53). -func Overlined(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Overlined() - } - return value{value: arg, color: OverlinedFm} -} - -// -// Foreground colors -// -// - -// Black foreground color (30) -func Black(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Black() - } - return value{value: arg, color: BlackFg} -} - -// Red foreground color (31) -func Red(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Red() - } - return value{value: arg, color: RedFg} -} - -// Green foreground color (32) -func Green(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Green() - } - return value{value: arg, color: GreenFg} -} - -// Yellow foreground color (33) -func Yellow(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Yellow() - } - return value{value: arg, color: YellowFg} -} - -// Brown foreground color (33) -// -// Deprecated: use Yellow instead, following specification -func Brown(arg interface{}) Value { - return Yellow(arg) -} - -// Blue foreground color (34) -func Blue(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Blue() - } - return value{value: arg, color: BlueFg} -} - -// Magenta foreground color (35) -func Magenta(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Magenta() - } - return value{value: arg, color: MagentaFg} -} - -// Cyan foreground color (36) -func Cyan(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Cyan() - } - return value{value: arg, color: CyanFg} -} - -// White foreground color (37) -func White(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.White() - } - return value{value: arg, color: WhiteFg} -} - -// -// Bright foreground colors -// - -// BrightBlack foreground color (90) -func BrightBlack(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightBlack() - } - return value{value: arg, color: BrightFg | BlackFg} -} - -// BrightRed foreground color (91) -func BrightRed(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightRed() - } - return value{value: arg, color: BrightFg | RedFg} -} - -// BrightGreen foreground color (92) -func BrightGreen(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightGreen() - } - return value{value: arg, color: BrightFg | GreenFg} -} - -// BrightYellow foreground color (93) -func BrightYellow(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightYellow() - } - return value{value: arg, color: BrightFg | YellowFg} -} - -// BrightBlue foreground color (94) -func BrightBlue(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightBlue() - } - return value{value: arg, color: BrightFg | BlueFg} -} - -// BrightMagenta foreground color (95) -func BrightMagenta(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightMagenta() - } - return value{value: arg, color: BrightFg | MagentaFg} -} - -// BrightCyan foreground color (96) -func BrightCyan(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightCyan() - } - return value{value: arg, color: BrightFg | CyanFg} -} - -// BrightWhite foreground color (97) -func BrightWhite(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BrightWhite() - } - return value{value: arg, color: BrightFg | WhiteFg} -} - -// -// Other -// - -// Index of pre-defined 8-bit foreground color -// from 0 to 255 (38;5;n). -// -// 0- 7: standard colors (as in ESC [ 30–37 m) -// 8- 15: high intensity colors (as in ESC [ 90–97 m) -// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) -// 232-255: grayscale from black to white in 24 steps -// -func Index(n uint8, arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Index(n) - } - return value{value: arg, color: (Color(n) << shiftFg) | flagFg} -} - -// Gray from 0 to 24. -func Gray(n uint8, arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.Gray(n) - } - if n > 23 { - n = 23 - } - return value{value: arg, color: (Color(232+n) << shiftFg) | flagFg} -} - -// -// Background colors -// -// - -// BgBlack background color (40) -func BgBlack(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBlack() - } - return value{value: arg, color: BlackBg} -} - -// BgRed background color (41) -func BgRed(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgRed() - } - return value{value: arg, color: RedBg} -} - -// BgGreen background color (42) -func BgGreen(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgGreen() - } - return value{value: arg, color: GreenBg} -} - -// BgYellow background color (43) -func BgYellow(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgYellow() - } - return value{value: arg, color: YellowBg} -} - -// BgBrown background color (43) -// -// Deprecated: use BgYellow instead, following specification -func BgBrown(arg interface{}) Value { - return BgYellow(arg) -} - -// BgBlue background color (44) -func BgBlue(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBlue() - } - return value{value: arg, color: BlueBg} -} - -// BgMagenta background color (45) -func BgMagenta(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgMagenta() - } - return value{value: arg, color: MagentaBg} -} - -// BgCyan background color (46) -func BgCyan(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgCyan() - } - return value{value: arg, color: CyanBg} -} - -// BgWhite background color (47) -func BgWhite(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgWhite() - } - return value{value: arg, color: WhiteBg} -} - -// -// Bright background colors -// - -// BgBrightBlack background color (100) -func BgBrightBlack(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightBlack() - } - return value{value: arg, color: BrightBg | BlackBg} -} - -// BgBrightRed background color (101) -func BgBrightRed(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightRed() - } - return value{value: arg, color: BrightBg | RedBg} -} - -// BgBrightGreen background color (102) -func BgBrightGreen(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightGreen() - } - return value{value: arg, color: BrightBg | GreenBg} -} - -// BgBrightYellow background color (103) -func BgBrightYellow(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightYellow() - } - return value{value: arg, color: BrightBg | YellowBg} -} - -// BgBrightBlue background color (104) -func BgBrightBlue(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightBlue() - } - return value{value: arg, color: BrightBg | BlueBg} -} - -// BgBrightMagenta background color (105) -func BgBrightMagenta(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightMagenta() - } - return value{value: arg, color: BrightBg | MagentaBg} -} - -// BgBrightCyan background color (106) -func BgBrightCyan(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightCyan() - } - return value{value: arg, color: BrightBg | CyanBg} -} - -// BgBrightWhite background color (107) -func BgBrightWhite(arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgBrightWhite() - } - return value{value: arg, color: BrightBg | WhiteBg} -} - -// -// Other -// - -// BgIndex of 8-bit pre-defined background color -// from 0 to 255 (48;5;n). -// -// 0- 7: standard colors (as in ESC [ 40–47 m) -// 8- 15: high intensity colors (as in ESC [100–107 m) -// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) -// 232-255: grayscale from black to white in 24 steps -// -func BgIndex(n uint8, arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgIndex(n) - } - return value{value: arg, color: (Color(n) << shiftBg) | flagBg} -} - -// BgGray from 0 to 24. -func BgGray(n uint8, arg interface{}) Value { - if val, ok := arg.(Value); ok { - return val.BgGray(n) - } - if n > 23 { - n = 23 - } - return value{value: arg, color: (Color(n+232) << shiftBg) | flagBg} -} diff --git a/vendor/github.com/magefile/mage/sh/cmd.go b/vendor/github.com/magefile/mage/sh/cmd.go index 35d232a4..312de65a 100644 --- a/vendor/github.com/magefile/mage/sh/cmd.go +++ b/vendor/github.com/magefile/mage/sh/cmd.go @@ -89,14 +89,14 @@ func OutputWith(env map[string]string, cmd string, args ...string) (string, erro return strings.TrimSuffix(buf.String(), "\n"), err } -// Exec executes the command, piping its stderr to mage's stderr and -// piping its stdout to the given writer. If the command fails, it will return -// an error that, if returned from a target or mg.Deps call, will cause mage to -// exit with the same code as the command failed with. Env is a list of -// environment variables to set when running the command, these override the -// current environment variables set (which are also passed to the command). cmd -// and args may include references to environment variables in $FOO format, in -// which case these will be expanded before the command is run. +// Exec executes the command, piping its stdout and stderr to the given +// writers. If the command fails, it will return an error that, if returned +// from a target or mg.Deps call, will cause mage to exit with the same code as +// the command failed with. Env is a list of environment variables to set when +// running the command, these override the current environment variables set +// (which are also passed to the command). cmd and args may include references +// to environment variables in $FOO format, in which case these will be +// expanded before the command is run. // // Ran reports if the command ran (rather than was not found or not executable). // Code reports the exit code the command returned if it ran. If err == nil, ran diff --git a/vendor/github.com/pterm/pterm/.gitignore b/vendor/github.com/pterm/pterm/.gitignore index e4354651..dc83dde7 100644 --- a/vendor/github.com/pterm/pterm/.gitignore +++ b/vendor/github.com/pterm/pterm/.gitignore @@ -15,7 +15,7 @@ vendor/ # This is where we test stuff -/experimenting/ +/experimenting/* /.history /.vscode diff --git a/vendor/github.com/pterm/pterm/.golangci.yml b/vendor/github.com/pterm/pterm/.golangci.yml index 57ba35d8..a964631f 100644 --- a/vendor/github.com/pterm/pterm/.golangci.yml +++ b/vendor/github.com/pterm/pterm/.golangci.yml @@ -28,7 +28,6 @@ linters: - gosec - govet - ineffassign - - interfacer - unconvert - gosimple - godox diff --git a/vendor/github.com/pterm/pterm/CHANGELOG.md b/vendor/github.com/pterm/pterm/CHANGELOG.md deleted file mode 100644 index d8f4c8e3..00000000 --- a/vendor/github.com/pterm/pterm/CHANGELOG.md +++ /dev/null @@ -1,1237 +0,0 @@ - -## [Unreleased] - -### Features -- **logger:** implemented structured logging -- **logger:** implemented structured logging -- **logger:** implemented structured logging -- **logger:** added logger -- **logger:** create logger -- **rgb:** made it possible to use RGB colors as background -- **rgb:** made it possible to use RGB colors as background -- **rgb:** made it possible to use RGB colors as background - -### Bug Fixes -- **rgb:** fix Fade maxValue == current not displaying the last color - - - -## [v0.12.57] - 2023-03-28 -### Code Refactoring -- use `pterm.Print` instead of `fmt.Print` functions - - - -## [v0.12.56] - 2023-03-14 -### Bug Fixes -- **table:** fixed panic when multiple lines contained color in a single row - - - -## [v0.12.55] - 2023-03-04 -### Features -- **table:** multiline support for table printer -- **table:** multiline support for table printer - -### Code Refactoring -- **table:** fixed linting - - - -## [v0.12.54] - 2023-01-22 -### Bug Fixes -- **tree:** print top node [#443](https://github.com/pterm/pterm/issues/443) - - - -## [v0.12.53] - 2023-01-05 -### Features -- **color:** added `color.ToStyle()` -- **color:** added `color.ToStyle()` -- **progressbar:** added optional title to `Start` method - -### Bug Fixes -- **prefix:** fixed line numbers in different print functions - - - -## [v0.12.52] - 2023-01-05 -### Features -- **multiselect:** added theme support for checkmarks -- **multiselect:** added theme support for checkmarks - -### Test -- **multiselect:** fixed test - -### Code Refactoring -- **progressbar:** make add more safe - - - -## [v0.12.51] - 2022-12-24 -### Bug Fixes -- Make sure the confirm printer can clean up after Ctrl+C - - - -## [v0.12.50] - 2022-11-22 -### Bug Fixes -- revert original test & add new test -- slice bounds out of range on select printer - - - -## [v0.12.49] - 2022-10-03 - - -## [v0.12.48] - 2022-10-02 -### Features -- custom select/confirm key for interactive printer -- add flag to disable filter/search for interactive printer - - - -## [v0.12.47] - 2022-09-19 -### Features -- adding interactive continue printer - -### Bug Fixes -- typo -- append the selected value to the prompt - -### Code Refactoring -- ignore invalid custom handles -- initiazile handles on getSuffix -- comment format -- address renaming PR comments -- show full handles by default -- use a map for the options - -### Reverts -- refactor: use a map for the options - - - -## [v0.12.46] - 2022-09-05 -### Features -- **putils:** add `CenterText` in putils - -### Bug Fixes -- **textinput:** fixed overwriting the default values - - - -## [v0.12.45] - 2022-07-26 -### Bug Fixes -- make sure the interactive printers can cleanup after Ctrl+C -- the interactive confirm answers should match the confirm/reject text - -### Test -- add tests for custom answers - - - -## [v0.12.44] - 2022-07-22 - - -## [v0.12.43] - 2022-07-17 -### Bug Fixes -- **spinner:** fix line didn't clear properly -- **table:** fixed column length calculation for Chinese strings - - - -## [v0.12.42] - 2022-06-21 -### Features -- **input:** added text input printer - - - -## [v0.12.41] - 2022-04-12 - - -## [v0.12.40] - 2022-03-28 -### Features -- added a custom writer for all printers - - - -## [v0.12.39] - 2022-03-18 -### Features -- use fallback color in `BigTextPrinter` when `RGB` is not supported - -### Test -- fix `BigTextPrinter` test -- removed `testdata` -- removed snapshot testing - - - -## [v0.12.38] - 2022-03-09 -### Features -- added `NewLettersFromStringWithRGB` -- added `NewLettersFromStringWithRGB` - -### Test -- **bigtext:** fix formatting error in bigtext test - - - -## [v0.12.37] - 2022-02-17 -### Features -- **progressbar:** Add option to set the `MaxWidth` of the progressbar - -### Test -- **progressbar:** added 100% test coverage for new features - -### Code Refactoring -- **putils:** Improved styling - - - -## [v0.12.36] - 2022-02-01 -### Features -- proposal horizontal table header row and row separators fixes -- proposal horizontal table header row and row separators changes -- proposal horizontal table header row and row separators changes -- proposal horizontal table header row and row separators changes -- proposal horizontal table header row and row separators changes -- proposal horizontal table header row and row separators changes -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators changes -- proposal horizontal table header row and row separators changes -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators -- proposal horizontal table header row and row separators - - - -## [v0.12.35] - 2022-02-01 -### Code Refactoring -- fix linting -- regenerate snapshots - - - -## [v0.12.34] - 2022-01-16 -### Bug Fixes -- **progressbar:** refresh progressbars on every PTerm print ([#302](https://github.com/pterm/pterm/issues/302)) - -### Test -- removed `AreaPrinter` test output -- **table:** changed mock reader from `os.Stdin` to `outBuf` - - - -## [v0.12.33] - 2021-10-24 -### Features -- add `PrintOnErrorf` for every `TextPrinter` ([#279](https://github.com/pterm/pterm/issues/279)) -- **coverage:** add unit test -- **progressbar:** made updating the progressbar title easier. ([#267](https://github.com/pterm/pterm/issues/267)) -- **table:** increase test coverage -- **table:** revamp to follow practice -- **table:** add support for right data alignment - -### Bug Fixes -- **idea:** revert unwanted config changes -- **linter:** do linter recommendation to delete fallthrough - - - -## [v0.12.32] - 2021-10-15 -### Features -- added `AreaPrinter.Clear()` - -### Bug Fixes -- progressbar method name -- **header:** fixed length calculation for Chinese strings - -### Code Refactoring -- change bitSize size - - - -## [v0.12.31] - 2021-09-21 -### Features -- **prefix:** added `LineNumberOffset` to `PrefixPrinter` - - - -## [v0.12.30] - 2021-08-16 -### Bug Fixes -- **style:** resetting to previous color also resets attributes - -### Code Refactoring -- adapt new testza function name - - - -## [v0.12.29] - 2021-07-19 -### Features -- **putils:** add `PrintAverageExecutionTime` - -### Test -- fix rgb error test -- fix internal test import cycle -- move tests into own package - -### Code Refactoring -- replace `testify` with `testza` - - - -## [v0.12.28] - 2021-07-17 -### Features -- **spinner:** add option to show a timer - -### Bug Fixes -- **bar chart:** fix panic when rendering empty horizontal bar chart - -### Test -- **spinner:** try to fix RawOutput text -- **spinner:** add raw output test - -### Code Refactoring -- **spinner:** better raw output logic -- **spinner:** refactor - - - -## [v0.12.27] - 2021-07-05 -### Bug Fixes -- **style:** fix multiline style coloring - -### Test -- **style:** fix multiline style coloring -- **style:** fix multiline style coloring - - - -## [v0.12.26] - 2021-07-01 -### Bug Fixes -- **spinner:** Override previous text in `UpdateText` - - - -## [v0.12.25] - 2021-07-01 -### Features -- **table:** add `Boxed` option - -### Test -- add tests for boxed `TablePrinter` - - - -## [v0.12.24] - 2021-06-13 -### Features -- **boxprinter:** replace line breaks in title with space -- **boxprinter:** add title center position to `BoxPrinter` -- **boxprinter:** add title & title position to `BoxPrinter` -- **boxprinter:** add title & title position to `BoxPrinter` -- **putils:** add `TableDataFromSeparatedValues` -- **putils:** add `TableDataFromTSV` -- **putils:** add `TableDataFromCSV` -- **putils:** add function to convert TSV to `TableData` -- **putils:** add function to convert CSV to `TableData` - -### Test -- add test for putils `TableData` generation -- **boxprinter:** add tests for title center position to `BoxPrinter` -- **boxprinter:** add tests for title & title position - -### Code Refactoring -- **boxprinter:** prefix title positions with `Title` -- **putils:** add `rowSeparator` to `TableFromSeparatedValues` - - - -## [v0.12.23] - 2021-06-07 -### Features -- Add util functions to create tables from slices of structs ([#217](https://github.com/pterm/pterm/issues/217)) - -### Bug Fixes -- **headerprinter:** don't panic if content width > terminal width - -### Test -- **prefix:** `pterm.Error` default no line number shown - -### Code Refactoring -- **prefix:** `pterm.Error` default no line number shown - - - -## [v0.12.22] - 2021-05-30 -### Features -- make spinner update faster - -### Performance Improvements -- improve performance of `SpinnerPrinter` - - - -## [v0.12.21] - 2021-05-30 -### Features -- print lines above active spinners -- **putils:** add `DownloadFileWithProgressbar` - -### Test -- clear active spinners after tests complete - -### Code Refactoring -- **putils:** change internal variable name - - - -## [v0.12.20] - 2021-05-29 -### Features -- force color output by default - - - -## [v0.12.19] - 2021-05-29 -### Features -- add `PrintOnError` for all printers and interface -- **putils:** add `putils` package ([#206](https://github.com/pterm/pterm/issues/206)) - -### Bug Fixes -- **header:** fix multiline header - -### Test -- add tests for all printers for `PrintOnError` - -### Code Refactoring -- make `PrintOnError` return `*TextPrinter` -- **area:** better height calculation - - - -## [v0.12.18] - 2021-05-22 -### Features -- add `AreaPrinter` -- **area:** add `Center` option -- **area:** add `Fullscreen` option -- **area:** add `GetContent` function -- **area:** add `AreaPrinter` - -### Test -- **area:** fix tests for `AreaPrinter` -- **area:** add `AreaPrinter` tests - -### Code Refactoring -- fix linting errors - - - -## [v0.12.17] - 2021-05-14 -### Bug Fixes -- fix `pterm.Fatal.Printfln` not panicking -- **prefix:** fix `pterm.Fatal.Printfln` not panicking and had output in debug mode - -### Test -- **prefix:** add tests for `Sprintfln` and `Printfln` function when in debug mode - - - -## [v0.12.16] - 2021-05-13 -### Code Refactoring -- **prefix:** make `PrintOnError` accept multiple inputs - - - -## [v0.12.15] - 2021-05-13 -### Features -- add raw output mode for `BarChart` -- add disable styling boolean option -- **bigtext:** add raw output mode -- **centerprinter:** add raw output mode -- **headerprinter:** add raw output mode -- **panelprinter:** add raw output mode -- **paragraph:** add raw output mode -- **prefix:** add `PrintIfError` -- **prefix:** add raw output mode -- **progressbar:** add raw output mode -- **spinner:** add raw output mode - -### Bug Fixes -- **prefix:** fix `PrintOnError` - -### Test -- add tests with `RawOutput` enabled -- add interface tests for `Color` and `RGB` -- added tests for `DisableStyling` and `EnableStyling` - -### Code Refactoring -- correct behaviour of Enable-/DisableStyling -- fix variable names - - - -## [v0.12.14] - 2021-05-09 -### Features -- **basic-text:** add `Sprintfln` and `Printfln` function -- **boxprinter:** add `Sprintfln` and `Printfln` function -- **centerprinter:** add `Sprintfln` and `Printfln` function -- **color:** add `Sprintfln` and `Printfln` function -- **header:** add `Sprintfln` and `Printfln` function -- **paragraph:** add `Sprintfln` and `Printfln` function -- **prefix:** add `Sprintfln` and `Printfln` function -- **print:** add `Sprintfln` and `Printfln` function -- **printer-interface:** add `Sprintfln` and `Printfln` to the interface -- **rgb:** add `Sprintfln` and `Printfln` function -- **section:** add `Sprintfln` and `Printfln` function - -### Bug Fixes -- **header:** fix inline color in `Header` - -### Test -- add tests for `Sprintfln` and `Printfln` function - -### Code Refactoring -- refactor `Sprintfln` and `Printfln` func. for better performance - -### Reverts -- ci: change color scheme for rendered examples - - - -## [v0.12.13] - 2021-04-10 -### Bug Fixes -- **bigtext:** fix height of some characters [#180](https://github.com/pterm/pterm/issues/180) -- **color:** make color implement `TextPrinter` - -### Test -- add interface tests - -### Code Refactoring -- **examples:** center the intro of `demo` -- **examples:** add note to box printer - - - -## [v0.12.12] - 2021-03-01 -### Features -- **prefixprinter:** Add option to show line number of caller - -### Code Refactoring -- **examples:** Update `PrefixPrinter` example - - - -## [v0.12.11] - 2021-02-26 -### Code Refactoring -- refactor print logic of `BoxPrinter` -- refactor print logic of `CenterPrinter` - - - -## [v0.12.10] - 2021-02-26 -### Bug Fixes -- correct `pterm.Println()` behaviour to fit to `fmt.Println()` - - - -## [v0.12.9] - 2021-02-23 -### Bug Fixes -- correct `pterm.Println()` behaviour to fit to `fmt.Println()` -- change terminal package import path to updated version - - - -## [v0.12.8] - 2020-12-11 -### Features -- **boxprinter:** add `WithHorizontalString` to `BoxPrinter` -- **boxprinter:** add `BoxPrinter` -- **panel:** add optional border for `Panel` -- **panelprinter:** add theme support to `PanelPrinter` -- **theme:** add `BoxStyle` and `BoxTextStyle` -- **theme:** add optional theme for border in `Panel` - -### Bug Fixes -- revert change horizontal string change - -### Test -- **boxprinter:** add test -- **boxprinter:** test multiple lines in one box -- **boxprinter:** add tests for `BoxPrinter` -- **panelprinter:** add tests for adding box printer -- **panelprinter:** add tests for optional border for `Panel` -- **theme:** add tests for `BoxStyle` and `BoxTextStyle` - -### Code Refactoring -- remove analytics -- **boxprinter:** return theme when style is nil -- **boxprinter:** change `DefaultBox` top and bottom padding to 0 -- **boxprinter:** fix spacing between boxes and in boxes -- **boxprinter:** refactor code -- **boxprinter:** change from `RenderablePrinter` to `TextPrinter` -- **panelprinter:** add `BoxPrinter` to surround panels with a fully custom box -- **panelprinter:** optional border for `Panel` - - - -## [v0.12.7] - 2020-11-24 -### Features -- add values to chart -- add horizontal `BarChartPrinter` -- add `BarChartPrinter` -- add `BarChartPrinter` -- add `BarChartPrinter` -- **theme:** add theme support to `BarChart` - -### Bug Fixes -- center bars over colored labels in `BarChart` - -### Test -- add tests to `BarChartPrinter` - - - -## [v0.12.6] - 2020-11-17 -### Bug Fixes -- disabling output works as expected now ([#149](https://github.com/pterm/pterm/issues/149)) - - - -## [v0.12.5] - 2020-11-17 -### Bug Fixes -- fix `PrefixPrinter` with multiple trailing newline endings. - - - -## [v0.12.4] - 2020-11-17 -### Bug Fixes -- fix `Printf` of `PrefixPrinter` - - - -## [v0.12.3] - 2020-11-12 -### Test -- reduce tests -- different test logic for rgb printing -- add better test names for `RGB` tests - - - -## [v0.12.2] - 2020-11-05 -### Features -- color each line separately when using multi line input - -### Bug Fixes -- fix internal `GetStringMaxWidth` max width - -### Test -- **basictext:** proxy print functions to DevNull -- **progressbar:** proxy print functions to DevNull - -### Code Refactoring -- use `pterm.Sprint` to print - - - -## [v0.12.1] - 2020-11-04 -### Bug Fixes -- **panel:** Fix output when input is colored - -### Performance Improvements -- **header:** calculate margin faster - - - -## [v0.12.0] - 2020-11-04 -### Features -- **panel:** add an option to make a padding beneath `panel` -- **panel:** add an option to make columns the same length - -### Bug Fixes -- **panel:** add invalid check for `padding` in `panel` - -### Test -- **bulletlist:** `BulletListItem` remove `Render` and `Srender` -- **bulletlist:** change `BulletList` to `BulletListPrinter` -- **panel:** add invalid check for `padding` in `panel` -- **panel:** add test for `WithBottomPadding` -- **panel:** add test for `WithSameColumnWidth` & multiple `panel` -- **panel:** add test for `WithSameColumnWidth` -- **progressbar:** change directory name `progressbar_test` to `progressbar_printer_test` -- **progressbar:** change `Progressbar` to `ProgressbarPrinter` -- **spinner:** change directory name `spinner_test` to `spinner_printer_test` -- **spinner:** change `Spinner` to `SpinnerPrinter` -- **table:** change `Table` to `TablePrinter` -- **tree:** change `Tree` to `TreePrinter` - -### Code Refactoring -- make all printer names end with `Printer` ([#134](https://github.com/pterm/pterm/issues/134)) -- **bulletlist:** remove `DefaultBulletListItem` -- **bulletlist:** `BulletListItem` remove `Render` and `Srender` -- **bulletlist:** `BulletListItem` is no renderable anymore -- **bulletlist:** change `BulletList` to `BulletListPrinter` -- **progressbar:** change `ActiveProgressbars` to `ActiveProgressbarPrinters` -- **progressbar:** change directory name `progressbar` to `progressbar_printer` -- **progressbar:** change `Progressbar` to `ProgressbarPrinter` -- **spinner:** change directory name `spinner` to `spinner_printer` -- **spinner:** change `Spinner` to `SpinnerPrinter` -- **table:** change `Table` to `TablePrinter` -- **tree:** change `Tree` to `TreePrinter` - -### BREAKING CHANGE - -Removed `DefaultBulletListItem`. - -Change names of printers which didn't end with `Printer`. Every printer name ends with `Printer` now to fit into the new naming convention. - -change `ActiveProgressbars` to `ActiveProgressbarPrinters` - -`BulletListItem` is no renderable anymore, removed `Render` and `Srender` - -`BulletListItem` is no renderable anymore, removed `Render` and `Srender` - -`BulletListItem` is no renderable anymore - -change `Tree` to `TreePrinter` to unify the naming scheme - -change `Tree` to `TreePrinter` to unify the naming scheme - -change `Table` to `TablePrinter` to unify the naming scheme - -change `Table` to `TablePrinter` to unify the naming scheme - -change `Spinner` to `SpinnerPrinter` to unify the naming scheme - -change `Spinner` to `SpinnerPrinter` to unify the naming scheme - -change `Progressbar` to `ProgressbarPrinter` to unify the naming scheme - -change `Progressbar` to `ProgressbarPrinter` to unify the naming scheme - -change `BulletList` to `BulletListPrinter` to unify the naming scheme - -change `BulletList` to `BulletListPrinter` to unify the naming scheme - - - -## [v0.11.0] - 2020-11-03 -### Features -- add `PanelPrinter` - -### Bug Fixes -- **centerprinter:** make centerprinter func return pointer - -### BREAKING CHANGE - -make centerprinter func `WithCenterEachLineSeparately` return a pointer of centerprinter - - - -## [v0.10.1] - 2020-11-02 -### Features -- add `CenterPrinter` - - - -## [v0.10.0] - 2020-11-01 -### Features -- make printers return errors -- add `DisableOutput()` and `EnableOutput()` ([#108](https://github.com/pterm/pterm/issues/108)) - -### Code Refactoring -- ignore errors where no errors can occur -- **theme:** change `ListTextStyle` to `BulletListTextStyle` ([#104](https://github.com/pterm/pterm/issues/104)) -- **theme:** change `ProgressbarBarStyle` to `FgCyan` ([#106](https://github.com/pterm/pterm/issues/106)) -- **theme:** change white to default color in `Theme` ([#103](https://github.com/pterm/pterm/issues/103)) - -### BREAKING CHANGE - -Interface of `RenderablePrinter` and `LivePrinter` changed. - -The global variable `DisableOutput` was renamed to `Output`. - - - -## [v0.9.3] - 2020-10-31 -### Features -- add a levelList converter for TreeListPrinter -- add `TreeListPrinter` as a renderable printer -- add `TreeListPrinter` as a renderable printer -- **theme:** add theme support for `Tree` - -### Test -- **tree:** add `Tree` tests - -### Code Refactoring -- clean up `Tree` -- **theme:** change `TreeTextStyle` to `FgDefault` -- **tree:** add Indent to control the spacing between levels and changed docs(examples) -- **tree:** add more spacing between levels -- **tree:** refactor `Tree` code and write tests for `Tree` -- **tree:** refactor `Tree` code and write tests for `Tree` -- **tree:** refactor `Tree` code -- **tree:** refactor `Tree` code -- **tree:** refactor `Tree` code - - - -## [v0.9.2] - 2020-10-29 -### Features -- add option to disable and enable colors - - - -## [v0.9.1] - 2020-10-27 -### Code Refactoring -- make the prefix of `Info` smaller again - - - -## [v0.9.0] - 2020-10-27 -### Features -- add `Debug` `PrefixPrinter` -- add support for enabling and disabling debug messages - -### Bug Fixes -- progressbar disappears when done and something is printed after - -### Test -- add debugger tests to `PrefixPrinter` -- add progressbar tests - -### Code Refactoring -- remove `UpdateDelay` from `Progressbar` -- change `NewList` to `NewBulletList` -- change `NewList` to `NewBulletList` -- deprecate `UpdateDelay` in `Progressbar` - -### BREAKING CHANGE - -Removed `UpdateDelay` from `Progressbar`. It's no longer used. The Progressbar automatically updates on every change to the current value. - -Changed `NewList` to `NewBulletList`. - - - -## [v0.8.1] - 2020-10-26 -### Features -- add fade from one RGB over several RGBs to another RGB - -### Code Refactoring -- refactor doc -- refactor code - - - -## [v0.8.0] - 2020-10-24 -### Features -- add `BigTextPrinter` ([#75](https://github.com/pterm/pterm/issues/75)) -- use level of section printer -- add `BulletListPrinter` ([#67](https://github.com/pterm/pterm/issues/67)) - -### Test -- test that `%s` won't fail to print - -### Code Refactoring -- make `BigTextPrinter` release ready -- change `LineCharacter` to `BarCharacter` ([#70](https://github.com/pterm/pterm/issues/70)) - -### BREAKING CHANGE - -Changed `LineCharacter` to `BarCharacter`. - - - -## [v0.7.0] - 2020-10-20 -### Features -- **progressbar:** add RemoveWhenDone - -### Bug Fixes -- make theme accept pointer styles -- make Spinner accept pointer Style -- make WithMessageStyle accept Style pointer -- add nil check to SectionPrinter Style -- section printer Style to pointer - -### Test -- add tests color and style -- add tests to root print functions -- add tests to progressbar -- add tests to terminal -- add tests to theme -- fix internal percentage test -- add tests to Spinner -- add tests for TablePrinter -- special tests for special statements -- complete PrefixPrinter tests -- add PrefixPrinter tests -- rename HeaderPrinter tests -- complete HeaderPrinter tests -- add ParagraphPrinter tests -- add HeaderPrinter tests -- make unit test system check different types -- add SectionPrinter tests -- implement test utils -- add rgb tests - -### Code Refactoring -- use log output -- remove obsolete if -- fit progressbar to new percentage calculation method -- make fatal panic -- rename parameters -- don't show empty line when removing a progressbar - - - -## [v0.6.1] - 2020-10-20 -### Bug Fixes -- fix RGB methods - - - -## [v0.6.0] - 2020-10-19 -### Features -- add BasicTextPrinter -- add theme support to section and table printer -- add theme support to spinner -- add theme support to headers -- add template support for progressbars -- add default theme - -### Test -- **benchmark:** fix spinner benchmark - -### Code Refactoring -- make printers accept pointers to styles -- remove emojis to comply with cross-platform policy -- change LivePrinter interface to pointer output -- change TextPrinter interface to pointer output - -### BREAKING CHANGE - -All printers only accept pointers as any `Style` attribute. - -LivePrinter now requires to return a pointer. - -TextPrinter now requires to return a pointer. - - - -## [v0.5.1] - 2020-10-14 -### Features -- add ability to disable output ([#44](https://github.com/pterm/pterm/issues/44)) -- add `Srender` to `RenderPrinter` interface -- add csv table support ([#42](https://github.com/pterm/pterm/issues/42)) -- add HEX to RGB converter in `RGB` ([#41](https://github.com/pterm/pterm/issues/41)) -- add theme to generated animations -- add color fade example ([#38](https://github.com/pterm/pterm/issues/38)) -- implement `TextPrinter` into `RGB` -- implement color fade to `Progressbar` ([#37](https://github.com/pterm/pterm/issues/37)) -- add color fade function and `RBG` ([#34](https://github.com/pterm/pterm/issues/34)) -- change `Section` style - -### Code Refactoring -- declare function name as `WithCSVReader` - - - -## [v0.5.0] - 2020-10-08 -### Features -- implement `LivePrinter` in `Spinner` -- add `BottomPadding` to `SectionPrinter` -- add `RenderPrinter` interface -- implement `LivePrinter` in `Progressbar` -- add `LivePrinter` interface -- add `TablePrinter` ([#27](https://github.com/pterm/pterm/issues/27)) -- add `ParagraphPrinter` ([#24](https://github.com/pterm/pterm/issues/24)) - -### Test -- add `Print` equals `Sprint` tests for `GenericPrinter` -- add `Spinner` benchmarks - -### Code Refactoring -- rename spinner_printer.go to spinner.go -- rename `GenericPrinter` to `TextPrinter` - -### BREAKING CHANGE - -The `GenericPrinter` is now called `TextPrinter`. - - - -## [v0.4.1] - 2020-10-07 - - -## [v0.4.0] - 2020-10-07 -### Features -- add `Add` to `Style` -- add options shorthands to `SectionPrinter` - -### Test -- ignore writer close errors in stdout capture - -### Code Refactoring -- use `Style` instead of colors -- refactor function parameters to fit expectation -- rename `RemoveColors` to `RemoveColorFromString` - -### BREAKING CHANGE - -use `Style` instead of colors - -Refactor function parameters to fit expectation. -Affects: `WithStyle(colors -> style)`, `WithScope(string, colors -> scope)` - -rename `RemoveColors` to `RemoveColorFromString` - - - -## [v0.3.2] - 2020-10-06 -### Features -- add `SectionPrinter` - -### Bug Fixes -- fix `Sprintf` function of `HeaderPrinter` - -### Test -- add tests for `HeaderPrinter` and `SectionPrinter` - - - -## [v0.3.1] - 2020-10-06 -### Features -- add `BarFiller` to `Progressbar` - -### Test -- fix import cycle -- change to inbuilt `SetDefaultOutput` option -- add more benchmarks -- add benchmarks -- add tests to `GenericPrinter` and default print methods - -### Code Refactoring -- set default `BarFiller` to space -- move tests directly into `pterm` module - - - -## [v0.3.0] - 2020-10-05 -### Bug Fixes -- fix `WithXYZ(b ...bool)` to detect booleans correctly - -### Code Refactoring -- remove `Version` constant -- change `WithXXX(b bool)` to `WithXXX(b ...bool)` -- change `SetXXX` to `WithXXX` -- change `Header` to `DefaultHeader` - -### BREAKING CHANGE - -remove `Version` constant - -rename `SetXXX` to `WithXXX` - -rename `Header` to `DefaultHeader` - - - -## [v0.2.4] - 2020-10-04 -### Bug Fixes -- `Printf` works again - - - -## [v0.2.3] - 2020-10-04 -### Features -- automatically print above `Progressbar` - -### Code Refactoring -- remove goroutine from `Progressbar` - - - -## [v0.2.2] - 2020-10-04 -### Features -- add `Fatal` printer - - - -## [v0.2.1] - 2020-10-04 -### Features -- make progressbar configurable -- add percentage helper -- add `RemoveColors` -- add `Progressbar` ([#5](https://github.com/pterm/pterm/issues/5)) -- add `Progressbar` -- add fatal to `PrefixPrinter` ([#4](https://github.com/pterm/pterm/issues/4)) -- **progressbar:** fade percentage color according to value - -### Code Refactoring -- bump version to "v0.2.1" - - - -## [v0.2.0] - 2020-09-30 -### Features -- change style of `Description` printer -- add color in color support -- add `RemoveWhenDone` to `Spinner` -- add multiline support to `PrefixPrinter` -- add `UpdateText` to spinner - -### Bug Fixes -- spinners spin evenly when multiple spinners are started - -### Performance Improvements -- improve spinner performance - -### Code Refactoring -- bump version to "v0.2.0" -- change `WithXXX` to `SetXXX` -- removed `Println` aliases - -### BREAKING CHANGE - -every `WithXXX` is renamed to `SetXXX` - -remove `GetFormattedMessage` from `PrefixPrinter` - -removed `Println` aliases - - - -## [v0.1.0] - 2020-09-28 -### Features -- add spinners -- shorten printer names and add builder methods to printers -- add `Printo` to override printed text -- add `FullWidth` to `HeaderPrinter` -- add terminal size detection - -### Code Refactoring -- bump version to "v0.1.0" -- consistent example code for `Printo` -- better comments for `Printo` -- simplify `HeaderPrinter` - -### BREAKING CHANGE - -printer names changed - -removed `Header` and put it's content directly into `HeaderPrinter` - - - -## [v0.0.1] - 2020-09-21 -### Features -- add aliases to default printers -- add header example -- integrate ci -- add `HeaderPrinter` -- add exported version variable -- add example `override-default-printer` -- change prefix text color to `LightWhite` - -### Bug Fixes -- header should now work in CI - -### Code Refactoring -- bump version to "v0.0.1" -- refactor project -- add comments to functions - - - -## v0.0.0 - 2020-09-18 -### Features -- add changelog template -- configs -- initial commit - - -[Unreleased]: https://github.com/pterm/pterm/compare/v0.12.57...HEAD -[v0.12.57]: https://github.com/pterm/pterm/compare/v0.12.56...v0.12.57 -[v0.12.56]: https://github.com/pterm/pterm/compare/v0.12.55...v0.12.56 -[v0.12.55]: https://github.com/pterm/pterm/compare/v0.12.54...v0.12.55 -[v0.12.54]: https://github.com/pterm/pterm/compare/v0.12.53...v0.12.54 -[v0.12.53]: https://github.com/pterm/pterm/compare/v0.12.52...v0.12.53 -[v0.12.52]: https://github.com/pterm/pterm/compare/v0.12.51...v0.12.52 -[v0.12.51]: https://github.com/pterm/pterm/compare/v0.12.50...v0.12.51 -[v0.12.50]: https://github.com/pterm/pterm/compare/v0.12.49...v0.12.50 -[v0.12.49]: https://github.com/pterm/pterm/compare/v0.12.48...v0.12.49 -[v0.12.48]: https://github.com/pterm/pterm/compare/v0.12.47...v0.12.48 -[v0.12.47]: https://github.com/pterm/pterm/compare/v0.12.46...v0.12.47 -[v0.12.46]: https://github.com/pterm/pterm/compare/v0.12.45...v0.12.46 -[v0.12.45]: https://github.com/pterm/pterm/compare/v0.12.44...v0.12.45 -[v0.12.44]: https://github.com/pterm/pterm/compare/v0.12.43...v0.12.44 -[v0.12.43]: https://github.com/pterm/pterm/compare/v0.12.42...v0.12.43 -[v0.12.42]: https://github.com/pterm/pterm/compare/v0.12.41...v0.12.42 -[v0.12.41]: https://github.com/pterm/pterm/compare/v0.12.40...v0.12.41 -[v0.12.40]: https://github.com/pterm/pterm/compare/v0.12.39...v0.12.40 -[v0.12.39]: https://github.com/pterm/pterm/compare/v0.12.38...v0.12.39 -[v0.12.38]: https://github.com/pterm/pterm/compare/v0.12.37...v0.12.38 -[v0.12.37]: https://github.com/pterm/pterm/compare/v0.12.36...v0.12.37 -[v0.12.36]: https://github.com/pterm/pterm/compare/v0.12.35...v0.12.36 -[v0.12.35]: https://github.com/pterm/pterm/compare/v0.12.34...v0.12.35 -[v0.12.34]: https://github.com/pterm/pterm/compare/v0.12.33...v0.12.34 -[v0.12.33]: https://github.com/pterm/pterm/compare/v0.12.32...v0.12.33 -[v0.12.32]: https://github.com/pterm/pterm/compare/v0.12.31...v0.12.32 -[v0.12.31]: https://github.com/pterm/pterm/compare/v0.12.30...v0.12.31 -[v0.12.30]: https://github.com/pterm/pterm/compare/v0.12.29...v0.12.30 -[v0.12.29]: https://github.com/pterm/pterm/compare/v0.12.28...v0.12.29 -[v0.12.28]: https://github.com/pterm/pterm/compare/v0.12.27...v0.12.28 -[v0.12.27]: https://github.com/pterm/pterm/compare/v0.12.26...v0.12.27 -[v0.12.26]: https://github.com/pterm/pterm/compare/v0.12.25...v0.12.26 -[v0.12.25]: https://github.com/pterm/pterm/compare/v0.12.24...v0.12.25 -[v0.12.24]: https://github.com/pterm/pterm/compare/v0.12.23...v0.12.24 -[v0.12.23]: https://github.com/pterm/pterm/compare/v0.12.22...v0.12.23 -[v0.12.22]: https://github.com/pterm/pterm/compare/v0.12.21...v0.12.22 -[v0.12.21]: https://github.com/pterm/pterm/compare/v0.12.20...v0.12.21 -[v0.12.20]: https://github.com/pterm/pterm/compare/v0.12.19...v0.12.20 -[v0.12.19]: https://github.com/pterm/pterm/compare/v0.12.18...v0.12.19 -[v0.12.18]: https://github.com/pterm/pterm/compare/v0.12.17...v0.12.18 -[v0.12.17]: https://github.com/pterm/pterm/compare/v0.12.16...v0.12.17 -[v0.12.16]: https://github.com/pterm/pterm/compare/v0.12.15...v0.12.16 -[v0.12.15]: https://github.com/pterm/pterm/compare/v0.12.14...v0.12.15 -[v0.12.14]: https://github.com/pterm/pterm/compare/v0.12.13...v0.12.14 -[v0.12.13]: https://github.com/pterm/pterm/compare/v0.12.12...v0.12.13 -[v0.12.12]: https://github.com/pterm/pterm/compare/v0.12.11...v0.12.12 -[v0.12.11]: https://github.com/pterm/pterm/compare/v0.12.10...v0.12.11 -[v0.12.10]: https://github.com/pterm/pterm/compare/v0.12.9...v0.12.10 -[v0.12.9]: https://github.com/pterm/pterm/compare/v0.12.8...v0.12.9 -[v0.12.8]: https://github.com/pterm/pterm/compare/v0.12.7...v0.12.8 -[v0.12.7]: https://github.com/pterm/pterm/compare/v0.12.6...v0.12.7 -[v0.12.6]: https://github.com/pterm/pterm/compare/v0.12.5...v0.12.6 -[v0.12.5]: https://github.com/pterm/pterm/compare/v0.12.4...v0.12.5 -[v0.12.4]: https://github.com/pterm/pterm/compare/v0.12.3...v0.12.4 -[v0.12.3]: https://github.com/pterm/pterm/compare/v0.12.2...v0.12.3 -[v0.12.2]: https://github.com/pterm/pterm/compare/v0.12.1...v0.12.2 -[v0.12.1]: https://github.com/pterm/pterm/compare/v0.12.0...v0.12.1 -[v0.12.0]: https://github.com/pterm/pterm/compare/v0.11.0...v0.12.0 -[v0.11.0]: https://github.com/pterm/pterm/compare/v0.10.1...v0.11.0 -[v0.10.1]: https://github.com/pterm/pterm/compare/v0.10.0...v0.10.1 -[v0.10.0]: https://github.com/pterm/pterm/compare/v0.9.3...v0.10.0 -[v0.9.3]: https://github.com/pterm/pterm/compare/v0.9.2...v0.9.3 -[v0.9.2]: https://github.com/pterm/pterm/compare/v0.9.1...v0.9.2 -[v0.9.1]: https://github.com/pterm/pterm/compare/v0.9.0...v0.9.1 -[v0.9.0]: https://github.com/pterm/pterm/compare/v0.8.1...v0.9.0 -[v0.8.1]: https://github.com/pterm/pterm/compare/v0.8.0...v0.8.1 -[v0.8.0]: https://github.com/pterm/pterm/compare/v0.7.0...v0.8.0 -[v0.7.0]: https://github.com/pterm/pterm/compare/v0.6.1...v0.7.0 -[v0.6.1]: https://github.com/pterm/pterm/compare/v0.6.0...v0.6.1 -[v0.6.0]: https://github.com/pterm/pterm/compare/v0.5.1...v0.6.0 -[v0.5.1]: https://github.com/pterm/pterm/compare/v0.5.0...v0.5.1 -[v0.5.0]: https://github.com/pterm/pterm/compare/v0.4.1...v0.5.0 -[v0.4.1]: https://github.com/pterm/pterm/compare/v0.4.0...v0.4.1 -[v0.4.0]: https://github.com/pterm/pterm/compare/v0.3.2...v0.4.0 -[v0.3.2]: https://github.com/pterm/pterm/compare/v0.3.1...v0.3.2 -[v0.3.1]: https://github.com/pterm/pterm/compare/v0.3.0...v0.3.1 -[v0.3.0]: https://github.com/pterm/pterm/compare/v0.2.4...v0.3.0 -[v0.2.4]: https://github.com/pterm/pterm/compare/v0.2.3...v0.2.4 -[v0.2.3]: https://github.com/pterm/pterm/compare/v0.2.2...v0.2.3 -[v0.2.2]: https://github.com/pterm/pterm/compare/v0.2.1...v0.2.2 -[v0.2.1]: https://github.com/pterm/pterm/compare/v0.2.0...v0.2.1 -[v0.2.0]: https://github.com/pterm/pterm/compare/v0.1.0...v0.2.0 -[v0.1.0]: https://github.com/pterm/pterm/compare/v0.0.1...v0.1.0 -[v0.0.1]: https://github.com/pterm/pterm/compare/v0.0.0...v0.0.1 diff --git a/vendor/github.com/pterm/pterm/README.md b/vendor/github.com/pterm/pterm/README.md index 73d4b098..c7a37292 100644 --- a/vendor/github.com/pterm/pterm/README.md +++ b/vendor/github.com/pterm/pterm/README.md @@ -35,13 +35,17 @@ Downloads + + + +

PTerm -

Show Demo Code

+

Show Demo Code

@@ -88,25 +92,30 @@ go get github.com/pterm/pterm ### Printers (Components) +
+ | Feature | Feature | Feature | Feature | Feature | | :-------: | :-------: | :-------: | :-------: | :-------: | | Area
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/area) |Barchart
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/barchart) |Basictext
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/basictext) |Bigtext
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/bigtext) |Box
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/box) | -| Bulletlist
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/bulletlist) |Center
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/center) |Coloring
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/coloring) |Demo
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/demo) |Header
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/header) | +| Bulletlist
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/bulletlist) |Center
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/center) |Coloring
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/coloring) |Header
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/header) |Heatmap
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/heatmap) | | Interactive confirm
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_confirm) |Interactive continue
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_continue) |Interactive multiselect
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_multiselect) |Interactive select
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_select) |Interactive textinput
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_textinput) | -| Logger
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/logger) |Panel
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/panel) |Paragraph
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/paragraph) |Prefix
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/prefix) |Progressbar
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/progressbar) | -| Section
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/section) |Spinner
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/spinner) |Style
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/style) |Table
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/table) |Theme
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/theme) | -| Tree
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/tree) | | | | | +| Logger
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/logger) |Multiple-live-printers
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/multiple-live-printers) |Panel
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/panel) |Paragraph
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/paragraph) |Prefix
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/prefix) | +| Progressbar
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/progressbar) |Section
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/section) |Slog
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/slog) |Spinner
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/spinner) |Style
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/style) | +| Table
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/table) |Theme
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/theme) |Tree
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/tree) | | | +
+ +---
-### 🦸‍♂️ Supporters +### 🦸‍♂️ Sponsors -|-|User|💸| -|---|---|---| -|![Jens Lauterbach](https://avatars.githubusercontent.com/u/1292368?s=25)|[@jenslauterbach](https://github.com/jenslauterbach)|25$| + + +---
@@ -126,9 +135,9 @@ go get github.com/pterm/pterm

-### area/center +### area/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/center/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/demo/animation.svg)
@@ -141,16 +150,37 @@ import ( "time" "github.com/pterm/pterm" + "github.com/pterm/pterm/putils" ) func main() { + // Print an informational message using PTerm's Info printer. + // This message will stay in place while the area updates. + pterm.Info.Println("The previous text will stay in place, while the area updates.") + + // Print two new lines as spacer. + pterm.Print("\n\n") + + // Start the Area printer from PTerm's DefaultArea, with the Center option. + // The Area printer allows us to update a specific area of the console output. + // The returned 'area' object is used to control the area updates. area, _ := pterm.DefaultArea.WithCenter().Start() - for i := 0; i < 5; i++ { - area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i)) + // Loop 10 times to update the area with the current time. + for i := 0; i < 10; i++ { + // Get the current time, format it as "15:04:05" (hour:minute:second), and convert it to a string. + // Then, create a BigText from the time string using PTerm's DefaultBigText and putils NewLettersFromString. + // The Srender() function is used to save the BigText as a string. + str, _ := pterm.DefaultBigText.WithLetters(putils.LettersFromString(time.Now().Format("15:04:05"))).Srender() + + // Update the Area contents with the current time string. + area.Update(str) + + // Sleep for a second before the next update. time.Sleep(time.Second) } + // Stop the Area printer after all updates are done. area.Stop() } @@ -158,9 +188,9 @@ func main() {
-### area/default +### area/center -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/default/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/center/animation.svg)
@@ -176,13 +206,21 @@ import ( ) func main() { - area, _ := pterm.DefaultArea.Start() + // Start a new default area in the center of the terminal. + // The Start() function returns the created area and an error. + area, _ := pterm.DefaultArea.WithCenter().Start() + // Loop 5 times to simulate a dynamic update. for i := 0; i < 5; i++ { + // Update the content of the area with the current count. + // The Sprintf function is used to format the string. area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i)) + + // Pause for a second to simulate a time-consuming task. time.Sleep(time.Second) } + // Stop the area after all updates are done. area.Stop() } @@ -190,9 +228,9 @@ func main() {
-### area/demo +### area/default -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/default/animation.svg)
@@ -208,15 +246,22 @@ import ( ) func main() { - pterm.Info.Println("The previous text will stay in place, while the area updates.") - pterm.Print("\n\n") // Add two new lines as spacer. + // Start a new default area and get a reference to it. + // The second return value is an error which is ignored here. + area, _ := pterm.DefaultArea.Start() - area, _ := pterm.DefaultArea.WithCenter().Start() // Start the Area printer, with the Center option. - for i := 0; i < 10; i++ { - str, _ := pterm.DefaultBigText.WithLetters(pterm.NewLettersFromString(time.Now().Format("15:04:05"))).Srender() // Save current time in str. - area.Update(str) // Update Area contents. + // Loop 5 times + for i := 0; i < 5; i++ { + // Update the content of the area dynamically. + // Here we're just displaying the current count. + area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i)) + + // Pause for a second before the next update. time.Sleep(time.Second) } + + // Stop the area after all updates are done. + // This will clean up and free resources used by the area. area.Stop() } @@ -242,23 +287,35 @@ import ( ) func main() { + // Start a new fullscreen centered area. + // This area will be used to display the bar chart. area, _ := pterm.DefaultArea.WithFullscreen().WithCenter().Start() + // Ensure the area stops updating when we're done. defer area.Stop() + // Loop to update the bar chart 10 times. for i := 0; i < 10; i++ { + // Create a new bar chart with dynamic bars. + // The bars will change based on the current iteration. barchart := pterm.DefaultBarChart.WithBars(dynamicBars(i)) + // Render the bar chart to a string. + // This string will be used to update the area. content, _ := barchart.Srender() + // Update the area with the new bar chart. area.Update(content) + // Wait for half a second before the next update. time.Sleep(500 * time.Millisecond) } } +// dynamicBars generates a set of bars for the bar chart. +// The bars will change based on the current iteration. func dynamicBars(i int) pterm.Bars { return pterm.Bars{ - {Label: "A", Value: 10}, - {Label: "B", Value: 20 * i}, - {Label: "C", Value: 30}, - {Label: "D", Value: 40 + i}, + {Label: "A", Value: 10}, // A static bar. + {Label: "B", Value: 20 * i}, // A bar that grows with each iteration. + {Label: "C", Value: 30}, // Another static bar. + {Label: "D", Value: 40 + i}, // A bar that grows slowly with each iteration. } } @@ -284,13 +341,21 @@ import ( ) func main() { + // Start a new fullscreen area. This will return an area instance and an error. + // The underscore (_) is used to ignore the error. area, _ := pterm.DefaultArea.WithFullscreen().Start() + // Loop 5 times to update the area content. for i := 0; i < 5; i++ { + // Update the content of the area with the current count. + // The Sprintf function is used to format the string. area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i)) + + // Pause for a second before the next update. time.Sleep(time.Second) } + // Stop the area after all updates are done. area.Stop() } @@ -316,13 +381,22 @@ import ( ) func main() { + // Initialize a new PTerm area with fullscreen and center options + // The Start() function returns the created area and an error (ignored here) area, _ := pterm.DefaultArea.WithFullscreen().WithCenter().Start() + // Loop 5 times to demonstrate dynamic content update for i := 0; i < 5; i++ { + // Update the content of the area with the current count + // The Sprintf function is used to format the string with the count area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i)) + + // Pause for a second time.Sleep(time.Second) } + // Stop the area after all updates are done + // This will clear the area and return the terminal to its normal state area.Stop() } @@ -330,9 +404,9 @@ func main() {
-### barchart/custom-height +### barchart/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/custom-height/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/demo/animation.svg)
@@ -341,29 +415,39 @@ func main() { ```go package main -import "github.com/pterm/pterm" +import ( + "github.com/pterm/pterm" +) func main() { - pterm.DefaultBarChart.WithBars([]pterm.Bar{ - {Label: "A", Value: 10}, - {Label: "B", Value: 20}, - {Label: "C", Value: 30}, - {Label: "D", Value: 40}, - {Label: "E", Value: 50}, - {Label: "F", Value: 40}, - {Label: "G", Value: 30}, - {Label: "H", Value: 20}, - {Label: "I", Value: 10}, - }).WithHeight(5).Render() + // Define the bars for the chart + bars := []pterm.Bar{ + {Label: "Bar 1", Value: 5}, + {Label: "Bar 2", Value: 3}, + {Label: "Longer Label", Value: 7}, + } + + // Print an informational message + pterm.Info.Println("Chart example with positive only values (bars use 100% of chart area)") + + // Create a bar chart with the defined bars and render it + // The DefaultBarChart is used as a base, and the bars are added with the WithBars option + // The Render function is then called to display the chart + pterm.DefaultBarChart.WithBars(bars).Render() + + // Create a horizontal bar chart with the defined bars and render it + // The DefaultBarChart is used as a base, the chart is made horizontal with the WithHorizontal option, and the bars are added with the WithBars option + // The Render function is then called to display the chart + pterm.DefaultBarChart.WithHorizontal().WithBars(bars).Render() } ```
-### barchart/custom-width +### barchart/custom-height -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/custom-width/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/custom-height/animation.svg)
@@ -375,7 +459,9 @@ package main import "github.com/pterm/pterm" func main() { - pterm.DefaultBarChart.WithBars([]pterm.Bar{ + // Define a slice of Bar structs. Each struct represents a bar in the chart. + // The Label field is the name of the bar and the Value field is the height of the bar. + bars := []pterm.Bar{ {Label: "A", Value: 10}, {Label: "B", Value: 20}, {Label: "C", Value: 30}, @@ -385,16 +471,22 @@ func main() { {Label: "G", Value: 30}, {Label: "H", Value: 20}, {Label: "I", Value: 10}, - }).WithHorizontal().WithWidth(5).Render() + } + + // Create and render a bar chart with the defined bars and a height of 5. + // The WithBars method is used to set the bars of the chart. + // The WithHeight method is used to set the height of the chart. + // The Render method is used to display the chart in the terminal. + pterm.DefaultBarChart.WithBars(bars).WithHeight(5).Render() } ```
-### barchart/default +### barchart/custom-width -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/default/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/custom-width/animation.svg)
@@ -406,7 +498,8 @@ package main import "github.com/pterm/pterm" func main() { - pterm.DefaultBarChart.WithBars([]pterm.Bar{ + // Define the data for the bar chart + barData := []pterm.Bar{ {Label: "A", Value: 10}, {Label: "B", Value: 20}, {Label: "C", Value: 30}, @@ -416,16 +509,21 @@ func main() { {Label: "G", Value: 30}, {Label: "H", Value: 20}, {Label: "I", Value: 10}, - }).Render() + } + + // Create a bar chart with the defined data + // The chart is horizontal and has a width of 5 + // The Render() function is called to display the chart + pterm.DefaultBarChart.WithBars(barData).WithHorizontal().WithWidth(5).Render() } ```
-### barchart/demo +### barchart/default -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/default/animation.svg)
@@ -434,29 +532,27 @@ func main() { ```go package main -import ( - "github.com/pterm/pterm" -) +import "github.com/pterm/pterm" func main() { - positiveBars := pterm.Bars{ - pterm.Bar{ - Label: "Bar 1", - Value: 5, - }, - pterm.Bar{ - Label: "Bar 2", - Value: 3, - }, - pterm.Bar{ - Label: "Longer Label", - Value: 7, - }, + // Define the data for the bar chart. Each bar is represented by a `pterm.Bar` struct. + // The `Label` field represents the label of the bar, and the `Value` field represents the value of the bar. + bars := []pterm.Bar{ + {Label: "A", Value: 10}, + {Label: "B", Value: 20}, + {Label: "C", Value: 30}, + {Label: "D", Value: 40}, + {Label: "E", Value: 50}, + {Label: "F", Value: 40}, + {Label: "G", Value: 30}, + {Label: "H", Value: 20}, + {Label: "I", Value: 10}, } - pterm.Info.Println("Chart example with positive only values (bars use 100% of chart area)") - _ = pterm.DefaultBarChart.WithBars(positiveBars).Render() - _ = pterm.DefaultBarChart.WithHorizontal().WithBars(positiveBars).Render() + // Use the `DefaultBarChart` from the `pterm` package to create a bar chart. + // The `WithBars` method is used to set the bars of the chart. + // The `Render` method is used to display the chart. + pterm.DefaultBarChart.WithBars(bars).Render() } ``` @@ -477,7 +573,8 @@ package main import "github.com/pterm/pterm" func main() { - pterm.DefaultBarChart.WithBars([]pterm.Bar{ + // Define the data for the bar chart + bars := []pterm.Bar{ {Label: "A", Value: 10}, {Label: "B", Value: 20}, {Label: "C", Value: 30}, @@ -487,7 +584,12 @@ func main() { {Label: "G", Value: 30}, {Label: "H", Value: 20}, {Label: "I", Value: 10}, - }).WithHorizontal().Render() + } + + // Create a bar chart with the defined data + // The chart is displayed horizontally + // The Render() function is called to display the chart + pterm.DefaultBarChart.WithBars(bars).WithHorizontal().Render() } ``` @@ -508,7 +610,8 @@ package main import "github.com/pterm/pterm" func main() { - pterm.DefaultBarChart.WithBars([]pterm.Bar{ + // Define the data for the bar chart + barData := []pterm.Bar{ {Label: "A", Value: 10}, {Label: "B", Value: 20}, {Label: "C", Value: 30}, @@ -518,7 +621,12 @@ func main() { {Label: "G", Value: 30}, {Label: "H", Value: 20}, {Label: "I", Value: 10}, - }).WithHorizontal().WithShowValue().Render() + } + + // Create a bar chart with the defined data + // The chart is horizontal and displays the value of each bar + // The Render() function is called to display the chart + pterm.DefaultBarChart.WithBars(barData).WithHorizontal().WithShowValue().Render() } ``` @@ -541,32 +649,29 @@ import ( ) func main() { - mixedBars := pterm.Bars{ - pterm.Bar{ - Label: "Bar 1", - Value: 2, - }, - pterm.Bar{ - Label: "Bar 2", - Value: -3, - }, - pterm.Bar{ - Label: "Bar 3", - Value: -2, - }, - pterm.Bar{ - Label: "Bar 4", - Value: 5, - }, - pterm.Bar{ - Label: "Longer Label", - Value: 7, - }, + // Define a set of bars for the chart. + // Each bar has a label and a value. + bars := []pterm.Bar{ + {Label: "Bar 1", Value: 2}, + {Label: "Bar 2", Value: -3}, + {Label: "Bar 3", Value: -2}, + {Label: "Bar 4", Value: 5}, + {Label: "Longer Label", Value: 7}, } + // Print a section header. + // This is useful for separating different parts of the output. pterm.DefaultSection.Println("Chart example with mixed values (note screen space usage in case when ABSOLUTE values of negative and positive parts are differ too much)") - _ = pterm.DefaultBarChart.WithBars(mixedBars).WithShowValue().Render() - _ = pterm.DefaultBarChart.WithHorizontal().WithBars(mixedBars).WithShowValue().Render() + + // Create a bar chart with the defined bars. + // The chart will display the value of each bar. + // The Render() function is called to display the chart. + pterm.DefaultBarChart.WithBars(bars).WithShowValue().Render() + + // Create a horizontal bar chart with the same bars. + // The chart will display the value of each bar. + // The Render() function is called to display the chart. + pterm.DefaultBarChart.WithHorizontal().WithBars(bars).WithShowValue().Render() } ``` @@ -589,23 +694,25 @@ import ( ) func main() { + // Define a set of bars with negative values. + // Each bar is represented by a struct with a label and a value. negativeBars := pterm.Bars{ - pterm.Bar{ - Label: "Bar 1", - Value: -5, - }, - pterm.Bar{ - Label: "Bar 2", - Value: -3, - }, - pterm.Bar{ - Label: "Longer Label", - Value: -7, - }, + {Label: "Bar 1", Value: -5}, + {Label: "Bar 2", Value: -3}, + {Label: "Longer Label", Value: -7}, } + // Print an informational message to the console. pterm.Info.Println("Chart example with negative only values (bars use 100% of chart area)") + + // Create a vertical bar chart with the defined bars. + // The WithShowValue() option is used to display the value of each bar in the chart. + // The Render() method is called to draw the chart. _ = pterm.DefaultBarChart.WithBars(negativeBars).WithShowValue().Render() + + // Create a horizontal bar chart with the same bars. + // The WithHorizontal() option is used to orient the chart horizontally. + // The WithShowValue() option and Render() method are used in the same way as before. _ = pterm.DefaultBarChart.WithHorizontal().WithBars(negativeBars).WithShowValue().Render() } @@ -627,7 +734,10 @@ package main import "github.com/pterm/pterm" func main() { - pterm.DefaultBarChart.WithBars([]pterm.Bar{ + // Define a slice of bars for the bar chart. Each bar is represented by a struct + // with a Label and a Value. The Label is a string that represents the name of the bar, + // and the Value is an integer that represents the height of the bar. + bars := []pterm.Bar{ {Label: "A", Value: 10}, {Label: "B", Value: 20}, {Label: "C", Value: 30}, @@ -637,7 +747,13 @@ func main() { {Label: "G", Value: 30}, {Label: "H", Value: 20}, {Label: "I", Value: 10}, - }).WithShowValue().Render() + } + + // Create a bar chart with the defined bars using the DefaultBarChart object from PTerm. + // Chain the WithBars method to set the bars of the chart. + // Chain the WithShowValue method to display the value of each bar on the chart. + // Finally, call the Render method to display the chart. + pterm.DefaultBarChart.WithBars(bars).WithShowValue().Render() } ``` @@ -658,22 +774,24 @@ package main import "github.com/pterm/pterm" func main() { - // A BasicText printer is used to print text, without special formatting. - // As it implements the TextPrinter interface, you can use it in combination with other printers. + // The DefaultBasicText is a basic text printer provided by PTerm. + // It is used to print text without any special formatting. pterm.DefaultBasicText.Println("Default basic text printer.") + + // The DefaultBasicText can be used in any context that requires a TextPrinter. + // Here, we're using it with the LightMagenta function to color a portion of the text. pterm.DefaultBasicText.Println("Can be used in any" + pterm.LightMagenta(" TextPrinter ") + "context.") - pterm.DefaultBasicText.Println("For example to resolve progressbars and spinners.") - // If you just want to print text, you should use this instead: - // pterm.Println("Hello, World!") + + // The DefaultBasicText is also useful for resolving progress bars and spinners. } ```
-### bigtext/colored +### bigtext/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/colored/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/demo/animation.svg)
@@ -688,19 +806,33 @@ import ( ) func main() { + // Create a large text with the LetterStyle from the standard theme. + // This is useful for creating title screens. + pterm.DefaultBigText.WithLetters(putils.LettersFromString("PTerm")).Render() + + // Create a large text with differently colored letters. + // Here, the first letter 'P' is colored cyan and the rest 'Term' is colored light magenta. + // This can be used to highlight specific parts of the text. pterm.DefaultBigText.WithLetters( putils.LettersFromStringWithStyle("P", pterm.FgCyan.ToStyle()), - putils.LettersFromStringWithStyle("Term", pterm.FgLightMagenta.ToStyle())). - Render() + putils.LettersFromStringWithStyle("Term", pterm.FgLightMagenta.ToStyle()), + ).Render() + + // Create a large text with a specific RGB color. + // This can be used when you need a specific color that is not available in the standard colors. + // Here, the color is gold (RGB: 255, 215, 0). + pterm.DefaultBigText.WithLetters( + putils.LettersFromStringWithRGB("PTerm", pterm.NewRGB(255, 215, 0)), + ).Render() } ```
-### bigtext/default +### bigtext/colored -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/default/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/colored/animation.svg)
@@ -715,16 +847,21 @@ import ( ) func main() { - pterm.DefaultBigText.WithLetters(putils.LettersFromString("PTerm")).Render() + // Initialize a big text display with the letters "P" and "Term" + // "P" is displayed in cyan and "Term" is displayed in light magenta + pterm.DefaultBigText.WithLetters( + putils.LettersFromStringWithStyle("P", pterm.FgCyan.ToStyle()), + putils.LettersFromStringWithStyle("Term", pterm.FgLightMagenta.ToStyle())). + Render() // Render the big text to the terminal } ```
-### bigtext/demo +### bigtext/default -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/default/animation.svg)
@@ -739,29 +876,23 @@ import ( ) func main() { - // Print a large text with the LetterStyle from the standard theme. - // Useful for title screens. - pterm.DefaultBigText.WithLetters(putils.LettersFromString("PTerm")).Render() + // Define the text to be rendered + var text = "PTerm" - // Print a large text with differently colored letters. - pterm.DefaultBigText.WithLetters( - putils.LettersFromStringWithStyle("P", pterm.FgCyan.ToStyle()), - putils.LettersFromStringWithStyle("Term", pterm.FgLightMagenta.ToStyle())). - Render() + // Convert the text into a format suitable for PTerm + var letters = putils.LettersFromString(text) - // LettersFromStringWithRGB can be used to create a large text with a specific RGB color. - pterm.DefaultBigText.WithLetters( - putils.LettersFromStringWithRGB("PTerm", pterm.NewRGB(255, 215, 0))). - Render() + // Render the text using PTerm's default big text style + pterm.DefaultBigText.WithLetters(letters).Render() } ```
-### box/custom-padding +### box/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/custom-padding/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/demo/animation.svg)
@@ -773,21 +904,35 @@ package main import "github.com/pterm/pterm" func main() { - pterm.DefaultBox. - WithRightPadding(10). - WithLeftPadding(10). - WithTopPadding(2). - WithBottomPadding(2). - Println("Hello, World!") + // Print an informational message. + pterm.Info.Println("This might not be rendered correctly on GitHub,\nbut it will work in a real terminal.\nThis is because GitHub does not use a monospaced font by default for SVGs") + + // Create three panels with text, some of them with titles. + // The panels are created using the DefaultBox style. + panel1 := pterm.DefaultBox.Sprint("Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt\nut labore et dolore\nmagna aliqua.") + panel2 := pterm.DefaultBox.WithTitle("title").Sprint("Ut enim ad minim veniam,\nquis nostrud exercitation\nullamco laboris\nnisi ut aliquip\nex ea commodo\nconsequat.") + panel3 := pterm.DefaultBox.WithTitle("bottom center title").WithTitleBottomCenter().Sprint("Duis aute irure\ndolor in reprehenderit\nin voluptate velit esse cillum\ndolore eu fugiat\nnulla pariatur.") + + // Combine the panels into a layout using the DefaultPanel style. + // The layout is a 2D grid, with each row being an array of panels. + // In this case, the first row contains panel1 and panel2, and the second row contains only panel3. + panels, _ := pterm.DefaultPanel.WithPanels(pterm.Panels{ + {{Data: panel1}, {Data: panel2}}, + {{Data: panel3}}, + }).Srender() + + // Print the panels layout inside a box with a title. + // The box is created using the DefaultBox style, with the title positioned at the bottom right. + pterm.DefaultBox.WithTitle("Lorem Ipsum").WithTitleBottomRight().WithRightPadding(0).WithBottomPadding(0).Println(panels) } ```
-### box/default +### box/custom-padding -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/default/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/custom-padding/animation.svg)
@@ -799,16 +944,17 @@ package main import "github.com/pterm/pterm" func main() { - pterm.DefaultBox.Println("Hello, World!") + // Create a default box with custom padding options and print "Hello, World!" inside it. + pterm.DefaultBox.WithRightPadding(10).WithLeftPadding(10).WithTopPadding(2).WithBottomPadding(2).Println("Hello, World!") } ```
-### box/demo +### box/default -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/default/animation.svg)
@@ -820,18 +966,9 @@ package main import "github.com/pterm/pterm" func main() { - pterm.Info.Println("This might not be rendered correctly on GitHub,\nbut it will work in a real terminal.\nThis is because GitHub does not use a monospaced font by default for SVGs") - - panel1 := pterm.DefaultBox.Sprint("Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt\nut labore et dolore\nmagna aliqua.") - panel2 := pterm.DefaultBox.WithTitle("title").Sprint("Ut enim ad minim veniam,\nquis nostrud exercitation\nullamco laboris\nnisi ut aliquip\nex ea commodo\nconsequat.") - panel3 := pterm.DefaultBox.WithTitle("bottom center title").WithTitleBottomCenter().Sprint("Duis aute irure\ndolor in reprehenderit\nin voluptate velit esse cillum\ndolore eu fugiat\nnulla pariatur.") - - panels, _ := pterm.DefaultPanel.WithPanels(pterm.Panels{ - {{Data: panel1}, {Data: panel2}}, - {{Data: panel3}}, - }).Srender() - - pterm.DefaultBox.WithTitle("Lorem Ipsum").WithTitleBottomRight().WithRightPadding(0).WithBottomPadding(0).Println(panels) + // Create a default box with PTerm and print a message in it. + // The DefaultBox.Println method automatically starts, prints the message, and stops the box. + pterm.DefaultBox.Println("Hello, World!") } ``` @@ -852,19 +989,22 @@ package main import "github.com/pterm/pterm" func main() { - // Default titled bpx + // Create a default box with specified padding paddedBox := pterm.DefaultBox.WithLeftPadding(4).WithRightPadding(4).WithTopPadding(1).WithBottomPadding(1) + // Define a title for the box title := pterm.LightRed("I'm a box!") - box1 := paddedBox.WithTitle(title).Sprint("Hello, World!\n 1") - box2 := paddedBox.WithTitle(title).WithTitleTopCenter().Sprint("Hello, World!\n 2") - box3 := paddedBox.WithTitle(title).WithTitleTopRight().Sprint("Hello, World!\n 3") - box4 := paddedBox.WithTitle(title).WithTitleBottomRight().Sprint("Hello, World!\n 4") - box5 := paddedBox.WithTitle(title).WithTitleBottomCenter().Sprint("Hello, World!\n 5") - box6 := paddedBox.WithTitle(title).WithTitleBottomLeft().Sprint("Hello, World!\n 6") - box7 := paddedBox.WithTitle(title).WithTitleTopLeft().Sprint("Hello, World!\n 7") + // Create boxes with the title positioned differently and containing different content + box1 := paddedBox.WithTitle(title).Sprint("Hello, World!\n 1") // Title at default position (top left) + box2 := paddedBox.WithTitle(title).WithTitleTopCenter().Sprint("Hello, World!\n 2") // Title at top center + box3 := paddedBox.WithTitle(title).WithTitleTopRight().Sprint("Hello, World!\n 3") // Title at top right + box4 := paddedBox.WithTitle(title).WithTitleBottomRight().Sprint("Hello, World!\n 4") // Title at bottom right + box5 := paddedBox.WithTitle(title).WithTitleBottomCenter().Sprint("Hello, World!\n 5") // Title at bottom center + box6 := paddedBox.WithTitle(title).WithTitleBottomLeft().Sprint("Hello, World!\n 6") // Title at bottom left + box7 := paddedBox.WithTitle(title).WithTitleTopLeft().Sprint("Hello, World!\n 7") // Title at top left + // Render the boxes in a panel layout pterm.DefaultPanel.WithPanels([][]pterm.Panel{ {{box1}, {box2}, {box3}}, {{box4}, {box5}, {box6}}, @@ -876,9 +1016,9 @@ func main() {
-### bulletlist/customized +### bulletlist/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bulletlist/customized/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bulletlist/demo/animation.svg)
@@ -889,24 +1029,37 @@ package main import ( "github.com/pterm/pterm" + "github.com/pterm/pterm/putils" ) func main() { - // Print a customized list with different styles and levels. - pterm.DefaultBulletList.WithItems([]pterm.BulletListItem{ - {Level: 0, Text: "Blue", TextStyle: pterm.NewStyle(pterm.FgBlue), BulletStyle: pterm.NewStyle(pterm.FgRed)}, - {Level: 1, Text: "Green", TextStyle: pterm.NewStyle(pterm.FgGreen), Bullet: "-", BulletStyle: pterm.NewStyle(pterm.FgLightWhite)}, - {Level: 2, Text: "Cyan", TextStyle: pterm.NewStyle(pterm.FgCyan), Bullet: ">", BulletStyle: pterm.NewStyle(pterm.FgYellow)}, - }).Render() + // Define a list of bullet list items with different levels. + bulletListItems := []pterm.BulletListItem{ + {Level: 0, Text: "Level 0"}, // Level 0 item + {Level: 1, Text: "Level 1"}, // Level 1 item + {Level: 2, Text: "Level 2"}, // Level 2 item + } + + // Use the default bullet list style to render the list items. + pterm.DefaultBulletList.WithItems(bulletListItems).Render() + + // Define a string with different levels of indentation. + text := `0 + 1 + 2 + 3` + + // Convert the indented string to a bullet list and render it. + putils.BulletListFromString(text, " ").Render() } ```
-### bulletlist/demo +### bulletlist/customized -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bulletlist/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bulletlist/customized/animation.svg)
@@ -917,23 +1070,35 @@ package main import ( "github.com/pterm/pterm" - "github.com/pterm/pterm/putils" ) func main() { - // Print a list with different levels. - // Useful to generate lists automatically from data. - pterm.DefaultBulletList.WithItems([]pterm.BulletListItem{ - {Level: 0, Text: "Level 0"}, - {Level: 1, Text: "Level 1"}, - {Level: 2, Text: "Level 2"}, - }).Render() + // Define a list of bullet list items with different styles and levels. + bulletListItems := []pterm.BulletListItem{ + { + Level: 0, // Level 0 (top level) + Text: "Blue", // Text to display + TextStyle: pterm.NewStyle(pterm.FgBlue), // Text color + BulletStyle: pterm.NewStyle(pterm.FgRed), // Bullet color + }, + { + Level: 1, // Level 1 (sub-item) + Text: "Green", // Text to display + TextStyle: pterm.NewStyle(pterm.FgGreen), // Text color + Bullet: "-", // Custom bullet symbol + BulletStyle: pterm.NewStyle(pterm.FgLightWhite), // Bullet color + }, + { + Level: 2, // Level 2 (sub-sub-item) + Text: "Cyan", // Text to display + TextStyle: pterm.NewStyle(pterm.FgCyan), // Text color + Bullet: ">", // Custom bullet symbol + BulletStyle: pterm.NewStyle(pterm.FgYellow), // Bullet color + }, + } - // Convert a text to a list and print it. - putils.BulletListFromString(`0 - 1 - 2 - 3`, " ").Render() + // Create a bullet list with the defined items and render it. + pterm.DefaultBulletList.WithItems(bulletListItems).Render() } ``` @@ -951,15 +1116,22 @@ func main() { ```go package main -import "github.com/pterm/pterm" +import ( + "github.com/pterm/pterm" + "github.com/pterm/pterm/putils" +) func main() { - pterm.DefaultCenter.Println("This text is centered!\nIt centeres the whole block by default.\nIn that way you can do stuff like this:") + // Print a block of text centered in the terminal + pterm.DefaultCenter.Println("This text is centered!\nIt centers the whole block by default.\nIn that way you can do stuff like this:") + + // Generate BigLetters and store in 's' + s, _ := pterm.DefaultBigText.WithLetters(putils.LettersFromString("PTerm")).Srender() - // Generate BigLetters - s, _ := pterm.DefaultBigText.WithLetters(pterm.NewLettersFromString("PTerm")).Srender() - pterm.DefaultCenter.Println(s) // Print BigLetters with the default CenterPrinter + // Print the BigLetters 's' centered in the terminal + pterm.DefaultCenter.Println(s) + // Print each line of the text separately centered in the terminal pterm.DefaultCenter.WithCenterEachLineSeparately().Println("This text is centered!\nBut each line is\ncentered\nseparately") } @@ -981,8 +1153,7 @@ package main import "github.com/pterm/pterm" func main() { - // Print all colors - + // Create a table with different foreground and background colors. pterm.DefaultTable.WithData([][]string{ {pterm.FgBlack.Sprint("Black"), pterm.FgRed.Sprint("Red"), pterm.FgGreen.Sprint("Green"), pterm.FgYellow.Sprint("Yellow")}, {"", pterm.FgLightRed.Sprint("Light Red"), pterm.FgLightGreen.Sprint("Light Green"), pterm.FgLightYellow.Sprint("Light Yellow")}, @@ -992,18 +1163,19 @@ func main() { {pterm.FgLightBlue.Sprint("Light Blue"), pterm.FgLightMagenta.Sprint("Light Magenta"), pterm.FgLightCyan.Sprint("Light Cyan"), pterm.FgLightWhite.Sprint("Light White")}, {pterm.BgBlue.Sprint("Blue"), pterm.BgMagenta.Sprint("Magenta"), pterm.BgCyan.Sprint("Cyan"), pterm.BgWhite.Sprint("White")}, {pterm.BgLightBlue.Sprint("Light Blue"), pterm.BgLightMagenta.Sprint("Light Magenta"), pterm.BgLightCyan.Sprint("Light Cyan"), pterm.BgLightWhite.Sprint("Light White")}, - }).Render() + }).Render() // Render the table. pterm.Println() - // Print different colored words. + // Print words in different colors. pterm.Println(pterm.Red("Hello, ") + pterm.Green("World") + pterm.Cyan("!")) pterm.Println(pterm.Red("Even " + pterm.Cyan("nested ") + pterm.Green("colors ") + "are supported!")) pterm.Println() - // Or print colors as a style + // Create a new style with a red background, light green foreground, and bold text. style := pterm.NewStyle(pterm.BgRed, pterm.FgLightGreen, pterm.Bold) + // Print text using the created style. style.Println("This text uses a style and is bold and light green with a red background!") } @@ -1025,16 +1197,20 @@ package main import "github.com/pterm/pterm" func main() { + // Loop from 0 to 14 for i := 0; i < 15; i++ { switch i { case 5: + // At the 5th iteration, print a message and disable the output pterm.Info.Println("Disabled Output!") pterm.DisableOutput() case 10: + // At the 10th iteration, enable the output and print a message pterm.EnableOutput() pterm.Info.Println("Enabled Output!") } + // Print a progress message for each iteration pterm.Printf("Printing something... [%d/%d]\n", i, 15) } } @@ -1059,16 +1235,26 @@ import ( ) func main() { - // Print info. + // Print an informational message. pterm.Info.Println("RGB colors only work in Terminals which support TrueColor.") - from := pterm.NewRGB(0, 255, 255) // This RGB value is used as the gradients start point. - to := pterm.NewRGB(255, 0, 255) // This RGB value is used as the gradients end point. + // Define the start and end points for the color gradient. + startColor := pterm.NewRGB(0, 255, 255) // Cyan + endColor := pterm.NewRGB(255, 0, 255) // Magenta + + // Get the terminal height to determine the gradient range. + terminalHeight := pterm.GetTerminalHeight() + + // Loop over the range of the terminal height to create a color gradient. + for i := 0; i < terminalHeight-2; i++ { + // Calculate the fade factor for the current step in the gradient. + fadeFactor := float32(i) / float32(terminalHeight-2) - // For loop over the range of the terminal height. - for i := 0; i < pterm.GetTerminalHeight()-2; i++ { - // Print string which is colored with the faded RGB value. - from.Fade(0, float32(pterm.GetTerminalHeight()-2), float32(i), to).Println("Hello, World!") + // Create a color that represents the current step in the gradient. + currentColor := startColor.Fade(0, 1, fadeFactor, endColor) + + // Print a string with the current color. + currentColor.Println("Hello, World!") } } @@ -1076,9 +1262,9 @@ func main() {
-### coloring/fade-multiple-colors +### coloring/fade-colors-rgb-style -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/fade-multiple-colors/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/fade-colors-rgb-style/animation.svg)
@@ -1094,38 +1280,73 @@ import ( ) func main() { - from := pterm.NewRGB(0, 255, 255) // This RGB value is used as the gradients start point. - to := pterm.NewRGB(255, 0, 255) // This RGB value is used as the gradients first point. - to2 := pterm.NewRGB(255, 0, 0) // This RGB value is used as the gradients second point. - to3 := pterm.NewRGB(0, 255, 0) // This RGB value is used as the gradients third point. - to4 := pterm.NewRGB(255, 255, 255) // This RGB value is used as the gradients end point. + // Define RGB colors + white := pterm.NewRGB(255, 255, 255) + grey := pterm.NewRGB(128, 128, 128) + black := pterm.NewRGB(0, 0, 0) + red := pterm.NewRGB(255, 0, 0) + purple := pterm.NewRGB(255, 0, 255) + green := pterm.NewRGB(0, 255, 0) - str := "RGB colors only work in Terminals which support TrueColor." + // Define strings to be printed + str1 := "RGB colors only work in Terminals which support TrueColor." + str2 := "The background and foreground colors can be customized individually." + str3 := "Styles can also be applied. For example: Bold or Italic." + + // Print first string with color fading from white to purple + printFadedString(str1, white, purple, grey, black) + + // Print second string with color fading from purple to red + printFadedString(str2, black, purple, red, red) + + // Print third string with color fading from white to green and style changes + printStyledString(str3, white, green, red, black) +} + +// printFadedString prints a string with color fading effect +func printFadedString(str string, fgStart, fgEnd, bgStart, bgEnd pterm.RGB) { strs := strings.Split(str, "") - var fadeInfo string // String which will be used to print info. - // For loop over the range of the string length. + var result string for i := 0; i < len(str); i++ { - // Append faded letter to info string. - fadeInfo += from.Fade(0, float32(len(str)), float32(i), to).Sprint(strs[i]) + // Create a style with color fading effect + style := pterm.NewRGBStyle(fgStart.Fade(0, float32(len(str)), float32(i), fgEnd), bgStart.Fade(0, float32(len(str)), float32(i), bgEnd)) + // Append styled letter to result string + result += style.Sprint(strs[i]) } + pterm.Println(result) +} - // Print info. - pterm.Info.Println(fadeInfo) - - // For loop over the range of the terminal height. - for i := 0; i < pterm.GetTerminalHeight()-2; i++ { - // Print string which is colored with the faded RGB value. - from.Fade(0, float32(pterm.GetTerminalHeight()-2), float32(i), to, to2, to3, to4).Println("Hello, World!") +// printStyledString prints a string with color fading and style changes +func printStyledString(str string, fgStart, fgEnd, bgStart, bgEnd pterm.RGB) { + strs := strings.Split(str, "") + var result string + boldStr := strings.Split("Bold", "") + italicStr := strings.Split("Italic", "") + bold, italic := 0, 0 + for i := 0; i < len(str); i++ { + // Create a style with color fading effect + style := pterm.NewRGBStyle(fgStart.Fade(0, float32(len(str)), float32(i), fgEnd), bgStart.Fade(0, float32(len(str)), float32(i), bgEnd)) + // Check if the next letters are "Bold" or "Italic" and add the corresponding style + if bold < len(boldStr) && i+len(boldStr)-bold <= len(strs) && strings.Join(strs[i:i+len(boldStr)-bold], "") == strings.Join(boldStr[bold:], "") { + style = style.AddOptions(pterm.Bold) + bold++ + } else if italic < len(italicStr) && i+len(italicStr)-italic < len(strs) && strings.Join(strs[i:i+len(italicStr)-italic], "") == strings.Join(italicStr[italic:], "") { + style = style.AddOptions(pterm.Italic) + italic++ + } + // Append styled letter to result string + result += style.Sprint(strs[i]) } + pterm.Println(result) } ```
-### coloring/override-default-printers +### coloring/fade-multiple-colors -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/override-default-printers/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/fade-multiple-colors/animation.svg)
@@ -1134,27 +1355,79 @@ func main() { ```go package main -import "github.com/pterm/pterm" +import ( + "strings" + + "github.com/pterm/pterm" +) func main() { - // Print default error. - pterm.Error.Println("This is the default Error") + // Define RGB values for gradient points. + startColor := pterm.NewRGB(0, 255, 255) + firstPoint := pterm.NewRGB(255, 0, 255) + secondPoint := pterm.NewRGB(255, 0, 0) + thirdPoint := pterm.NewRGB(0, 255, 0) + endColor := pterm.NewRGB(255, 255, 255) + + // Define the string to be printed. + str := "RGB colors only work in Terminals which support TrueColor." + strs := strings.Split(str, "") - // Customize default error. - pterm.Error.Prefix = pterm.Prefix{ - Text: "OVERRIDE", - Style: pterm.NewStyle(pterm.BgCyan, pterm.FgRed), + // Initialize an empty string for the faded info. + var fadeInfo string + + // Loop over the string length to create a gradient effect. + for i := 0; i < len(str); i++ { + // Append each character of the string with a faded color to the info string. + fadeInfo += startColor.Fade(0, float32(len(str)), float32(i), firstPoint).Sprint(strs[i]) } - // Print new default error. - pterm.Error.Println("This is the default Error after the prefix was overridden") + // Print the info string with gradient effect. + pterm.Info.Println(fadeInfo) + + // Get the terminal height. + terminalHeight := pterm.GetTerminalHeight() + + // Loop over the terminal height to print "Hello, World!" with a gradient effect. + for i := 0; i < terminalHeight-2; i++ { + // Print the string with a color that fades from startColor to endColor. + startColor.Fade(0, float32(terminalHeight-2), float32(i), firstPoint, secondPoint, thirdPoint, endColor).Println("Hello, World!") + } } ```
-### coloring/print-color-rgb +### coloring/override-default-printers + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/override-default-printers/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import "github.com/pterm/pterm" + +func main() { + // Print a default error message with PTerm's built-in Error style. + pterm.Error.Println("This is the default Error") + + // Override the default error prefix with a new text and style. + pterm.Error.Prefix = pterm.Prefix{Text: "OVERRIDE", Style: pterm.NewStyle(pterm.BgCyan, pterm.FgRed)} + + // Print the error message again, this time with the overridden prefix. + pterm.Error.Println("This is the default Error after the prefix was overridden") +} + +``` + +
+ +### coloring/print-color-rgb ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/print-color-rgb/animation.svg) @@ -1168,10 +1441,17 @@ package main import "github.com/pterm/pterm" func main() { - // Print strings with a custom RGB color. - // NOTICE: This only works with terminals which support TrueColor. + // Create a new RGB color with values 178, 44, 199. + // This color will be used for the text. pterm.NewRGB(178, 44, 199).Println("This text is printed with a custom RGB!") + + // Create a new RGB color with values 15, 199, 209. + // This color will be used for the text. pterm.NewRGB(15, 199, 209).Println("This text is printed with a custom RGB!") + + // Create a new RGB color with values 201, 144, 30. + // This color will be used for the background. + // The 'true' argument indicates that the color is for the background. pterm.NewRGB(201, 144, 30, true).Println("This text is printed with a custom RGB background!") } @@ -1179,6 +1459,43 @@ func main() { +### coloring/print-color-rgb-style + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/print-color-rgb-style/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + // Define RGB colors for foreground and background. + foregroundRGB := pterm.RGB{R: 187, G: 80, B: 0} + backgroundRGB := pterm.RGB{R: 0, G: 50, B: 123} + + // Create a new RGB style with the defined foreground and background colors. + rgbStyle := pterm.NewRGBStyle(foregroundRGB, backgroundRGB) + + // Print a string with the custom RGB style. + rgbStyle.Println("This text is not styled.") + + // Add the 'Bold' option to the RGB style and print a string with this style. + rgbStyle.AddOptions(pterm.Bold).Println("This text is bold.") + + // Add the 'Italic' option to the RGB style and print a string with this style. + rgbStyle.AddOptions(pterm.Italic).Println("This text is italic.") +} + +``` + +
+ ### demo/demo ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/demo/demo/animation.svg) @@ -1321,7 +1638,7 @@ func main() { pterm.DefaultCenter.WithCenterEachLineSeparately().Println(fadeInfo) }) - showcase("Fully Customizale", 2, func() { + showcase("Fully Customizable", 2, func() { for i := 0; i < 4; i++ { pterm.Println() } @@ -1461,9 +1778,9 @@ func randomInt(min, max int) int { -### header/custom +### header/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header/custom/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header/demo/animation.svg)
@@ -1475,35 +1792,25 @@ package main import "github.com/pterm/pterm" func main() { - // All available options: https://pkg.go.dev/github.com/pterm/pterm#HeaderPrinter - - // Build on top of DefaultHeader - pterm.DefaultHeader. // Use DefaultHeader as base - WithMargin(15). - WithBackgroundStyle(pterm.NewStyle(pterm.BgCyan)). - WithTextStyle(pterm.NewStyle(pterm.FgBlack)). - Println("This is a custom header!") - // Instead of printing the header you can set it to a variable. - // You can then reuse your custom header. + // Print a default header. + // This uses the default settings of PTerm to print a header. + pterm.DefaultHeader.Println("This is the default header!") - // Making a completely new HeaderPrinter - newHeader := pterm.HeaderPrinter{ - TextStyle: pterm.NewStyle(pterm.FgBlack), - BackgroundStyle: pterm.NewStyle(pterm.BgRed), - Margin: 20, - } + // Print a spacer line for better readability. + pterm.Println() - // Print header. - newHeader.Println("This is a custom header!") + // Print a full-width header. + // This uses the WithFullWidth() option of PTerm to print a header that spans the full width of the terminal. + pterm.DefaultHeader.WithFullWidth().Println("This is a full-width header.") } ```
-### header/demo +### header/custom -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header/custom/animation.svg)
@@ -1515,19 +1822,27 @@ package main import "github.com/pterm/pterm" func main() { - // Print a default header. - pterm.DefaultHeader.Println("This is the default header!") - pterm.Println() // spacer - pterm.DefaultHeader.WithFullWidth().Println("This is a full-width header.") + // Customize the DefaultHeader with a cyan background, black text, and a margin of 15. + pterm.DefaultHeader.WithMargin(15).WithBackgroundStyle(pterm.NewStyle(pterm.BgCyan)).WithTextStyle(pterm.NewStyle(pterm.FgBlack)).Println("This is a custom header!") + + // Define a new HeaderPrinter with a red background, black text, and a margin of 20. + newHeader := pterm.HeaderPrinter{ + TextStyle: pterm.NewStyle(pterm.FgBlack), + BackgroundStyle: pterm.NewStyle(pterm.BgRed), + Margin: 20, + } + + // Print the custom header using the new HeaderPrinter. + newHeader.Println("This is a custom header!") } ```
-### interactive_confirm/demo +### heatmap/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_confirm/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/heatmap/demo/animation.svg)
@@ -1541,25 +1856,33 @@ import ( ) func main() { - result, _ := pterm.DefaultInteractiveConfirm.Show() - pterm.Println() // Blank line - pterm.Info.Printfln("You answered: %s", boolToText(result)) -} + // Define the data for the heatmap. Each sub-array represents a row in the heatmap. + data := [][]float32{ + {0.9, 0.2, -0.7, 0.4, -0.5, 0.6, -0.3, 0.8, -0.1, -1.0, 0.1, -0.8, 0.3}, + {0.2, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.9, -0.9, -0.7, -0.5, -0.3}, + {0.4, 0.4, -0.3, -1.0, 0.3, -0.2, -0.9, 0.5, -0.3, -1.0, 0.6, -0.2, -0.9}, + {0.9, -0.5, -0.1, 0.3, 1, -0.7, -0.3, 0.1, 0.7, -0.9, -0.5, 0.2, 0.6}, + {0.5, 0.6, 0.1, -0.2, -0.7, 0.8, 0.6, 0.1, -0.5, -0.7, 0.7, 0.3, 0.0}, + } -func boolToText(b bool) string { - if b { - return pterm.Green("Yes") + // Define the labels for the X and Y axes of the heatmap. + headerData := pterm.HeatmapAxis{ + XAxis: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"}, + YAxis: []string{"1", "2", "3", "4", "5"}, } - return pterm.Red("No") + + // Create a heatmap with the defined data and axis labels, and enable RGB colors. + // Then render the heatmap. + pterm.DefaultHeatmap.WithAxisData(headerData).WithData(data).WithEnableRGB().Render() } ```
-### interactive_continue/demo +### heatmap/custom_colors -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/heatmap/custom_colors/animation.svg)
@@ -1573,18 +1896,43 @@ import ( ) func main() { - result, _ := pterm.DefaultInteractiveContinue.Show() - pterm.Println() // Blank line - pterm.Info.Printfln("You answered: %s", result) + // Define the data for the heatmap + data := [][]float32{ + {0.9, 0.2, -0.7, 0.4, -0.5, 0.6, -0.3, 0.8, -0.1, -1.0, 0.1, -0.8, 0.3}, + {0.2, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.9, -0.9, -0.7, -0.5, -0.3}, + {0.4, 0.4, -0.3, -1.0, 0.3, -0.2, -0.9, 0.5, -0.3, -1.0, 0.6, -0.2, -0.9}, + {0.9, -0.5, -0.1, 0.3, 1, -0.7, -0.3, 0.1, 0.7, -0.9, -0.5, 0.2, 0.6}, + {0.5, 0.6, 0.1, -0.2, -0.7, 0.8, 0.6, 0.1, -0.5, -0.7, 0.7, 0.3, 0.0}, + } + + // Define the axis labels for the heatmap + headerData := pterm.HeatmapAxis{ + XAxis: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"}, + YAxis: []string{"1", "2", "3", "4", "5"}, + } + + // Print an informational message + pterm.Info.Println("The following table has no rgb (supported by every terminal), no axis data and a legend.") + pterm.Println() + + // Create the heatmap with the defined data and options, and render it + pterm.DefaultHeatmap. + WithData(data). + WithBoxed(false). + WithAxisData(headerData). + WithLegend(false). + WithColors(pterm.BgBlue, pterm.BgRed, pterm.BgGreen, pterm.BgYellow). + WithLegend(). + Render() } ```
-### interactive_multiselect/custom-checkmarks +### heatmap/custom_legend -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/custom-checkmarks/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/heatmap/custom_legend/animation.svg)
@@ -1594,36 +1942,48 @@ func main() { package main import ( - "fmt" - - "atomicgo.dev/keyboard/keys" - "github.com/pterm/pterm" ) func main() { - var options []string + // Define the data for the heatmap + data := [][]float32{ + {0.9, 0.2, -0.7, 0.4, -0.5, 0.6, -0.3, 0.8, -0.1, -1.0, 0.1, -0.8, 0.3}, + {0.2, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.9, -0.9, -0.7, -0.5, -0.3}, + {0.4, 0.4, -0.3, -1.0, 0.3, -0.2, -0.9, 0.5, -0.3, -1.0, 0.6, -0.2, -0.9}, + {0.9, -0.5, -0.1, 0.3, 1, -0.7, -0.3, 0.1, 0.7, -0.9, -0.5, 0.2, 0.6}, + {0.5, 0.6, 0.1, -0.2, -0.7, 0.8, 0.6, 0.1, -0.5, -0.7, 0.7, 0.3, 0.0}, + } - for i := 0; i < 5; i++ { - options = append(options, fmt.Sprintf("Option %d", i)) + // Define the header data for the heatmap + headerData := pterm.HeatmapAxis{ + XAxis: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"}, + YAxis: []string{"1", "2", "3", "4", "5"}, } - printer := pterm.DefaultInteractiveMultiselect.WithOptions(options) - printer.Filter = false - printer.KeyConfirm = keys.Enter - printer.KeySelect = keys.Space - printer.Checkmark = &pterm.Checkmark{Checked: pterm.Green("+"), Unchecked: pterm.Red("-")} - selectedOptions, _ := printer.Show() - pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions)) + // Print an informational message + pterm.Info.Println("The following table has rgb (not supported by every terminal), axis data and a custom legend.") + pterm.Println() + + // Create the heatmap with the defined data and options + // Options are chained in a single line for simplicity + pterm.DefaultHeatmap. + WithData(data). + WithBoxed(false). + WithAxisData(headerData). + WithEnableRGB(). + WithLegendLabel("custom"). + WithLegendOnlyColoredCells(). + Render() // Render the heatmap } ```
-### interactive_multiselect/custom-keys +### heatmap/custom_rgb -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/custom-keys/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/heatmap/custom_rgb/animation.svg)
@@ -1633,34 +1993,54 @@ func main() { package main import ( - "fmt" - - "atomicgo.dev/keyboard/keys" "github.com/pterm/pterm" ) func main() { - var options []string + // Define the data for the heatmap. + data := [][]float32{ + {0.9, 0.2, -0.7, 0.4, -0.5, 0.6, -0.3, 0.8, -0.1, -1.0, 0.1, -0.8, 0.3}, + {0.2, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.9, -0.9, -0.7, -0.5, -0.3}, + {0.4, 0.4, -0.3, -1.0, 0.3, -0.2, -0.9, 0.5, -0.3, -1.0, 0.6, -0.2, -0.9}, + {0.9, -0.5, -0.1, 0.3, 1, -0.7, -0.3, 0.1, 0.7, -0.9, -0.5, 0.2, 0.6}, + {0.5, 0.6, 0.1, -0.2, -0.7, 0.8, 0.6, 0.1, -0.5, -0.7, 0.7, 0.3, 0.0}, + } - for i := 0; i < 5; i++ { - options = append(options, fmt.Sprintf("Option %d", i)) + // Define the axis labels for the heatmap. + axisLabels := pterm.HeatmapAxis{ + XAxis: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"}, + YAxis: []string{"1", "2", "3", "4", "5"}, } - printer := pterm.DefaultInteractiveMultiselect.WithOptions(options) - printer.Filter = false - printer.KeyConfirm = keys.Enter - printer.KeySelect = keys.Space - selectedOptions, _ := printer.Show() - pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions)) + // Print an informational message. + pterm.Info.Println("The following table has rgb (not supported by every terminal), axis data and a legend.") + pterm.Println() + + // Define the color range for the heatmap. + rgbRange := []pterm.RGB{ + pterm.NewRGB(0, 0, 255), + pterm.NewRGB(255, 0, 0), + pterm.NewRGB(0, 255, 0), + pterm.NewRGB(255, 255, 0), + } + + // Create and render the heatmap. + pterm.DefaultHeatmap. + WithData(data). + WithBoxed(false). + WithAxisData(axisLabels). + WithEnableRGB(). + WithRGBRange(rgbRange...). + Render() } ```
-### interactive_multiselect/demo +### heatmap/no_grid -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/heatmap/no_grid/animation.svg)
@@ -1670,33 +2050,40 @@ func main() { package main import ( - "fmt" - "github.com/pterm/pterm" ) func main() { - var options []string - - for i := 0; i < 100; i++ { - options = append(options, fmt.Sprintf("Option %d", i)) + // Define the data for the heatmap. + data := [][]float32{ + {0.9, 0.2, -0.7, 0.4, -0.5, 0.6, -0.3, 0.8, -0.1, -1.0, 0.1, -0.8, 0.3}, + {0.2, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.9, -0.9, -0.7, -0.5, -0.3}, + {0.4, 0.4, -0.3, -1.0, 0.3, -0.2, -0.9, 0.5, -0.3, -1.0, 0.6, -0.2, -0.9}, + {0.9, -0.5, -0.1, 0.3, 1, -0.7, -0.3, 0.1, 0.7, -0.9, -0.5, 0.2, 0.6}, + {0.5, 0.6, 0.1, -0.2, -0.7, 0.8, 0.6, 0.1, -0.5, -0.7, 0.7, 0.3, 0.0}, } - for i := 0; i < 5; i++ { - options = append(options, fmt.Sprintf("You can use fuzzy searching (%d)", i)) + // Define the axis data for the heatmap. + axisData := pterm.HeatmapAxis{ + XAxis: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"}, + YAxis: []string{"1", "2", "3", "4", "5"}, } - selectedOptions, _ := pterm.DefaultInteractiveMultiselect.WithOptions(options).Show() - pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions)) + // Print an informational message. + pterm.Info.Println("The following table has rgb (not supported by every terminal), axis data and a legend.") + pterm.Println() + + // Create the heatmap with the defined data and options, then render it. + pterm.DefaultHeatmap.WithData(data).WithBoxed(false).WithAxisData(axisData).WithEnableRGB().WithLegend().WithGrid(false).Render() } ```
-### interactive_select/demo +### heatmap/separated -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_select/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/heatmap/separated/animation.svg)
@@ -1705,34 +2092,39 @@ func main() { ```go package main -import ( - "fmt" - - "github.com/pterm/pterm" -) +import "github.com/pterm/pterm" func main() { - var options []string - - for i := 0; i < 100; i++ { - options = append(options, fmt.Sprintf("Option %d", i)) + // Define the data for the heatmap. + data := [][]float32{ + {0.9, 0.2, -0.7, 0.4, -0.5, 0.6, -0.3, 0.8, -0.1, -1.0, 0.1, -0.8, 0.3}, + {0.2, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.9, -0.9, -0.7, -0.5, -0.3}, + {0.4, 0.4, -0.3, -1.0, 0.3, -0.2, -0.9, 0.5, -0.3, -1.0, 0.6, -0.2, -0.9}, + {0.9, -0.5, -0.1, 0.3, 1, -0.7, -0.3, 0.1, 0.7, -0.9, -0.5, 0.2, 0.6}, + {0.5, 0.6, 0.1, -0.2, -0.7, 0.8, 0.6, 0.1, -0.5, -0.7, 0.7, 0.3, 0.0}, } - for i := 0; i < 5; i++ { - options = append(options, fmt.Sprintf("You can use fuzzy searching (%d)", i)) + // Define the axis labels for the heatmap. + headerData := pterm.HeatmapAxis{ + XAxis: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"}, + YAxis: []string{"1", "2", "3", "4", "5"}, } - selectedOption, _ := pterm.DefaultInteractiveSelect.WithOptions(options).Show() - pterm.Info.Printfln("Selected option: %s", pterm.Green(selectedOption)) + // Print an informational message. + pterm.Info.Println("The following table has no rgb (supported by every terminal), no axis data and no legend.") + pterm.Println() + + // Create the heatmap with the specified data and options, and render it. + pterm.DefaultHeatmap.WithData(data).WithBoxed(false).WithAxisData(headerData).WithLegend(false).Render() } ```
-### interactive_textinput/demo +### interactive_confirm/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_textinput/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_confirm/demo/animation.svg)
@@ -1746,18 +2138,33 @@ import ( ) func main() { - result, _ := pterm.DefaultInteractiveTextInput.WithMultiLine(false).Show() - pterm.Println() // Blank line - pterm.Info.Printfln("You answered: %s", result) + // Show an interactive confirmation dialog and get the result. + result, _ := pterm.DefaultInteractiveConfirm.Show() + + // Print a blank line for better readability. + pterm.Println() + + // Print the user's answer in a formatted way. + pterm.Info.Printfln("You answered: %s", boolToText(result)) +} + +// boolToText converts a boolean value to a colored text. +// If the value is true, it returns a green "Yes". +// If the value is false, it returns a red "No". +func boolToText(b bool) string { + if b { + return pterm.Green("Yes") + } + return pterm.Red("No") } ```
-### interactive_textinput/multi-line +### interactive_continue/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_textinput/multi-line/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg)
@@ -1771,8 +2178,21 @@ import ( ) func main() { - result, _ := pterm.DefaultInteractiveTextInput.WithMultiLine().Show() // Text input with multi line enabled - pterm.Println() // Blank line + // Create an interactive continue prompt with default settings + // This will pause the program execution until the user presses enter + // The message displayed is "Press 'Enter' to continue..." + prompt := pterm.DefaultInteractiveContinue + + // Show the prompt and wait for user input + // The returned result is the user's input (should be empty as it's a continue prompt) + // The second return value is an error which is ignored here + result, _ := prompt.Show() + + // Print a blank line for better readability + pterm.Println() + + // Print the user's input with an info prefix + // As this is a continue prompt, the input should be empty pterm.Info.Printfln("You answered: %s", result) } @@ -1780,9 +2200,9 @@ func main() {
-### logger/custom-key-styles +### interactive_multiselect/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/custom-key-styles/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/demo/animation.svg)
@@ -1791,33 +2211,40 @@ func main() { ```go package main -import "github.com/pterm/pterm" +import ( + "fmt" + "github.com/pterm/pterm" +) func main() { - logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace) // Only show logs with a level of Trace or higher. + // Initialize an empty slice to hold the options. + var options []string - // Overwrite all key styles with a new map - logger = logger.WithKeyStyles(map[string]pterm.Style{ - "priority": *pterm.NewStyle(pterm.FgRed), - }) + // Populate the options slice with 100 options. + for i := 0; i < 100; i++ { + options = append(options, fmt.Sprintf("Option %d", i)) + } - // The priority key should now be red - logger.Info("The priority key should now be red", logger.Args("priority", "low", "foo", "bar")) + // Add 5 more options to the slice, indicating the availability of fuzzy searching. + for i := 0; i < 5; i++ { + options = append(options, fmt.Sprintf("You can use fuzzy searching (%d)", i)) + } - // Append a key style to the exisiting ones - logger.AppendKeyStyle("foo", *pterm.NewStyle(pterm.FgBlue)) + // Use PTerm's interactive multiselect to present the options to the user and capture their selections. + // The Show() method displays the options and waits for user input. + selectedOptions, _ := pterm.DefaultInteractiveMultiselect.WithOptions(options).Show() - // The foo key should now be blue - logger.Info("The foo key should now be blue", logger.Args("priority", "low", "foo", "bar")) + // Print the selected options, highlighted in green. + pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions)) } ```
-### logger/default +### interactive_multiselect/custom-checkmarks -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/default/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/custom-checkmarks/animation.svg)
@@ -1827,38 +2254,40 @@ func main() { package main import ( + "fmt" "github.com/pterm/pterm" - "time" ) func main() { - logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace) // Only show logs with a level of Trace or higher. - - logger.Trace("Doing not so important stuff", logger.Args("priority", "super low")) + // Initialize an empty slice to hold the options + var options []string - // You can also use the `ArgsFromMap` function to create a `Args` object from a map. - interstingStuff := map[string]any{ - "when were crayons invented": "1903", - "what is the meaning of life": 42, - "is this interesting": true, + // Populate the options slice with 5 options + for i := 0; i < 5; i++ { + options = append(options, fmt.Sprintf("Option %d", i)) } - logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff)) - logger.Info("That was actually interesting", logger.Args("such", "wow")) - logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph")) - logger.Error("Damn, here it is!", logger.Args("error", "something went wrong")) - logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long")) - time.Sleep(time.Second * 2) - logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true)) + // Create a new interactive multiselect printer with the options + // Disable the filter and define the checkmark symbols + printer := pterm.DefaultInteractiveMultiselect. + WithOptions(options). + WithFilter(false). + WithCheckmark(&pterm.Checkmark{Checked: pterm.Green("+"), Unchecked: pterm.Red("-")}) + + // Show the interactive multiselect and get the selected options + selectedOptions, _ := printer.Show() + + // Print the selected options + pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions)) } ```
-### logger/demo +### interactive_multiselect/custom-keys -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/custom-keys/animation.svg)
@@ -1868,37 +2297,250 @@ func main() { package main import ( + "atomicgo.dev/keyboard/keys" + "fmt" "github.com/pterm/pterm" - "time" ) func main() { - logger := pterm.DefaultLogger. - WithLevel(pterm.LogLevelTrace) + // Initialize an empty slice to hold the options + var options []string - logger.Trace("Doing not so important stuff", logger.Args("priority", "super low")) + // Populate the options slice with 5 options + for i := 0; i < 5; i++ { + options = append(options, fmt.Sprintf("Option %d", i)) + } - sleep() + // Create a new interactive multiselect printer with the options + // Disable the filter and set the keys for confirming and selecting options + printer := pterm.DefaultInteractiveMultiselect. + WithOptions(options). + WithFilter(false). + WithKeyConfirm(keys.Enter). + WithKeySelect(keys.Space) - interstingStuff := map[string]any{ - "when were crayons invented": "1903", - "what is the meaning of life": 42, + // Show the interactive multiselect and get the selected options + selectedOptions, _ := printer.Show() + + // Print the selected options + pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions)) +} + +``` + +
+ +### interactive_select/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_select/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "fmt" + "github.com/pterm/pterm" +) + +func main() { + // Initialize an empty slice to hold the options + var options []string + + // Generate 100 options and add them to the options slice + for i := 0; i < 100; i++ { + options = append(options, fmt.Sprintf("Option %d", i)) + } + + // Generate 5 additional options with a specific message and add them to the options slice + for i := 0; i < 5; i++ { + options = append(options, fmt.Sprintf("You can use fuzzy searching (%d)", i)) + } + + // Use PTerm's interactive select feature to present the options to the user and capture their selection + // The Show() method displays the options and waits for the user's input + selectedOption, _ := pterm.DefaultInteractiveSelect.WithOptions(options).Show() + + // Display the selected option to the user with a green color for emphasis + pterm.Info.Printfln("Selected option: %s", pterm.Green(selectedOption)) +} + +``` + +
+ +### interactive_textinput/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_textinput/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + // Create an interactive text input with single line input mode + textInput := pterm.DefaultInteractiveTextInput.WithMultiLine(false) + + // Show the text input and get the result + result, _ := textInput.Show() + + // Print a blank line for better readability + pterm.Println() + + // Print the user's answer with an info prefix + pterm.Info.Printfln("You answered: %s", result) +} + +``` + +
+ +### interactive_textinput/multi-line + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_textinput/multi-line/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + // Create a default interactive text input with multi-line enabled. + // This allows the user to input multiple lines of text. + textInput := pterm.DefaultInteractiveTextInput.WithMultiLine() + + // Show the text input to the user and store the result. + // The second return value (an error) is ignored with '_'. + result, _ := textInput.Show() + + // Print a blank line for better readability in the output. + pterm.Println() + + // Print the user's input prefixed with an informational message. + // The '%s' placeholder is replaced with the user's input. + pterm.Info.Printfln("You answered: %s", result) +} + +``` + +
+ +### interactive_textinput/password + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_textinput/password/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import "github.com/pterm/pterm" + +func main() { + // Create an interactive text input with a mask for password input + passwordInput := pterm.DefaultInteractiveTextInput.WithMask("*") + + // Show the password input prompt and store the result + result, _ := passwordInput.Show("Enter your password") + + // Get the default logger from PTerm + logger := pterm.DefaultLogger + + // Log the received password (masked) + // Note: In a real-world application, you should never log passwords + logger.Info("Password received", logger.Args("password", result)) +} + +``` + +
+ +### logger/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" + "time" +) + +func main() { + // Create a logger with trace level + logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace) + + // Log a trace level message + logger.Trace("Doing not so important stuff", logger.Args("priority", "super low")) + + // Pause for 3 seconds + sleep() + + // Define a map with interesting stuff + interstingStuff := map[string]any{ + "when were crayons invented": "1903", + "what is the meaning of life": 42, "is this interesting": true, } + + // Log a debug level message with arguments from the map logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff)) + + // Pause for 3 seconds sleep() + // Log an info level message logger.Info("That was actually interesting", logger.Args("such", "wow")) + + // Pause for 3 seconds sleep() + + // Log a warning level message logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph")) + + // Pause for 3 seconds sleep() + + // Log an error level message logger.Error("Damn, here it is!", logger.Args("error", "something went wrong")) + + // Pause for 3 seconds sleep() + + // Log an info level message with a long text that will be automatically wrapped logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long")) + + // Pause for 3 seconds sleep() + + // Log a fatal level message logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true)) } +// Function to pause the execution for 3 seconds func sleep() { time.Sleep(time.Second * 3) } @@ -1907,9 +2549,9 @@ func sleep() {
-### logger/json +### logger/custom-key-styles -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/json/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/custom-key-styles/animation.svg)
@@ -1921,20 +2563,121 @@ package main import "github.com/pterm/pterm" func main() { - logger := pterm.DefaultLogger. - WithLevel(pterm.LogLevelTrace). // Only show logs with a level of Trace or higher. - WithFormatter(pterm.LogFormatterJSON) // ! Make the logger print JSON logs. + // Create a logger with a level of Trace or higher. + logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace) + + // Define a new style for the "priority" key. + priorityStyle := map[string]pterm.Style{ + "priority": *pterm.NewStyle(pterm.FgRed), + } + + // Overwrite all key styles with the new map. + logger = logger.WithKeyStyles(priorityStyle) + + // Log an info message. The "priority" key will be displayed in red. + logger.Info("The priority key should now be red", logger.Args("priority", "low", "foo", "bar")) + + // Define a new style for the "foo" key. + fooStyle := *pterm.NewStyle(pterm.FgBlue) + + // Append the new style to the existing ones. + logger.AppendKeyStyle("foo", fooStyle) + // Log another info message. The "foo" key will be displayed in blue. + logger.Info("The foo key should now be blue", logger.Args("priority", "low", "foo", "bar")) +} + +``` + +
+ +### logger/default + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/default/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" + "time" +) + +func main() { + // Create a logger with a level of Trace or higher. + logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace) + + // Log a trace message with additional arguments. logger.Trace("Doing not so important stuff", logger.Args("priority", "super low")) - // You can also use the `ArgsFromMap` function to create a `Args` object from a map. + // Create a map of interesting stuff. interstingStuff := map[string]any{ "when were crayons invented": "1903", "what is the meaning of life": 42, "is this interesting": true, } + + // Log a debug message with arguments from a map. logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff)) + // Log an info message with additional arguments. + logger.Info("That was actually interesting", logger.Args("such", "wow")) + + // Log a warning message with additional arguments. + logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph")) + + // Log an error message with additional arguments. + logger.Error("Damn, here it is!", logger.Args("error", "something went wrong")) + + // Log an info message with additional arguments. PTerm will automatically wrap long logs. + logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long")) + + // Pause for 2 seconds. + time.Sleep(time.Second * 2) + + // Log a fatal message with additional arguments. This will terminate the process. + logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true)) +} + +``` + +
+ +### logger/json + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/json/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import "github.com/pterm/pterm" + +func main() { + // Create a logger with Trace level and JSON formatter + logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace).WithFormatter(pterm.LogFormatterJSON) + + // Log a Trace level message with additional arguments + logger.Trace("Doing not so important stuff", logger.Args("priority", "super low")) + + // Create a map of interesting stuff + interestingStuff := map[string]any{ + "when were crayons invented": "1903", + "what is the meaning of life": 42, + "is this interesting": true, + } + + // Log a Debug level message with arguments from the map + logger.Debug("This might be interesting", logger.ArgsFromMap(interestingStuff)) + + // Log Info, Warn, Error, and Fatal level messages with additional arguments logger.Info("That was actually interesting", logger.Args("such", "wow")) logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph")) logger.Error("Damn, here it is!", logger.Args("error", "something went wrong")) @@ -1960,24 +2703,35 @@ package main import "github.com/pterm/pterm" func main() { - logger := pterm.DefaultLogger. - WithLevel(pterm.LogLevelTrace). // Only show logs with a level of Trace or higher. - WithCaller() // ! Show the caller of the log function. + // Create a logger with Trace level and caller information + logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace).WithCaller() + // Log a trace message with additional arguments logger.Trace("Doing not so important stuff", logger.Args("priority", "super low")) - // You can also use the `ArgsFromMap` function to create a `Args` object from a map. - interstingStuff := map[string]any{ + // Create a map of interesting stuff + interestingStuff := map[string]any{ "when were crayons invented": "1903", "what is the meaning of life": 42, "is this interesting": true, } - logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff)) + // Log a debug message with arguments from a map + logger.Debug("This might be interesting", logger.ArgsFromMap(interestingStuff)) + + // Log an info message with additional arguments logger.Info("That was actually interesting", logger.Args("such", "wow")) + + // Log a warning message with additional arguments logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph")) + + // Log an error message with additional arguments logger.Error("Damn, here it is!", logger.Args("error", "something went wrong")) + + // Log an info message with additional arguments. PTerm will automatically wrap long logs. logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long")) + + // Log a fatal message with additional arguments. This will terminate the process. logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true)) } @@ -1985,6 +2739,80 @@ func main() {
+### multiple-live-printers/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/multiple-live-printers/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "time" + + "github.com/pterm/pterm" +) + +func main() { + // Create a multi printer for managing multiple printers + multi := pterm.DefaultMultiPrinter + + // Create two spinners with their own writers + spinner1, _ := pterm.DefaultSpinner.WithWriter(multi.NewWriter()).Start("Spinner 1") + spinner2, _ := pterm.DefaultSpinner.WithWriter(multi.NewWriter()).Start("Spinner 2") + + // Create five progress bars with their own writers and a total of 100 + pb1, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 1") + pb2, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 2") + pb3, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 3") + pb4, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 4") + pb5, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 5") + + // Start the multi printer + multi.Start() + + // Increment progress bars and spinners based on certain conditions + for i := 1; i <= 100; i++ { + pb1.Increment() // Increment progress bar 1 every iteration + + if i%2 == 0 { + pb2.Add(3) // Add 3 to progress bar 2 every even iteration + } + + if i%5 == 0 { + pb3.Increment() // Increment progress bar 3 every 5th iteration + } + + if i%10 == 0 { + pb4.Increment() // Increment progress bar 4 every 10th iteration + } + + if i%3 == 0 { + pb5.Increment() // Increment progress bar 5 every 3rd iteration + } + + if i%50 == 0 { + spinner1.Success("Spinner 1 is done!") // Mark spinner 1 as successful every 50th iteration + } + + if i%60 == 0 { + spinner2.Fail("Spinner 2 failed!") // Mark spinner 2 as failed every 60th iteration + } + + time.Sleep(time.Millisecond * 50) // Sleep for 50 milliseconds between each iteration + } + + // Stop the multi printer + multi.Stop() +} + +``` + +
+ ### panel/demo ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/panel/demo/animation.svg) @@ -1999,13 +2827,20 @@ package main import "github.com/pterm/pterm" func main() { - // Declare panels in a two dimensional grid system. + // Define panels in a 2D grid system panels := pterm.Panels{ - {{Data: "This is the first panel"}, {Data: pterm.DefaultHeader.Sprint("Hello, World!")}, {Data: "This\npanel\ncontains\nmultiple\nlines"}}, - {{Data: pterm.Red("This is another\npanel line")}, {Data: "This is the second panel\nwith a new line"}}, + { + {Data: "This is the first panel"}, + {Data: pterm.DefaultHeader.Sprint("Hello, World!")}, + {Data: "This\npanel\ncontains\nmultiple\nlines"}, + }, + { + {Data: pterm.Red("This is another\npanel line")}, + {Data: "This is the second panel\nwith a new line"}, + }, } - // Print panels. + // Render the panels with a padding of 5 _ = pterm.DefaultPanel.WithPanels(panels).WithPadding(5).Render() } @@ -2013,9 +2848,9 @@ func main() { -### paragraph/customized +### paragraph/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/paragraph/customized/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/paragraph/demo/animation.svg)
@@ -2027,15 +2862,18 @@ package main import "github.com/pterm/pterm" func main() { - // Print a paragraph with a custom maximal width. - pterm.DefaultParagraph.WithMaxWidth(60).Println("This is a custom paragraph printer. As you can see, no words are separated, " + + // Using the default paragraph printer to print a long text. + // The text is split at the spaces, which is useful for continuous text of all kinds. + // The line width can be manually adjusted if needed. + pterm.DefaultParagraph.Println("This is the default paragraph printer. As you can see, no words are separated, " + "but the text is split at the spaces. This is useful for continuous text of all kinds. You can manually change the line width if you want to." + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam") - // Print one line space. + // Printing a line space for separation. pterm.Println() - // Print text without a paragraph printer. + // Printing a long text without using the paragraph printer. + // The default Println() function is used here, which does not provide intelligent splitting. pterm.Println("This text is written with the default Println() function. No intelligent splitting here." + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam") } @@ -2044,9 +2882,9 @@ func main() {
-### paragraph/demo +### paragraph/customized -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/paragraph/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/paragraph/customized/animation.svg)
@@ -2058,17 +2896,23 @@ package main import "github.com/pterm/pterm" func main() { - // Print long text with default paragraph printer. - pterm.DefaultParagraph.Println("This is the default paragraph printer. As you can see, no words are separated, " + + // Define a long text to be printed as a paragraph. + longText := "This is a custom paragraph printer. As you can see, no words are separated, " + "but the text is split at the spaces. This is useful for continuous text of all kinds. You can manually change the line width if you want to." + - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam") + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam" - // Print one line space. + // Print the long text as a paragraph with a custom maximal width of 60 characters. + pterm.DefaultParagraph.WithMaxWidth(60).Println(longText) + + // Print a line space to separate the paragraph from the following text. pterm.Println() - // Print long text without paragraph printer. - pterm.Println("This text is written with the default Println() function. No intelligent splitting here." + - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam") + // Define another long text to be printed without a paragraph printer. + longTextWithoutParagraph := "This text is written with the default Println() function. No intelligent splitting here." + + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam" + + // Print the long text without using a paragraph printer. + pterm.Println(longTextWithoutParagraph) } ``` @@ -2089,17 +2933,31 @@ package main import "github.com/pterm/pterm" func main() { - // Enable debug messages. + // Enable debug messages in PTerm. pterm.EnableDebugMessages() - pterm.Debug.Println("Hello, World!") // Print Debug. - pterm.Info.Println("Hello, World!") // Print Info. - pterm.Success.Println("Hello, World!") // Print Success. - pterm.Warning.Println("Hello, World!") // Print Warning. - pterm.Error.Println("Errors show the filename and linenumber inside the terminal!") // Print Error. - pterm.Info.WithShowLineNumber().Println("Other PrefixPrinters can do that too!") // Print Error. + // Print a debug message with PTerm. + pterm.Debug.Println("Hello, World!") + + // Print an informational message with PTerm. + pterm.Info.Println("Hello, World!") + + // Print a success message with PTerm. + pterm.Success.Println("Hello, World!") + + // Print a warning message with PTerm. + pterm.Warning.Println("Hello, World!") + + // Print an error message with PTerm. This will also display the filename and line number in the terminal. + pterm.Error.Println("Errors show the filename and linenumber inside the terminal!") + + // Print an informational message with PTerm, with line number. + // This demonstrates that other PrefixPrinters can also display line numbers. + pterm.Info.WithShowLineNumber().Println("Other PrefixPrinters can do that too!") + // Temporarily set Fatal to false, so that the CI won't crash. - pterm.Fatal.WithFatal(false).Println("Hello, World!") // Print Fatal. + // This will print a fatal message with PTerm, but won't terminate the program. + pterm.Fatal.WithFatal(false).Println("Hello, World!") } ``` @@ -2124,20 +2982,96 @@ import ( "github.com/pterm/pterm" ) -// Slice of strings with placeholder text. +// Slice of strings representing names of pseudo applications to be downloaded. var fakeInstallList = strings.Split("pseudo-excel pseudo-photoshop pseudo-chrome pseudo-outlook pseudo-explorer "+ "pseudo-dops pseudo-git pseudo-vsc pseudo-intellij pseudo-minecraft pseudo-scoop pseudo-chocolatey", " ") func main() { - // Create progressbar as fork from the default progressbar. + // Create a progressbar with the total steps equal to the number of items in fakeInstallList. + // Set the initial title of the progressbar to "Downloading stuff". p, _ := pterm.DefaultProgressbar.WithTotal(len(fakeInstallList)).WithTitle("Downloading stuff").Start() + // Loop over each item in the fakeInstallList. for i := 0; i < p.Total; i++ { - p.UpdateTitle("Downloading " + fakeInstallList[i]) // Update the title of the progressbar. - pterm.Success.Println("Downloading " + fakeInstallList[i]) // If a progressbar is running, each print will be printed above the progressbar. - p.Increment() // Increment the progressbar by one. Use Add(x int) to increment by a custom amount. - time.Sleep(time.Millisecond * 350) // Sleep 350 milliseconds. + // Simulate a slow download for the 7th item. + if i == 6 { + time.Sleep(time.Second * 3) + } + + // Update the title of the progressbar with the current item being downloaded. + p.UpdateTitle("Downloading " + fakeInstallList[i]) + + // Print a success message for the current download. This will be printed above the progressbar. + pterm.Success.Println("Downloading " + fakeInstallList[i]) + + // Increment the progressbar by one to indicate progress. + p.Increment() + + // Pause for 350 milliseconds to simulate the time taken for each download. + time.Sleep(time.Millisecond * 350) + } +} + +``` + +
+ +### progressbar/multiple + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/progressbar/multiple/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "time" + + "github.com/pterm/pterm" +) + +func main() { + // Create a multi printer instance from the default one + multi := pterm.DefaultMultiPrinter + + // Create five progress bars with a total of 100 units each, and assign each a new writer from the multi printer + pb1, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 1") + pb2, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 2") + pb3, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 3") + pb4, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 4") + pb5, _ := pterm.DefaultProgressbar.WithTotal(100).WithWriter(multi.NewWriter()).Start("Progressbar 5") + + // Start the multi printer + multi.Start() + + // Loop to increment progress bars based on certain conditions + for i := 1; i <= 100; i++ { + pb1.Increment() // Increment the first progress bar at each iteration + + if i%2 == 0 { + pb2.Add(3) // Add 3 units to the second progress bar at every even iteration + } + + if i%5 == 0 { + pb3.Increment() // Increment the third progress bar at every fifth iteration + } + + if i%10 == 0 { + pb4.Increment() // Increment the fourth progress bar at every tenth iteration + } + + if i%3 == 0 { + pb5.Increment() // Increment the fifth progress bar at every third iteration + } + + time.Sleep(time.Millisecond * 50) // Pause for 50 milliseconds at each iteration } + + // Stop the multi printer + multi.Stop() } ``` @@ -2158,14 +3092,16 @@ package main import "github.com/pterm/pterm" func main() { - // Print a section with level one. + // Create a section with level one and print it. pterm.DefaultSection.Println("This is a section!") - // Print placeholder. + + // Print an informational message. pterm.Info.Println("And here is some text.\nThis text could be anything.\nBasically it's just a placeholder") - // Print a section with level two. + // Create a section with level two and print it. pterm.DefaultSection.WithLevel(2).Println("This is another section!") - // Print placeholder. + + // Print another informational message. pterm.Info.Println("And this is\nmore placeholder text") } @@ -2173,6 +3109,53 @@ func main() {
+### slog/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/slog/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "log/slog" + + "github.com/pterm/pterm" +) + +func main() { + // Create a new slog handler with the default PTerm logger + handler := pterm.NewSlogHandler(&pterm.DefaultLogger) + + // Create a new slog logger with the handler + logger := slog.New(handler) + + // Log a debug message (won't show by default) + logger.Debug("This is a debug message that won't show") + + // Change the log level to debug to enable debug messages + pterm.DefaultLogger.Level = pterm.LogLevelDebug + + // Log a debug message (will show because debug level is enabled) + logger.Debug("This is a debug message", "changedLevel", true) + + // Log an info message + logger.Info("This is an info message") + + // Log a warning message + logger.Warn("This is a warning message") + + // Log an error message + logger.Error("This is an error message") +} + +``` + +
+ ### spinner/demo ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/spinner/demo/animation.svg) @@ -2238,6 +3221,68 @@ func main() { +### spinner/multiple + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/spinner/multiple/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "time" + + "github.com/pterm/pterm" +) + +func main() { + // Create a multi printer. This allows multiple spinners to print simultaneously. + multi := pterm.DefaultMultiPrinter + + // Create and start spinner 1 with a new writer from the multi printer. + // The spinner will display the message "Spinner 1". + spinner1, _ := pterm.DefaultSpinner.WithWriter(multi.NewWriter()).Start("Spinner 1") + + // Create and start spinner 2 with a new writer from the multi printer. + // The spinner will display the message "Spinner 2". + spinner2, _ := pterm.DefaultSpinner.WithWriter(multi.NewWriter()).Start("Spinner 2") + + // Create and start spinner 3 with a new writer from the multi printer. + // The spinner will display the message "Spinner 3". + spinner3, _ := pterm.DefaultSpinner.WithWriter(multi.NewWriter()).Start("Spinner 3") + + // Start the multi printer. This will start printing all the spinners. + multi.Start() + + // Wait for 1 second. + time.Sleep(time.Millisecond * 1000) + + // Stop spinner 1 with a success message. + spinner1.Success("Spinner 1 is done!") + + // Wait for 750 milliseconds. + time.Sleep(time.Millisecond * 750) + + // Stop spinner 2 with a failure message. + spinner2.Fail("Spinner 2 failed!") + + // Wait for 500 milliseconds. + time.Sleep(time.Millisecond * 500) + + // Stop spinner 3 with a warning message. + spinner3.Warning("Spinner 3 has a warning!") + + // Stop the multi printer. This will stop printing all the spinners. + multi.Stop() +} + +``` + +
+ ### style/demo ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/style/demo/animation.svg) @@ -2252,12 +3297,16 @@ package main import "github.com/pterm/pterm" func main() { - // Create styles as new variables + // Define a primary style with light cyan foreground, gray background, and bold text primary := pterm.NewStyle(pterm.FgLightCyan, pterm.BgGray, pterm.Bold) + + // Define a secondary style with light green foreground, white background, and italic text secondary := pterm.NewStyle(pterm.FgLightGreen, pterm.BgWhite, pterm.Italic) - // Use created styles + // Print "Hello, World!" with the primary style primary.Println("Hello, World!") + + // Print "Hello, World!" with the secondary style secondary.Println("Hello, World!") } @@ -2265,9 +3314,9 @@ func main() { -### table/boxed +### table/demo -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/boxed/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/demo/animation.svg)
@@ -2279,23 +3328,40 @@ package main import "github.com/pterm/pterm" func main() { - // Create a fork of the default table, fill it with data and print it. - // Data can also be generated and inserted later. - pterm.DefaultTable.WithHasHeader().WithBoxed().WithData(pterm.TableData{ + // Define the data for the first table + tableData1 := pterm.TableData{ {"Firstname", "Lastname", "Email", "Note"}, - {"Paul", "Dean", "nisi.dictum.augue@velitAliquam.co.uk", ""}, - {"Callie", "Mckay", "egestas.nunc.sed@est.com", "这是一个测试, haha!"}, - {"Libby", "Camacho", "aliquet.lobortis@semper.com", "just a test, hey!"}, - }).Render() + {"Paul", "Dean", "augue@velitAliquam.co.uk", ""}, + {"Callie", "Mckay", "nunc.sed@est.com", "这是一个测试, haha!"}, + {"Libby", "Camacho", "lobortis@semper.com", "just a test, hey!"}, + {"张", "小宝", "zhang@example.com", ""}, + } + + // Create a table with a header and the defined data, then render it + pterm.DefaultTable.WithHasHeader().WithData(tableData1).Render() + + pterm.Println() // Blank line + + // Define the data for the second table + tableData2 := pterm.TableData{ + {"Firstname", "Lastname", "Email"}, + {"Paul\n\nNewline", "Dean", "augue@velitAliquam.co.uk"}, + {"Callie", "Mckay", "nunc.sed@est.com\nNewline"}, + {"Libby", "Camacho", "lobortis@semper.com"}, + {"张", "小宝", "zhang@example.com"}, + } + + // Create another table with a header and the defined data, then render it + pterm.DefaultTable.WithHasHeader().WithData(tableData2).Render() } ```
-### table/demo +### table/boxed -![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/demo/animation.svg) +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/boxed/animation.svg)
@@ -2307,25 +3373,21 @@ package main import "github.com/pterm/pterm" func main() { - // Create a fork of the default table, fill it with data and print it. - // Data can also be generated and inserted later. - pterm.DefaultTable.WithHasHeader().WithData(pterm.TableData{ + // Define the data for the table. + // Each inner slice represents a row in the table. + // The first row is considered as the header of the table. + tableData := pterm.TableData{ {"Firstname", "Lastname", "Email", "Note"}, - {"Paul", "Dean", "nisi.dictum.augue@velitAliquam.co.uk", ""}, - {"Callie", "Mckay", "egestas.nunc.sed@est.com", "这是一个测试, haha!"}, - {"Libby", "Camacho", "aliquet.lobortis@semper.com", "just a test, hey!"}, - }).Render() - - pterm.Println() // Blank line + {"Paul", "Dean", "augue@velitAliquam.co.uk", ""}, + {"Callie", "Mckay", "nunc.sed@est.com", "这是一个测试, haha!"}, + {"Libby", "Camacho", "lobortis@semper.com", "just a test, hey!"}, + {"张", "小宝", "zhang@example.com", ""}, + } - // Create a table with multiple lines in a row. - pterm.DefaultTable.WithHasHeader().WithData(pterm.TableData{ - {"Firstname", "Lastname", "Email"}, - {"Paul\n\nNewline", "Dean", "nisi.dictum.augue@velitAliquam.co.uk"}, - {"Callie", "Mckay", "egestas.nunc.sed@est.com\nNewline"}, - {"Libby", "Camacho", "aliquet.lobortis@semper.com"}, - {"张", "小宝", "zhang@example.com"}, - }).Render() + // Create a table with the defined data. + // The table has a header and is boxed. + // Finally, render the table to print it. + pterm.DefaultTable.WithHasHeader().WithBoxed().WithData(tableData).Render() } ``` @@ -2346,14 +3408,19 @@ package main import "github.com/pterm/pterm" func main() { - // Create a table with multiple lines in a row and set a row separator. - pterm.DefaultTable.WithHasHeader().WithRowSeparator("-").WithHeaderRowSeparator("-").WithData(pterm.TableData{ + // Define the data for the table. + data := pterm.TableData{ {"Firstname", "Lastname", "Email"}, - {"Paul\n\nNewline", "Dean", "nisi.dictum.augue@velitAliquam.co.uk"}, - {"Callie", "Mckay", "egestas.nunc.sed@est.com\nNewline"}, - {"Libby", "Camacho", "aliquet.lobortis@semper.com"}, + {"Paul\n\nNewline", "Dean", "augue@velitAliquam.co.uk"}, + {"Callie", "Mckay", "nunc.sed@est.com\nNewline"}, + {"Libby", "Camacho", "lobortis@semper.com"}, {"张", "小宝", "zhang@example.com"}, - }).Render() + } + + // Create and render the table. + // The options are chained in a single line for simplicity. + // The table has a header, a row separator, and a header row separator. + pterm.DefaultTable.WithHasHeader().WithRowSeparator("-").WithHeaderRowSeparator("-").WithData(data).Render() } ``` @@ -2374,14 +3441,21 @@ package main import "github.com/pterm/pterm" func main() { - // Create a fork of the default table, fill it with data and print it. - // Data can also be generated and inserted later. - pterm.DefaultTable.WithHasHeader().WithRightAlignment().WithData(pterm.TableData{ + // Define the data for the table. + // Each inner slice represents a row in the table. + // The first row is considered as the header. + tableData := pterm.TableData{ {"Firstname", "Lastname", "Email", "Note"}, - {"Paul", "Dean", "nisi.dictum.augue@velitAliquam.co.uk", ""}, - {"Callie", "Mckay", "egestas.nunc.sed@est.com", "这是一个测试, haha!"}, - {"Libby", "Camacho", "aliquet.lobortis@semper.com", "just a test, hey!"}, - }).Render() + {"Paul", "Dean", "augue@velitAliquam.co.uk", ""}, + {"Callie", "Mckay", "nunc.sed@est.com", "这是一个测试, haha!"}, + {"Libby", "Camacho", "lobortis@semper.com", "just a test, hey!"}, + {"张", "小宝", "zhang@example.com", ""}, + } + + // Create a table with the defined data. + // The table has a header and the text in the cells is right-aligned. + // The Render() method is used to print the table to the console. + pterm.DefaultTable.WithHasHeader().WithRightAlignment().WithData(tableData).Render() } ``` @@ -2406,23 +3480,27 @@ import ( ) func main() { - // Print info. - pterm.Info.Println("These are the default theme styles.\n" + - "You can modify them easily to your personal preference,\n" + - "or create new themes from scratch :)") + // Print an informational message about the default theme styles. + pterm.Info.Println("These are the default theme styles.\nYou can modify them easily to your personal preference,\nor create new themes from scratch :)") - pterm.Println() // Print one line space. + // Print a blank line for better readability. + pterm.Println() - // Print every value of the default theme with its own style. + // Get the value and type of the default theme. v := reflect.ValueOf(pterm.ThemeDefault) typeOfS := v.Type() + // Check if the type of the default theme is 'pterm.Theme'. if typeOfS == reflect.TypeOf(pterm.Theme{}) { + // Iterate over each field in the default theme. for i := 0; i < v.NumField(); i++ { + // Try to convert the field to 'pterm.Style'. field, ok := v.Field(i).Interface().(pterm.Style) if ok { + // Print the field name using its own style. field.Println(typeOfS.Field(i).Name) } + // Pause for a quarter of a second to make the output easier to read. time.Sleep(time.Millisecond * 250) } } @@ -2448,11 +3526,17 @@ import ( ) func main() { + // Define a tree structure using pterm.TreeNode tree := pterm.TreeNode{ + // The top node of the tree Text: "Top node", + // The children of the top node Children: []pterm.TreeNode{{ + // A child node Text: "Child node", + // The children of the child node Children: []pterm.TreeNode{ + // Grandchildren nodes {Text: "Grandchild node"}, {Text: "Grandchild node"}, {Text: "Grandchild node"}, @@ -2460,6 +3544,7 @@ func main() { }}, } + // Render the tree with the defined structure as the root pterm.DefaultTree.WithRoot(tree).Render() } @@ -2484,37 +3569,37 @@ import ( ) func main() { - // You can use a LeveledList here, for easy generation. + // Define a leveled list to represent the structure of the directories. leveledList := pterm.LeveledList{ - pterm.LeveledListItem{Level: 0, Text: "C:"}, - pterm.LeveledListItem{Level: 1, Text: "Users"}, - pterm.LeveledListItem{Level: 1, Text: "Windows"}, - pterm.LeveledListItem{Level: 1, Text: "Programs"}, - pterm.LeveledListItem{Level: 1, Text: "Programs(x86)"}, - pterm.LeveledListItem{Level: 1, Text: "dev"}, - pterm.LeveledListItem{Level: 0, Text: "D:"}, - pterm.LeveledListItem{Level: 0, Text: "E:"}, - pterm.LeveledListItem{Level: 1, Text: "Movies"}, - pterm.LeveledListItem{Level: 1, Text: "Music"}, - pterm.LeveledListItem{Level: 2, Text: "LinkinPark"}, - pterm.LeveledListItem{Level: 1, Text: "Games"}, - pterm.LeveledListItem{Level: 2, Text: "Shooter"}, - pterm.LeveledListItem{Level: 3, Text: "CallOfDuty"}, - pterm.LeveledListItem{Level: 3, Text: "CS:GO"}, - pterm.LeveledListItem{Level: 3, Text: "Battlefield"}, - pterm.LeveledListItem{Level: 4, Text: "Battlefield 1"}, - pterm.LeveledListItem{Level: 4, Text: "Battlefield 2"}, - pterm.LeveledListItem{Level: 0, Text: "F:"}, - pterm.LeveledListItem{Level: 1, Text: "dev"}, - pterm.LeveledListItem{Level: 2, Text: "dops"}, - pterm.LeveledListItem{Level: 2, Text: "PTerm"}, - } - - // Generate tree from LeveledList. + {Level: 0, Text: "C:"}, + {Level: 1, Text: "Users"}, + {Level: 1, Text: "Windows"}, + {Level: 1, Text: "Programs"}, + {Level: 1, Text: "Programs(x86)"}, + {Level: 1, Text: "dev"}, + {Level: 0, Text: "D:"}, + {Level: 0, Text: "E:"}, + {Level: 1, Text: "Movies"}, + {Level: 1, Text: "Music"}, + {Level: 2, Text: "LinkinPark"}, + {Level: 1, Text: "Games"}, + {Level: 2, Text: "Shooter"}, + {Level: 3, Text: "CallOfDuty"}, + {Level: 3, Text: "CS:GO"}, + {Level: 3, Text: "Battlefield"}, + {Level: 4, Text: "Battlefield 1"}, + {Level: 4, Text: "Battlefield 2"}, + {Level: 0, Text: "F:"}, + {Level: 1, Text: "dev"}, + {Level: 2, Text: "dops"}, + {Level: 2, Text: "PTerm"}, + } + + // Convert the leveled list into a tree structure. root := putils.TreeFromLeveledList(leveledList) - root.Text = "Computer" + root.Text = "Computer" // Set the root node text. - // Render TreePrinter + // Render the tree structure using the default tree printer. pterm.DefaultTree.WithRoot(root).Render() } diff --git a/vendor/github.com/pterm/pterm/SECURITY.md b/vendor/github.com/pterm/pterm/SECURITY.md new file mode 100644 index 00000000..3e4653f8 --- /dev/null +++ b/vendor/github.com/pterm/pterm/SECURITY.md @@ -0,0 +1,24 @@ +# PTerm Security Policy +This security policy applies to the PTerm GitHub repository and outlines the process for reporting security issues and handling security incidents. The primary goal of this policy is to ensure the safety and integrity of the PTerm codebase and to minimize the impact of security incidents on our users. + +## 1. Overview +PTerm is a command-line interface (CLI) tool library, and we believe the security risks associated with it are minimal. However, we recognize that vulnerabilities can still arise, and we are committed to addressing them promptly and transparently. + +## 2. Reporting Security Issues +If you discover a security issue in PTerm, please follow these steps: + +Open a new issue in the PTerm GitHub repository, describing the security problem in detail. + +## 3. Vulnerable Dependencies +If a dependency of PTerm is found to be vulnerable or infected and requires immediate updates, please follow these steps: + +1. Open a new issue in the PTerm GitHub repository, describing the vulnerable dependency and the need for an update. +2. *Optional: Contact @MarvinJWendt directly via Twitter or Discord to alert them to the issue.* + +## 4. Incident Response +Upon receiving a security report, the PTerm team will: + +1. Acknowledge receipt of the report and review the issue. +2. Investigate the issue and determine the severity and impact. +3. Develop and implement a fix or mitigation plan, as necessary. +4. Update the PTerm repository and notify users, if applicable. diff --git a/vendor/github.com/pterm/pterm/area_printer.go b/vendor/github.com/pterm/pterm/area_printer.go index 5f3e5b04..ed4b0c22 100644 --- a/vendor/github.com/pterm/pterm/area_printer.go +++ b/vendor/github.com/pterm/pterm/area_printer.go @@ -1,6 +1,7 @@ package pterm import ( + "io" "strings" "atomicgo.dev/cursor" @@ -47,6 +48,11 @@ func (p AreaPrinter) WithCenter(b ...bool) *AreaPrinter { return &p } +// SetWriter sets the writer for the AreaPrinter. +func (p *AreaPrinter) SetWriter(writer io.Writer) { + +} + // Update overwrites the content of the AreaPrinter. // Can be used live. func (p *AreaPrinter) Update(text ...interface{}) { diff --git a/vendor/github.com/pterm/pterm/box_printer.go b/vendor/github.com/pterm/pterm/box_printer.go index 8bff4671..3185e024 100644 --- a/vendor/github.com/pterm/pterm/box_printer.go +++ b/vendor/github.com/pterm/pterm/box_printer.go @@ -240,8 +240,8 @@ func (p BoxPrinter) Sprint(a ...interface{}) string { maxWidth+p.LeftPadding+p.RightPadding) + p.BoxStyle.Sprint(p.TopLeftCornerString) } else { p.Title = strings.ReplaceAll(p.Title, "\n", " ") - if (maxWidth + p.RightPadding + p.LeftPadding - 4) < len(RemoveColorFromString(p.Title)) { - p.RightPadding = len(RemoveColorFromString(p.Title)) - (maxWidth + p.RightPadding + p.LeftPadding - 5) + if (maxWidth + p.RightPadding + p.LeftPadding - 4) < internal.GetStringMaxWidth(p.Title) { + p.RightPadding = internal.GetStringMaxWidth(p.Title) - (maxWidth + p.RightPadding + p.LeftPadding - 5) } if p.TitleTopLeft { topLine = p.BoxStyle.Sprint(p.BottomRightCornerString) + internal.AddTitleToLine(p.Title, p.BoxStyle.Sprint(p.HorizontalString), maxWidth+p.LeftPadding+p.RightPadding, true) + p.BoxStyle.Sprint(p.BottomLeftCornerString) diff --git a/vendor/github.com/pterm/pterm/bulletlist_printer.go b/vendor/github.com/pterm/pterm/bulletlist_printer.go index ed997aa5..8e6afcb0 100644 --- a/vendor/github.com/pterm/pterm/bulletlist_printer.go +++ b/vendor/github.com/pterm/pterm/bulletlist_printer.go @@ -116,10 +116,18 @@ func (l BulletListPrinter) Srender() (string, error) { item.BulletStyle = l.BulletStyle } } - if item.Bullet == "" { - ret += strings.Repeat(" ", item.Level) + item.BulletStyle.Sprint(l.Bullet) + " " + item.TextStyle.Sprint(item.Text) + "\n" - } else { - ret += strings.Repeat(" ", item.Level) + item.BulletStyle.Sprint(item.Bullet) + " " + item.TextStyle.Sprint(item.Text) + "\n" + + split := strings.Split(item.Text, "\n") + for i, line := range split { + if i == 0 { + if item.Bullet == "" { + ret += strings.Repeat(" ", item.Level) + item.BulletStyle.Sprint(l.Bullet) + " " + item.TextStyle.Sprint(line) + "\n" + } else { + ret += strings.Repeat(" ", item.Level) + item.BulletStyle.Sprint(item.Bullet) + " " + item.TextStyle.Sprint(line) + "\n" + } + } else { + ret += strings.Repeat(" ", item.Level) + strings.Repeat(" ", len(item.Bullet)) + " " + item.TextStyle.Sprint(line) + "\n" + } } } return ret, nil diff --git a/vendor/github.com/pterm/pterm/heatmap_printer.go b/vendor/github.com/pterm/pterm/heatmap_printer.go new file mode 100644 index 00000000..4fac71e3 --- /dev/null +++ b/vendor/github.com/pterm/pterm/heatmap_printer.go @@ -0,0 +1,744 @@ +package pterm + +import ( + "bytes" + "errors" + "io" + "math" + "strings" + + "github.com/pterm/pterm/internal" +) + +// DefaultHeatmap contains standards, which can be used to print a HeatmapPrinter. +var DefaultHeatmap = HeatmapPrinter{ + AxisStyle: &ThemeDefault.HeatmapHeaderStyle, + SeparatorStyle: &ThemeDefault.HeatmapSeparatorStyle, + VerticalSeparator: "│", + TopRightCornerSeparator: "└", + TopLeftCornerSeparator: "┘", + BottomLeftCornerSeparator: "┐", + BottomRightCornerSeparator: "┌", + HorizontalSeparator: "─", + TSeparator: "┬", + TReverseSeparator: "┴", + LSeparator: "├", + LReverseSeparator: "┤", + TCrossSeparator: "┼", + LegendLabel: "Legend", + Boxed: true, + Grid: true, + Legend: true, + TextRGB: RGB{0, 0, 0, false}, + RGBRange: []RGB{{R: 255, G: 0, B: 0, Background: true}, {R: 255, G: 165, B: 0, Background: true}, {R: 0, G: 255, B: 0, Background: true}}, + TextColor: FgBlack, + Colors: []Color{BgRed, BgLightRed, BgYellow, BgLightYellow, BgLightGreen, BgGreen}, + + EnableRGB: false, +} + +// HeatmapData is the type that contains the data of a HeatmapPrinter. +type HeatmapData [][]float32 + +type HeatmapAxis struct { + XAxis []string + YAxis []string +} + +// HeatmapPrinter is able to render tables. +type HeatmapPrinter struct { + HasHeader bool + AxisStyle *Style + VerticalSeparator string + TopRightCornerSeparator string + TopLeftCornerSeparator string + BottomLeftCornerSeparator string + BottomRightCornerSeparator string + HorizontalSeparator string + TSeparator string + TReverseSeparator string + LSeparator string + LReverseSeparator string + TCrossSeparator string + LegendLabel string + SeparatorStyle *Style + Data HeatmapData + Axis HeatmapAxis + Boxed bool + Grid bool + OnlyColoredCells bool + LegendOnlyColoredCells bool + EnableComplementaryColor bool + Legend bool + CellSize int + Colors []Color + TextColor Color + EnableRGB bool + RGBRange []RGB + TextRGB RGB + Writer io.Writer + + minValue float32 + maxValue float32 + + rgbLegendValue int +} + +var complementaryColors = map[Color]Color{ + BgBlack: FgLightWhite, + BgRed: FgCyan, + BgGreen: FgMagenta, + BgYellow: FgBlue, + BgBlue: FgYellow, + BgMagenta: FgGreen, + BgCyan: FgRed, + BgWhite: FgBlack, + BgDefault: FgBlack, + BgDarkGray: FgLightWhite, + BgLightRed: FgLightCyan, + BgLightGreen: FgLightMagenta, + BgLightYellow: FgLightBlue, + BgLightBlue: FgLightYellow, + BgLightMagenta: FgLightGreen, + BgLightCyan: FgLightRed, + BgLightWhite: FgBlack, +} + +// WithAxisData returns a new HeatmapPrinter, where the first line and row are headers. +func (p HeatmapPrinter) WithAxisData(hd HeatmapAxis) *HeatmapPrinter { + p.HasHeader = true + p.Axis = hd + return &p +} + +// WithAxisStyle returns a new HeatmapPrinter with a specific AxisStyle. +func (p HeatmapPrinter) WithAxisStyle(style *Style) *HeatmapPrinter { + p.AxisStyle = style + return &p +} + +// WithSeparatorStyle returns a new HeatmapPrinter with a specific SeparatorStyle. +func (p HeatmapPrinter) WithSeparatorStyle(style *Style) *HeatmapPrinter { + p.SeparatorStyle = style + return &p +} + +// WithData returns a new HeatmapPrinter with specific Data. +func (p HeatmapPrinter) WithData(data [][]float32) *HeatmapPrinter { + p.Data = data + return &p +} + +// WithTextColor returns a new HeatmapPrinter with a specific TextColor. +// This sets EnableComplementaryColor to false. +func (p HeatmapPrinter) WithTextColor(color Color) *HeatmapPrinter { + p.TextColor = color + p.EnableComplementaryColor = false + return &p +} + +// WithTextRGB returns a new HeatmapPrinter with a specific TextRGB. +// This sets EnableComplementaryColor to false. +func (p HeatmapPrinter) WithTextRGB(rgb RGB) *HeatmapPrinter { + p.TextRGB = rgb + p.EnableComplementaryColor = false + return &p +} + +// WithBoxed returns a new HeatmapPrinter with a box around the table. +// If set to true, Grid will be set to true too. +func (p HeatmapPrinter) WithBoxed(b ...bool) *HeatmapPrinter { + p.Boxed = internal.WithBoolean(b) + if p.Boxed && !p.Grid { + p.Grid = true + } + return &p +} + +// WithGrid returns a new HeatmapPrinter with a grid. +// If set to false, Boxed will be set to false too. +func (p HeatmapPrinter) WithGrid(b ...bool) *HeatmapPrinter { + b2 := internal.WithBoolean(b) + p.Grid = b2 + if !b2 && p.Boxed { + p.Boxed = false + } + return &p +} + +// WithEnableRGB returns a new HeatmapPrinter with RGB colors. +func (p HeatmapPrinter) WithEnableRGB(b ...bool) *HeatmapPrinter { + p.EnableRGB = internal.WithBoolean(b) + return &p +} + +// WithOnlyColoredCells returns a new HeatmapPrinter with only colored cells. +func (p HeatmapPrinter) WithOnlyColoredCells(b ...bool) *HeatmapPrinter { + b2 := internal.WithBoolean(b) + p.OnlyColoredCells = b2 + return &p +} + +// WithLegendOnlyColoredCells returns a new HeatmapPrinter with legend with only colored cells. +// This sets the Legend to true. +func (p HeatmapPrinter) WithLegendOnlyColoredCells(b ...bool) *HeatmapPrinter { + b2 := internal.WithBoolean(b) + p.LegendOnlyColoredCells = b2 + if b2 { + p.Legend = true + } + return &p +} + +// WithEnableComplementaryColor returns a new HeatmapPrinter with complement color. +func (p HeatmapPrinter) WithEnableComplementaryColor(b ...bool) *HeatmapPrinter { + p.EnableComplementaryColor = internal.WithBoolean(b) + return &p +} + +// WithLegend returns a new HeatmapPrinter with a legend. +func (p HeatmapPrinter) WithLegend(b ...bool) *HeatmapPrinter { + p.Legend = internal.WithBoolean(b) + return &p +} + +// WithCellSize returns a new HeatmapPrinter with a specific cell size. +// This only works if there is no header and OnlyColoredCells == true! +func (p HeatmapPrinter) WithCellSize(i int) *HeatmapPrinter { + p.CellSize = i + return &p +} + +// WithLegendLabel returns a new HeatmapPrinter with a specific legend tag. +// This sets the Legend to true. +func (p HeatmapPrinter) WithLegendLabel(s string) *HeatmapPrinter { + p.LegendLabel = s + p.Legend = true + return &p +} + +// WithRGBRange returns a new HeatmapPrinter with a specific RGBRange. +func (p HeatmapPrinter) WithRGBRange(rgb ...RGB) *HeatmapPrinter { + p.RGBRange = rgb + return &p +} + +// WithColors returns a new HeatmapPrinter with a specific Colors. +func (p HeatmapPrinter) WithColors(colors ...Color) *HeatmapPrinter { + p.Colors = colors + return &p +} + +// WithWriter sets the Writer. +func (p HeatmapPrinter) WithWriter(writer io.Writer) *HeatmapPrinter { + p.Writer = writer + return &p +} + +// Srender renders the HeatmapPrinter as a string. +func (p HeatmapPrinter) Srender() (string, error) { + if err := p.errCheck(); err != nil { + return "", err + } + + if p.SeparatorStyle == nil { + p.SeparatorStyle = DefaultHeatmap.SeparatorStyle + } + if p.AxisStyle == nil { + p.AxisStyle = DefaultHeatmap.AxisStyle + } + + if RawOutput { + p.Legend = false + } + + buffer := bytes.NewBufferString("") + xAmount := len(p.Data[0]) - 1 + yAmount := len(p.Data) - 1 + p.minValue, p.maxValue = minMaxFloat32(p.Data) + + var data string + for _, datum := range p.Data { + for _, f := range datum { + data += Sprintf("%v\n", f) + } + } + + if p.HasHeader { + data, xAmount, yAmount = p.computeAxisData(data, xAmount, yAmount) + } + + colWidth := internal.GetStringMaxWidth(data) + legendColWidth := colWidth + 2 + + if p.OnlyColoredCells && (p.CellSize > colWidth || !p.HasHeader) { + colWidth = p.CellSize + } + + if p.Boxed { + p.renderSeparatorRow(buffer, colWidth, xAmount, true) + } + + p.renderData(buffer, colWidth, xAmount, yAmount) + + if p.HasHeader { + p.renderHeader(buffer, colWidth, xAmount) + } + + if p.Boxed { + p.renderSeparatorRow(buffer, colWidth, xAmount, false) + } + + if p.Legend { + p.renderLegend(buffer, legendColWidth) + } + + buffer.WriteString("\n") + + return buffer.String(), nil +} + +func (p HeatmapPrinter) computeAxisData(data string, xAmount, yAmount int) (string, int, int) { + var header string + for _, h := range p.Axis.XAxis { + header += h + "\n" + } + for _, h := range p.Axis.YAxis { + header += h + "\n" + } + + if p.OnlyColoredCells { + data = header + } else { + data += header + } + xAmount++ + yAmount++ + + p.Axis.YAxis = append(p.Axis.YAxis, "") + + return data, xAmount, yAmount +} + +func (p HeatmapPrinter) renderSeparatorRow(buffer *bytes.Buffer, colWidth, xAmount int, top bool) { + tSep := p.TReverseSeparator + rightSep := p.TopRightCornerSeparator + leftSep := p.TopLeftCornerSeparator + + if top { + tSep = p.TSeparator + rightSep = p.BottomRightCornerSeparator + leftSep = p.BottomLeftCornerSeparator + } else { + buffer.WriteString("\n") + } + buffer.WriteString(p.SeparatorStyle.Sprint(rightSep)) + for i := 0; i < xAmount+1; i++ { + buffer.WriteString(strings.Repeat(p.SeparatorStyle.Sprint(p.HorizontalSeparator), colWidth)) + if i < xAmount { + buffer.WriteString(p.SeparatorStyle.Sprint(tSep)) + } + } + buffer.WriteString(p.SeparatorStyle.Sprint(leftSep)) + + if top { + buffer.WriteString("\n") + } +} + +func (p HeatmapPrinter) renderLegend(buffer *bytes.Buffer, legendColWidth int) { + buffer.WriteString("\n") + buffer.WriteString("\n") + if p.Boxed { + p.boxLegend(buffer, p.LegendLabel, legendColWidth) + } else { + p.generateLegend(buffer, p.LegendLabel, legendColWidth) + } +} + +func (p HeatmapPrinter) renderHeader(buffer *bytes.Buffer, colWidth int, xAmount int) { + buffer.WriteString("\n") + if p.Boxed { + buffer.WriteString(p.SeparatorStyle.Sprint(p.LSeparator)) + } + if p.Grid { + for i := 0; i < xAmount+1; i++ { + buffer.WriteString(strings.Repeat(p.SeparatorStyle.Sprint(p.HorizontalSeparator), colWidth)) + if i < xAmount { + buffer.WriteString(p.SeparatorStyle.Sprint(p.TCrossSeparator)) + } + } + } + if p.Boxed { + buffer.WriteString(p.SeparatorStyle.Sprint(p.LReverseSeparator)) + } + if p.Grid { + buffer.WriteString("\n") + } + for j, f := range p.Axis.XAxis { + if j == 0 { + if p.Boxed { + buffer.WriteString(p.SeparatorStyle.Sprint(p.VerticalSeparator)) + } + ct := internal.CenterText(" ", colWidth) + if len(ct) < colWidth { + ct += strings.Repeat(" ", colWidth-len(ct)) + } + buffer.WriteString(p.AxisStyle.Sprint(ct)) + if p.Grid { + buffer.WriteString(p.SeparatorStyle.Sprint(p.VerticalSeparator)) + } + } + var ct string + ct = internal.CenterText(Sprintf("%v", f), colWidth) + if len(ct) < colWidth { + ct += strings.Repeat(" ", colWidth-len(ct)) + } + buffer.WriteString(p.AxisStyle.Sprint(ct)) + + if j < xAmount { + if !p.Boxed && j == xAmount-1 { + continue + } + if p.Grid { + buffer.WriteString(p.SeparatorStyle.Sprint(p.VerticalSeparator)) + } + } + } +} + +func (p HeatmapPrinter) renderData(buffer *bytes.Buffer, colWidth int, xAmount int, yAmount int) { + for i, datum := range p.Data { + if p.Boxed { + buffer.WriteString(p.SeparatorStyle.Sprint(p.VerticalSeparator)) + } + for j, f := range datum { + if j == 0 && p.HasHeader { + ct := internal.CenterText(p.Axis.YAxis[i], colWidth) + if len(ct) < colWidth { + ct += strings.Repeat(" ", colWidth-len(ct)) + } + buffer.WriteString(p.AxisStyle.Sprint(ct)) + if p.Grid { + buffer.WriteString(p.SeparatorStyle.Sprint(p.VerticalSeparator)) + } + } + var ct string + if p.OnlyColoredCells { + ct = internal.CenterText(" ", colWidth) + } else { + ct = internal.CenterText(Sprintf("%v", f), colWidth) + } + if len(ct) < colWidth { + if len(Sprintf("%v", f)) == 1 { + ct += strings.Repeat(" ", colWidth-len(ct)) + } else { + ct = strings.Repeat(" ", colWidth-len(ct)) + ct + } + } + if p.EnableRGB { + rgb := p.RGBRange[0].Fade(p.minValue, p.maxValue, f, p.RGBRange[1:]...) + rgbStyle := NewRGBStyle(p.TextRGB, rgb) + if p.EnableComplementaryColor { + complimentary := NewRGB(internal.Complementary(rgb.R, rgb.G, rgb.B)) + rgbStyle = NewRGBStyle(complimentary, rgb) + } + buffer.WriteString(rgbStyle.Sprint(ct)) + } else { + color := getColor(p.minValue, p.maxValue, f, p.Colors...) + fgColor := p.TextColor + if p.EnableComplementaryColor { + fgColor = complementaryColors[color] + } + buffer.WriteString(fgColor.Sprint(color.Sprintf(ct))) + } + if j < xAmount { + if !p.Boxed && p.HasHeader && j == xAmount-1 { + continue + } + if p.Grid { + buffer.WriteString(p.SeparatorStyle.Sprint(p.VerticalSeparator)) + } + } + if p.Boxed && !p.HasHeader && j == xAmount { + buffer.WriteString(p.SeparatorStyle.Sprint(p.VerticalSeparator)) + } + } + + if i < yAmount { + if p.HasHeader && i == yAmount-1 { + continue + } + buffer.WriteString("\n") + if p.Boxed { + buffer.WriteString(p.SeparatorStyle.Sprint(p.LSeparator)) + } + if p.Grid { + for i := 0; i < xAmount+1; i++ { + buffer.WriteString(strings.Repeat(p.SeparatorStyle.Sprint(p.HorizontalSeparator), colWidth)) + if i < xAmount { + buffer.WriteString(p.SeparatorStyle.Sprint(p.TCrossSeparator)) + } + } + } + if p.Boxed { + buffer.WriteString(p.SeparatorStyle.Sprint(p.LReverseSeparator)) + } + if p.Grid { + buffer.WriteString("\n") + } + } + } +} + +func (p HeatmapPrinter) generateLegend(buffer *bytes.Buffer, legend string, legendColWidth int) { + buffer.WriteString(p.AxisStyle.Sprint(legend)) + if p.Grid { + buffer.WriteString(p.SeparatorStyle.Sprintf("%s", p.VerticalSeparator)) + } else { + buffer.WriteString(" ") + } + if p.EnableRGB { + p.generateRGBLegend(buffer, legendColWidth) + } else { + p.generateColorLegend(buffer, legendColWidth) + } +} + +func (p HeatmapPrinter) generateColorLegend(buffer *bytes.Buffer, legendColWidth int) { + for i, color := range p.Colors { + // the first color is the min value and the last color is the max value + var f float32 + if i == 0 { + f = p.minValue + } else if i == len(p.Colors)-1 { + f = p.maxValue + } else { + f = p.minValue + (p.maxValue-p.minValue)*float32(i)/float32(len(p.Colors)-1) + } + fgColor := p.TextColor + if p.EnableComplementaryColor { + fgColor = complementaryColors[color] + } + buffer.WriteString(fgColor.Sprint(color.Sprint(centerAndShorten(f, legendColWidth, p.LegendOnlyColoredCells)))) + if p.Grid && i < len(p.Colors)-1 && !p.LegendOnlyColoredCells { + buffer.WriteString(p.SeparatorStyle.Sprintf("%s", p.VerticalSeparator)) + } + } +} + +func (p HeatmapPrinter) generateRGBLegend(buffer *bytes.Buffer, legendColWidth int) { + p.rgbLegendValue = 10 + steps := len(p.RGBRange) + if steps < p.rgbLegendValue { + steps = p.rgbLegendValue + } + if p.LegendOnlyColoredCells { + steps *= 3 + } + for i := 0; i < steps; i++ { + // the first color is the min value and the last color is the max value + var f float32 + if i == 0 { + f = p.minValue + } else if i == steps-1 { + f = p.maxValue + } else { + f = p.minValue + (p.maxValue-p.minValue)*float32(i)/float32(steps-1) + } + rgb := p.RGBRange[0].Fade(p.minValue, p.maxValue, f, p.RGBRange[1:]...) + rgbStyle := NewRGBStyle(p.TextRGB, rgb) + if p.EnableComplementaryColor { + complimentary := NewRGB(internal.Complementary(rgb.R, rgb.G, rgb.B)) + rgbStyle = NewRGBStyle(complimentary, rgb) + } + if p.LegendOnlyColoredCells { + buffer.WriteString(rgbStyle.Sprint(centerAndShorten(f, 1, p.LegendOnlyColoredCells))) + } else { + buffer.WriteString(rgbStyle.Sprint(centerAndShorten(f, legendColWidth, p.LegendOnlyColoredCells))) + } + if p.Grid && i < steps-1 && !p.LegendOnlyColoredCells { + buffer.WriteString(p.SeparatorStyle.Sprintf("%s", p.VerticalSeparator)) + } + } +} + +func (p HeatmapPrinter) boxLegend(buffer *bytes.Buffer, legend string, legendColWidth int) { + buffer.WriteString(p.SeparatorStyle.Sprint(p.BottomRightCornerSeparator)) + + p.generateSeparatorRow(buffer, legend, legendColWidth, true) + + buffer.WriteString(p.SeparatorStyle.Sprint(p.BottomLeftCornerSeparator)) + buffer.WriteString("\n") + buffer.WriteString(p.SeparatorStyle.Sprintf("%s", p.VerticalSeparator)) + + p.generateLegend(buffer, legend, legendColWidth) + + buffer.WriteString(p.SeparatorStyle.Sprintf("%s", p.VerticalSeparator)) + buffer.WriteString("\n") + + buffer.WriteString(p.SeparatorStyle.Sprint(p.TopRightCornerSeparator)) + + p.generateSeparatorRow(buffer, legend, legendColWidth, false) + + buffer.WriteString(p.SeparatorStyle.Sprint(p.TopLeftCornerSeparator)) +} + +func (p HeatmapPrinter) generateSeparatorRow(buffer *bytes.Buffer, legend string, legendColWidth int, top bool) { + p.rgbLegendValue = 10 + steps := len(p.RGBRange) + if steps < p.rgbLegendValue { + steps = p.rgbLegendValue + } + if p.LegendOnlyColoredCells { + steps *= 3 + } + + var xValue int + if p.EnableRGB { + xValue = len(p.RGBRange) + if xValue < p.rgbLegendValue { + xValue = p.rgbLegendValue + } + } else { + xValue = len(p.Colors) + } + + for i := 0; i < xValue+1; i++ { + if i == 0 { + firstLength := len(legend) + buffer.WriteString(strings.Repeat(p.SeparatorStyle.Sprint(p.HorizontalSeparator), firstLength)) + } else { + if p.LegendOnlyColoredCells { + if p.EnableRGB { + buffer.WriteString(strings.Repeat(p.SeparatorStyle.Sprint(p.HorizontalSeparator), steps/(xValue))) + } else { + buffer.WriteString(strings.Repeat(p.SeparatorStyle.Sprint(p.HorizontalSeparator), legendColWidth)) + } + } else { + buffer.WriteString(strings.Repeat(p.SeparatorStyle.Sprint(p.HorizontalSeparator), legendColWidth)) + } + } + if i < xValue && !p.LegendOnlyColoredCells || i == 0 { + if top { + buffer.WriteString(p.SeparatorStyle.Sprint(p.TSeparator)) + } else { + buffer.WriteString(p.SeparatorStyle.Sprint(p.TReverseSeparator)) + } + } + } +} + +func centerAndShorten(f float32, lineLength int, onlyColor bool) string { + value := "" + if !onlyColor { + value = Sprintf("%.2v", f) + } + if len(value) > lineLength { + value = value[:lineLength] + if strings.HasSuffix(value, ".") { + value = Sprintf("%.1v", f) + lineLength = len(value) + } + } + ct := internal.CenterText(value, lineLength) + if len(ct) < lineLength { + if len(Sprintf("%v", f)) == 1 { + ct += strings.Repeat(" ", lineLength-len(ct)) + } else { + ct = strings.Repeat(" ", lineLength-len(ct)) + ct + } + } + + return ct +} + +func getColor(min float32, max float32, current float32, colors ...Color) Color { + // split the range into equal parts + // and assign a color to each part + // the last color is assigned to the max value + // and the first color to the min value + // the rest of the colors are assigned to the + // middle values + step := (max - min) / float32(len(colors)) + for i := range colors { + if current >= min+float32(i)*step && current < min+float32(i+1)*step { + return colors[i] + } + } + return colors[len(colors)-1] +} + +// Render prints the HeatmapPrinter to the terminal. +func (p HeatmapPrinter) Render() error { + s, err := p.Srender() + if err != nil { + return err + } + Fprintln(p.Writer, s) + + return nil +} + +func (p HeatmapPrinter) errCheck() error { + if p.HasHeader { + if p.Axis.XAxis == nil { + return errors.New("x axis is nil") + } + if p.Axis.YAxis == nil { + return errors.New("y axis is nil") + } + + if len(p.Axis.XAxis) == 0 { + return errors.New("x axis is empty") + } + if len(p.Axis.YAxis) == 0 { + return errors.New("y axis is empty") + } + + for i := 1; i < len(p.Data); i++ { + if len(p.Data[i]) != len(p.Axis.XAxis) { + return errors.New("x axis length does not match data") + } + } + if len(p.Axis.YAxis) != len(p.Data) { + return errors.New("y axis length does not match data") + } + } + + if p.Data == nil { + return errors.New("data is nil") + } + + if len(p.Data) == 0 { + return errors.New("data is empty") + } + + // check if p.Data[n] has the same length + for i := 1; i < len(p.Data); i++ { + if len(p.Data[i]) != len(p.Data[0]) { + return errors.New("data is not rectangular") + } + } + + return nil +} + +// return min and max value of a slice +func minMaxFloat32(s [][]float32) (float32, float32) { + var min, max float32 + min = math.MaxFloat32 + max = -math.MaxFloat32 + + for _, r := range s { + for _, c := range r { + if c < min { + min = c + } + if c > max { + max = c + } + } + } + return min, max +} diff --git a/vendor/github.com/pterm/pterm/interactive_confirm_printer.go b/vendor/github.com/pterm/pterm/interactive_confirm_printer.go index 6760ce69..a94cc5dc 100644 --- a/vendor/github.com/pterm/pterm/interactive_confirm_printer.go +++ b/vendor/github.com/pterm/pterm/interactive_confirm_printer.go @@ -7,35 +7,37 @@ import ( "atomicgo.dev/cursor" "atomicgo.dev/keyboard" "atomicgo.dev/keyboard/keys" + "github.com/pterm/pterm/internal" ) -var ( - // DefaultInteractiveConfirm is the default InteractiveConfirm printer. - // Pressing "y" will return true, "n" will return false. - // Pressing enter without typing "y" or "n" will return the configured default value (by default set to "no"). - DefaultInteractiveConfirm = InteractiveConfirmPrinter{ - DefaultValue: false, - DefaultText: "Please confirm", - TextStyle: &ThemeDefault.PrimaryStyle, - ConfirmText: "Yes", - ConfirmStyle: &ThemeDefault.SuccessMessageStyle, - RejectText: "No", - RejectStyle: &ThemeDefault.ErrorMessageStyle, - SuffixStyle: &ThemeDefault.SecondaryStyle, - } -) +// DefaultInteractiveConfirm is the default InteractiveConfirm printer. +// Pressing "y" will return true, "n" will return false. +// Pressing enter without typing "y" or "n" will return the configured default value (by default set to "no"). +var DefaultInteractiveConfirm = InteractiveConfirmPrinter{ + DefaultValue: false, + DefaultText: "Please confirm", + TextStyle: &ThemeDefault.PrimaryStyle, + ConfirmText: "Yes", + ConfirmStyle: &ThemeDefault.SuccessMessageStyle, + RejectText: "No", + RejectStyle: &ThemeDefault.ErrorMessageStyle, + SuffixStyle: &ThemeDefault.SecondaryStyle, + Delimiter: ": ", +} // InteractiveConfirmPrinter is a printer for interactive confirm prompts. type InteractiveConfirmPrinter struct { - DefaultValue bool - DefaultText string - TextStyle *Style - ConfirmText string - ConfirmStyle *Style - RejectText string - RejectStyle *Style - SuffixStyle *Style + DefaultValue bool + DefaultText string + Delimiter string + TextStyle *Style + ConfirmText string + ConfirmStyle *Style + RejectText string + RejectStyle *Style + SuffixStyle *Style + OnInterruptFunc func() } // WithDefaultText sets the default text. @@ -86,6 +88,18 @@ func (p InteractiveConfirmPrinter) WithSuffixStyle(style *Style) *InteractiveCon return &p } +// OnInterrupt sets the function to execute on exit of the input reader +func (p InteractiveConfirmPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveConfirmPrinter { + p.OnInterruptFunc = exitFunc + return &p +} + +// WithDelimiter sets the delimiter between the message and the input. +func (p InteractiveConfirmPrinter) WithDelimiter(delimiter string) *InteractiveConfirmPrinter { + p.Delimiter = delimiter + return &p +} + // Show shows the confirm prompt. // // Example: @@ -95,7 +109,7 @@ func (p InteractiveConfirmPrinter) WithSuffixStyle(style *Style) *InteractiveCon func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) { // should be the first defer statement to make sure it is executed last // and all the needed cleanup can be done before - cancel, exit := internal.NewCancelationSignal() + cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc) defer exit() var result bool @@ -104,7 +118,7 @@ func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) { text = []string{p.DefaultText} } - p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ") + p.TextStyle.Print(text[0] + " " + p.getSuffix() + p.Delimiter) y, n := p.getShortHandles() var interrupted bool diff --git a/vendor/github.com/pterm/pterm/interactive_continue_printer.go b/vendor/github.com/pterm/pterm/interactive_continue_printer.go index d0f91f13..4049cde9 100644 --- a/vendor/github.com/pterm/pterm/interactive_continue_printer.go +++ b/vendor/github.com/pterm/pterm/interactive_continue_printer.go @@ -2,7 +2,6 @@ package pterm import ( "fmt" - "os" "strings" "atomicgo.dev/cursor" @@ -14,24 +13,24 @@ import ( "github.com/pterm/pterm/internal" ) -var ( - // DefaultInteractiveContinue is the default InteractiveContinue printer. - // Pressing "y" will return yes, "n" will return no, "a" returns all and "s" returns stop. - // Pressing enter without typing any letter will return the configured default value (by default set to "yes", the fisrt option). - DefaultInteractiveContinue = InteractiveContinuePrinter{ - DefaultValueIndex: 0, - DefaultText: "Do you want to continue", - TextStyle: &ThemeDefault.PrimaryStyle, - Options: []string{"yes", "no", "all", "cancel"}, - OptionsStyle: &ThemeDefault.SuccessMessageStyle, - SuffixStyle: &ThemeDefault.SecondaryStyle, - } -) +// DefaultInteractiveContinue is the default InteractiveContinue printer. +// Pressing "y" will return yes, "n" will return no, "a" returns all and "s" returns stop. +// Pressing enter without typing any letter will return the configured default value (by default set to "yes", the fisrt option). +var DefaultInteractiveContinue = InteractiveContinuePrinter{ + DefaultValueIndex: 0, + DefaultText: "Do you want to continue", + TextStyle: &ThemeDefault.PrimaryStyle, + Options: []string{"yes", "no", "all", "cancel"}, + OptionsStyle: &ThemeDefault.SuccessMessageStyle, + SuffixStyle: &ThemeDefault.SecondaryStyle, + Delimiter: ": ", +} // InteractiveContinuePrinter is a printer for interactive continue prompts. type InteractiveContinuePrinter struct { DefaultValueIndex int DefaultText string + Delimiter string TextStyle *Style Options []string OptionsStyle *Style @@ -108,6 +107,12 @@ func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveCo return &p } +// WithDelimiter sets the delimiter between the message and the input. +func (p InteractiveContinuePrinter) WithDelimiter(delimiter string) *InteractiveContinuePrinter { + p.Delimiter = delimiter + return &p +} + // Show shows the continue prompt. // // Example: @@ -121,7 +126,7 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { text = []string{p.DefaultText} } - p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ") + p.TextStyle.Print(text[0] + " " + p.getSuffix() + p.Delimiter) err := keyboard.Listen(func(keyInfo keys.Key) (stop bool, err error) { if err != nil { @@ -149,7 +154,7 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { result = p.Options[p.DefaultValueIndex] return true, nil case keys.CtrlC: - os.Exit(1) + internal.Exit(1) return true, nil } return false, nil diff --git a/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go b/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go index c80f9355..754ba98a 100644 --- a/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go +++ b/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go @@ -32,16 +32,17 @@ var ( // InteractiveMultiselectPrinter is a printer for interactive multiselect menus. type InteractiveMultiselectPrinter struct { - DefaultText string - TextStyle *Style - Options []string - OptionStyle *Style - DefaultOptions []string - MaxHeight int - Selector string - SelectorStyle *Style - Filter bool - Checkmark *Checkmark + DefaultText string + TextStyle *Style + Options []string + OptionStyle *Style + DefaultOptions []string + MaxHeight int + Selector string + SelectorStyle *Style + Filter bool + Checkmark *Checkmark + OnInterruptFunc func() selectedOption int selectedOptions []int @@ -52,7 +53,10 @@ type InteractiveMultiselectPrinter struct { displayedOptionsStart int displayedOptionsEnd int - KeySelect keys.KeyCode + // KeySelect is the select key. It cannot be keys.Space when Filter is enabled. + KeySelect keys.KeyCode + + // KeyConfirm is the confirm key. It cannot be keys.Space when Filter is enabled. KeyConfirm keys.KeyCode } @@ -81,18 +85,20 @@ func (p InteractiveMultiselectPrinter) WithMaxHeight(maxHeight int) *Interactive } // WithFilter sets the Filter option -func (p InteractiveMultiselectPrinter) WithFilter(filter bool) *InteractiveMultiselectPrinter { - p.Filter = filter +func (p InteractiveMultiselectPrinter) WithFilter(b ...bool) *InteractiveMultiselectPrinter { + p.Filter = internal.WithBoolean(b) return &p } // WithKeySelect sets the confirm key +// It cannot be keys.Space when Filter is enabled. func (p InteractiveMultiselectPrinter) WithKeySelect(keySelect keys.KeyCode) *InteractiveMultiselectPrinter { p.KeySelect = keySelect return &p } // WithKeyConfirm sets the confirm key +// It cannot be keys.Space when Filter is enabled. func (p InteractiveMultiselectPrinter) WithKeyConfirm(keyConfirm keys.KeyCode) *InteractiveMultiselectPrinter { p.KeyConfirm = keyConfirm return &p @@ -104,11 +110,17 @@ func (p InteractiveMultiselectPrinter) WithCheckmark(checkmark *Checkmark) *Inte return &p } +// OnInterrupt sets the function to execute on exit of the input reader +func (p InteractiveMultiselectPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveMultiselectPrinter { + p.OnInterruptFunc = exitFunc + return &p +} + // Show shows the interactive multiselect menu and returns the selected entry. func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) { // should be the first defer statement to make sure it is executed last // and all the needed cleanup can be done before - cancel, exit := internal.NewCancelationSignal() + cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc) defer exit() if len(text) == 0 || Sprint(text[0]) == "" { @@ -228,7 +240,7 @@ func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) { p.selectedOptions = append(p.selectedOptions, i) } area.Update(p.renderSelectMenu()) - case keys.Up: + case keys.Up, keys.CtrlP: if len(p.fuzzySearchMatches) == 0 { return false, nil } @@ -251,7 +263,7 @@ func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) { } area.Update(p.renderSelectMenu()) - case keys.Down: + case keys.Down, keys.CtrlN: if len(p.fuzzySearchMatches) == 0 { return false, nil } diff --git a/vendor/github.com/pterm/pterm/interactive_select_printer.go b/vendor/github.com/pterm/pterm/interactive_select_printer.go index 0af1a0d6..0c4bc995 100644 --- a/vendor/github.com/pterm/pterm/interactive_select_printer.go +++ b/vendor/github.com/pterm/pterm/interactive_select_printer.go @@ -23,19 +23,22 @@ var ( MaxHeight: 5, Selector: ">", SelectorStyle: &ThemeDefault.SecondaryStyle, + Filter: true, } ) // InteractiveSelectPrinter is a printer for interactive select menus. type InteractiveSelectPrinter struct { - TextStyle *Style - DefaultText string - Options []string - OptionStyle *Style - DefaultOption string - MaxHeight int - Selector string - SelectorStyle *Style + TextStyle *Style + DefaultText string + Options []string + OptionStyle *Style + DefaultOption string + MaxHeight int + Selector string + SelectorStyle *Style + OnInterruptFunc func() + Filter bool selectedOption int result string @@ -71,11 +74,23 @@ func (p InteractiveSelectPrinter) WithMaxHeight(maxHeight int) *InteractiveSelec return &p } +// OnInterrupt sets the function to execute on exit of the input reader +func (p InteractiveSelectPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveSelectPrinter { + p.OnInterruptFunc = exitFunc + return &p +} + +// WithFilter sets the Filter option +func (p InteractiveSelectPrinter) WithFilter(b ...bool) *InteractiveSelectPrinter { + p.Filter = internal.WithBoolean(b) + return &p +} + // Show shows the interactive select menu and returns the selected entry. func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) { // should be the first defer statement to make sure it is executed last // and all the needed cleanup can be done before - cancel, exit := internal.NewCancelationSignal() + cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc) defer exit() if len(text) == 0 || Sprint(text[0]) == "" { @@ -142,14 +157,16 @@ func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) { switch key { case keys.RuneKey: - // Fuzzy search for options - // append to fuzzy search string - p.fuzzySearchString += keyInfo.String() - p.selectedOption = 0 - p.displayedOptionsStart = 0 - p.displayedOptionsEnd = maxHeight - p.displayedOptions = append([]string{}, p.fuzzySearchMatches[:maxHeight]...) - area.Update(p.renderSelectMenu()) + if p.Filter { + // Fuzzy search for options + // append to fuzzy search string + p.fuzzySearchString += keyInfo.String() + p.selectedOption = 0 + p.displayedOptionsStart = 0 + p.displayedOptionsEnd = maxHeight + p.displayedOptions = append([]string{}, p.fuzzySearchMatches[:maxHeight]...) + area.Update(p.renderSelectMenu()) + } case keys.Space: p.fuzzySearchString += " " p.selectedOption = 0 @@ -179,7 +196,7 @@ func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) { p.displayedOptions = append([]string{}, p.fuzzySearchMatches[p.displayedOptionsStart:p.displayedOptionsEnd]...) area.Update(p.renderSelectMenu()) - case keys.Up: + case keys.Up, keys.CtrlP: if len(p.fuzzySearchMatches) == 0 { return false, nil } @@ -202,7 +219,7 @@ func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) { } area.Update(p.renderSelectMenu()) - case keys.Down: + case keys.Down, keys.CtrlN: if len(p.fuzzySearchMatches) == 0 { return false, nil } @@ -245,7 +262,11 @@ func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) { func (p *InteractiveSelectPrinter) renderSelectMenu() string { var content string - content += Sprintf("%s %s: %s\n", p.text, p.SelectorStyle.Sprint("[type to search]"), p.fuzzySearchString) + if p.Filter { + content += Sprintf("%s %s: %s\n", p.text, p.SelectorStyle.Sprint("[type to search]"), p.fuzzySearchString) + } else { + content += Sprintf("%s:\n", p.text) + } // find options that match fuzzy search string rankedResults := fuzzy.RankFindFold(p.fuzzySearchString, p.Options) diff --git a/vendor/github.com/pterm/pterm/interactive_textinput_printer.go b/vendor/github.com/pterm/pterm/interactive_textinput_printer.go index e7174555..88535c51 100644 --- a/vendor/github.com/pterm/pterm/interactive_textinput_printer.go +++ b/vendor/github.com/pterm/pterm/interactive_textinput_printer.go @@ -10,19 +10,23 @@ import ( "github.com/pterm/pterm/internal" ) -var ( - // DefaultInteractiveTextInput is the default InteractiveTextInput printer. - DefaultInteractiveTextInput = InteractiveTextInputPrinter{ - DefaultText: "Input text", - TextStyle: &ThemeDefault.PrimaryStyle, - } -) +// DefaultInteractiveTextInput is the default InteractiveTextInput printer. +var DefaultInteractiveTextInput = InteractiveTextInputPrinter{ + DefaultText: "Input text", + Delimiter: ": ", + TextStyle: &ThemeDefault.PrimaryStyle, + Mask: "", +} // InteractiveTextInputPrinter is a printer for interactive select menus. type InteractiveTextInputPrinter struct { - TextStyle *Style - DefaultText string - MultiLine bool + TextStyle *Style + DefaultText string + DefaultValue string + Delimiter string + MultiLine bool + Mask string + OnInterruptFunc func() input []string cursorXPos int @@ -36,6 +40,12 @@ func (p InteractiveTextInputPrinter) WithDefaultText(text string) *InteractiveTe return &p } +// WithDefaultValue sets the default value. +func (p InteractiveTextInputPrinter) WithDefaultValue(value string) *InteractiveTextInputPrinter { + p.DefaultValue = value + return &p +} + // WithTextStyle sets the text style. func (p InteractiveTextInputPrinter) WithTextStyle(style *Style) *InteractiveTextInputPrinter { p.TextStyle = style @@ -48,11 +58,29 @@ func (p InteractiveTextInputPrinter) WithMultiLine(multiLine ...bool) *Interacti return &p } +// WithMask sets the mask. +func (p InteractiveTextInputPrinter) WithMask(mask string) *InteractiveTextInputPrinter { + p.Mask = mask + return &p +} + +// WithOnInterruptFunc sets the function to execute on exit of the input reader +func (p InteractiveTextInputPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveTextInputPrinter { + p.OnInterruptFunc = exitFunc + return &p +} + +// WithDelimiter sets the delimiter between the message and the input. +func (p InteractiveTextInputPrinter) WithDelimiter(delimiter string) *InteractiveTextInputPrinter { + p.Delimiter = delimiter + return &p +} + // Show shows the interactive select menu and returns the selected entry. func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) { // should be the first defer statement to make sure it is executed last // and all the needed cleanup can be done before - cancel, exit := internal.NewCancelationSignal() + cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc) defer exit() var areaText string @@ -62,24 +90,26 @@ func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) { } if p.MultiLine { - areaText = p.TextStyle.Sprintfln("%s %s :", text[0], ThemeDefault.SecondaryStyle.Sprint("[Press tab to submit]")) + areaText = p.TextStyle.Sprintfln("%s %s %s", text[0], ThemeDefault.SecondaryStyle.Sprint("[Press tab to submit]"), p.Delimiter) } else { - areaText = p.TextStyle.Sprintf("%s: ", text[0]) + areaText = p.TextStyle.Sprintf("%s%s", text[0], p.Delimiter) } + p.text = areaText - area, err := DefaultArea.Start(areaText) - defer area.Stop() - if err != nil { - return "", err - } + area := cursor.NewArea() + area.Update(areaText) + area.StartOfLine() - cursor.Up(1) - cursor.StartOfLine() if !p.MultiLine { cursor.Right(len(RemoveColorFromString(areaText))) } - err = keyboard.Listen(func(key keys.Key) (stop bool, err error) { + if p.DefaultValue != "" { + p.input = append(p.input, p.DefaultValue) + p.updateArea(&area) + } + + err := keyboard.Listen(func(key keys.Key) (stop bool, err error) { if !p.MultiLine { p.cursorYPos = 0 } @@ -90,6 +120,7 @@ func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) { switch key.Code { case keys.Tab: if p.MultiLine { + area.Bottom() return true, nil } case keys.Enter: @@ -104,7 +135,6 @@ func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) { p.input = append(p.input, appendAfterY...) p.cursorYPos++ p.cursorXPos = -internal.GetStringMaxWidth(p.input[p.cursorYPos]) - cursor.Down(1) cursor.StartOfLine() } else { return true, nil @@ -173,7 +203,7 @@ func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) { } } - p.updateArea(area) + p.updateArea(&area) return false, nil }) @@ -195,11 +225,12 @@ func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) { return strings.ReplaceAll(areaText, p.text, ""), nil } -func (p InteractiveTextInputPrinter) updateArea(area *AreaPrinter) string { +func (p InteractiveTextInputPrinter) updateArea(area *cursor.Area) string { if !p.MultiLine { p.cursorYPos = 0 } areaText := p.text + for i, s := range p.input { if i < len(p.input)-1 { areaText += s + "\n" @@ -207,14 +238,19 @@ func (p InteractiveTextInputPrinter) updateArea(area *AreaPrinter) string { areaText += s } } + + if p.Mask != "" { + areaText = p.text + strings.Repeat(p.Mask, internal.GetStringMaxWidth(areaText)-internal.GetStringMaxWidth(p.text)) + } + if p.cursorXPos+internal.GetStringMaxWidth(p.input[p.cursorYPos]) < 1 { p.cursorXPos = -internal.GetStringMaxWidth(p.input[p.cursorYPos]) } - cursor.StartOfLine() area.Update(areaText) - cursor.Up(len(p.input) - p.cursorYPos) - cursor.StartOfLine() + area.Top() + area.Down(p.cursorYPos + 1) + area.StartOfLine() if p.MultiLine { cursor.Right(internal.GetStringMaxWidth(p.input[p.cursorYPos]) + p.cursorXPos) } else { diff --git a/vendor/github.com/pterm/pterm/interface_live_printer.go b/vendor/github.com/pterm/pterm/interface_live_printer.go index b8d3dbe3..69dce345 100644 --- a/vendor/github.com/pterm/pterm/interface_live_printer.go +++ b/vendor/github.com/pterm/pterm/interface_live_printer.go @@ -1,5 +1,7 @@ package pterm +import "io" + // LivePrinter is a printer which can update it's output live. type LivePrinter interface { // GenericStart runs Start, but returns a LivePrinter. @@ -11,4 +13,6 @@ type LivePrinter interface { // This is used for the interface LivePrinter. // You most likely want to use Stop instead of this in your program. GenericStop() (*LivePrinter, error) + + SetWriter(writer io.Writer) } diff --git a/vendor/github.com/pterm/pterm/internal/cancelation_signal.go b/vendor/github.com/pterm/pterm/internal/cancelation_signal.go index d6f79b0a..33f17cc1 100644 --- a/vendor/github.com/pterm/pterm/internal/cancelation_signal.go +++ b/vendor/github.com/pterm/pterm/internal/cancelation_signal.go @@ -1,19 +1,20 @@ package internal -import ( - "os" -) - // NewCancelationSignal for keeping track of a cancelation -func NewCancelationSignal() (func(), func()) { +func NewCancelationSignal(interruptFunc func()) (func(), func()) { canceled := false cancel := func() { canceled = true } + exit := func() { if canceled { - os.Exit(1) + if interruptFunc != nil { + interruptFunc() + } else { + Exit(1) + } } } diff --git a/vendor/github.com/pterm/pterm/internal/exit.go b/vendor/github.com/pterm/pterm/internal/exit.go new file mode 100644 index 00000000..f07a0045 --- /dev/null +++ b/vendor/github.com/pterm/pterm/internal/exit.go @@ -0,0 +1,14 @@ +package internal + +import "os" + +// ExitFuncType is the type of function used to exit the program. +type ExitFuncType func(int) + +// DefaultExitFunc is the default function used to exit the program. +var DefaultExitFunc ExitFuncType = os.Exit + +// Exit calls the current exit function. +func Exit(code int) { + DefaultExitFunc(code) +} diff --git a/vendor/github.com/pterm/pterm/internal/rgb_complementary.go b/vendor/github.com/pterm/pterm/internal/rgb_complementary.go new file mode 100644 index 00000000..71520b33 --- /dev/null +++ b/vendor/github.com/pterm/pterm/internal/rgb_complementary.go @@ -0,0 +1,5 @@ +package internal + +func Complementary(r, g, b uint8) (uint8, uint8, uint8) { + return 255 - r, 255 - g, 255 - b +} diff --git a/vendor/github.com/pterm/pterm/internal/title_in_line.go b/vendor/github.com/pterm/pterm/internal/title_in_line.go index 3aae546b..68e28ec6 100644 --- a/vendor/github.com/pterm/pterm/internal/title_in_line.go +++ b/vendor/github.com/pterm/pterm/internal/title_in_line.go @@ -2,17 +2,15 @@ package internal import ( "strings" - - "github.com/gookit/color" ) // AddTitleToLine adds a title to a site of a line ex: "─ This is the title ──────" func AddTitleToLine(title, line string, length int, left bool) string { var ret string if left { - ret += line + " " + title + " " + line + strings.Repeat(line, length-(4+len(color.ClearCode(title)))) + ret += line + " " + title + " " + line + strings.Repeat(line, length-(4+GetStringMaxWidth(title))) } else { - ret += strings.Repeat(line, length-(4+len(color.ClearCode(title)))) + line + " " + title + " " + line + ret += strings.Repeat(line, length-(4+GetStringMaxWidth(title))) + line + " " + title + " " + line } return ret @@ -21,7 +19,7 @@ func AddTitleToLine(title, line string, length int, left bool) string { // AddTitleToLineCenter adds a title to the center of a line ex: "─ This is the title ──────" func AddTitleToLineCenter(title, line string, length int) string { var ret string - repeatString := length - (4 + len(color.ClearCode(title))) + repeatString := length - (4 + GetStringMaxWidth(title)) unevenRepeatString := repeatString % 2 ret += strings.Repeat(line, repeatString/2) + line + " " + title + " " + line + strings.Repeat(line, repeatString/2+unevenRepeatString) diff --git a/vendor/github.com/pterm/pterm/logger.go b/vendor/github.com/pterm/pterm/logger.go index 4fed9283..26eea5b0 100644 --- a/vendor/github.com/pterm/pterm/logger.go +++ b/vendor/github.com/pterm/pterm/logger.go @@ -200,6 +200,9 @@ func (l Logger) AppendKeyStyle(key string, style Style) *Logger { // CanPrint checks if the logger can print a specific log level. func (l Logger) CanPrint(level LogLevel) bool { + if l.Level == LogLevelDisabled { + return false + } return l.Level <= level } @@ -261,7 +264,7 @@ func (l Logger) combineArgs(args ...[]LoggerArgument) []LoggerArgument { } func (l Logger) print(level LogLevel, msg string, args []LoggerArgument) { - if l.Level > level { + if !l.CanPrint(level) { return } diff --git a/vendor/github.com/pterm/pterm/multi_live_printer.go b/vendor/github.com/pterm/pterm/multi_live_printer.go new file mode 100644 index 00000000..b9e0d0e8 --- /dev/null +++ b/vendor/github.com/pterm/pterm/multi_live_printer.go @@ -0,0 +1,124 @@ +package pterm + +import ( + "atomicgo.dev/schedule" + "bytes" + "io" + "os" + "strings" + "time" +) + +var DefaultMultiPrinter = MultiPrinter{ + printers: []LivePrinter{}, + Writer: os.Stdout, + UpdateDelay: time.Millisecond * 200, + + buffers: []*bytes.Buffer{}, + area: DefaultArea, +} + +type MultiPrinter struct { + IsActive bool + Writer io.Writer + UpdateDelay time.Duration + + printers []LivePrinter + buffers []*bytes.Buffer + area AreaPrinter +} + +// SetWriter sets the writer for the AreaPrinter. +func (p *MultiPrinter) SetWriter(writer io.Writer) { + p.Writer = writer +} + +// WithWriter returns a fork of the MultiPrinter with a new writer. +func (p MultiPrinter) WithWriter(writer io.Writer) *MultiPrinter { + p.Writer = writer + return &p +} + +// WithUpdateDelay returns a fork of the MultiPrinter with a new update delay. +func (p MultiPrinter) WithUpdateDelay(delay time.Duration) *MultiPrinter { + p.UpdateDelay = delay + return &p +} + +func (p *MultiPrinter) NewWriter() io.Writer { + buf := bytes.NewBufferString("") + p.buffers = append(p.buffers, buf) + return buf +} + +// getString returns all buffers appended and separated by a newline. +func (p *MultiPrinter) getString() string { + var buffer bytes.Buffer + for _, b := range p.buffers { + s := b.String() + s = strings.Trim(s, "\n") + + parts := strings.Split(s, "\r") // only get the last override + s = parts[len(parts)-1] + + // check if s is empty, if so get one part before, repeat until not empty + for s == "" { + parts = parts[:len(parts)-1] + s = parts[len(parts)-1] + } + + s = strings.Trim(s, "\n\r") + buffer.WriteString(s) + buffer.WriteString("\n") + } + return buffer.String() +} + +func (p *MultiPrinter) Start() (*MultiPrinter, error) { + p.IsActive = true + for _, printer := range p.printers { + printer.GenericStart() + } + + schedule.Every(p.UpdateDelay, func() bool { + if !p.IsActive { + return false + } + + p.area.Update(p.getString()) + + return true + }) + + return p, nil +} + +func (p *MultiPrinter) Stop() (*MultiPrinter, error) { + p.IsActive = false + for _, printer := range p.printers { + printer.GenericStop() + } + time.Sleep(time.Millisecond * 20) + p.area.Update(p.getString()) + p.area.Stop() + + return p, nil +} + +// GenericStart runs Start, but returns a LivePrinter. +// This is used for the interface LivePrinter. +// You most likely want to use Start instead of this in your program. +func (p MultiPrinter) GenericStart() (*LivePrinter, error) { + p2, _ := p.Start() + lp := LivePrinter(p2) + return &lp, nil +} + +// GenericStop runs Stop, but returns a LivePrinter. +// This is used for the interface LivePrinter. +// You most likely want to use Stop instead of this in your program. +func (p MultiPrinter) GenericStop() (*LivePrinter, error) { + p2, _ := p.Stop() + lp := LivePrinter(p2) + return &lp, nil +} diff --git a/vendor/github.com/pterm/pterm/panel_printer.go b/vendor/github.com/pterm/pterm/panel_printer.go index 709f7c80..090498e7 100644 --- a/vendor/github.com/pterm/pterm/panel_printer.go +++ b/vendor/github.com/pterm/pterm/panel_printer.go @@ -183,7 +183,7 @@ func (p PanelPrinter) Srender() (string, error) { // Render prints the Template to the terminal. func (p PanelPrinter) Render() error { s, _ := p.Srender() - Println(s) + Fprintln(p.Writer, s) return nil } diff --git a/vendor/github.com/pterm/pterm/progressbar_printer.go b/vendor/github.com/pterm/pterm/progressbar_printer.go index 2fb3f910..1cf4a0c2 100644 --- a/vendor/github.com/pterm/pterm/progressbar_printer.go +++ b/vendor/github.com/pterm/pterm/progressbar_printer.go @@ -1,8 +1,11 @@ package pterm import ( + "atomicgo.dev/cursor" + "atomicgo.dev/schedule" + "fmt" "io" - "strconv" + "math" "strings" "time" @@ -15,23 +18,21 @@ import ( // Generally, there should only be one active ProgressbarPrinter at a time. var ActiveProgressBarPrinters []*ProgressbarPrinter -var ( - // DefaultProgressbar is the default ProgressbarPrinter. - DefaultProgressbar = ProgressbarPrinter{ - Total: 100, - BarCharacter: "█", - LastCharacter: "█", - ElapsedTimeRoundingFactor: time.Second, - BarStyle: &ThemeDefault.ProgressbarBarStyle, - TitleStyle: &ThemeDefault.ProgressbarTitleStyle, - ShowTitle: true, - ShowCount: true, - ShowPercentage: true, - ShowElapsedTime: true, - BarFiller: " ", - MaxWidth: 80, - } -) +// DefaultProgressbar is the default ProgressbarPrinter. +var DefaultProgressbar = ProgressbarPrinter{ + Total: 100, + BarCharacter: "█", + LastCharacter: "█", + ElapsedTimeRoundingFactor: time.Second, + BarStyle: &ThemeDefault.ProgressbarBarStyle, + TitleStyle: &ThemeDefault.ProgressbarTitleStyle, + ShowTitle: true, + ShowCount: true, + ShowPercentage: true, + ShowElapsedTime: true, + BarFiller: Gray("█"), + MaxWidth: 80, +} // ProgressbarPrinter shows a progress animation in the terminal. type ProgressbarPrinter struct { @@ -55,7 +56,8 @@ type ProgressbarPrinter struct { IsActive bool - startedAt time.Time + startedAt time.Time + rerenderTask *schedule.Task Writer io.Writer } @@ -158,6 +160,11 @@ func (p ProgressbarPrinter) WithWriter(writer io.Writer) *ProgressbarPrinter { return &p } +// SetWriter sets the custom Writer. +func (p *ProgressbarPrinter) SetWriter(writer io.Writer) { + p.Writer = writer +} + // Increment current value by one. func (p *ProgressbarPrinter) Increment() *ProgressbarPrinter { p.Add(1) @@ -173,6 +180,14 @@ func (p *ProgressbarPrinter) UpdateTitle(title string) *ProgressbarPrinter { // This is the update logic, renders the progressbar func (p *ProgressbarPrinter) updateProgress() *ProgressbarPrinter { + Fprinto(p.Writer, p.getString()) + return p +} + +func (p *ProgressbarPrinter) getString() string { + if !p.IsActive { + return "" + } if p.TitleStyle == nil { p.TitleStyle = NewStyle() } @@ -180,7 +195,7 @@ func (p *ProgressbarPrinter) updateProgress() *ProgressbarPrinter { p.BarStyle = NewStyle() } if p.Total == 0 { - return nil + return "" } var before string @@ -195,25 +210,20 @@ func (p *ProgressbarPrinter) updateProgress() *ProgressbarPrinter { width = p.MaxWidth } - currentPercentage := int(internal.PercentageRound(float64(int64(p.Total)), float64(int64(p.Current)))) - - decoratorCount := Gray("[") + LightWhite(p.Current) + Gray("/") + LightWhite(p.Total) + Gray("]") - - decoratorCurrentPercentage := color.RGB(NewRGB(255, 0, 0).Fade(0, float32(p.Total), float32(p.Current), NewRGB(0, 255, 0)).GetValues()). - Sprint(strconv.Itoa(currentPercentage) + "%") - - decoratorTitle := p.TitleStyle.Sprint(p.Title) - if p.ShowTitle { - before += decoratorTitle + " " + before += p.TitleStyle.Sprint(p.Title) + " " } if p.ShowCount { - before += decoratorCount + " " + padding := 1 + int(math.Log10(float64(p.Total))) + before += Gray("[") + LightWhite(fmt.Sprintf("%0*d", padding, p.Current)) + Gray("/") + LightWhite(p.Total) + Gray("]") + " " } after += " " if p.ShowPercentage { + currentPercentage := int(internal.PercentageRound(float64(int64(p.Total)), float64(int64(p.Current)))) + decoratorCurrentPercentage := color.RGB(NewRGB(255, 0, 0).Fade(0, float32(p.Total), float32(p.Current), NewRGB(0, 255, 0)).GetValues()). + Sprintf("%3d%%", currentPercentage) after += decoratorCurrentPercentage + " " } if p.ShowElapsedTime { @@ -228,17 +238,12 @@ func (p *ProgressbarPrinter) updateProgress() *ProgressbarPrinter { barFiller = strings.Repeat(p.BarFiller, barMaxLength-barCurrentLength) } - var bar string + bar := barFiller if barCurrentLength > 0 { - bar = p.BarStyle.Sprint(strings.Repeat(p.BarCharacter, barCurrentLength)+p.LastCharacter) + barFiller - } else { - bar = "" + bar = p.BarStyle.Sprint(strings.Repeat(p.BarCharacter, barCurrentLength)+p.LastCharacter) + bar } - if !RawOutput { - Fprinto(p.Writer, before+bar+after) - } - return p + return before + bar + after } // Add to current value. @@ -251,6 +256,8 @@ func (p *ProgressbarPrinter) Add(count int) *ProgressbarPrinter { p.updateProgress() if p.Current >= p.Total { + p.Total = p.Current + p.updateProgress() p.Stop() } return p @@ -258,6 +265,7 @@ func (p *ProgressbarPrinter) Add(count int) *ProgressbarPrinter { // Start the ProgressbarPrinter. func (p ProgressbarPrinter) Start(title ...interface{}) (*ProgressbarPrinter, error) { + cursor.Hide() if RawOutput && p.ShowTitle { Fprintln(p.Writer, p.Title) } @@ -270,11 +278,23 @@ func (p ProgressbarPrinter) Start(title ...interface{}) (*ProgressbarPrinter, er p.updateProgress() + if p.ShowElapsedTime { + p.rerenderTask = schedule.Every(time.Second, func() bool { + p.updateProgress() + return true + }) + } + return &p, nil } // Stop the ProgressbarPrinter. func (p *ProgressbarPrinter) Stop() (*ProgressbarPrinter, error) { + if p.rerenderTask != nil && p.rerenderTask.IsActive() { + p.rerenderTask.Stop() + } + cursor.Show() + if !p.IsActive { return p, nil } @@ -291,7 +311,7 @@ func (p *ProgressbarPrinter) Stop() (*ProgressbarPrinter, error) { // GenericStart runs Start, but returns a LivePrinter. // This is used for the interface LivePrinter. // You most likely want to use Start instead of this in your program. -func (p ProgressbarPrinter) GenericStart() (*LivePrinter, error) { +func (p *ProgressbarPrinter) GenericStart() (*LivePrinter, error) { p2, _ := p.Start() lp := LivePrinter(p2) return &lp, nil @@ -300,7 +320,7 @@ func (p ProgressbarPrinter) GenericStart() (*LivePrinter, error) { // GenericStop runs Stop, but returns a LivePrinter. // This is used for the interface LivePrinter. // You most likely want to use Stop instead of this in your program. -func (p ProgressbarPrinter) GenericStop() (*LivePrinter, error) { +func (p *ProgressbarPrinter) GenericStop() (*LivePrinter, error) { p2, _ := p.Stop() lp := LivePrinter(p2) return &lp, nil diff --git a/vendor/github.com/pterm/pterm/pterm.go b/vendor/github.com/pterm/pterm/pterm.go index b42a1140..f9d76ca3 100644 --- a/vendor/github.com/pterm/pterm/pterm.go +++ b/vendor/github.com/pterm/pterm/pterm.go @@ -63,7 +63,7 @@ func DisableStyling() { DisableColor() } -// RecalculateTerminalSize updates already initialized terminal dimensions. Has to be called after a termina resize to guarantee proper rendering. Applies only to new instances. +// RecalculateTerminalSize updates already initialized terminal dimensions. Has to be called after a terminal resize to guarantee proper rendering. Applies only to new instances. func RecalculateTerminalSize() { // keep in sync with DefaultBarChart DefaultBarChart.Width = GetTerminalWidth() * 2 / 3 diff --git a/vendor/github.com/pterm/pterm/rgb.go b/vendor/github.com/pterm/pterm/rgb.go index 23b2fa48..a35dd1a0 100644 --- a/vendor/github.com/pterm/pterm/rgb.go +++ b/vendor/github.com/pterm/pterm/rgb.go @@ -18,6 +18,133 @@ type RGB struct { Background bool } +type RGBStyle struct { + Options []Color + Foreground, Background RGB + + hasBg bool +} + +// NewRGBStyle returns a new RGBStyle. +// The foreground color is required, the background color is optional. +// The colors will be set as is, ignoring the RGB.Background property. +func NewRGBStyle(foreground RGB, background ...RGB) RGBStyle { + var s RGBStyle + s.Foreground = foreground + if len(background) > 0 { + s.Background = background[0] + s.hasBg = true + } + return s +} + +// AddOptions adds options to the RGBStyle. +func (p RGBStyle) AddOptions(opts ...Color) RGBStyle { + p.Options = append(p.Options, opts...) + return p +} + +// Print formats using the default formats for its operands and writes to standard output. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +func (p RGBStyle) Print(a ...interface{}) *TextPrinter { + Print(p.Sprint(a...)) + tp := TextPrinter(p) + return &tp +} + +// Println formats using the default formats for its operands and writes to standard output. +// Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. +func (p RGBStyle) Println(a ...interface{}) *TextPrinter { + Println(p.Sprint(a...)) + tp := TextPrinter(p) + return &tp +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +func (p RGBStyle) Printf(format string, a ...interface{}) *TextPrinter { + Printf(format, p.Sprint(a...)) + tp := TextPrinter(p) + return &tp +} + +// Printfln formats according to a format specifier and writes to standard output. +// Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. +func (p RGBStyle) Printfln(format string, a ...interface{}) *TextPrinter { + Printf(format, p.Sprint(a...)) + tp := TextPrinter(p) + return &tp +} + +// PrintOnError prints every error which is not nil. +// If every error is nil, nothing will be printed. +// This can be used for simple error checking. +func (p RGBStyle) PrintOnError(a ...interface{}) *TextPrinter { + for _, arg := range a { + if err, ok := arg.(error); ok { + if err != nil { + p.Println(err) + } + } + } + + tp := TextPrinter(p) + return &tp +} + +// PrintOnErrorf wraps every error which is not nil and prints it. +// If every error is nil, nothing will be printed. +// This can be used for simple error checking. +func (p RGBStyle) PrintOnErrorf(format string, a ...interface{}) *TextPrinter { + for _, arg := range a { + if err, ok := arg.(error); ok { + if err != nil { + p.Println(fmt.Errorf(format, err)) + } + } + } + + tp := TextPrinter(p) + return &tp +} + +// Sprint formats using the default formats for its operands and returns the resulting string. +// Spaces are added between operands when neither is a string. +func (p RGBStyle) Sprint(a ...interface{}) string { + var rgbStyle *color.RGBStyle + if !p.hasBg { + rgbStyle = color.NewRGBStyle(color.RGB(p.Foreground.R, p.Foreground.G, p.Foreground.B)) + } else { + rgbStyle = color.NewRGBStyle(color.RGB(p.Foreground.R, p.Foreground.G, p.Foreground.B), color.RGB(p.Background.R, p.Background.G, p.Background.B)) + } + if len(p.Options) > 0 { + for _, opt := range p.Options { + rgbStyle.AddOpts(color.Color(opt)) + } + } + return rgbStyle.Sprint(a...) +} + +// Sprintln formats using the default formats for its operands and returns the resulting string. +// Spaces are always added between operands and a newline is appended. +func (p RGBStyle) Sprintln(a ...interface{}) string { + return p.Sprint(a...) + "\n" +} + +// Sprintf formats according to a format specifier and returns the resulting string. +func (p RGBStyle) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, p.Sprint(a...)) +} + +// Sprintfln formats according to a format specifier and returns the resulting string. +// Spaces are always added between operands and a newline is appended. +func (p RGBStyle) Sprintfln(format string, a ...interface{}) string { + return fmt.Sprintf(format, p.Sprint(a...)) + "\n" +} + // GetValues returns the RGB values separately. func (p RGB) GetValues() (r, g, b uint8) { return p.R, p.G, p.B @@ -160,3 +287,11 @@ func (p RGB) PrintOnErrorf(format string, a ...interface{}) *TextPrinter { tp := TextPrinter(p) return &tp } + +func (p RGB) ToRGBStyle() RGBStyle { + if p.Background { + return RGBStyle{Background: p} + } + + return RGBStyle{Foreground: p} +} diff --git a/vendor/github.com/pterm/pterm/slog_handler.go b/vendor/github.com/pterm/pterm/slog_handler.go new file mode 100644 index 00000000..a2b2acfe --- /dev/null +++ b/vendor/github.com/pterm/pterm/slog_handler.go @@ -0,0 +1,90 @@ +package pterm + +import ( + "context" + + "log/slog" +) + +type SlogHandler struct { + logger *Logger + attrs []slog.Attr +} + +// Enabled returns true if the given level is enabled. +func (s *SlogHandler) Enabled(ctx context.Context, level slog.Level) bool { + switch level { + case slog.LevelDebug: + return s.logger.CanPrint(LogLevelDebug) + case slog.LevelInfo: + return s.logger.CanPrint(LogLevelInfo) + case slog.LevelWarn: + return s.logger.CanPrint(LogLevelWarn) + case slog.LevelError: + return s.logger.CanPrint(LogLevelError) + } + return false +} + +// Handle handles the given record. +func (s *SlogHandler) Handle(ctx context.Context, record slog.Record) error { + level := record.Level + message := record.Message + + // Convert slog Attrs to a map. + keyValsMap := make(map[string]interface{}) + + record.Attrs(func(attr slog.Attr) bool { + keyValsMap[attr.Key] = attr.Value + return true + }) + + for _, attr := range s.attrs { + keyValsMap[attr.Key] = attr.Value + } + + args := s.logger.ArgsFromMap(keyValsMap) + + // Wrapping args inside another slice to match [][]LoggerArgument + argsWrapped := [][]LoggerArgument{args} + + logger := s.logger + + // Must be done here, see https://github.com/pterm/pterm/issues/608#issuecomment-1876001650 + if logger.CallerOffset == 0 { + logger = logger.WithCallerOffset(3) + } + + switch level { + case slog.LevelDebug: + logger.Debug(message, argsWrapped...) + case slog.LevelInfo: + logger.Info(message, argsWrapped...) + case slog.LevelWarn: + logger.Warn(message, argsWrapped...) + case slog.LevelError: + logger.Error(message, argsWrapped...) + default: + logger.Print(message, argsWrapped...) + } + + return nil +} + +// WithAttrs returns a new handler with the given attributes. +func (s *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + newS := *s + newS.attrs = attrs + return &newS +} + +// WithGroup is not yet supported. +func (s *SlogHandler) WithGroup(name string) slog.Handler { + // Grouping is not yet supported by pterm. + return s +} + +// NewSlogHandler returns a new logging handler that can be intrgrated with log/slog. +func NewSlogHandler(logger *Logger) *SlogHandler { + return &SlogHandler{logger: logger} +} diff --git a/vendor/github.com/pterm/pterm/spinner_printer.go b/vendor/github.com/pterm/pterm/spinner_printer.go index 82b84af7..044b6976 100644 --- a/vendor/github.com/pterm/pterm/spinner_printer.go +++ b/vendor/github.com/pterm/pterm/spinner_printer.go @@ -110,15 +110,18 @@ func (p SpinnerPrinter) WithWriter(writer io.Writer) *SpinnerPrinter { return &p } +// SetWriter sets the custom Writer. +func (p *SpinnerPrinter) SetWriter(writer io.Writer) { + p.Writer = writer +} + // UpdateText updates the message of the active SpinnerPrinter. // Can be used live. func (s *SpinnerPrinter) UpdateText(text string) { s.Text = text if !RawOutput { - fClearLine(s.Writer) Fprinto(s.Writer, s.Style.Sprint(s.currentSequence)+" "+s.MessageStyle.Sprint(s.Text)) - } - if RawOutput { + } else { Fprintln(s.Writer, s.Text) } } @@ -152,7 +155,6 @@ func (s SpinnerPrinter) Start(text ...interface{}) (*SpinnerPrinter, error) { if s.ShowTimer { timer = " (" + time.Since(s.startedAt).Round(s.TimerRoundingFactor).String() + ")" } - fClearLine(s.Writer) Fprinto(s.Writer, s.Style.Sprint(seq)+" "+s.MessageStyle.Sprint(s.Text)+s.TimerStyle.Sprint(timer)) s.currentSequence = seq time.Sleep(s.Delay) @@ -182,8 +184,8 @@ func (s *SpinnerPrinter) Stop() error { // This is used for the interface LivePrinter. // You most likely want to use Start instead of this in your program. func (s *SpinnerPrinter) GenericStart() (*LivePrinter, error) { - _, _ = s.Start() - lp := LivePrinter(s) + p2, _ := s.Start() + lp := LivePrinter(p2) return &lp, nil } diff --git a/vendor/github.com/pterm/pterm/table_printer.go b/vendor/github.com/pterm/pterm/table_printer.go index cbef5019..54e8965a 100644 --- a/vendor/github.com/pterm/pterm/table_printer.go +++ b/vendor/github.com/pterm/pterm/table_printer.go @@ -2,9 +2,10 @@ package pterm import ( "encoding/csv" - "github.com/pterm/pterm/internal" "io" "strings" + + "github.com/pterm/pterm/internal" ) // DefaultTable contains standards, which can be used to print a TablePrinter. @@ -183,8 +184,8 @@ func (p TablePrinter) Srender() (string, error) { c.lines = strings.Split(cRaw, "\n") c.height = len(c.lines) for _, l := range c.lines { - if internal.GetStringMaxWidth(l) > c.width { - c.width = internal.GetStringMaxWidth(l) + if maxWidth := internal.GetStringMaxWidth(l); maxWidth > c.width { + c.width = maxWidth } } r.cells = append(r.cells, c) @@ -262,7 +263,7 @@ func (p TablePrinter) renderRow(t table, r row) string { } if i < len(c.lines) { - s += strings.TrimSpace(c.lines[i]) + s += c.lines[i] } if j < len(r.cells)-1 { diff --git a/vendor/github.com/pterm/pterm/theme.go b/vendor/github.com/pterm/pterm/theme.go index 22466ea7..91594c8c 100644 --- a/vendor/github.com/pterm/pterm/theme.go +++ b/vendor/github.com/pterm/pterm/theme.go @@ -30,6 +30,9 @@ var ( TableStyle: Style{FgDefault}, TableHeaderStyle: Style{FgLightCyan}, TableSeparatorStyle: Style{FgGray}, + HeatmapStyle: Style{FgDefault}, + HeatmapHeaderStyle: Style{FgLightCyan}, + HeatmapSeparatorStyle: Style{FgDefault}, SectionStyle: Style{Bold, FgYellow}, BulletListTextStyle: Style{FgDefault}, BulletListBulletStyle: Style{FgGray}, @@ -81,6 +84,9 @@ type Theme struct { TableStyle Style TableHeaderStyle Style TableSeparatorStyle Style + HeatmapStyle Style + HeatmapHeaderStyle Style + HeatmapSeparatorStyle Style SectionStyle Style BulletListTextStyle Style BulletListBulletStyle Style diff --git a/vendor/github.com/pterm/pterm/tree_printer.go b/vendor/github.com/pterm/pterm/tree_printer.go index fe10c133..113f9202 100644 --- a/vendor/github.com/pterm/pterm/tree_printer.go +++ b/vendor/github.com/pterm/pterm/tree_printer.go @@ -124,7 +124,7 @@ func (p TreePrinter) Srender() (string, error) { var result string if p.Root.Text != "" { - result += p.Root.Text + "\n" + result += p.TextStyle.Sprint(p.Root.Text) + "\n" } result += walkOverTree(p.Root.Children, p, "") return result, nil diff --git a/vendor/github.com/sheldonhull/magetools/ci/ci.go b/vendor/github.com/sheldonhull/magetools/ci/ci.go index 670dadcb..162c3f79 100644 --- a/vendor/github.com/sheldonhull/magetools/ci/ci.go +++ b/vendor/github.com/sheldonhull/magetools/ci/ci.go @@ -16,19 +16,35 @@ import ( // IsCI will set the global variable for IsCI based on lookup of the environment variable. func IsCI() bool { magetoolsutils.CheckPtermDebug() + _, exists := os.LookupEnv("AGENT_ID") if exists { pterm.Info.Println("Azure DevOps match based on AGENT_ID. Setting IS_CI = 1") return true } + _, exists = os.LookupEnv("GITLAB_CI") + if exists { + pterm.Info.Println("Gitlab Runner match based on [GITLAB_CI] env variable. Setting IS_CI = 1") + + return true + } + _, exists = os.LookupEnv("NETLIFY") if exists { pterm.Info.Println("Netlify match based on [NETLIFY] environment. Setting IS_CI = 1") return true } + _, exists = os.LookupEnv("GITHUB_ACTIONS") + if exists { + pterm.Info.Println("GitHub Actions match based on [GITHUB_ACTIONS] environment. Setting IS_CI = 1") + + return true + } + // CI is also set for Netlify, so it's important to run the NETFLIFY check before the CI check. + // It might be used by others since it's so common so let's leave this check to the very end. _, exists = os.LookupEnv("CI") if exists { pterm.Info.Println("GitHub actions match based on [CI] env variable. Setting IS_CI = 1") diff --git a/vendor/github.com/sheldonhull/magetools/gotools/gotools.go b/vendor/github.com/sheldonhull/magetools/gotools/gotools.go index f952722b..3bf5cc17 100644 --- a/vendor/github.com/sheldonhull/magetools/gotools/gotools.go +++ b/vendor/github.com/sheldonhull/magetools/gotools/gotools.go @@ -270,7 +270,7 @@ func (Go) Lint() error { Printfln("unable to find %s: %v", appgolangcilint, err) return err } - pterm.Info.Printfln("gotestsum found: %s", golangcilint) + pterm.Info.Printfln("golangci-lint found: %s", golangcilint) if err := sh.RunV(golangcilint, "run"); err != nil { pterm.Error.WithShowLineNumber(true).WithLineNumberOffset(1).Println("golangci-lint failure") diff --git a/vendor/github.com/sheldonhull/magetools/pkg/magetoolsutils/magetoolsutils.go b/vendor/github.com/sheldonhull/magetools/pkg/magetoolsutils/magetoolsutils.go index 20b6762b..ca60bfe7 100644 --- a/vendor/github.com/sheldonhull/magetools/pkg/magetoolsutils/magetoolsutils.go +++ b/vendor/github.com/sheldonhull/magetools/pkg/magetoolsutils/magetoolsutils.go @@ -32,7 +32,7 @@ func CheckPtermDebug() { //nolint:cyclop,funlen // cyclop,funlen: i'm sure there } if debug { pterm.Debug.Println("strconv.ParseBool(\"DEBUG\") true, enabling debug output and exiting") - pterm.Info.Println("DEBUG env var detected, setting tasks to debug level output") + pterm.Debug.Println("DEBUG env var detected, setting tasks to debug level output") pterm.EnableDebugMessages() return } @@ -50,7 +50,7 @@ func CheckPtermDebug() { //nolint:cyclop,funlen // cyclop,funlen: i'm sure there if debug { pterm.Debug.Println("strconv.ParseBool(\"SYSTEM_DEBUG\") true, enabling debug output and exiting") - pterm.Info.Println("SYSTEM_DEBUG env var detected, setting tasks to debug level output") + pterm.Debug.Println("SYSTEM_DEBUG env var detected, setting tasks to debug level output") pterm.EnableDebugMessages() return } @@ -74,7 +74,7 @@ func CheckPtermDebug() { //nolint:cyclop,funlen // cyclop,funlen: i'm sure there } if mg.Verbose() { pterm.Debug.Printfln("mg.Verbose() true, setting pterm.EnableDebugMessages()") - pterm.Info.Println("mg.Verbose() true (-v or MAGEFILE_VERBOSE env var), setting tasks to debug level output") + pterm.Debug.Println("mg.Verbose() true (-v or MAGEFILE_VERBOSE env var), setting tasks to debug level output") pterm.EnableDebugMessages() return } diff --git a/vendor/github.com/sirupsen/logrus/.gitignore b/vendor/github.com/sirupsen/logrus/.gitignore index 6b7d7d1e..1fb13abe 100644 --- a/vendor/github.com/sirupsen/logrus/.gitignore +++ b/vendor/github.com/sirupsen/logrus/.gitignore @@ -1,2 +1,4 @@ logrus vendor + +.idea/ diff --git a/vendor/github.com/sirupsen/logrus/.golangci.yml b/vendor/github.com/sirupsen/logrus/.golangci.yml new file mode 100644 index 00000000..65dc2850 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/.golangci.yml @@ -0,0 +1,40 @@ +run: + # do not run on test files yet + tests: false + +# all available settings of specific linters +linters-settings: + errcheck: + # report about not checking of errors in type assetions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + lll: + line-length: 100 + tab-width: 4 + + prealloc: + simple: false + range-loops: false + for-loops: false + + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + +linters: + enable: + - megacheck + - govet + disable: + - maligned + - prealloc + disable-all: false + presets: + - bugs + - unused + fast: false diff --git a/vendor/github.com/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml index 1f953beb..c1dbd5a3 100644 --- a/vendor/github.com/sirupsen/logrus/.travis.yml +++ b/vendor/github.com/sirupsen/logrus/.travis.yml @@ -1,51 +1,15 @@ language: go +go_import_path: github.com/sirupsen/logrus +git: + depth: 1 env: - - GOMAXPROCS=4 GORACE=halt_on_error=1 -matrix: - include: - - go: 1.10.x - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v ./... - - go: 1.11.x - env: GO111MODULE=on - install: - - go mod download - script: - - go test -race -v ./... - - go: 1.11.x - env: GO111MODULE=off - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v ./... - - go: 1.10.x - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v -tags appengine ./... - - go: 1.11.x - env: GO111MODULE=on - install: - - go mod download - script: - - go test -race -v -tags appengine ./... - - go: 1.11.x - env: GO111MODULE=off - install: - - go get github.com/stretchr/testify/assert - - go get golang.org/x/crypto/ssh/terminal - - go get golang.org/x/sys/unix - - go get golang.org/x/sys/windows - script: - - go test -race -v -tags appengine ./... + - GO111MODULE=on +go: 1.15.x +os: linux +install: + - ./travis/install.sh +script: + - cd ci + - go run mage.go -v -w ../ crossBuild + - go run mage.go -v -w ../ lint + - go run mage.go -v -w ../ test diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md index cb85d9f9..7567f612 100644 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md @@ -1,3 +1,97 @@ +# 1.8.1 +Code quality: + * move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer + * improve timestamp format documentation + +Fixes: + * fix race condition on logger hooks + + +# 1.8.0 + +Correct versioning number replacing v1.7.1. + +# 1.7.1 + +Beware this release has introduced a new public API and its semver is therefore incorrect. + +Code quality: + * use go 1.15 in travis + * use magefile as task runner + +Fixes: + * small fixes about new go 1.13 error formatting system + * Fix for long time race condiction with mutating data hooks + +Features: + * build support for zos + +# 1.7.0 +Fixes: + * the dependency toward a windows terminal library has been removed + +Features: + * a new buffer pool management API has been added + * a set of `Fn()` functions have been added + +# 1.6.0 +Fixes: + * end of line cleanup + * revert the entry concurrency bug fix whic leads to deadlock under some circumstances + * update dependency on go-windows-terminal-sequences to fix a crash with go 1.14 + +Features: + * add an option to the `TextFormatter` to completely disable fields quoting + +# 1.5.0 +Code quality: + * add golangci linter run on travis + +Fixes: + * add mutex for hooks concurrent access on `Entry` data + * caller function field for go1.14 + * fix build issue for gopherjs target + +Feature: + * add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level + * add a `DisableHTMLEscape` option in the `JSONFormatter` + * add `ForceQuote` and `PadLevelText` options in the `TextFormatter` + +# 1.4.2 + * Fixes build break for plan9, nacl, solaris +# 1.4.1 +This new release introduces: + * Enhance TextFormatter to not print caller information when they are empty (#944) + * Remove dependency on golang.org/x/crypto (#932, #943) + +Fixes: + * Fix Entry.WithContext method to return a copy of the initial entry (#941) + +# 1.4.0 +This new release introduces: + * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). + * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911) + * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). + +Fixes: + * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893). + * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903) + * Fix infinite recursion on unknown `Level.String()` (#907) + * Fix race condition in `getCaller` (#916). + + +# 1.3.0 +This new release introduces: + * Log, Logf, Logln functions for Logger and Entry that take a Level + +Fixes: + * Building prometheus node_exporter on AIX (#840) + * Race condition in TextFormatter (#468) + * Travis CI import path (#868) + * Remove coloured output on Windows (#862) + * Pointer to func as field in JSONFormatter (#870) + * Properly marshal Levels (#873) + # 1.2.0 This new release introduces: * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md index 093bb13f..d1d4a85f 100644 --- a/vendor/github.com/sirupsen/logrus/README.md +++ b/vendor/github.com/sirupsen/logrus/README.md @@ -1,8 +1,28 @@ -# Logrus :walrus: [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/sirupsen/logrus?status.svg)](https://godoc.org/github.com/sirupsen/logrus) +# Logrus :walrus: [![Build Status](https://github.com/sirupsen/logrus/workflows/CI/badge.svg)](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![Go Reference](https://pkg.go.dev/badge/github.com/sirupsen/logrus.svg)](https://pkg.go.dev/github.com/sirupsen/logrus) Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. +**Logrus is in maintenance-mode.** We will not be introducing new features. It's +simply too hard to do in a way that won't break many people's projects, which is +the last thing you want from your Logging library (again...). + +This does not mean Logrus is dead. Logrus will continue to be maintained for +security, (backwards compatible) bug fixes, and performance (where we are +limited by the interface). + +I believe Logrus' biggest contribution is to have played a part in today's +widespread use of structured logging in Golang. There doesn't seem to be a +reason to do a major, breaking iteration into Logrus V2, since the fantastic Go +community has built those independently. Many fantastic alternatives have sprung +up. Logrus would look like those, had it been re-designed with what we know +about structured logging in Go today. Check out, for example, +[Zerolog][zerolog], [Zap][zap], and [Apex][apex]. + +[zerolog]: https://github.com/rs/zerolog +[zap]: https://github.com/uber-go/zap +[apex]: https://github.com/apex/log + **Seeing weird case-sensitive problems?** It's in the past been possible to import Logrus as both upper- and lower-case. Due to the Go package environment, this caused issues in the community and we needed a standard. Some environments @@ -15,11 +35,6 @@ comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). For an in-depth explanation of the casing issue, see [this comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). -**Are you interested in assisting in maintaining Logrus?** Currently I have a -lot of obligations, and I am unable to provide Logrus with the maintainership it -needs. If you'd like to help, please reach out to me at `simon at author's -username dot com`. - Nicely color-coded in development (when a TTY is attached, otherwise just plain text): @@ -28,7 +43,7 @@ plain text): With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash or Splunk: -```json +```text {"animal":"walrus","level":"info","msg":"A group of walrus emerges from the ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} @@ -84,7 +99,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcr ``` Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: +environment via benchmarks: ``` go test -bench=.*CallerTracing ``` @@ -187,7 +202,7 @@ func main() { log.Out = os.Stdout // You could set this to any `io.Writer` such as a file - // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666) + // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) // if err == nil { // log.Out = file // } else { @@ -272,7 +287,7 @@ func init() { ``` Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). -A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) +A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) #### Level logging @@ -302,6 +317,8 @@ log.SetLevel(log.InfoLevel) It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose environment if your application has that. +Note: If you want different log levels for global (`log.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging). + #### Entries Besides the fields added with `WithField` or `WithFields` some fields are @@ -326,7 +343,7 @@ import ( log "github.com/sirupsen/logrus" ) -init() { +func init() { // do something here to set environment depending on an environment variable // or command-line flag if Environment == "production" { @@ -354,6 +371,7 @@ The built-in logging formatters are: [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). * When colors are enabled, levels are truncated to 4 characters by default. To disable truncation set the `DisableLevelTruncation` field to `true`. + * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter). * `logrus.JSONFormatter`. Logs fields as JSON. * All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter). @@ -361,9 +379,13 @@ The built-in logging formatters are: Third party logging formatters: * [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine. +* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html). * [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. * [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. -* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. +* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo. +* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. +* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files. +* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a @@ -382,7 +404,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { // source of the official loggers. serialized, err := json.Marshal(entry.Data) if err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err) } return append(serialized, '\n'), nil } @@ -428,14 +450,14 @@ entries. It should not be a feature of the application-level logger. | Tool | Description | | ---- | ----------- | -|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.| +|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.| |[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) | #### Testing Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: -* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook +* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook * a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): ```go @@ -463,7 +485,7 @@ func TestSomething(t*testing.T){ Logrus can register one or more functions that will be called when any `fatal` level message is logged. The registered handlers will be executed before -logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need +logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted. ``` @@ -488,6 +510,6 @@ Situation when locking is not needed includes: 1) logger.Out is protected by locks. - 2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing) + 2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing) (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go index 8af90637..8fd189e1 100644 --- a/vendor/github.com/sirupsen/logrus/alt_exit.go +++ b/vendor/github.com/sirupsen/logrus/alt_exit.go @@ -51,9 +51,9 @@ func Exit(code int) { os.Exit(code) } -// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke -// all handlers. The handlers will also be invoked when any Fatal log entry is -// made. +// RegisterExitHandler appends a Logrus Exit handler to the list of handlers, +// call logrus.Exit to invoke all handlers. The handlers will also be invoked when +// any Fatal log entry is made. // // This method is useful when a caller wishes to use logrus to log a fatal // message but also needs to gracefully shutdown. An example usecase could be @@ -62,3 +62,15 @@ func Exit(code int) { func RegisterExitHandler(handler func()) { handlers = append(handlers, handler) } + +// DeferExitHandler prepends a Logrus Exit handler to the list of handlers, +// call logrus.Exit to invoke all handlers. The handlers will also be invoked when +// any Fatal log entry is made. +// +// This method is useful when a caller wishes to use logrus to log a fatal +// message but also needs to gracefully shutdown. An example usecase could be +// closing database connections, or sending a alert that the application is +// closing. +func DeferExitHandler(handler func()) { + handlers = append([]func(){handler}, handlers...) +} diff --git a/vendor/github.com/sirupsen/logrus/appveyor.yml b/vendor/github.com/sirupsen/logrus/appveyor.yml index b4ffca27..df9d65c3 100644 --- a/vendor/github.com/sirupsen/logrus/appveyor.yml +++ b/vendor/github.com/sirupsen/logrus/appveyor.yml @@ -1,14 +1,14 @@ version: "{build}" platform: x64 clone_folder: c:\gopath\src\github.com\sirupsen\logrus -environment: +environment: GOPATH: c:\gopath -branches: +branches: only: - master -install: +install: - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% - go version -build_script: +build_script: - go get -t - go test diff --git a/vendor/github.com/sirupsen/logrus/buffer_pool.go b/vendor/github.com/sirupsen/logrus/buffer_pool.go new file mode 100644 index 00000000..c7787f77 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/buffer_pool.go @@ -0,0 +1,43 @@ +package logrus + +import ( + "bytes" + "sync" +) + +var ( + bufferPool BufferPool +) + +type BufferPool interface { + Put(*bytes.Buffer) + Get() *bytes.Buffer +} + +type defaultPool struct { + pool *sync.Pool +} + +func (p *defaultPool) Put(buf *bytes.Buffer) { + p.pool.Put(buf) +} + +func (p *defaultPool) Get() *bytes.Buffer { + return p.pool.Get().(*bytes.Buffer) +} + +// SetBufferPool allows to replace the default logrus buffer pool +// to better meets the specific needs of an application. +func SetBufferPool(bp BufferPool) { + bufferPool = bp +} + +func init() { + SetBufferPool(&defaultPool{ + pool: &sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + }, + }) +} diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go index cc85d3aa..71cdbbc3 100644 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ b/vendor/github.com/sirupsen/logrus/entry.go @@ -2,6 +2,7 @@ package logrus import ( "bytes" + "context" "fmt" "os" "reflect" @@ -12,7 +13,6 @@ import ( ) var ( - bufferPool *sync.Pool // qualified package name, cached at first use logrusPackage string @@ -30,12 +30,6 @@ const ( ) func init() { - bufferPool = &sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - } - // start at the bottom of the stack before the package-name cache is primed minimumCallerDepth = 1 } @@ -69,6 +63,9 @@ type Entry struct { // When formatter is called in entry.log(), a Buffer may be set to entry Buffer *bytes.Buffer + // Contains the context set by the user. Useful for hook processing etc. + Context context.Context + // err may contain a field formatting error err string } @@ -81,10 +78,23 @@ func NewEntry(logger *Logger) *Entry { } } +func (entry *Entry) Dup() *Entry { + data := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + data[k] = v + } + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err} +} + +// Returns the bytes representation of this entry from the formatter. +func (entry *Entry) Bytes() ([]byte, error) { + return entry.Logger.Formatter.Format(entry) +} + // Returns the string representation from the reader and ultimately the // formatter. func (entry *Entry) String() (string, error) { - serialized, err := entry.Logger.Formatter.Format(entry) + serialized, err := entry.Bytes() if err != nil { return "", err } @@ -97,6 +107,15 @@ func (entry *Entry) WithError(err error) *Entry { return entry.WithField(ErrorKey, err) } +// Add a context to the Entry. +func (entry *Entry) WithContext(ctx context.Context) *Entry { + dataCopy := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + dataCopy[k] = v + } + return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx} +} + // Add a single field to the Entry. func (entry *Entry) WithField(key string, value interface{}) *Entry { return entry.WithFields(Fields{key: value}) @@ -108,23 +127,36 @@ func (entry *Entry) WithFields(fields Fields) *Entry { for k, v := range entry.Data { data[k] = v } - var field_err string + fieldErr := entry.err for k, v := range fields { - if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func { - field_err = fmt.Sprintf("can not add field %q", k) - if entry.err != "" { - field_err = entry.err + ", " + field_err + isErrField := false + if t := reflect.TypeOf(v); t != nil { + switch { + case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func: + isErrField = true + } + } + if isErrField { + tmp := fmt.Sprintf("can not add field %q", k) + if fieldErr != "" { + fieldErr = entry.err + ", " + tmp + } else { + fieldErr = tmp } } else { data[k] = v } } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err} + return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context} } // Overrides the time of the Entry. func (entry *Entry) WithTime(t time.Time) *Entry { - return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t} + dataCopy := make(Fields, len(entry.Data)) + for k, v := range entry.Data { + dataCopy[k] = v + } + return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context} } // getPackageName reduces a fully qualified function name to the package name @@ -145,26 +177,34 @@ func getPackageName(f string) string { // getCaller retrieves the name of the first non-logrus calling function func getCaller() *runtime.Frame { - // Restrict the lookback frames to avoid runaway lookups - pcs := make([]uintptr, maximumCallerDepth) - depth := runtime.Callers(minimumCallerDepth, pcs) - frames := runtime.CallersFrames(pcs[:depth]) - // cache this package's fully-qualified name callerInitOnce.Do(func() { - logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name()) + pcs := make([]uintptr, maximumCallerDepth) + _ = runtime.Callers(0, pcs) + + // dynamic get the package name and the minimum caller depth + for i := 0; i < maximumCallerDepth; i++ { + funcName := runtime.FuncForPC(pcs[i]).Name() + if strings.Contains(funcName, "getCaller") { + logrusPackage = getPackageName(funcName) + break + } + } - // now that we have the cache, we can skip a minimum count of known-logrus functions - // XXX this is dubious, the number of frames may vary store an entry in a logger interface minimumCallerDepth = knownLogrusFrames }) + // Restrict the lookback frames to avoid runaway lookups + pcs := make([]uintptr, maximumCallerDepth) + depth := runtime.Callers(minimumCallerDepth, pcs) + frames := runtime.CallersFrames(pcs[:depth]) + for f, again := frames.Next(); again; f, again = frames.Next() { pkg := getPackageName(f.Function) // If the caller isn't part of this package, we're done if pkg != logrusPackage { - return &f + return &f //nolint:scopelint } } @@ -178,49 +218,66 @@ func (entry Entry) HasCaller() (has bool) { entry.Caller != nil } -// This function is not declared with a pointer value because otherwise -// race conditions will occur when using multiple goroutines -func (entry Entry) log(level Level, msg string) { +func (entry *Entry) log(level Level, msg string) { var buffer *bytes.Buffer - // Default to now, but allow users to override if they want. - // - // We don't have to worry about polluting future calls to Entry#log() - // with this assignment because this function is declared with a - // non-pointer receiver. - if entry.Time.IsZero() { - entry.Time = time.Now() - } + newEntry := entry.Dup() - entry.Level = level - entry.Message = msg - if entry.Logger.ReportCaller { - entry.Caller = getCaller() + if newEntry.Time.IsZero() { + newEntry.Time = time.Now() } - entry.fireHooks() + newEntry.Level = level + newEntry.Message = msg + + newEntry.Logger.mu.Lock() + reportCaller := newEntry.Logger.ReportCaller + bufPool := newEntry.getBufferPool() + newEntry.Logger.mu.Unlock() - buffer = bufferPool.Get().(*bytes.Buffer) + if reportCaller { + newEntry.Caller = getCaller() + } + + newEntry.fireHooks() + buffer = bufPool.Get() + defer func() { + newEntry.Buffer = nil + buffer.Reset() + bufPool.Put(buffer) + }() buffer.Reset() - defer bufferPool.Put(buffer) - entry.Buffer = buffer + newEntry.Buffer = buffer - entry.write() + newEntry.write() - entry.Buffer = nil + newEntry.Buffer = nil // To avoid Entry#log() returning a value that only would make sense for // panic() to use in Entry#Panic(), we avoid the allocation by checking // directly here. if level <= PanicLevel { - panic(&entry) + panic(newEntry) } } +func (entry *Entry) getBufferPool() (pool BufferPool) { + if entry.Logger.BufferPool != nil { + return entry.Logger.BufferPool + } + return bufferPool +} + func (entry *Entry) fireHooks() { + var tmpHooks LevelHooks entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - err := entry.Logger.Hooks.Fire(entry.Level, entry) + tmpHooks = make(LevelHooks, len(entry.Logger.Hooks)) + for k, v := range entry.Logger.Hooks { + tmpHooks[k] = v + } + entry.Logger.mu.Unlock() + + err := tmpHooks.Fire(entry.Level, entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) } @@ -232,24 +289,28 @@ func (entry *Entry) write() { serialized, err := entry.Logger.Formatter.Format(entry) if err != nil { fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) - } else { - _, err = entry.Logger.Out.Write(serialized) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) - } + return + } + if _, err := entry.Logger.Out.Write(serialized); err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) } } -func (entry *Entry) Trace(args ...interface{}) { - if entry.Logger.IsLevelEnabled(TraceLevel) { - entry.log(TraceLevel, fmt.Sprint(args...)) +// Log will log a message at the level given as parameter. +// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit. +// For this behaviour Entry.Panic or Entry.Fatal should be used instead. +func (entry *Entry) Log(level Level, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.log(level, fmt.Sprint(args...)) } } +func (entry *Entry) Trace(args ...interface{}) { + entry.Log(TraceLevel, args...) +} + func (entry *Entry) Debug(args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.log(DebugLevel, fmt.Sprint(args...)) - } + entry.Log(DebugLevel, args...) } func (entry *Entry) Print(args ...interface{}) { @@ -257,15 +318,11 @@ func (entry *Entry) Print(args ...interface{}) { } func (entry *Entry) Info(args ...interface{}) { - if entry.Logger.IsLevelEnabled(InfoLevel) { - entry.log(InfoLevel, fmt.Sprint(args...)) - } + entry.Log(InfoLevel, args...) } func (entry *Entry) Warn(args ...interface{}) { - if entry.Logger.IsLevelEnabled(WarnLevel) { - entry.log(WarnLevel, fmt.Sprint(args...)) - } + entry.Log(WarnLevel, args...) } func (entry *Entry) Warning(args ...interface{}) { @@ -273,43 +330,36 @@ func (entry *Entry) Warning(args ...interface{}) { } func (entry *Entry) Error(args ...interface{}) { - if entry.Logger.IsLevelEnabled(ErrorLevel) { - entry.log(ErrorLevel, fmt.Sprint(args...)) - } + entry.Log(ErrorLevel, args...) } func (entry *Entry) Fatal(args ...interface{}) { - if entry.Logger.IsLevelEnabled(FatalLevel) { - entry.log(FatalLevel, fmt.Sprint(args...)) - } + entry.Log(FatalLevel, args...) entry.Logger.Exit(1) } func (entry *Entry) Panic(args ...interface{}) { - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.log(PanicLevel, fmt.Sprint(args...)) - } - panic(fmt.Sprint(args...)) + entry.Log(PanicLevel, args...) } // Entry Printf family functions -func (entry *Entry) Tracef(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(TraceLevel) { - entry.Trace(fmt.Sprintf(format, args...)) +func (entry *Entry) Logf(level Level, format string, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.Log(level, fmt.Sprintf(format, args...)) } } +func (entry *Entry) Tracef(format string, args ...interface{}) { + entry.Logf(TraceLevel, format, args...) +} + func (entry *Entry) Debugf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.Debug(fmt.Sprintf(format, args...)) - } + entry.Logf(DebugLevel, format, args...) } func (entry *Entry) Infof(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(InfoLevel) { - entry.Info(fmt.Sprintf(format, args...)) - } + entry.Logf(InfoLevel, format, args...) } func (entry *Entry) Printf(format string, args ...interface{}) { @@ -317,9 +367,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) { } func (entry *Entry) Warnf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(WarnLevel) { - entry.Warn(fmt.Sprintf(format, args...)) - } + entry.Logf(WarnLevel, format, args...) } func (entry *Entry) Warningf(format string, args ...interface{}) { @@ -327,42 +375,36 @@ func (entry *Entry) Warningf(format string, args ...interface{}) { } func (entry *Entry) Errorf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(ErrorLevel) { - entry.Error(fmt.Sprintf(format, args...)) - } + entry.Logf(ErrorLevel, format, args...) } func (entry *Entry) Fatalf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(FatalLevel) { - entry.Fatal(fmt.Sprintf(format, args...)) - } + entry.Logf(FatalLevel, format, args...) entry.Logger.Exit(1) } func (entry *Entry) Panicf(format string, args ...interface{}) { - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.Panic(fmt.Sprintf(format, args...)) - } + entry.Logf(PanicLevel, format, args...) } // Entry Println family functions -func (entry *Entry) Traceln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(TraceLevel) { - entry.Trace(entry.sprintlnn(args...)) +func (entry *Entry) Logln(level Level, args ...interface{}) { + if entry.Logger.IsLevelEnabled(level) { + entry.Log(level, entry.sprintlnn(args...)) } } +func (entry *Entry) Traceln(args ...interface{}) { + entry.Logln(TraceLevel, args...) +} + func (entry *Entry) Debugln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(DebugLevel) { - entry.Debug(entry.sprintlnn(args...)) - } + entry.Logln(DebugLevel, args...) } func (entry *Entry) Infoln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(InfoLevel) { - entry.Info(entry.sprintlnn(args...)) - } + entry.Logln(InfoLevel, args...) } func (entry *Entry) Println(args ...interface{}) { @@ -370,9 +412,7 @@ func (entry *Entry) Println(args ...interface{}) { } func (entry *Entry) Warnln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(WarnLevel) { - entry.Warn(entry.sprintlnn(args...)) - } + entry.Logln(WarnLevel, args...) } func (entry *Entry) Warningln(args ...interface{}) { @@ -380,22 +420,16 @@ func (entry *Entry) Warningln(args ...interface{}) { } func (entry *Entry) Errorln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(ErrorLevel) { - entry.Error(entry.sprintlnn(args...)) - } + entry.Logln(ErrorLevel, args...) } func (entry *Entry) Fatalln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(FatalLevel) { - entry.Fatal(entry.sprintlnn(args...)) - } + entry.Logln(FatalLevel, args...) entry.Logger.Exit(1) } func (entry *Entry) Panicln(args ...interface{}) { - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.Panic(entry.sprintlnn(args...)) - } + entry.Logln(PanicLevel, args...) } // Sprintlnn => Sprint no newline. This is to get the behavior of how diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go index 7342613c..017c30ce 100644 --- a/vendor/github.com/sirupsen/logrus/exported.go +++ b/vendor/github.com/sirupsen/logrus/exported.go @@ -1,6 +1,7 @@ package logrus import ( + "context" "io" "time" ) @@ -55,6 +56,11 @@ func WithError(err error) *Entry { return std.WithField(ErrorKey, err) } +// WithContext creates an entry from the standard logger and adds a context to it. +func WithContext(ctx context.Context) *Entry { + return std.WithContext(ctx) +} + // WithField creates an entry from the standard logger and adds a field to // it. If you want multiple fields, use `WithFields`. // @@ -74,7 +80,7 @@ func WithFields(fields Fields) *Entry { return std.WithFields(fields) } -// WithTime creats an entry from the standard logger and overrides the time of +// WithTime creates an entry from the standard logger and overrides the time of // logs generated with it. // // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal @@ -128,6 +134,51 @@ func Fatal(args ...interface{}) { std.Fatal(args...) } +// TraceFn logs a message from a func at level Trace on the standard logger. +func TraceFn(fn LogFunction) { + std.TraceFn(fn) +} + +// DebugFn logs a message from a func at level Debug on the standard logger. +func DebugFn(fn LogFunction) { + std.DebugFn(fn) +} + +// PrintFn logs a message from a func at level Info on the standard logger. +func PrintFn(fn LogFunction) { + std.PrintFn(fn) +} + +// InfoFn logs a message from a func at level Info on the standard logger. +func InfoFn(fn LogFunction) { + std.InfoFn(fn) +} + +// WarnFn logs a message from a func at level Warn on the standard logger. +func WarnFn(fn LogFunction) { + std.WarnFn(fn) +} + +// WarningFn logs a message from a func at level Warn on the standard logger. +func WarningFn(fn LogFunction) { + std.WarningFn(fn) +} + +// ErrorFn logs a message from a func at level Error on the standard logger. +func ErrorFn(fn LogFunction) { + std.ErrorFn(fn) +} + +// PanicFn logs a message from a func at level Panic on the standard logger. +func PanicFn(fn LogFunction) { + std.PanicFn(fn) +} + +// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1. +func FatalFn(fn LogFunction) { + std.FatalFn(fn) +} + // Tracef logs a message at level Trace on the standard logger. func Tracef(format string, args ...interface{}) { std.Tracef(format, args...) diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go index 26057535..c96dc563 100644 --- a/vendor/github.com/sirupsen/logrus/json_formatter.go +++ b/vendor/github.com/sirupsen/logrus/json_formatter.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "runtime" ) type fieldKey string @@ -22,11 +23,17 @@ func (f FieldMap) resolve(key fieldKey) string { // JSONFormatter formats logs into parsable json type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. + // The format to use is the same than for time.Format or time.Parse from the standard + // library. + // The standard Library already provides a set of predefined format. TimestampFormat string // DisableTimestamp allows disabling automatic timestamps in output DisableTimestamp bool + // DisableHTMLEscape allows disabling html escaping in output + DisableHTMLEscape bool + // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. DataKey string @@ -42,6 +49,12 @@ type JSONFormatter struct { // } FieldMap FieldMap + // CallerPrettyfier can be set by the user to modify the content + // of the function and file keys in the json data when ReportCaller is + // activated. If any of the returned value is the empty string the + // corresponding key will be removed from json fields. + CallerPrettyfier func(*runtime.Frame) (function string, file string) + // PrettyPrint will indent all json logs PrettyPrint bool } @@ -82,8 +95,17 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() if entry.HasCaller() { - data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function - data[f.FieldMap.resolve(FieldKeyFile)] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + funcVal := entry.Caller.Function + fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } + if funcVal != "" { + data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal + } + if fileVal != "" { + data[f.FieldMap.resolve(FieldKeyFile)] = fileVal + } } var b *bytes.Buffer @@ -94,11 +116,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } encoder := json.NewEncoder(b) + encoder.SetEscapeHTML(!f.DisableHTMLEscape) if f.PrettyPrint { encoder.SetIndent("", " ") } if err := encoder.Encode(data); err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err) } return b.Bytes(), nil diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go index 5ceca0ea..5ff0aef6 100644 --- a/vendor/github.com/sirupsen/logrus/logger.go +++ b/vendor/github.com/sirupsen/logrus/logger.go @@ -1,6 +1,7 @@ package logrus import ( + "context" "io" "os" "sync" @@ -8,6 +9,11 @@ import ( "time" ) +// LogFunction For big messages, it can be more efficient to pass a function +// and only call it if the log level is actually enables rather than +// generating the log message and then checking if the level is enabled +type LogFunction func() []interface{} + type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // file, or leave it default which is `os.Stderr`. You can also set this to @@ -38,6 +44,9 @@ type Logger struct { entryPool sync.Pool // Function to exit the application, defaults to `os.Exit()` ExitFunc exitFunc + // The buffer pool used to format the log. If it is nil, the default global + // buffer pool will be used. + BufferPool BufferPool } type exitFunc func(int) @@ -67,10 +76,10 @@ func (mw *MutexWrap) Disable() { // `Out` and `Hooks` directly on the default logger instance. You can also just // instantiate your own: // -// var log = &Logger{ +// var log = &logrus.Logger{ // Out: os.Stderr, -// Formatter: new(JSONFormatter), -// Hooks: make(LevelHooks), +// Formatter: new(logrus.TextFormatter), +// Hooks: make(logrus.LevelHooks), // Level: logrus.DebugLevel, // } // @@ -99,8 +108,9 @@ func (logger *Logger) releaseEntry(entry *Entry) { logger.entryPool.Put(entry) } -// Adds a field to the log entry, note that it doesn't log until you call -// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry. +// WithField allocates a new entry and adds a field to it. +// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to +// this new returned entry. // If you want multiple fields, use `WithFields`. func (logger *Logger) WithField(key string, value interface{}) *Entry { entry := logger.newEntry() @@ -124,6 +134,13 @@ func (logger *Logger) WithError(err error) *Entry { return entry.WithError(err) } +// Add a context to the log entry. +func (logger *Logger) WithContext(ctx context.Context) *Entry { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + return entry.WithContext(ctx) +} + // Overrides the time of the log entry. func (logger *Logger) WithTime(t time.Time) *Entry { entry := logger.newEntry() @@ -131,28 +148,24 @@ func (logger *Logger) WithTime(t time.Time) *Entry { return entry.WithTime(t) } -func (logger *Logger) Tracef(format string, args ...interface{}) { - if logger.IsLevelEnabled(TraceLevel) { +func (logger *Logger) Logf(level Level, format string, args ...interface{}) { + if logger.IsLevelEnabled(level) { entry := logger.newEntry() - entry.Tracef(format, args...) + entry.Logf(level, format, args...) logger.releaseEntry(entry) } } +func (logger *Logger) Tracef(format string, args ...interface{}) { + logger.Logf(TraceLevel, format, args...) +} + func (logger *Logger) Debugf(format string, args ...interface{}) { - if logger.IsLevelEnabled(DebugLevel) { - entry := logger.newEntry() - entry.Debugf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(DebugLevel, format, args...) } func (logger *Logger) Infof(format string, args ...interface{}) { - if logger.IsLevelEnabled(InfoLevel) { - entry := logger.newEntry() - entry.Infof(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(InfoLevel, format, args...) } func (logger *Logger) Printf(format string, args ...interface{}) { @@ -162,139 +175,141 @@ func (logger *Logger) Printf(format string, args ...interface{}) { } func (logger *Logger) Warnf(format string, args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(WarnLevel, format, args...) } func (logger *Logger) Warningf(format string, args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnf(format, args...) - logger.releaseEntry(entry) - } + logger.Warnf(format, args...) } func (logger *Logger) Errorf(format string, args ...interface{}) { - if logger.IsLevelEnabled(ErrorLevel) { - entry := logger.newEntry() - entry.Errorf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(ErrorLevel, format, args...) } func (logger *Logger) Fatalf(format string, args ...interface{}) { - if logger.IsLevelEnabled(FatalLevel) { - entry := logger.newEntry() - entry.Fatalf(format, args...) - logger.releaseEntry(entry) - } + logger.Logf(FatalLevel, format, args...) logger.Exit(1) } func (logger *Logger) Panicf(format string, args ...interface{}) { - if logger.IsLevelEnabled(PanicLevel) { + logger.Logf(PanicLevel, format, args...) +} + +// Log will log a message at the level given as parameter. +// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit. +// For this behaviour Logger.Panic or Logger.Fatal should be used instead. +func (logger *Logger) Log(level Level, args ...interface{}) { + if logger.IsLevelEnabled(level) { entry := logger.newEntry() - entry.Panicf(format, args...) + entry.Log(level, args...) logger.releaseEntry(entry) } } -func (logger *Logger) Trace(args ...interface{}) { - if logger.IsLevelEnabled(TraceLevel) { +func (logger *Logger) LogFn(level Level, fn LogFunction) { + if logger.IsLevelEnabled(level) { entry := logger.newEntry() - entry.Trace(args...) + entry.Log(level, fn()...) logger.releaseEntry(entry) } } +func (logger *Logger) Trace(args ...interface{}) { + logger.Log(TraceLevel, args...) +} + func (logger *Logger) Debug(args ...interface{}) { - if logger.IsLevelEnabled(DebugLevel) { - entry := logger.newEntry() - entry.Debug(args...) - logger.releaseEntry(entry) - } + logger.Log(DebugLevel, args...) } func (logger *Logger) Info(args ...interface{}) { - if logger.IsLevelEnabled(InfoLevel) { - entry := logger.newEntry() - entry.Info(args...) - logger.releaseEntry(entry) - } + logger.Log(InfoLevel, args...) } func (logger *Logger) Print(args ...interface{}) { entry := logger.newEntry() - entry.Info(args...) + entry.Print(args...) logger.releaseEntry(entry) } func (logger *Logger) Warn(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warn(args...) - logger.releaseEntry(entry) - } + logger.Log(WarnLevel, args...) } func (logger *Logger) Warning(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warn(args...) - logger.releaseEntry(entry) - } + logger.Warn(args...) } func (logger *Logger) Error(args ...interface{}) { - if logger.IsLevelEnabled(ErrorLevel) { - entry := logger.newEntry() - entry.Error(args...) - logger.releaseEntry(entry) - } + logger.Log(ErrorLevel, args...) } func (logger *Logger) Fatal(args ...interface{}) { - if logger.IsLevelEnabled(FatalLevel) { - entry := logger.newEntry() - entry.Fatal(args...) - logger.releaseEntry(entry) - } + logger.Log(FatalLevel, args...) logger.Exit(1) } func (logger *Logger) Panic(args ...interface{}) { - if logger.IsLevelEnabled(PanicLevel) { + logger.Log(PanicLevel, args...) +} + +func (logger *Logger) TraceFn(fn LogFunction) { + logger.LogFn(TraceLevel, fn) +} + +func (logger *Logger) DebugFn(fn LogFunction) { + logger.LogFn(DebugLevel, fn) +} + +func (logger *Logger) InfoFn(fn LogFunction) { + logger.LogFn(InfoLevel, fn) +} + +func (logger *Logger) PrintFn(fn LogFunction) { + entry := logger.newEntry() + entry.Print(fn()...) + logger.releaseEntry(entry) +} + +func (logger *Logger) WarnFn(fn LogFunction) { + logger.LogFn(WarnLevel, fn) +} + +func (logger *Logger) WarningFn(fn LogFunction) { + logger.WarnFn(fn) +} + +func (logger *Logger) ErrorFn(fn LogFunction) { + logger.LogFn(ErrorLevel, fn) +} + +func (logger *Logger) FatalFn(fn LogFunction) { + logger.LogFn(FatalLevel, fn) + logger.Exit(1) +} + +func (logger *Logger) PanicFn(fn LogFunction) { + logger.LogFn(PanicLevel, fn) +} + +func (logger *Logger) Logln(level Level, args ...interface{}) { + if logger.IsLevelEnabled(level) { entry := logger.newEntry() - entry.Panic(args...) + entry.Logln(level, args...) logger.releaseEntry(entry) } } func (logger *Logger) Traceln(args ...interface{}) { - if logger.IsLevelEnabled(TraceLevel) { - entry := logger.newEntry() - entry.Traceln(args...) - logger.releaseEntry(entry) - } + logger.Logln(TraceLevel, args...) } func (logger *Logger) Debugln(args ...interface{}) { - if logger.IsLevelEnabled(DebugLevel) { - entry := logger.newEntry() - entry.Debugln(args...) - logger.releaseEntry(entry) - } + logger.Logln(DebugLevel, args...) } func (logger *Logger) Infoln(args ...interface{}) { - if logger.IsLevelEnabled(InfoLevel) { - entry := logger.newEntry() - entry.Infoln(args...) - logger.releaseEntry(entry) - } + logger.Logln(InfoLevel, args...) } func (logger *Logger) Println(args ...interface{}) { @@ -304,44 +319,24 @@ func (logger *Logger) Println(args ...interface{}) { } func (logger *Logger) Warnln(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnln(args...) - logger.releaseEntry(entry) - } + logger.Logln(WarnLevel, args...) } func (logger *Logger) Warningln(args ...interface{}) { - if logger.IsLevelEnabled(WarnLevel) { - entry := logger.newEntry() - entry.Warnln(args...) - logger.releaseEntry(entry) - } + logger.Warnln(args...) } func (logger *Logger) Errorln(args ...interface{}) { - if logger.IsLevelEnabled(ErrorLevel) { - entry := logger.newEntry() - entry.Errorln(args...) - logger.releaseEntry(entry) - } + logger.Logln(ErrorLevel, args...) } func (logger *Logger) Fatalln(args ...interface{}) { - if logger.IsLevelEnabled(FatalLevel) { - entry := logger.newEntry() - entry.Fatalln(args...) - logger.releaseEntry(entry) - } + logger.Logln(FatalLevel, args...) logger.Exit(1) } func (logger *Logger) Panicln(args ...interface{}) { - if logger.IsLevelEnabled(PanicLevel) { - entry := logger.newEntry() - entry.Panicln(args...) - logger.releaseEntry(entry) - } + logger.Logln(PanicLevel, args...) } func (logger *Logger) Exit(code int) { @@ -413,3 +408,10 @@ func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { logger.mu.Unlock() return oldHooks } + +// SetBufferPool sets the logger buffer pool. +func (logger *Logger) SetBufferPool(pool BufferPool) { + logger.mu.Lock() + defer logger.mu.Unlock() + logger.BufferPool = pool +} diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go index 4ef45186..2f16224c 100644 --- a/vendor/github.com/sirupsen/logrus/logrus.go +++ b/vendor/github.com/sirupsen/logrus/logrus.go @@ -14,24 +14,11 @@ type Level uint32 // Convert the Level to a string. E.g. PanicLevel becomes "panic". func (level Level) String() string { - switch level { - case TraceLevel: - return "trace" - case DebugLevel: - return "debug" - case InfoLevel: - return "info" - case WarnLevel: - return "warning" - case ErrorLevel: - return "error" - case FatalLevel: - return "fatal" - case PanicLevel: - return "panic" + if b, err := level.MarshalText(); err == nil { + return string(b) + } else { + return "unknown" } - - return "unknown" } // ParseLevel takes a string level and returns the Logrus log level constant. @@ -64,11 +51,32 @@ func (level *Level) UnmarshalText(text []byte) error { return err } - *level = Level(l) + *level = l return nil } +func (level Level) MarshalText() ([]byte, error) { + switch level { + case TraceLevel: + return []byte("trace"), nil + case DebugLevel: + return []byte("debug"), nil + case InfoLevel: + return []byte("info"), nil + case WarnLevel: + return []byte("warning"), nil + case ErrorLevel: + return []byte("error"), nil + case FatalLevel: + return []byte("fatal"), nil + case PanicLevel: + return []byte("panic"), nil + } + + return nil, fmt.Errorf("not a valid logrus level %d", level) +} + // A constant exposing all logging levels var AllLevels = []Level{ PanicLevel, diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go new file mode 100644 index 00000000..49978998 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go @@ -0,0 +1,13 @@ +// +build darwin dragonfly freebsd netbsd openbsd +// +build !js + +package logrus + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TIOCGETA + +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_js.go b/vendor/github.com/sirupsen/logrus/terminal_check_js.go index 0c209750..ebdae3ec 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_js.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_js.go @@ -2,10 +2,6 @@ package logrus -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { +func isTerminal(fd int) bool { return false } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go new file mode 100644 index 00000000..97af92c6 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go @@ -0,0 +1,11 @@ +// +build js nacl plan9 + +package logrus + +import ( + "io" +) + +func checkIfTerminal(w io.Writer) bool { + return false +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go index cf309d6f..3293fb3c 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go @@ -1,18 +1,16 @@ -// +build !appengine,!js,!windows +// +build !appengine,!js,!windows,!nacl,!plan9 package logrus import ( "io" "os" - - "golang.org/x/crypto/ssh/terminal" ) func checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: - return terminal.IsTerminal(int(v.Fd())) + return isTerminal(int(v.Fd())) default: return false } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go new file mode 100644 index 00000000..f6710b3b --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go @@ -0,0 +1,11 @@ +package logrus + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermio(fd, unix.TCGETA) + return err == nil +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go new file mode 100644 index 00000000..04748b85 --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go @@ -0,0 +1,13 @@ +// +build linux aix zos +// +build !js + +package logrus + +import "golang.org/x/sys/unix" + +const ioctlReadTermios = unix.TCGETS + +func isTerminal(fd int) bool { + _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) + return err == nil +} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go index 3b9d2864..2879eb50 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go @@ -5,16 +5,23 @@ package logrus import ( "io" "os" - "syscall" + + "golang.org/x/sys/windows" ) func checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: + handle := windows.Handle(v.Fd()) var mode uint32 - err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode) - return err == nil - default: - return false + if err := windows.GetConsoleMode(handle, &mode); err != nil { + return false + } + mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + if err := windows.SetConsoleMode(handle, mode); err != nil { + return false + } + return true } + return false } diff --git a/vendor/github.com/sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/sirupsen/logrus/terminal_notwindows.go deleted file mode 100644 index 3dbd2372..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_notwindows.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build !windows - -package logrus - -import "io" - -func initTerminal(w io.Writer) { -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_windows.go b/vendor/github.com/sirupsen/logrus/terminal_windows.go deleted file mode 100644 index b4ef5286..00000000 --- a/vendor/github.com/sirupsen/logrus/terminal_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build !appengine,!js,windows - -package logrus - -import ( - "io" - "os" - "syscall" - - sequences "github.com/konsorten/go-windows-terminal-sequences" -) - -func initTerminal(w io.Writer) { - switch v := w.(type) { - case *os.File: - sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true) - } -} diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go index 49ec92f1..be2c6efe 100644 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ b/vendor/github.com/sirupsen/logrus/text_formatter.go @@ -4,25 +4,23 @@ import ( "bytes" "fmt" "os" + "runtime" "sort" + "strconv" "strings" "sync" "time" + "unicode/utf8" ) const ( - nocolor = 0 - red = 31 - green = 32 - yellow = 33 - blue = 36 - gray = 37 + red = 31 + yellow = 33 + blue = 36 + gray = 37 ) -var ( - baseTimestamp time.Time - emptyFieldMap FieldMap -) +var baseTimestamp time.Time func init() { baseTimestamp = time.Now() @@ -36,6 +34,14 @@ type TextFormatter struct { // Force disabling colors. DisableColors bool + // Force quoting of all values + ForceQuote bool + + // DisableQuote disables quoting for all values. + // DisableQuote will have a lower priority than ForceQuote. + // If both of them are set to true, quote will be forced on all values. + DisableQuote bool + // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ EnvironmentOverrideColors bool @@ -47,7 +53,10 @@ type TextFormatter struct { // the time passed since beginning of execution. FullTimestamp bool - // TimestampFormat to use for display when a full timestamp is printed + // TimestampFormat to use for display when a full timestamp is printed. + // The format to use is the same than for time.Format or time.Parse from the standard + // library. + // The standard Library already provides a set of predefined format. TimestampFormat string // The fields are sorted by default for a consistent output. For applications @@ -61,6 +70,10 @@ type TextFormatter struct { // Disables the truncation of the level text to 4 characters. DisableLevelTruncation bool + // PadLevelText Adds padding the level text so that all the levels output at the same length + // PadLevelText is a superset of the DisableLevelTruncation option + PadLevelText bool + // QuoteEmptyFields will wrap empty fields in quotes if true QuoteEmptyFields bool @@ -76,28 +89,39 @@ type TextFormatter struct { // FieldKeyMsg: "@message"}} FieldMap FieldMap + // CallerPrettyfier can be set by the user to modify the content + // of the function and file keys in the data when ReportCaller is + // activated. If any of the returned value is the empty string the + // corresponding key will be removed from fields. + CallerPrettyfier func(*runtime.Frame) (function string, file string) + terminalInitOnce sync.Once + + // The max length of the level text, generated dynamically on init + levelTextMaxLength int } func (f *TextFormatter) init(entry *Entry) { if entry.Logger != nil { f.isTerminal = checkIfTerminal(entry.Logger.Out) - - if f.isTerminal { - initTerminal(entry.Logger.Out) + } + // Get the max length of the level text + for _, level := range AllLevels { + levelTextLength := utf8.RuneCount([]byte(level.String())) + if levelTextLength > f.levelTextMaxLength { + f.levelTextMaxLength = levelTextLength } } } func (f *TextFormatter) isColored() bool { - isColored := f.ForceColors || f.isTerminal + isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) if f.EnvironmentOverrideColors { - if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" { + switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); { + case ok && force != "0": isColored = true - } else if ok && force == "0" { - isColored = false - } else if os.Getenv("CLICOLOR") == "0" { + case ok && force == "0", os.Getenv("CLICOLOR") == "0": isColored = false } } @@ -107,14 +131,19 @@ func (f *TextFormatter) isColored() bool { // Format renders a single log entry func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - prefixFieldClashes(entry.Data, f.FieldMap, entry.HasCaller()) - - keys := make([]string, 0, len(entry.Data)) - for k := range entry.Data { + data := make(Fields) + for k, v := range entry.Data { + data[k] = v + } + prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + keys := make([]string, 0, len(data)) + for k := range data { keys = append(keys, k) } - fixedKeys := make([]string, 0, 4+len(entry.Data)) + var funcVal, fileVal string + + fixedKeys := make([]string, 0, 4+len(data)) if !f.DisableTimestamp { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) } @@ -126,8 +155,19 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) } if entry.HasCaller() { - fixedKeys = append(fixedKeys, - f.FieldMap.resolve(FieldKeyFunc), f.FieldMap.resolve(FieldKeyFile)) + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } else { + funcVal = entry.Caller.Function + fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + } + + if funcVal != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc)) + } + if fileVal != "" { + fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile)) + } } if !f.DisableSorting { @@ -160,8 +200,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { timestampFormat = defaultTimestampFormat } if f.isColored() { - f.printColored(b, entry, keys, timestampFormat) + f.printColored(b, entry, keys, data, timestampFormat) } else { + for _, key := range fixedKeys { var value interface{} switch { @@ -174,11 +215,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { case key == f.FieldMap.resolve(FieldKeyLogrusError): value = entry.err case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): - value = entry.Caller.Function + value = funcVal case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): - value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + value = fileVal default: - value = entry.Data[key] + value = data[key] } f.appendKeyValue(b, key, value) } @@ -188,7 +229,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { return b.Bytes(), nil } -func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) { var levelColor int switch entry.Level { case DebugLevel, TraceLevel: @@ -197,44 +238,73 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin levelColor = yellow case ErrorLevel, FatalLevel, PanicLevel: levelColor = red + case InfoLevel: + levelColor = blue default: levelColor = blue } levelText := strings.ToUpper(entry.Level.String()) - if !f.DisableLevelTruncation { + if !f.DisableLevelTruncation && !f.PadLevelText { levelText = levelText[0:4] } + if f.PadLevelText { + // Generates the format string used in the next line, for example "%-6s" or "%-7s". + // Based on the max level text length. + formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s" + // Formats the level text by appending spaces up to the max length, for example: + // - "INFO " + // - "WARNING" + levelText = fmt.Sprintf(formatString, levelText) + } // Remove a single newline if it already exists in the message to keep // the behavior of logrus text_formatter the same as the stdlib log package entry.Message = strings.TrimSuffix(entry.Message, "\n") caller := "" - if entry.HasCaller() { - caller = fmt.Sprintf("%s:%d %s()", - entry.Caller.File, entry.Caller.Line, entry.Caller.Function) + funcVal := fmt.Sprintf("%s()", entry.Caller.Function) + fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + + if f.CallerPrettyfier != nil { + funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + } + + if fileVal == "" { + caller = funcVal + } else if funcVal == "" { + caller = fileVal + } else { + caller = fileVal + " " + funcVal + } } - if f.DisableTimestamp { + switch { + case f.DisableTimestamp: fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) - } else if !f.FullTimestamp { + case !f.FullTimestamp: fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) - } else { + default: fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) } for _, k := range keys { - v := entry.Data[k] + v := data[k] fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) f.appendValue(b, v) } } func (f *TextFormatter) needsQuoting(text string) bool { + if f.ForceQuote { + return true + } if f.QuoteEmptyFields && len(text) == 0 { return true } + if f.DisableQuote { + return false + } for _, ch := range text { if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go index 9e1f7513..074fd4b8 100644 --- a/vendor/github.com/sirupsen/logrus/writer.go +++ b/vendor/github.com/sirupsen/logrus/writer.go @@ -4,25 +4,35 @@ import ( "bufio" "io" "runtime" + "strings" ) +// Writer at INFO level. See WriterLevel for details. func (logger *Logger) Writer() *io.PipeWriter { return logger.WriterLevel(InfoLevel) } +// WriterLevel returns an io.Writer that can be used to write arbitrary text to +// the logger at the given log level. Each line written to the writer will be +// printed in the usual way using formatters and hooks. The writer is part of an +// io.Pipe and it is the callers responsibility to close the writer when done. +// This can be used to override the standard library logger easily. func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { return NewEntry(logger).WriterLevel(level) } +// Writer returns an io.Writer that writes to the logger at the info log level func (entry *Entry) Writer() *io.PipeWriter { return entry.WriterLevel(InfoLevel) } +// WriterLevel returns an io.Writer that writes to the logger at the given log level func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { reader, writer := io.Pipe() var printFunc func(args ...interface{}) + // Determine which log function to use based on the specified log level switch level { case TraceLevel: printFunc = entry.Trace @@ -42,23 +52,51 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { printFunc = entry.Print } + // Start a new goroutine to scan the input and write it to the logger using the specified print function. + // It splits the input into chunks of up to 64KB to avoid buffer overflows. go entry.writerScanner(reader, printFunc) + + // Set a finalizer function to close the writer when it is garbage collected runtime.SetFinalizer(writer, writerFinalizer) return writer } +// writerScanner scans the input from the reader and writes it to the logger func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { scanner := bufio.NewScanner(reader) + + // Set the buffer size to the maximum token size to avoid buffer overflows + scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize) + + // Define a split function to split the input into chunks of up to 64KB + chunkSize := bufio.MaxScanTokenSize // 64KB + splitFunc := func(data []byte, atEOF bool) (int, []byte, error) { + if len(data) >= chunkSize { + return chunkSize, data[:chunkSize], nil + } + + return bufio.ScanLines(data, atEOF) + } + + // Use the custom split function to split the input + scanner.Split(splitFunc) + + // Scan the input and write it to the logger using the specified print function for scanner.Scan() { - printFunc(scanner.Text()) + printFunc(strings.TrimRight(scanner.Text(), "\r\n")) } + + // If there was an error while scanning the input, log an error if err := scanner.Err(); err != nil { entry.Errorf("Error while reading from Writer: %s", err) } + + // Close the reader when we are done reader.Close() } +// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected func writerFinalizer(writer *io.PipeWriter) { writer.Close() } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 95d8e59d..b774da88 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -352,9 +352,9 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +364,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +377,9 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,10 +389,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -402,8 +402,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -414,8 +414,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 7880b8f9..84dbd6c7 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -22,9 +22,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -56,7 +56,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -66,7 +66,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -81,8 +81,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -90,10 +90,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) } +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -103,10 +120,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -126,8 +143,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -147,7 +164,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -155,9 +172,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,7 +225,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{} // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -202,9 +244,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -214,10 +256,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -228,7 +270,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -241,7 +283,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -253,7 +295,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -265,7 +307,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -277,7 +319,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -289,7 +331,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -301,7 +343,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -311,7 +353,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,9 +395,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -365,9 +407,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +419,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,9 +431,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -409,7 +451,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -420,7 +462,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -430,9 +472,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -442,10 +484,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -455,8 +497,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -467,7 +509,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -477,7 +519,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -496,10 +538,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -519,9 +561,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -532,9 +574,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -544,7 +586,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -557,7 +599,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -576,7 +618,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -586,7 +628,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -596,8 +638,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -607,7 +649,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -621,7 +663,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -639,7 +681,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -651,7 +693,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -662,7 +704,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -672,8 +714,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -683,8 +725,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -694,7 +736,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -708,7 +750,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -718,7 +760,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -728,7 +770,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -738,7 +780,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 339515b8..b1d94aec 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -30,9 +30,9 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{} // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -43,9 +43,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -98,7 +98,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -109,7 +109,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -119,7 +119,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -134,8 +134,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -146,8 +146,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -155,10 +155,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a return EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -169,7 +203,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -179,7 +213,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -193,10 +227,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -225,8 +259,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -237,8 +271,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -266,10 +300,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -280,7 +314,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -288,10 +322,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -301,7 +385,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -311,7 +395,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -353,7 +437,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -363,7 +447,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -391,9 +475,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -403,10 +487,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -416,10 +500,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -429,9 +513,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -442,7 +526,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -455,7 +539,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -468,7 +552,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -481,7 +565,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -493,7 +577,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -505,7 +589,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -517,7 +601,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -529,7 +613,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -541,7 +625,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -553,7 +637,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -565,7 +649,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -577,7 +661,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -589,7 +673,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -599,7 +683,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -609,7 +693,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -651,7 +735,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -693,9 +777,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -705,9 +789,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -717,9 +801,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -729,9 +813,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -741,9 +825,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -753,9 +837,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -765,9 +849,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -777,9 +861,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -805,7 +889,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -815,7 +899,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -826,7 +910,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -837,7 +921,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -847,9 +931,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -859,10 +943,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -872,10 +956,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -885,9 +969,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -897,8 +981,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -908,8 +992,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -920,7 +1004,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) b // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -931,7 +1015,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -941,7 +1025,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -951,7 +1035,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -979,10 +1063,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -992,10 +1076,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1024,9 +1108,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1037,9 +1121,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1050,9 +1134,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1063,9 +1147,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1075,7 +1159,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1088,7 +1172,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1098,7 +1182,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1108,7 +1192,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1139,7 +1223,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1149,7 +1233,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1159,7 +1243,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1169,7 +1253,7 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1179,8 +1263,8 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{} // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1190,8 +1274,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1201,7 +1285,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1214,7 +1298,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1228,7 +1312,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1239,7 +1323,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1265,7 +1349,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1277,7 +1361,7 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1289,7 +1373,7 @@ func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1300,7 +1384,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg str // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1311,7 +1395,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1321,7 +1405,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1331,8 +1415,8 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1342,8 +1426,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1353,8 +1437,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) b // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1364,8 +1448,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1375,7 +1459,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1388,7 +1472,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1402,7 +1486,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1413,7 +1497,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1423,7 +1507,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1433,7 +1517,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1443,7 +1527,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1453,7 +1537,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1463,7 +1547,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1473,7 +1557,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 75944878..00df62a0 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 2924cf3a..a55d1bba 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -75,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool { return bytes.Equal(exp, act) } +// copyExportedFields iterates downward through nested data structures and creates a copy +// that only contains the exported struct fields. +func copyExportedFields(expected interface{}) interface{} { + if isNil(expected) { + return expected + } + + expectedType := reflect.TypeOf(expected) + expectedKind := expectedType.Kind() + expectedValue := reflect.ValueOf(expected) + + switch expectedKind { + case reflect.Struct: + result := reflect.New(expectedType).Elem() + for i := 0; i < expectedType.NumField(); i++ { + field := expectedType.Field(i) + isExported := field.IsExported() + if isExported { + fieldValue := expectedValue.Field(i) + if isNil(fieldValue) || isNil(fieldValue.Interface()) { + continue + } + newValue := copyExportedFields(fieldValue.Interface()) + result.Field(i).Set(reflect.ValueOf(newValue)) + } + } + return result.Interface() + + case reflect.Ptr: + result := reflect.New(expectedType.Elem()) + unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) + result.Elem().Set(reflect.ValueOf(unexportedRemoved)) + return result.Interface() + + case reflect.Array, reflect.Slice: + result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) + for i := 0; i < expectedValue.Len(); i++ { + index := expectedValue.Index(i) + if isNil(index) { + continue + } + unexportedRemoved := copyExportedFields(index.Interface()) + result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + case reflect.Map: + result := reflect.MakeMap(expectedType) + for _, k := range expectedValue.MapKeys() { + index := expectedValue.MapIndex(k) + unexportedRemoved := copyExportedFields(index.Interface()) + result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + default: + return expected + } +} + +// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are +// considered equal. This comparison of only exported fields is applied recursively to nested data +// structures. +// +// This function does no assertion of any kind. +func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { + expectedCleaned := copyExportedFields(expected) + actualCleaned := copyExportedFields(actual) + return ObjectsAreEqualValues(expectedCleaned, actualCleaned) +} + // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected, actual interface{}) bool { @@ -271,7 +342,7 @@ type labeledContent struct { // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // -// \t{{label}}:{{align_spaces}}\t{{content}}\n +// \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this @@ -294,7 +365,7 @@ func labeledOutput(content ...labeledContent) string { // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -326,7 +397,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -367,7 +438,7 @@ func validateEqualArgs(expected, actual interface{}) error { // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -387,7 +458,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -455,7 +526,7 @@ func truncatingFormat(data interface{}) string { // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -473,9 +544,53 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) + } + + if aType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) + } + + if bType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) + } + + expected = copyExportedFields(expected) + actual = copyExportedFields(actual) + + if !ObjectsAreEqualValues(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true +} + // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -494,7 +609,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if !isNil(object) { return true @@ -540,7 +655,7 @@ func isNil(object interface{}) bool { // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if isNil(object) { return true @@ -583,7 +698,7 @@ func isEmpty(object interface{}) bool { // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -600,9 +715,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := !isEmpty(object) if !pass { @@ -631,7 +746,7 @@ func getLen(x interface{}) (ok bool, length int) { // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -649,7 +764,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { if !value { if h, ok := t.(tHelper); ok { @@ -664,7 +779,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { if value { if h, ok := t.(tHelper); ok { @@ -679,7 +794,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -702,7 +817,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -761,9 +876,9 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -784,9 +899,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -794,10 +909,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) } return true @@ -807,7 +922,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -863,7 +978,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1048,7 +1163,7 @@ func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1064,7 +1179,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1085,7 +1200,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1105,7 +1220,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1120,7 +1235,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1136,7 +1251,7 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1195,7 +1310,7 @@ func toFloat(x interface{}) (float64, bool) { // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1368,10 +1483,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if err != nil { if h, ok := t.(tHelper); ok { @@ -1385,10 +1500,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1403,8 +1518,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1426,8 +1541,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1460,8 +1575,8 @@ func matchRegexp(rx interface{}, str interface{}) bool { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1478,8 +1593,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1591,7 +1706,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1714,7 +1829,7 @@ type tHelper interface { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1744,10 +1859,93 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } } +// CollectT implements the TestingT interface and collects all errors. +type CollectT struct { + errors []error +} + +// Errorf collects the error. +func (c *CollectT) Errorf(format string, args ...interface{}) { + c.errors = append(c.errors, fmt.Errorf(format, args...)) +} + +// FailNow panics. +func (c *CollectT) FailNow() { + panic("Assertion failed") +} + +// Reset clears the collected errors. +func (c *CollectT) Reset() { + c.errors = nil +} + +// Copy copies the collected errors to the supplied t. +func (c *CollectT) Copy(t TestingT) { + if tt, ok := t.(tHelper); ok { + tt.Helper() + } + for _, err := range c.errors { + t.Errorf("%v", err) + } +} + +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + collect := new(CollectT) + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + collect.Copy(t) + return Fail(t, "Condition never satisfied", msgAndArgs...) + case <-tick: + tick = nil + collect.Reset() + go func() { + condition(collect) + ch <- len(collect.errors) == 0 + }() + case v := <-ch: + if v { + return true + } + tick = ticker.C + } + } +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index c9dccc4d..4953981d 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -1,39 +1,40 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // -// Example Usage +// # Example Usage // // The following is a complete example using assert in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// assert.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } // // if you assert many times, use the format below: // -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// func TestSomething(t *testing.T) { -// assert := assert.New(t) +// func TestSomething(t *testing.T) { +// assert := assert.New(t) // -// var a string = "Hello" -// var b string = "Hello" +// var a string = "Hello" +// var b string = "Hello" // -// assert.Equal(a, b, "The two words should be the same.") -// } +// assert.Equal(a, b, "The two words should be the same.") +// } // -// Assertions +// # Assertions // // Assertions allow you to easily write test code, and are global funcs in the `assert` package. // All assertion functions take, as the first argument, the `*testing.T` object provided by the diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 4ed341dd..d8038c28 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) ( // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -124,7 +124,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -144,7 +144,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go index 169de392..96843472 100644 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -1,24 +1,25 @@ // Package require implements the same assertions as the `assert` package but // stops test execution when a test fails. // -// Example Usage +// # Example Usage // // The following is a complete example using require in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/require" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// require.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// require.Equal(t, a, b, "The two words should be the same.") // -// Assertions +// } +// +// # Assertions // // The `require` package have same global functions as in the `assert` package, // but instead of returning a boolean result they call `t.FailNow()`. diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 880853f5..63f85214 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -37,9 +37,9 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -53,9 +53,9 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -123,7 +123,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -137,7 +137,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -150,7 +150,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -168,8 +168,8 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,8 +183,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -195,10 +195,50 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args t.FailNow() } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -212,7 +252,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -225,7 +265,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -242,10 +282,10 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -283,8 +323,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -298,8 +338,8 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -336,10 +376,10 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,7 +393,7 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +404,66 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t t.FailNow() } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -380,7 +476,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -393,7 +489,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -450,7 +546,7 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -463,7 +559,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -500,9 +596,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -515,10 +611,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -531,10 +627,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -547,9 +643,9 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -563,7 +659,7 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -579,7 +675,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -595,7 +691,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -611,7 +707,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -626,7 +722,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -641,7 +737,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -656,7 +752,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -671,7 +767,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -686,7 +782,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -701,7 +797,7 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -716,7 +812,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -731,7 +827,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -746,7 +842,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -759,7 +855,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -772,7 +868,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -829,7 +925,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -886,9 +982,9 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -901,9 +997,9 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -916,9 +1012,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -931,9 +1027,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -946,9 +1042,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -961,9 +1057,9 @@ func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -976,9 +1072,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -991,9 +1087,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1028,7 +1124,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1041,7 +1137,7 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1055,7 +1151,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1069,7 +1165,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1082,9 +1178,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,10 +1193,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1113,10 +1209,10 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1129,9 +1225,9 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1144,8 +1240,8 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1158,8 +1254,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1173,7 +1269,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1187,7 +1283,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1200,7 +1296,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1213,7 +1309,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1250,10 +1346,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1266,10 +1362,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1307,9 +1403,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1323,9 +1419,9 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1339,9 +1435,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1355,9 +1451,9 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1370,7 +1466,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1386,7 +1482,7 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1399,7 +1495,7 @@ func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAnd // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1412,7 +1508,7 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1452,7 +1548,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1465,7 +1561,7 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1478,7 +1574,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1491,7 +1587,7 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1504,8 +1600,8 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1518,8 +1614,8 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1532,7 +1628,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1548,7 +1644,7 @@ func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1565,7 +1661,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1579,7 +1675,7 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1614,7 +1710,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1629,7 +1725,7 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1644,7 +1740,7 @@ func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAn // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1658,7 +1754,7 @@ func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1672,7 +1768,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1685,7 +1781,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1698,8 +1794,8 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1712,8 +1808,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1726,8 +1822,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1740,8 +1836,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1754,7 +1850,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1770,7 +1866,7 @@ func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1787,7 +1883,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1801,7 +1897,7 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1814,7 +1910,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1827,7 +1923,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1840,7 +1936,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1853,7 +1949,7 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1866,7 +1962,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1879,7 +1975,7 @@ func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, m // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 960bf6f2..3b5b0933 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -31,9 +31,9 @@ func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...inte // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -44,9 +44,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -99,7 +99,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -110,7 +110,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -120,7 +120,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -135,8 +135,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -147,8 +147,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -156,10 +156,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -170,7 +204,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -180,7 +214,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -194,10 +228,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -226,8 +260,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -238,8 +272,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -267,10 +301,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -281,7 +315,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -289,10 +323,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -302,7 +386,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -312,7 +396,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -354,7 +438,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -364,7 +448,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -392,9 +476,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -404,10 +488,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -417,10 +501,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -430,9 +514,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -443,7 +527,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -456,7 +540,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -469,7 +553,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -482,7 +566,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -494,7 +578,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -506,7 +590,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -518,7 +602,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -530,7 +614,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -542,7 +626,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -554,7 +638,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -566,7 +650,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -578,7 +662,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -590,7 +674,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -600,7 +684,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -610,7 +694,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -652,7 +736,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -694,9 +778,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -706,9 +790,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -718,9 +802,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -730,9 +814,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -742,9 +826,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -754,9 +838,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -766,9 +850,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -778,9 +862,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -806,7 +890,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -816,7 +900,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -827,7 +911,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -838,7 +922,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -848,9 +932,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -860,10 +944,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -873,10 +957,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -886,9 +970,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -898,8 +982,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -909,8 +993,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -921,7 +1005,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -932,7 +1016,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -942,7 +1026,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -952,7 +1036,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -980,10 +1064,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -993,10 +1077,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1025,9 +1109,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1038,9 +1122,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1051,9 +1135,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1064,9 +1148,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1076,7 +1160,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1089,7 +1173,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1099,7 +1183,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1109,7 +1193,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1140,7 +1224,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1150,7 +1234,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1160,7 +1244,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1170,7 +1254,7 @@ func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{} // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1180,8 +1264,8 @@ func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...inte // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1191,8 +1275,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1202,7 +1286,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1215,7 +1299,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1229,7 +1313,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1240,7 +1324,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1266,7 +1350,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1278,7 +1362,7 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1290,7 +1374,7 @@ func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, m // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1301,7 +1385,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1312,7 +1396,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFun // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1322,7 +1406,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFu // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1332,8 +1416,8 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1343,8 +1427,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1354,8 +1438,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1365,8 +1449,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1376,7 +1460,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1389,7 +1473,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1403,7 +1487,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1414,7 +1498,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1424,7 +1508,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1434,7 +1518,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1444,7 +1528,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1454,7 +1538,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1464,7 +1548,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1474,7 +1558,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/suite/doc.go b/vendor/github.com/stretchr/testify/suite/doc.go index f91a245d..8d55a3aa 100644 --- a/vendor/github.com/stretchr/testify/suite/doc.go +++ b/vendor/github.com/stretchr/testify/suite/doc.go @@ -29,37 +29,38 @@ // Suite object has assertion methods. // // A crude example: -// // Basic imports -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// "github.com/stretchr/testify/suite" -// ) // -// // Define the suite, and absorb the built-in basic suite -// // functionality from testify - including a T() method which -// // returns the current testing context -// type ExampleTestSuite struct { -// suite.Suite -// VariableThatShouldStartAtFive int -// } +// // Basic imports +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/suite" +// ) // -// // Make sure that VariableThatShouldStartAtFive is set to five -// // before each test -// func (suite *ExampleTestSuite) SetupTest() { -// suite.VariableThatShouldStartAtFive = 5 -// } +// // Define the suite, and absorb the built-in basic suite +// // functionality from testify - including a T() method which +// // returns the current testing context +// type ExampleTestSuite struct { +// suite.Suite +// VariableThatShouldStartAtFive int +// } // -// // All methods that begin with "Test" are run as tests within a -// // suite. -// func (suite *ExampleTestSuite) TestExample() { -// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) -// suite.Equal(5, suite.VariableThatShouldStartAtFive) -// } +// // Make sure that VariableThatShouldStartAtFive is set to five +// // before each test +// func (suite *ExampleTestSuite) SetupTest() { +// suite.VariableThatShouldStartAtFive = 5 +// } // -// // In order for 'go test' to run this suite, we need to create -// // a normal test function and pass our suite to suite.Run -// func TestExampleTestSuite(t *testing.T) { -// suite.Run(t, new(ExampleTestSuite)) -// } +// // All methods that begin with "Test" are run as tests within a +// // suite. +// func (suite *ExampleTestSuite) TestExample() { +// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) +// suite.Equal(5, suite.VariableThatShouldStartAtFive) +// } +// +// // In order for 'go test' to run this suite, we need to create +// // a normal test function and pass our suite to suite.Run +// func TestExampleTestSuite(t *testing.T) { +// suite.Run(t, new(ExampleTestSuite)) +// } package suite diff --git a/vendor/github.com/ztrue/tracerr/.travis.yml b/vendor/github.com/ztrue/tracerr/.travis.yml index 6fa4d856..6e20d5cd 100644 --- a/vendor/github.com/ztrue/tracerr/.travis.yml +++ b/vendor/github.com/ztrue/tracerr/.travis.yml @@ -1,7 +1,6 @@ language: go before_install: - - go get github.com/logrusorgru/aurora - go get github.com/mattn/goveralls go: diff --git a/vendor/github.com/ztrue/tracerr/CHANGELOG.md b/vendor/github.com/ztrue/tracerr/CHANGELOG.md index b8560c97..7a8a2a84 100644 --- a/vendor/github.com/ztrue/tracerr/CHANGELOG.md +++ b/vendor/github.com/ztrue/tracerr/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.4.0] - 2023-05-21 + +### Changed + +- `github.com/logrusorgru/aurora` dependency removed. +- Deprecated `ioutil.ReadFile` replaced with `os.ReadFile`. + ## [0.3.0] - 2019-03-15 ### Added diff --git a/vendor/github.com/ztrue/tracerr/colors.go b/vendor/github.com/ztrue/tracerr/colors.go new file mode 100644 index 00000000..c96999e9 --- /dev/null +++ b/vendor/github.com/ztrue/tracerr/colors.go @@ -0,0 +1,27 @@ +package tracerr + +import ( + "fmt" +) + +// Colorize outputs using [ANSI Escape Codes](https://en.wikipedia.org/wiki/ANSI_escape_code) + +func color(code int, in string) string { + return fmt.Sprintf("\x1b[%dm%s\x1b[0m", code, in) +} + +func bold(in string) string { + return color(1, in) +} + +func black(in string) string { + return color(30, in) +} + +func red(in string) string { + return color(31, in) +} + +func yellow(in string) string { + return color(33, in) +} diff --git a/vendor/github.com/ztrue/tracerr/print.go b/vendor/github.com/ztrue/tracerr/print.go index 2249ace6..c6d36406 100644 --- a/vendor/github.com/ztrue/tracerr/print.go +++ b/vendor/github.com/ztrue/tracerr/print.go @@ -2,11 +2,10 @@ package tracerr import ( "fmt" - "io/ioutil" + "os" + "strconv" "strings" "sync" - - "github.com/logrusorgru/aurora" ) // DefaultLinesAfter is number of source lines after traced line to display. @@ -26,7 +25,7 @@ func Print(err error) { // PrintSource prints error message with stack trace and source fragments. // -// By default 6 lines of source code will be printed, +// By default, 6 lines of source code will be printed, // see DefaultLinesAfter and DefaultLinesBefore. // // Pass a single number to specify a total number of source lines. @@ -95,7 +94,7 @@ func readLines(path string) ([]string, error) { return lines, nil } - b, err := ioutil.ReadFile(path) + b, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("tracerr: file %s not found", path) } @@ -111,7 +110,7 @@ func sourceRows(rows []string, frame Frame, before, after int, colorized bool) [ if err != nil { message := err.Error() if colorized { - message = aurora.Brown(message).String() + message = yellow(message) } return append(rows, message, "") } @@ -121,7 +120,7 @@ func sourceRows(rows []string, frame Frame, before, after int, colorized bool) [ len(lines), frame.Line, ) if colorized { - message = aurora.Brown(message).String() + message = yellow(message) } return append(rows, message, "") } @@ -136,14 +135,14 @@ func sourceRows(rows []string, frame Frame, before, after int, colorized bool) [ var message string // TODO Pad to the same length. if i == frame.Line-1 { - message = fmt.Sprintf("%d\t%s", i+1, string(line)) + message = fmt.Sprintf("%d\t%s", i+1, line) if colorized { - message = aurora.Red(message).String() + message = red(message) } } else if colorized { - message = aurora.Sprintf("%d\t%s", aurora.Black(i+1), string(line)) + message = fmt.Sprintf("%s\t%s", black(strconv.Itoa(i+1)), line) } else { - message = fmt.Sprintf("%d\t%s", i+1, string(line)) + message = fmt.Sprintf("%d\t%s", i+1, line) } rows = append(rows, message) } @@ -172,7 +171,7 @@ func sprint(err error, nums []int, colorized bool) string { for _, frame := range frames { message := frame.String() if colorized { - message = aurora.Bold(message).String() + message = bold(message) } rows = append(rows, message) if withSource { diff --git a/vendor/golang.org/x/crypto/PATENTS b/vendor/golang.org/x/crypto/PATENTS deleted file mode 100644 index 73309904..00000000 --- a/vendor/golang.org/x/crypto/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google 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, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go deleted file mode 100644 index a4d1919a..00000000 --- a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package terminal provides support functions for dealing with terminals, as -// commonly found on UNIX systems. -// -// Deprecated: this package moved to golang.org/x/term. -package terminal - -import ( - "io" - - "golang.org/x/term" -) - -// EscapeCodes contains escape sequences that can be written to the terminal in -// order to achieve different styles of text. -type EscapeCodes = term.EscapeCodes - -// Terminal contains the state for running a VT100 terminal that is capable of -// reading lines of input. -type Terminal = term.Terminal - -// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is -// a local terminal, that terminal must first have been put into raw mode. -// prompt is a string that is written at the start of each input line (i.e. -// "> "). -func NewTerminal(c io.ReadWriter, prompt string) *Terminal { - return term.NewTerminal(c, prompt) -} - -// ErrPasteIndicator may be returned from ReadLine as the error, in addition -// to valid line data. It indicates that bracketed paste mode is enabled and -// that the returned line consists only of pasted data. Programs may wish to -// interpret pasted data more literally than typed data. -var ErrPasteIndicator = term.ErrPasteIndicator - -// State contains the state of a terminal. -type State = term.State - -// IsTerminal returns whether the given file descriptor is a terminal. -func IsTerminal(fd int) bool { - return term.IsTerminal(fd) -} - -// ReadPassword reads a line of input from a terminal without local echo. This -// is commonly used for inputting passwords and other sensitive data. The slice -// returned does not include the \n. -func ReadPassword(fd int) ([]byte, error) { - return term.ReadPassword(fd) -} - -// MakeRaw puts the terminal connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be -// restored. -func MakeRaw(fd int) (*State, error) { - return term.MakeRaw(fd) -} - -// Restore restores the terminal connected to the given file descriptor to a -// previous state. -func Restore(fd int, oldState *State) error { - return term.Restore(fd, oldState) -} - -// GetState returns the current state of a terminal which may be useful to -// restore the terminal after a signal. -func GetState(fd int) (*State, error) { - return term.GetState(fd) -} - -// GetSize returns the dimensions of the given terminal. -func GetSize(fd int) (width, height int, err error) { - return term.GetSize(fd) -} diff --git a/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go index 2681af35..150f887e 100644 --- a/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go +++ b/vendor/golang.org/x/mod/internal/lazyregexp/lazyre.go @@ -13,7 +13,7 @@ import ( "sync" ) -// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be +// Regexp is a wrapper around [regexp.Regexp], where the underlying regexp will be // compiled the first time it is needed. type Regexp struct { str string diff --git a/vendor/golang.org/x/mod/modfile/print.go b/vendor/golang.org/x/mod/modfile/print.go index 524f9302..2a0123d4 100644 --- a/vendor/golang.org/x/mod/modfile/print.go +++ b/vendor/golang.org/x/mod/modfile/print.go @@ -16,7 +16,13 @@ import ( func Format(f *FileSyntax) []byte { pr := &printer{} pr.file(f) - return pr.Bytes() + + // remove trailing blank lines + b := pr.Bytes() + for len(b) > 0 && b[len(b)-1] == '\n' && (len(b) == 1 || b[len(b)-2] == '\n') { + b = b[:len(b)-1] + } + return b } // A printer collects the state during printing of a file or expression. @@ -59,7 +65,11 @@ func (p *printer) newline() { } p.trim() - p.printf("\n") + if b := p.Bytes(); len(b) == 0 || (len(b) >= 2 && b[len(b)-1] == '\n' && b[len(b)-2] == '\n') { + // skip the blank line at top of file or after a blank line + } else { + p.printf("\n") + } for i := 0; i < p.margin; i++ { p.printf("\t") } diff --git a/vendor/golang.org/x/mod/modfile/read.go b/vendor/golang.org/x/mod/modfile/read.go index a503bc21..5b5bb5e1 100644 --- a/vendor/golang.org/x/mod/modfile/read.go +++ b/vendor/golang.org/x/mod/modfile/read.go @@ -65,7 +65,7 @@ type Comments struct { } // Comment returns the receiver. This isn't useful by itself, but -// a Comments struct is embedded into all the expression +// a [Comments] struct is embedded into all the expression // implementation types, and this gives each of those a Comment // method to satisfy the Expr interface. func (c *Comments) Comment() *Comments { diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go index 6bcde8fa..35fd1f53 100644 --- a/vendor/golang.org/x/mod/modfile/rule.go +++ b/vendor/golang.org/x/mod/modfile/rule.go @@ -5,17 +5,17 @@ // Package modfile implements a parser and formatter for go.mod files. // // The go.mod syntax is described in -// https://golang.org/cmd/go/#hdr-The_go_mod_file. +// https://pkg.go.dev/cmd/go/#hdr-The_go_mod_file. // -// The Parse and ParseLax functions both parse a go.mod file and return an +// The [Parse] and [ParseLax] functions both parse a go.mod file and return an // abstract syntax tree. ParseLax ignores unknown statements and may be used to // parse go.mod files that may have been developed with newer versions of Go. // -// The File struct returned by Parse and ParseLax represent an abstract -// go.mod file. File has several methods like AddNewRequire and DropReplace -// that can be used to programmatically edit a file. +// The [File] struct returned by Parse and ParseLax represent an abstract +// go.mod file. File has several methods like [File.AddNewRequire] and +// [File.DropReplace] that can be used to programmatically edit a file. // -// The Format function formats a File back to a byte slice which can be +// The [Format] function formats a File back to a byte slice which can be // written to a file. package modfile @@ -35,12 +35,13 @@ import ( // A File is the parsed, interpreted form of a go.mod file. type File struct { - Module *Module - Go *Go - Require []*Require - Exclude []*Exclude - Replace []*Replace - Retract []*Retract + Module *Module + Go *Go + Toolchain *Toolchain + Require []*Require + Exclude []*Exclude + Replace []*Replace + Retract []*Retract Syntax *FileSyntax } @@ -58,6 +59,12 @@ type Go struct { Syntax *Line } +// A Toolchain is the toolchain statement. +type Toolchain struct { + Name string // "go1.21rc1" + Syntax *Line +} + // An Exclude is a single exclude statement. type Exclude struct { Mod module.Version @@ -219,7 +226,7 @@ var dontFixRetract VersionFixer = func(_, vers string) (string, error) { // data is the content of the file. // // fix is an optional function that canonicalizes module versions. -// If fix is nil, all module versions must be canonical (module.CanonicalVersion +// If fix is nil, all module versions must be canonical ([module.CanonicalVersion] // must return the same string). func Parse(file string, data []byte, fix VersionFixer) (*File, error) { return parseToFile(file, data, fix, true) @@ -296,9 +303,13 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse return f, nil } -var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)$`) +var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`) var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`) +// Toolchains must be named beginning with `go1`, +// like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted. +var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`) + func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) { // If strict is false, this module is a dependency. // We ignore all unknown directives as well as main-module-only @@ -356,7 +367,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a } } if !fixed { - errorf("invalid go version '%s': must match format 1.23", args[0]) + errorf("invalid go version '%s': must match format 1.23.0", args[0]) return } } @@ -364,6 +375,21 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a f.Go = &Go{Syntax: line} f.Go.Version = args[0] + case "toolchain": + if f.Toolchain != nil { + errorf("repeated toolchain statement") + return + } + if len(args) != 1 { + errorf("toolchain directive expects exactly one argument") + return + } else if strict && !ToolchainRE.MatchString(args[0]) { + errorf("invalid toolchain version '%s': must match format go1.23.0 or local", args[0]) + return + } + f.Toolchain = &Toolchain{Syntax: line} + f.Toolchain.Name = args[0] + case "module": if f.Module != nil { errorf("repeated module statement") @@ -516,7 +542,7 @@ func parseReplace(filename string, line *Line, verb string, args []string, fix V if strings.Contains(ns, "@") { return nil, errorf("replacement module must match format 'path version', not 'path@version'") } - return nil, errorf("replacement module without version must be directory path (rooted or starting with ./ or ../)") + return nil, errorf("replacement module without version must be directory path (rooted or starting with . or ..)") } if filepath.Separator == '/' && strings.Contains(ns, `\`) { return nil, errorf("replacement directory appears to be Windows path (on a non-windows system)") @@ -529,7 +555,6 @@ func parseReplace(filename string, line *Line, verb string, args []string, fix V } if IsDirectoryPath(ns) { return nil, errorf("replacement module directory path %q cannot have version", ns) - } } return &Replace{ @@ -612,6 +637,22 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, f.Go = &Go{Syntax: line} f.Go.Version = args[0] + case "toolchain": + if f.Toolchain != nil { + errorf("repeated toolchain statement") + return + } + if len(args) != 1 { + errorf("toolchain directive expects exactly one argument") + return + } else if !ToolchainRE.MatchString(args[0]) { + errorf("invalid toolchain version '%s': must match format go1.23 or local", args[0]) + return + } + + f.Toolchain = &Toolchain{Syntax: line} + f.Toolchain.Name = args[0] + case "use": if len(args) != 1 { errorf("usage: %s local/dir", verb) @@ -637,14 +678,15 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, } } -// IsDirectoryPath reports whether the given path should be interpreted -// as a directory path. Just like on the go command line, relative paths +// IsDirectoryPath reports whether the given path should be interpreted as a directory path. +// Just like on the go command line, relative paths starting with a '.' or '..' path component // and rooted paths are directory paths; the rest are module paths. func IsDirectoryPath(ns string) bool { // Because go.mod files can move from one system to another, // we check all known path syntaxes, both Unix and Windows. - return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") || - strings.HasPrefix(ns, `.\`) || strings.HasPrefix(ns, `..\`) || strings.HasPrefix(ns, `\`) || + return ns == "." || strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, `.\`) || + ns == ".." || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, `..\`) || + strings.HasPrefix(ns, "/") || strings.HasPrefix(ns, `\`) || len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':' } @@ -881,7 +923,7 @@ func (f *File) Format() ([]byte, error) { } // Cleanup cleans up the file f after any edit operations. -// To avoid quadratic behavior, modifications like DropRequire +// To avoid quadratic behavior, modifications like [File.DropRequire] // clear the entry but do not remove it from the slice. // Cleanup cleans out all the cleared entries. func (f *File) Cleanup() { @@ -926,7 +968,7 @@ func (f *File) Cleanup() { func (f *File) AddGoStmt(version string) error { if !GoVersionRE.MatchString(version) { - return fmt.Errorf("invalid language version string %q", version) + return fmt.Errorf("invalid language version %q", version) } if f.Go == nil { var hint Expr @@ -944,6 +986,44 @@ func (f *File) AddGoStmt(version string) error { return nil } +// DropGoStmt deletes the go statement from the file. +func (f *File) DropGoStmt() { + if f.Go != nil { + f.Go.Syntax.markRemoved() + f.Go = nil + } +} + +// DropToolchainStmt deletes the toolchain statement from the file. +func (f *File) DropToolchainStmt() { + if f.Toolchain != nil { + f.Toolchain.Syntax.markRemoved() + f.Toolchain = nil + } +} + +func (f *File) AddToolchainStmt(name string) error { + if !ToolchainRE.MatchString(name) { + return fmt.Errorf("invalid toolchain name %q", name) + } + if f.Toolchain == nil { + var hint Expr + if f.Go != nil && f.Go.Syntax != nil { + hint = f.Go.Syntax + } else if f.Module != nil && f.Module.Syntax != nil { + hint = f.Module.Syntax + } + f.Toolchain = &Toolchain{ + Name: name, + Syntax: f.Syntax.addLine(hint, "toolchain", name), + } + } else { + f.Toolchain.Name = name + f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name) + } + return nil +} + // AddRequire sets the first require line for path to version vers, // preserving any existing comments for that line and removing all // other lines for path. @@ -995,8 +1075,8 @@ func (f *File) AddNewRequire(path, vers string, indirect bool) { // The requirements in req must specify at most one distinct version for each // module path. // -// If any existing requirements may be removed, the caller should call Cleanup -// after all edits are complete. +// If any existing requirements may be removed, the caller should call +// [File.Cleanup] after all edits are complete. func (f *File) SetRequire(req []*Require) { type elem struct { version string @@ -1387,13 +1467,21 @@ func (f *File) DropRetract(vi VersionInterval) error { func (f *File) SortBlocks() { f.removeDups() // otherwise sorting is unsafe + // semanticSortForExcludeVersionV is the Go version (plus leading "v") at which + // lines in exclude blocks start to use semantic sort instead of lexicographic sort. + // See go.dev/issue/60028. + const semanticSortForExcludeVersionV = "v1.21" + useSemanticSortForExclude := f.Go != nil && semver.Compare("v"+f.Go.Version, semanticSortForExcludeVersionV) >= 0 + for _, stmt := range f.Syntax.Stmt { block, ok := stmt.(*LineBlock) if !ok { continue } less := lineLess - if block.Token[0] == "retract" { + if block.Token[0] == "exclude" && useSemanticSortForExclude { + less = lineExcludeLess + } else if block.Token[0] == "retract" { less = lineRetractLess } sort.SliceStable(block.Line, func(i, j int) bool { @@ -1496,6 +1584,22 @@ func lineLess(li, lj *Line) bool { return len(li.Token) < len(lj.Token) } +// lineExcludeLess reports whether li should be sorted before lj for lines in +// an "exclude" block. +func lineExcludeLess(li, lj *Line) bool { + if len(li.Token) != 2 || len(lj.Token) != 2 { + // Not a known exclude specification. + // Fall back to sorting lexicographically. + return lineLess(li, lj) + } + // An exclude specification has two tokens: ModulePath and Version. + // Compare module path by string order and version by semver rules. + if pi, pj := li.Token[0], lj.Token[0]; pi != pj { + return pi < pj + } + return semver.Compare(li.Token[1], lj.Token[1]) < 0 +} + // lineRetractLess returns whether li should be sorted before lj for lines in // a "retract" block. It treats each line as a version interval. Single versions // are compared as if they were intervals with the same low and high version. diff --git a/vendor/golang.org/x/mod/modfile/work.go b/vendor/golang.org/x/mod/modfile/work.go index 0c0e5215..d7b99376 100644 --- a/vendor/golang.org/x/mod/modfile/work.go +++ b/vendor/golang.org/x/mod/modfile/work.go @@ -12,9 +12,10 @@ import ( // A WorkFile is the parsed, interpreted form of a go.work file. type WorkFile struct { - Go *Go - Use []*Use - Replace []*Replace + Go *Go + Toolchain *Toolchain + Use []*Use + Replace []*Replace Syntax *FileSyntax } @@ -33,7 +34,7 @@ type Use struct { // data is the content of the file. // // fix is an optional function that canonicalizes module versions. -// If fix is nil, all module versions must be canonical (module.CanonicalVersion +// If fix is nil, all module versions must be canonical ([module.CanonicalVersion] // must return the same string). func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) { fs, err := parse(file, data) @@ -82,7 +83,7 @@ func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) { } // Cleanup cleans up the file f after any edit operations. -// To avoid quadratic behavior, modifications like DropRequire +// To avoid quadratic behavior, modifications like [WorkFile.DropRequire] // clear the entry but do not remove it from the slice. // Cleanup cleans out all the cleared entries. func (f *WorkFile) Cleanup() { @@ -109,7 +110,7 @@ func (f *WorkFile) Cleanup() { func (f *WorkFile) AddGoStmt(version string) error { if !GoVersionRE.MatchString(version) { - return fmt.Errorf("invalid language version string %q", version) + return fmt.Errorf("invalid language version %q", version) } if f.Go == nil { stmt := &Line{Token: []string{"go", version}} @@ -117,7 +118,7 @@ func (f *WorkFile) AddGoStmt(version string) error { Version: version, Syntax: stmt, } - // Find the first non-comment-only block that's and add + // Find the first non-comment-only block and add // the go statement before it. That will keep file comments at the top. i := 0 for i = 0; i < len(f.Syntax.Stmt); i++ { @@ -133,6 +134,56 @@ func (f *WorkFile) AddGoStmt(version string) error { return nil } +func (f *WorkFile) AddToolchainStmt(name string) error { + if !ToolchainRE.MatchString(name) { + return fmt.Errorf("invalid toolchain name %q", name) + } + if f.Toolchain == nil { + stmt := &Line{Token: []string{"toolchain", name}} + f.Toolchain = &Toolchain{ + Name: name, + Syntax: stmt, + } + // Find the go line and add the toolchain line after it. + // Or else find the first non-comment-only block and add + // the toolchain line before it. That will keep file comments at the top. + i := 0 + for i = 0; i < len(f.Syntax.Stmt); i++ { + if line, ok := f.Syntax.Stmt[i].(*Line); ok && len(line.Token) > 0 && line.Token[0] == "go" { + i++ + goto Found + } + } + for i = 0; i < len(f.Syntax.Stmt); i++ { + if _, ok := f.Syntax.Stmt[i].(*CommentBlock); !ok { + break + } + } + Found: + f.Syntax.Stmt = append(append(f.Syntax.Stmt[:i:i], stmt), f.Syntax.Stmt[i:]...) + } else { + f.Toolchain.Name = name + f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name) + } + return nil +} + +// DropGoStmt deletes the go statement from the file. +func (f *WorkFile) DropGoStmt() { + if f.Go != nil { + f.Go.Syntax.markRemoved() + f.Go = nil + } +} + +// DropToolchainStmt deletes the toolchain statement from the file. +func (f *WorkFile) DropToolchainStmt() { + if f.Toolchain != nil { + f.Toolchain.Syntax.markRemoved() + f.Toolchain = nil + } +} + func (f *WorkFile) AddUse(diskPath, modulePath string) error { need := true for _, d := range f.Use { diff --git a/vendor/golang.org/x/mod/module/module.go b/vendor/golang.org/x/mod/module/module.go index e9dec6e6..2a364b22 100644 --- a/vendor/golang.org/x/mod/module/module.go +++ b/vendor/golang.org/x/mod/module/module.go @@ -4,7 +4,7 @@ // Package module defines the module.Version type along with support code. // -// The module.Version type is a simple Path, Version pair: +// The [module.Version] type is a simple Path, Version pair: // // type Version struct { // Path string @@ -12,7 +12,7 @@ // } // // There are no restrictions imposed directly by use of this structure, -// but additional checking functions, most notably Check, verify that +// but additional checking functions, most notably [Check], verify that // a particular path, version pair is valid. // // # Escaped Paths @@ -140,7 +140,7 @@ type ModuleError struct { Err error } -// VersionError returns a ModuleError derived from a Version and error, +// VersionError returns a [ModuleError] derived from a [Version] and error, // or err itself if it is already such an error. func VersionError(v Version, err error) error { var mErr *ModuleError @@ -169,7 +169,7 @@ func (e *ModuleError) Unwrap() error { return e.Err } // An InvalidVersionError indicates an error specific to a version, with the // module path unknown or specified externally. // -// A ModuleError may wrap an InvalidVersionError, but an InvalidVersionError +// A [ModuleError] may wrap an InvalidVersionError, but an InvalidVersionError // must not wrap a ModuleError. type InvalidVersionError struct { Version string @@ -193,8 +193,8 @@ func (e *InvalidVersionError) Error() string { func (e *InvalidVersionError) Unwrap() error { return e.Err } // An InvalidPathError indicates a module, import, or file path doesn't -// satisfy all naming constraints. See CheckPath, CheckImportPath, -// and CheckFilePath for specific restrictions. +// satisfy all naming constraints. See [CheckPath], [CheckImportPath], +// and [CheckFilePath] for specific restrictions. type InvalidPathError struct { Kind string // "module", "import", or "file" Path string @@ -294,7 +294,7 @@ func fileNameOK(r rune) bool { } // CheckPath checks that a module path is valid. -// A valid module path is a valid import path, as checked by CheckImportPath, +// A valid module path is a valid import path, as checked by [CheckImportPath], // with three additional constraints. // First, the leading path element (up to the first slash, if any), // by convention a domain name, must contain only lower-case ASCII letters, @@ -380,7 +380,7 @@ const ( // checkPath returns an error describing why the path is not valid. // Because these checks apply to module, import, and file paths, // and because other checks may be applied, the caller is expected to wrap -// this error with InvalidPathError. +// this error with [InvalidPathError]. func checkPath(path string, kind pathKind) error { if !utf8.ValidString(path) { return fmt.Errorf("invalid UTF-8") @@ -532,7 +532,7 @@ var badWindowsNames = []string{ // they require ".vN" instead of "/vN", and for all N, not just N >= 2. // SplitPathVersion returns with ok = false when presented with // a path whose last path element does not satisfy the constraints -// applied by CheckPath, such as "example.com/pkg/v1" or "example.com/pkg/v1.2". +// applied by [CheckPath], such as "example.com/pkg/v1" or "example.com/pkg/v1.2". func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { if strings.HasPrefix(path, "gopkg.in/") { return splitGopkgIn(path) @@ -582,7 +582,7 @@ func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { // MatchPathMajor reports whether the semantic version v // matches the path major version pathMajor. // -// MatchPathMajor returns true if and only if CheckPathMajor returns nil. +// MatchPathMajor returns true if and only if [CheckPathMajor] returns nil. func MatchPathMajor(v, pathMajor string) bool { return CheckPathMajor(v, pathMajor) == nil } @@ -622,7 +622,7 @@ func CheckPathMajor(v, pathMajor string) error { // PathMajorPrefix returns the major-version tag prefix implied by pathMajor. // An empty PathMajorPrefix allows either v0 or v1. // -// Note that MatchPathMajor may accept some versions that do not actually begin +// Note that [MatchPathMajor] may accept some versions that do not actually begin // with this prefix: namely, it accepts a 'v0.0.0-' prefix for a '.v1' // pathMajor, even though that pathMajor implies 'v1' tagging. func PathMajorPrefix(pathMajor string) string { @@ -643,7 +643,7 @@ func PathMajorPrefix(pathMajor string) string { } // CanonicalVersion returns the canonical form of the version string v. -// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". +// It is the same as [semver.Canonical] except that it preserves the special build suffix "+incompatible". func CanonicalVersion(v string) string { cv := semver.Canonical(v) if semver.Build(v) == "+incompatible" { @@ -652,8 +652,8 @@ func CanonicalVersion(v string) string { return cv } -// Sort sorts the list by Path, breaking ties by comparing Version fields. -// The Version fields are interpreted as semantic versions (using semver.Compare) +// Sort sorts the list by Path, breaking ties by comparing [Version] fields. +// The Version fields are interpreted as semantic versions (using [semver.Compare]) // optionally followed by a tie-breaking suffix introduced by a slash character, // like in "v0.0.1/go.mod". func Sort(list []Version) { @@ -793,7 +793,7 @@ func unescapeString(escaped string) (string, bool) { } // MatchPrefixPatterns reports whether any path prefix of target matches one of -// the glob patterns (as defined by path.Match) in the comma-separated globs +// the glob patterns (as defined by [path.Match]) in the comma-separated globs // list. This implements the algorithm used when matching a module path to the // GOPRIVATE environment variable, as described by 'go help module-private'. // diff --git a/vendor/golang.org/x/mod/module/pseudo.go b/vendor/golang.org/x/mod/module/pseudo.go index f04ad378..9cf19d32 100644 --- a/vendor/golang.org/x/mod/module/pseudo.go +++ b/vendor/golang.org/x/mod/module/pseudo.go @@ -125,7 +125,7 @@ func IsPseudoVersion(v string) bool { } // IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base, -// timestamp, and revision, as returned by ZeroPseudoVersion. +// timestamp, and revision, as returned by [ZeroPseudoVersion]. func IsZeroPseudoVersion(v string) bool { return v == ZeroPseudoVersion(semver.Major(v)) } diff --git a/vendor/golang.org/x/mod/semver/semver.go b/vendor/golang.org/x/mod/semver/semver.go index a30a22bf..9a2dfd33 100644 --- a/vendor/golang.org/x/mod/semver/semver.go +++ b/vendor/golang.org/x/mod/semver/semver.go @@ -140,7 +140,7 @@ func Compare(v, w string) int { // Max canonicalizes its arguments and then returns the version string // that compares greater. // -// Deprecated: use Compare instead. In most cases, returning a canonicalized +// Deprecated: use [Compare] instead. In most cases, returning a canonicalized // version is not expected or desired. func Max(v, w string) string { v = Canonical(v) @@ -151,7 +151,7 @@ func Max(v, w string) string { return w } -// ByVersion implements sort.Interface for sorting semantic version strings. +// ByVersion implements [sort.Interface] for sorting semantic version strings. type ByVersion []string func (vs ByVersion) Len() int { return len(vs) } @@ -164,7 +164,7 @@ func (vs ByVersion) Less(i, j int) bool { return vs[i] < vs[j] } -// Sort sorts a list of semantic version strings using ByVersion. +// Sort sorts a list of semantic version strings using [ByVersion]. func Sort(list []string) { sort.Sort(ByVersion(list)) } diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go index 2cb9c408..0c1b8679 100644 --- a/vendor/golang.org/x/net/context/go17.go +++ b/vendor/golang.org/x/net/context/go17.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.7 -// +build go1.7 package context diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go index 64d31ecc..e31e35a9 100644 --- a/vendor/golang.org/x/net/context/go19.go +++ b/vendor/golang.org/x/net/context/go19.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.9 -// +build go1.9 package context diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go index 7b6b6851..065ff3df 100644 --- a/vendor/golang.org/x/net/context/pre_go17.go +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.7 -// +build !go1.7 package context diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go index 1f971534..ec5a6380 100644 --- a/vendor/golang.org/x/net/context/pre_go19.go +++ b/vendor/golang.org/x/net/context/pre_go19.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.9 -// +build !go1.9 package context diff --git a/vendor/golang.org/x/net/http2/databuffer.go b/vendor/golang.org/x/net/http2/databuffer.go index a3067f8d..e6f55cbd 100644 --- a/vendor/golang.org/x/net/http2/databuffer.go +++ b/vendor/golang.org/x/net/http2/databuffer.go @@ -20,41 +20,44 @@ import ( // TODO: Benchmark to determine if the pools are necessary. The GC may have // improved enough that we can instead allocate chunks like this: // make([]byte, max(16<<10, expectedBytesRemaining)) -var ( - dataChunkSizeClasses = []int{ - 1 << 10, - 2 << 10, - 4 << 10, - 8 << 10, - 16 << 10, - } - dataChunkPools = [...]sync.Pool{ - {New: func() interface{} { return make([]byte, 1<<10) }}, - {New: func() interface{} { return make([]byte, 2<<10) }}, - {New: func() interface{} { return make([]byte, 4<<10) }}, - {New: func() interface{} { return make([]byte, 8<<10) }}, - {New: func() interface{} { return make([]byte, 16<<10) }}, - } -) +var dataChunkPools = [...]sync.Pool{ + {New: func() interface{} { return new([1 << 10]byte) }}, + {New: func() interface{} { return new([2 << 10]byte) }}, + {New: func() interface{} { return new([4 << 10]byte) }}, + {New: func() interface{} { return new([8 << 10]byte) }}, + {New: func() interface{} { return new([16 << 10]byte) }}, +} func getDataBufferChunk(size int64) []byte { - i := 0 - for ; i < len(dataChunkSizeClasses)-1; i++ { - if size <= int64(dataChunkSizeClasses[i]) { - break - } + switch { + case size <= 1<<10: + return dataChunkPools[0].Get().(*[1 << 10]byte)[:] + case size <= 2<<10: + return dataChunkPools[1].Get().(*[2 << 10]byte)[:] + case size <= 4<<10: + return dataChunkPools[2].Get().(*[4 << 10]byte)[:] + case size <= 8<<10: + return dataChunkPools[3].Get().(*[8 << 10]byte)[:] + default: + return dataChunkPools[4].Get().(*[16 << 10]byte)[:] } - return dataChunkPools[i].Get().([]byte) } func putDataBufferChunk(p []byte) { - for i, n := range dataChunkSizeClasses { - if len(p) == n { - dataChunkPools[i].Put(p) - return - } + switch len(p) { + case 1 << 10: + dataChunkPools[0].Put((*[1 << 10]byte)(p)) + case 2 << 10: + dataChunkPools[1].Put((*[2 << 10]byte)(p)) + case 4 << 10: + dataChunkPools[2].Put((*[4 << 10]byte)(p)) + case 8 << 10: + dataChunkPools[3].Put((*[8 << 10]byte)(p)) + case 16 << 10: + dataChunkPools[4].Put((*[16 << 10]byte)(p)) + default: + panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } - panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } // dataBuffer is an io.ReadWriter backed by a list of data chunks. diff --git a/vendor/golang.org/x/net/http2/go111.go b/vendor/golang.org/x/net/http2/go111.go deleted file mode 100644 index 5bf62b03..00000000 --- a/vendor/golang.org/x/net/http2/go111.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.11 -// +build go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { - return trace != nil && trace.WroteHeaderField != nil -} - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(k, []string{v}) - } -} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - if trace != nil { - return trace.Got1xxResponse - } - return nil -} diff --git a/vendor/golang.org/x/net/http2/go115.go b/vendor/golang.org/x/net/http2/go115.go deleted file mode 100644 index 908af1ab..00000000 --- a/vendor/golang.org/x/net/http2/go115.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.15 -// +build go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS -// connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - dialer := &tls.Dialer{ - Config: cfg, - } - cn, err := dialer.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed - return tlsCn, nil -} diff --git a/vendor/golang.org/x/net/http2/go118.go b/vendor/golang.org/x/net/http2/go118.go deleted file mode 100644 index aca4b2b3..00000000 --- a/vendor/golang.org/x/net/http2/go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.18 -// +build go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return tc.NetConn() -} diff --git a/vendor/golang.org/x/net/http2/not_go111.go b/vendor/golang.org/x/net/http2/not_go111.go deleted file mode 100644 index cc0baa81..00000000 --- a/vendor/golang.org/x/net/http2/not_go111.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.11 -// +build !go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false } - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - return nil -} diff --git a/vendor/golang.org/x/net/http2/not_go115.go b/vendor/golang.org/x/net/http2/not_go115.go deleted file mode 100644 index e6c04cf7..00000000 --- a/vendor/golang.org/x/net/http2/not_go115.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.15 -// +build !go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext opens a TLS connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - cn, err := tls.Dial(network, addr, cfg) - if err != nil { - return nil, err - } - if err := cn.Handshake(); err != nil { - return nil, err - } - if cfg.InsecureSkipVerify { - return cn, nil - } - if err := cn.VerifyHostname(cfg.ServerName); err != nil { - return nil, err - } - return cn, nil -} diff --git a/vendor/golang.org/x/net/http2/not_go118.go b/vendor/golang.org/x/net/http2/not_go118.go deleted file mode 100644 index eab532c9..00000000 --- a/vendor/golang.org/x/net/http2/not_go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return nil -} diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 02c88b6b..ae94c640 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -2549,7 +2549,6 @@ type responseWriterState struct { wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. sentHeader bool // have we sent the header frame? handlerDone bool // handler has finished - dirty bool // a Write failed; don't reuse this responseWriterState sentContentLen int64 // non-zero if handler set a Content-Length header wroteBytes int64 @@ -2669,7 +2668,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { date: date, }) if err != nil { - rws.dirty = true return 0, err } if endStream { @@ -2690,7 +2688,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { if len(p) > 0 || endStream { // only send a 0 byte DATA frame if we're ending the stream. if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { - rws.dirty = true return 0, err } } @@ -2702,9 +2699,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { trailers: rws.trailers, endStream: true, }) - if err != nil { - rws.dirty = true - } return len(p), err } return len(p), nil @@ -2920,14 +2914,12 @@ func (rws *responseWriterState) writeHeader(code int) { h.Del("Transfer-Encoding") } - if rws.conn.writeHeaders(rws.stream, &writeResHeaders{ + rws.conn.writeHeaders(rws.stream, &writeResHeaders{ streamID: rws.stream.id, httpResCode: code, h: h, endStream: rws.handlerDone && !rws.hasTrailers(), - }) != nil { - rws.dirty = true - } + }) return } @@ -2992,19 +2984,10 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, func (w *responseWriter) handlerDone() { rws := w.rws - dirty := rws.dirty rws.handlerDone = true w.Flush() w.rws = nil - if !dirty { - // Only recycle the pool if all prior Write calls to - // the serverConn goroutine completed successfully. If - // they returned earlier due to resets from the peer - // there might still be write goroutines outstanding - // from the serverConn referencing the rws memory. See - // issue 20704. - responseWriterStatePool.Put(rws) - } + responseWriterStatePool.Put(rws) } // Push errors. @@ -3187,6 +3170,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) { panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) } + sc.curHandlers++ go sc.runHandler(rw, req, sc.handler.ServeHTTP) return promisedID, nil } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 4515b22c..df578b86 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -1018,7 +1018,7 @@ func (cc *ClientConn) forceCloseConn() { if !ok { return } - if nc := tlsUnderlyingConn(tc); nc != nil { + if nc := tc.NetConn(); nc != nil { nc.Close() } } @@ -3201,3 +3201,34 @@ func traceFirstResponseByte(trace *httptrace.ClientTrace) { trace.GotFirstResponseByte() } } + +func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { + return trace != nil && trace.WroteHeaderField != nil +} + +func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(k, []string{v}) + } +} + +func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { + if trace != nil { + return trace.Got1xxResponse + } + return nil +} + +// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS +// connection. +func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { + dialer := &tls.Dialer{ + Config: cfg, + } + cn, err := dialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed + return tlsCn, nil +} diff --git a/vendor/golang.org/x/net/idna/go118.go b/vendor/golang.org/x/net/idna/go118.go index c5c4338d..712f1ad8 100644 --- a/vendor/golang.org/x/net/idna/go118.go +++ b/vendor/golang.org/x/net/idna/go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.18 -// +build go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/idna10.0.0.go b/vendor/golang.org/x/net/idna/idna10.0.0.go index 64ccf85f..7b371788 100644 --- a/vendor/golang.org/x/net/idna/idna10.0.0.go +++ b/vendor/golang.org/x/net/idna/idna10.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.10 -// +build go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/idna9.0.0.go b/vendor/golang.org/x/net/idna/idna9.0.0.go index ee1698ce..cc6a892a 100644 --- a/vendor/golang.org/x/net/idna/idna9.0.0.go +++ b/vendor/golang.org/x/net/idna/idna9.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.10 -// +build !go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/pre_go118.go b/vendor/golang.org/x/net/idna/pre_go118.go index 3aaccab1..40e74bb3 100644 --- a/vendor/golang.org/x/net/idna/pre_go118.go +++ b/vendor/golang.org/x/net/idna/pre_go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.18 -// +build !go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/tables10.0.0.go b/vendor/golang.org/x/net/idna/tables10.0.0.go index d1d62ef4..c6c2bf10 100644 --- a/vendor/golang.org/x/net/idna/tables10.0.0.go +++ b/vendor/golang.org/x/net/idna/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package idna diff --git a/vendor/golang.org/x/net/idna/tables11.0.0.go b/vendor/golang.org/x/net/idna/tables11.0.0.go index 167efba7..76789393 100644 --- a/vendor/golang.org/x/net/idna/tables11.0.0.go +++ b/vendor/golang.org/x/net/idna/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package idna diff --git a/vendor/golang.org/x/net/idna/tables12.0.0.go b/vendor/golang.org/x/net/idna/tables12.0.0.go index ab40f7bc..0600cd2a 100644 --- a/vendor/golang.org/x/net/idna/tables12.0.0.go +++ b/vendor/golang.org/x/net/idna/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/tables13.0.0.go b/vendor/golang.org/x/net/idna/tables13.0.0.go index 66701ead..2fb768ef 100644 --- a/vendor/golang.org/x/net/idna/tables13.0.0.go +++ b/vendor/golang.org/x/net/idna/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables15.0.0.go b/vendor/golang.org/x/net/idna/tables15.0.0.go index 40033778..5ff05fe1 100644 --- a/vendor/golang.org/x/net/idna/tables15.0.0.go +++ b/vendor/golang.org/x/net/idna/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables9.0.0.go b/vendor/golang.org/x/net/idna/tables9.0.0.go index 4074b533..0f25e84c 100644 --- a/vendor/golang.org/x/net/idna/tables9.0.0.go +++ b/vendor/golang.org/x/net/idna/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package idna diff --git a/vendor/golang.org/x/net/idna/trie12.0.0.go b/vendor/golang.org/x/net/idna/trie12.0.0.go index bb63f904..8a75b966 100644 --- a/vendor/golang.org/x/net/idna/trie12.0.0.go +++ b/vendor/golang.org/x/net/idna/trie12.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.16 -// +build !go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/trie13.0.0.go b/vendor/golang.org/x/net/idna/trie13.0.0.go index 7d68a8dc..fa45bb90 100644 --- a/vendor/golang.org/x/net/idna/trie13.0.0.go +++ b/vendor/golang.org/x/net/idna/trie13.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.16 -// +build go1.16 package idna diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index 6202638b..c6492020 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -248,6 +248,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -283,10 +284,6 @@ struct ltchars { #include #endif -#ifndef MSG_FASTOPEN -#define MSG_FASTOPEN 0x20000000 -#endif - #ifndef PTRACE_GETREGS #define PTRACE_GETREGS 0xc #endif @@ -295,14 +292,6 @@ struct ltchars { #define PTRACE_SETREGS 0xd #endif -#ifndef SOL_NETLINK -#define SOL_NETLINK 270 -#endif - -#ifndef SOL_SMC -#define SOL_SMC 286 -#endif - #ifdef SOL_BLUETOOTH // SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h // but it is already in bluetooth_linux.go @@ -319,10 +308,23 @@ struct ltchars { #undef TIPC_WAIT_FOREVER #define TIPC_WAIT_FOREVER 0xffffffff -// Copied from linux/l2tp.h -// Including linux/l2tp.h here causes conflicts between linux/in.h -// and netinet/in.h included via net/route.h above. -#define IPPROTO_L2TP 115 +// Copied from linux/netfilter/nf_nat.h +// Including linux/netfilter/nf_nat.h here causes conflicts between linux/in.h +// and netinet/in.h. +#define NF_NAT_RANGE_MAP_IPS (1 << 0) +#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1) +#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2) +#define NF_NAT_RANGE_PERSISTENT (1 << 3) +#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4) +#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5) +#define NF_NAT_RANGE_NETMAP (1 << 6) +#define NF_NAT_RANGE_PROTO_RANDOM_ALL \ + (NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY) +#define NF_NAT_RANGE_MASK \ + (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \ + NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \ + NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \ + NF_NAT_RANGE_NETMAP) // Copied from linux/hid.h. // Keep in sync with the size of the referenced fields. @@ -603,6 +605,9 @@ ccflags="$@" $2 ~ /^FSOPT_/ || $2 ~ /^WDIO[CFS]_/ || $2 ~ /^NFN/ || + $2 !~ /^NFT_META_IIFTYPE/ && + $2 ~ /^NFT_/ || + $2 ~ /^NF_NAT_/ || $2 ~ /^XDP_/ || $2 ~ /^RWF_/ || $2 ~ /^(HDIO|WIN|SMART)_/ || diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index c73cfe2f..a5d3ff8d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -2127,6 +2127,60 @@ const ( NFNL_SUBSYS_QUEUE = 0x3 NFNL_SUBSYS_ULOG = 0x4 NFS_SUPER_MAGIC = 0x6969 + NFT_CHAIN_FLAGS = 0x7 + NFT_CHAIN_MAXNAMELEN = 0x100 + NFT_CT_MAX = 0x17 + NFT_DATA_RESERVED_MASK = 0xffffff00 + NFT_DATA_VALUE_MAXLEN = 0x40 + NFT_EXTHDR_OP_MAX = 0x4 + NFT_FIB_RESULT_MAX = 0x3 + NFT_INNER_MASK = 0xf + NFT_LOGLEVEL_MAX = 0x8 + NFT_NAME_MAXLEN = 0x100 + NFT_NG_MAX = 0x1 + NFT_OBJECT_CONNLIMIT = 0x5 + NFT_OBJECT_COUNTER = 0x1 + NFT_OBJECT_CT_EXPECT = 0x9 + NFT_OBJECT_CT_HELPER = 0x3 + NFT_OBJECT_CT_TIMEOUT = 0x7 + NFT_OBJECT_LIMIT = 0x4 + NFT_OBJECT_MAX = 0xa + NFT_OBJECT_QUOTA = 0x2 + NFT_OBJECT_SECMARK = 0x8 + NFT_OBJECT_SYNPROXY = 0xa + NFT_OBJECT_TUNNEL = 0x6 + NFT_OBJECT_UNSPEC = 0x0 + NFT_OBJ_MAXNAMELEN = 0x100 + NFT_OSF_MAXGENRELEN = 0x10 + NFT_QUEUE_FLAG_BYPASS = 0x1 + NFT_QUEUE_FLAG_CPU_FANOUT = 0x2 + NFT_QUEUE_FLAG_MASK = 0x3 + NFT_REG32_COUNT = 0x10 + NFT_REG32_SIZE = 0x4 + NFT_REG_MAX = 0x4 + NFT_REG_SIZE = 0x10 + NFT_REJECT_ICMPX_MAX = 0x3 + NFT_RT_MAX = 0x4 + NFT_SECMARK_CTX_MAXLEN = 0x100 + NFT_SET_MAXNAMELEN = 0x100 + NFT_SOCKET_MAX = 0x3 + NFT_TABLE_F_MASK = 0x3 + NFT_TABLE_MAXNAMELEN = 0x100 + NFT_TRACETYPE_MAX = 0x3 + NFT_TUNNEL_F_MASK = 0x7 + NFT_TUNNEL_MAX = 0x1 + NFT_TUNNEL_MODE_MAX = 0x2 + NFT_USERDATA_MAXLEN = 0x100 + NFT_XFRM_KEY_MAX = 0x6 + NF_NAT_RANGE_MAP_IPS = 0x1 + NF_NAT_RANGE_MASK = 0x7f + NF_NAT_RANGE_NETMAP = 0x40 + NF_NAT_RANGE_PERSISTENT = 0x8 + NF_NAT_RANGE_PROTO_OFFSET = 0x20 + NF_NAT_RANGE_PROTO_RANDOM = 0x4 + NF_NAT_RANGE_PROTO_RANDOM_ALL = 0x14 + NF_NAT_RANGE_PROTO_RANDOM_FULLY = 0x10 + NF_NAT_RANGE_PROTO_SPECIFIED = 0x2 NILFS_SUPER_MAGIC = 0x3434 NL0 = 0x0 NL1 = 0x100 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index a1d06159..9dc42410 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -2297,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) { var libc_unveil_trampoline_addr uintptr //go:cgo_import_dynamic libc_unveil unveil "libc.so" - - diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index 5b2a7409..0d3a0751 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -2297,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) { var libc_unveil_trampoline_addr uintptr //go:cgo_import_dynamic libc_unveil unveil "libc.so" - - diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index f6eda134..c39f7776 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -2297,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) { var libc_unveil_trampoline_addr uintptr //go:cgo_import_dynamic libc_unveil unveil "libc.so" - - diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index 55df20ae..57571d07 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -2297,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) { var libc_unveil_trampoline_addr uintptr //go:cgo_import_dynamic libc_unveil unveil "libc.so" - - diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index 8c1155cb..e62963e6 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -2297,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) { var libc_unveil_trampoline_addr uintptr //go:cgo_import_dynamic libc_unveil unveil "libc.so" - - diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 7cc80c58..00831354 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -2297,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) { var libc_unveil_trampoline_addr uintptr //go:cgo_import_dynamic libc_unveil unveil "libc.so" - - diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index 0688737f..79029ed5 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -2297,5 +2297,3 @@ func unveil(path *byte, flags *byte) (err error) { var libc_unveil_trampoline_addr uintptr //go:cgo_import_dynamic libc_unveil unveil "libc.so" - - diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 47dc5796..ffb8708c 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -194,6 +194,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW //sys GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys SetEndOfFile(handle Handle) (err error) +//sys SetFileValidData(handle Handle, validDataLength int64) (err error) //sys GetSystemTimeAsFileTime(time *Filetime) //sys GetSystemTimePreciseAsFileTime(time *Filetime) //sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) [failretval==0xffffffff] diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 146a1f01..e8791c82 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -342,6 +342,7 @@ var ( procSetDefaultDllDirectories = modkernel32.NewProc("SetDefaultDllDirectories") procSetDllDirectoryW = modkernel32.NewProc("SetDllDirectoryW") procSetEndOfFile = modkernel32.NewProc("SetEndOfFile") + procSetFileValidData = modkernel32.NewProc("SetFileValidData") procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW") procSetErrorMode = modkernel32.NewProc("SetErrorMode") procSetEvent = modkernel32.NewProc("SetEvent") @@ -2988,6 +2989,14 @@ func SetEndOfFile(handle Handle) (err error) { return } +func SetFileValidData(handle Handle, validDataLength int64) (err error) { + r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SetEnvironmentVariable(name *uint16, value *uint16) (err error) { r1, _, e1 := syscall.Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0) if r1 == 0 { diff --git a/vendor/modules.txt b/vendor/modules.txt index b72314fd..263f1c43 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# atomicgo.dev/cursor v0.1.1 +# atomicgo.dev/cursor v0.2.0 ## explicit; go 1.15 atomicgo.dev/cursor # atomicgo.dev/keyboard v0.2.9 @@ -6,10 +6,10 @@ atomicgo.dev/cursor atomicgo.dev/keyboard atomicgo.dev/keyboard/internal atomicgo.dev/keyboard/keys -# bitbucket.org/creachadair/shell v0.0.7 -## explicit; go 1.16 -bitbucket.org/creachadair/shell -# github.com/bitfield/script v0.21.4 +# atomicgo.dev/schedule v0.1.0 +## explicit; go 1.18 +atomicgo.dev/schedule +# github.com/bitfield/script v0.22.0 ## explicit; go 1.12 github.com/bitfield/script # github.com/containerd/console v1.0.3 @@ -18,11 +18,8 @@ github.com/containerd/console # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 -## explicit; go 1.12 -github.com/dgrijalva/jwt-go/v4 -# github.com/dustin/go-humanize v1.0.0 -## explicit +# github.com/dustin/go-humanize v1.0.1 +## explicit; go 1.16 github.com/dustin/go-humanize # github.com/ericchiang/k8s v1.2.0 ## explicit @@ -35,6 +32,9 @@ github.com/ericchiang/k8s/runtime github.com/ericchiang/k8s/runtime/schema github.com/ericchiang/k8s/util/intstr github.com/ericchiang/k8s/watch/versioned +# github.com/golang-jwt/jwt/v5 v5.2.0 +## explicit; go 1.18 +github.com/golang-jwt/jwt/v5 # github.com/golang/mock v1.6.0 ## explicit; go 1.11 github.com/golang/mock/gomock @@ -45,47 +45,39 @@ github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp -# github.com/gookit/color v1.5.3 +# github.com/gookit/color v1.5.4 ## explicit; go 1.18 github.com/gookit/color -# github.com/gorilla/mux v1.8.0 -## explicit; go 1.12 +# github.com/gorilla/mux v1.8.1 +## explicit; go 1.20 github.com/gorilla/mux -# github.com/itchyny/gojq v0.12.7 -## explicit; go 1.16 +# github.com/itchyny/gojq v0.12.14 +## explicit; go 1.19 github.com/itchyny/gojq -# github.com/itchyny/timefmt-go v0.1.3 -## explicit; go 1.14 +# github.com/itchyny/timefmt-go v0.1.5 +## explicit; go 1.17 github.com/itchyny/timefmt-go -# github.com/konsorten/go-windows-terminal-sequences v1.0.1 -## explicit -github.com/konsorten/go-windows-terminal-sequences -# github.com/kr/pretty v0.2.1 -## explicit; go 1.12 -# github.com/lithammer/fuzzysearch v1.1.5 +# github.com/lithammer/fuzzysearch v1.1.8 ## explicit; go 1.15 github.com/lithammer/fuzzysearch/fuzzy -# github.com/logrusorgru/aurora v2.0.3+incompatible -## explicit -github.com/logrusorgru/aurora -# github.com/magefile/mage v1.14.0 +# github.com/magefile/mage v1.15.0 ## explicit; go 1.12 github.com/magefile/mage/mg github.com/magefile/mage/sh -# github.com/mattn/go-runewidth v0.0.14 +# github.com/mattn/go-runewidth v0.0.15 ## explicit; go 1.9 github.com/mattn/go-runewidth # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/pterm/pterm v0.12.58 -## explicit; go 1.18 +# github.com/pterm/pterm v0.12.75 +## explicit; go 1.21 github.com/pterm/pterm github.com/pterm/pterm/internal # github.com/rivo/uniseg v0.4.4 ## explicit; go 1.18 github.com/rivo/uniseg -# github.com/sheldonhull/magetools v1.0.0 +# github.com/sheldonhull/magetools v1.0.1 ## explicit; go 1.18 github.com/sheldonhull/magetools/ci github.com/sheldonhull/magetools/docgen @@ -94,31 +86,28 @@ github.com/sheldonhull/magetools/gotools github.com/sheldonhull/magetools/pkg/magetoolsutils github.com/sheldonhull/magetools/pkg/req github.com/sheldonhull/magetools/tooling -# github.com/sirupsen/logrus v1.2.0 -## explicit -github.com/sirupsen/logrus -# github.com/stretchr/testify v1.8.2 +# github.com/sirupsen/logrus v1.9.3 ## explicit; go 1.13 +github.com/sirupsen/logrus +# github.com/stretchr/testify v1.8.4 +## explicit; go 1.20 github.com/stretchr/testify/assert github.com/stretchr/testify/require github.com/stretchr/testify/suite # github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e ## explicit; go 1.19 github.com/xo/terminfo -# github.com/ztrue/tracerr v0.3.0 +# github.com/ztrue/tracerr v0.4.0 ## explicit github.com/ztrue/tracerr -# golang.org/x/crypto v0.17.0 +# golang.org/x/mod v0.14.0 ## explicit; go 1.18 -golang.org/x/crypto/ssh/terminal -# golang.org/x/mod v0.8.0 -## explicit; go 1.17 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.17.0 -## explicit; go 1.17 +# golang.org/x/net v0.20.0 +## explicit; go 1.18 golang.org/x/net/context golang.org/x/net/http/httpguts golang.org/x/net/http2 @@ -126,14 +115,12 @@ golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/internal/timeseries golang.org/x/net/trace -# golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 -## explicit -# golang.org/x/sys v0.15.0 +# golang.org/x/sys v0.16.0 ## explicit; go 1.18 golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.15.0 +# golang.org/x/term v0.16.0 ## explicit; go 1.18 golang.org/x/term # golang.org/x/text v0.14.0 @@ -183,3 +170,10 @@ google.golang.org/grpc/tap # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 +# mvdan.cc/sh/v3 v3.7.0 +## explicit; go 1.19 +mvdan.cc/sh/v3/expand +mvdan.cc/sh/v3/fileutil +mvdan.cc/sh/v3/pattern +mvdan.cc/sh/v3/shell +mvdan.cc/sh/v3/syntax diff --git a/vendor/golang.org/x/crypto/LICENSE b/vendor/mvdan.cc/sh/v3/LICENSE similarity index 91% rename from vendor/golang.org/x/crypto/LICENSE rename to vendor/mvdan.cc/sh/v3/LICENSE index 6a66aea5..2a5268e5 100644 --- a/vendor/golang.org/x/crypto/LICENSE +++ b/vendor/mvdan.cc/sh/v3/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright (c) 2016, Daniel Martí. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/mvdan.cc/sh/v3/expand/arith.go b/vendor/mvdan.cc/sh/v3/expand/arith.go new file mode 100644 index 00000000..20a45960 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/expand/arith.go @@ -0,0 +1,221 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +package expand + +import ( + "fmt" + "strconv" + "strings" + + "mvdan.cc/sh/v3/syntax" +) + +func Arithm(cfg *Config, expr syntax.ArithmExpr) (int, error) { + switch x := expr.(type) { + case *syntax.Word: + str, err := Literal(cfg, x) + if err != nil { + return 0, err + } + // recursively fetch vars + i := 0 + for syntax.ValidName(str) { + val := cfg.envGet(str) + if val == "" { + break + } + if i++; i >= maxNameRefDepth { + break + } + str = val + } + // default to 0 + return atoi(str), nil + case *syntax.ParenArithm: + return Arithm(cfg, x.X) + case *syntax.UnaryArithm: + switch x.Op { + case syntax.Inc, syntax.Dec: + name := x.X.(*syntax.Word).Lit() + old := atoi(cfg.envGet(name)) + val := old + if x.Op == syntax.Inc { + val++ + } else { + val-- + } + if err := cfg.envSet(name, strconv.Itoa(val)); err != nil { + return 0, err + } + if x.Post { + return old, nil + } + return val, nil + } + val, err := Arithm(cfg, x.X) + if err != nil { + return 0, err + } + switch x.Op { + case syntax.Not: + return oneIf(val == 0), nil + case syntax.BitNegation: + return ^val, nil + case syntax.Plus: + return val, nil + default: // syntax.Minus + return -val, nil + } + case *syntax.BinaryArithm: + switch x.Op { + case syntax.Assgn, syntax.AddAssgn, syntax.SubAssgn, + syntax.MulAssgn, syntax.QuoAssgn, syntax.RemAssgn, + syntax.AndAssgn, syntax.OrAssgn, syntax.XorAssgn, + syntax.ShlAssgn, syntax.ShrAssgn: + return cfg.assgnArit(x) + case syntax.TernQuest: // TernColon can't happen here + cond, err := Arithm(cfg, x.X) + if err != nil { + return 0, err + } + b2 := x.Y.(*syntax.BinaryArithm) // must have Op==TernColon + if cond == 1 { + return Arithm(cfg, b2.X) + } + return Arithm(cfg, b2.Y) + } + left, err := Arithm(cfg, x.X) + if err != nil { + return 0, err + } + right, err := Arithm(cfg, x.Y) + if err != nil { + return 0, err + } + return binArit(x.Op, left, right) + default: + panic(fmt.Sprintf("unexpected arithm expr: %T", x)) + } +} + +func oneIf(b bool) int { + if b { + return 1 + } + return 0 +} + +// atoi is like [strconv.Atoi], but it ignores errors and trims whitespace. +func atoi(s string) int { + s = strings.TrimSpace(s) + n, _ := strconv.Atoi(s) + return n +} + +func (cfg *Config) assgnArit(b *syntax.BinaryArithm) (int, error) { + name := b.X.(*syntax.Word).Lit() + val := atoi(cfg.envGet(name)) + arg, err := Arithm(cfg, b.Y) + if err != nil { + return 0, err + } + switch b.Op { + case syntax.Assgn: + val = arg + case syntax.AddAssgn: + val += arg + case syntax.SubAssgn: + val -= arg + case syntax.MulAssgn: + val *= arg + case syntax.QuoAssgn: + if arg == 0 { + return 0, fmt.Errorf("division by zero") + } + val /= arg + case syntax.RemAssgn: + if arg == 0 { + return 0, fmt.Errorf("division by zero") + } + val %= arg + case syntax.AndAssgn: + val &= arg + case syntax.OrAssgn: + val |= arg + case syntax.XorAssgn: + val ^= arg + case syntax.ShlAssgn: + val <<= uint(arg) + case syntax.ShrAssgn: + val >>= uint(arg) + } + if err := cfg.envSet(name, strconv.Itoa(val)); err != nil { + return 0, err + } + return val, nil +} + +func intPow(a, b int) int { + p := 1 + for b > 0 { + if b&1 != 0 { + p *= a + } + b >>= 1 + a *= a + } + return p +} + +func binArit(op syntax.BinAritOperator, x, y int) (int, error) { + switch op { + case syntax.Add: + return x + y, nil + case syntax.Sub: + return x - y, nil + case syntax.Mul: + return x * y, nil + case syntax.Quo: + if y == 0 { + return 0, fmt.Errorf("division by zero") + } + return x / y, nil + case syntax.Rem: + if y == 0 { + return 0, fmt.Errorf("division by zero") + } + return x % y, nil + case syntax.Pow: + return intPow(x, y), nil + case syntax.Eql: + return oneIf(x == y), nil + case syntax.Gtr: + return oneIf(x > y), nil + case syntax.Lss: + return oneIf(x < y), nil + case syntax.Neq: + return oneIf(x != y), nil + case syntax.Leq: + return oneIf(x <= y), nil + case syntax.Geq: + return oneIf(x >= y), nil + case syntax.And: + return x & y, nil + case syntax.Or: + return x | y, nil + case syntax.Xor: + return x ^ y, nil + case syntax.Shr: + return x >> uint(y), nil + case syntax.Shl: + return x << uint(y), nil + case syntax.AndArit: + return oneIf(x != 0 && y != 0), nil + case syntax.OrArit: + return oneIf(x != 0 || y != 0), nil + default: // syntax.Comma + // x is executed but its result discarded + return y, nil + } +} diff --git a/vendor/mvdan.cc/sh/v3/expand/braces.go b/vendor/mvdan.cc/sh/v3/expand/braces.go new file mode 100644 index 00000000..1938a8f0 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/expand/braces.go @@ -0,0 +1,85 @@ +// Copyright (c) 2018, Daniel Martí +// See LICENSE for licensing information + +package expand + +import ( + "strconv" + + "mvdan.cc/sh/v3/syntax" +) + +// Braces performs brace expansion on a word, given that it contains any +// [syntax.BraceExp] parts. For example, the word with a brace expansion +// "foo{bar,baz}" will return two literal words, "foobar" and "foobaz". +// +// Note that the resulting words may share word parts. +func Braces(word *syntax.Word) []*syntax.Word { + var all []*syntax.Word + var left []syntax.WordPart + for i, wp := range word.Parts { + br, ok := wp.(*syntax.BraceExp) + if !ok { + left = append(left, wp) + continue + } + if br.Sequence { + chars := false + from, err1 := strconv.Atoi(br.Elems[0].Lit()) + to, err2 := strconv.Atoi(br.Elems[1].Lit()) + if err1 != nil || err2 != nil { + chars = true + from = int(br.Elems[0].Lit()[0]) + to = int(br.Elems[1].Lit()[0]) + } + upward := from <= to + incr := 1 + if !upward { + incr = -1 + } + if len(br.Elems) > 2 { + n, _ := strconv.Atoi(br.Elems[2].Lit()) + if n != 0 && n > 0 == upward { + incr = n + } + } + n := from + for { + if upward && n > to { + break + } + if !upward && n < to { + break + } + next := *word + next.Parts = next.Parts[i+1:] + lit := &syntax.Lit{} + if chars { + lit.Value = string(rune(n)) + } else { + lit.Value = strconv.Itoa(n) + } + next.Parts = append([]syntax.WordPart{lit}, next.Parts...) + exp := Braces(&next) + for _, w := range exp { + w.Parts = append(left, w.Parts...) + } + all = append(all, exp...) + n += incr + } + return all + } + for _, elem := range br.Elems { + next := *word + next.Parts = next.Parts[i+1:] + next.Parts = append(elem.Parts, next.Parts...) + exp := Braces(&next) + for _, w := range exp { + w.Parts = append(left, w.Parts...) + } + all = append(all, exp...) + } + return all + } + return []*syntax.Word{{Parts: left}} +} diff --git a/vendor/mvdan.cc/sh/v3/expand/doc.go b/vendor/mvdan.cc/sh/v3/expand/doc.go new file mode 100644 index 00000000..19d95180 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/expand/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) 2018, Daniel Martí +// See LICENSE for licensing information + +// Package expand contains code to perform various shell expansions. +package expand diff --git a/vendor/mvdan.cc/sh/v3/expand/environ.go b/vendor/mvdan.cc/sh/v3/expand/environ.go new file mode 100644 index 00000000..49cbf052 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/expand/environ.go @@ -0,0 +1,227 @@ +// Copyright (c) 2018, Daniel Martí +// See LICENSE for licensing information + +package expand + +import ( + "runtime" + "sort" + "strings" +) + +// Environ is the base interface for a shell's environment, allowing it to fetch +// variables by name and to iterate over all the currently set variables. +type Environ interface { + // Get retrieves a variable by its name. To check if the variable is + // set, use Variable.IsSet. + Get(name string) Variable + + // Each iterates over all the currently set variables, calling the + // supplied function on each variable. Iteration is stopped if the + // function returns false. + // + // The names used in the calls aren't required to be unique or sorted. + // If a variable name appears twice, the latest occurrence takes + // priority. + // + // Each is required to forward exported variables when executing + // programs. + Each(func(name string, vr Variable) bool) +} + +// WriteEnviron is an extension on Environ that supports modifying and deleting +// variables. +type WriteEnviron interface { + Environ + // Set sets a variable by name. If !vr.IsSet(), the variable is being + // unset; otherwise, the variable is being replaced. + // + // It is the implementation's responsibility to handle variable + // attributes correctly. For example, changing an exported variable's + // value does not unexport it, and overwriting a name reference variable + // should modify its target. + // + // An error may be returned if the operation is invalid, such as if the + // name is empty or if we're trying to overwrite a read-only variable. + Set(name string, vr Variable) error +} + +type ValueKind uint8 + +const ( + Unset ValueKind = iota + String + NameRef + Indexed + Associative +) + +// Variable describes a shell variable, which can have a number of attributes +// and a value. +// +// A Variable is unset if its Kind field is Unset, which can be checked via +// [Variable.IsSet]. The zero value of a Variable is thus a valid unset variable. +// +// If a variable is set, its Value field will be a []string if it is an indexed +// array, a map[string]string if it's an associative array, or a string +// otherwise. +type Variable struct { + Local bool + Exported bool + ReadOnly bool + + Kind ValueKind + + Str string // Used when Kind is String or NameRef. + List []string // Used when Kind is Indexed. + Map map[string]string // Used when Kind is Associative. +} + +// IsSet returns whether the variable is set. An empty variable is set, but an +// undeclared variable is not. +func (v Variable) IsSet() bool { + return v.Kind != Unset +} + +// String returns the variable's value as a string. In general, this only makes +// sense if the variable has a string value or no value at all. +func (v Variable) String() string { + switch v.Kind { + case String: + return v.Str + case Indexed: + if len(v.List) > 0 { + return v.List[0] + } + case Associative: + // nothing to do + } + return "" +} + +// maxNameRefDepth defines the maximum number of times to follow references when +// resolving a variable. Otherwise, simple name reference loops could crash a +// program quite easily. +const maxNameRefDepth = 100 + +// Resolve follows a number of nameref variables, returning the last reference +// name that was followed and the variable that it points to. +func (v Variable) Resolve(env Environ) (string, Variable) { + name := "" + for i := 0; i < maxNameRefDepth; i++ { + if v.Kind != NameRef { + return name, v + } + name = v.Str // keep name for the next iteration + v = env.Get(name) + } + return name, Variable{} +} + +// FuncEnviron wraps a function mapping variable names to their string values, +// and implements Environ. Empty strings returned by the function will be +// treated as unset variables. All variables will be exported. +// +// Note that the returned Environ's Each method will be a no-op. +func FuncEnviron(fn func(string) string) Environ { + return funcEnviron(fn) +} + +type funcEnviron func(string) string + +func (f funcEnviron) Get(name string) Variable { + value := f(name) + if value == "" { + return Variable{} + } + return Variable{Exported: true, Kind: String, Str: value} +} + +func (f funcEnviron) Each(func(name string, vr Variable) bool) {} + +// ListEnviron returns an Environ with the supplied variables, in the form +// "key=value". All variables will be exported. The last value in pairs is used +// if multiple values are present. +// +// On Windows, where environment variable names are case-insensitive, the +// resulting variable names will all be uppercase. +func ListEnviron(pairs ...string) Environ { + return listEnvironWithUpper(runtime.GOOS == "windows", pairs...) +} + +// listEnvironWithUpper implements ListEnviron, but letting the tests specify +// whether to uppercase all names or not. +func listEnvironWithUpper(upper bool, pairs ...string) Environ { + list := append([]string{}, pairs...) + if upper { + // Uppercase before sorting, so that we can remove duplicates + // without the need for linear search nor a map. + for i, s := range list { + if sep := strings.IndexByte(s, '='); sep > 0 { + list[i] = strings.ToUpper(s[:sep]) + s[sep:] + } + } + } + + sort.SliceStable(list, func(i, j int) bool { + isep := strings.IndexByte(list[i], '=') + jsep := strings.IndexByte(list[j], '=') + if isep < 0 { + isep = 0 + } else { + isep += 1 + } + if jsep < 0 { + jsep = 0 + } else { + jsep += 1 + } + return list[i][:isep] < list[j][:jsep] + }) + + last := "" + for i := 0; i < len(list); { + s := list[i] + sep := strings.IndexByte(s, '=') + if sep <= 0 { + // invalid element; remove it + list = append(list[:i], list[i+1:]...) + continue + } + name := s[:sep] + if last == name { + // duplicate; the last one wins + list = append(list[:i-1], list[i:]...) + continue + } + last = name + i++ + } + return listEnviron(list) +} + +// listEnviron is a sorted list of "name=value" strings. +type listEnviron []string + +func (l listEnviron) Get(name string) Variable { + prefix := name + "=" + i := sort.SearchStrings(l, prefix) + if i < len(l) && strings.HasPrefix(l[i], prefix) { + return Variable{Exported: true, Kind: String, Str: strings.TrimPrefix(l[i], prefix)} + } + return Variable{} +} + +func (l listEnviron) Each(fn func(name string, vr Variable) bool) { + for _, pair := range l { + i := strings.IndexByte(pair, '=') + if i < 0 { + // should never happen; see listEnvironWithUpper + panic("expand.listEnviron: did not expect malformed name-value pair: " + pair) + } + name, value := pair[:i], pair[i+1:] + if !fn(name, Variable{Exported: true, Kind: String, Str: value}) { + return + } + } +} diff --git a/vendor/mvdan.cc/sh/v3/expand/expand.go b/vendor/mvdan.cc/sh/v3/expand/expand.go new file mode 100644 index 00000000..996d496f --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/expand/expand.go @@ -0,0 +1,1040 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +package expand + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/fs" + "os" + "os/user" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" + "syscall" + + "mvdan.cc/sh/v3/pattern" + "mvdan.cc/sh/v3/syntax" +) + +// A Config specifies details about how shell expansion should be performed. The +// zero value is a valid configuration. +type Config struct { + // Env is used to get and set environment variables when performing + // shell expansions. Some special parameters are also expanded via this + // interface, such as: + // + // * "#", "@", "*", "0"-"9" for the shell's parameters + // * "?", "$", "PPID" for the shell's status and process + // * "HOME foo" to retrieve user foo's home directory (if unset, + // os/user.Lookup will be used) + // + // If nil, there are no environment variables set. Use + // ListEnviron(os.Environ()...) to use the system's environment + // variables. + Env Environ + + // CmdSubst expands a command substitution node, writing its standard + // output to the provided io.Writer. + // + // If nil, encountering a command substitution will result in an + // UnexpectedCommandError. + CmdSubst func(io.Writer, *syntax.CmdSubst) error + + // ProcSubst expands a process substitution node. + // + // Note that this feature is a work in progress, and the signature of + // this field might change until #451 is completely fixed. + ProcSubst func(*syntax.ProcSubst) (string, error) + + // TODO(v4): update to os.Readdir with fs.DirEntry. + // We could possibly expose that as a preferred ReadDir2 before then, + // to allow users to opt into better performance in v3. + + // ReadDir is used for file path globbing. If nil, globbing is disabled. + // Use ioutil.ReadDir to use the filesystem directly. + ReadDir func(string) ([]os.FileInfo, error) + + // GlobStar corresponds to the shell option that allows globbing with + // "**". + GlobStar bool + + // NullGlob corresponds to the shell option that allows globbing + // patterns which match nothing to result in zero fields. + NullGlob bool + + // NoUnset corresponds to the shell option that treats unset variables + // as errors. + NoUnset bool + + bufferAlloc bytes.Buffer // TODO: use strings.Builder + fieldAlloc [4]fieldPart + fieldsAlloc [4][]fieldPart + + ifs string + // A pointer to a parameter expansion node, if we're inside one. + // Necessary for ${LINENO}. + curParam *syntax.ParamExp +} + +// UnexpectedCommandError is returned if a command substitution is encountered +// when [Config.CmdSubst] is nil. +type UnexpectedCommandError struct { + Node *syntax.CmdSubst +} + +func (u UnexpectedCommandError) Error() string { + return fmt.Sprintf("unexpected command substitution at %s", u.Node.Pos()) +} + +var zeroConfig = &Config{} + +func prepareConfig(cfg *Config) *Config { + if cfg == nil { + cfg = zeroConfig + } + if cfg.Env == nil { + cfg.Env = FuncEnviron(func(string) string { return "" }) + } + + cfg.ifs = " \t\n" + if vr := cfg.Env.Get("IFS"); vr.IsSet() { + cfg.ifs = vr.String() + } + return cfg +} + +func (cfg *Config) ifsRune(r rune) bool { + for _, r2 := range cfg.ifs { + if r == r2 { + return true + } + } + return false +} + +func (cfg *Config) ifsJoin(strs []string) string { + sep := "" + if cfg.ifs != "" { + sep = cfg.ifs[:1] + } + return strings.Join(strs, sep) +} + +func (cfg *Config) strBuilder() *bytes.Buffer { + b := &cfg.bufferAlloc + b.Reset() + return b +} + +func (cfg *Config) envGet(name string) string { + return cfg.Env.Get(name).String() +} + +func (cfg *Config) envSet(name, value string) error { + wenv, ok := cfg.Env.(WriteEnviron) + if !ok { + return fmt.Errorf("environment is read-only") + } + return wenv.Set(name, Variable{Kind: String, Str: value}) +} + +// Literal expands a single shell word. It is similar to Fields, but the result +// is a single string. This is the behavior when a word is used as the value in +// a shell variable assignment, for example. +// +// The config specifies shell expansion options; nil behaves the same as an +// empty config. +func Literal(cfg *Config, word *syntax.Word) (string, error) { + if word == nil { + return "", nil + } + cfg = prepareConfig(cfg) + field, err := cfg.wordField(word.Parts, quoteNone) + if err != nil { + return "", err + } + return cfg.fieldJoin(field), nil +} + +// Document expands a single shell word as if it were within double quotes. It +// is similar to Literal, but without brace expansion, tilde expansion, and +// globbing. +// +// The config specifies shell expansion options; nil behaves the same as an +// empty config. +func Document(cfg *Config, word *syntax.Word) (string, error) { + if word == nil { + return "", nil + } + cfg = prepareConfig(cfg) + field, err := cfg.wordField(word.Parts, quoteDouble) + if err != nil { + return "", err + } + return cfg.fieldJoin(field), nil +} + +const patMode = pattern.Filenames | pattern.Braces + +// Pattern expands a single shell word as a pattern, using [syntax.QuotePattern] +// on any non-quoted parts of the input word. The result can be used on +// [syntax.TranslatePattern] directly. +// +// The config specifies shell expansion options; nil behaves the same as an +// empty config. +func Pattern(cfg *Config, word *syntax.Word) (string, error) { + cfg = prepareConfig(cfg) + field, err := cfg.wordField(word.Parts, quoteNone) + if err != nil { + return "", err + } + buf := cfg.strBuilder() + for _, part := range field { + if part.quote > quoteNone { + buf.WriteString(pattern.QuoteMeta(part.val, patMode)) + } else { + buf.WriteString(part.val) + } + } + return buf.String(), nil +} + +// Format expands a format string with a number of arguments, following the +// shell's format specifications. These include printf(1), among others. +// +// The resulting string is returned, along with the number of arguments used. +// +// The config specifies shell expansion options; nil behaves the same as an +// empty config. +func Format(cfg *Config, format string, args []string) (string, int, error) { + cfg = prepareConfig(cfg) + buf := cfg.strBuilder() + + consumed, err := formatIntoBuffer(buf, format, args) + if err != nil { + return "", 0, err + } + + return buf.String(), consumed, err +} + +// Format expands a format string with a number of arguments, following the +// shell's format specifications. These include printf(1), among others. +// +// The resulting string is written to the provided buffer, and the number +// of arguments used is returned. +// +// The config specifies shell expansion options; nil behaves the same as an +// empty config. +func formatIntoBuffer(buf *bytes.Buffer, format string, args []string) (int, error) { + var fmts []byte + initialArgs := len(args) + +formatLoop: + for i := 0; i < len(format); i++ { + // readDigits reads from 0 to max digits, either octal or + // hexadecimal. + readDigits := func(max int, hex bool) string { + j := 0 + for ; j < max; j++ { + c := format[i+j] + if (c >= '0' && c <= '9') || + (hex && c >= 'a' && c <= 'f') || + (hex && c >= 'A' && c <= 'F') { + // valid octal or hex char + } else { + break + } + } + digits := format[i : i+j] + i += j - 1 // -1 since the outer loop does i++ + return digits + } + c := format[i] + switch { + case c == '\\': // escaped + i++ + switch c = format[i]; c { + case 'a': // bell + buf.WriteByte('\a') + case 'b': // backspace + buf.WriteByte('\b') + case 'e', 'E': // escape + buf.WriteByte('\x1b') + case 'f': // form feed + buf.WriteByte('\f') + case 'n': // new line + buf.WriteByte('\n') + case 'r': // carriage return + buf.WriteByte('\r') + case 't': // horizontal tab + buf.WriteByte('\t') + case 'v': // vertical tab + buf.WriteByte('\v') + case '\\', '\'', '"', '?': // just the character + buf.WriteByte(c) + case '0', '1', '2', '3', '4', '5', '6', '7': + digits := readDigits(3, false) + // if digits don't fit in 8 bits, 0xff via strconv + n, _ := strconv.ParseUint(digits, 8, 8) + buf.WriteByte(byte(n)) + case 'x', 'u', 'U': + i++ + max := 2 + switch c { + case 'u': + max = 4 + case 'U': + max = 8 + } + digits := readDigits(max, true) + if len(digits) > 0 { + // can't error + n, _ := strconv.ParseUint(digits, 16, 32) + if n == 0 { + // If we're about to print \x00, + // stop the entire loop, like bash. + break formatLoop + } + if c == 'x' { + // always as a single byte + buf.WriteByte(byte(n)) + } else { + buf.WriteRune(rune(n)) + } + break + } + fallthrough + default: // no escape sequence + buf.WriteByte('\\') + buf.WriteByte(c) + } + case len(fmts) > 0: + switch c { + case '%': + buf.WriteByte('%') + fmts = nil + case 'c': + var b byte + if len(args) > 0 { + arg := "" + arg, args = args[0], args[1:] + if len(arg) > 0 { + b = arg[0] + } + } + buf.WriteByte(b) + fmts = nil + case '+', '-', ' ': + if len(fmts) > 1 { + return 0, fmt.Errorf("invalid format char: %c", c) + } + fmts = append(fmts, c) + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + fmts = append(fmts, c) + case 's', 'b', 'd', 'i', 'u', 'o', 'x': + arg := "" + if len(args) > 0 { + arg, args = args[0], args[1:] + } + var farg any + if c == 'b' { + // Passing in nil for args ensures that % format + // strings aren't processed; only escape sequences + // will be handled. + _, err := formatIntoBuffer(buf, arg, nil) + if err != nil { + return 0, err + } + } else if c != 's' { + n, _ := strconv.ParseInt(arg, 0, 0) + if c == 'i' || c == 'd' { + farg = int(n) + } else { + farg = uint(n) + } + if c == 'i' || c == 'u' { + c = 'd' + } + } else { + farg = arg + } + if farg != nil { + fmts = append(fmts, c) + fmt.Fprintf(buf, string(fmts), farg) + } + fmts = nil + default: + return 0, fmt.Errorf("invalid format char: %c", c) + } + case args != nil && c == '%': + // if args == nil, we are not doing format + // arguments + fmts = []byte{c} + default: + buf.WriteByte(c) + } + } + if len(fmts) > 0 { + return 0, fmt.Errorf("missing format char") + } + return initialArgs - len(args), nil +} + +func (cfg *Config) fieldJoin(parts []fieldPart) string { + switch len(parts) { + case 0: + return "" + case 1: // short-cut without a string copy + return parts[0].val + } + buf := cfg.strBuilder() + for _, part := range parts { + buf.WriteString(part.val) + } + return buf.String() +} + +func (cfg *Config) escapedGlobField(parts []fieldPart) (escaped string, glob bool) { + buf := cfg.strBuilder() + for _, part := range parts { + if part.quote > quoteNone { + buf.WriteString(pattern.QuoteMeta(part.val, patMode)) + continue + } + buf.WriteString(part.val) + if pattern.HasMeta(part.val, patMode) { + glob = true + } + } + if glob { // only copy the string if it will be used + escaped = buf.String() + } + return escaped, glob +} + +// Fields expands a number of words as if they were arguments in a shell +// command. This includes brace expansion, tilde expansion, parameter expansion, +// command substitution, arithmetic expansion, and quote removal. +func Fields(cfg *Config, words ...*syntax.Word) ([]string, error) { + cfg = prepareConfig(cfg) + fields := make([]string, 0, len(words)) + dir := cfg.envGet("PWD") + for _, word := range words { + word := *word // make a copy, since SplitBraces replaces the Parts slice + afterBraces := []*syntax.Word{&word} + if syntax.SplitBraces(&word) { + afterBraces = Braces(&word) + } + for _, word2 := range afterBraces { + wfields, err := cfg.wordFields(word2.Parts) + if err != nil { + return nil, err + } + for _, field := range wfields { + path, doGlob := cfg.escapedGlobField(field) + var matches []string + var syntaxError *pattern.SyntaxError + if doGlob && cfg.ReadDir != nil { + matches, err = cfg.glob(dir, path) + if !errors.As(err, &syntaxError) { + if err != nil { + return nil, err + } + if len(matches) > 0 || cfg.NullGlob { + fields = append(fields, matches...) + continue + } + } + } + fields = append(fields, cfg.fieldJoin(field)) + } + } + } + return fields, nil +} + +type fieldPart struct { + val string + quote quoteLevel +} + +type quoteLevel uint + +const ( + quoteNone quoteLevel = iota + quoteDouble + quoteSingle +) + +func (cfg *Config) wordField(wps []syntax.WordPart, ql quoteLevel) ([]fieldPart, error) { + var field []fieldPart + for i, wp := range wps { + switch x := wp.(type) { + case *syntax.Lit: + s := x.Value + if i == 0 && ql == quoteNone { + if prefix, rest := cfg.expandUser(s); prefix != "" { + // TODO: return two separate fieldParts, + // like in wordFields? + s = prefix + rest + } + } + if ql == quoteDouble && strings.Contains(s, "\\") { + buf := cfg.strBuilder() + for i := 0; i < len(s); i++ { + b := s[i] + if b == '\\' && i+1 < len(s) { + switch s[i+1] { + case '"', '\\', '$', '`': // special chars + continue + } + } + buf.WriteByte(b) + } + s = buf.String() + } + if i := strings.IndexByte(s, '\x00'); i >= 0 { + s = s[:i] + } + field = append(field, fieldPart{val: s}) + case *syntax.SglQuoted: + fp := fieldPart{quote: quoteSingle, val: x.Value} + if x.Dollar { + fp.val, _, _ = Format(cfg, fp.val, nil) + } + field = append(field, fp) + case *syntax.DblQuoted: + wfield, err := cfg.wordField(x.Parts, quoteDouble) + if err != nil { + return nil, err + } + for _, part := range wfield { + part.quote = quoteDouble + field = append(field, part) + } + case *syntax.ParamExp: + val, err := cfg.paramExp(x) + if err != nil { + return nil, err + } + field = append(field, fieldPart{val: val}) + case *syntax.CmdSubst: + val, err := cfg.cmdSubst(x) + if err != nil { + return nil, err + } + field = append(field, fieldPart{val: val}) + case *syntax.ArithmExp: + n, err := Arithm(cfg, x.X) + if err != nil { + return nil, err + } + field = append(field, fieldPart{val: strconv.Itoa(n)}) + case *syntax.ProcSubst: + path, err := cfg.ProcSubst(x) + if err != nil { + return nil, err + } + field = append(field, fieldPart{val: path}) + default: + panic(fmt.Sprintf("unhandled word part: %T", x)) + } + } + return field, nil +} + +func (cfg *Config) cmdSubst(cs *syntax.CmdSubst) (string, error) { + if cfg.CmdSubst == nil { + return "", UnexpectedCommandError{Node: cs} + } + buf := cfg.strBuilder() + if err := cfg.CmdSubst(buf, cs); err != nil { + return "", err + } + out := buf.String() + if strings.IndexByte(out, '\x00') >= 0 { + out = strings.ReplaceAll(out, "\x00", "") + } + return strings.TrimRight(out, "\n"), nil +} + +func (cfg *Config) wordFields(wps []syntax.WordPart) ([][]fieldPart, error) { + fields := cfg.fieldsAlloc[:0] + curField := cfg.fieldAlloc[:0] + allowEmpty := false + flush := func() { + if len(curField) == 0 { + return + } + fields = append(fields, curField) + curField = nil + } + splitAdd := func(val string) { + fieldStart := -1 + for i, r := range val { + if cfg.ifsRune(r) { + if fieldStart >= 0 { // ending a field + curField = append(curField, fieldPart{val: val[fieldStart:i]}) + fieldStart = -1 + } + flush() + } else { + if fieldStart < 0 { // starting a new field + fieldStart = i + } + } + } + if fieldStart >= 0 { // ending a field without IFS + curField = append(curField, fieldPart{val: val[fieldStart:]}) + } + } + for i, wp := range wps { + switch x := wp.(type) { + case *syntax.Lit: + s := x.Value + if i == 0 { + prefix, rest := cfg.expandUser(s) + curField = append(curField, fieldPart{ + quote: quoteSingle, + val: prefix, + }) + s = rest + } + if strings.Contains(s, "\\") { + buf := cfg.strBuilder() + for i := 0; i < len(s); i++ { + b := s[i] + if b == '\\' { + if i++; i >= len(s) { + break + } + b = s[i] + } + buf.WriteByte(b) + } + s = buf.String() + } + curField = append(curField, fieldPart{val: s}) + case *syntax.SglQuoted: + allowEmpty = true + fp := fieldPart{quote: quoteSingle, val: x.Value} + if x.Dollar { + fp.val, _, _ = Format(cfg, fp.val, nil) + } + curField = append(curField, fp) + case *syntax.DblQuoted: + if len(x.Parts) == 1 { + pe, _ := x.Parts[0].(*syntax.ParamExp) + if elems := cfg.quotedElemFields(pe); elems != nil { + for i, elem := range elems { + if i > 0 { + flush() + } + curField = append(curField, fieldPart{ + quote: quoteDouble, + val: elem, + }) + } + continue + } + } + allowEmpty = true + wfield, err := cfg.wordField(x.Parts, quoteDouble) + if err != nil { + return nil, err + } + for _, part := range wfield { + part.quote = quoteDouble + curField = append(curField, part) + } + case *syntax.ParamExp: + val, err := cfg.paramExp(x) + if err != nil { + return nil, err + } + splitAdd(val) + case *syntax.CmdSubst: + val, err := cfg.cmdSubst(x) + if err != nil { + return nil, err + } + splitAdd(val) + case *syntax.ArithmExp: + n, err := Arithm(cfg, x.X) + if err != nil { + return nil, err + } + curField = append(curField, fieldPart{val: strconv.Itoa(n)}) + case *syntax.ProcSubst: + path, err := cfg.ProcSubst(x) + if err != nil { + return nil, err + } + splitAdd(path) + case *syntax.ExtGlob: + return nil, fmt.Errorf("extended globbing is not supported") + default: + panic(fmt.Sprintf("unhandled word part: %T", x)) + } + } + flush() + if allowEmpty && len(fields) == 0 { + fields = append(fields, curField) + } + return fields, nil +} + +// quotedElemFields returns the list of elements resulting from a quoted +// parameter expansion that should be treated especially, like "${foo[@]}". +func (cfg *Config) quotedElemFields(pe *syntax.ParamExp) []string { + if pe == nil || pe.Length || pe.Width { + return nil + } + name := pe.Param.Value + if pe.Excl { + switch pe.Names { + case syntax.NamesPrefixWords: // "${!prefix@}" + return cfg.namesByPrefix(pe.Param.Value) + case syntax.NamesPrefix: // "${!prefix*}" + return nil + } + switch nodeLit(pe.Index) { + case "@": // "${!name[@]}" + switch vr := cfg.Env.Get(name); vr.Kind { + case Indexed: + keys := make([]string, 0, len(vr.Map)) + for key := range vr.List { + keys = append(keys, strconv.Itoa(key)) + } + return keys + case Associative: + keys := make([]string, 0, len(vr.Map)) + for key := range vr.Map { + keys = append(keys, key) + } + return keys + } + } + return nil + } + switch name { + case "*": // "${*}" + return []string{cfg.ifsJoin(cfg.Env.Get(name).List)} + case "@": // "${@}" + return cfg.Env.Get(name).List + } + switch nodeLit(pe.Index) { + case "@": // "${name[@]}" + switch vr := cfg.Env.Get(name); vr.Kind { + case Indexed: + return vr.List + case Associative: + elems := make([]string, 0, len(vr.Map)) + for _, elem := range vr.Map { + elems = append(elems, elem) + } + return elems + } + case "*": // "${name[*]}" + if vr := cfg.Env.Get(name); vr.Kind == Indexed { + return []string{cfg.ifsJoin(vr.List)} + } + } + return nil +} + +func (cfg *Config) expandUser(field string) (prefix, rest string) { + if len(field) == 0 || field[0] != '~' { + return "", field + } + name := field[1:] + if i := strings.Index(name, "/"); i >= 0 { + rest = name[i:] + name = name[:i] + } + if name == "" { + // Current user; try via "HOME", otherwise fall back to the + // system's appropriate home dir env var. Don't use os/user, as + // that's overkill. We can't use os.UserHomeDir, because we want + // to use cfg.Env, and we always want to check "HOME" first. + + if vr := cfg.Env.Get("HOME"); vr.IsSet() { + return vr.String(), rest + } + + if runtime.GOOS == "windows" { + if vr := cfg.Env.Get("USERPROFILE"); vr.IsSet() { + return vr.String(), rest + } + } + return "", field + } + + // Not the current user; try via "HOME ", otherwise fall back to + // os/user. There isn't a way to lookup user home dirs without cgo. + + if vr := cfg.Env.Get("HOME " + name); vr.IsSet() { + return vr.String(), rest + } + + u, err := user.Lookup(name) + if err != nil { + return "", field + } + return u.HomeDir, rest +} + +func findAllIndex(pat, name string, n int) [][]int { + expr, err := pattern.Regexp(pat, 0) + if err != nil { + return nil + } + rx := regexp.MustCompile(expr) + return rx.FindAllStringIndex(name, n) +} + +var rxGlobStar = regexp.MustCompile(".*") + +// pathJoin2 is a simpler version of [filepath.Join] without cleaning the result, +// since that's needed for globbing. +func pathJoin2(elem1, elem2 string) string { + if elem1 == "" { + return elem2 + } + if strings.HasSuffix(elem1, string(filepath.Separator)) { + return elem1 + elem2 + } + return elem1 + string(filepath.Separator) + elem2 +} + +// pathSplit splits a file path into its elements, retaining empty ones. Before +// splitting, slashes are replaced with [filepath.Separator], so that splitting +// Unix paths on Windows works as well. +func pathSplit(path string) []string { + path = filepath.FromSlash(path) + return strings.Split(path, string(filepath.Separator)) +} + +func (cfg *Config) glob(base, pat string) ([]string, error) { + parts := pathSplit(pat) + matches := []string{""} + if filepath.IsAbs(pat) { + if parts[0] == "" { + // unix-like + matches[0] = string(filepath.Separator) + } else { + // windows (for some reason it won't work without the + // trailing separator) + matches[0] = parts[0] + string(filepath.Separator) + } + parts = parts[1:] + } + // TODO: as an optimization, we could do chunks of the path all at once, + // like doing a single stat for "/foo/bar" in "/foo/bar/*". + + // TODO: Another optimization would be to reduce the number of ReadDir calls. + // For example, /foo/* can end up doing one duplicate call: + // + // ReadDir("/foo") to ensure that "/foo/" exists and only matches a directory + // ReadDir("/foo") glob "*" + + for i, part := range parts { + // Keep around for debugging. + // log.Printf("matches %q part %d %q", matches, i, part) + + wantDir := i < len(parts)-1 + switch { + case part == "", part == ".", part == "..": + for i, dir := range matches { + matches[i] = pathJoin2(dir, part) + } + continue + case !pattern.HasMeta(part, patMode): + var newMatches []string + for _, dir := range matches { + match := dir + if !filepath.IsAbs(match) { + match = filepath.Join(base, match) + } + match = pathJoin2(match, part) + // We can't use ReadDir on the parent and match the directory + // entry by name, because short paths on Windows break that. + // Our only option is to ReadDir on the directory entry itself, + // which can be wasteful if we only want to see if it exists, + // but at least it's correct in all scenarios. + if _, err := cfg.ReadDir(match); err != nil { + const errPathNotFound = syscall.Errno(3) // from syscall/types_windows.go, to avoid a build tag + var pathErr *os.PathError + if runtime.GOOS == "windows" && errors.As(err, &pathErr) && pathErr.Err == errPathNotFound { + // Unfortunately, os.File.Readdir on a regular file on + // Windows returns an error that satisfies ErrNotExist. + // Luckily, it returns a special "path not found" rather + // than the normal "file not found" for missing files, + // so we can use that knowledge to work around the bug. + // See https://github.com/golang/go/issues/46734. + // TODO: remove when the Go issue above is resolved. + } else if errors.Is(err, fs.ErrNotExist) { + continue // simply doesn't exist + } + if wantDir { + continue // exists but not a directory + } + } + newMatches = append(newMatches, pathJoin2(dir, part)) + } + matches = newMatches + continue + case part == "**" && cfg.GlobStar: + // Find all recursive matches for "**". + // Note that we need the results to be in depth-first order, + // and to avoid recursion, we use a slice as a stack. + // Since we pop from the back, we populate the stack backwards. + stack := make([]string, 0, len(matches)) + for i := len(matches) - 1; i >= 0; i-- { + // "a/**" should match "a/ a/b a/b/cfg ..."; + // note how the zero-match case has a trailing separator. + stack = append(stack, pathJoin2(matches[i], "")) + } + matches = matches[:0] + var newMatches []string // to reuse its capacity + for len(stack) > 0 { + dir := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + // Don't include the original "" match as it's not a valid path. + if dir != "" { + matches = append(matches, dir) + } + + // If dir is not a directory, we keep the stack as-is and continue. + newMatches = newMatches[:0] + newMatches, _ = cfg.globDir(base, dir, rxGlobStar, false, wantDir, newMatches) + for i := len(newMatches) - 1; i >= 0; i-- { + stack = append(stack, newMatches[i]) + } + } + continue + } + expr, err := pattern.Regexp(part, pattern.Filenames|pattern.EntireString) + if err != nil { + return nil, err + } + rx := regexp.MustCompile(expr) + matchHidden := part[0] == byte('.') + var newMatches []string + for _, dir := range matches { + newMatches, err = cfg.globDir(base, dir, rx, matchHidden, wantDir, newMatches) + if err != nil { + return nil, err + } + } + matches = newMatches + } + return matches, nil +} + +func (cfg *Config) globDir(base, dir string, rx *regexp.Regexp, matchHidden bool, wantDir bool, matches []string) ([]string, error) { + fullDir := dir + if !filepath.IsAbs(dir) { + fullDir = filepath.Join(base, dir) + } + infos, err := cfg.ReadDir(fullDir) + if err != nil { + // We still want to return matches, for the sake of reusing slices. + return matches, err + } + for _, info := range infos { + name := info.Name() + if !wantDir { + // No filtering. + } else if mode := info.Mode(); mode&os.ModeSymlink != 0 { + // We need to know if the symlink points to a directory. + // This requires an extra syscall, as ReadDir on the parent directory + // does not follow symlinks for each of the directory entries. + // ReadDir is somewhat wasteful here, as we only want its error result, + // but we could try to reuse its result as per the TODO in Config.glob. + if _, err := cfg.ReadDir(filepath.Join(fullDir, info.Name())); err != nil { + continue + } + } else if !mode.IsDir() { + // Not a symlink nor a directory. + continue + } + if !matchHidden && name[0] == '.' { + continue + } + if rx.MatchString(name) { + matches = append(matches, pathJoin2(dir, name)) + } + } + return matches, nil +} + +// ReadFields splits and returns n fields from s, like the "read" shell builtin. +// If raw is set, backslash escape sequences are not interpreted. +// +// The config specifies shell expansion options; nil behaves the same as an +// empty config. +func ReadFields(cfg *Config, s string, n int, raw bool) []string { + cfg = prepareConfig(cfg) + type pos struct { + start, end int + } + var fpos []pos + + runes := make([]rune, 0, len(s)) + infield := false + esc := false + for _, r := range s { + if infield { + if cfg.ifsRune(r) && (raw || !esc) { + fpos[len(fpos)-1].end = len(runes) + infield = false + } + } else { + if !cfg.ifsRune(r) && (raw || !esc) { + fpos = append(fpos, pos{start: len(runes), end: -1}) + infield = true + } + } + if r == '\\' { + if raw || esc { + runes = append(runes, r) + } + esc = !esc + continue + } + runes = append(runes, r) + esc = false + } + if len(fpos) == 0 { + return nil + } + if infield { + fpos[len(fpos)-1].end = len(runes) + } + + switch { + case n == 1: + // include heading/trailing IFSs + fpos[0].start, fpos[0].end = 0, len(runes) + fpos = fpos[:1] + case n != -1 && n < len(fpos): + // combine to max n fields + fpos[n-1].end = fpos[len(fpos)-1].end + fpos = fpos[:n] + } + + fields := make([]string, len(fpos)) + for i, p := range fpos { + fields[i] = string(runes[p.start:p.end]) + } + return fields +} diff --git a/vendor/mvdan.cc/sh/v3/expand/param.go b/vendor/mvdan.cc/sh/v3/expand/param.go new file mode 100644 index 00000000..1f1a475f --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/expand/param.go @@ -0,0 +1,427 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +package expand + +import ( + "fmt" + "regexp" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "mvdan.cc/sh/v3/pattern" + "mvdan.cc/sh/v3/syntax" +) + +func nodeLit(node syntax.Node) string { + if word, ok := node.(*syntax.Word); ok { + return word.Lit() + } + return "" +} + +type UnsetParameterError struct { + Node *syntax.ParamExp + Message string +} + +func (u UnsetParameterError) Error() string { + return fmt.Sprintf("%s: %s", u.Node.Param.Value, u.Message) +} + +func overridingUnset(pe *syntax.ParamExp) bool { + if pe.Exp == nil { + return false + } + switch pe.Exp.Op { + case syntax.AlternateUnset, syntax.AlternateUnsetOrNull, + syntax.DefaultUnset, syntax.DefaultUnsetOrNull, + syntax.ErrorUnset, syntax.ErrorUnsetOrNull, + syntax.AssignUnset, syntax.AssignUnsetOrNull: + return true + } + return false +} + +func (cfg *Config) paramExp(pe *syntax.ParamExp) (string, error) { + oldParam := cfg.curParam + cfg.curParam = pe + defer func() { cfg.curParam = oldParam }() + + name := pe.Param.Value + index := pe.Index + switch name { + case "@", "*": + index = &syntax.Word{Parts: []syntax.WordPart{ + &syntax.Lit{Value: name}, + }} + } + var vr Variable + switch name { + case "LINENO": + // This is the only parameter expansion that the environment + // interface cannot satisfy. + line := uint64(cfg.curParam.Pos().Line()) + vr = Variable{Kind: String, Str: strconv.FormatUint(line, 10)} + default: + vr = cfg.Env.Get(name) + } + orig := vr + _, vr = vr.Resolve(cfg.Env) + if cfg.NoUnset && vr.Kind == Unset && !overridingUnset(pe) { + return "", UnsetParameterError{ + Node: pe, + Message: "unbound variable", + } + } + + var sliceOffset, sliceLen int + if pe.Slice != nil { + var err error + if pe.Slice.Offset != nil { + sliceOffset, err = Arithm(cfg, pe.Slice.Offset) + if err != nil { + return "", err + } + } + if pe.Slice.Length != nil { + sliceLen, err = Arithm(cfg, pe.Slice.Length) + if err != nil { + return "", err + } + } + } + + var ( + str string + elems []string + + indexAllElements bool // true if var has been accessed with * or @ index + callVarInd = true + ) + + switch nodeLit(index) { + case "@", "*": + switch vr.Kind { + case Unset: + elems = nil + indexAllElements = true + case Indexed: + indexAllElements = true + callVarInd = false + elems = vr.List + slicePos := func(n int) int { + if n < 0 { + n = len(elems) + n + if n < 0 { + n = len(elems) + } + } else if n > len(elems) { + n = len(elems) + } + return n + } + if pe.Slice != nil && pe.Slice.Offset != nil { + elems = elems[slicePos(sliceOffset):] + } + if pe.Slice != nil && pe.Slice.Length != nil { + elems = elems[:slicePos(sliceLen)] + } + str = strings.Join(elems, " ") + } + } + if callVarInd { + var err error + str, err = cfg.varInd(vr, index) + if err != nil { + return "", err + } + } + if !indexAllElements { + elems = []string{str} + } + + switch { + case pe.Length: + n := len(elems) + switch nodeLit(index) { + case "@", "*": + default: + n = utf8.RuneCountInString(str) + } + str = strconv.Itoa(n) + case pe.Excl: + var strs []string + switch { + case pe.Names != 0: + strs = cfg.namesByPrefix(pe.Param.Value) + case orig.Kind == NameRef: + strs = append(strs, orig.Str) + case pe.Index != nil && vr.Kind == Indexed: + for i, e := range vr.List { + if e != "" { + strs = append(strs, strconv.Itoa(i)) + } + } + case pe.Index != nil && vr.Kind == Associative: + for k := range vr.Map { + strs = append(strs, k) + } + case vr.Kind == Unset: + return "", fmt.Errorf("invalid indirect expansion") + case str == "": + return "", nil + default: + vr = cfg.Env.Get(str) + strs = append(strs, vr.String()) + } + sort.Strings(strs) + str = strings.Join(strs, " ") + case pe.Slice != nil: + if callVarInd { + slicePos := func(n int) int { + if n < 0 { + n = len(str) + n + if n < 0 { + n = len(str) + } + } else if n > len(str) { + n = len(str) + } + return n + } + if pe.Slice.Offset != nil { + str = str[slicePos(sliceOffset):] + } + if pe.Slice.Length != nil { + str = str[:slicePos(sliceLen)] + } + } // else, elems are already sliced + case pe.Repl != nil: + orig, err := Pattern(cfg, pe.Repl.Orig) + if err != nil { + return "", err + } + with, err := Literal(cfg, pe.Repl.With) + if err != nil { + return "", err + } + n := 1 + if pe.Repl.All { + n = -1 + } + locs := findAllIndex(orig, str, n) + buf := cfg.strBuilder() + last := 0 + for _, loc := range locs { + buf.WriteString(str[last:loc[0]]) + buf.WriteString(with) + last = loc[1] + } + buf.WriteString(str[last:]) + str = buf.String() + case pe.Exp != nil: + arg, err := Literal(cfg, pe.Exp.Word) + if err != nil { + return "", err + } + switch op := pe.Exp.Op; op { + case syntax.AlternateUnsetOrNull: + if str == "" { + break + } + fallthrough + case syntax.AlternateUnset: + if vr.IsSet() { + str = arg + } + case syntax.DefaultUnset: + if vr.IsSet() { + break + } + fallthrough + case syntax.DefaultUnsetOrNull: + if str == "" { + str = arg + } + case syntax.ErrorUnset: + if vr.IsSet() { + break + } + fallthrough + case syntax.ErrorUnsetOrNull: + if str == "" { + return "", UnsetParameterError{ + Node: pe, + Message: arg, + } + } + case syntax.AssignUnset: + if vr.IsSet() { + break + } + fallthrough + case syntax.AssignUnsetOrNull: + if str == "" { + if err := cfg.envSet(name, arg); err != nil { + return "", err + } + str = arg + } + case syntax.RemSmallPrefix, syntax.RemLargePrefix, + syntax.RemSmallSuffix, syntax.RemLargeSuffix: + suffix := op == syntax.RemSmallSuffix || op == syntax.RemLargeSuffix + small := op == syntax.RemSmallPrefix || op == syntax.RemSmallSuffix + for i, elem := range elems { + elems[i] = removePattern(elem, arg, suffix, small) + } + str = strings.Join(elems, " ") + case syntax.UpperFirst, syntax.UpperAll, + syntax.LowerFirst, syntax.LowerAll: + + caseFunc := unicode.ToLower + if op == syntax.UpperFirst || op == syntax.UpperAll { + caseFunc = unicode.ToUpper + } + all := op == syntax.UpperAll || op == syntax.LowerAll + + // empty string means '?'; nothing to do there + expr, err := pattern.Regexp(arg, 0) + if err != nil { + return str, nil + } + rx := regexp.MustCompile(expr) + + for i, elem := range elems { + rs := []rune(elem) + for ri, r := range rs { + if rx.MatchString(string(r)) { + rs[ri] = caseFunc(r) + if !all { + break + } + } + } + elems[i] = string(rs) + } + str = strings.Join(elems, " ") + case syntax.OtherParamOps: + switch arg { + case "Q": + str, err = syntax.Quote(str, syntax.LangBash) + if err != nil { + // Is this even possible? If a user runs into this panic, + // it's most likely a bug we need to fix. + panic(err) + } + case "E": + tail := str + var rns []rune + for tail != "" { + var rn rune + rn, _, tail, _ = strconv.UnquoteChar(tail, 0) + rns = append(rns, rn) + } + str = string(rns) + case "P", "A", "a": + panic(fmt.Sprintf("unhandled @%s param expansion", arg)) + default: + panic(fmt.Sprintf("unexpected @%s param expansion", arg)) + } + } + } + return str, nil +} + +func removePattern(str, pat string, fromEnd, shortest bool) string { + var mode pattern.Mode + if shortest { + mode |= pattern.Shortest + } + expr, err := pattern.Regexp(pat, mode) + if err != nil { + return str + } + switch { + case fromEnd && shortest: + // use .* to get the right-most shortest match + expr = ".*(" + expr + ")$" + case fromEnd: + // simple suffix + expr = "(" + expr + ")$" + default: + // simple prefix + expr = "^(" + expr + ")" + } + // no need to check error as Translate returns one + rx := regexp.MustCompile(expr) + if loc := rx.FindStringSubmatchIndex(str); loc != nil { + // remove the original pattern (the submatch) + str = str[:loc[2]] + str[loc[3]:] + } + return str +} + +func (cfg *Config) varInd(vr Variable, idx syntax.ArithmExpr) (string, error) { + if idx == nil { + return vr.String(), nil + } + switch vr.Kind { + case String: + n, err := Arithm(cfg, idx) + if err != nil { + return "", err + } + if n == 0 { + return vr.Str, nil + } + case Indexed: + switch nodeLit(idx) { + case "*", "@": + return strings.Join(vr.List, " "), nil + } + i, err := Arithm(cfg, idx) + if err != nil { + return "", err + } + if i < 0 { + return "", fmt.Errorf("negative array index") + } + if i < len(vr.List) { + return vr.List[i], nil + } + case Associative: + switch lit := nodeLit(idx); lit { + case "@", "*": + strs := make([]string, 0, len(vr.Map)) + for _, val := range vr.Map { + strs = append(strs, val) + } + sort.Strings(strs) + if lit == "*" { + return cfg.ifsJoin(strs), nil + } + return strings.Join(strs, " "), nil + } + val, err := Literal(cfg, idx.(*syntax.Word)) + if err != nil { + return "", err + } + return vr.Map[val], nil + } + return "", nil +} + +func (cfg *Config) namesByPrefix(prefix string) []string { + var names []string + cfg.Env.Each(func(name string, vr Variable) bool { + if strings.HasPrefix(name, prefix) { + names = append(names, name) + } + return true + }) + return names +} diff --git a/vendor/mvdan.cc/sh/v3/fileutil/file.go b/vendor/mvdan.cc/sh/v3/fileutil/file.go new file mode 100644 index 00000000..249ae94c --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/fileutil/file.go @@ -0,0 +1,85 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +// Package fileutil allows inspecting shell files, such as detecting whether a +// file may be shell or extracting its shebang. +package fileutil + +import ( + "io/fs" + "os" + "regexp" + "strings" +) + +var ( + shebangRe = regexp.MustCompile(`^#!\s?/(usr/)?bin/(env\s+)?(sh|bash|mksh|bats|zsh)(\s|$)`) + extRe = regexp.MustCompile(`\.(sh|bash|mksh|bats|zsh)$`) +) + +// TODO: consider removing HasShebang in favor of Shebang in v4 + +// HasShebang reports whether bs begins with a valid shell shebang. +// It supports variations with /usr and env. +func HasShebang(bs []byte) bool { + return Shebang(bs) != "" +} + +// Shebang parses a "#!" sequence from the beginning of the input bytes, +// and returns the shell that it points to. +// +// For instance, it returns "sh" for "#!/bin/sh", +// and "bash" for "#!/usr/bin/env bash". +func Shebang(bs []byte) string { + m := shebangRe.FindSubmatch(bs) + if m == nil { + return "" + } + return string(m[3]) +} + +// ScriptConfidence defines how likely a file is to be a shell script, +// from complete certainty that it is not one to complete certainty that +// it is one. +type ScriptConfidence int + +const ( + // ConfNotScript describes files which are definitely not shell scripts, + // such as non-regular files or files with a non-shell extension. + ConfNotScript ScriptConfidence = iota + + // ConfIfShebang describes files which might be shell scripts, depending + // on the shebang line in the file's contents. Since CouldBeScript only + // works on os.FileInfo, the answer in this case can't be final. + ConfIfShebang + + // ConfIsScript describes files which are definitely shell scripts, + // which are regular files with a valid shell extension. + ConfIsScript +) + +// CouldBeScript is a shortcut for CouldBeScript2(fs.FileInfoToDirEntry(info)). +// +// Deprecated: prefer CouldBeScript2, which usually requires fewer syscalls. +func CouldBeScript(info os.FileInfo) ScriptConfidence { + return CouldBeScript2(fs.FileInfoToDirEntry(info)) +} + +// CouldBeScript2 reports how likely a directory entry is to be a shell script. +// It discards directories, symlinks, hidden files and files with non-shell +// extensions. +func CouldBeScript2(entry fs.DirEntry) ScriptConfidence { + name := entry.Name() + switch { + case entry.IsDir(), name[0] == '.': + return ConfNotScript + case entry.Type()&os.ModeSymlink != 0: + return ConfNotScript + case extRe.MatchString(name): + return ConfIsScript + case strings.IndexByte(name, '.') > 0: + return ConfNotScript // different extension + default: + return ConfIfShebang + } +} diff --git a/vendor/mvdan.cc/sh/v3/pattern/pattern.go b/vendor/mvdan.cc/sh/v3/pattern/pattern.go new file mode 100644 index 00000000..7cd98d14 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/pattern/pattern.go @@ -0,0 +1,335 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +// Package pattern allows working with shell pattern matching notation, also +// known as wildcards or globbing. +// +// For reference, see +// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13. +package pattern + +import ( + "bytes" + "fmt" + "regexp" + "strconv" + "strings" +) + +// Mode can be used to supply a number of options to the package's functions. +// Not all functions change their behavior with all of the options below. +type Mode uint + +type SyntaxError struct { + msg string + err error +} + +func (e SyntaxError) Error() string { return e.msg } + +func (e SyntaxError) Unwrap() error { return e.err } + +const ( + Shortest Mode = 1 << iota // prefer the shortest match. + Filenames // "*" and "?" don't match slashes; only "**" does + Braces // support "{a,b}" and "{1..4}" + EntireString // match the entire string using ^$ delimiters +) + +var numRange = regexp.MustCompile(`^([+-]?\d+)\.\.([+-]?\d+)}`) + +// Regexp turns a shell pattern into a regular expression that can be used with +// [regexp.Compile]. It will return an error if the input pattern was incorrect. +// Otherwise, the returned expression can be passed to [regexp.MustCompile]. +// +// For example, Regexp(`foo*bar?`, true) returns `foo.*bar.`. +// +// Note that this function (and [QuoteMeta]) should not be directly used with file +// paths if Windows is supported, as the path separator on that platform is the +// same character as the escaping character for shell patterns. +func Regexp(pat string, mode Mode) (string, error) { + needsEscaping := false +noopLoop: + for _, r := range pat { + switch r { + // including those that need escaping since they are + // regular expression metacharacters + case '*', '?', '[', '\\', '.', '+', '(', ')', '|', + ']', '{', '}', '^', '$': + needsEscaping = true + break noopLoop + } + } + if !needsEscaping && mode&EntireString == 0 { // short-cut without a string copy + return pat, nil + } + closingBraces := []int{} + var buf bytes.Buffer + // Enable matching `\n` with the `.` metacharacter as globs match `\n` + buf.WriteString("(?s)") + dotMeta := false + if mode&EntireString != 0 { + buf.WriteString("^") + } +writeLoop: + for i := 0; i < len(pat); i++ { + switch c := pat[i]; c { + case '*': + if mode&Filenames != 0 { + if i++; i < len(pat) && pat[i] == '*' { + if i++; i < len(pat) && pat[i] == '/' { + buf.WriteString("(.*/|)") + dotMeta = true + } else { + buf.WriteString(".*") + dotMeta = true + i-- + } + } else { + buf.WriteString("[^/]*") + i-- + } + } else { + buf.WriteString(".*") + dotMeta = true + } + if mode&Shortest != 0 { + buf.WriteByte('?') + } + case '?': + if mode&Filenames != 0 { + buf.WriteString("[^/]") + } else { + buf.WriteByte('.') + dotMeta = true + } + case '\\': + if i++; i >= len(pat) { + return "", &SyntaxError{msg: `\ at end of pattern`} + } + buf.WriteString(regexp.QuoteMeta(string(pat[i]))) + case '[': + name, err := charClass(pat[i:]) + if err != nil { + return "", &SyntaxError{msg: "charClass invalid", err: err} + } + if name != "" { + buf.WriteString(name) + i += len(name) - 1 + break + } + if mode&Filenames != 0 { + for _, c := range pat[i:] { + if c == ']' { + break + } else if c == '/' { + buf.WriteString("\\[") + continue writeLoop + } + } + } + buf.WriteByte(c) + if i++; i >= len(pat) { + return "", &SyntaxError{msg: "[ was not matched with a closing ]"} + } + switch c = pat[i]; c { + case '!', '^': + buf.WriteByte('^') + if i++; i >= len(pat) { + return "", &SyntaxError{msg: "[ was not matched with a closing ]"} + } + } + if c = pat[i]; c == ']' { + buf.WriteByte(']') + if i++; i >= len(pat) { + return "", &SyntaxError{msg: "[ was not matched with a closing ]"} + } + } + rangeStart := byte(0) + loopBracket: + for ; i < len(pat); i++ { + c = pat[i] + buf.WriteByte(c) + switch c { + case '\\': + if i++; i < len(pat) { + buf.WriteByte(pat[i]) + } + continue + case ']': + break loopBracket + } + if rangeStart != 0 && rangeStart > c { + return "", &SyntaxError{msg: fmt.Sprintf("invalid range: %c-%c", rangeStart, c)} + } + if c == '-' { + rangeStart = pat[i-1] + } else { + rangeStart = 0 + } + } + if i >= len(pat) { + return "", &SyntaxError{msg: "[ was not matched with a closing ]"} + } + case '{': + if mode&Braces == 0 { + buf.WriteString(regexp.QuoteMeta(string(c))) + break + } + innerLevel := 1 + commas := false + peekBrace: + for j := i + 1; j < len(pat); j++ { + switch c := pat[j]; c { + case '{': + innerLevel++ + case ',': + commas = true + case '\\': + j++ + case '}': + if innerLevel--; innerLevel > 0 { + continue + } + if !commas { + break peekBrace + } + closingBraces = append(closingBraces, j) + buf.WriteString("(?:") + continue writeLoop + } + } + if match := numRange.FindStringSubmatch(pat[i+1:]); len(match) == 3 { + start, err1 := strconv.Atoi(match[1]) + end, err2 := strconv.Atoi(match[2]) + if err1 != nil || err2 != nil || start > end { + return "", &SyntaxError{msg: fmt.Sprintf("invalid range: %q", match[0])} + } + // TODO: can we do better here? + buf.WriteString("(?:") + for n := start; n <= end; n++ { + if n > start { + buf.WriteByte('|') + } + fmt.Fprintf(&buf, "%d", n) + } + buf.WriteByte(')') + i += len(match[0]) + break + } + buf.WriteString(regexp.QuoteMeta(string(c))) + case ',': + if len(closingBraces) == 0 { + buf.WriteString(regexp.QuoteMeta(string(c))) + } else { + buf.WriteByte('|') + } + case '}': + if len(closingBraces) > 0 && closingBraces[len(closingBraces)-1] == i { + buf.WriteByte(')') + closingBraces = closingBraces[:len(closingBraces)-1] + } else { + buf.WriteString(regexp.QuoteMeta(string(c))) + } + default: + if c > 128 { + buf.WriteByte(c) + } else { + buf.WriteString(regexp.QuoteMeta(string(c))) + } + } + } + if mode&EntireString != 0 { + buf.WriteString("$") + } + // No `.` metacharacters were used, so don't return the flag. + if !dotMeta { + return string(buf.Bytes()[4:]), nil + } + return buf.String(), nil +} + +func charClass(s string) (string, error) { + if strings.HasPrefix(s, "[[.") || strings.HasPrefix(s, "[[=") { + return "", fmt.Errorf("collating features not available") + } + if !strings.HasPrefix(s, "[[:") { + return "", nil + } + name := s[3:] + end := strings.Index(name, ":]]") + if end < 0 { + return "", fmt.Errorf("[[: was not matched with a closing :]]") + } + name = name[:end] + switch name { + case "alnum", "alpha", "ascii", "blank", "cntrl", "digit", "graph", + "lower", "print", "punct", "space", "upper", "word", "xdigit": + default: + return "", fmt.Errorf("invalid character class: %q", name) + } + return s[:len(name)+6], nil +} + +// HasMeta returns whether a string contains any unescaped pattern +// metacharacters: '*', '?', or '['. When the function returns false, the given +// pattern can only match at most one string. +// +// For example, HasMeta(`foo\*bar`) returns false, but HasMeta(`foo*bar`) +// returns true. +// +// This can be useful to avoid extra work, like TranslatePattern. Note that this +// function cannot be used to avoid QuotePattern, as backslashes are quoted by +// that function but ignored here. +func HasMeta(pat string, mode Mode) bool { + for i := 0; i < len(pat); i++ { + switch pat[i] { + case '\\': + i++ + case '*', '?', '[': + return true + case '{': + if mode&Braces != 0 { + return true + } + } + } + return false +} + +// QuoteMeta returns a string that quotes all pattern metacharacters in the +// given text. The returned string is a pattern that matches the literal text. +// +// For example, QuoteMeta(`foo*bar?`) returns `foo\*bar\?`. +func QuoteMeta(pat string, mode Mode) string { + needsEscaping := false +loop: + for _, r := range pat { + switch r { + case '{': + if mode&Braces == 0 { + continue + } + fallthrough + case '*', '?', '[', '\\': + needsEscaping = true + break loop + } + } + if !needsEscaping { // short-cut without a string copy + return pat + } + var buf bytes.Buffer + for _, r := range pat { + switch r { + case '*', '?', '[', '\\': + buf.WriteByte('\\') + case '{': + if mode&Braces != 0 { + buf.WriteByte('\\') + } + } + buf.WriteRune(r) + } + return buf.String() +} diff --git a/vendor/mvdan.cc/sh/v3/shell/doc.go b/vendor/mvdan.cc/sh/v3/shell/doc.go new file mode 100644 index 00000000..81e0c2f7 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/shell/doc.go @@ -0,0 +1,14 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +// Package shell contains high-level features that use the syntax, expand, and +// interp packages under the hood. +// +// Please note that this package uses POSIX Shell syntax. As such, path names on +// Windows need to use double backslashes or be within single quotes when given +// to functions like Fields. For example: +// +// shell.Fields("echo /foo/bar") // on Unix-like +// shell.Fields("echo C:\\foo\\bar") // on Windows +// shell.Fields("echo 'C:\foo\bar'") // on Windows, with quotes +package shell diff --git a/vendor/mvdan.cc/sh/v3/shell/expand.go b/vendor/mvdan.cc/sh/v3/shell/expand.go new file mode 100644 index 00000000..e286c0a9 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/shell/expand.go @@ -0,0 +1,63 @@ +// Copyright (c) 2018, Daniel Martí +// See LICENSE for licensing information + +package shell + +import ( + "os" + "strings" + + "mvdan.cc/sh/v3/expand" + "mvdan.cc/sh/v3/syntax" +) + +// Expand performs shell expansion on s as if it were within double quotes, +// using env to resolve variables. This includes parameter expansion, arithmetic +// expansion, and quote removal. +// +// If env is nil, the current environment variables are used. Empty variables +// are treated as unset; to support variables which are set but empty, use the +// expand package directly. +// +// Command substitutions like $(echo foo) aren't supported to avoid running +// arbitrary code. To support those, use an interpreter with the expand package. +// +// An error will be reported if the input string had invalid syntax. +func Expand(s string, env func(string) string) (string, error) { + p := syntax.NewParser() + word, err := p.Document(strings.NewReader(s)) + if err != nil { + return "", err + } + if env == nil { + env = os.Getenv + } + cfg := &expand.Config{Env: expand.FuncEnviron(env)} + return expand.Document(cfg, word) +} + +// Fields performs shell expansion on s as if it were a command's arguments, +// using env to resolve variables. It is similar to Expand, but includes brace +// expansion, tilde expansion, and globbing. +// +// If env is nil, the current environment variables are used. Empty variables +// are treated as unset; to support variables which are set but empty, use the +// expand package directly. +// +// An error will be reported if the input string had invalid syntax. +func Fields(s string, env func(string) string) ([]string, error) { + p := syntax.NewParser() + var words []*syntax.Word + err := p.Words(strings.NewReader(s), func(w *syntax.Word) bool { + words = append(words, w) + return true + }) + if err != nil { + return nil, err + } + if env == nil { + env = os.Getenv + } + cfg := &expand.Config{Env: expand.FuncEnviron(env)} + return expand.Fields(cfg, words...) +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/braces.go b/vendor/mvdan.cc/sh/v3/syntax/braces.go new file mode 100644 index 00000000..f3452819 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/braces.go @@ -0,0 +1,177 @@ +// Copyright (c) 2018, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import "strconv" + +var ( + litLeftBrace = &Lit{Value: "{"} + litComma = &Lit{Value: ","} + litDots = &Lit{Value: ".."} + litRightBrace = &Lit{Value: "}"} +) + +// SplitBraces parses brace expansions within a word's literal parts. If any +// valid brace expansions are found, they are replaced with BraceExp nodes, and +// the function returns true. Otherwise, the word is left untouched and the +// function returns false. +// +// For example, a literal word "foo{bar,baz}" will result in a word containing +// the literal "foo", and a brace expansion with the elements "bar" and "baz". +// +// It does not return an error; malformed brace expansions are simply skipped. +// For example, the literal word "a{b" is left unchanged. +func SplitBraces(word *Word) bool { + toSplit := false + top := &Word{} + acc := top + var cur *BraceExp + open := []*BraceExp{} + + pop := func() *BraceExp { + old := cur + open = open[:len(open)-1] + if len(open) == 0 { + cur = nil + acc = top + } else { + cur = open[len(open)-1] + acc = cur.Elems[len(cur.Elems)-1] + } + return old + } + addLit := func(lit *Lit) { + acc.Parts = append(acc.Parts, lit) + } + + for _, wp := range word.Parts { + lit, ok := wp.(*Lit) + if !ok { + acc.Parts = append(acc.Parts, wp) + continue + } + last := 0 + for j := 0; j < len(lit.Value); j++ { + addlitidx := func() { + if last == j { + return // empty lit + } + l2 := *lit + l2.Value = l2.Value[last:j] + addLit(&l2) + } + switch lit.Value[j] { + case '{': + addlitidx() + acc = &Word{} + cur = &BraceExp{Elems: []*Word{acc}} + open = append(open, cur) + case ',': + if cur == nil { + continue + } + addlitidx() + acc = &Word{} + cur.Elems = append(cur.Elems, acc) + case '.': + if cur == nil { + continue + } + if j+1 >= len(lit.Value) || lit.Value[j+1] != '.' { + continue + } + addlitidx() + cur.Sequence = true + acc = &Word{} + cur.Elems = append(cur.Elems, acc) + j++ + case '}': + if cur == nil { + continue + } + toSplit = true + addlitidx() + br := pop() + if len(br.Elems) == 1 { + // return {x} to a non-brace + addLit(litLeftBrace) + acc.Parts = append(acc.Parts, br.Elems[0].Parts...) + addLit(litRightBrace) + break + } + if !br.Sequence { + acc.Parts = append(acc.Parts, br) + break + } + var chars [2]bool + broken := false + for i, elem := range br.Elems[:2] { + val := elem.Lit() + if _, err := strconv.Atoi(val); err == nil { + } else if len(val) == 1 && + 'a' <= val[0] && val[0] <= 'z' { + chars[i] = true + } else { + broken = true + } + } + if len(br.Elems) == 3 { + // increment must be a number + val := br.Elems[2].Lit() + if _, err := strconv.Atoi(val); err != nil { + broken = true + } + } + // are start and end both chars or + // non-chars? + if chars[0] != chars[1] { + broken = true + } + if !broken { + acc.Parts = append(acc.Parts, br) + break + } + // return broken {x..y[..incr]} to a non-brace + addLit(litLeftBrace) + for i, elem := range br.Elems { + if i > 0 { + addLit(litDots) + } + acc.Parts = append(acc.Parts, elem.Parts...) + } + addLit(litRightBrace) + default: + continue + } + last = j + 1 + } + if last == 0 { + addLit(lit) + } else { + left := *lit + left.Value = left.Value[last:] + addLit(&left) + } + } + if !toSplit { + return false + } + // open braces that were never closed fall back to non-braces + for acc != top { + br := pop() + addLit(litLeftBrace) + for i, elem := range br.Elems { + if i > 0 { + if br.Sequence { + addLit(litDots) + } else { + addLit(litComma) + } + } + acc.Parts = append(acc.Parts, elem.Parts...) + } + } + *word = *top + return true +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/canonical.sh b/vendor/mvdan.cc/sh/v3/syntax/canonical.sh new file mode 100644 index 00000000..012f48dd --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/canonical.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# separate comment + +! foo bar >a & + +foo() { bar; } + +{ + var1="some long value" # var1 comment + var2=short # var2 comment +} + +if foo; then bar; fi + +for foo in a b c; do + bar +done + +case $foo in +a) A ;; +b) + B + ;; +esac + +foo | bar +foo && + $(bar) && + (more) + +foo 2>&1 +foo <<-EOF + bar +EOF + +$((3 + 4)) diff --git a/vendor/mvdan.cc/sh/v3/syntax/doc.go b/vendor/mvdan.cc/sh/v3/syntax/doc.go new file mode 100644 index 00000000..5c6275e4 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/doc.go @@ -0,0 +1,6 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +// Package syntax implements parsing and formatting of shell programs. +// It supports POSIX Shell, Bash, and mksh. +package syntax diff --git a/vendor/mvdan.cc/sh/v3/syntax/lexer.go b/vendor/mvdan.cc/sh/v3/syntax/lexer.go new file mode 100644 index 00000000..b5dddab7 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/lexer.go @@ -0,0 +1,1209 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "bytes" + "io" + "unicode/utf8" +) + +// bytes that form or start a token +func regOps(r rune) bool { + switch r { + case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`': + return true + } + return false +} + +// tokenize these inside parameter expansions +func paramOps(r rune) bool { + switch r { + case '}', '#', '!', ':', '-', '+', '=', '?', '%', '[', ']', '/', '^', + ',', '@', '*': + return true + } + return false +} + +// these start a parameter expansion name +func paramNameOp(r rune) bool { + switch r { + case '}', ':', '+', '=', '%', '[', ']', '/', '^', ',': + return false + } + return true +} + +// tokenize these inside arithmetic expansions +func arithmOps(r rune) bool { + switch r { + case '+', '-', '!', '~', '*', '/', '%', '(', ')', '^', '<', '>', ':', '=', + ',', '?', '|', '&', '[', ']', '#': + return true + } + return false +} + +func bquoteEscaped(b byte) bool { + switch b { + case '$', '`', '\\': + return true + } + return false +} + +const escNewl rune = utf8.RuneSelf + 1 + +func (p *Parser) rune() rune { + if p.r == '\n' || p.r == escNewl { + // p.r instead of b so that newline + // character positions don't have col 0. + if p.line++; p.line > lineMax { + p.lineOverflow = true + } + p.col = 0 + p.colOverflow = false + } + if p.col += p.w; p.col > colMax { + p.colOverflow = true + } + bquotes := 0 +retry: + if p.bsp < len(p.bs) { + if b := p.bs[p.bsp]; b < utf8.RuneSelf { + p.bsp++ + if b == '\x00' { + // Ignore null bytes while parsing, like bash. + goto retry + } + if b == '\\' { + if p.r == '\\' { + } else if p.peekByte('\n') { + p.bsp++ + p.w, p.r = 1, escNewl + return escNewl + } else if p.peekBytes("\r\n") { + p.bsp += 2 + p.w, p.r = 2, escNewl + return escNewl + } + if p.openBquotes > 0 && bquotes < p.openBquotes && + p.bsp < len(p.bs) && bquoteEscaped(p.bs[p.bsp]) { + bquotes++ + goto retry + } + } + if b == '`' { + p.lastBquoteEsc = bquotes + } + if p.litBs != nil { + p.litBs = append(p.litBs, b) + } + p.w, p.r = 1, rune(b) + return p.r + } + if !utf8.FullRune(p.bs[p.bsp:]) { + // we need more bytes to read a full non-ascii rune + p.fill() + } + var w int + p.r, w = utf8.DecodeRune(p.bs[p.bsp:]) + if p.litBs != nil { + p.litBs = append(p.litBs, p.bs[p.bsp:p.bsp+w]...) + } + p.bsp += w + if p.r == utf8.RuneError && w == 1 { + p.posErr(p.nextPos(), "invalid UTF-8 encoding") + } + p.w = w + } else { + if p.r == utf8.RuneSelf { + } else if p.fill(); p.bs == nil { + p.bsp++ + p.r = utf8.RuneSelf + p.w = 1 + } else { + goto retry + } + } + return p.r +} + +// fill reads more bytes from the input src into readBuf. Any bytes that +// had not yet been used at the end of the buffer are slid into the +// beginning of the buffer. +func (p *Parser) fill() { + p.offs += p.bsp + left := len(p.bs) - p.bsp + copy(p.readBuf[:left], p.readBuf[p.bsp:]) +readAgain: + n, err := 0, p.readErr + if err == nil { + n, err = p.src.Read(p.readBuf[left:]) + p.readErr = err + } + if n == 0 { + if err == nil { + goto readAgain + } + // don't use p.errPass as we don't want to overwrite p.tok + if err != io.EOF { + p.err = err + } + if left > 0 { + p.bs = p.readBuf[:left] + } else { + p.bs = nil + } + } else { + p.bs = p.readBuf[:left+n] + } + p.bsp = 0 +} + +func (p *Parser) nextKeepSpaces() { + r := p.r + if p.quote != hdocBody && p.quote != hdocBodyTabs { + // Heredocs handle escaped newlines in a special way, but others + // do not. + for r == escNewl { + r = p.rune() + } + } + p.pos = p.nextPos() + switch p.quote { + case paramExpRepl: + switch r { + case '}', '/': + p.tok = p.paramToken(r) + case '`', '"', '$', '\'': + p.tok = p.regToken(r) + default: + p.advanceLitOther(r) + } + case dblQuotes: + switch r { + case '`', '"', '$': + p.tok = p.dqToken(r) + default: + p.advanceLitDquote(r) + } + case hdocBody, hdocBodyTabs: + switch r { + case '`', '$': + p.tok = p.dqToken(r) + default: + p.advanceLitHdoc(r) + } + default: // paramExpExp: + switch r { + case '}': + p.tok = p.paramToken(r) + case '`', '"', '$', '\'': + p.tok = p.regToken(r) + default: + p.advanceLitOther(r) + } + } + if p.err != nil && p.tok != _EOF { + p.tok = _EOF + } +} + +func (p *Parser) next() { + if p.r == utf8.RuneSelf { + p.tok = _EOF + return + } + p.spaced = false + if p.quote&allKeepSpaces != 0 { + p.nextKeepSpaces() + return + } + r := p.r + for r == escNewl { + r = p.rune() + } +skipSpace: + for { + switch r { + case utf8.RuneSelf: + p.tok = _EOF + return + case escNewl: + r = p.rune() + case ' ', '\t', '\r': + p.spaced = true + r = p.rune() + case '\n': + if p.tok == _Newl { + // merge consecutive newline tokens + r = p.rune() + continue + } + p.spaced = true + p.tok = _Newl + if p.quote != hdocWord && len(p.heredocs) > p.buriedHdocs { + p.doHeredocs() + } + return + default: + break skipSpace + } + } + if p.stopAt != nil && (p.spaced || p.tok == illegalTok || p.stopToken()) { + w := utf8.RuneLen(r) + if bytes.HasPrefix(p.bs[p.bsp-w:], p.stopAt) { + p.r = utf8.RuneSelf + p.w = 1 + p.tok = _EOF + return + } + } + p.pos = p.nextPos() + switch { + case p.quote&allRegTokens != 0: + switch r { + case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`': + p.tok = p.regToken(r) + case '#': + // If we're parsing $foo#bar, ${foo}#bar, 'foo'#bar, or "foo"#bar, + // #bar is a continuation of the same word, not a comment. + // TODO: support $(foo)#bar and `foo`#bar as well, which is slightly tricky, + // as we can't easily tell them apart from (foo)#bar and `#bar`, + // where #bar should remain a comment. + if !p.spaced { + switch p.tok { + case _LitWord, rightBrace, sglQuote, dblQuote: + p.advanceLitNone(r) + return + } + } + r = p.rune() + p.newLit(r) + runeLoop: + for { + switch r { + case '\n', utf8.RuneSelf: + break runeLoop + case escNewl: + p.litBs = append(p.litBs, '\\', '\n') + break runeLoop + case '`': + if p.backquoteEnd() { + break runeLoop + } + } + r = p.rune() + } + if p.keepComments { + *p.curComs = append(*p.curComs, Comment{ + Hash: p.pos, + Text: p.endLit(), + }) + } else { + p.litBs = nil + } + p.next() + case '[', '=': + if p.quote == arrayElems { + p.tok = p.paramToken(r) + } else { + p.advanceLitNone(r) + } + case '?', '*', '+', '@', '!': + if p.extendedGlob() { + switch r { + case '?': + p.tok = globQuest + case '*': + p.tok = globStar + case '+': + p.tok = globPlus + case '@': + p.tok = globAt + default: // '!' + p.tok = globExcl + } + p.rune() + p.rune() + } else { + p.advanceLitNone(r) + } + default: + p.advanceLitNone(r) + } + case p.quote&allArithmExpr != 0 && arithmOps(r): + p.tok = p.arithmToken(r) + case p.quote&allParamExp != 0 && paramOps(r): + p.tok = p.paramToken(r) + case p.quote == testExprRegexp: + if !p.rxFirstPart && p.spaced { + p.quote = noState + goto skipSpace + } + p.rxFirstPart = false + switch r { + case ';', '"', '\'', '$', '&', '>', '<', '`': + p.tok = p.regToken(r) + case ')': + if p.rxOpenParens > 0 { + // continuation of open paren + p.advanceLitRe(r) + } else { + p.tok = rightParen + p.quote = noState + p.rune() // we are tokenizing manually + } + default: // including '(', '|' + p.advanceLitRe(r) + } + case regOps(r): + p.tok = p.regToken(r) + default: + p.advanceLitOther(r) + } + if p.err != nil && p.tok != _EOF { + p.tok = _EOF + } +} + +// extendedGlob determines whether we're parsing a Bash extended globbing expression. +// For example, whether `*` or `@` are followed by `(` to form `@(foo)`. +func (p *Parser) extendedGlob() bool { + if p.val == "function" { + return false + } + if p.peekByte('(') { + // NOTE: empty pattern list is a valid globbing syntax like `@()`, + // but we'll operate on the "likelihood" that it is a function; + // only tokenize if its a non-empty pattern list. + // We do this after peeking for just one byte, so that the input `echo *` + // followed by a newline does not hang an interactive shell parser until + // another byte is input. + return !p.peekBytes("()") + } + return false +} + +func (p *Parser) peekBytes(s string) bool { + peekEnd := p.bsp + len(s) + // TODO: This should loop for slow readers, e.g. those providing one byte at + // a time. Use a loop and test it with testing/iotest.OneByteReader. + if peekEnd > len(p.bs) { + p.fill() + } + return peekEnd <= len(p.bs) && bytes.HasPrefix(p.bs[p.bsp:peekEnd], []byte(s)) +} + +func (p *Parser) peekByte(b byte) bool { + if p.bsp == len(p.bs) { + p.fill() + } + return p.bsp < len(p.bs) && p.bs[p.bsp] == b +} + +func (p *Parser) regToken(r rune) token { + switch r { + case '\'': + if p.openBquotes > 0 { + // bury openBquotes + p.buriedBquotes = p.openBquotes + p.openBquotes = 0 + } + p.rune() + return sglQuote + case '"': + p.rune() + return dblQuote + case '`': + // Don't call p.rune, as we need to work out p.openBquotes to + // properly handle backslashes in the lexer. + return bckQuote + case '&': + switch p.rune() { + case '&': + p.rune() + return andAnd + case '>': + if p.rune() == '>' { + p.rune() + return appAll + } + return rdrAll + } + return and + case '|': + switch p.rune() { + case '|': + p.rune() + return orOr + case '&': + if p.lang == LangPOSIX { + break + } + p.rune() + return orAnd + } + return or + case '$': + switch p.rune() { + case '\'': + if p.lang == LangPOSIX { + break + } + p.rune() + return dollSglQuote + case '"': + if p.lang == LangPOSIX { + break + } + p.rune() + return dollDblQuote + case '{': + p.rune() + return dollBrace + case '[': + if !p.lang.isBash() || p.quote == paramExpName { + // latter to not tokenise ${$[@]} as $[ + break + } + p.rune() + return dollBrack + case '(': + if p.rune() == '(' { + p.rune() + return dollDblParen + } + return dollParen + } + return dollar + case '(': + if p.rune() == '(' && p.lang != LangPOSIX && p.quote != testExpr { + p.rune() + return dblLeftParen + } + return leftParen + case ')': + p.rune() + return rightParen + case ';': + switch p.rune() { + case ';': + if p.rune() == '&' && p.lang.isBash() { + p.rune() + return dblSemiAnd + } + return dblSemicolon + case '&': + if p.lang == LangPOSIX { + break + } + p.rune() + return semiAnd + case '|': + if p.lang != LangMirBSDKorn { + break + } + p.rune() + return semiOr + } + return semicolon + case '<': + switch p.rune() { + case '<': + if r = p.rune(); r == '-' { + p.rune() + return dashHdoc + } else if r == '<' { + p.rune() + return wordHdoc + } + return hdoc + case '>': + p.rune() + return rdrInOut + case '&': + p.rune() + return dplIn + case '(': + if !p.lang.isBash() { + break + } + p.rune() + return cmdIn + } + return rdrIn + default: // '>' + switch p.rune() { + case '>': + p.rune() + return appOut + case '&': + p.rune() + return dplOut + case '|': + p.rune() + return clbOut + case '(': + if !p.lang.isBash() { + break + } + p.rune() + return cmdOut + } + return rdrOut + } +} + +func (p *Parser) dqToken(r rune) token { + switch r { + case '"': + p.rune() + return dblQuote + case '`': + // Don't call p.rune, as we need to work out p.openBquotes to + // properly handle backslashes in the lexer. + return bckQuote + default: // '$' + switch p.rune() { + case '{': + p.rune() + return dollBrace + case '[': + if !p.lang.isBash() { + break + } + p.rune() + return dollBrack + case '(': + if p.rune() == '(' { + p.rune() + return dollDblParen + } + return dollParen + } + return dollar + } +} + +func (p *Parser) paramToken(r rune) token { + switch r { + case '}': + p.rune() + return rightBrace + case ':': + switch p.rune() { + case '+': + p.rune() + return colPlus + case '-': + p.rune() + return colMinus + case '?': + p.rune() + return colQuest + case '=': + p.rune() + return colAssgn + } + return colon + case '+': + p.rune() + return plus + case '-': + p.rune() + return minus + case '?': + p.rune() + return quest + case '=': + p.rune() + return assgn + case '%': + if p.rune() == '%' { + p.rune() + return dblPerc + } + return perc + case '#': + if p.rune() == '#' { + p.rune() + return dblHash + } + return hash + case '!': + p.rune() + return exclMark + case '[': + p.rune() + return leftBrack + case ']': + p.rune() + return rightBrack + case '/': + if p.rune() == '/' && p.quote != paramExpRepl { + p.rune() + return dblSlash + } + return slash + case '^': + if p.rune() == '^' { + p.rune() + return dblCaret + } + return caret + case ',': + if p.rune() == ',' { + p.rune() + return dblComma + } + return comma + case '@': + p.rune() + return at + default: // '*' + p.rune() + return star + } +} + +func (p *Parser) arithmToken(r rune) token { + switch r { + case '!': + if p.rune() == '=' { + p.rune() + return nequal + } + return exclMark + case '=': + if p.rune() == '=' { + p.rune() + return equal + } + return assgn + case '~': + p.rune() + return tilde + case '(': + p.rune() + return leftParen + case ')': + p.rune() + return rightParen + case '&': + switch p.rune() { + case '&': + p.rune() + return andAnd + case '=': + p.rune() + return andAssgn + } + return and + case '|': + switch p.rune() { + case '|': + p.rune() + return orOr + case '=': + p.rune() + return orAssgn + } + return or + case '<': + switch p.rune() { + case '<': + if p.rune() == '=' { + p.rune() + return shlAssgn + } + return hdoc + case '=': + p.rune() + return lequal + } + return rdrIn + case '>': + switch p.rune() { + case '>': + if p.rune() == '=' { + p.rune() + return shrAssgn + } + return appOut + case '=': + p.rune() + return gequal + } + return rdrOut + case '+': + switch p.rune() { + case '+': + p.rune() + return addAdd + case '=': + p.rune() + return addAssgn + } + return plus + case '-': + switch p.rune() { + case '-': + p.rune() + return subSub + case '=': + p.rune() + return subAssgn + } + return minus + case '%': + if p.rune() == '=' { + p.rune() + return remAssgn + } + return perc + case '*': + switch p.rune() { + case '*': + p.rune() + return power + case '=': + p.rune() + return mulAssgn + } + return star + case '/': + if p.rune() == '=' { + p.rune() + return quoAssgn + } + return slash + case '^': + if p.rune() == '=' { + p.rune() + return xorAssgn + } + return caret + case '[': + p.rune() + return leftBrack + case ']': + p.rune() + return rightBrack + case ',': + p.rune() + return comma + case '?': + p.rune() + return quest + case ':': + p.rune() + return colon + default: // '#' + p.rune() + return hash + } +} + +func (p *Parser) newLit(r rune) { + switch { + case r < utf8.RuneSelf: + p.litBs = p.litBuf[:1] + p.litBs[0] = byte(r) + case r > escNewl: + w := utf8.RuneLen(r) + p.litBs = append(p.litBuf[:0], p.bs[p.bsp-w:p.bsp]...) + default: + // don't let r == utf8.RuneSelf go to the second case as RuneLen + // would return -1 + p.litBs = p.litBuf[:0] + } +} + +func (p *Parser) endLit() (s string) { + if p.r == utf8.RuneSelf || p.r == escNewl { + s = string(p.litBs) + } else { + s = string(p.litBs[:len(p.litBs)-p.w]) + } + p.litBs = nil + return +} + +func (p *Parser) isLitRedir() bool { + lit := p.litBs[:len(p.litBs)-1] + if lit[0] == '{' && lit[len(lit)-1] == '}' { + return ValidName(string(lit[1 : len(lit)-1])) + } + for _, b := range lit { + switch b { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + default: + return false + } + } + return true +} + +func (p *Parser) advanceNameCont(r rune) { + // we know that r is a letter or underscore +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch { + case 'a' <= r && r <= 'z': + case 'A' <= r && r <= 'Z': + case r == '_': + case '0' <= r && r <= '9': + case r == escNewl: + default: + break loop + } + } + p.tok, p.val = _LitWord, p.endLit() +} + +func (p *Parser) advanceLitOther(r rune) { + tok := _LitWord +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch r { + case '\\': // escaped byte follows + p.rune() + case '\'', '"', '`', '$': + tok = _Lit + break loop + case '}': + if p.quote&allParamExp != 0 { + break loop + } + case '/': + if p.quote != paramExpExp { + break loop + } + case ':', '=', '%', '^', ',', '?', '!', '~', '*': + if p.quote&allArithmExpr != 0 || p.quote == paramExpName { + break loop + } + case '[', ']': + if p.lang != LangPOSIX && p.quote&allArithmExpr != 0 { + break loop + } + fallthrough + case '#', '@': + if p.quote&allParamReg != 0 { + break loop + } + case '+', '-', ' ', '\t', ';', '&', '>', '<', '|', '(', ')', '\n', '\r': + if p.quote&allKeepSpaces == 0 { + break loop + } + } + } + p.tok, p.val = tok, p.endLit() +} + +func (p *Parser) advanceLitNone(r rune) { + p.eqlOffs = -1 + tok := _LitWord +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch r { + case ' ', '\t', '\n', '\r', '&', '|', ';', '(', ')': + break loop + case '\\': // escaped byte follows + p.rune() + case '>', '<': + if p.peekByte('(') { + tok = _Lit + } else if p.isLitRedir() { + tok = _LitRedir + } + break loop + case '`': + if p.quote != subCmdBckquo { + tok = _Lit + } + break loop + case '"', '\'', '$': + tok = _Lit + break loop + case '?', '*', '+', '@', '!': + if p.extendedGlob() { + tok = _Lit + break loop + } + case '=': + if p.eqlOffs < 0 { + p.eqlOffs = len(p.litBs) - 1 + } + case '[': + if p.lang != LangPOSIX && len(p.litBs) > 1 && p.litBs[0] != '[' { + tok = _Lit + break loop + } + } + } + p.tok, p.val = tok, p.endLit() +} + +func (p *Parser) advanceLitDquote(r rune) { + tok := _LitWord +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch r { + case '"': + break loop + case '\\': // escaped byte follows + p.rune() + case escNewl, '`', '$': + tok = _Lit + break loop + } + } + p.tok, p.val = tok, p.endLit() +} + +func (p *Parser) advanceLitHdoc(r rune) { + // Unlike the rest of nextKeepSpaces quote states, we handle escaped + // newlines here. If lastTok==_Lit, then we know we're following an + // escaped newline, so the first line can't end the heredoc. + lastTok := p.tok + for r == escNewl { + r = p.rune() + lastTok = _Lit + } + p.pos = p.nextPos() + + p.tok = _Lit + p.newLit(r) + if p.quote == hdocBodyTabs { + for r == '\t' { + r = p.rune() + } + } + lStart := len(p.litBs) - 1 + stop := p.hdocStops[len(p.hdocStops)-1] + for ; ; r = p.rune() { + switch r { + case escNewl, '$': + p.val = p.endLit() + return + case '\\': // escaped byte follows + p.rune() + case '`': + if !p.backquoteEnd() { + p.val = p.endLit() + return + } + fallthrough + case '\n', utf8.RuneSelf: + if p.parsingDoc { + if r == utf8.RuneSelf { + p.tok = _LitWord + p.val = p.endLit() + return + } + } else if lStart == 0 && lastTok == _Lit { + // This line starts right after an escaped + // newline, so it should never end the heredoc. + } else if lStart >= 0 { + // Compare the current line with the stop word. + line := p.litBs[lStart:] + if r != utf8.RuneSelf && len(line) > 0 { + line = line[:len(line)-1] // minus trailing character + } + if bytes.Equal(line, stop) { + p.tok = _LitWord + p.val = p.endLit()[:lStart] + if p.val == "" { + p.tok = _Newl + } + p.hdocStops[len(p.hdocStops)-1] = nil + return + } + } + if r != '\n' { + return // hit an unexpected EOF or closing backquote + } + if p.quote == hdocBodyTabs { + for p.peekByte('\t') { + p.rune() + } + } + lStart = len(p.litBs) + } + } +} + +func (p *Parser) quotedHdocWord() *Word { + r := p.r + p.newLit(r) + pos := p.nextPos() + stop := p.hdocStops[len(p.hdocStops)-1] + for ; ; r = p.rune() { + if r == utf8.RuneSelf { + return nil + } + if p.quote == hdocBodyTabs { + for r == '\t' { + r = p.rune() + } + } + lStart := len(p.litBs) - 1 + runeLoop: + for { + switch r { + case utf8.RuneSelf, '\n': + break runeLoop + case '`': + if p.backquoteEnd() { + break runeLoop + } + case escNewl: + p.litBs = append(p.litBs, '\\', '\n') + break runeLoop + } + r = p.rune() + } + if lStart < 0 { + continue + } + // Compare the current line with the stop word. + line := p.litBs[lStart:] + if r != utf8.RuneSelf && len(line) > 0 { + line = line[:len(line)-1] // minus \n + } + if bytes.Equal(line, stop) { + p.hdocStops[len(p.hdocStops)-1] = nil + val := p.endLit()[:lStart] + if val == "" { + return nil + } + return p.wordOne(p.lit(pos, val)) + } + } +} + +func (p *Parser) advanceLitRe(r rune) { + for p.newLit(r); ; r = p.rune() { + switch r { + case '\\': + p.rune() + case '(': + p.rxOpenParens++ + case ')': + if p.rxOpenParens--; p.rxOpenParens < 0 { + p.tok, p.val = _LitWord, p.endLit() + p.quote = noState + return + } + case ' ', '\t', '\r', '\n', ';', '&', '>', '<': + if p.rxOpenParens <= 0 { + p.tok, p.val = _LitWord, p.endLit() + p.quote = noState + return + } + case '"', '\'', '$', '`': + p.tok, p.val = _Lit, p.endLit() + return + case utf8.RuneSelf: + p.tok, p.val = _LitWord, p.endLit() + p.quote = noState + return + } + } +} + +func testUnaryOp(val string) UnTestOperator { + switch val { + case "!": + return TsNot + case "-e", "-a": + return TsExists + case "-f": + return TsRegFile + case "-d": + return TsDirect + case "-c": + return TsCharSp + case "-b": + return TsBlckSp + case "-p": + return TsNmPipe + case "-S": + return TsSocket + case "-L", "-h": + return TsSmbLink + case "-k": + return TsSticky + case "-g": + return TsGIDSet + case "-u": + return TsUIDSet + case "-G": + return TsGrpOwn + case "-O": + return TsUsrOwn + case "-N": + return TsModif + case "-r": + return TsRead + case "-w": + return TsWrite + case "-x": + return TsExec + case "-s": + return TsNoEmpty + case "-t": + return TsFdTerm + case "-z": + return TsEmpStr + case "-n": + return TsNempStr + case "-o": + return TsOptSet + case "-v": + return TsVarSet + case "-R": + return TsRefVar + default: + return 0 + } +} + +func testBinaryOp(val string) BinTestOperator { + switch val { + case "=": + return TsMatchShort + case "==": + return TsMatch + case "!=": + return TsNoMatch + case "=~": + return TsReMatch + case "-nt": + return TsNewer + case "-ot": + return TsOlder + case "-ef": + return TsDevIno + case "-eq": + return TsEql + case "-ne": + return TsNeq + case "-le": + return TsLeq + case "-ge": + return TsGeq + case "-lt": + return TsLss + case "-gt": + return TsGtr + default: + return 0 + } +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/nodes.go b/vendor/mvdan.cc/sh/v3/syntax/nodes.go new file mode 100644 index 00000000..88eb7fea --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/nodes.go @@ -0,0 +1,953 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "strconv" + "strings" +) + +// Node represents a syntax tree node. +type Node interface { + // Pos returns the position of the first character of the node. Comments + // are ignored, except if the node is a *File. + Pos() Pos + // End returns the position of the character immediately after the node. + // If the character is a newline, the line number won't cross into the + // next line. Comments are ignored, except if the node is a *File. + End() Pos +} + +// File represents a shell source file. +type File struct { + Name string + + Stmts []*Stmt + Last []Comment +} + +func (f *File) Pos() Pos { return stmtsPos(f.Stmts, f.Last) } +func (f *File) End() Pos { return stmtsEnd(f.Stmts, f.Last) } + +func stmtsPos(stmts []*Stmt, last []Comment) Pos { + if len(stmts) > 0 { + s := stmts[0] + sPos := s.Pos() + if len(s.Comments) > 0 { + if cPos := s.Comments[0].Pos(); sPos.After(cPos) { + return cPos + } + } + return sPos + } + if len(last) > 0 { + return last[0].Pos() + } + return Pos{} +} + +func stmtsEnd(stmts []*Stmt, last []Comment) Pos { + if len(last) > 0 { + return last[len(last)-1].End() + } + if len(stmts) > 0 { + s := stmts[len(stmts)-1] + sEnd := s.End() + if len(s.Comments) > 0 { + if cEnd := s.Comments[0].End(); cEnd.After(sEnd) { + return cEnd + } + } + return sEnd + } + return Pos{} +} + +// Pos is a position within a shell source file. +type Pos struct { + offs, lineCol uint32 +} + +// We used to split line and column numbers evenly in 16 bits, but line numbers +// are significantly more important in practice. Use more bits for them. +const ( + lineBitSize = 18 + lineMax = (1 << lineBitSize) - 1 + + colBitSize = 32 - lineBitSize + colMax = (1 << colBitSize) - 1 + colBitMask = colMax +) + +// TODO(v4): consider using uint32 for Offset/Line/Col to better represent bit sizes. +// Or go with int64, which more closely resembles portable "sizes" elsewhere. +// The latter is probably nicest, as then we can change the number of internal +// bits later, and we can also do overflow checks for the user in NewPos. + +// NewPos creates a position with the given offset, line, and column. +// +// Note that Pos uses a limited number of bits to store these numbers. +// If line or column overflow their allocated space, they are replaced with 0. +func NewPos(offset, line, column uint) Pos { + if line > lineMax { + line = 0 // protect against overflows; rendered as "?" + } + if column > colMax { + column = 0 // protect against overflows; rendered as "?" + } + return Pos{ + offs: uint32(offset), + lineCol: (uint32(line) << colBitSize) | uint32(column), + } +} + +// Offset returns the byte offset of the position in the original source file. +// Byte offsets start at 0. +// +// Note that Offset is not protected against overflows; +// if an input is larger than 4GiB, the offset will wrap around to 0. +func (p Pos) Offset() uint { return uint(p.offs) } + +// Line returns the line number of the position, starting at 1. +// +// Line is protected against overflows; if an input has too many lines, extra +// lines will have a line number of 0, rendered as "?" by [Pos.String]. +func (p Pos) Line() uint { return uint(p.lineCol >> colBitSize) } + +// Col returns the column number of the position, starting at 1. It counts in +// bytes. +// +// Col is protected against overflows; if an input line has too many columns, +// extra columns will have a column number of 0, rendered as "?" by [Pos.String]. +func (p Pos) Col() uint { return uint(p.lineCol & colBitMask) } + +func (p Pos) String() string { + var b strings.Builder + if line := p.Line(); line > 0 { + b.WriteString(strconv.FormatUint(uint64(line), 10)) + } else { + b.WriteByte('?') + } + b.WriteByte(':') + if col := p.Col(); col > 0 { + b.WriteString(strconv.FormatUint(uint64(col), 10)) + } else { + b.WriteByte('?') + } + return b.String() +} + +// IsValid reports whether the position contains useful position information. +// Some positions returned via [Parse] may be invalid: for example, [Stmt.Semicolon] +// will only be valid if a statement contained a closing token such as ';'. +func (p Pos) IsValid() bool { return p != Pos{} } + +// After reports whether the position p is after p2. It is a more expressive +// version of p.Offset() > p2.Offset(). +func (p Pos) After(p2 Pos) bool { return p.offs > p2.offs } + +func posAddCol(p Pos, n int) Pos { + // TODO: guard against overflows + p.lineCol += uint32(n) + p.offs += uint32(n) + return p +} + +func posMax(p1, p2 Pos) Pos { + if p2.After(p1) { + return p2 + } + return p1 +} + +// Comment represents a single comment on a single line. +type Comment struct { + Hash Pos + Text string +} + +func (c *Comment) Pos() Pos { return c.Hash } +func (c *Comment) End() Pos { return posAddCol(c.Hash, 1+len(c.Text)) } + +// Stmt represents a statement, also known as a "complete command". It is +// compromised of a command and other components that may come before or after +// it. +type Stmt struct { + Comments []Comment + Cmd Command + Position Pos + Semicolon Pos // position of ';', '&', or '|&', if any + Negated bool // ! stmt + Background bool // stmt & + Coprocess bool // mksh's |& + + Redirs []*Redirect // stmt >a 0 { + end = posMax(end, s.Redirs[len(s.Redirs)-1].End()) + } + return end +} + +// Command represents all nodes that are simple or compound commands, including +// function declarations. +// +// These are *CallExpr, *IfClause, *WhileClause, *ForClause, *CaseClause, +// *Block, *Subshell, *BinaryCmd, *FuncDecl, *ArithmCmd, *TestClause, +// *DeclClause, *LetClause, *TimeClause, and *CoprocClause. +type Command interface { + Node + commandNode() +} + +func (*CallExpr) commandNode() {} +func (*IfClause) commandNode() {} +func (*WhileClause) commandNode() {} +func (*ForClause) commandNode() {} +func (*CaseClause) commandNode() {} +func (*Block) commandNode() {} +func (*Subshell) commandNode() {} +func (*BinaryCmd) commandNode() {} +func (*FuncDecl) commandNode() {} +func (*ArithmCmd) commandNode() {} +func (*TestClause) commandNode() {} +func (*DeclClause) commandNode() {} +func (*LetClause) commandNode() {} +func (*TimeClause) commandNode() {} +func (*CoprocClause) commandNode() {} +func (*TestDecl) commandNode() {} + +// Assign represents an assignment to a variable. +// +// Here and elsewhere, Index can mean either an index expression into an indexed +// array, or a string key into an associative array. +// +// If Index is non-nil, the value will be a word and not an array as nested +// arrays are not allowed. +// +// If Naked is true and Name is nil, the assignment is part of a DeclClause and +// the argument (in the Value field) will be evaluated at run-time. This +// includes parameter expansions, which may expand to assignments or options. +type Assign struct { + Append bool // += + Naked bool // without '=' + Name *Lit // must be a valid name + Index ArithmExpr // [i], ["k"] + Value *Word // =val + Array *ArrayExpr // =(arr) +} + +func (a *Assign) Pos() Pos { + if a.Name == nil { + return a.Value.Pos() + } + return a.Name.Pos() +} + +func (a *Assign) End() Pos { + if a.Value != nil { + return a.Value.End() + } + if a.Array != nil { + return a.Array.End() + } + if a.Index != nil { + return posAddCol(a.Index.End(), 2) + } + if a.Naked { + return a.Name.End() + } + return posAddCol(a.Name.End(), 1) +} + +// Redirect represents an input/output redirection. +type Redirect struct { + OpPos Pos + Op RedirOperator + N *Lit // fd>, or {varname}> in Bash + Word *Word // >word + Hdoc *Word // here-document body +} + +func (r *Redirect) Pos() Pos { + if r.N != nil { + return r.N.Pos() + } + return r.OpPos +} + +func (r *Redirect) End() Pos { + if r.Hdoc != nil { + return r.Hdoc.End() + } + return r.Word.End() +} + +// CallExpr represents a command execution or function call, otherwise known as +// a "simple command". +// +// If Args is empty, Assigns apply to the shell environment. Otherwise, they are +// variables that cannot be arrays and which only apply to the call. +type CallExpr struct { + Assigns []*Assign // a=x b=y args + Args []*Word +} + +func (c *CallExpr) Pos() Pos { + if len(c.Assigns) > 0 { + return c.Assigns[0].Pos() + } + return c.Args[0].Pos() +} + +func (c *CallExpr) End() Pos { + if len(c.Args) == 0 { + return c.Assigns[len(c.Assigns)-1].End() + } + return c.Args[len(c.Args)-1].End() +} + +// Subshell represents a series of commands that should be executed in a nested +// shell environment. +type Subshell struct { + Lparen, Rparen Pos + + Stmts []*Stmt + Last []Comment +} + +func (s *Subshell) Pos() Pos { return s.Lparen } +func (s *Subshell) End() Pos { return posAddCol(s.Rparen, 1) } + +// Block represents a series of commands that should be executed in a nested +// scope. It is essentially a list of statements within curly braces. +type Block struct { + Lbrace, Rbrace Pos + + Stmts []*Stmt + Last []Comment +} + +func (b *Block) Pos() Pos { return b.Lbrace } +func (b *Block) End() Pos { return posAddCol(b.Rbrace, 1) } + +// IfClause represents an if statement. +type IfClause struct { + Position Pos // position of the starting "if", "elif", or "else" token + ThenPos Pos // position of "then", empty if this is an "else" + FiPos Pos // position of "fi", shared with .Else if non-nil + + Cond []*Stmt + CondLast []Comment + Then []*Stmt + ThenLast []Comment + + Else *IfClause // if non-nil, an "elif" or an "else" + + Last []Comment // comments on the first "elif", "else", or "fi" +} + +func (c *IfClause) Pos() Pos { return c.Position } +func (c *IfClause) End() Pos { return posAddCol(c.FiPos, 2) } + +// WhileClause represents a while or an until clause. +type WhileClause struct { + WhilePos, DoPos, DonePos Pos + Until bool + + Cond []*Stmt + CondLast []Comment + Do []*Stmt + DoLast []Comment +} + +func (w *WhileClause) Pos() Pos { return w.WhilePos } +func (w *WhileClause) End() Pos { return posAddCol(w.DonePos, 4) } + +// ForClause represents a for or a select clause. The latter is only present in +// Bash. +type ForClause struct { + ForPos, DoPos, DonePos Pos + Select bool + Braces bool // deprecated form with { } instead of do/done + Loop Loop + + Do []*Stmt + DoLast []Comment +} + +func (f *ForClause) Pos() Pos { return f.ForPos } +func (f *ForClause) End() Pos { return posAddCol(f.DonePos, 4) } + +// Loop holds either *WordIter or *CStyleLoop. +type Loop interface { + Node + loopNode() +} + +func (*WordIter) loopNode() {} +func (*CStyleLoop) loopNode() {} + +// WordIter represents the iteration of a variable over a series of words in a +// for clause. If InPos is an invalid position, the "in" token was missing, so +// the iteration is over the shell's positional parameters. +type WordIter struct { + Name *Lit + InPos Pos // position of "in" + Items []*Word +} + +func (w *WordIter) Pos() Pos { return w.Name.Pos() } +func (w *WordIter) End() Pos { + if len(w.Items) > 0 { + return wordLastEnd(w.Items) + } + return posMax(w.Name.End(), posAddCol(w.InPos, 2)) +} + +// CStyleLoop represents the behavior of a for clause similar to the C +// language. +// +// This node will only appear with LangBash. +type CStyleLoop struct { + Lparen, Rparen Pos + // Init, Cond, Post can each be nil, if the for loop construct omits it. + Init, Cond, Post ArithmExpr +} + +func (c *CStyleLoop) Pos() Pos { return c.Lparen } +func (c *CStyleLoop) End() Pos { return posAddCol(c.Rparen, 2) } + +// BinaryCmd represents a binary expression between two statements. +type BinaryCmd struct { + OpPos Pos + Op BinCmdOperator + X, Y *Stmt +} + +func (b *BinaryCmd) Pos() Pos { return b.X.Pos() } +func (b *BinaryCmd) End() Pos { return b.Y.End() } + +// FuncDecl represents the declaration of a function. +type FuncDecl struct { + Position Pos + RsrvWord bool // non-posix "function f" style + Parens bool // with () parentheses, only meaningful with RsrvWord=true + Name *Lit + Body *Stmt +} + +func (f *FuncDecl) Pos() Pos { return f.Position } +func (f *FuncDecl) End() Pos { return f.Body.End() } + +// Word represents a shell word, containing one or more word parts contiguous to +// each other. The word is delimited by word boundaries, such as spaces, +// newlines, semicolons, or parentheses. +type Word struct { + Parts []WordPart +} + +func (w *Word) Pos() Pos { return w.Parts[0].Pos() } +func (w *Word) End() Pos { return w.Parts[len(w.Parts)-1].End() } + +// Lit returns the word as a literal value, if the word consists of *Lit nodes +// only. An empty string is returned otherwise. Words with multiple literals, +// which can appear in some edge cases, are handled properly. +// +// For example, the word "foo" will return "foo", but the word "foo${bar}" will +// return "". +func (w *Word) Lit() string { + // In the usual case, we'll have either a single part that's a literal, + // or one of the parts being a non-literal. Using strings.Join instead + // of a strings.Builder avoids extra work in these cases, since a single + // part is a shortcut, and many parts don't incur string copies. + lits := make([]string, 0, 1) + for _, part := range w.Parts { + lit, ok := part.(*Lit) + if !ok { + return "" + } + lits = append(lits, lit.Value) + } + return strings.Join(lits, "") +} + +// WordPart represents all nodes that can form part of a word. +// +// These are *Lit, *SglQuoted, *DblQuoted, *ParamExp, *CmdSubst, *ArithmExp, +// *ProcSubst, and *ExtGlob. +type WordPart interface { + Node + wordPartNode() +} + +func (*Lit) wordPartNode() {} +func (*SglQuoted) wordPartNode() {} +func (*DblQuoted) wordPartNode() {} +func (*ParamExp) wordPartNode() {} +func (*CmdSubst) wordPartNode() {} +func (*ArithmExp) wordPartNode() {} +func (*ProcSubst) wordPartNode() {} +func (*ExtGlob) wordPartNode() {} +func (*BraceExp) wordPartNode() {} + +// Lit represents a string literal. +// +// Note that a parsed string literal may not appear as-is in the original source +// code, as it is possible to split literals by escaping newlines. The splitting +// is lost, but the end position is not. +type Lit struct { + ValuePos, ValueEnd Pos + Value string +} + +func (l *Lit) Pos() Pos { return l.ValuePos } +func (l *Lit) End() Pos { return l.ValueEnd } + +// SglQuoted represents a string within single quotes. +type SglQuoted struct { + Left, Right Pos + Dollar bool // $'' + Value string +} + +func (q *SglQuoted) Pos() Pos { return q.Left } +func (q *SglQuoted) End() Pos { return posAddCol(q.Right, 1) } + +// DblQuoted represents a list of nodes within double quotes. +type DblQuoted struct { + Left, Right Pos + Dollar bool // $"" + Parts []WordPart +} + +func (q *DblQuoted) Pos() Pos { return q.Left } +func (q *DblQuoted) End() Pos { return posAddCol(q.Right, 1) } + +// CmdSubst represents a command substitution. +type CmdSubst struct { + Left, Right Pos + + Stmts []*Stmt + Last []Comment + + Backquotes bool // deprecated `foo` + TempFile bool // mksh's ${ foo;} + ReplyVar bool // mksh's ${|foo;} +} + +func (c *CmdSubst) Pos() Pos { return c.Left } +func (c *CmdSubst) End() Pos { return posAddCol(c.Right, 1) } + +// ParamExp represents a parameter expansion. +type ParamExp struct { + Dollar, Rbrace Pos + + Short bool // $a instead of ${a} + Excl bool // ${!a} + Length bool // ${#a} + Width bool // ${%a} + Param *Lit + Index ArithmExpr // ${a[i]}, ${a["k"]} + Slice *Slice // ${a:x:y} + Repl *Replace // ${a/x/y} + Names ParNamesOperator // ${!prefix*} or ${!prefix@} + Exp *Expansion // ${a:-b}, ${a#b}, etc +} + +func (p *ParamExp) Pos() Pos { return p.Dollar } +func (p *ParamExp) End() Pos { + if !p.Short { + return posAddCol(p.Rbrace, 1) + } + if p.Index != nil { + return posAddCol(p.Index.End(), 1) + } + return p.Param.End() +} + +func (p *ParamExp) nakedIndex() bool { + return p.Short && p.Index != nil +} + +// Slice represents a character slicing expression inside a ParamExp. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type Slice struct { + Offset, Length ArithmExpr +} + +// Replace represents a search and replace expression inside a ParamExp. +type Replace struct { + All bool + Orig, With *Word +} + +// Expansion represents string manipulation in a ParamExp other than those +// covered by Replace. +type Expansion struct { + Op ParExpOperator + Word *Word +} + +// ArithmExp represents an arithmetic expansion. +type ArithmExp struct { + Left, Right Pos + Bracket bool // deprecated $[expr] form + Unsigned bool // mksh's $((# expr)) + + X ArithmExpr +} + +func (a *ArithmExp) Pos() Pos { return a.Left } +func (a *ArithmExp) End() Pos { + if a.Bracket { + return posAddCol(a.Right, 1) + } + return posAddCol(a.Right, 2) +} + +// ArithmCmd represents an arithmetic command. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type ArithmCmd struct { + Left, Right Pos + Unsigned bool // mksh's ((# expr)) + + X ArithmExpr +} + +func (a *ArithmCmd) Pos() Pos { return a.Left } +func (a *ArithmCmd) End() Pos { return posAddCol(a.Right, 2) } + +// ArithmExpr represents all nodes that form arithmetic expressions. +// +// These are *BinaryArithm, *UnaryArithm, *ParenArithm, and *Word. +type ArithmExpr interface { + Node + arithmExprNode() +} + +func (*BinaryArithm) arithmExprNode() {} +func (*UnaryArithm) arithmExprNode() {} +func (*ParenArithm) arithmExprNode() {} +func (*Word) arithmExprNode() {} + +// BinaryArithm represents a binary arithmetic expression. +// +// If Op is any assign operator, X will be a word with a single *Lit whose value +// is a valid name. +// +// Ternary operators like "a ? b : c" are fit into this structure. Thus, if +// Op==TernQuest, Y will be a *BinaryArithm with Op==TernColon. Op can only be +// TernColon in that scenario. +type BinaryArithm struct { + OpPos Pos + Op BinAritOperator + X, Y ArithmExpr +} + +func (b *BinaryArithm) Pos() Pos { return b.X.Pos() } +func (b *BinaryArithm) End() Pos { return b.Y.End() } + +// UnaryArithm represents an unary arithmetic expression. The unary operator +// may come before or after the sub-expression. +// +// If Op is Inc or Dec, X will be a word with a single *Lit whose value is a +// valid name. +type UnaryArithm struct { + OpPos Pos + Op UnAritOperator + Post bool + X ArithmExpr +} + +func (u *UnaryArithm) Pos() Pos { + if u.Post { + return u.X.Pos() + } + return u.OpPos +} + +func (u *UnaryArithm) End() Pos { + if u.Post { + return posAddCol(u.OpPos, 2) + } + return u.X.End() +} + +// ParenArithm represents an arithmetic expression within parentheses. +type ParenArithm struct { + Lparen, Rparen Pos + + X ArithmExpr +} + +func (p *ParenArithm) Pos() Pos { return p.Lparen } +func (p *ParenArithm) End() Pos { return posAddCol(p.Rparen, 1) } + +// CaseClause represents a case (switch) clause. +type CaseClause struct { + Case, In, Esac Pos + Braces bool // deprecated mksh form with braces instead of in/esac + + Word *Word + Items []*CaseItem + Last []Comment +} + +func (c *CaseClause) Pos() Pos { return c.Case } +func (c *CaseClause) End() Pos { return posAddCol(c.Esac, 4) } + +// CaseItem represents a pattern list (case) within a CaseClause. +type CaseItem struct { + Op CaseOperator + OpPos Pos // unset if it was finished by "esac" + Comments []Comment + Patterns []*Word + + Stmts []*Stmt + Last []Comment +} + +func (c *CaseItem) Pos() Pos { return c.Patterns[0].Pos() } +func (c *CaseItem) End() Pos { + if c.OpPos.IsValid() { + return posAddCol(c.OpPos, len(c.Op.String())) + } + return stmtsEnd(c.Stmts, c.Last) +} + +// TestClause represents a Bash extended test clause. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type TestClause struct { + Left, Right Pos + + X TestExpr +} + +func (t *TestClause) Pos() Pos { return t.Left } +func (t *TestClause) End() Pos { return posAddCol(t.Right, 2) } + +// TestExpr represents all nodes that form test expressions. +// +// These are *BinaryTest, *UnaryTest, *ParenTest, and *Word. +type TestExpr interface { + Node + testExprNode() +} + +func (*BinaryTest) testExprNode() {} +func (*UnaryTest) testExprNode() {} +func (*ParenTest) testExprNode() {} +func (*Word) testExprNode() {} + +// BinaryTest represents a binary test expression. +type BinaryTest struct { + OpPos Pos + Op BinTestOperator + X, Y TestExpr +} + +func (b *BinaryTest) Pos() Pos { return b.X.Pos() } +func (b *BinaryTest) End() Pos { return b.Y.End() } + +// UnaryTest represents a unary test expression. The unary operator may come +// before or after the sub-expression. +type UnaryTest struct { + OpPos Pos + Op UnTestOperator + X TestExpr +} + +func (u *UnaryTest) Pos() Pos { return u.OpPos } +func (u *UnaryTest) End() Pos { return u.X.End() } + +// ParenTest represents a test expression within parentheses. +type ParenTest struct { + Lparen, Rparen Pos + + X TestExpr +} + +func (p *ParenTest) Pos() Pos { return p.Lparen } +func (p *ParenTest) End() Pos { return posAddCol(p.Rparen, 1) } + +// DeclClause represents a Bash declare clause. +// +// Args can contain a mix of regular and naked assignments. The naked +// assignments can represent either options or variable names. +// +// This node will only appear with LangBash. +type DeclClause struct { + // Variant is one of "declare", "local", "export", "readonly", + // "typeset", or "nameref". + Variant *Lit + Args []*Assign +} + +func (d *DeclClause) Pos() Pos { return d.Variant.Pos() } +func (d *DeclClause) End() Pos { + if len(d.Args) > 0 { + return d.Args[len(d.Args)-1].End() + } + return d.Variant.End() +} + +// ArrayExpr represents a Bash array expression. +// +// This node will only appear with LangBash. +type ArrayExpr struct { + Lparen, Rparen Pos + + Elems []*ArrayElem + Last []Comment +} + +func (a *ArrayExpr) Pos() Pos { return a.Lparen } +func (a *ArrayExpr) End() Pos { return posAddCol(a.Rparen, 1) } + +// ArrayElem represents a Bash array element. +// +// Index can be nil; for example, declare -a x=(value). +// Value can be nil; for example, declare -A x=([index]=). +// Finally, neither can be nil; for example, declare -A x=([index]=value) +type ArrayElem struct { + Index ArithmExpr + Value *Word + Comments []Comment +} + +func (a *ArrayElem) Pos() Pos { + if a.Index != nil { + return a.Index.Pos() + } + return a.Value.Pos() +} + +func (a *ArrayElem) End() Pos { + if a.Value != nil { + return a.Value.End() + } + return posAddCol(a.Index.Pos(), 1) +} + +// ExtGlob represents a Bash extended globbing expression. Note that these are +// parsed independently of whether shopt has been called or not. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type ExtGlob struct { + OpPos Pos + Op GlobOperator + Pattern *Lit +} + +func (e *ExtGlob) Pos() Pos { return e.OpPos } +func (e *ExtGlob) End() Pos { return posAddCol(e.Pattern.End(), 1) } + +// ProcSubst represents a Bash process substitution. +// +// This node will only appear with LangBash. +type ProcSubst struct { + OpPos, Rparen Pos + Op ProcOperator + + Stmts []*Stmt + Last []Comment +} + +func (s *ProcSubst) Pos() Pos { return s.OpPos } +func (s *ProcSubst) End() Pos { return posAddCol(s.Rparen, 1) } + +// TimeClause represents a Bash time clause. PosixFormat corresponds to the -p +// flag. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type TimeClause struct { + Time Pos + PosixFormat bool + Stmt *Stmt +} + +func (c *TimeClause) Pos() Pos { return c.Time } +func (c *TimeClause) End() Pos { + if c.Stmt == nil { + return posAddCol(c.Time, 4) + } + return c.Stmt.End() +} + +// CoprocClause represents a Bash coproc clause. +// +// This node will only appear with LangBash. +type CoprocClause struct { + Coproc Pos + Name *Word + Stmt *Stmt +} + +func (c *CoprocClause) Pos() Pos { return c.Coproc } +func (c *CoprocClause) End() Pos { return c.Stmt.End() } + +// LetClause represents a Bash let clause. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type LetClause struct { + Let Pos + Exprs []ArithmExpr +} + +func (l *LetClause) Pos() Pos { return l.Let } +func (l *LetClause) End() Pos { return l.Exprs[len(l.Exprs)-1].End() } + +// BraceExp represents a Bash brace expression, such as "{a,f}" or "{1..10}". +// +// This node will only appear as a result of SplitBraces. +type BraceExp struct { + Sequence bool // {x..y[..incr]} instead of {x,y[,...]} + Elems []*Word +} + +func (b *BraceExp) Pos() Pos { + return posAddCol(b.Elems[0].Pos(), -1) +} + +func (b *BraceExp) End() Pos { + return posAddCol(wordLastEnd(b.Elems), 1) +} + +// TestDecl represents the declaration of a Bats test function. +type TestDecl struct { + Position Pos + Description *Word + Body *Stmt +} + +func (f *TestDecl) Pos() Pos { return f.Position } +func (f *TestDecl) End() Pos { return f.Body.End() } + +func wordLastEnd(ws []*Word) Pos { + if len(ws) == 0 { + return Pos{} + } + return ws[len(ws)-1].End() +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/parser.go b/vendor/mvdan.cc/sh/v3/syntax/parser.go new file mode 100644 index 00000000..99ae17c2 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/parser.go @@ -0,0 +1,2489 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + "unicode/utf8" +) + +// ParserOption is a function which can be passed to NewParser +// to alter its behavior. To apply option to existing Parser +// call it directly, for example KeepComments(true)(parser). +type ParserOption func(*Parser) + +// KeepComments makes the parser parse comments and attach them to +// nodes, as opposed to discarding them. +func KeepComments(enabled bool) ParserOption { + return func(p *Parser) { p.keepComments = enabled } +} + +// LangVariant describes a shell language variant to use when tokenizing and +// parsing shell code. The zero value is LangBash. +type LangVariant int + +const ( + // LangBash corresponds to the GNU Bash language, as described in its + // manual at https://www.gnu.org/software/bash/manual/bash.html. + // + // We currently follow Bash version 5.1. + // + // Its string representation is "bash". + LangBash LangVariant = iota + + // LangPOSIX corresponds to the POSIX Shell language, as described at + // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html. + // + // Its string representation is "posix" or "sh". + LangPOSIX + + // LangMirBSDKorn corresponds to the MirBSD Korn Shell, also known as + // mksh, as described at http://www.mirbsd.org/htman/i386/man1/mksh.htm. + // Note that it shares some features with Bash, due to the the shared + // ancestry that is ksh. + // + // We currently follow mksh version 59. + // + // Its string representation is "mksh". + LangMirBSDKorn + + // LangBats corresponds to the Bash Automated Testing System language, + // as described at https://github.com/bats-core/bats-core. Note that + // it's just a small extension of the Bash language. + // + // Its string representation is "bats". + LangBats + + // LangAuto corresponds to automatic language detection, + // commonly used by end-user applications like shfmt, + // which can guess a file's language variant given its filename or shebang. + // + // At this time, the Parser does not support LangAuto. + LangAuto +) + +// Variant changes the shell language variant that the parser will +// accept. +// +// The passed language variant must be one of the constant values defined in +// this package. +func Variant(l LangVariant) ParserOption { + switch l { + case LangBash, LangPOSIX, LangMirBSDKorn, LangBats: + case LangAuto: + panic("LangAuto is not supported by the parser at this time") + default: + panic(fmt.Sprintf("unknown shell language variant: %d", l)) + } + return func(p *Parser) { p.lang = l } +} + +func (l LangVariant) String() string { + switch l { + case LangBash: + return "bash" + case LangPOSIX: + return "posix" + case LangMirBSDKorn: + return "mksh" + case LangBats: + return "bats" + case LangAuto: + return "auto" + } + return "unknown shell language variant" +} + +func (l *LangVariant) Set(s string) error { + switch s { + case "bash": + *l = LangBash + case "posix", "sh": + *l = LangPOSIX + case "mksh": + *l = LangMirBSDKorn + case "bats": + *l = LangBats + case "auto": + *l = LangAuto + default: + return fmt.Errorf("unknown shell language variant: %q", s) + } + return nil +} + +func (l LangVariant) isBash() bool { + return l == LangBash || l == LangBats +} + +// StopAt configures the lexer to stop at an arbitrary word, treating it +// as if it were the end of the input. It can contain any characters +// except whitespace, and cannot be over four bytes in size. +// +// This can be useful to embed shell code within another language, as +// one can use a special word to mark the delimiters between the two. +// +// As a word, it will only apply when following whitespace or a +// separating token. For example, StopAt("$$") will act on the inputs +// "foo $$" and "foo;$$", but not on "foo '$$'". +// +// The match is done by prefix, so the example above will also act on +// "foo $$bar". +func StopAt(word string) ParserOption { + if len(word) > 4 { + panic("stop word can't be over four bytes in size") + } + if strings.ContainsAny(word, " \t\n\r") { + panic("stop word can't contain whitespace characters") + } + return func(p *Parser) { p.stopAt = []byte(word) } +} + +// NewParser allocates a new Parser and applies any number of options. +func NewParser(options ...ParserOption) *Parser { + p := &Parser{} + for _, opt := range options { + opt(p) + } + return p +} + +// Parse reads and parses a shell program with an optional name. It +// returns the parsed program if no issues were encountered. Otherwise, +// an error is returned. Reads from r are buffered. +// +// Parse can be called more than once, but not concurrently. That is, a +// Parser can be reused once it is done working. +func (p *Parser) Parse(r io.Reader, name string) (*File, error) { + p.reset() + p.f = &File{Name: name} + p.src = r + p.rune() + p.next() + p.f.Stmts, p.f.Last = p.stmtList() + if p.err == nil { + // EOF immediately after heredoc word so no newline to + // trigger it + p.doHeredocs() + } + return p.f, p.err +} + +// Stmts reads and parses statements one at a time, calling a function +// each time one is parsed. If the function returns false, parsing is +// stopped and the function is not called again. +func (p *Parser) Stmts(r io.Reader, fn func(*Stmt) bool) error { + p.reset() + p.f = &File{} + p.src = r + p.rune() + p.next() + p.stmts(fn) + if p.err == nil { + // EOF immediately after heredoc word so no newline to + // trigger it + p.doHeredocs() + } + return p.err +} + +type wrappedReader struct { + *Parser + io.Reader + + lastLine int + accumulated []*Stmt + fn func([]*Stmt) bool +} + +func (w *wrappedReader) Read(p []byte) (n int, err error) { + // If we lexed a newline for the first time, we just finished a line, so + // we may need to give a callback for the edge cases below not covered + // by Parser.Stmts. + if (w.r == '\n' || w.r == escNewl) && w.line > w.lastLine { + if w.Incomplete() { + // Incomplete statement; call back to print "> ". + if !w.fn(w.accumulated) { + return 0, io.EOF + } + } else if len(w.accumulated) == 0 { + // Nothing was parsed; call back to print another "$ ". + if !w.fn(nil) { + return 0, io.EOF + } + } + w.lastLine = w.line + } + return w.Reader.Read(p) +} + +// Interactive implements what is necessary to parse statements in an +// interactive shell. The parser will call the given function under two +// circumstances outlined below. +// +// If a line containing any number of statements is parsed, the function will be +// called with said statements. +// +// If a line ending in an incomplete statement is parsed, the function will be +// called with any fully parsed statements, and [Parser.Incomplete] will return true. +// +// One can imagine a simple interactive shell implementation as follows: +// +// fmt.Fprintf(os.Stdout, "$ ") +// parser.Interactive(os.Stdin, func(stmts []*syntax.Stmt) bool { +// if parser.Incomplete() { +// fmt.Fprintf(os.Stdout, "> ") +// return true +// } +// run(stmts) +// fmt.Fprintf(os.Stdout, "$ ") +// return true +// } +// +// If the callback function returns false, parsing is stopped and the function +// is not called again. +func (p *Parser) Interactive(r io.Reader, fn func([]*Stmt) bool) error { + w := wrappedReader{Parser: p, Reader: r, fn: fn} + return p.Stmts(&w, func(stmt *Stmt) bool { + w.accumulated = append(w.accumulated, stmt) + // We finished parsing a statement and we're at a newline token, + // so we finished fully parsing a number of statements. Call + // back to run the statements and print "$ ". + if p.tok == _Newl { + if !fn(w.accumulated) { + return false + } + w.accumulated = w.accumulated[:0] + // The callback above would already print "$ ", so we + // don't want the subsequent wrappedReader.Read to cause + // another "$ " print thinking that nothing was parsed. + w.lastLine = w.line + 1 + } + return true + }) +} + +// Words reads and parses words one at a time, calling a function each time one +// is parsed. If the function returns false, parsing is stopped and the function +// is not called again. +// +// Newlines are skipped, meaning that multi-line input will work fine. If the +// parser encounters a token that isn't a word, such as a semicolon, an error +// will be returned. +// +// Note that the lexer doesn't currently tokenize spaces, so it may need to read +// a non-space byte such as a newline or a letter before finishing the parsing +// of a word. This will be fixed in the future. +func (p *Parser) Words(r io.Reader, fn func(*Word) bool) error { + p.reset() + p.f = &File{} + p.src = r + p.rune() + p.next() + for { + p.got(_Newl) + w := p.getWord() + if w == nil { + if p.tok != _EOF { + p.curErr("%s is not a valid word", p.tok) + } + return p.err + } + if !fn(w) { + return nil + } + } +} + +// Document parses a single here-document word. That is, it parses the input as +// if they were lines following a < 0 || p.litBs != nil +} + +const bufSize = 1 << 10 + +func (p *Parser) reset() { + p.tok, p.val = illegalTok, "" + p.eqlOffs = 0 + p.bs, p.bsp = nil, 0 + p.offs, p.line, p.col = 0, 1, 1 + p.r, p.w = 0, 0 + p.err, p.readErr = nil, nil + p.quote, p.forbidNested = noState, false + p.openStmts = 0 + p.heredocs, p.buriedHdocs = p.heredocs[:0], 0 + p.parsingDoc = false + p.openBquotes, p.buriedBquotes = 0, 0 + p.accComs, p.curComs = nil, &p.accComs + p.litBatch = nil + p.wordBatch = nil + p.stmtBatch = nil + p.callBatch = nil +} + +func (p *Parser) nextPos() Pos { + // TODO: detect offset overflow while lexing as well. + var line, col uint + if !p.lineOverflow { + line = uint(p.line) + } + if !p.colOverflow { + col = uint(p.col) + } + return NewPos(uint(p.offs+p.bsp-p.w), line, col) +} + +func (p *Parser) lit(pos Pos, val string) *Lit { + if len(p.litBatch) == 0 { + p.litBatch = make([]Lit, 64) + } + l := &p.litBatch[0] + p.litBatch = p.litBatch[1:] + l.ValuePos = pos + l.ValueEnd = p.nextPos() + l.Value = val + return l +} + +type wordAlloc struct { + word Word + parts [1]WordPart +} + +func (p *Parser) wordAnyNumber() *Word { + if len(p.wordBatch) == 0 { + p.wordBatch = make([]wordAlloc, 32) + } + alloc := &p.wordBatch[0] + p.wordBatch = p.wordBatch[1:] + w := &alloc.word + w.Parts = p.wordParts(alloc.parts[:0]) + return w +} + +func (p *Parser) wordOne(part WordPart) *Word { + if len(p.wordBatch) == 0 { + p.wordBatch = make([]wordAlloc, 32) + } + alloc := &p.wordBatch[0] + p.wordBatch = p.wordBatch[1:] + w := &alloc.word + w.Parts = alloc.parts[:1] + w.Parts[0] = part + return w +} + +func (p *Parser) stmt(pos Pos) *Stmt { + if len(p.stmtBatch) == 0 { + p.stmtBatch = make([]Stmt, 32) + } + s := &p.stmtBatch[0] + p.stmtBatch = p.stmtBatch[1:] + s.Position = pos + return s +} + +type callAlloc struct { + ce CallExpr + ws [4]*Word +} + +func (p *Parser) call(w *Word) *CallExpr { + if len(p.callBatch) == 0 { + p.callBatch = make([]callAlloc, 32) + } + alloc := &p.callBatch[0] + p.callBatch = p.callBatch[1:] + ce := &alloc.ce + ce.Args = alloc.ws[:1] + ce.Args[0] = w + return ce +} + +//go:generate stringer -type=quoteState + +type quoteState uint32 + +const ( + noState quoteState = 1 << iota + subCmd + subCmdBckquo + dblQuotes + hdocWord + hdocBody + hdocBodyTabs + arithmExpr + arithmExprLet + arithmExprCmd + arithmExprBrack + testExpr + testExprRegexp + switchCase + paramExpName + paramExpSlice + paramExpRepl + paramExpExp + arrayElems + + allKeepSpaces = paramExpRepl | dblQuotes | hdocBody | + hdocBodyTabs | paramExpExp + allRegTokens = noState | subCmd | subCmdBckquo | hdocWord | + switchCase | arrayElems | testExpr + allArithmExpr = arithmExpr | arithmExprLet | arithmExprCmd | + arithmExprBrack | paramExpSlice + allParamReg = paramExpName | paramExpSlice + allParamExp = allParamReg | paramExpRepl | paramExpExp | arithmExprBrack +) + +type saveState struct { + quote quoteState + buriedHdocs int +} + +func (p *Parser) preNested(quote quoteState) (s saveState) { + s.quote, s.buriedHdocs = p.quote, p.buriedHdocs + p.buriedHdocs, p.quote = len(p.heredocs), quote + return +} + +func (p *Parser) postNested(s saveState) { + p.quote, p.buriedHdocs = s.quote, s.buriedHdocs +} + +func (p *Parser) unquotedWordBytes(w *Word) ([]byte, bool) { + buf := make([]byte, 0, 4) + didUnquote := false + for _, wp := range w.Parts { + buf, didUnquote = p.unquotedWordPart(buf, wp, false) + } + return buf, didUnquote +} + +func (p *Parser) unquotedWordPart(buf []byte, wp WordPart, quotes bool) (_ []byte, quoted bool) { + switch x := wp.(type) { + case *Lit: + for i := 0; i < len(x.Value); i++ { + if b := x.Value[i]; b == '\\' && !quotes { + if i++; i < len(x.Value) { + buf = append(buf, x.Value[i]) + } + quoted = true + } else { + buf = append(buf, b) + } + } + case *SglQuoted: + buf = append(buf, []byte(x.Value)...) + quoted = true + case *DblQuoted: + for _, wp2 := range x.Parts { + buf, _ = p.unquotedWordPart(buf, wp2, true) + } + quoted = true + } + return buf, quoted +} + +func (p *Parser) doHeredocs() { + hdocs := p.heredocs[p.buriedHdocs:] + if len(hdocs) == 0 { + // Nothing do do; don't even issue a read. + return + } + p.rune() // consume '\n', since we know p.tok == _Newl + old := p.quote + p.heredocs = p.heredocs[:p.buriedHdocs] + for i, r := range hdocs { + if p.err != nil { + break + } + p.quote = hdocBody + if r.Op == DashHdoc { + p.quote = hdocBodyTabs + } + stop, quoted := p.unquotedWordBytes(r.Word) + p.hdocStops = append(p.hdocStops, stop) + if i > 0 && p.r == '\n' { + p.rune() + } + lastLine := p.line + if quoted { + r.Hdoc = p.quotedHdocWord() + } else { + p.next() + r.Hdoc = p.getWord() + } + if r.Hdoc != nil { + lastLine = int(r.Hdoc.End().Line()) + } + if lastLine < p.line { + // TODO: It seems like this triggers more often than it + // should. Look into it. + l := p.lit(p.nextPos(), "") + if r.Hdoc == nil { + r.Hdoc = p.wordOne(l) + } else { + r.Hdoc.Parts = append(r.Hdoc.Parts, l) + } + } + if stop := p.hdocStops[len(p.hdocStops)-1]; stop != nil { + p.posErr(r.Pos(), "unclosed here-document '%s'", stop) + } + p.hdocStops = p.hdocStops[:len(p.hdocStops)-1] + } + p.quote = old +} + +func (p *Parser) got(tok token) bool { + if p.tok == tok { + p.next() + return true + } + return false +} + +func (p *Parser) gotRsrv(val string) (Pos, bool) { + pos := p.pos + if p.tok == _LitWord && p.val == val { + p.next() + return pos, true + } + return pos, false +} + +func readableStr(s string) string { + // don't quote tokens like & or } + if s != "" && s[0] >= 'a' && s[0] <= 'z' { + return strconv.Quote(s) + } + return s +} + +func (p *Parser) followErr(pos Pos, left, right string) { + leftStr := readableStr(left) + p.posErr(pos, "%s must be followed by %s", leftStr, right) +} + +func (p *Parser) followErrExp(pos Pos, left string) { + p.followErr(pos, left, "an expression") +} + +func (p *Parser) follow(lpos Pos, left string, tok token) { + if !p.got(tok) { + p.followErr(lpos, left, tok.String()) + } +} + +func (p *Parser) followRsrv(lpos Pos, left, val string) Pos { + pos, ok := p.gotRsrv(val) + if !ok { + p.followErr(lpos, left, fmt.Sprintf("%q", val)) + } + return pos +} + +func (p *Parser) followStmts(left string, lpos Pos, stops ...string) ([]*Stmt, []Comment) { + if p.got(semicolon) { + return nil, nil + } + newLine := p.got(_Newl) + stmts, last := p.stmtList(stops...) + if len(stmts) < 1 && !newLine { + p.followErr(lpos, left, "a statement list") + } + return stmts, last +} + +func (p *Parser) followWordTok(tok token, pos Pos) *Word { + w := p.getWord() + if w == nil { + p.followErr(pos, tok.String(), "a word") + } + return w +} + +func (p *Parser) stmtEnd(n Node, start, end string) Pos { + pos, ok := p.gotRsrv(end) + if !ok { + p.posErr(n.Pos(), "%s statement must end with %q", start, end) + } + return pos +} + +func (p *Parser) quoteErr(lpos Pos, quote token) { + p.posErr(lpos, "reached %s without closing quote %s", + p.tok.String(), quote) +} + +func (p *Parser) matchingErr(lpos Pos, left, right any) { + p.posErr(lpos, "reached %s without matching %s with %s", + p.tok.String(), left, right) +} + +func (p *Parser) matched(lpos Pos, left, right token) Pos { + pos := p.pos + if !p.got(right) { + p.matchingErr(lpos, left, right) + } + return pos +} + +func (p *Parser) errPass(err error) { + if p.err == nil { + p.err = err + p.bsp = len(p.bs) + 1 + p.r = utf8.RuneSelf + p.w = 1 + p.tok = _EOF + } +} + +// IsIncomplete reports whether a Parser error could have been avoided with +// extra input bytes. For example, if an [io.EOF] was encountered while there was +// an unclosed quote or parenthesis. +func IsIncomplete(err error) bool { + perr, ok := err.(ParseError) + return ok && perr.Incomplete +} + +// IsKeyword returns true if the given word is part of the language keywords. +func IsKeyword(word string) bool { + // This list has been copied from the bash 5.1 source code, file y.tab.c +4460 + switch word { + case + "!", + "[[", // only if COND_COMMAND is defined + "]]", // only if COND_COMMAND is defined + "case", + "coproc", // only if COPROCESS_SUPPORT is defined + "do", + "done", + "else", + "esac", + "fi", + "for", + "function", + "if", + "in", + "select", // only if SELECT_COMMAND is defined + "then", + "time", // only if COMMAND_TIMING is defined + "until", + "while", + "{", + "}": + return true + } + return false +} + +// ParseError represents an error found when parsing a source file, from which +// the parser cannot recover. +type ParseError struct { + Filename string + Pos Pos + Text string + + Incomplete bool +} + +func (e ParseError) Error() string { + if e.Filename == "" { + return fmt.Sprintf("%s: %s", e.Pos.String(), e.Text) + } + return fmt.Sprintf("%s:%s: %s", e.Filename, e.Pos.String(), e.Text) +} + +// LangError is returned when the parser encounters code that is only valid in +// other shell language variants. The error includes what feature is not present +// in the current language variant, and what languages support it. +type LangError struct { + Filename string + Pos Pos + Feature string + Langs []LangVariant +} + +func (e LangError) Error() string { + var buf bytes.Buffer + if e.Filename != "" { + buf.WriteString(e.Filename + ":") + } + buf.WriteString(e.Pos.String() + ": ") + buf.WriteString(e.Feature) + if strings.HasSuffix(e.Feature, "s") { + buf.WriteString(" are a ") + } else { + buf.WriteString(" is a ") + } + for i, lang := range e.Langs { + if i > 0 { + buf.WriteString("/") + } + buf.WriteString(lang.String()) + } + buf.WriteString(" feature") + return buf.String() +} + +func (p *Parser) posErr(pos Pos, format string, a ...any) { + p.errPass(ParseError{ + Filename: p.f.Name, + Pos: pos, + Text: fmt.Sprintf(format, a...), + Incomplete: p.tok == _EOF && p.Incomplete(), + }) +} + +func (p *Parser) curErr(format string, a ...any) { + p.posErr(p.pos, format, a...) +} + +func (p *Parser) langErr(pos Pos, feature string, langs ...LangVariant) { + p.errPass(LangError{ + Filename: p.f.Name, + Pos: pos, + Feature: feature, + Langs: langs, + }) +} + +func (p *Parser) stmts(fn func(*Stmt) bool, stops ...string) { + gotEnd := true +loop: + for p.tok != _EOF { + newLine := p.got(_Newl) + switch p.tok { + case _LitWord: + for _, stop := range stops { + if p.val == stop { + break loop + } + } + case rightParen: + if p.quote == subCmd { + break loop + } + case bckQuote: + if p.backquoteEnd() { + break loop + } + case dblSemicolon, semiAnd, dblSemiAnd, semiOr: + if p.quote == switchCase { + break loop + } + p.curErr("%s can only be used in a case clause", p.tok) + } + if !newLine && !gotEnd { + p.curErr("statements must be separated by &, ; or a newline") + } + if p.tok == _EOF { + break + } + p.openStmts++ + s := p.getStmt(true, false, false) + p.openStmts-- + if s == nil { + p.invalidStmtStart() + break + } + gotEnd = s.Semicolon.IsValid() + if !fn(s) { + break + } + } +} + +func (p *Parser) stmtList(stops ...string) ([]*Stmt, []Comment) { + var stmts []*Stmt + var last []Comment + fn := func(s *Stmt) bool { + stmts = append(stmts, s) + return true + } + p.stmts(fn, stops...) + split := len(p.accComs) + if p.tok == _LitWord && (p.val == "elif" || p.val == "else" || p.val == "fi") { + // Split the comments, so that any aligned with an opening token + // get attached to it. For example: + // + // if foo; then + // # inside the body + // # document the else + // else + // fi + // TODO(mvdan): look into deduplicating this with similar logic + // in caseItems. + for i := len(p.accComs) - 1; i >= 0; i-- { + c := p.accComs[i] + if c.Pos().Col() != p.pos.Col() { + break + } + split = i + } + } + if split > 0 { // keep last nil if empty + last = p.accComs[:split] + } + p.accComs = p.accComs[split:] + return stmts, last +} + +func (p *Parser) invalidStmtStart() { + switch p.tok { + case semicolon, and, or, andAnd, orOr: + p.curErr("%s can only immediately follow a statement", p.tok) + case rightParen: + p.curErr("%s can only be used to close a subshell", p.tok) + default: + p.curErr("%s is not a valid start for a statement", p.tok) + } +} + +func (p *Parser) getWord() *Word { + if w := p.wordAnyNumber(); len(w.Parts) > 0 && p.err == nil { + return w + } + return nil +} + +func (p *Parser) getLit() *Lit { + switch p.tok { + case _Lit, _LitWord, _LitRedir: + l := p.lit(p.pos, p.val) + p.next() + return l + } + return nil +} + +func (p *Parser) wordParts(wps []WordPart) []WordPart { + for { + n := p.wordPart() + if n == nil { + if len(wps) == 0 { + return nil // normalize empty lists into nil + } + return wps + } + wps = append(wps, n) + if p.spaced { + return wps + } + } +} + +func (p *Parser) ensureNoNested() { + if p.forbidNested { + p.curErr("expansions not allowed in heredoc words") + } +} + +func (p *Parser) wordPart() WordPart { + switch p.tok { + case _Lit, _LitWord, _LitRedir: + l := p.lit(p.pos, p.val) + p.next() + return l + case dollBrace: + p.ensureNoNested() + switch p.r { + case '|': + if p.lang != LangMirBSDKorn { + p.curErr(`"${|stmts;}" is a mksh feature`) + } + fallthrough + case ' ', '\t', '\n': + if p.lang != LangMirBSDKorn { + p.curErr(`"${ stmts;}" is a mksh feature`) + } + cs := &CmdSubst{ + Left: p.pos, + TempFile: p.r != '|', + ReplyVar: p.r == '|', + } + old := p.preNested(subCmd) + p.rune() // don't tokenize '|' + p.next() + cs.Stmts, cs.Last = p.stmtList("}") + p.postNested(old) + pos, ok := p.gotRsrv("}") + if !ok { + p.matchingErr(cs.Left, "${", "}") + } + cs.Right = pos + return cs + default: + return p.paramExp() + } + case dollDblParen, dollBrack: + p.ensureNoNested() + left := p.tok + ar := &ArithmExp{Left: p.pos, Bracket: left == dollBrack} + var old saveState + if ar.Bracket { + old = p.preNested(arithmExprBrack) + } else { + old = p.preNested(arithmExpr) + } + p.next() + if p.got(hash) { + if p.lang != LangMirBSDKorn { + p.langErr(ar.Pos(), "unsigned expressions", LangMirBSDKorn) + } + ar.Unsigned = true + } + ar.X = p.followArithm(left, ar.Left) + if ar.Bracket { + if p.tok != rightBrack { + p.arithmMatchingErr(ar.Left, dollBrack, rightBrack) + } + p.postNested(old) + ar.Right = p.pos + p.next() + } else { + ar.Right = p.arithmEnd(dollDblParen, ar.Left, old) + } + return ar + case dollParen: + p.ensureNoNested() + cs := &CmdSubst{Left: p.pos} + old := p.preNested(subCmd) + p.next() + cs.Stmts, cs.Last = p.stmtList() + p.postNested(old) + cs.Right = p.matched(cs.Left, leftParen, rightParen) + return cs + case dollar: + r := p.r + switch { + case singleRuneParam(r): + p.tok, p.val = _LitWord, string(r) + p.rune() + case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', + '0' <= r && r <= '9', r == '_', r == '\\': + p.advanceNameCont(r) + default: + l := p.lit(p.pos, "$") + p.next() + return l + } + p.ensureNoNested() + pe := &ParamExp{Dollar: p.pos, Short: true} + p.pos = posAddCol(p.pos, 1) + pe.Param = p.getLit() + if pe.Param != nil && pe.Param.Value == "" { + l := p.lit(pe.Dollar, "$") + // e.g. "$\\\"" within double quotes, so we must + // keep the rest of the literal characters. + l.ValueEnd = posAddCol(l.ValuePos, 1) + return l + } + return pe + case cmdIn, cmdOut: + p.ensureNoNested() + ps := &ProcSubst{Op: ProcOperator(p.tok), OpPos: p.pos} + old := p.preNested(subCmd) + p.next() + ps.Stmts, ps.Last = p.stmtList() + p.postNested(old) + ps.Rparen = p.matched(ps.OpPos, token(ps.Op), rightParen) + return ps + case sglQuote, dollSglQuote: + sq := &SglQuoted{Left: p.pos, Dollar: p.tok == dollSglQuote} + r := p.r + for p.newLit(r); ; r = p.rune() { + switch r { + case '\\': + if sq.Dollar { + p.rune() + } + case '\'': + sq.Right = p.nextPos() + sq.Value = p.endLit() + + // restore openBquotes + p.openBquotes = p.buriedBquotes + p.buriedBquotes = 0 + + p.rune() + p.next() + return sq + case escNewl: + p.litBs = append(p.litBs, '\\', '\n') + case utf8.RuneSelf: + p.tok = _EOF + p.quoteErr(sq.Pos(), sglQuote) + return nil + } + } + case dblQuote, dollDblQuote: + if p.quote == dblQuotes { + // p.tok == dblQuote, as "foo$" puts $ in the lit + return nil + } + return p.dblQuoted() + case bckQuote: + if p.backquoteEnd() { + return nil + } + p.ensureNoNested() + cs := &CmdSubst{Left: p.pos, Backquotes: true} + old := p.preNested(subCmdBckquo) + p.openBquotes++ + + // The lexer didn't call p.rune for us, so that it could have + // the right p.openBquotes to properly handle backslashes. + p.rune() + + p.next() + cs.Stmts, cs.Last = p.stmtList() + if p.tok == bckQuote && p.lastBquoteEsc < p.openBquotes-1 { + // e.g. found ` before the nested backquote \` was closed. + p.tok = _EOF + p.quoteErr(cs.Pos(), bckQuote) + } + p.postNested(old) + p.openBquotes-- + cs.Right = p.pos + + // Like above, the lexer didn't call p.rune for us. + p.rune() + if !p.got(bckQuote) { + p.quoteErr(cs.Pos(), bckQuote) + } + return cs + case globQuest, globStar, globPlus, globAt, globExcl: + if p.lang == LangPOSIX { + p.langErr(p.pos, "extended globs", LangBash, LangMirBSDKorn) + } + eg := &ExtGlob{Op: GlobOperator(p.tok), OpPos: p.pos} + lparens := 1 + r := p.r + globLoop: + for p.newLit(r); ; r = p.rune() { + switch r { + case utf8.RuneSelf: + break globLoop + case '(': + lparens++ + case ')': + if lparens--; lparens == 0 { + break globLoop + } + } + } + eg.Pattern = p.lit(posAddCol(eg.OpPos, 2), p.endLit()) + p.rune() + p.next() + if lparens != 0 { + p.matchingErr(eg.OpPos, eg.Op, rightParen) + } + return eg + default: + return nil + } +} + +func (p *Parser) dblQuoted() *DblQuoted { + alloc := &struct { + quoted DblQuoted + parts [1]WordPart + }{ + quoted: DblQuoted{Left: p.pos, Dollar: p.tok == dollDblQuote}, + } + q := &alloc.quoted + old := p.quote + p.quote = dblQuotes + p.next() + q.Parts = p.wordParts(alloc.parts[:0]) + p.quote = old + q.Right = p.pos + if !p.got(dblQuote) { + p.quoteErr(q.Pos(), dblQuote) + } + return q +} + +func singleRuneParam(r rune) bool { + switch r { + case '@', '*', '#', '$', '?', '!', '-', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return true + } + return false +} + +func (p *Parser) paramExp() *ParamExp { + pe := &ParamExp{Dollar: p.pos} + old := p.quote + p.quote = paramExpName + if p.r == '#' { + p.tok = hash + p.pos = p.nextPos() + p.rune() + } else { + p.next() + } + switch p.tok { + case hash: + if paramNameOp(p.r) { + pe.Length = true + p.next() + } + case perc: + if p.lang != LangMirBSDKorn { + p.posErr(pe.Pos(), `"${%%foo}" is a mksh feature`) + } + if paramNameOp(p.r) { + pe.Width = true + p.next() + } + case exclMark: + if paramNameOp(p.r) { + pe.Excl = true + p.next() + } + } + op := p.tok + switch p.tok { + case _Lit, _LitWord: + if !numberLiteral(p.val) && !ValidName(p.val) { + p.curErr("invalid parameter name") + } + pe.Param = p.lit(p.pos, p.val) + p.next() + case quest, minus: + if pe.Length && p.r != '}' { + // actually ${#-default}, not ${#-}; fix the ambiguity + pe.Length = false + pe.Param = p.lit(posAddCol(p.pos, -1), "#") + pe.Param.ValueEnd = p.pos + break + } + fallthrough + case at, star, hash, exclMark, dollar: + pe.Param = p.lit(p.pos, p.tok.String()) + p.next() + default: + p.curErr("parameter expansion requires a literal") + } + switch p.tok { + case _Lit, _LitWord: + p.curErr("%s cannot be followed by a word", op) + case rightBrace: + if pe.Excl && p.lang == LangPOSIX { + p.posErr(pe.Pos(), `"${!foo}" is a bash/mksh feature`) + } + pe.Rbrace = p.pos + p.quote = old + p.next() + return pe + case leftBrack: + if p.lang == LangPOSIX { + p.langErr(p.pos, "arrays", LangBash, LangMirBSDKorn) + } + if !ValidName(pe.Param.Value) { + p.curErr("cannot index a special parameter name") + } + pe.Index = p.eitherIndex() + } + if p.tok == rightBrace { + pe.Rbrace = p.pos + p.quote = old + p.next() + return pe + } + if p.tok != _EOF && (pe.Length || pe.Width) { + p.curErr("cannot combine multiple parameter expansion operators") + } + switch p.tok { + case slash, dblSlash: + // pattern search and replace + if p.lang == LangPOSIX { + p.langErr(p.pos, "search and replace", LangBash, LangMirBSDKorn) + } + pe.Repl = &Replace{All: p.tok == dblSlash} + p.quote = paramExpRepl + p.next() + pe.Repl.Orig = p.getWord() + p.quote = paramExpExp + if p.got(slash) { + pe.Repl.With = p.getWord() + } + case colon: + // slicing + if p.lang == LangPOSIX { + p.langErr(p.pos, "slicing", LangBash, LangMirBSDKorn) + } + pe.Slice = &Slice{} + colonPos := p.pos + p.quote = paramExpSlice + if p.next(); p.tok != colon { + pe.Slice.Offset = p.followArithm(colon, colonPos) + } + colonPos = p.pos + if p.got(colon) { + pe.Slice.Length = p.followArithm(colon, colonPos) + } + // Need to use a different matched style so arithm errors + // get reported correctly + p.quote = old + pe.Rbrace = p.pos + p.matchedArithm(pe.Dollar, dollBrace, rightBrace) + return pe + case caret, dblCaret, comma, dblComma: + // upper/lower case + if !p.lang.isBash() { + p.langErr(p.pos, "this expansion operator", LangBash) + } + pe.Exp = p.paramExpExp() + case at, star: + switch { + case p.tok == at && p.lang == LangPOSIX: + p.langErr(p.pos, "this expansion operator", LangBash, LangMirBSDKorn) + case p.tok == star && !pe.Excl: + p.curErr("not a valid parameter expansion operator: %v", p.tok) + case pe.Excl && p.r == '}': + if !p.lang.isBash() { + p.posErr(pe.Pos(), `"${!foo`+p.tok.String()+`}" is a bash feature`) + } + pe.Names = ParNamesOperator(p.tok) + p.next() + default: + pe.Exp = p.paramExpExp() + } + case plus, colPlus, minus, colMinus, quest, colQuest, assgn, colAssgn, + perc, dblPerc, hash, dblHash: + pe.Exp = p.paramExpExp() + case _EOF: + default: + p.curErr("not a valid parameter expansion operator: %v", p.tok) + } + p.quote = old + pe.Rbrace = p.pos + p.matched(pe.Dollar, dollBrace, rightBrace) + return pe +} + +func (p *Parser) paramExpExp() *Expansion { + op := ParExpOperator(p.tok) + p.quote = paramExpExp + p.next() + if op == OtherParamOps { + switch p.tok { + case _Lit, _LitWord: + default: + p.curErr("@ expansion operator requires a literal") + } + switch p.val { + case "a", "u", "A", "E", "K", "L", "P", "U": + if !p.lang.isBash() { + p.langErr(p.pos, "this expansion operator", LangBash) + } + case "#": + if p.lang != LangMirBSDKorn { + p.langErr(p.pos, "this expansion operator", LangMirBSDKorn) + } + case "Q": + default: + p.curErr("invalid @ expansion operator") + } + } + return &Expansion{Op: op, Word: p.getWord()} +} + +func (p *Parser) eitherIndex() ArithmExpr { + old := p.quote + lpos := p.pos + p.quote = arithmExprBrack + p.next() + if p.tok == star || p.tok == at { + p.tok, p.val = _LitWord, p.tok.String() + } + expr := p.followArithm(leftBrack, lpos) + p.quote = old + p.matchedArithm(lpos, leftBrack, rightBrack) + return expr +} + +func (p *Parser) stopToken() bool { + switch p.tok { + case _EOF, _Newl, semicolon, and, or, andAnd, orOr, orAnd, dblSemicolon, + semiAnd, dblSemiAnd, semiOr, rightParen: + return true + case bckQuote: + return p.backquoteEnd() + } + return false +} + +func (p *Parser) backquoteEnd() bool { + return p.lastBquoteEsc < p.openBquotes +} + +// ValidName returns whether val is a valid name as per the POSIX spec. +func ValidName(val string) bool { + if val == "" { + return false + } + for i, r := range val { + switch { + case 'a' <= r && r <= 'z': + case 'A' <= r && r <= 'Z': + case r == '_': + case i > 0 && '0' <= r && r <= '9': + default: + return false + } + } + return true +} + +func numberLiteral(val string) bool { + for _, r := range val { + if '0' > r || r > '9' { + return false + } + } + return true +} + +func (p *Parser) hasValidIdent() bool { + if p.tok != _Lit && p.tok != _LitWord { + return false + } + if end := p.eqlOffs; end > 0 { + if p.val[end-1] == '+' && p.lang != LangPOSIX { + end-- // a+=x + } + if ValidName(p.val[:end]) { + return true + } + } else if !ValidName(p.val) { + return false // *[i]=x + } + return p.r == '[' // a[i]=x +} + +func (p *Parser) getAssign(needEqual bool) *Assign { + as := &Assign{} + if p.eqlOffs > 0 { // foo=bar + nameEnd := p.eqlOffs + if p.lang != LangPOSIX && p.val[p.eqlOffs-1] == '+' { + // a+=b + as.Append = true + nameEnd-- + } + as.Name = p.lit(p.pos, p.val[:nameEnd]) + // since we're not using the entire p.val + as.Name.ValueEnd = posAddCol(as.Name.ValuePos, nameEnd) + left := p.lit(posAddCol(p.pos, 1), p.val[p.eqlOffs+1:]) + if left.Value != "" { + left.ValuePos = posAddCol(left.ValuePos, p.eqlOffs) + as.Value = p.wordOne(left) + } + p.next() + } else { // foo[x]=bar + as.Name = p.lit(p.pos, p.val) + // hasValidIdent already checks p.r is '[' + p.rune() + p.pos = posAddCol(p.pos, 1) + as.Index = p.eitherIndex() + if p.spaced || p.stopToken() { + if needEqual { + p.followErr(as.Pos(), "a[b]", "=") + } else { + as.Naked = true + return as + } + } + if len(p.val) > 0 && p.val[0] == '+' { + as.Append = true + p.val = p.val[1:] + p.pos = posAddCol(p.pos, 1) + } + if len(p.val) < 1 || p.val[0] != '=' { + if as.Append { + p.followErr(as.Pos(), "a[b]+", "=") + } else { + p.followErr(as.Pos(), "a[b]", "=") + } + return nil + } + p.pos = posAddCol(p.pos, 1) + p.val = p.val[1:] + if p.val == "" { + p.next() + } + } + if p.spaced || p.stopToken() { + return as + } + if as.Value == nil && p.tok == leftParen { + if p.lang == LangPOSIX { + p.langErr(p.pos, "arrays", LangBash, LangMirBSDKorn) + } + if as.Index != nil { + p.curErr("arrays cannot be nested") + } + as.Array = &ArrayExpr{Lparen: p.pos} + newQuote := p.quote + if p.lang.isBash() { + newQuote = arrayElems + } + old := p.preNested(newQuote) + p.next() + p.got(_Newl) + for p.tok != _EOF && p.tok != rightParen { + ae := &ArrayElem{} + ae.Comments, p.accComs = p.accComs, nil + if p.tok == leftBrack { + left := p.pos + ae.Index = p.eitherIndex() + p.follow(left, `"[x]"`, assgn) + } + if ae.Value = p.getWord(); ae.Value == nil { + switch p.tok { + case leftParen: + p.curErr("arrays cannot be nested") + return nil + case _Newl, rightParen, leftBrack: + // TODO: support [index]=[ + default: + p.curErr("array element values must be words") + return nil + } + } + if len(p.accComs) > 0 { + c := p.accComs[0] + if c.Pos().Line() == ae.End().Line() { + ae.Comments = append(ae.Comments, c) + p.accComs = p.accComs[1:] + } + } + as.Array.Elems = append(as.Array.Elems, ae) + p.got(_Newl) + } + as.Array.Last, p.accComs = p.accComs, nil + p.postNested(old) + as.Array.Rparen = p.matched(as.Array.Lparen, leftParen, rightParen) + } else if w := p.getWord(); w != nil { + if as.Value == nil { + as.Value = w + } else { + as.Value.Parts = append(as.Value.Parts, w.Parts...) + } + } + return as +} + +func (p *Parser) peekRedir() bool { + switch p.tok { + case rdrOut, appOut, rdrIn, dplIn, dplOut, clbOut, rdrInOut, + hdoc, dashHdoc, wordHdoc, rdrAll, appAll, _LitRedir: + return true + } + return false +} + +func (p *Parser) doRedirect(s *Stmt) { + var r *Redirect + if s.Redirs == nil { + var alloc struct { + redirs [4]*Redirect + redir Redirect + } + s.Redirs = alloc.redirs[:0] + r = &alloc.redir + s.Redirs = append(s.Redirs, r) + } else { + r = &Redirect{} + s.Redirs = append(s.Redirs, r) + } + r.N = p.getLit() + if !p.lang.isBash() && r.N != nil && r.N.Value[0] == '{' { + p.langErr(r.N.Pos(), "{varname} redirects", LangBash) + } + if p.lang == LangPOSIX && (p.tok == rdrAll || p.tok == appAll) { + p.langErr(p.pos, "&> redirects", LangBash, LangMirBSDKorn) + } + r.Op, r.OpPos = RedirOperator(p.tok), p.pos + p.next() + switch r.Op { + case Hdoc, DashHdoc: + old := p.quote + p.quote, p.forbidNested = hdocWord, true + p.heredocs = append(p.heredocs, r) + r.Word = p.followWordTok(token(r.Op), r.OpPos) + p.quote, p.forbidNested = old, false + if p.tok == _Newl { + if len(p.accComs) > 0 { + c := p.accComs[0] + if c.Pos().Line() == s.End().Line() { + s.Comments = append(s.Comments, c) + p.accComs = p.accComs[1:] + } + } + p.doHeredocs() + } + case WordHdoc: + if p.lang == LangPOSIX { + p.langErr(r.OpPos, "herestrings", LangBash, LangMirBSDKorn) + } + fallthrough + default: + r.Word = p.followWordTok(token(r.Op), r.OpPos) + } +} + +func (p *Parser) getStmt(readEnd, binCmd, fnBody bool) *Stmt { + pos, ok := p.gotRsrv("!") + s := p.stmt(pos) + if ok { + s.Negated = true + if p.stopToken() { + p.posErr(s.Pos(), `"!" cannot form a statement alone`) + } + if _, ok := p.gotRsrv("!"); ok { + p.posErr(s.Pos(), `cannot negate a command multiple times`) + } + } + if s = p.gotStmtPipe(s, false); s == nil || p.err != nil { + return nil + } + // instead of using recursion, iterate manually + for p.tok == andAnd || p.tok == orOr { + if binCmd { + // left associativity: in a list of BinaryCmds, the + // right recursion should only read a single element + return s + } + b := &BinaryCmd{ + OpPos: p.pos, + Op: BinCmdOperator(p.tok), + X: s, + } + p.next() + p.got(_Newl) + b.Y = p.getStmt(false, true, false) + if b.Y == nil || p.err != nil { + p.followErr(b.OpPos, b.Op.String(), "a statement") + return nil + } + s = p.stmt(s.Position) + s.Cmd = b + s.Comments, b.X.Comments = b.X.Comments, nil + } + if readEnd { + switch p.tok { + case semicolon: + s.Semicolon = p.pos + p.next() + case and: + s.Semicolon = p.pos + p.next() + s.Background = true + case orAnd: + s.Semicolon = p.pos + p.next() + s.Coprocess = true + } + } + if len(p.accComs) > 0 && !binCmd && !fnBody { + c := p.accComs[0] + if c.Pos().Line() == s.End().Line() { + s.Comments = append(s.Comments, c) + p.accComs = p.accComs[1:] + } + } + return s +} + +func (p *Parser) gotStmtPipe(s *Stmt, binCmd bool) *Stmt { + s.Comments, p.accComs = p.accComs, nil + switch p.tok { + case _LitWord: + switch p.val { + case "{": + p.block(s) + case "if": + p.ifClause(s) + case "while", "until": + p.whileClause(s, p.val == "until") + case "for": + p.forClause(s) + case "case": + p.caseClause(s) + case "}": + p.curErr(`%q can only be used to close a block`, p.val) + case "then": + p.curErr(`%q can only be used in an if`, p.val) + case "elif": + p.curErr(`%q can only be used in an if`, p.val) + case "fi": + p.curErr(`%q can only be used to end an if`, p.val) + case "do": + p.curErr(`%q can only be used in a loop`, p.val) + case "done": + p.curErr(`%q can only be used to end a loop`, p.val) + case "esac": + p.curErr(`%q can only be used to end a case`, p.val) + case "!": + if !s.Negated { + p.curErr(`"!" can only be used in full statements`) + break + } + case "[[": + if p.lang != LangPOSIX { + p.testClause(s) + } + case "]]": + if p.lang != LangPOSIX { + p.curErr(`%q can only be used to close a test`, p.val) + } + case "let": + if p.lang != LangPOSIX { + p.letClause(s) + } + case "function": + if p.lang != LangPOSIX { + p.bashFuncDecl(s) + } + case "declare": + if p.lang.isBash() { // Note that mksh lacks this one. + p.declClause(s) + } + case "local", "export", "readonly", "typeset", "nameref": + if p.lang != LangPOSIX { + p.declClause(s) + } + case "time": + if p.lang != LangPOSIX { + p.timeClause(s) + } + case "coproc": + if p.lang.isBash() { // Note that mksh lacks this one. + p.coprocClause(s) + } + case "select": + if p.lang != LangPOSIX { + p.selectClause(s) + } + case "@test": + if p.lang == LangBats { + p.testDecl(s) + } + } + if s.Cmd != nil { + break + } + if p.hasValidIdent() { + p.callExpr(s, nil, true) + break + } + name := p.lit(p.pos, p.val) + if p.next(); p.got(leftParen) { + p.follow(name.ValuePos, "foo(", rightParen) + if p.lang == LangPOSIX && !ValidName(name.Value) { + p.posErr(name.Pos(), "invalid func name") + } + p.funcDecl(s, name, name.ValuePos, true) + } else { + p.callExpr(s, p.wordOne(name), false) + } + case rdrOut, appOut, rdrIn, dplIn, dplOut, clbOut, rdrInOut, + hdoc, dashHdoc, wordHdoc, rdrAll, appAll, _LitRedir: + p.doRedirect(s) + p.callExpr(s, nil, false) + case bckQuote: + if p.backquoteEnd() { + return nil + } + fallthrough + case _Lit, dollBrace, dollDblParen, dollParen, dollar, cmdIn, cmdOut, + sglQuote, dollSglQuote, dblQuote, dollDblQuote, dollBrack, + globQuest, globStar, globPlus, globAt, globExcl: + if p.hasValidIdent() { + p.callExpr(s, nil, true) + break + } + w := p.wordAnyNumber() + if p.got(leftParen) { + p.posErr(w.Pos(), "invalid func name") + } + p.callExpr(s, w, false) + case leftParen: + p.subshell(s) + case dblLeftParen: + p.arithmExpCmd(s) + default: + if len(s.Redirs) == 0 { + return nil + } + } + for p.peekRedir() { + p.doRedirect(s) + } + // instead of using recursion, iterate manually + for p.tok == or || p.tok == orAnd { + if binCmd { + // left associativity: in a list of BinaryCmds, the + // right recursion should only read a single element + return s + } + if p.tok == orAnd && p.lang == LangMirBSDKorn { + // No need to check for LangPOSIX, as on that language + // we parse |& as two tokens. + break + } + b := &BinaryCmd{OpPos: p.pos, Op: BinCmdOperator(p.tok), X: s} + p.next() + p.got(_Newl) + if b.Y = p.gotStmtPipe(p.stmt(p.pos), true); b.Y == nil || p.err != nil { + p.followErr(b.OpPos, b.Op.String(), "a statement") + break + } + s = p.stmt(s.Position) + s.Cmd = b + s.Comments, b.X.Comments = b.X.Comments, nil + // in "! x | y", the bang applies to the entire pipeline + s.Negated = b.X.Negated + b.X.Negated = false + } + return s +} + +func (p *Parser) subshell(s *Stmt) { + sub := &Subshell{Lparen: p.pos} + old := p.preNested(subCmd) + p.next() + sub.Stmts, sub.Last = p.stmtList() + p.postNested(old) + sub.Rparen = p.matched(sub.Lparen, leftParen, rightParen) + s.Cmd = sub +} + +func (p *Parser) arithmExpCmd(s *Stmt) { + ar := &ArithmCmd{Left: p.pos} + old := p.preNested(arithmExprCmd) + p.next() + if p.got(hash) { + if p.lang != LangMirBSDKorn { + p.langErr(ar.Pos(), "unsigned expressions", LangMirBSDKorn) + } + ar.Unsigned = true + } + ar.X = p.followArithm(dblLeftParen, ar.Left) + ar.Right = p.arithmEnd(dblLeftParen, ar.Left, old) + s.Cmd = ar +} + +func (p *Parser) block(s *Stmt) { + b := &Block{Lbrace: p.pos} + p.next() + b.Stmts, b.Last = p.stmtList("}") + pos, ok := p.gotRsrv("}") + b.Rbrace = pos + if !ok { + p.matchingErr(b.Lbrace, "{", "}") + } + s.Cmd = b +} + +func (p *Parser) ifClause(s *Stmt) { + rootIf := &IfClause{Position: p.pos} + p.next() + rootIf.Cond, rootIf.CondLast = p.followStmts("if", rootIf.Position, "then") + rootIf.ThenPos = p.followRsrv(rootIf.Position, "if ", "then") + rootIf.Then, rootIf.ThenLast = p.followStmts("then", rootIf.ThenPos, "fi", "elif", "else") + curIf := rootIf + for p.tok == _LitWord && p.val == "elif" { + elf := &IfClause{Position: p.pos} + curIf.Last = p.accComs + p.accComs = nil + p.next() + elf.Cond, elf.CondLast = p.followStmts("elif", elf.Position, "then") + elf.ThenPos = p.followRsrv(elf.Position, "elif ", "then") + elf.Then, elf.ThenLast = p.followStmts("then", elf.ThenPos, "fi", "elif", "else") + curIf.Else = elf + curIf = elf + } + if elsePos, ok := p.gotRsrv("else"); ok { + curIf.Last = p.accComs + p.accComs = nil + els := &IfClause{Position: elsePos} + els.Then, els.ThenLast = p.followStmts("else", els.Position, "fi") + curIf.Else = els + curIf = els + } + curIf.Last = p.accComs + p.accComs = nil + rootIf.FiPos = p.stmtEnd(rootIf, "if", "fi") + for els := rootIf.Else; els != nil; els = els.Else { + // All the nested IfClauses share the same FiPos. + els.FiPos = rootIf.FiPos + } + s.Cmd = rootIf +} + +func (p *Parser) whileClause(s *Stmt, until bool) { + wc := &WhileClause{WhilePos: p.pos, Until: until} + rsrv := "while" + rsrvCond := "while " + if wc.Until { + rsrv = "until" + rsrvCond = "until " + } + p.next() + wc.Cond, wc.CondLast = p.followStmts(rsrv, wc.WhilePos, "do") + wc.DoPos = p.followRsrv(wc.WhilePos, rsrvCond, "do") + wc.Do, wc.DoLast = p.followStmts("do", wc.DoPos, "done") + wc.DonePos = p.stmtEnd(wc, rsrv, "done") + s.Cmd = wc +} + +func (p *Parser) forClause(s *Stmt) { + fc := &ForClause{ForPos: p.pos} + p.next() + fc.Loop = p.loop(fc.ForPos) + + start, end := "do", "done" + if pos, ok := p.gotRsrv("{"); ok { + if p.lang == LangPOSIX { + p.langErr(pos, "for loops with braces", LangBash, LangMirBSDKorn) + } + fc.DoPos = pos + fc.Braces = true + start, end = "{", "}" + } else { + fc.DoPos = p.followRsrv(fc.ForPos, "for foo [in words]", start) + } + + s.Comments = append(s.Comments, p.accComs...) + p.accComs = nil + fc.Do, fc.DoLast = p.followStmts(start, fc.DoPos, end) + fc.DonePos = p.stmtEnd(fc, "for", end) + s.Cmd = fc +} + +func (p *Parser) loop(fpos Pos) Loop { + if !p.lang.isBash() { + switch p.tok { + case leftParen, dblLeftParen: + p.langErr(p.pos, "c-style fors", LangBash) + } + } + if p.tok == dblLeftParen { + cl := &CStyleLoop{Lparen: p.pos} + old := p.preNested(arithmExprCmd) + p.next() + cl.Init = p.arithmExpr(false) + if !p.got(dblSemicolon) { + p.follow(p.pos, "expr", semicolon) + cl.Cond = p.arithmExpr(false) + p.follow(p.pos, "expr", semicolon) + } + cl.Post = p.arithmExpr(false) + cl.Rparen = p.arithmEnd(dblLeftParen, cl.Lparen, old) + p.got(semicolon) + p.got(_Newl) + return cl + } + return p.wordIter("for", fpos) +} + +func (p *Parser) wordIter(ftok string, fpos Pos) *WordIter { + wi := &WordIter{} + if wi.Name = p.getLit(); wi.Name == nil { + p.followErr(fpos, ftok, "a literal") + } + if p.got(semicolon) { + p.got(_Newl) + return wi + } + p.got(_Newl) + if pos, ok := p.gotRsrv("in"); ok { + wi.InPos = pos + for !p.stopToken() { + if w := p.getWord(); w == nil { + p.curErr("word list can only contain words") + } else { + wi.Items = append(wi.Items, w) + } + } + p.got(semicolon) + p.got(_Newl) + } else if p.tok == _LitWord && p.val == "do" { + } else { + p.followErr(fpos, ftok+" foo", `"in", "do", ;, or a newline`) + } + return wi +} + +func (p *Parser) selectClause(s *Stmt) { + fc := &ForClause{ForPos: p.pos, Select: true} + p.next() + fc.Loop = p.wordIter("select", fc.ForPos) + fc.DoPos = p.followRsrv(fc.ForPos, "select foo [in words]", "do") + fc.Do, fc.DoLast = p.followStmts("do", fc.DoPos, "done") + fc.DonePos = p.stmtEnd(fc, "select", "done") + s.Cmd = fc +} + +func (p *Parser) caseClause(s *Stmt) { + cc := &CaseClause{Case: p.pos} + p.next() + cc.Word = p.getWord() + if cc.Word == nil { + p.followErr(cc.Case, "case", "a word") + } + end := "esac" + p.got(_Newl) + if pos, ok := p.gotRsrv("{"); ok { + cc.In = pos + cc.Braces = true + if p.lang != LangMirBSDKorn { + p.posErr(cc.Pos(), `"case i {" is a mksh feature`) + } + end = "}" + } else { + cc.In = p.followRsrv(cc.Case, "case x", "in") + } + cc.Items = p.caseItems(end) + cc.Last, p.accComs = p.accComs, nil + cc.Esac = p.stmtEnd(cc, "case", end) + s.Cmd = cc +} + +func (p *Parser) caseItems(stop string) (items []*CaseItem) { + p.got(_Newl) + for p.tok != _EOF && (p.tok != _LitWord || p.val != stop) { + ci := &CaseItem{} + ci.Comments, p.accComs = p.accComs, nil + p.got(leftParen) + for p.tok != _EOF { + if w := p.getWord(); w == nil { + p.curErr("case patterns must consist of words") + } else { + ci.Patterns = append(ci.Patterns, w) + } + if p.tok == rightParen { + break + } + if !p.got(or) { + p.curErr("case patterns must be separated with |") + } + } + old := p.preNested(switchCase) + p.next() + ci.Stmts, ci.Last = p.stmtList(stop) + p.postNested(old) + switch p.tok { + case dblSemicolon, semiAnd, dblSemiAnd, semiOr: + default: + ci.Op = Break + items = append(items, ci) + return + } + ci.Last = append(ci.Last, p.accComs...) + p.accComs = nil + ci.OpPos = p.pos + ci.Op = CaseOperator(p.tok) + p.next() + p.got(_Newl) + + // Split the comments: + // + // case x in + // a) + // foo + // ;; + // # comment for a + // # comment for b + // b) + // [...] + split := len(p.accComs) + for i := len(p.accComs) - 1; i >= 0; i-- { + c := p.accComs[i] + if c.Pos().Col() != p.pos.Col() { + break + } + split = i + } + ci.Comments = append(ci.Comments, p.accComs[:split]...) + p.accComs = p.accComs[split:] + + items = append(items, ci) + } + return +} + +func (p *Parser) testClause(s *Stmt) { + tc := &TestClause{Left: p.pos} + old := p.preNested(testExpr) + p.next() + if _, ok := p.gotRsrv("]]"); ok || p.tok == _EOF { + p.posErr(tc.Left, "test clause requires at least one expression") + } + tc.X = p.testExpr(dblLeftBrack, tc.Left, false) + if tc.X == nil { + p.followErrExp(tc.Left, "[[") + } + tc.Right = p.pos + if _, ok := p.gotRsrv("]]"); !ok { + p.matchingErr(tc.Left, "[[", "]]") + } + p.postNested(old) + s.Cmd = tc +} + +func (p *Parser) testExpr(ftok token, fpos Pos, pastAndOr bool) TestExpr { + p.got(_Newl) + var left TestExpr + if pastAndOr { + left = p.testExprBase() + } else { + left = p.testExpr(ftok, fpos, true) + } + if left == nil { + return left + } + p.got(_Newl) + switch p.tok { + case andAnd, orOr: + case _LitWord: + if p.val == "]]" { + return left + } + if p.tok = token(testBinaryOp(p.val)); p.tok == illegalTok { + p.curErr("not a valid test operator: %s", p.val) + } + case rdrIn, rdrOut: + case _EOF, rightParen: + return left + case _Lit: + p.curErr("test operator words must consist of a single literal") + default: + p.curErr("not a valid test operator: %v", p.tok) + } + b := &BinaryTest{ + OpPos: p.pos, + Op: BinTestOperator(p.tok), + X: left, + } + // Save the previous quoteState, since we change it in TsReMatch. + oldQuote := p.quote + + switch b.Op { + case AndTest, OrTest: + p.next() + if b.Y = p.testExpr(token(b.Op), b.OpPos, false); b.Y == nil { + p.followErrExp(b.OpPos, b.Op.String()) + } + case TsReMatch: + if !p.lang.isBash() { + p.langErr(p.pos, "regex tests", LangBash) + } + p.rxOpenParens = 0 + p.rxFirstPart = true + // TODO(mvdan): Using nested states within a regex will break in + // all sorts of ways. The better fix is likely to use a stop + // token, like we do with heredocs. + p.quote = testExprRegexp + fallthrough + default: + if _, ok := b.X.(*Word); !ok { + p.posErr(b.OpPos, "expected %s, %s or %s after complex expr", + AndTest, OrTest, "]]") + } + p.next() + b.Y = p.followWordTok(token(b.Op), b.OpPos) + } + p.quote = oldQuote + return b +} + +func (p *Parser) testExprBase() TestExpr { + switch p.tok { + case _EOF, rightParen: + return nil + case _LitWord: + op := token(testUnaryOp(p.val)) + switch op { + case illegalTok: + case tsRefVar, tsModif: // not available in mksh + if p.lang.isBash() { + p.tok = op + } + default: + p.tok = op + } + } + switch p.tok { + case exclMark: + u := &UnaryTest{OpPos: p.pos, Op: TsNot} + p.next() + if u.X = p.testExpr(token(u.Op), u.OpPos, false); u.X == nil { + p.followErrExp(u.OpPos, u.Op.String()) + } + return u + case tsExists, tsRegFile, tsDirect, tsCharSp, tsBlckSp, tsNmPipe, + tsSocket, tsSmbLink, tsSticky, tsGIDSet, tsUIDSet, tsGrpOwn, + tsUsrOwn, tsModif, tsRead, tsWrite, tsExec, tsNoEmpty, + tsFdTerm, tsEmpStr, tsNempStr, tsOptSet, tsVarSet, tsRefVar: + u := &UnaryTest{OpPos: p.pos, Op: UnTestOperator(p.tok)} + p.next() + u.X = p.followWordTok(token(u.Op), u.OpPos) + return u + case leftParen: + pe := &ParenTest{Lparen: p.pos} + p.next() + if pe.X = p.testExpr(leftParen, pe.Lparen, false); pe.X == nil { + p.followErrExp(pe.Lparen, "(") + } + pe.Rparen = p.matched(pe.Lparen, leftParen, rightParen) + return pe + case _LitWord: + if p.val == "]]" { + return nil + } + fallthrough + default: + if w := p.getWord(); w != nil { + return w + } + // otherwise we'd return a typed nil above + return nil + } +} + +func (p *Parser) declClause(s *Stmt) { + ds := &DeclClause{Variant: p.lit(p.pos, p.val)} + p.next() + for !p.stopToken() && !p.peekRedir() { + if p.hasValidIdent() { + ds.Args = append(ds.Args, p.getAssign(false)) + } else if p.eqlOffs > 0 { + p.curErr("invalid var name") + } else if p.tok == _LitWord && ValidName(p.val) { + ds.Args = append(ds.Args, &Assign{ + Naked: true, + Name: p.getLit(), + }) + } else if w := p.getWord(); w != nil { + ds.Args = append(ds.Args, &Assign{ + Naked: true, + Value: w, + }) + } else { + p.followErr(p.pos, ds.Variant.Value, "names or assignments") + } + } + s.Cmd = ds +} + +func isBashCompoundCommand(tok token, val string) bool { + switch tok { + case leftParen, dblLeftParen: + return true + case _LitWord: + switch val { + case "{", "if", "while", "until", "for", "case", "[[", + "coproc", "let", "function", "declare", "local", + "export", "readonly", "typeset", "nameref": + return true + } + } + return false +} + +func (p *Parser) timeClause(s *Stmt) { + tc := &TimeClause{Time: p.pos} + p.next() + if _, ok := p.gotRsrv("-p"); ok { + tc.PosixFormat = true + } + tc.Stmt = p.gotStmtPipe(p.stmt(p.pos), false) + s.Cmd = tc +} + +func (p *Parser) coprocClause(s *Stmt) { + cc := &CoprocClause{Coproc: p.pos} + if p.next(); isBashCompoundCommand(p.tok, p.val) { + // has no name + cc.Stmt = p.gotStmtPipe(p.stmt(p.pos), false) + s.Cmd = cc + return + } + cc.Name = p.getWord() + cc.Stmt = p.gotStmtPipe(p.stmt(p.pos), false) + if cc.Stmt == nil { + if cc.Name == nil { + p.posErr(cc.Coproc, "coproc clause requires a command") + return + } + // name was in fact the stmt + cc.Stmt = p.stmt(cc.Name.Pos()) + cc.Stmt.Cmd = p.call(cc.Name) + cc.Name = nil + } else if cc.Name != nil { + if call, ok := cc.Stmt.Cmd.(*CallExpr); ok { + // name was in fact the start of a call + call.Args = append([]*Word{cc.Name}, call.Args...) + cc.Name = nil + } + } + s.Cmd = cc +} + +func (p *Parser) letClause(s *Stmt) { + lc := &LetClause{Let: p.pos} + old := p.preNested(arithmExprLet) + p.next() + for !p.stopToken() && !p.peekRedir() { + x := p.arithmExpr(true) + if x == nil { + break + } + lc.Exprs = append(lc.Exprs, x) + } + if len(lc.Exprs) == 0 { + p.followErrExp(lc.Let, "let") + } + p.postNested(old) + s.Cmd = lc +} + +func (p *Parser) bashFuncDecl(s *Stmt) { + fpos := p.pos + if p.next(); p.tok != _LitWord { + p.followErr(fpos, "function", "a name") + } + name := p.lit(p.pos, p.val) + hasParens := false + if p.next(); p.got(leftParen) { + hasParens = true + p.follow(name.ValuePos, "foo(", rightParen) + } + p.funcDecl(s, name, fpos, hasParens) +} + +func (p *Parser) testDecl(s *Stmt) { + td := &TestDecl{Position: p.pos} + p.next() + if td.Description = p.getWord(); td.Description == nil { + p.followErr(td.Position, "@test", "a description word") + } + if td.Body = p.getStmt(false, false, true); td.Body == nil { + p.followErr(td.Position, `@test "desc"`, "a statement") + } + s.Cmd = td +} + +func (p *Parser) callExpr(s *Stmt, w *Word, assign bool) { + ce := p.call(w) + if w == nil { + ce.Args = ce.Args[:0] + } + if assign { + ce.Assigns = append(ce.Assigns, p.getAssign(true)) + } +loop: + for { + switch p.tok { + case _EOF, _Newl, semicolon, and, or, andAnd, orOr, orAnd, + dblSemicolon, semiAnd, dblSemiAnd, semiOr: + break loop + case _LitWord: + if len(ce.Args) == 0 && p.hasValidIdent() { + ce.Assigns = append(ce.Assigns, p.getAssign(true)) + break + } + ce.Args = append(ce.Args, p.wordOne(p.lit(p.pos, p.val))) + p.next() + case _Lit: + if len(ce.Args) == 0 && p.hasValidIdent() { + ce.Assigns = append(ce.Assigns, p.getAssign(true)) + break + } + ce.Args = append(ce.Args, p.wordAnyNumber()) + case bckQuote: + if p.backquoteEnd() { + break loop + } + fallthrough + case dollBrace, dollDblParen, dollParen, dollar, cmdIn, cmdOut, + sglQuote, dollSglQuote, dblQuote, dollDblQuote, dollBrack, + globQuest, globStar, globPlus, globAt, globExcl: + ce.Args = append(ce.Args, p.wordAnyNumber()) + case rdrOut, appOut, rdrIn, dplIn, dplOut, clbOut, rdrInOut, + hdoc, dashHdoc, wordHdoc, rdrAll, appAll, _LitRedir: + p.doRedirect(s) + case dblLeftParen: + p.curErr("%s can only be used to open an arithmetic cmd", p.tok) + case rightParen: + if p.quote == subCmd { + break loop + } + fallthrough + default: + // Note that we'll only keep the first error that happens. + if len(ce.Args) > 0 { + if cmd := ce.Args[0].Lit(); p.lang == LangPOSIX && isBashCompoundCommand(_LitWord, cmd) { + p.curErr("the %q builtin exists in bash; tried parsing as posix", cmd) + } + } + p.curErr("a command can only contain words and redirects; encountered %s", p.tok) + } + } + if len(ce.Assigns) == 0 && len(ce.Args) == 0 { + return + } + if len(ce.Args) == 0 { + ce.Args = nil + } else { + for _, asgn := range ce.Assigns { + if asgn.Index != nil || asgn.Array != nil { + p.posErr(asgn.Pos(), "inline variables cannot be arrays") + } + } + } + s.Cmd = ce +} + +func (p *Parser) funcDecl(s *Stmt, name *Lit, pos Pos, withParens bool) { + fd := &FuncDecl{ + Position: pos, + RsrvWord: pos != name.ValuePos, + Parens: withParens, + Name: name, + } + p.got(_Newl) + if fd.Body = p.getStmt(false, false, true); fd.Body == nil { + p.followErr(fd.Pos(), "foo()", "a statement") + } + s.Cmd = fd +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/parser_arithm.go b/vendor/mvdan.cc/sh/v3/syntax/parser_arithm.go new file mode 100644 index 00000000..a6d6a951 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/parser_arithm.go @@ -0,0 +1,353 @@ +package syntax + +// compact specifies whether we allow spaces between expressions. +// This is true for let +func (p *Parser) arithmExpr(compact bool) ArithmExpr { + return p.arithmExprComma(compact) +} + +// These function names are inspired by Bash's expr.c + +func (p *Parser) arithmExprComma(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprAssign, Comma) +} + +func (p *Parser) arithmExprAssign(compact bool) ArithmExpr { + // Assign is different from the other binary operators because it's + // right-associative and needs to check that it's placed after a name + value := p.arithmExprTernary(compact) + switch BinAritOperator(p.tok) { + case AddAssgn, SubAssgn, MulAssgn, QuoAssgn, RemAssgn, AndAssgn, + OrAssgn, XorAssgn, ShlAssgn, ShrAssgn, Assgn: + if compact && p.spaced { + return value + } + if !isArithName(value) { + p.posErr(p.pos, "%s must follow a name", p.tok.String()) + } + pos := p.pos + tok := p.tok + p.nextArithOp(compact) + y := p.arithmExprAssign(compact) + if y == nil { + p.followErrExp(pos, tok.String()) + } + return &BinaryArithm{ + OpPos: pos, + Op: BinAritOperator(tok), + X: value, + Y: y, + } + } + return value +} + +func (p *Parser) arithmExprTernary(compact bool) ArithmExpr { + value := p.arithmExprLor(compact) + if BinAritOperator(p.tok) != TernQuest || (compact && p.spaced) { + return value + } + + if value == nil { + p.curErr("%s must follow an expression", p.tok.String()) + } + questPos := p.pos + p.nextArithOp(compact) + if BinAritOperator(p.tok) == TernColon { + p.followErrExp(questPos, TernQuest.String()) + } + trueExpr := p.arithmExpr(compact) + if trueExpr == nil { + p.followErrExp(questPos, TernQuest.String()) + } + if BinAritOperator(p.tok) != TernColon { + p.posErr(questPos, "ternary operator missing : after ?") + } + colonPos := p.pos + p.nextArithOp(compact) + falseExpr := p.arithmExprTernary(compact) + if falseExpr == nil { + p.followErrExp(colonPos, TernColon.String()) + } + return &BinaryArithm{ + OpPos: questPos, + Op: TernQuest, + X: value, + Y: &BinaryArithm{ + OpPos: colonPos, + Op: TernColon, + X: trueExpr, + Y: falseExpr, + }, + } +} + +func (p *Parser) arithmExprLor(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprLand, OrArit) +} + +func (p *Parser) arithmExprLand(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprBor, AndArit) +} + +func (p *Parser) arithmExprBor(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprBxor, Or) +} + +func (p *Parser) arithmExprBxor(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprBand, Xor) +} + +func (p *Parser) arithmExprBand(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprEquality, And) +} + +func (p *Parser) arithmExprEquality(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprComparison, Eql, Neq) +} + +func (p *Parser) arithmExprComparison(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprShift, Lss, Gtr, Leq, Geq) +} + +func (p *Parser) arithmExprShift(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprAddition, Shl, Shr) +} + +func (p *Parser) arithmExprAddition(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprMultiplication, Add, Sub) +} + +func (p *Parser) arithmExprMultiplication(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprPower, Mul, Quo, Rem) +} + +func (p *Parser) arithmExprPower(compact bool) ArithmExpr { + // Power is different from the other binary operators because it's right-associative + value := p.arithmExprUnary(compact) + if BinAritOperator(p.tok) != Pow || (compact && p.spaced) { + return value + } + + if value == nil { + p.curErr("%s must follow an expression", p.tok.String()) + } + + op := p.tok + pos := p.pos + p.nextArithOp(compact) + y := p.arithmExprPower(compact) + if y == nil { + p.followErrExp(pos, op.String()) + } + return &BinaryArithm{ + OpPos: pos, + Op: BinAritOperator(op), + X: value, + Y: y, + } +} + +func (p *Parser) arithmExprUnary(compact bool) ArithmExpr { + if !compact { + p.got(_Newl) + } + + switch UnAritOperator(p.tok) { + case Not, BitNegation, Plus, Minus: + ue := &UnaryArithm{OpPos: p.pos, Op: UnAritOperator(p.tok)} + p.nextArithOp(compact) + if ue.X = p.arithmExprUnary(compact); ue.X == nil { + p.followErrExp(ue.OpPos, ue.Op.String()) + } + return ue + } + return p.arithmExprValue(compact) +} + +func (p *Parser) arithmExprValue(compact bool) ArithmExpr { + var x ArithmExpr + switch p.tok { + case addAdd, subSub: + ue := &UnaryArithm{OpPos: p.pos, Op: UnAritOperator(p.tok)} + p.nextArith(compact) + if p.tok != _LitWord { + p.followErr(ue.OpPos, token(ue.Op).String(), "a literal") + } + ue.X = p.arithmExprValue(compact) + return ue + case leftParen: + pe := &ParenArithm{Lparen: p.pos} + p.nextArithOp(compact) + pe.X = p.followArithm(leftParen, pe.Lparen) + pe.Rparen = p.matched(pe.Lparen, leftParen, rightParen) + x = pe + case leftBrack: + p.curErr("[ must follow a name") + case colon: + p.curErr("ternary operator missing ? before :") + case _LitWord: + l := p.getLit() + if p.tok != leftBrack { + x = p.wordOne(l) + break + } + pe := &ParamExp{Dollar: l.ValuePos, Short: true, Param: l} + pe.Index = p.eitherIndex() + x = p.wordOne(pe) + case bckQuote: + if p.quote == arithmExprLet && p.openBquotes > 0 { + return nil + } + fallthrough + default: + if w := p.getWord(); w != nil { + x = w + } else { + return nil + } + } + + if compact && p.spaced { + return x + } + if !compact { + p.got(_Newl) + } + + // we want real nil, not (*Word)(nil) as that + // sets the type to non-nil and then x != nil + if p.tok == addAdd || p.tok == subSub { + if !isArithName(x) { + p.curErr("%s must follow a name", p.tok.String()) + } + u := &UnaryArithm{ + Post: true, + OpPos: p.pos, + Op: UnAritOperator(p.tok), + X: x, + } + p.nextArith(compact) + return u + } + return x +} + +// nextArith consumes a token. +// It returns true if compact and the token was followed by spaces +func (p *Parser) nextArith(compact bool) bool { + p.next() + if compact && p.spaced { + return true + } + if !compact { + p.got(_Newl) + } + return false +} + +func (p *Parser) nextArithOp(compact bool) { + pos := p.pos + tok := p.tok + if p.nextArith(compact) { + p.followErrExp(pos, tok.String()) + } +} + +// arithmExprBinary is used for all left-associative binary operators +func (p *Parser) arithmExprBinary(compact bool, nextOp func(bool) ArithmExpr, operators ...BinAritOperator) ArithmExpr { + value := nextOp(compact) + for { + var foundOp BinAritOperator + for _, op := range operators { + if p.tok == token(op) { + foundOp = op + break + } + } + + if token(foundOp) == illegalTok || (compact && p.spaced) { + return value + } + + if value == nil { + p.curErr("%s must follow an expression", p.tok.String()) + } + + pos := p.pos + p.nextArithOp(compact) + y := nextOp(compact) + if y == nil { + p.followErrExp(pos, foundOp.String()) + } + + value = &BinaryArithm{ + OpPos: pos, + Op: foundOp, + X: value, + Y: y, + } + } +} + +func isArithName(left ArithmExpr) bool { + w, ok := left.(*Word) + if !ok || len(w.Parts) != 1 { + return false + } + switch x := w.Parts[0].(type) { + case *Lit: + return ValidName(x.Value) + case *ParamExp: + return x.nakedIndex() + default: + return false + } +} + +func (p *Parser) followArithm(ftok token, fpos Pos) ArithmExpr { + x := p.arithmExpr(false) + if x == nil { + p.followErrExp(fpos, ftok.String()) + } + return x +} + +func (p *Parser) peekArithmEnd() bool { + return p.tok == rightParen && p.r == ')' +} + +func (p *Parser) arithmMatchingErr(pos Pos, left, right token) { + switch p.tok { + case _Lit, _LitWord: + p.curErr("not a valid arithmetic operator: %s", p.val) + case leftBrack: + p.curErr("[ must follow a name") + case colon: + p.curErr("ternary operator missing ? before :") + case rightParen, _EOF: + p.matchingErr(pos, left, right) + default: + if p.quote == arithmExpr { + p.curErr("not a valid arithmetic operator: %v", p.tok) + } + p.matchingErr(pos, left, right) + } +} + +func (p *Parser) matchedArithm(lpos Pos, left, right token) { + if !p.got(right) { + p.arithmMatchingErr(lpos, left, right) + } +} + +func (p *Parser) arithmEnd(ltok token, lpos Pos, old saveState) Pos { + if !p.peekArithmEnd() { + p.arithmMatchingErr(lpos, ltok, dblRightParen) + } + p.rune() + p.postNested(old) + pos := p.pos + p.next() + return pos +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/printer.go b/vendor/mvdan.cc/sh/v3/syntax/printer.go new file mode 100644 index 00000000..84ad6850 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/printer.go @@ -0,0 +1,1530 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "bufio" + "bytes" + "fmt" + "io" + "strings" + "text/tabwriter" + "unicode" + + "mvdan.cc/sh/v3/fileutil" +) + +// PrinterOption is a function which can be passed to NewPrinter +// to alter its behavior. To apply option to existing Printer +// call it directly, for example KeepPadding(true)(printer). +type PrinterOption func(*Printer) + +// Indent sets the number of spaces used for indentation. If set to 0, +// tabs will be used instead. +func Indent(spaces uint) PrinterOption { + return func(p *Printer) { p.indentSpaces = spaces } +} + +// BinaryNextLine will make binary operators appear on the next line +// when a binary command, such as a pipe, spans multiple lines. A +// backslash will be used. +func BinaryNextLine(enabled bool) PrinterOption { + return func(p *Printer) { p.binNextLine = enabled } +} + +// SwitchCaseIndent will make switch cases be indented. As such, switch +// case bodies will be two levels deeper than the switch itself. +func SwitchCaseIndent(enabled bool) PrinterOption { + return func(p *Printer) { p.swtCaseIndent = enabled } +} + +// TODO(v4): consider turning this into a "space all operators" option, to also +// allow foo=( bar baz ), (( x + y )), and so on. + +// SpaceRedirects will put a space after most redirection operators. The +// exceptions are '>&', '<&', '>(', and '<('. +func SpaceRedirects(enabled bool) PrinterOption { + return func(p *Printer) { p.spaceRedirects = enabled } +} + +// KeepPadding will keep most nodes and tokens in the same column that +// they were in the original source. This allows the user to decide how +// to align and pad their code with spaces. +// +// Note that this feature is best-effort and will only keep the +// alignment stable, so it may need some human help the first time it is +// run. +func KeepPadding(enabled bool) PrinterOption { + return func(p *Printer) { + if enabled && !p.keepPadding { + // Enable the flag, and set up the writer wrapper. + p.keepPadding = true + p.cols.Writer = p.bufWriter.(*bufio.Writer) + p.bufWriter = &p.cols + + } else if !enabled && p.keepPadding { + // Ensure we reset the state to that of NewPrinter. + p.keepPadding = false + p.bufWriter = p.cols.Writer + p.cols = colCounter{} + } + } +} + +// Minify will print programs in a way to save the most bytes possible. +// For example, indentation and comments are skipped, and extra +// whitespace is avoided when possible. +func Minify(enabled bool) PrinterOption { + return func(p *Printer) { p.minify = enabled } +} + +// SingleLine will attempt to print programs in one line. For example, lists of +// commands or nested blocks do not use newlines in this mode. Note that some +// newlines must still appear, such as those following comments or around +// here-documents. +// +// Print's trailing newline when given a *File is not affected by this option. +func SingleLine(enabled bool) PrinterOption { + return func(p *Printer) { p.singleLine = enabled } +} + +// FunctionNextLine will place a function's opening braces on the next line. +func FunctionNextLine(enabled bool) PrinterOption { + return func(p *Printer) { p.funcNextLine = enabled } +} + +// NewPrinter allocates a new Printer and applies any number of options. +func NewPrinter(opts ...PrinterOption) *Printer { + p := &Printer{ + bufWriter: bufio.NewWriter(nil), + tabWriter: new(tabwriter.Writer), + } + for _, opt := range opts { + opt(p) + } + return p +} + +// Print "pretty-prints" the given syntax tree node to the given writer. Writes +// to w are buffered. +// +// The node types supported at the moment are *File, *Stmt, *Word, *Assign, any +// Command node, and any WordPart node. A trailing newline will only be printed +// when a *File is used. +func (p *Printer) Print(w io.Writer, node Node) error { + p.reset() + + if p.minify && p.singleLine { + return fmt.Errorf("Minify and SingleLine together are not supported yet; please file an issue describing your use case: https://github.com/mvdan/sh/issues") + } + + // TODO: consider adding a raw mode to skip the tab writer, much like in + // go/printer. + twmode := tabwriter.DiscardEmptyColumns | tabwriter.StripEscape + tabwidth := 8 + if p.indentSpaces == 0 { + // indenting with tabs + twmode |= tabwriter.TabIndent + } else { + // indenting with spaces + tabwidth = int(p.indentSpaces) + } + p.tabWriter.Init(w, 0, tabwidth, 1, ' ', twmode) + w = p.tabWriter + + p.bufWriter.Reset(w) + switch x := node.(type) { + case *File: + p.stmtList(x.Stmts, x.Last) + p.newline(Pos{}) + case *Stmt: + p.stmtList([]*Stmt{x}, nil) + case Command: + p.command(x, nil) + case *Word: + p.line = x.Pos().Line() + p.word(x) + case WordPart: + p.line = x.Pos().Line() + p.wordPart(x, nil) + case *Assign: + p.line = x.Pos().Line() + p.assigns([]*Assign{x}) + default: + return fmt.Errorf("unsupported node type: %T", x) + } + p.flushHeredocs() + p.flushComments() + + // flush the writers + if err := p.bufWriter.Flush(); err != nil { + return err + } + if tw, _ := w.(*tabwriter.Writer); tw != nil { + if err := tw.Flush(); err != nil { + return err + } + } + return nil +} + +type bufWriter interface { + Write([]byte) (int, error) + WriteString(string) (int, error) + WriteByte(byte) error + Reset(io.Writer) + Flush() error +} + +type colCounter struct { + *bufio.Writer + column int + lineStart bool +} + +func (c *colCounter) addByte(b byte) { + switch b { + case '\n': + c.column = 0 + c.lineStart = true + case '\t', ' ', tabwriter.Escape: + default: + c.lineStart = false + } + c.column++ +} + +func (c *colCounter) WriteByte(b byte) error { + c.addByte(b) + return c.Writer.WriteByte(b) +} + +func (c *colCounter) WriteString(s string) (int, error) { + for _, b := range []byte(s) { + c.addByte(b) + } + return c.Writer.WriteString(s) +} + +func (c *colCounter) Reset(w io.Writer) { + c.column = 1 + c.lineStart = true + c.Writer.Reset(w) +} + +// Printer holds the internal state of the printing mechanism of a +// program. +type Printer struct { + bufWriter // TODO: embedding this makes the methods part of the API, which we did not intend + tabWriter *tabwriter.Writer + cols colCounter + + indentSpaces uint + binNextLine bool + swtCaseIndent bool + spaceRedirects bool + keepPadding bool + minify bool + singleLine bool + funcNextLine bool + + wantSpace wantSpaceState // whether space is required or has been written + + wantNewline bool // newline is wanted for pretty-printing; ignored by singleLine; ignored by singleLine + mustNewline bool // newline is required to keep shell syntax valid + wroteSemi bool // wrote ';' for the current statement + + // pendingComments are any comments in the current line or statement + // that we have yet to print. This is useful because that way, we can + // ensure that all comments are written immediately before a newline. + // Otherwise, in some edge cases we might wrongly place words after a + // comment in the same line, breaking programs. + pendingComments []Comment + + // firstLine means we are still writing the first line + firstLine bool + // line is the current line number + line uint + + // lastLevel is the last level of indentation that was used. + lastLevel uint + // level is the current level of indentation. + level uint + // levelIncs records which indentation level increments actually + // took place, to revert them once their section ends. + levelIncs []bool + + nestedBinary bool + + // pendingHdocs is the list of pending heredocs to write. + pendingHdocs []*Redirect + + // used when printing <<- heredocs with tab indentation + tabsPrinter *Printer +} + +func (p *Printer) reset() { + p.wantSpace = spaceWritten + p.wantNewline, p.mustNewline = false, false + p.pendingComments = p.pendingComments[:0] + + // minification uses its own newline logic + p.firstLine = !p.minify + p.line = 0 + + p.lastLevel, p.level = 0, 0 + p.levelIncs = p.levelIncs[:0] + p.nestedBinary = false + p.pendingHdocs = p.pendingHdocs[:0] +} + +func (p *Printer) spaces(n uint) { + for i := uint(0); i < n; i++ { + p.WriteByte(' ') + } +} + +func (p *Printer) space() { + p.WriteByte(' ') + p.wantSpace = spaceWritten +} + +func (p *Printer) spacePad(pos Pos) { + if p.cols.lineStart && p.indentSpaces == 0 { + // Never add padding at the start of a line unless we are indenting + // with spaces, since this may result in mixing of spaces and tabs. + return + } + if p.wantSpace == spaceRequired { + p.WriteByte(' ') + p.wantSpace = spaceWritten + } + for p.cols.column > 0 && p.cols.column < int(pos.Col()) { + p.WriteByte(' ') + } +} + +// wantsNewline reports whether we want to print at least one newline before +// printing a node at a given position. A zero position can be given to simply +// tell if we want a newline following what's just been printed. +func (p *Printer) wantsNewline(pos Pos, escapingNewline bool) bool { + if p.mustNewline { + // We must have a newline here. + return true + } + if p.singleLine && len(p.pendingComments) == 0 { + // The newline is optional, and singleLine skips it. + // Don't skip if there are any pending comments, + // as that might move them further down to the wrong place. + return false + } + if escapingNewline && p.minify { + return false + } + // The newline is optional, and we want it via either wantNewline or via + // the position's line. + return p.wantNewline || pos.Line() > p.line +} + +func (p *Printer) bslashNewl() { + if p.wantSpace == spaceRequired { + p.space() + } + p.WriteString("\\\n") + p.line++ + p.indent() +} + +func (p *Printer) spacedString(s string, pos Pos) { + p.spacePad(pos) + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) spacedToken(s string, pos Pos) { + if p.minify { + p.WriteString(s) + p.wantSpace = spaceNotRequired + return + } + p.spacePad(pos) + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) semiOrNewl(s string, pos Pos) { + if p.wantsNewline(Pos{}, false) { + p.newline(pos) + p.indent() + } else { + if !p.wroteSemi { + p.WriteByte(';') + } + if !p.minify { + p.space() + } + p.advanceLine(pos.Line()) + } + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) writeLit(s string) { + // If p.tabWriter is nil, this is the nested printer being used to print + // <<- heredoc bodies, so the parent printer will add the escape bytes + // later. + if p.tabWriter != nil && strings.Contains(s, "\t") { + p.WriteByte(tabwriter.Escape) + defer p.WriteByte(tabwriter.Escape) + } + p.WriteString(s) +} + +func (p *Printer) incLevel() { + inc := false + if p.level <= p.lastLevel || len(p.levelIncs) == 0 { + p.level++ + inc = true + } else if last := &p.levelIncs[len(p.levelIncs)-1]; *last { + *last = false + inc = true + } + p.levelIncs = append(p.levelIncs, inc) +} + +func (p *Printer) decLevel() { + if p.levelIncs[len(p.levelIncs)-1] { + p.level-- + } + p.levelIncs = p.levelIncs[:len(p.levelIncs)-1] +} + +func (p *Printer) indent() { + if p.minify { + return + } + p.lastLevel = p.level + switch { + case p.level == 0: + case p.indentSpaces == 0: + p.WriteByte(tabwriter.Escape) + for i := uint(0); i < p.level; i++ { + p.WriteByte('\t') + } + p.WriteByte(tabwriter.Escape) + default: + p.spaces(p.indentSpaces * p.level) + } +} + +// TODO(mvdan): add an indent call at the end of newline? + +// newline prints one newline and advances p.line to pos.Line(). +func (p *Printer) newline(pos Pos) { + p.flushHeredocs() + p.flushComments() + p.WriteByte('\n') + p.wantSpace = spaceWritten + p.wantNewline, p.mustNewline = false, false + p.advanceLine(pos.Line()) +} + +func (p *Printer) advanceLine(line uint) { + if p.line < line { + p.line = line + } +} + +func (p *Printer) flushHeredocs() { + if len(p.pendingHdocs) == 0 { + return + } + hdocs := p.pendingHdocs + p.pendingHdocs = p.pendingHdocs[:0] + coms := p.pendingComments + p.pendingComments = nil + if len(coms) > 0 { + c := coms[0] + if c.Pos().Line() == p.line { + p.pendingComments = append(p.pendingComments, c) + p.flushComments() + coms = coms[1:] + } + } + + // Reuse the last indentation level, as + // indentation levels are usually changed before + // newlines are printed along with their + // subsequent indentation characters. + newLevel := p.level + p.level = p.lastLevel + + for _, r := range hdocs { + p.line++ + p.WriteByte('\n') + p.wantSpace = spaceWritten + p.wantNewline, p.wantNewline = false, false + if r.Op == DashHdoc && p.indentSpaces == 0 && !p.minify { + if r.Hdoc != nil { + extra := extraIndenter{ + bufWriter: p.bufWriter, + baseIndent: int(p.level + 1), + firstIndent: -1, + } + p.tabsPrinter = &Printer{ + bufWriter: &extra, + + // The options need to persist. + indentSpaces: p.indentSpaces, + binNextLine: p.binNextLine, + swtCaseIndent: p.swtCaseIndent, + spaceRedirects: p.spaceRedirects, + keepPadding: p.keepPadding, + minify: p.minify, + funcNextLine: p.funcNextLine, + + line: r.Hdoc.Pos().Line(), + } + p.tabsPrinter.wordParts(r.Hdoc.Parts, true) + } + p.indent() + } else if r.Hdoc != nil { + p.wordParts(r.Hdoc.Parts, true) + } + p.unquotedWord(r.Word) + if r.Hdoc != nil { + // Overwrite p.line, since printing r.Word again can set + // p.line to the beginning of the heredoc again. + p.advanceLine(r.Hdoc.End().Line()) + } + p.wantSpace = spaceNotRequired + } + p.level = newLevel + p.pendingComments = coms + p.mustNewline = true +} + +// newline prints between zero and two newlines. +// If any newlines are printed, it advances p.line to pos.Line(). +func (p *Printer) newlines(pos Pos) { + if p.firstLine && len(p.pendingComments) == 0 { + p.firstLine = false + return // no empty lines at the top + } + if !p.wantsNewline(pos, false) { + return + } + p.flushHeredocs() + p.flushComments() + p.WriteByte('\n') + p.wantSpace = spaceWritten + p.wantNewline, p.mustNewline = false, false + + l := pos.Line() + if l > p.line+1 && !p.minify { + p.WriteByte('\n') // preserve single empty lines + } + p.advanceLine(l) + p.indent() +} + +func (p *Printer) rightParen(pos Pos) { + if len(p.pendingHdocs) > 0 || !p.minify { + p.newlines(pos) + } + p.WriteByte(')') + p.wantSpace = spaceRequired +} + +func (p *Printer) semiRsrv(s string, pos Pos) { + if p.wantsNewline(pos, false) { + p.newlines(pos) + } else { + if !p.wroteSemi { + p.WriteByte(';') + } + if !p.minify { + p.spacePad(pos) + } + } + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) flushComments() { + for i, c := range p.pendingComments { + if i == 0 { + // Flush any pending heredocs first. Otherwise, the + // comments would become part of a heredoc body. + p.flushHeredocs() + } + p.firstLine = false + // We can't call any of the newline methods, as they call this + // function and we'd recurse forever. + cline := c.Hash.Line() + switch { + case p.mustNewline, i > 0, cline > p.line && p.line > 0: + p.WriteByte('\n') + if cline > p.line+1 { + p.WriteByte('\n') + } + p.indent() + p.wantSpace = spaceWritten + p.spacePad(c.Pos()) + case p.wantSpace == spaceRequired: + if p.keepPadding { + p.spacePad(c.Pos()) + } else { + p.WriteByte('\t') + } + case p.wantSpace != spaceWritten: + p.space() + } + // don't go back one line, which may happen in some edge cases + p.advanceLine(cline) + p.WriteByte('#') + p.writeLit(strings.TrimRightFunc(c.Text, unicode.IsSpace)) + p.wantNewline = true + p.mustNewline = true + } + p.pendingComments = nil +} + +func (p *Printer) comments(comments ...Comment) { + if p.minify { + for _, c := range comments { + if fileutil.Shebang([]byte("#"+c.Text)) != "" && c.Hash.Col() == 1 && c.Hash.Line() == 1 { + p.WriteString(strings.TrimRightFunc("#"+c.Text, unicode.IsSpace)) + p.WriteString("\n") + p.line++ + } + } + return + } + p.pendingComments = append(p.pendingComments, comments...) +} + +func (p *Printer) wordParts(wps []WordPart, quoted bool) { + // We disallow unquoted escaped newlines between word parts below. + // However, we want to allow a leading escaped newline for cases such as: + // + // foo <<< \ + // "bar baz" + if !quoted && !p.singleLine && wps[0].Pos().Line() > p.line { + p.bslashNewl() + } + for i, wp := range wps { + var next WordPart + if i+1 < len(wps) { + next = wps[i+1] + } + // Keep escaped newlines separating word parts when quoted. + // Note that those escaped newlines don't cause indentaiton. + // When not quoted, we strip them out consistently, + // because attempting to keep them would prevent indentation. + // Can't use p.wantsNewline here, since this is only about + // escaped newlines. + for quoted && !p.singleLine && wp.Pos().Line() > p.line { + p.WriteString("\\\n") + p.line++ + } + p.wordPart(wp, next) + p.advanceLine(wp.End().Line()) + } +} + +func (p *Printer) wordPart(wp, next WordPart) { + switch x := wp.(type) { + case *Lit: + p.writeLit(x.Value) + case *SglQuoted: + if x.Dollar { + p.WriteByte('$') + } + p.WriteByte('\'') + p.writeLit(x.Value) + p.WriteByte('\'') + p.advanceLine(x.End().Line()) + case *DblQuoted: + p.dblQuoted(x) + case *CmdSubst: + p.advanceLine(x.Pos().Line()) + switch { + case x.TempFile: + p.WriteString("${") + p.wantSpace = spaceRequired + p.nestedStmts(x.Stmts, x.Last, x.Right) + p.wantSpace = spaceNotRequired + p.semiRsrv("}", x.Right) + case x.ReplyVar: + p.WriteString("${|") + p.nestedStmts(x.Stmts, x.Last, x.Right) + p.wantSpace = spaceNotRequired + p.semiRsrv("}", x.Right) + // Special case: `# inline comment` + case x.Backquotes && len(x.Stmts) == 0 && + len(x.Last) == 1 && x.Right.Line() == p.line: + p.WriteString("`#") + p.WriteString(x.Last[0].Text) + p.WriteString("`") + default: + p.WriteString("$(") + if len(x.Stmts) > 0 && startsWithLparen(x.Stmts[0]) { + p.wantSpace = spaceRequired + } else { + p.wantSpace = spaceNotRequired + } + p.nestedStmts(x.Stmts, x.Last, x.Right) + p.rightParen(x.Right) + } + case *ParamExp: + litCont := ";" + if nextLit, ok := next.(*Lit); ok && nextLit.Value != "" { + litCont = nextLit.Value[:1] + } + name := x.Param.Value + switch { + case !p.minify: + case x.Excl, x.Length, x.Width: + case x.Index != nil, x.Slice != nil: + case x.Repl != nil, x.Exp != nil: + case len(name) > 1 && !ValidName(name): // ${10} + case ValidName(name + litCont): // ${var}cont + default: + x2 := *x + x2.Short = true + p.paramExp(&x2) + return + } + p.paramExp(x) + case *ArithmExp: + p.WriteString("$((") + if x.Unsigned { + p.WriteString("# ") + } + p.arithmExpr(x.X, false, false) + p.WriteString("))") + case *ExtGlob: + p.WriteString(x.Op.String()) + p.writeLit(x.Pattern.Value) + p.WriteByte(')') + case *ProcSubst: + // avoid conflict with << and others + if p.wantSpace == spaceRequired { + p.space() + } + p.WriteString(x.Op.String()) + p.nestedStmts(x.Stmts, x.Last, x.Rparen) + p.rightParen(x.Rparen) + } +} + +func (p *Printer) dblQuoted(dq *DblQuoted) { + if dq.Dollar { + p.WriteByte('$') + } + p.WriteByte('"') + if len(dq.Parts) > 0 { + p.wordParts(dq.Parts, true) + } + // Add any trailing escaped newlines. + for p.line < dq.Right.Line() { + p.WriteString("\\\n") + p.line++ + } + p.WriteByte('"') +} + +func (p *Printer) wroteIndex(index ArithmExpr) bool { + if index == nil { + return false + } + p.WriteByte('[') + p.arithmExpr(index, false, false) + p.WriteByte(']') + return true +} + +func (p *Printer) paramExp(pe *ParamExp) { + if pe.nakedIndex() { // arr[x] + p.writeLit(pe.Param.Value) + p.wroteIndex(pe.Index) + return + } + if pe.Short { // $var + p.WriteByte('$') + p.writeLit(pe.Param.Value) + return + } + // ${var...} + p.WriteString("${") + switch { + case pe.Length: + p.WriteByte('#') + case pe.Width: + p.WriteByte('%') + case pe.Excl: + p.WriteByte('!') + } + p.writeLit(pe.Param.Value) + p.wroteIndex(pe.Index) + switch { + case pe.Slice != nil: + p.WriteByte(':') + p.arithmExpr(pe.Slice.Offset, true, true) + if pe.Slice.Length != nil { + p.WriteByte(':') + p.arithmExpr(pe.Slice.Length, true, false) + } + case pe.Repl != nil: + if pe.Repl.All { + p.WriteByte('/') + } + p.WriteByte('/') + if pe.Repl.Orig != nil { + p.word(pe.Repl.Orig) + } + p.WriteByte('/') + if pe.Repl.With != nil { + p.word(pe.Repl.With) + } + case pe.Names != 0: + p.writeLit(pe.Names.String()) + case pe.Exp != nil: + p.WriteString(pe.Exp.Op.String()) + if pe.Exp.Word != nil { + p.word(pe.Exp.Word) + } + } + p.WriteByte('}') +} + +func (p *Printer) loop(loop Loop) { + switch x := loop.(type) { + case *WordIter: + p.writeLit(x.Name.Value) + if x.InPos.IsValid() { + p.spacedString(" in", Pos{}) + p.wordJoin(x.Items) + } + case *CStyleLoop: + p.WriteString("((") + if x.Init == nil { + p.space() + } + p.arithmExpr(x.Init, false, false) + p.WriteString("; ") + p.arithmExpr(x.Cond, false, false) + p.WriteString("; ") + p.arithmExpr(x.Post, false, false) + p.WriteString("))") + } +} + +func (p *Printer) arithmExpr(expr ArithmExpr, compact, spacePlusMinus bool) { + if p.minify { + compact = true + } + switch x := expr.(type) { + case *Word: + p.word(x) + case *BinaryArithm: + if compact { + p.arithmExpr(x.X, compact, spacePlusMinus) + p.WriteString(x.Op.String()) + p.arithmExpr(x.Y, compact, false) + } else { + p.arithmExpr(x.X, compact, spacePlusMinus) + if x.Op != Comma { + p.space() + } + p.WriteString(x.Op.String()) + p.space() + p.arithmExpr(x.Y, compact, false) + } + case *UnaryArithm: + if x.Post { + p.arithmExpr(x.X, compact, spacePlusMinus) + p.WriteString(x.Op.String()) + } else { + if spacePlusMinus { + switch x.Op { + case Plus, Minus: + p.space() + } + } + p.WriteString(x.Op.String()) + p.arithmExpr(x.X, compact, false) + } + case *ParenArithm: + p.WriteByte('(') + p.arithmExpr(x.X, false, false) + p.WriteByte(')') + } +} + +func (p *Printer) testExpr(expr TestExpr) { + // Multi-line test expressions don't need to escape newlines. + if expr.Pos().Line() > p.line { + p.newlines(expr.Pos()) + p.spacePad(expr.Pos()) + } else if p.wantSpace == spaceRequired { + p.space() + } + p.testExprSameLine(expr) +} + +func (p *Printer) testExprSameLine(expr TestExpr) { + p.advanceLine(expr.Pos().Line()) + switch x := expr.(type) { + case *Word: + p.word(x) + case *BinaryTest: + p.testExprSameLine(x.X) + p.space() + p.WriteString(x.Op.String()) + switch x.Op { + case AndTest, OrTest: + p.wantSpace = spaceRequired + p.testExpr(x.Y) + default: + p.space() + p.testExprSameLine(x.Y) + } + case *UnaryTest: + p.WriteString(x.Op.String()) + p.space() + p.testExprSameLine(x.X) + case *ParenTest: + p.WriteByte('(') + if startsWithLparen(x.X) { + p.wantSpace = spaceRequired + } else { + p.wantSpace = spaceNotRequired + } + p.testExpr(x.X) + p.WriteByte(')') + } +} + +func (p *Printer) word(w *Word) { + p.wordParts(w.Parts, false) + p.wantSpace = spaceRequired +} + +func (p *Printer) unquotedWord(w *Word) { + for _, wp := range w.Parts { + switch x := wp.(type) { + case *SglQuoted: + p.writeLit(x.Value) + case *DblQuoted: + p.wordParts(x.Parts, true) + case *Lit: + for i := 0; i < len(x.Value); i++ { + if b := x.Value[i]; b == '\\' { + if i++; i < len(x.Value) { + p.WriteByte(x.Value[i]) + } + } else { + p.WriteByte(b) + } + } + } + } +} + +func (p *Printer) wordJoin(ws []*Word) { + anyNewline := false + for _, w := range ws { + if pos := w.Pos(); pos.Line() > p.line && !p.singleLine { + if !anyNewline { + p.incLevel() + anyNewline = true + } + p.bslashNewl() + } + p.spacePad(w.Pos()) + p.word(w) + } + if anyNewline { + p.decLevel() + } +} + +func (p *Printer) casePatternJoin(pats []*Word) { + anyNewline := false + for i, w := range pats { + if i > 0 { + p.spacedToken("|", Pos{}) + } + if p.wantsNewline(w.Pos(), true) { + if !anyNewline { + p.incLevel() + anyNewline = true + } + p.bslashNewl() + } else { + p.spacePad(w.Pos()) + } + p.word(w) + } + if anyNewline { + p.decLevel() + } +} + +func (p *Printer) elemJoin(elems []*ArrayElem, last []Comment) { + p.incLevel() + for _, el := range elems { + var left []Comment + for _, c := range el.Comments { + if c.Pos().After(el.Pos()) { + left = append(left, c) + break + } + p.comments(c) + } + // Multi-line array expressions don't need to escape newlines. + if el.Pos().Line() > p.line { + p.newlines(el.Pos()) + p.spacePad(el.Pos()) + } else if p.wantSpace == spaceRequired { + p.space() + } + if p.wroteIndex(el.Index) { + p.WriteByte('=') + } + if el.Value != nil { + p.word(el.Value) + } + p.comments(left...) + } + if len(last) > 0 { + p.comments(last...) + p.flushComments() + } + p.decLevel() +} + +func (p *Printer) stmt(s *Stmt) { + p.wroteSemi = false + if s.Negated { + p.spacedString("!", s.Pos()) + } + var startRedirs int + if s.Cmd != nil { + startRedirs = p.command(s.Cmd, s.Redirs) + } + p.incLevel() + for _, r := range s.Redirs[startRedirs:] { + if p.wantsNewline(r.OpPos, true) { + p.bslashNewl() + } + if p.wantSpace == spaceRequired { + p.spacePad(r.Pos()) + } + if r.N != nil { + p.writeLit(r.N.Value) + } + p.WriteString(r.Op.String()) + if p.spaceRedirects && (r.Op != DplIn && r.Op != DplOut) { + p.space() + } else { + p.wantSpace = spaceRequired + } + p.word(r.Word) + if r.Op == Hdoc || r.Op == DashHdoc { + p.pendingHdocs = append(p.pendingHdocs, r) + } + } + sep := s.Semicolon.IsValid() && s.Semicolon.Line() > p.line && !p.singleLine + if sep || s.Background || s.Coprocess { + if sep { + p.bslashNewl() + } else if !p.minify { + p.space() + } + if s.Background { + p.WriteString("&") + } else if s.Coprocess { + p.WriteString("|&") + } else { + p.WriteString(";") + } + p.wroteSemi = true + p.wantSpace = spaceRequired + } + p.decLevel() +} + +func (p *Printer) command(cmd Command, redirs []*Redirect) (startRedirs int) { + p.advanceLine(cmd.Pos().Line()) + p.spacePad(cmd.Pos()) + switch x := cmd.(type) { + case *CallExpr: + p.assigns(x.Assigns) + if len(x.Args) <= 1 { + p.wordJoin(x.Args) + return 0 + } + p.wordJoin(x.Args[:1]) + for _, r := range redirs { + if r.Pos().After(x.Args[1].Pos()) || r.Op == Hdoc || r.Op == DashHdoc { + break + } + if p.wantSpace == spaceRequired { + p.spacePad(r.Pos()) + } + if r.N != nil { + p.writeLit(r.N.Value) + } + p.WriteString(r.Op.String()) + if p.spaceRedirects && (r.Op != DplIn && r.Op != DplOut) { + p.space() + } else { + p.wantSpace = spaceRequired + } + p.word(r.Word) + startRedirs++ + } + p.wordJoin(x.Args[1:]) + case *Block: + p.WriteByte('{') + p.wantSpace = spaceRequired + // Forbid "foo()\n{ bar; }" + p.wantNewline = p.wantNewline || p.funcNextLine + p.nestedStmts(x.Stmts, x.Last, x.Rbrace) + p.semiRsrv("}", x.Rbrace) + case *IfClause: + p.ifClause(x, false) + case *Subshell: + p.WriteByte('(') + stmts := x.Stmts + if len(stmts) > 0 && startsWithLparen(stmts[0]) { + p.wantSpace = spaceRequired + // Add a space between nested parentheses if we're printing them in a single line, + // to avoid the ambiguity between `((` and `( (`. + if (x.Lparen.Line() != stmts[0].Pos().Line() || len(stmts) > 1) && !p.singleLine { + p.wantSpace = spaceNotRequired + + if p.minify { + p.mustNewline = true + } + } + } else { + p.wantSpace = spaceNotRequired + } + + p.spacePad(stmtsPos(x.Stmts, x.Last)) + p.nestedStmts(x.Stmts, x.Last, x.Rparen) + p.wantSpace = spaceNotRequired + p.spacePad(x.Rparen) + p.rightParen(x.Rparen) + case *WhileClause: + if x.Until { + p.spacedString("until", x.Pos()) + } else { + p.spacedString("while", x.Pos()) + } + p.nestedStmts(x.Cond, x.CondLast, Pos{}) + p.semiOrNewl("do", x.DoPos) + p.nestedStmts(x.Do, x.DoLast, x.DonePos) + p.semiRsrv("done", x.DonePos) + case *ForClause: + if x.Select { + p.WriteString("select ") + } else { + p.WriteString("for ") + } + p.loop(x.Loop) + p.semiOrNewl("do", x.DoPos) + p.nestedStmts(x.Do, x.DoLast, x.DonePos) + p.semiRsrv("done", x.DonePos) + case *BinaryCmd: + p.stmt(x.X) + if p.minify || p.singleLine || x.Y.Pos().Line() <= p.line { + // leave p.nestedBinary untouched + p.spacedToken(x.Op.String(), x.OpPos) + p.advanceLine(x.Y.Pos().Line()) + p.stmt(x.Y) + break + } + indent := !p.nestedBinary + if indent { + p.incLevel() + } + if p.binNextLine { + if len(p.pendingHdocs) == 0 { + p.bslashNewl() + } + p.spacedToken(x.Op.String(), x.OpPos) + if len(x.Y.Comments) > 0 { + p.wantSpace = spaceNotRequired + p.newline(x.Y.Pos()) + p.indent() + p.comments(x.Y.Comments...) + p.newline(Pos{}) + p.indent() + } + } else { + p.spacedToken(x.Op.String(), x.OpPos) + p.advanceLine(x.OpPos.Line()) + p.comments(x.Y.Comments...) + p.newline(Pos{}) + p.indent() + } + p.advanceLine(x.Y.Pos().Line()) + _, p.nestedBinary = x.Y.Cmd.(*BinaryCmd) + p.stmt(x.Y) + if indent { + p.decLevel() + } + p.nestedBinary = false + case *FuncDecl: + if x.RsrvWord { + p.WriteString("function ") + } + p.writeLit(x.Name.Value) + if !x.RsrvWord || x.Parens { + p.WriteString("()") + } + if p.funcNextLine { + p.newline(Pos{}) + p.indent() + } else if !x.Parens || !p.minify { + p.space() + } + p.advanceLine(x.Body.Pos().Line()) + p.comments(x.Body.Comments...) + p.stmt(x.Body) + case *CaseClause: + p.WriteString("case ") + p.word(x.Word) + p.WriteString(" in") + p.advanceLine(x.In.Line()) + p.wantSpace = spaceRequired + if p.swtCaseIndent { + p.incLevel() + } + if len(x.Items) == 0 { + // Apparently "case x in; esac" is invalid shell. + p.mustNewline = true + } + for i, ci := range x.Items { + var last []Comment + for i, c := range ci.Comments { + if c.Pos().After(ci.Pos()) { + last = ci.Comments[i:] + break + } + p.comments(c) + } + p.newlines(ci.Pos()) + p.spacePad(ci.Pos()) + p.casePatternJoin(ci.Patterns) + p.WriteByte(')') + if !p.minify { + p.wantSpace = spaceRequired + } else { + p.wantSpace = spaceNotRequired + } + + bodyPos := stmtsPos(ci.Stmts, ci.Last) + bodyEnd := stmtsEnd(ci.Stmts, ci.Last) + sep := len(ci.Stmts) > 1 || bodyPos.Line() > p.line || + (bodyEnd.IsValid() && ci.OpPos.Line() > bodyEnd.Line()) + p.nestedStmts(ci.Stmts, ci.Last, ci.OpPos) + p.level++ + if !p.minify || i != len(x.Items)-1 { + if sep { + p.newlines(ci.OpPos) + p.wantNewline = true + } + p.spacedToken(ci.Op.String(), ci.OpPos) + p.advanceLine(ci.OpPos.Line()) + // avoid ; directly after tokens like ;; + p.wroteSemi = true + } + p.comments(last...) + p.flushComments() + p.level-- + } + p.comments(x.Last...) + if p.swtCaseIndent { + p.flushComments() + p.decLevel() + } + p.semiRsrv("esac", x.Esac) + case *ArithmCmd: + p.WriteString("((") + if x.Unsigned { + p.WriteString("# ") + } + p.arithmExpr(x.X, false, false) + p.WriteString("))") + case *TestClause: + p.WriteString("[[ ") + p.incLevel() + p.testExpr(x.X) + p.decLevel() + p.spacedString("]]", x.Right) + case *DeclClause: + p.spacedString(x.Variant.Value, x.Pos()) + p.assigns(x.Args) + case *TimeClause: + p.spacedString("time", x.Pos()) + if x.PosixFormat { + p.spacedString("-p", x.Pos()) + } + if x.Stmt != nil { + p.stmt(x.Stmt) + } + case *CoprocClause: + p.spacedString("coproc", x.Pos()) + if x.Name != nil { + p.space() + p.word(x.Name) + } + p.space() + p.stmt(x.Stmt) + case *LetClause: + p.spacedString("let", x.Pos()) + for _, n := range x.Exprs { + p.space() + p.arithmExpr(n, true, false) + } + case *TestDecl: + p.spacedString("@test", x.Pos()) + p.space() + p.word(x.Description) + p.space() + p.stmt(x.Body) + default: + panic(fmt.Sprintf("syntax.Printer: unexpected node type %T", x)) + } + return startRedirs +} + +func (p *Printer) ifClause(ic *IfClause, elif bool) { + if !elif { + p.spacedString("if", ic.Pos()) + } + p.nestedStmts(ic.Cond, ic.CondLast, Pos{}) + p.semiOrNewl("then", ic.ThenPos) + thenEnd := ic.FiPos + el := ic.Else + if el != nil { + thenEnd = el.Position + } + p.nestedStmts(ic.Then, ic.ThenLast, thenEnd) + + if el != nil && el.ThenPos.IsValid() { + p.comments(ic.Last...) + p.semiRsrv("elif", el.Position) + p.ifClause(el, true) + return + } + if el == nil { + p.comments(ic.Last...) + } else { + var left []Comment + for _, c := range ic.Last { + if c.Pos().After(el.Position) { + left = append(left, c) + break + } + p.comments(c) + } + p.semiRsrv("else", el.Position) + p.comments(left...) + p.nestedStmts(el.Then, el.ThenLast, ic.FiPos) + p.comments(el.Last...) + } + p.semiRsrv("fi", ic.FiPos) +} + +func (p *Printer) stmtList(stmts []*Stmt, last []Comment) { + sep := p.wantNewline || (len(stmts) > 0 && stmts[0].Pos().Line() > p.line) + for i, s := range stmts { + if i > 0 && p.singleLine && p.wantNewline && !p.wroteSemi { + // In singleLine mode, ensure we use semicolons between + // statements. + p.WriteByte(';') + p.wantSpace = spaceRequired + } + pos := s.Pos() + var midComs, endComs []Comment + for _, c := range s.Comments { + // Comments after the end of this command. Note that + // this includes "< 1: + // Force a newline if we find: + // { stmt; stmt; } + p.wantNewline = true + case closing.Line() > p.line && len(stmts) > 0 && + stmtsEnd(stmts, last).Line() < closing.Line(): + // Force a newline if we find: + // { stmt + // } + p.wantNewline = true + case len(p.pendingComments) > 0 && len(stmts) > 0: + // Force a newline if we find: + // for i in a b # stmt + // do foo; done + p.wantNewline = true + } + p.stmtList(stmts, last) + if closing.IsValid() { + p.flushComments() + } + p.decLevel() +} + +func (p *Printer) assigns(assigns []*Assign) { + p.incLevel() + for _, a := range assigns { + if p.wantsNewline(a.Pos(), true) { + p.bslashNewl() + } else { + p.spacePad(a.Pos()) + } + if a.Name != nil { + p.writeLit(a.Name.Value) + p.wroteIndex(a.Index) + if a.Append { + p.WriteByte('+') + } + if !a.Naked { + p.WriteByte('=') + } + } + if a.Value != nil { + // Ensure we don't use an escaped newline after '=', + // because that can result in indentation, thus + // splitting "foo=bar" into "foo= bar". + p.advanceLine(a.Value.Pos().Line()) + p.word(a.Value) + } else if a.Array != nil { + p.wantSpace = spaceNotRequired + p.WriteByte('(') + p.elemJoin(a.Array.Elems, a.Array.Last) + p.rightParen(a.Array.Rparen) + } + p.wantSpace = spaceRequired + } + p.decLevel() +} + +type wantSpaceState uint8 + +const ( + spaceNotRequired wantSpaceState = iota + spaceRequired // we should generally print a space or a newline next + spaceWritten // we have just written a space or newline +) + +// extraIndenter ensures that all lines in a '<<-' heredoc body have at least +// baseIndent leading tabs. Those that had more tab indentation than the first +// heredoc line will keep that relative indentation. +type extraIndenter struct { + bufWriter + baseIndent int + + firstIndent int + firstChange int + curLine []byte +} + +func (e *extraIndenter) WriteByte(b byte) error { + e.curLine = append(e.curLine, b) + if b != '\n' { + return nil + } + trimmed := bytes.TrimLeft(e.curLine, "\t") + if len(trimmed) == 1 { + // no tabs if this is an empty line, i.e. "\n" + e.bufWriter.Write(trimmed) + e.curLine = e.curLine[:0] + return nil + } + + lineIndent := len(e.curLine) - len(trimmed) + if e.firstIndent < 0 { + // This is the first heredoc line we add extra indentation to. + // Keep track of how much we indented. + e.firstIndent = lineIndent + e.firstChange = e.baseIndent - lineIndent + lineIndent = e.baseIndent + + } else if lineIndent < e.firstIndent { + // This line did not have enough indentation; simply indent it + // like the first line. + lineIndent = e.firstIndent + } else { + // This line had plenty of indentation. Add the extra + // indentation that the first line had, for consistency. + lineIndent += e.firstChange + } + e.bufWriter.WriteByte(tabwriter.Escape) + for i := 0; i < lineIndent; i++ { + e.bufWriter.WriteByte('\t') + } + e.bufWriter.WriteByte(tabwriter.Escape) + e.bufWriter.Write(trimmed) + e.curLine = e.curLine[:0] + return nil +} + +func (e *extraIndenter) WriteString(s string) (int, error) { + for i := 0; i < len(s); i++ { + e.WriteByte(s[i]) + } + return len(s), nil +} + +func startsWithLparen(node Node) bool { + switch node := node.(type) { + case *Stmt: + return startsWithLparen(node.Cmd) + case *BinaryCmd: + return startsWithLparen(node.X) + case *Subshell: + return true // keep ( ( + case *ArithmCmd: + return true // keep ( (( + } + return false +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/quote.go b/vendor/mvdan.cc/sh/v3/syntax/quote.go new file mode 100644 index 00000000..6f27eba1 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/quote.go @@ -0,0 +1,185 @@ +// Copyright (c) 2021, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +type QuoteError struct { + ByteOffset int + Message string +} + +func (e QuoteError) Error() string { + return fmt.Sprintf("cannot quote character at byte %d: %s", e.ByteOffset, e.Message) +} + +const ( + quoteErrNull = "shell strings cannot contain null bytes" + quoteErrPOSIX = "POSIX shell lacks escape sequences" + quoteErrRange = "rune out of range" + quoteErrMksh = "mksh cannot escape codepoints above 16 bits" +) + +// Quote returns a quoted version of the input string, +// so that the quoted version is expanded or interpreted +// as the original string in the given language variant. +// +// Quoting is necessary when using arbitrary literal strings +// as words in a shell script or command. +// Without quoting, one can run into syntax errors, +// as well as the possibility of running unintended code. +// +// An error is returned when a string cannot be quoted for a variant. +// For instance, POSIX lacks escape sequences for non-printable characters, +// and no language variant can represent a string containing null bytes. +// In such cases, the returned error type will be *QuoteError. +// +// The quoting strategy is chosen on a best-effort basis, +// to minimize the amount of extra bytes necessary. +// +// Some strings do not require any quoting and are returned unchanged. +// Those strings can be directly surrounded in single quotes as well. +func Quote(s string, lang LangVariant) (string, error) { + if s == "" { + // Special case; an empty string must always be quoted, + // as otherwise it expands to zero fields. + return "''", nil + } + shellChars := false + nonPrintable := false + offs := 0 + for rem := s; len(rem) > 0; { + r, size := utf8.DecodeRuneInString(rem) + switch r { + // Like regOps; token characters. + case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`', + // Whitespace; might result in multiple fields. + ' ', '\t', '\r', '\n', + // Escape sequences would be expanded. + '\\', + // Would start a comment unless quoted. + '#', + // Might result in brace expansion. + '{', + // Might result in tilde expansion. + '~', + // Might result in globbing. + '*', '?', '[', + // Might result in an assignment. + '=': + shellChars = true + case '\x00': + return "", &QuoteError{ByteOffset: offs, Message: quoteErrNull} + } + if r == utf8.RuneError || !unicode.IsPrint(r) { + if lang == LangPOSIX { + return "", &QuoteError{ByteOffset: offs, Message: quoteErrPOSIX} + } + nonPrintable = true + } + rem = rem[size:] + offs += size + } + if !shellChars && !nonPrintable && !IsKeyword(s) { + // Nothing to quote; avoid allocating. + return s, nil + } + + // Single quotes are usually best, + // as they don't require any escaping of characters. + // If we have any invalid utf8 or non-printable runes, + // use $'' so that we can escape them. + // Note that we can't use double quotes for those. + var b strings.Builder + if nonPrintable { + b.WriteString("$'") + lastRequoteIfHex := false + offs := 0 + for rem := s; len(rem) > 0; { + nextRequoteIfHex := false + r, size := utf8.DecodeRuneInString(rem) + switch { + case r == '\'', r == '\\': + b.WriteByte('\\') + b.WriteRune(r) + case unicode.IsPrint(r) && r != utf8.RuneError: + if lastRequoteIfHex && isHex(r) { + b.WriteString("'$'") + } + b.WriteRune(r) + case r == '\a': + b.WriteString(`\a`) + case r == '\b': + b.WriteString(`\b`) + case r == '\f': + b.WriteString(`\f`) + case r == '\n': + b.WriteString(`\n`) + case r == '\r': + b.WriteString(`\r`) + case r == '\t': + b.WriteString(`\t`) + case r == '\v': + b.WriteString(`\v`) + case r < utf8.RuneSelf, r == utf8.RuneError && size == 1: + // \xXX, fixed at two hexadecimal characters. + fmt.Fprintf(&b, "\\x%02x", rem[0]) + // Unfortunately, mksh allows \x to consume more hex characters. + // Ensure that we don't allow it to read more than two. + if lang == LangMirBSDKorn { + nextRequoteIfHex = true + } + case r > utf8.MaxRune: + // Not a valid Unicode code point? + return "", &QuoteError{ByteOffset: offs, Message: quoteErrRange} + case lang == LangMirBSDKorn && r > 0xFFFD: + // From the CAVEATS section in R59's man page: + // + // mksh currently uses OPTU-16 internally, which is the same as + // UTF-8 and CESU-8 with 0000..FFFD being valid codepoints. + return "", &QuoteError{ByteOffset: offs, Message: quoteErrMksh} + case r < 0x10000: + // \uXXXX, fixed at four hexadecimal characters. + fmt.Fprintf(&b, "\\u%04x", r) + default: + // \UXXXXXXXX, fixed at eight hexadecimal characters. + fmt.Fprintf(&b, "\\U%08x", r) + } + rem = rem[size:] + lastRequoteIfHex = nextRequoteIfHex + offs += size + } + b.WriteString("'") + return b.String(), nil + } + + // Single quotes without any need for escaping. + if !strings.Contains(s, "'") { + return "'" + s + "'", nil + } + + // The string contains single quotes, + // so fall back to double quotes. + b.WriteByte('"') + for _, r := range s { + switch r { + case '"', '\\', '`', '$': + b.WriteByte('\\') + } + b.WriteRune(r) + } + b.WriteByte('"') + return b.String(), nil +} + +func isHex(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/quotestate_string.go b/vendor/mvdan.cc/sh/v3/syntax/quotestate_string.go new file mode 100644 index 00000000..d43466f8 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/quotestate_string.go @@ -0,0 +1,61 @@ +// Code generated by "stringer -type=quoteState"; DO NOT EDIT. + +package syntax + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[noState-1] + _ = x[subCmd-2] + _ = x[subCmdBckquo-4] + _ = x[dblQuotes-8] + _ = x[hdocWord-16] + _ = x[hdocBody-32] + _ = x[hdocBodyTabs-64] + _ = x[arithmExpr-128] + _ = x[arithmExprLet-256] + _ = x[arithmExprCmd-512] + _ = x[arithmExprBrack-1024] + _ = x[testExpr-2048] + _ = x[testExprRegexp-4096] + _ = x[switchCase-8192] + _ = x[paramExpName-16384] + _ = x[paramExpSlice-32768] + _ = x[paramExpRepl-65536] + _ = x[paramExpExp-131072] + _ = x[arrayElems-262144] +} + +const _quoteState_name = "noStatesubCmdsubCmdBckquodblQuoteshdocWordhdocBodyhdocBodyTabsarithmExprarithmExprLetarithmExprCmdarithmExprBracktestExprtestExprRegexpswitchCaseparamExpNameparamExpSliceparamExpReplparamExpExparrayElems" + +var _quoteState_map = map[quoteState]string{ + 1: _quoteState_name[0:7], + 2: _quoteState_name[7:13], + 4: _quoteState_name[13:25], + 8: _quoteState_name[25:34], + 16: _quoteState_name[34:42], + 32: _quoteState_name[42:50], + 64: _quoteState_name[50:62], + 128: _quoteState_name[62:72], + 256: _quoteState_name[72:85], + 512: _quoteState_name[85:98], + 1024: _quoteState_name[98:113], + 2048: _quoteState_name[113:121], + 4096: _quoteState_name[121:135], + 8192: _quoteState_name[135:145], + 16384: _quoteState_name[145:157], + 32768: _quoteState_name[157:170], + 65536: _quoteState_name[170:182], + 131072: _quoteState_name[182:193], + 262144: _quoteState_name[193:203], +} + +func (i quoteState) String() string { + if str, ok := _quoteState_map[i]; ok { + return str + } + return "quoteState(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/simplify.go b/vendor/mvdan.cc/sh/v3/syntax/simplify.go new file mode 100644 index 00000000..e82fd55a --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/simplify.go @@ -0,0 +1,250 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import "bytes" + +// Simplify modifies a node to remove redundant pieces of syntax, and returns +// whether any changes were made. +// +// The changes currently applied are: +// +// Remove clearly useless parentheses $(( (expr) )) +// Remove dollars from vars in exprs (($var)) +// Remove duplicate subshells $( (stmts) ) +// Remove redundant quotes [[ "$var" == str ]] +// Merge negations with unary operators [[ ! -n $var ]] +// Use single quotes to shorten literals "\$foo" +func Simplify(n Node) bool { + s := simplifier{} + Walk(n, s.visit) + return s.modified +} + +type simplifier struct { + modified bool +} + +func (s *simplifier) visit(node Node) bool { + switch x := node.(type) { + case *Assign: + x.Index = s.removeParensArithm(x.Index) + // Don't inline params, as x[i] and x[$i] mean + // different things when x is an associative + // array; the first means "i", the second "$i". + case *ParamExp: + x.Index = s.removeParensArithm(x.Index) + // don't inline params - same as above. + + if x.Slice == nil { + break + } + x.Slice.Offset = s.removeParensArithm(x.Slice.Offset) + x.Slice.Offset = s.inlineSimpleParams(x.Slice.Offset) + x.Slice.Length = s.removeParensArithm(x.Slice.Length) + x.Slice.Length = s.inlineSimpleParams(x.Slice.Length) + case *ArithmExp: + x.X = s.removeParensArithm(x.X) + x.X = s.inlineSimpleParams(x.X) + case *ArithmCmd: + x.X = s.removeParensArithm(x.X) + x.X = s.inlineSimpleParams(x.X) + case *ParenArithm: + x.X = s.removeParensArithm(x.X) + x.X = s.inlineSimpleParams(x.X) + case *BinaryArithm: + x.X = s.inlineSimpleParams(x.X) + x.Y = s.inlineSimpleParams(x.Y) + case *CmdSubst: + x.Stmts = s.inlineSubshell(x.Stmts) + case *Subshell: + x.Stmts = s.inlineSubshell(x.Stmts) + case *Word: + x.Parts = s.simplifyWord(x.Parts) + case *TestClause: + x.X = s.removeParensTest(x.X) + x.X = s.removeNegateTest(x.X) + case *ParenTest: + x.X = s.removeParensTest(x.X) + x.X = s.removeNegateTest(x.X) + case *BinaryTest: + x.X = s.unquoteParams(x.X) + x.X = s.removeNegateTest(x.X) + if x.Op == TsMatchShort { + s.modified = true + x.Op = TsMatch + } + switch x.Op { + case TsMatch, TsNoMatch: + // unquoting enables globbing + default: + x.Y = s.unquoteParams(x.Y) + } + x.Y = s.removeNegateTest(x.Y) + case *UnaryTest: + x.X = s.unquoteParams(x.X) + } + return true +} + +func (s *simplifier) simplifyWord(wps []WordPart) []WordPart { +parts: + for i, wp := range wps { + dq, _ := wp.(*DblQuoted) + if dq == nil || len(dq.Parts) != 1 { + break + } + lit, _ := dq.Parts[0].(*Lit) + if lit == nil { + break + } + var buf bytes.Buffer + escaped := false + for _, r := range lit.Value { + switch r { + case '\\': + escaped = !escaped + if escaped { + continue + } + case '\'': + continue parts + case '$', '"', '`': + escaped = false + default: + if escaped { + continue parts + } + escaped = false + } + buf.WriteRune(r) + } + newVal := buf.String() + if newVal == lit.Value { + break + } + s.modified = true + wps[i] = &SglQuoted{ + Left: dq.Pos(), + Right: dq.End(), + Dollar: dq.Dollar, + Value: newVal, + } + } + return wps +} + +func (s *simplifier) removeParensArithm(x ArithmExpr) ArithmExpr { + for { + par, _ := x.(*ParenArithm) + if par == nil { + return x + } + s.modified = true + x = par.X + } +} + +func (s *simplifier) inlineSimpleParams(x ArithmExpr) ArithmExpr { + w, _ := x.(*Word) + if w == nil || len(w.Parts) != 1 { + return x + } + pe, _ := w.Parts[0].(*ParamExp) + if pe == nil || !ValidName(pe.Param.Value) { + // Not a parameter expansion, or not a valid name, like $3. + return x + } + if pe.Excl || pe.Length || pe.Width || pe.Slice != nil || + pe.Repl != nil || pe.Exp != nil || pe.Index != nil { + // A complex parameter expansion can't be simplified. + // + // Note that index expressions can't generally be simplified + // either. It's fine to turn ${a[0]} into a[0], but others like + // a[*] are invalid in many shells including Bash. + return x + } + s.modified = true + return &Word{Parts: []WordPart{pe.Param}} +} + +func (s *simplifier) inlineSubshell(stmts []*Stmt) []*Stmt { + for len(stmts) == 1 { + st := stmts[0] + if st.Negated || st.Background || st.Coprocess || + len(st.Redirs) > 0 { + break + } + sub, _ := st.Cmd.(*Subshell) + if sub == nil { + break + } + s.modified = true + stmts = sub.Stmts + } + return stmts +} + +func (s *simplifier) unquoteParams(x TestExpr) TestExpr { + w, _ := x.(*Word) + if w == nil || len(w.Parts) != 1 { + return x + } + dq, _ := w.Parts[0].(*DblQuoted) + if dq == nil || len(dq.Parts) != 1 { + return x + } + if _, ok := dq.Parts[0].(*ParamExp); !ok { + return x + } + s.modified = true + w.Parts = dq.Parts + return w +} + +func (s *simplifier) removeParensTest(x TestExpr) TestExpr { + for { + par, _ := x.(*ParenTest) + if par == nil { + return x + } + s.modified = true + x = par.X + } +} + +func (s *simplifier) removeNegateTest(x TestExpr) TestExpr { + u, _ := x.(*UnaryTest) + if u == nil || u.Op != TsNot { + return x + } + switch y := u.X.(type) { + case *UnaryTest: + switch y.Op { + case TsEmpStr: + y.Op = TsNempStr + s.modified = true + return y + case TsNempStr: + y.Op = TsEmpStr + s.modified = true + return y + case TsNot: + s.modified = true + return y.X + } + case *BinaryTest: + switch y.Op { + case TsMatch: + y.Op = TsNoMatch + s.modified = true + return y + case TsNoMatch: + y.Op = TsMatch + s.modified = true + return y + } + } + return x +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/token_string.go b/vendor/mvdan.cc/sh/v3/syntax/token_string.go new file mode 100644 index 00000000..ab5c83ac --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/token_string.go @@ -0,0 +1,149 @@ +// Code generated by "stringer -type token -linecomment -trimprefix _"; DO NOT EDIT. + +package syntax + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[illegalTok-0] + _ = x[_EOF-1] + _ = x[_Newl-2] + _ = x[_Lit-3] + _ = x[_LitWord-4] + _ = x[_LitRedir-5] + _ = x[sglQuote-6] + _ = x[dblQuote-7] + _ = x[bckQuote-8] + _ = x[and-9] + _ = x[andAnd-10] + _ = x[orOr-11] + _ = x[or-12] + _ = x[orAnd-13] + _ = x[dollar-14] + _ = x[dollSglQuote-15] + _ = x[dollDblQuote-16] + _ = x[dollBrace-17] + _ = x[dollBrack-18] + _ = x[dollParen-19] + _ = x[dollDblParen-20] + _ = x[leftBrack-21] + _ = x[dblLeftBrack-22] + _ = x[leftParen-23] + _ = x[dblLeftParen-24] + _ = x[rightBrace-25] + _ = x[rightBrack-26] + _ = x[rightParen-27] + _ = x[dblRightParen-28] + _ = x[semicolon-29] + _ = x[dblSemicolon-30] + _ = x[semiAnd-31] + _ = x[dblSemiAnd-32] + _ = x[semiOr-33] + _ = x[exclMark-34] + _ = x[tilde-35] + _ = x[addAdd-36] + _ = x[subSub-37] + _ = x[star-38] + _ = x[power-39] + _ = x[equal-40] + _ = x[nequal-41] + _ = x[lequal-42] + _ = x[gequal-43] + _ = x[addAssgn-44] + _ = x[subAssgn-45] + _ = x[mulAssgn-46] + _ = x[quoAssgn-47] + _ = x[remAssgn-48] + _ = x[andAssgn-49] + _ = x[orAssgn-50] + _ = x[xorAssgn-51] + _ = x[shlAssgn-52] + _ = x[shrAssgn-53] + _ = x[rdrOut-54] + _ = x[appOut-55] + _ = x[rdrIn-56] + _ = x[rdrInOut-57] + _ = x[dplIn-58] + _ = x[dplOut-59] + _ = x[clbOut-60] + _ = x[hdoc-61] + _ = x[dashHdoc-62] + _ = x[wordHdoc-63] + _ = x[rdrAll-64] + _ = x[appAll-65] + _ = x[cmdIn-66] + _ = x[cmdOut-67] + _ = x[plus-68] + _ = x[colPlus-69] + _ = x[minus-70] + _ = x[colMinus-71] + _ = x[quest-72] + _ = x[colQuest-73] + _ = x[assgn-74] + _ = x[colAssgn-75] + _ = x[perc-76] + _ = x[dblPerc-77] + _ = x[hash-78] + _ = x[dblHash-79] + _ = x[caret-80] + _ = x[dblCaret-81] + _ = x[comma-82] + _ = x[dblComma-83] + _ = x[at-84] + _ = x[slash-85] + _ = x[dblSlash-86] + _ = x[colon-87] + _ = x[tsExists-88] + _ = x[tsRegFile-89] + _ = x[tsDirect-90] + _ = x[tsCharSp-91] + _ = x[tsBlckSp-92] + _ = x[tsNmPipe-93] + _ = x[tsSocket-94] + _ = x[tsSmbLink-95] + _ = x[tsSticky-96] + _ = x[tsGIDSet-97] + _ = x[tsUIDSet-98] + _ = x[tsGrpOwn-99] + _ = x[tsUsrOwn-100] + _ = x[tsModif-101] + _ = x[tsRead-102] + _ = x[tsWrite-103] + _ = x[tsExec-104] + _ = x[tsNoEmpty-105] + _ = x[tsFdTerm-106] + _ = x[tsEmpStr-107] + _ = x[tsNempStr-108] + _ = x[tsOptSet-109] + _ = x[tsVarSet-110] + _ = x[tsRefVar-111] + _ = x[tsReMatch-112] + _ = x[tsNewer-113] + _ = x[tsOlder-114] + _ = x[tsDevIno-115] + _ = x[tsEql-116] + _ = x[tsNeq-117] + _ = x[tsLeq-118] + _ = x[tsGeq-119] + _ = x[tsLss-120] + _ = x[tsGtr-121] + _ = x[globQuest-122] + _ = x[globStar-123] + _ = x[globPlus-124] + _ = x[globAt-125] + _ = x[globExcl-126] +} + +const _token_name = "illegalTokEOFNewlLitLitWordLitRedir'\"`&&&||||&$$'$\"${$[$($(([[[(((}])));;;;&;;&;|!~++--***==!=<=>=+=-=*=/=%=&=|=^=<<=>>=>>><<><&>&>|<<<<-<<<&>&>><(>(+:+-:-?:?=:=%%%###^^^,,,@///:-e-f-d-c-b-p-S-L-k-g-u-G-O-N-r-w-x-s-t-z-n-o-v-R=~-nt-ot-ef-eq-ne-le-ge-lt-gt?(*(+(@(!(" + +var _token_index = [...]uint16{0, 10, 13, 17, 20, 27, 35, 36, 37, 38, 39, 41, 43, 44, 46, 47, 49, 51, 53, 55, 57, 60, 61, 63, 64, 66, 67, 68, 69, 71, 72, 74, 76, 79, 81, 82, 83, 85, 87, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 117, 120, 121, 123, 124, 126, 128, 130, 132, 134, 137, 140, 142, 145, 147, 149, 150, 152, 153, 155, 156, 158, 159, 161, 162, 164, 165, 167, 168, 170, 171, 173, 174, 175, 177, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 231, 234, 237, 240, 243, 246, 249, 252, 255, 257, 259, 261, 263, 265} + +func (i token) String() string { + if i >= token(len(_token_index)-1) { + return "token(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _token_name[_token_index[i]:_token_index[i+1]] +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/tokens.go b/vendor/mvdan.cc/sh/v3/syntax/tokens.go new file mode 100644 index 00000000..6a64b213 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/tokens.go @@ -0,0 +1,349 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +//go:generate stringer -type token -linecomment -trimprefix _ + +type token uint32 + +// The list of all possible tokens. +const ( + illegalTok token = iota + + _EOF + _Newl + _Lit + _LitWord + _LitRedir + + sglQuote // ' + dblQuote // " + bckQuote // ` + + and // & + andAnd // && + orOr // || + or // | + orAnd // |& + + dollar // $ + dollSglQuote // $' + dollDblQuote // $" + dollBrace // ${ + dollBrack // $[ + dollParen // $( + dollDblParen // $(( + leftBrack // [ + dblLeftBrack // [[ + leftParen // ( + dblLeftParen // (( + + rightBrace // } + rightBrack // ] + rightParen // ) + dblRightParen // )) + semicolon // ; + + dblSemicolon // ;; + semiAnd // ;& + dblSemiAnd // ;;& + semiOr // ;| + + exclMark // ! + tilde // ~ + addAdd // ++ + subSub // -- + star // * + power // ** + equal // == + nequal // != + lequal // <= + gequal // >= + + addAssgn // += + subAssgn // -= + mulAssgn // *= + quoAssgn // /= + remAssgn // %= + andAssgn // &= + orAssgn // |= + xorAssgn // ^= + shlAssgn // <<= + shrAssgn // >>= + + rdrOut // > + appOut // >> + rdrIn // < + rdrInOut // <> + dplIn // <& + dplOut // >& + clbOut // >| + hdoc // << + dashHdoc // <<- + wordHdoc // <<< + rdrAll // &> + appAll // &>> + + cmdIn // <( + cmdOut // >( + + plus // + + colPlus // :+ + minus // - + colMinus // :- + quest // ? + colQuest // :? + assgn // = + colAssgn // := + perc // % + dblPerc // %% + hash // # + dblHash // ## + caret // ^ + dblCaret // ^^ + comma // , + dblComma // ,, + at // @ + slash // / + dblSlash // // + colon // : + + tsExists // -e + tsRegFile // -f + tsDirect // -d + tsCharSp // -c + tsBlckSp // -b + tsNmPipe // -p + tsSocket // -S + tsSmbLink // -L + tsSticky // -k + tsGIDSet // -g + tsUIDSet // -u + tsGrpOwn // -G + tsUsrOwn // -O + tsModif // -N + tsRead // -r + tsWrite // -w + tsExec // -x + tsNoEmpty // -s + tsFdTerm // -t + tsEmpStr // -z + tsNempStr // -n + tsOptSet // -o + tsVarSet // -v + tsRefVar // -R + + tsReMatch // =~ + tsNewer // -nt + tsOlder // -ot + tsDevIno // -ef + tsEql // -eq + tsNeq // -ne + tsLeq // -le + tsGeq // -ge + tsLss // -lt + tsGtr // -gt + + globQuest // ?( + globStar // *( + globPlus // +( + globAt // @( + globExcl // !( +) + +type RedirOperator token + +const ( + RdrOut = RedirOperator(rdrOut) + iota // > + AppOut // >> + RdrIn // < + RdrInOut // <> + DplIn // <& + DplOut // >& + ClbOut // >| + Hdoc // << + DashHdoc // <<- + WordHdoc // <<< + RdrAll // &> + AppAll // &>> +) + +type ProcOperator token + +const ( + CmdIn = ProcOperator(cmdIn) + iota // <( + CmdOut // >( +) + +type GlobOperator token + +const ( + GlobZeroOrOne = GlobOperator(globQuest) + iota // ?( + GlobZeroOrMore // *( + GlobOneOrMore // +( + GlobOne // @( + GlobExcept // !( +) + +type BinCmdOperator token + +const ( + AndStmt = BinCmdOperator(andAnd) + iota // && + OrStmt // || + Pipe // | + PipeAll // |& +) + +type CaseOperator token + +const ( + Break = CaseOperator(dblSemicolon) + iota // ;; + Fallthrough // ;& + Resume // ;;& + ResumeKorn // ;| +) + +type ParNamesOperator token + +const ( + NamesPrefix = ParNamesOperator(star) // * + NamesPrefixWords = ParNamesOperator(at) // @ +) + +type ParExpOperator token + +const ( + AlternateUnset = ParExpOperator(plus) + iota // + + AlternateUnsetOrNull // :+ + DefaultUnset // - + DefaultUnsetOrNull // :- + ErrorUnset // ? + ErrorUnsetOrNull // :? + AssignUnset // = + AssignUnsetOrNull // := + RemSmallSuffix // % + RemLargeSuffix // %% + RemSmallPrefix // # + RemLargePrefix // ## + UpperFirst // ^ + UpperAll // ^^ + LowerFirst // , + LowerAll // ,, + OtherParamOps // @ +) + +type UnAritOperator token + +const ( + Not = UnAritOperator(exclMark) + iota // ! + BitNegation // ~ + Inc // ++ + Dec // -- + Plus = UnAritOperator(plus) // + + Minus = UnAritOperator(minus) // - +) + +type BinAritOperator token + +const ( + Add = BinAritOperator(plus) // + + Sub = BinAritOperator(minus) // - + Mul = BinAritOperator(star) // * + Quo = BinAritOperator(slash) // / + Rem = BinAritOperator(perc) // % + Pow = BinAritOperator(power) // ** + Eql = BinAritOperator(equal) // == + Gtr = BinAritOperator(rdrOut) // > + Lss = BinAritOperator(rdrIn) // < + Neq = BinAritOperator(nequal) // != + Leq = BinAritOperator(lequal) // <= + Geq = BinAritOperator(gequal) // >= + And = BinAritOperator(and) // & + Or = BinAritOperator(or) // | + Xor = BinAritOperator(caret) // ^ + Shr = BinAritOperator(appOut) // >> + Shl = BinAritOperator(hdoc) // << + + AndArit = BinAritOperator(andAnd) // && + OrArit = BinAritOperator(orOr) // || + Comma = BinAritOperator(comma) // , + TernQuest = BinAritOperator(quest) // ? + TernColon = BinAritOperator(colon) // : + + Assgn = BinAritOperator(assgn) // = + AddAssgn = BinAritOperator(addAssgn) // += + SubAssgn = BinAritOperator(subAssgn) // -= + MulAssgn = BinAritOperator(mulAssgn) // *= + QuoAssgn = BinAritOperator(quoAssgn) // /= + RemAssgn = BinAritOperator(remAssgn) // %= + AndAssgn = BinAritOperator(andAssgn) // &= + OrAssgn = BinAritOperator(orAssgn) // |= + XorAssgn = BinAritOperator(xorAssgn) // ^= + ShlAssgn = BinAritOperator(shlAssgn) // <<= + ShrAssgn = BinAritOperator(shrAssgn) // >>= +) + +type UnTestOperator token + +const ( + TsExists = UnTestOperator(tsExists) + iota // -e + TsRegFile // -f + TsDirect // -d + TsCharSp // -c + TsBlckSp // -b + TsNmPipe // -p + TsSocket // -S + TsSmbLink // -L + TsSticky // -k + TsGIDSet // -g + TsUIDSet // -u + TsGrpOwn // -G + TsUsrOwn // -O + TsModif // -N + TsRead // -r + TsWrite // -w + TsExec // -x + TsNoEmpty // -s + TsFdTerm // -t + TsEmpStr // -z + TsNempStr // -n + TsOptSet // -o + TsVarSet // -v + TsRefVar // -R + TsNot = UnTestOperator(exclMark) // ! +) + +type BinTestOperator token + +const ( + TsReMatch = BinTestOperator(tsReMatch) + iota // =~ + TsNewer // -nt + TsOlder // -ot + TsDevIno // -ef + TsEql // -eq + TsNeq // -ne + TsLeq // -le + TsGeq // -ge + TsLss // -lt + TsGtr // -gt + AndTest = BinTestOperator(andAnd) // && + OrTest = BinTestOperator(orOr) // || + TsMatchShort = BinTestOperator(assgn) // = + TsMatch = BinTestOperator(equal) // == + TsNoMatch = BinTestOperator(nequal) // != + TsBefore = BinTestOperator(rdrIn) // < + TsAfter = BinTestOperator(rdrOut) // > +) + +func (o RedirOperator) String() string { return token(o).String() } +func (o ProcOperator) String() string { return token(o).String() } +func (o GlobOperator) String() string { return token(o).String() } +func (o BinCmdOperator) String() string { return token(o).String() } +func (o CaseOperator) String() string { return token(o).String() } +func (o ParNamesOperator) String() string { return token(o).String() } +func (o ParExpOperator) String() string { return token(o).String() } +func (o UnAritOperator) String() string { return token(o).String() } +func (o BinAritOperator) String() string { return token(o).String() } +func (o UnTestOperator) String() string { return token(o).String() } +func (o BinTestOperator) String() string { return token(o).String() } diff --git a/vendor/mvdan.cc/sh/v3/syntax/walk.go b/vendor/mvdan.cc/sh/v3/syntax/walk.go new file mode 100644 index 00000000..5be8f9c6 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/walk.go @@ -0,0 +1,313 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "fmt" + "io" + "reflect" +) + +func walkStmts(stmts []*Stmt, last []Comment, f func(Node) bool) { + for _, s := range stmts { + Walk(s, f) + } + for _, c := range last { + Walk(&c, f) + } +} + +func walkWords(words []*Word, f func(Node) bool) { + for _, w := range words { + Walk(w, f) + } +} + +// Walk traverses a syntax tree in depth-first order: It starts by calling +// f(node); node must not be nil. If f returns true, Walk invokes f +// recursively for each of the non-nil children of node, followed by +// f(nil). +func Walk(node Node, f func(Node) bool) { + if !f(node) { + return + } + + switch x := node.(type) { + case *File: + walkStmts(x.Stmts, x.Last, f) + case *Comment: + case *Stmt: + for _, c := range x.Comments { + if !x.End().After(c.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } + if x.Cmd != nil { + Walk(x.Cmd, f) + } + for _, r := range x.Redirs { + Walk(r, f) + } + case *Assign: + if x.Name != nil { + Walk(x.Name, f) + } + if x.Value != nil { + Walk(x.Value, f) + } + if x.Index != nil { + Walk(x.Index, f) + } + if x.Array != nil { + Walk(x.Array, f) + } + case *Redirect: + if x.N != nil { + Walk(x.N, f) + } + Walk(x.Word, f) + if x.Hdoc != nil { + Walk(x.Hdoc, f) + } + case *CallExpr: + for _, a := range x.Assigns { + Walk(a, f) + } + walkWords(x.Args, f) + case *Subshell: + walkStmts(x.Stmts, x.Last, f) + case *Block: + walkStmts(x.Stmts, x.Last, f) + case *IfClause: + walkStmts(x.Cond, x.CondLast, f) + walkStmts(x.Then, x.ThenLast, f) + if x.Else != nil { + Walk(x.Else, f) + } + case *WhileClause: + walkStmts(x.Cond, x.CondLast, f) + walkStmts(x.Do, x.DoLast, f) + case *ForClause: + Walk(x.Loop, f) + walkStmts(x.Do, x.DoLast, f) + case *WordIter: + Walk(x.Name, f) + walkWords(x.Items, f) + case *CStyleLoop: + if x.Init != nil { + Walk(x.Init, f) + } + if x.Cond != nil { + Walk(x.Cond, f) + } + if x.Post != nil { + Walk(x.Post, f) + } + case *BinaryCmd: + Walk(x.X, f) + Walk(x.Y, f) + case *FuncDecl: + Walk(x.Name, f) + Walk(x.Body, f) + case *Word: + for _, wp := range x.Parts { + Walk(wp, f) + } + case *Lit: + case *SglQuoted: + case *DblQuoted: + for _, wp := range x.Parts { + Walk(wp, f) + } + case *CmdSubst: + walkStmts(x.Stmts, x.Last, f) + case *ParamExp: + Walk(x.Param, f) + if x.Index != nil { + Walk(x.Index, f) + } + if x.Repl != nil { + if x.Repl.Orig != nil { + Walk(x.Repl.Orig, f) + } + if x.Repl.With != nil { + Walk(x.Repl.With, f) + } + } + if x.Exp != nil && x.Exp.Word != nil { + Walk(x.Exp.Word, f) + } + case *ArithmExp: + Walk(x.X, f) + case *ArithmCmd: + Walk(x.X, f) + case *BinaryArithm: + Walk(x.X, f) + Walk(x.Y, f) + case *BinaryTest: + Walk(x.X, f) + Walk(x.Y, f) + case *UnaryArithm: + Walk(x.X, f) + case *UnaryTest: + Walk(x.X, f) + case *ParenArithm: + Walk(x.X, f) + case *ParenTest: + Walk(x.X, f) + case *CaseClause: + Walk(x.Word, f) + for _, ci := range x.Items { + Walk(ci, f) + } + for _, c := range x.Last { + Walk(&c, f) + } + case *CaseItem: + for _, c := range x.Comments { + if c.Pos().After(x.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } + walkWords(x.Patterns, f) + walkStmts(x.Stmts, x.Last, f) + case *TestClause: + Walk(x.X, f) + case *DeclClause: + for _, a := range x.Args { + Walk(a, f) + } + case *ArrayExpr: + for _, el := range x.Elems { + Walk(el, f) + } + for _, c := range x.Last { + Walk(&c, f) + } + case *ArrayElem: + for _, c := range x.Comments { + if c.Pos().After(x.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } + if x.Index != nil { + Walk(x.Index, f) + } + if x.Value != nil { + Walk(x.Value, f) + } + case *ExtGlob: + Walk(x.Pattern, f) + case *ProcSubst: + walkStmts(x.Stmts, x.Last, f) + case *TimeClause: + if x.Stmt != nil { + Walk(x.Stmt, f) + } + case *CoprocClause: + if x.Name != nil { + Walk(x.Name, f) + } + Walk(x.Stmt, f) + case *LetClause: + for _, expr := range x.Exprs { + Walk(expr, f) + } + case *TestDecl: + Walk(x.Description, f) + Walk(x.Body, f) + default: + panic(fmt.Sprintf("syntax.Walk: unexpected node type %T", x)) + } + + f(nil) +} + +// DebugPrint prints the provided syntax tree, spanning multiple lines and with +// indentation. Can be useful to investigate the content of a syntax tree. +func DebugPrint(w io.Writer, node Node) error { + p := debugPrinter{out: w} + p.print(reflect.ValueOf(node)) + return p.err +} + +type debugPrinter struct { + out io.Writer + level int + err error +} + +func (p *debugPrinter) printf(format string, args ...any) { + _, err := fmt.Fprintf(p.out, format, args...) + if err != nil && p.err == nil { + p.err = err + } +} + +func (p *debugPrinter) newline() { + p.printf("\n") + for i := 0; i < p.level; i++ { + p.printf(". ") + } +} + +func (p *debugPrinter) print(x reflect.Value) { + switch x.Kind() { + case reflect.Interface: + if x.IsNil() { + p.printf("nil") + return + } + p.print(x.Elem()) + case reflect.Ptr: + if x.IsNil() { + p.printf("nil") + return + } + p.printf("*") + p.print(x.Elem()) + case reflect.Slice: + p.printf("%s (len = %d) {", x.Type(), x.Len()) + if x.Len() > 0 { + p.level++ + p.newline() + for i := 0; i < x.Len(); i++ { + p.printf("%d: ", i) + p.print(x.Index(i)) + if i == x.Len()-1 { + p.level-- + } + p.newline() + } + } + p.printf("}") + + case reflect.Struct: + if v, ok := x.Interface().(Pos); ok { + p.printf("%v:%v", v.Line(), v.Col()) + return + } + t := x.Type() + p.printf("%s {", t) + p.level++ + p.newline() + for i := 0; i < t.NumField(); i++ { + p.printf("%s: ", t.Field(i).Name) + p.print(x.Field(i)) + if i == x.NumField()-1 { + p.level-- + } + p.newline() + } + p.printf("}") + default: + p.printf("%#v", x.Interface()) + } +}