Update dependencies

This commit is contained in:
Ingo Oppermann 2024-11-29 11:23:59 +01:00
parent 106454be84
commit 4317220705
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
174 changed files with 6616 additions and 3681 deletions

44
go.mod
View File

@ -5,13 +5,13 @@ go 1.22.5
toolchain go1.23.2
require (
github.com/99designs/gqlgen v0.17.55
github.com/Masterminds/semver/v3 v3.3.0
github.com/99designs/gqlgen v0.17.57
github.com/Masterminds/semver/v3 v3.3.1
github.com/atrox/haikunatorgo/v2 v2.0.1
github.com/caddyserver/certmagic v0.21.4
github.com/datarhei/gosrt v0.7.0
github.com/datarhei/gosrt v0.8.0
github.com/datarhei/joy4 v0.0.0-20240603190808-b1407345907e
github.com/go-playground/validator/v10 v10.22.1
github.com/go-playground/validator/v10 v10.23.0
github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
@ -19,20 +19,20 @@ require (
github.com/joho/godotenv v1.5.1
github.com/labstack/echo-jwt v0.0.0-20221127215225-c84d41a71003
github.com/labstack/echo/v4 v4.12.0
github.com/lithammer/shortuuid/v4 v4.0.0
github.com/lithammer/shortuuid/v4 v4.1.0
github.com/mattn/go-isatty v0.0.20
github.com/minio/minio-go/v7 v7.0.80
github.com/minio/minio-go/v7 v7.0.81
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3
github.com/prometheus/client_golang v1.20.5
github.com/puzpuzpuz/xsync/v3 v3.4.0
github.com/shirou/gopsutil/v3 v3.24.5
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/swag v1.16.4
github.com/vektah/gqlparser/v2 v2.5.18
github.com/vektah/gqlparser/v2 v2.5.20
github.com/xeipuuv/gojsonschema v1.2.0
go.uber.org/zap v1.27.0
golang.org/x/mod v0.21.0
golang.org/x/mod v0.22.0
)
require (
@ -42,10 +42,10 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gabriel-vasile/mimetype v1.4.7 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@ -55,6 +55,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
@ -63,7 +64,7 @@ require (
github.com/iancoleman/orderedmap v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/libdns/libdns v0.2.2 // indirect
@ -73,7 +74,6 @@ require (
github.com/mholt/acmez/v2 v2.0.3 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
@ -87,7 +87,7 @@ require (
github.com/swaggo/files/v2 v2.0.1 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/urfave/cli/v2 v2.27.4 // indirect
github.com/urfave/cli/v2 v2.27.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
@ -96,14 +96,14 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.26.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.27.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

90
go.sum
View File

@ -1,9 +1,9 @@
github.com/99designs/gqlgen v0.17.55 h1:3vzrNWYyzSZjGDFo68e5j9sSauLxfKvLp+6ioRokVtM=
github.com/99designs/gqlgen v0.17.55/go.mod h1:3Bq768f8hgVPGZxL8aY9MaYmbxa6llPM/qu1IGH1EJo=
github.com/99designs/gqlgen v0.17.57 h1:Ak4p60BRq6QibxY0lEc0JnQhDurfhxA67sp02lMjmPc=
github.com/99designs/gqlgen v0.17.57/go.mod h1:Jx61hzOSTcR4VJy/HFIgXiQ5rJ0Ypw8DxWLjbYDAUw0=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0=
github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U=
github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY=
@ -26,10 +26,10 @@ github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+Y
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/datarhei/gosrt v0.7.0 h1:1/IY66HVVgqGA9zkmL5l6jUFuI8t/76WkuamSkJqHqs=
github.com/datarhei/gosrt v0.7.0/go.mod h1:wTDoyog1z4au8Fd/QJBQAndzvccuxjqUL/qMm0EyJxE=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/datarhei/gosrt v0.8.0 h1:fna/FFRbVN7LvwAt2cR6pxwFz7rm979vdRzGfh9zbNM=
github.com/datarhei/gosrt v0.8.0/go.mod h1:ab1q3G0/DxsEU5iH/OCMaqYOWAqUI0SAbJ2sRKeQblA=
github.com/datarhei/joy4 v0.0.0-20240603190808-b1407345907e h1:Qc/0D4xvXrazFkoi/4UGqO15yQ1JN5I8h7RwdzCLgTY=
github.com/datarhei/joy4 v0.0.0-20240603190808-b1407345907e/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -39,8 +39,8 @@ github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7c
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
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/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
@ -62,8 +62,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
@ -76,7 +78,6 @@ github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17w
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
@ -95,8 +96,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -113,8 +114,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
github.com/lithammer/shortuuid/v4 v4.1.0 h1:1L0IC3luO1fdb8YNhfTDfflTwyVER4GCuCa7ZTYgbbU=
github.com/lithammer/shortuuid/v4 v4.1.0/go.mod h1:dawOscqzZBSdcTpJllSPiyGfvTCeUvq6CY0Cxc9eqjU=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@ -130,10 +131,8 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk=
github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/minio/minio-go/v7 v7.0.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA=
github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -172,8 +171,8 @@ github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERA
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk=
github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc=
github.com/swaggo/files/v2 v2.0.1 h1:XCVJO/i/VosCDsJu1YLpdejGsGnBE9deRMpjN4pJLHk=
@ -184,14 +183,14 @@ github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZ
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser/v2 v2.5.18 h1:zSND3GtutylAQ1JpWnTHcqtaRZjl+y3NROeW8vuNo6Y=
github.com/vektah/gqlparser/v2 v2.5.18/go.mod h1:6HLzf7JKv9Fi3APymudztFQNmLXR5qJeEo6BOFcXVfc=
github.com/vektah/gqlparser/v2 v2.5.20 h1:kPaWbhBntxoZPaNdBaIPT1Kh0i1b/onb5kXgEdP5JCo=
github.com/vektah/gqlparser/v2 v2.5.20/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -215,30 +214,29 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@ -1,7 +1,7 @@
How to write tests for gqlgen
===
Testing generated code is a little tricky, heres how its currently set up.
Testing generated code is a little tricky, here's how its currently set up.
### Testing responses from a server

View File

@ -20,6 +20,12 @@ type ExecConfig struct {
// Only for follow-schema layout:
FilenameTemplate string `yaml:"filename_template,omitempty"` // String template with {name} as placeholder for base name.
DirName string `yaml:"dir"`
// Maximum number of goroutines in concurrency to use when running multiple child resolvers
// Suppressing the number of goroutines generated can reduce memory consumption per request,
// but processing time may increase due to the reduced number of concurrences
// Default: 0 (unlimited)
WorkerLimit uint `yaml:"worker_limit"`
}
type ExecLayout string

View File

@ -19,6 +19,7 @@ type ResolverConfig struct {
DirName string `yaml:"dir"`
OmitTemplateComment bool `yaml:"omit_template_comment,omitempty"`
ResolverTemplate string `yaml:"resolver_template,omitempty"`
PreserveResolver bool `yaml:"preserve_resolver,omitempty"`
}
type ResolverLayout string

View File

@ -2,7 +2,6 @@ package codegen
import (
"fmt"
"strconv"
"strings"
"github.com/vektah/gqlparser/v2/ast"
@ -143,7 +142,7 @@ func (d *Directive) CallArgs() string {
args := []string{"ctx", "obj", "n"}
for _, arg := range d.Args {
args = append(args, "args["+strconv.Quote(arg.Name)+"].("+templates.CurrentImports.LookupType(arg.TypeReference.GO)+")")
args = append(args, fmt.Sprintf("args[%q].(%s)", arg.Name, templates.CurrentImports.LookupType(arg.TypeReference.GO)))
}
return strings.Join(args, ", ")

View File

@ -4,9 +4,7 @@ import (
"embed"
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/vektah/gqlparser/v2/ast"
@ -129,24 +127,18 @@ func addBuild(filename string, p *ast.Position, data *Data, builds *map[string]*
}
}
//go:embed root_.gotpl
var rootTemplate string
// Root file contains top-level definitions that should not be duplicated across the generated
// files for each schema file.
func generateRootFile(data *Data) error {
dir := data.Config.Exec.DirName
path := filepath.Join(dir, "root_.generated.go")
_, thisFile, _, _ := runtime.Caller(0)
rootDir := filepath.Dir(thisFile)
templatePath := filepath.Join(rootDir, "root_.gotpl")
templateBytes, err := os.ReadFile(templatePath)
if err != nil {
return err
}
template := string(templateBytes)
return templates.Render(templates.Options{
PackageName: data.Config.Exec.Package,
Template: template,
Template: rootTemplate,
Filename: path,
Data: data,
RegionTags: false,

View File

@ -10,6 +10,7 @@
{{ reserveImport "bytes" }}
{{ reserveImport "embed" }}
{{ reserveImport "golang.org/x/sync/semaphore"}}
{{ reserveImport "github.com/vektah/gqlparser/v2" "gqlparser" }}
{{ reserveImport "github.com/vektah/gqlparser/v2/ast" }}
{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
@ -150,8 +151,8 @@
}
func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
rc := graphql.GetOperationContext(ctx)
ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)}
opCtx := graphql.GetOperationContext(ctx)
ec := executionContext{opCtx, e, 0, 0, make(chan graphql.DeferredResult)}
inputUnmarshalMap := graphql.BuildUnmarshalerMap(
{{- range $input := .Inputs -}}
{{ if not $input.HasUnmarshal }}
@ -161,7 +162,7 @@
)
first := true
switch rc.Operation.Operation {
switch opCtx.Operation.Operation {
{{- if .QueryRoot }} case ast.Query:
return func(ctx context.Context) *graphql.Response {
var response graphql.Response
@ -170,11 +171,11 @@
first = false
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
{{ if .Directives.LocationDirectives "QUERY" -}}
data = ec._queryMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
data = ec._queryMiddleware(ctx, opCtx.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.QueryRoot.Name}}(ctx, opCtx.Operation.SelectionSet), nil
})
{{- else -}}
data = ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet)
data = ec._{{.QueryRoot.Name}}(ctx, opCtx.Operation.SelectionSet)
{{- end }}
} else {
if atomic.LoadInt32(&ec.pendingDeferred) > 0 {
@ -206,11 +207,11 @@
first = false
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
{{ if .Directives.LocationDirectives "MUTATION" -}}
data := ec._mutationMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
data := ec._mutationMiddleware(ctx, opCtx.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.MutationRoot.Name}}(ctx, opCtx.Operation.SelectionSet), nil
})
{{- else -}}
data := ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet)
data := ec._{{.MutationRoot.Name}}(ctx, opCtx.Operation.SelectionSet)
{{- end }}
var buf bytes.Buffer
data.MarshalGQL(&buf)
@ -223,11 +224,11 @@
{{- if .SubscriptionRoot }} case ast.Subscription:
{{ if .Directives.LocationDirectives "SUBSCRIPTION" -}}
next := ec._subscriptionMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet),nil
next := ec._subscriptionMiddleware(ctx, opCtx.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.SubscriptionRoot.Name}}(ctx, opCtx.Operation.SelectionSet),nil
})
{{- else -}}
next := ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet)
next := ec._{{.SubscriptionRoot.Name}}(ctx, opCtx.Operation.SelectionSet)
{{- end }}
var buf bytes.Buffer

View File

@ -123,8 +123,8 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
}
func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
rc := graphql.GetOperationContext(ctx)
ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)}
opCtx := graphql.GetOperationContext(ctx)
ec := executionContext{opCtx, e, 0, 0, make(chan graphql.DeferredResult)}
inputUnmarshalMap := graphql.BuildUnmarshalerMap(
{{- range $input := .Inputs -}}
{{ if not $input.HasUnmarshal }}
@ -134,7 +134,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
)
first := true
switch rc.Operation.Operation {
switch opCtx.Operation.Operation {
{{- if .QueryRoot }} case ast.Query:
return func(ctx context.Context) *graphql.Response {
var response graphql.Response
@ -143,11 +143,11 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
first = false
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
{{ if .Directives.LocationDirectives "QUERY" -}}
data = ec._queryMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
data = ec._queryMiddleware(ctx, opCtx.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.QueryRoot.Name}}(ctx, opCtx.Operation.SelectionSet), nil
})
{{- else -}}
data = ec._{{.QueryRoot.Name}}(ctx, rc.Operation.SelectionSet)
data = ec._{{.QueryRoot.Name}}(ctx, opCtx.Operation.SelectionSet)
{{- end }}
} else {
if atomic.LoadInt32(&ec.pendingDeferred) > 0 {
@ -179,11 +179,11 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
first = false
ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap)
{{ if .Directives.LocationDirectives "MUTATION" -}}
data := ec._mutationMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet), nil
data := ec._mutationMiddleware(ctx, opCtx.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.MutationRoot.Name}}(ctx, opCtx.Operation.SelectionSet), nil
})
{{- else -}}
data := ec._{{.MutationRoot.Name}}(ctx, rc.Operation.SelectionSet)
data := ec._{{.MutationRoot.Name}}(ctx, opCtx.Operation.SelectionSet)
{{- end }}
var buf bytes.Buffer
data.MarshalGQL(&buf)
@ -196,11 +196,11 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
{{- if .SubscriptionRoot }} case ast.Subscription:
{{ if .Directives.LocationDirectives "SUBSCRIPTION" -}}
next := ec._subscriptionMiddleware(ctx, rc.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet),nil
next := ec._subscriptionMiddleware(ctx, opCtx.Operation, func(ctx context.Context) (interface{}, error){
return ec._{{.SubscriptionRoot.Name}}(ctx, opCtx.Operation.SelectionSet),nil
})
{{- else -}}
next := ec._{{.SubscriptionRoot.Name}}(ctx, rc.Operation.SelectionSet)
next := ec._{{.SubscriptionRoot.Name}}(ctx, opCtx.Operation.SelectionSet)
{{- end }}
var buf bytes.Buffer

View File

@ -101,6 +101,9 @@
ret := make(graphql.Array, len(v))
{{- if not $type.IsScalar }}
var wg sync.WaitGroup
{{- if gt $.Config.Exec.WorkerLimit 0 }}
sm := semaphore.NewWeighted({{ $.Config.Exec.WorkerLimit }})
{{- end }}
isLen1 := len(v) == 1
if !isLen1 {
wg.Add(len(v))
@ -124,14 +127,29 @@
}()
{{- end }}
if !isLen1 {
defer wg.Done()
{{- if gt $.Config.Exec.WorkerLimit 0 }}
defer func(){
sm.Release(1)
wg.Done()
}()
{{- else }}
defer wg.Done()
{{- end }}
}
ret[i] = ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
{{- if gt $.Config.Exec.WorkerLimit 0 }}
if err := sm.Acquire(ctx, 1); err != nil {
ec.Error(ctx, ctx.Err())
} else {
go f(i)
}
{{- else }}
go f(i)
{{- end }}
}
{{ else }}
ret[i] = ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, v[i])

View File

@ -23,9 +23,13 @@ func (m MapCache[T]) Get(_ context.Context, key string) (value T, ok bool) {
// Add adds a value to the cache.
func (m MapCache[T]) Add(_ context.Context, key string, value T) { m[key] = value }
type NoCache[T any, T2 *T] struct{}
type NoCache[T any] struct{}
var _ Cache[*string] = (*NoCache[string, *string])(nil)
var _ Cache[string] = (*NoCache[string])(nil)
func (n NoCache[T, T2]) Get(_ context.Context, _ string) (value T2, ok bool) { return nil, false }
func (n NoCache[T, T2]) Add(_ context.Context, _ string, _ T2) {}
func (n NoCache[T]) Get(_ context.Context, _ string) (value T, ok bool) {
var val T
return val, false
}
func (n NoCache[T]) Add(_ context.Context, _ string, _ T) {}

View File

@ -36,14 +36,14 @@ type FieldContext struct {
//
// srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (interface{}, error) {
// fc := graphql.GetFieldContext(ctx)
// op := graphql.GetOperationContext(ctx)
// opCtx := graphql.GetOperationContext(ctx)
// collected := graphql.CollectFields(opCtx, fc.Field.Selections, []string{"User"})
//
// child, err := fc.Child(ctx, collected[0])
// if err != nil {
// return nil, err
// }
// fmt.Println("child context %q with args: %v", child.Field.Name, child.Args)
// fmt.Printf("child context %q with args: %v\n", child.Field.Name, child.Args)
//
// return next(ctx)
// })

View File

@ -65,8 +65,8 @@ func GetOperationContext(ctx context.Context) *OperationContext {
panic("missing operation context")
}
func WithOperationContext(ctx context.Context, rc *OperationContext) context.Context {
return context.WithValue(ctx, operationCtx, rc)
func WithOperationContext(ctx context.Context, opCtx *OperationContext) context.Context {
return context.WithValue(ctx, operationCtx, opCtx)
}
// HasOperationContext checks if the given context is part of an ongoing operation
@ -77,7 +77,7 @@ func HasOperationContext(ctx context.Context) bool {
return ok && val != nil
}
// This is just a convenient wrapper method for CollectFields
// CollectFieldsCtx is just a convenient wrapper method for CollectFields.
func CollectFieldsCtx(ctx context.Context, satisfies []string) []CollectedField {
resctx := GetFieldContext(ctx)
return CollectFields(GetOperationContext(ctx), resctx.Field.Selections, satisfies)

View File

@ -17,8 +17,7 @@ type ExecutableSchema interface {
}
// CollectFields returns the set of fields from an ast.SelectionSet where all collected fields satisfy at least one of the GraphQL types
// passed through satisfies. Providing an empty or nil slice for satisfies will return collect all fields regardless of fragment
// type conditions.
// passed through satisfies. Providing an empty slice for satisfies will collect all fields regardless of fragment type conditions.
func CollectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies []string) []CollectedField {
return collectFields(reqCtx, selSet, satisfies, map[string]bool{})
}
@ -39,13 +38,22 @@ func collectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies
f.Selections = append(f.Selections, sel.SelectionSet...)
case *ast.InlineFragment:
if !shouldIncludeNode(sel.Directives, reqCtx.Variables) {
continue
}
if len(satisfies) > 0 && !instanceOf(sel.TypeCondition, satisfies) {
// To allow simplified "collect all" types behavior, pass an empty list
// of types that the type condition must satisfy: we will apply the
// fragment regardless of type condition.
//
// When the type condition is not set (... { field }) we will apply the
// fragment to any satisfying types.
//
// We will only NOT apply the fragment when we have at least one type in
// the list we must satisfy and a type condition to compare them to.
if len(satisfies) > 0 && sel.TypeCondition != "" && !instanceOf(sel.TypeCondition, satisfies) {
continue
}
if !shouldIncludeNode(sel.Directives, reqCtx.Variables) {
continue
}
shouldDefer, label := deferrable(sel.Directives, reqCtx.Variables)
for _, childField := range collectFields(reqCtx, sel.SelectionSet, satisfies, visited) {
@ -61,9 +69,6 @@ func collectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies
}
case *ast.FragmentSpread:
if !shouldIncludeNode(sel.Directives, reqCtx.Variables) {
continue
}
fragmentName := sel.Name
if _, seen := visited[fragmentName]; seen {
continue
@ -80,6 +85,9 @@ func collectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies
continue
}
if !shouldIncludeNode(sel.Directives, reqCtx.Variables) {
continue
}
shouldDefer, label := deferrable(sel.Directives, reqCtx.Variables)
for _, childField := range collectFields(reqCtx, fragment.SelectionSet, satisfies, visited) {

View File

@ -36,7 +36,7 @@ func New(es graphql.ExecutableSchema) *Executor {
es: es,
errorPresenter: graphql.DefaultErrorPresenter,
recoverFunc: graphql.DefaultRecover,
queryCache: graphql.NoCache[ast.QueryDocument, *ast.QueryDocument]{},
queryCache: graphql.NoCache[*ast.QueryDocument]{},
ext: processExtensions(nil),
parserTokenLimit: parserTokenNoLimit,
}
@ -47,7 +47,7 @@ func (e *Executor) CreateOperationContext(
ctx context.Context,
params *graphql.RawParams,
) (*graphql.OperationContext, gqlerror.List) {
rc := &graphql.OperationContext{
opCtx := &graphql.OperationContext{
DisableIntrospection: true,
RecoverFunc: e.recoverFunc,
ResolverMiddleware: e.ext.fieldMiddleware,
@ -57,56 +57,56 @@ func (e *Executor) CreateOperationContext(
OperationStart: graphql.GetStartTime(ctx),
},
}
ctx = graphql.WithOperationContext(ctx, rc)
ctx = graphql.WithOperationContext(ctx, opCtx)
for _, p := range e.ext.operationParameterMutators {
if err := p.MutateOperationParameters(ctx, params); err != nil {
return rc, gqlerror.List{err}
return opCtx, gqlerror.List{err}
}
}
rc.RawQuery = params.Query
rc.OperationName = params.OperationName
rc.Headers = params.Headers
opCtx.RawQuery = params.Query
opCtx.OperationName = params.OperationName
opCtx.Headers = params.Headers
var listErr gqlerror.List
rc.Doc, listErr = e.parseQuery(ctx, &rc.Stats, params.Query)
opCtx.Doc, listErr = e.parseQuery(ctx, &opCtx.Stats, params.Query)
if len(listErr) != 0 {
return rc, listErr
return opCtx, listErr
}
rc.Operation = rc.Doc.Operations.ForName(params.OperationName)
if rc.Operation == nil {
opCtx.Operation = opCtx.Doc.Operations.ForName(params.OperationName)
if opCtx.Operation == nil {
err := gqlerror.Errorf("operation %s not found", params.OperationName)
errcode.Set(err, errcode.ValidationFailed)
return rc, gqlerror.List{err}
return opCtx, gqlerror.List{err}
}
var err error
rc.Variables, err = validator.VariableValues(e.es.Schema(), rc.Operation, params.Variables)
opCtx.Variables, err = validator.VariableValues(e.es.Schema(), opCtx.Operation, params.Variables)
if err != nil {
gqlErr, ok := err.(*gqlerror.Error)
if ok {
errcode.Set(gqlErr, errcode.ValidationFailed)
return rc, gqlerror.List{gqlErr}
return opCtx, gqlerror.List{gqlErr}
}
}
rc.Stats.Validation.End = graphql.Now()
opCtx.Stats.Validation.End = graphql.Now()
for _, p := range e.ext.operationContextMutators {
if err := p.MutateOperationContext(ctx, rc); err != nil {
return rc, gqlerror.List{err}
if err := p.MutateOperationContext(ctx, opCtx); err != nil {
return opCtx, gqlerror.List{err}
}
}
return rc, nil
return opCtx, nil
}
func (e *Executor) DispatchOperation(
ctx context.Context,
rc *graphql.OperationContext,
opCtx *graphql.OperationContext,
) (graphql.ResponseHandler, context.Context) {
ctx = graphql.WithOperationContext(ctx, rc)
ctx = graphql.WithOperationContext(ctx, opCtx)
var innerCtx context.Context
res := e.ext.operationMiddleware(ctx, func(ctx context.Context) graphql.ResponseHandler {

View File

@ -34,7 +34,7 @@ type (
GraphExecutor interface {
CreateOperationContext(ctx context.Context, params *RawParams) (*OperationContext, gqlerror.List)
DispatchOperation(ctx context.Context, rc *OperationContext) (ResponseHandler, context.Context)
DispatchOperation(ctx context.Context, opCtx *OperationContext) (ResponseHandler, context.Context)
DispatchError(ctx context.Context, list gqlerror.List) *Response
}
@ -65,7 +65,7 @@ type (
// OperationContextMutator is called after creating the request context, but before executing the root resolver.
OperationContextMutator interface {
MutateOperationContext(ctx context.Context, rc *OperationContext) *gqlerror.Error
MutateOperationContext(ctx context.Context, opCtx *OperationContext) *gqlerror.Error
}
// OperationInterceptor is called for each incoming query, for basic requests the writer will be invoked once,

View File

@ -6,7 +6,7 @@ import (
"encoding/hex"
"errors"
"github.com/mitchellh/mapstructure"
"github.com/go-viper/mapstructure/v2"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
@ -98,12 +98,12 @@ func (a AutomaticPersistedQuery) MutateOperationParameters(ctx context.Context,
}
func GetApqStats(ctx context.Context) *ApqStats {
rc := graphql.GetOperationContext(ctx)
if rc == nil {
opCtx := graphql.GetOperationContext(ctx)
if opCtx == nil {
return nil
}
s, _ := rc.Stats.GetExtension(apqExtension).(*ApqStats)
s, _ := opCtx.Stats.GetExtension(apqExtension).(*ApqStats)
return s
}

View File

@ -17,7 +17,7 @@ const errComplexityLimit = "COMPLEXITY_LIMIT_EXCEEDED"
//
// If a query is submitted that exceeds the limit, a 422 status code will be returned.
type ComplexityLimit struct {
Func func(ctx context.Context, rc *graphql.OperationContext) int
Func func(ctx context.Context, opCtx *graphql.OperationContext) int
es graphql.ExecutableSchema
}
@ -40,7 +40,7 @@ type ComplexityStats struct {
// FixedComplexityLimit sets a complexity limit that does not change
func FixedComplexityLimit(limit int) *ComplexityLimit {
return &ComplexityLimit{
Func: func(ctx context.Context, rc *graphql.OperationContext) int {
Func: func(ctx context.Context, opCtx *graphql.OperationContext) int {
return limit
},
}
@ -58,13 +58,13 @@ func (c *ComplexityLimit) Validate(schema graphql.ExecutableSchema) error {
return nil
}
func (c ComplexityLimit) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error {
op := rc.Doc.Operations.ForName(rc.OperationName)
complexityCalcs := complexity.Calculate(c.es, op, rc.Variables)
func (c ComplexityLimit) MutateOperationContext(ctx context.Context, opCtx *graphql.OperationContext) *gqlerror.Error {
op := opCtx.Doc.Operations.ForName(opCtx.OperationName)
complexityCalcs := complexity.Calculate(c.es, op, opCtx.Variables)
limit := c.Func(ctx, rc)
limit := c.Func(ctx, opCtx)
rc.Stats.SetExtension(complexityExtension, &ComplexityStats{
opCtx.Stats.SetExtension(complexityExtension, &ComplexityStats{
Complexity: complexityCalcs,
ComplexityLimit: limit,
})
@ -79,11 +79,11 @@ func (c ComplexityLimit) MutateOperationContext(ctx context.Context, rc *graphql
}
func GetComplexityStats(ctx context.Context) *ComplexityStats {
rc := graphql.GetOperationContext(ctx)
if rc == nil {
opCtx := graphql.GetOperationContext(ctx)
if opCtx == nil {
return nil
}
s, _ := rc.Stats.GetExtension(complexityExtension).(*ComplexityStats)
s, _ := opCtx.Stats.GetExtension(complexityExtension).(*ComplexityStats)
return s
}

View File

@ -24,7 +24,7 @@ func (c Introspection) Validate(schema graphql.ExecutableSchema) error {
return nil
}
func (c Introspection) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error {
rc.DisableIntrospection = false
func (c Introspection) MutateOperationContext(ctx context.Context, opCtx *graphql.OperationContext) *gqlerror.Error {
opCtx.DisableIntrospection = false
return nil
}

View File

@ -66,21 +66,21 @@ func (h GET) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecut
raw.ReadTime.End = graphql.Now()
rc, gqlError := exec.CreateOperationContext(r.Context(), raw)
opCtx, gqlError := exec.CreateOperationContext(r.Context(), raw)
if gqlError != nil {
w.WriteHeader(statusFor(gqlError))
resp := exec.DispatchError(graphql.WithOperationContext(r.Context(), rc), gqlError)
resp := exec.DispatchError(graphql.WithOperationContext(r.Context(), opCtx), gqlError)
writeJson(w, resp)
return
}
op := rc.Doc.Operations.ForName(rc.OperationName)
op := opCtx.Doc.Operations.ForName(opCtx.OperationName)
if op.Operation != ast.Query {
w.WriteHeader(http.StatusNotAcceptable)
writeJsonError(w, "GET requests only allow query operations")
return
}
responses, ctx := exec.DispatchOperation(r.Context(), rc)
responses, ctx := exec.DispatchOperation(r.Context(), opCtx)
writeJson(w, responses(ctx))
}

View File

@ -0,0 +1,286 @@
package transport
import (
"encoding/json"
"fmt"
"io"
"log"
"mime"
"net/http"
"strings"
"sync"
"time"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
)
// MultipartMixed is a transport that supports the multipart/mixed spec
type MultipartMixed struct {
Boundary string
DeliveryTimeout time.Duration
}
var _ graphql.Transport = MultipartMixed{}
// Supports checks if the request supports the multipart/mixed spec
// Might be worth check the spec required, but Apollo Client mislabel the spec in the headers.
func (t MultipartMixed) Supports(r *http.Request) bool {
if !strings.Contains(r.Header.Get("Accept"), "multipart/mixed") {
return false
}
mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return false
}
return r.Method == http.MethodPost && mediaType == "application/json"
}
// Do implements the multipart/mixed spec as a multipart/mixed response
func (t MultipartMixed) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) {
// Implements the multipart/mixed spec as a multipart/mixed response:
// * https://github.com/graphql/graphql-wg/blob/e4ef5f9d5997815d9de6681655c152b6b7838b4c/rfcs/DeferStream.md
// 2022/08/23 as implemented by gqlgen.
// * https://github.com/graphql/graphql-wg/blob/f22ea7748c6ebdf88fdbf770a8d9e41984ebd429/rfcs/DeferStream.md June 2023 Spec for the
// `incremental` field
// * https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md
// multipart specification
// Follows the format that is used in the Apollo Client tests:
// https://github.com/apollographql/apollo-client/blob/v3.11.8/src/link/http/__tests__/responseIterator.ts#L68
// Apollo Client, despite mentioning in its requests that they require the 2022 spec, it wants the
// `incremental` field to be an array of responses, not a single response. Theoretically we could
// batch responses in the `incremental` field, if we wanted to optimize this code.
ctx := r.Context()
flusher, ok := w.(http.Flusher)
if !ok {
SendErrorf(w, http.StatusInternalServerError, "streaming unsupported")
return
}
defer flusher.Flush()
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
// This header will be replaced below, but it's required in case we return errors.
w.Header().Set("Content-Type", "application/json")
boundary := t.Boundary
if boundary == "" {
boundary = "-"
}
timeout := t.DeliveryTimeout
if timeout.Milliseconds() < 1 {
// If the timeout is less than 1ms, we'll set it to 1ms to avoid a busy loop
timeout = 1 * time.Millisecond
}
params := &graphql.RawParams{}
start := graphql.Now()
params.Headers = r.Header
params.ReadTime = graphql.TraceTiming{
Start: start,
End: graphql.Now(),
}
bodyString, err := getRequestBody(r)
if err != nil {
gqlErr := gqlerror.Errorf("could not get json request body: %+v", err)
resp := exec.DispatchError(ctx, gqlerror.List{gqlErr})
log.Printf("could not get json request body: %+v", err.Error())
writeJson(w, resp)
return
}
bodyReader := io.NopCloser(strings.NewReader(bodyString))
if err = jsonDecode(bodyReader, &params); err != nil {
w.WriteHeader(http.StatusBadRequest)
gqlErr := gqlerror.Errorf(
"json request body could not be decoded: %+v body:%s",
err,
bodyString,
)
resp := exec.DispatchError(ctx, gqlerror.List{gqlErr})
log.Printf("decoding error: %+v body:%s", err.Error(), bodyString)
writeJson(w, resp)
return
}
rc, opErr := exec.CreateOperationContext(ctx, params)
ctx = graphql.WithOperationContext(ctx, rc)
if opErr != nil {
w.WriteHeader(statusFor(opErr))
resp := exec.DispatchError(ctx, opErr)
writeJson(w, resp)
return
}
// Example of the response format (note the new lines and boundaries are important!):
// https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md
// --graphql
// Content-Type: application/json
//
// {"data":{"apps":{"apps":[ .. ],"totalNumApps":161,"__typename":"AppsOutput"}},"hasNext":true}
// --graphql
// Content-Type: application/json
//
// {"incremental":[{"data":{"groupAccessCount":0},"label":"test","path":["apps","apps",7],"hasNext":true}],"hasNext":true}
// --graphql
// ...
// --graphql--
// Last boundary is a closing boundary with two dashes at the end.
w.Header().Set(
"Content-Type",
fmt.Sprintf(`multipart/mixed;boundary="%s";deferSpec=20220824`, boundary),
)
a := newMultipartResponseAggregator(w, boundary, timeout)
defer a.Done(w)
responses, ctx := exec.DispatchOperation(ctx, rc)
initialResponse := true
for {
response := responses(ctx)
if response == nil {
break
}
a.Add(response, initialResponse)
initialResponse = false
}
}
func writeIncrementalJson(w io.Writer, responses []*graphql.Response, hasNext bool) {
// TODO: Remove this wrapper on response once gqlgen supports the 2023 spec
b, err := json.Marshal(struct {
Incremental []*graphql.Response `json:"incremental"`
HasNext bool `json:"hasNext"`
}{
Incremental: responses,
HasNext: hasNext,
})
if err != nil {
panic(err)
}
w.Write(b)
}
func writeBoundary(w io.Writer, boundary string, finalResponse bool) {
if finalResponse {
fmt.Fprintf(w, "--%s--\r\n", boundary)
return
}
fmt.Fprintf(w, "--%s\r\n", boundary)
}
func writeContentTypeHeader(w io.Writer) {
fmt.Fprintf(w, "Content-Type: application/json\r\n\r\n")
}
// multipartResponseAggregator helps us reduce the number of responses sent to the frontend by batching all the
// incremental responses together.
type multipartResponseAggregator struct {
mu sync.Mutex
boundary string
initialResponse *graphql.Response
deferResponses []*graphql.Response
done chan bool
}
// newMultipartResponseAggregator creates a new multipartResponseAggregator
// The aggregator will flush responses to the client every `tickerDuration` (default 1ms) so that
// multiple incremental responses are batched together.
func newMultipartResponseAggregator(
w http.ResponseWriter,
boundary string,
tickerDuration time.Duration,
) *multipartResponseAggregator {
a := &multipartResponseAggregator{
boundary: boundary,
done: make(chan bool, 1),
}
go func() {
ticker := time.NewTicker(tickerDuration)
defer ticker.Stop()
for {
select {
case <-a.done:
return
case <-ticker.C:
a.flush(w)
}
}
}()
return a
}
// Done flushes the remaining responses
func (a *multipartResponseAggregator) Done(w http.ResponseWriter) {
a.done <- true
a.flush(w)
}
// Add accumulates the responses
func (a *multipartResponseAggregator) Add(resp *graphql.Response, initialResponse bool) {
a.mu.Lock()
defer a.mu.Unlock()
if initialResponse {
a.initialResponse = resp
return
}
a.deferResponses = append(a.deferResponses, resp)
}
// flush sends the accumulated responses to the client
func (a *multipartResponseAggregator) flush(w http.ResponseWriter) {
a.mu.Lock()
defer a.mu.Unlock()
// If we don't have any responses, we can return early
if a.initialResponse == nil && len(a.deferResponses) == 0 {
return
}
flusher, ok := w.(http.Flusher)
if !ok {
// This should never happen, as we check for this much earlier on
panic("response writer does not support flushing")
}
hasNext := false
if a.initialResponse != nil {
// Initial response will need to begin with the boundary
writeBoundary(w, a.boundary, false)
writeContentTypeHeader(w)
writeJson(w, a.initialResponse)
hasNext = a.initialResponse.HasNext != nil && *a.initialResponse.HasNext
// Handle when initial is aggregated with deferred responses.
if len(a.deferResponses) > 0 {
fmt.Fprintf(w, "\r\n")
writeBoundary(w, a.boundary, false)
}
// Reset the initial response so we don't send it again
a.initialResponse = nil
}
if len(a.deferResponses) > 0 {
writeContentTypeHeader(w)
hasNext = a.deferResponses[len(a.deferResponses)-1].HasNext != nil &&
*a.deferResponses[len(a.deferResponses)-1].HasNext
writeIncrementalJson(w, a.deferResponses, hasNext)
// Reset the deferResponses so we don't send them again
a.deferResponses = nil
}
// Make sure to put the delimiter after every request, so that Apollo Client knows that the
// current payload has been sent, and updates the UI. This is particular important for the first
// response and the last response, which may either hang or never get handled.
// Final response will have a closing boundary with two dashes at the end.
fmt.Fprintf(w, "\r\n")
writeBoundary(w, a.boundary, !hasNext)
flusher.Flush()
}

View File

@ -1,11 +1,12 @@
package transport
import (
"bytes"
"fmt"
"io"
"mime"
"net/http"
"strings"
"sync"
"github.com/vektah/gqlparser/v2/gqlerror"
@ -46,32 +47,49 @@ func getRequestBody(r *http.Request) (string, error) {
return string(body), nil
}
var pool = sync.Pool{
New: func() any {
return &graphql.RawParams{}
},
}
func (h POST) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) {
ctx := r.Context()
writeHeaders(w, h.ResponseHeaders)
params := &graphql.RawParams{}
start := graphql.Now()
params := pool.Get().(*graphql.RawParams)
defer func() {
params.Headers = nil
params.ReadTime = graphql.TraceTiming{}
params.Extensions = nil
params.OperationName = ""
params.Query = ""
params.Variables = nil
pool.Put(params)
}()
params.Headers = r.Header
start := graphql.Now()
params.ReadTime = graphql.TraceTiming{
Start: start,
End: graphql.Now(),
}
bodyString, err := getRequestBody(r)
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
gqlErr := gqlerror.Errorf("could not get json request body: %+v", err)
gqlErr := gqlerror.Errorf("could not read request body: %+v", err)
resp := exec.DispatchError(ctx, gqlerror.List{gqlErr})
writeJson(w, resp)
return
}
bodyReader := io.NopCloser(strings.NewReader(bodyString))
if err = jsonDecode(bodyReader, &params); err != nil {
bodyReader := bytes.NewReader(bodyBytes)
if err := jsonDecode(bodyReader, &params); err != nil {
w.WriteHeader(http.StatusBadRequest)
gqlErr := gqlerror.Errorf(
"json request body could not be decoded: %+v body:%s",
err,
bodyString,
string(bodyBytes),
)
resp := exec.DispatchError(ctx, gqlerror.List{gqlErr})
writeJson(w, resp)

View File

@ -13,7 +13,7 @@ import (
func writeJson(w io.Writer, response *graphql.Response) {
b, err := json.Marshal(response)
if err != nil {
panic(err)
panic(fmt.Errorf("unable to marshal %s: %w", string(response.Data), err))
}
w.Write(b)
}

View File

@ -54,6 +54,7 @@ type (
receivedPong bool
exec graphql.GraphExecutor
closed bool
headers http.Header
initPayload InitPayload
}
@ -119,6 +120,7 @@ func (t Websocket) Do(w http.ResponseWriter, r *http.Request, exec graphql.Graph
ctx: r.Context(),
exec: exec,
me: me,
headers: r.Header,
Websocket: t,
}
@ -387,6 +389,8 @@ func (c *wsConnection) subscribe(start time.Time, msg *message) {
End: graphql.Now(),
}
params.Headers = c.headers
rc, err := c.exec.CreateOperationContext(ctx, params)
if err != nil {
resp := c.exec.DispatchError(graphql.WithOperationContext(ctx, rc), err)

View File

@ -92,7 +92,10 @@ func Handler(title, endpoint string) http.HandlerFunc {
// HandlerWithHeaders sets up the playground.
// fetcherHeaders are used by the playground's fetcher instance and will not be visible in the UI.
// uiHeaders are default headers that will show up in the UI headers editor.
func HandlerWithHeaders(title, endpoint string, fetcherHeaders, uiHeaders map[string]string) http.HandlerFunc {
func HandlerWithHeaders(
title, endpoint string,
fetcherHeaders, uiHeaders map[string]string,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html; charset=UTF-8")
err := page.Execute(w, map[string]any{
@ -102,9 +105,9 @@ func HandlerWithHeaders(title, endpoint string, fetcherHeaders, uiHeaders map[st
"uiHeaders": uiHeaders,
"endpointIsAbsolute": endpointHasScheme(endpoint),
"subscriptionEndpoint": getSubscriptionEndpoint(endpoint),
"version": "3.0.6",
"cssSRI": "sha256-wTzfn13a+pLMB5rMeysPPR1hO7x0SwSeQI+cnw7VdbE=",
"jsSRI": "sha256-eNxH+Ah7Z9up9aJYTQycgyNuy953zYZwE9Rqf5rH+r4=",
"version": "3.7.0",
"cssSRI": "sha256-Dbkv2LUWis+0H4Z+IzxLBxM2ka1J133lSjqqtSu49o8=",
"jsSRI": "sha256-qsScAZytFdTAEOM8REpljROHu8DvdvxXBK7xhoq5XD0=",
"reactSRI": "sha256-S0lp+k7zWUMk2ixteM6HZvu8L9Eh//OVrt+ZfbCpmgY=",
"reactDOMSRI": "sha256-IXWO0ITNDjfnNXIu5POVfqlgYoop36bDzhodR6LW5Pc=",
})

View File

@ -1,3 +1,3 @@
package graphql
const Version = "v0.17.55"
const Version = "v0.17.57"

View File

@ -6,6 +6,8 @@ schema:
exec:
filename: graph/generated.go
package: graph
# Optional: Maximum number of goroutines in concurrency to use per child resolvers(default: unlimited)
# worker_limit: 1000
# Uncomment to enable federation
# federation:

View File

@ -231,8 +231,7 @@ func (p *Packages) ModTidy() error {
// Errors returns any errors that were returned by Load, either from the call itself or any of the loaded packages.
func (p *Packages) Errors() PkgErrors {
var res []error //nolint:prealloc
res = append(res, p.loadErrors...)
res := append([]error{}, p.loadErrors...)
for _, pkg := range p.packages {
for _, err := range pkg.Errors {
res = append(res, err)

View File

@ -1,8 +1,9 @@
package federation
import (
"github.com/99designs/gqlgen/codegen/config"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config"
)
// The name of the field argument that is injected into the resolver to support @requires.

View File

@ -279,10 +279,13 @@ func (f *Federation) GenerateCode(data *codegen.Data) error {
typeString := strings.Split(obj.Type.String(), ".")
requiresImports[strings.Join(typeString[:len(typeString)-1], ".")] = true
if containsUnionField(reqField) {
continue
}
cgField := reqField.Field.TypeReference(obj, data.Objects)
reqField.Type = cgField.TypeReference
}
// add type info to entity
e.Type = obj.Type
}
@ -329,6 +332,15 @@ func (f *Federation) GenerateCode(data *codegen.Data) error {
})
}
func containsUnionField(reqField *Requires) bool {
for _, requireFields := range reqField.Field {
if strings.HasPrefix(requireFields, "... on") {
return true
}
}
return false
}
// Fill in types for key fields
func populateKeyFieldTypes(
resolver *EntityResolver,

View File

@ -133,8 +133,22 @@ func (f Field) LastIndex() int {
// parseUnnestedKeyFieldSet // handles simple case where none of the fields are nested.
func parseUnnestedKeyFieldSet(raw string, prefix []string) Set {
ret := Set{}
unionField := false
for _, s := range strings.Fields(raw) {
if s == "..." {
continue
}
if s == "on" {
unionField = true
continue
}
if unionField {
s = "... on " + s
unionField = false
}
next := append(prefix[0:len(prefix):len(prefix)], s) //nolint:gocritic // set cap=len in order to force slice reallocation
ret = append(ret, next)
}

View File

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"go/ast"
"io/fs"
"os"
"path/filepath"
"strings"
@ -45,6 +44,7 @@ func (m *Plugin) GenerateCode(data *codegen.Data) error {
case config.LayoutSingleFile:
return m.generateSingleFile(data)
case config.LayoutFollowSchema:
return m.generatePerSchema(data)
}
@ -53,6 +53,14 @@ func (m *Plugin) GenerateCode(data *codegen.Data) error {
func (m *Plugin) generateSingleFile(data *codegen.Data) error {
file := File{}
if fileExists(data.Config.Resolver.Filename) &&
data.Config.Resolver.PreserveResolver {
// file already exists and config says not to update resolver
// so just return
return nil
}
rewriter, err := rewrite.New(data.Config.Resolver.Dir())
if err != nil {
return err
@ -85,7 +93,7 @@ func (m *Plugin) generateSingleFile(data *codegen.Data) error {
}
}
if _, err := os.Stat(data.Config.Resolver.Filename); err == nil {
if fileExists(data.Config.Resolver.Filename) {
file.name = data.Config.Resolver.Filename
file.imports = rewriter.ExistingImports(file.name)
file.RemainingSource = rewriter.RemainingSource(file.name)
@ -104,9 +112,14 @@ func (m *Plugin) generateSingleFile(data *codegen.Data) error {
newResolverTemplate = readResolverTemplate(data.Config.Resolver.ResolverTemplate)
}
fileNotice := `// THIS CODE WILL BE UPDATED WITH SCHEMA CHANGES. PREVIOUS IMPLEMENTATION FOR SCHEMA CHANGES WILL BE KEPT IN THE COMMENT SECTION. IMPLEMENTATION FOR UNCHANGED SCHEMA WILL BE KEPT.`
if data.Config.Resolver.PreserveResolver {
fileNotice = `// THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.`
}
return templates.Render(templates.Options{
PackageName: data.Config.Resolver.Package,
FileNotice: `// THIS CODE WILL BE UPDATED WITH SCHEMA CHANGES. PREVIOUS IMPLEMENTATION FOR SCHEMA CHANGES WILL BE KEPT IN THE COMMENT SECTION. IMPLEMENTATION FOR UNCHANGED SCHEMA WILL BE KEPT.`,
FileNotice: fileNotice,
Filename: data.Config.Resolver.Filename,
Data: resolverBuild,
Packages: data.Config.Packages,
@ -183,6 +196,11 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
}
for _, file := range files {
if fileExists(file.name) &&
data.Config.Resolver.PreserveResolver {
// file already exists and config says not to update resolver
continue
}
resolverBuild := &ResolverBuild{
File: file,
PackageName: data.Config.Resolver.Package,
@ -216,7 +234,7 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
}
}
if _, err := os.Stat(data.Config.Resolver.Filename); errors.Is(err, fs.ErrNotExist) {
if !fileExists(data.Config.Resolver.Filename) {
err := templates.Render(templates.Options{
PackageName: data.Config.Resolver.Package,
FileNotice: `
@ -304,3 +322,10 @@ func readResolverTemplate(customResolverTemplate string) string {
}
return string(contentBytes)
}
func fileExists(fileName string) bool {
if _, err := os.Stat(fileName); err == nil {
return true
}
return false
}

View File

@ -39,9 +39,11 @@ var (
)
// semVerRegex is the regular expression used to parse a semantic version.
const semVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
// This is not the official regex from the semver spec. It has been modified to allow for loose handling
// where versions like 2.1 are detected.
const semVerRegex string = `v?(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?` +
`(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?` +
`(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?`
// Version represents a single semantic version.
type Version struct {
@ -146,8 +148,8 @@ func NewVersion(v string) (*Version, error) {
}
sv := &Version{
metadata: m[8],
pre: m[5],
metadata: m[5],
pre: m[4],
original: v,
}
@ -158,7 +160,7 @@ func NewVersion(v string) (*Version, error) {
}
if m[2] != "" {
sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64)
sv.minor, err = strconv.ParseUint(m[2], 10, 64)
if err != nil {
return nil, fmt.Errorf("Error parsing version segment: %s", err)
}
@ -167,7 +169,7 @@ func NewVersion(v string) (*Version, error) {
}
if m[3] != "" {
sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64)
sv.patch, err = strconv.ParseUint(m[3], 10, 64)
if err != nil {
return nil, fmt.Errorf("Error parsing version segment: %s", err)
}
@ -612,7 +614,9 @@ func containsOnly(s string, comp string) bool {
func validatePrerelease(p string) error {
eparts := strings.Split(p, ".")
for _, p := range eparts {
if containsOnly(p, num) {
if p == "" {
return ErrInvalidMetadata
} else if containsOnly(p, num) {
if len(p) > 1 && p[0] == '0' {
return ErrSegmentStartsZero
}
@ -631,7 +635,9 @@ func validatePrerelease(p string) error {
func validateMetadata(m string) error {
eparts := strings.Split(m, ".")
for _, p := range eparts {
if !containsOnly(p, allowed) {
if p == "" {
return ErrInvalidMetadata
} else if !containsOnly(p, allowed) {
return ErrInvalidMetadata
}
}

View File

@ -0,0 +1,62 @@
package md2man
import (
"fmt"
"io"
"os"
"strings"
"github.com/russross/blackfriday/v2"
)
func fmtListFlags(flags blackfriday.ListType) string {
knownFlags := []struct {
name string
flag blackfriday.ListType
}{
{"ListTypeOrdered", blackfriday.ListTypeOrdered},
{"ListTypeDefinition", blackfriday.ListTypeDefinition},
{"ListTypeTerm", blackfriday.ListTypeTerm},
{"ListItemContainsBlock", blackfriday.ListItemContainsBlock},
{"ListItemBeginningOfList", blackfriday.ListItemBeginningOfList},
{"ListItemEndOfList", blackfriday.ListItemEndOfList},
}
var f []string
for _, kf := range knownFlags {
if flags&kf.flag != 0 {
f = append(f, kf.name)
flags &^= kf.flag
}
}
if flags != 0 {
f = append(f, fmt.Sprintf("Unknown(%#x)", flags))
}
return strings.Join(f, "|")
}
type debugDecorator struct {
blackfriday.Renderer
}
func depth(node *blackfriday.Node) int {
d := 0
for n := node.Parent; n != nil; n = n.Parent {
d++
}
return d
}
func (d *debugDecorator) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
fmt.Fprintf(os.Stderr, "%s%s %v %v\n",
strings.Repeat(" ", depth(node)),
map[bool]string{true: "+", false: "-"}[entering],
node,
fmtListFlags(node.ListFlags))
var b strings.Builder
status := d.Renderer.RenderNode(io.MultiWriter(&b, w), node, entering)
if b.Len() > 0 {
fmt.Fprintf(os.Stderr, ">> %q\n", b.String())
}
return status
}

View File

@ -1,16 +1,23 @@
package md2man
import (
"os"
"strconv"
"github.com/russross/blackfriday/v2"
)
// Render converts a markdown document into a roff formatted document.
func Render(doc []byte) []byte {
renderer := NewRoffRenderer()
var r blackfriday.Renderer = renderer
if v, _ := strconv.ParseBool(os.Getenv("MD2MAN_DEBUG")); v {
r = &debugDecorator{Renderer: r}
}
return blackfriday.Run(doc,
[]blackfriday.Option{
blackfriday.WithRenderer(renderer),
blackfriday.WithRenderer(r),
blackfriday.WithExtensions(renderer.GetExtensions()),
}...)
}

View File

@ -14,10 +14,8 @@ import (
// roffRenderer implements the blackfriday.Renderer interface for creating
// roff format (manpages) from markdown text
type roffRenderer struct {
extensions blackfriday.Extensions
listCounters []int
firstHeader bool
firstDD bool
listDepth int
}
@ -43,7 +41,7 @@ const (
quoteTag = "\n.PP\n.RS\n"
quoteCloseTag = "\n.RE\n"
listTag = "\n.RS\n"
listCloseTag = "\n.RE\n"
listCloseTag = ".RE\n"
dtTag = "\n.TP\n"
dd2Tag = "\n"
tableStart = "\n.TS\nallbox;\n"
@ -56,23 +54,18 @@ const (
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
// from markdown
func NewRoffRenderer() *roffRenderer { // nolint: golint
var extensions blackfriday.Extensions
extensions |= blackfriday.NoIntraEmphasis
extensions |= blackfriday.Tables
extensions |= blackfriday.FencedCode
extensions |= blackfriday.SpaceHeadings
extensions |= blackfriday.Footnotes
extensions |= blackfriday.Titleblock
extensions |= blackfriday.DefinitionLists
return &roffRenderer{
extensions: extensions,
}
return &roffRenderer{}
}
// GetExtensions returns the list of extensions used by this renderer implementation
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
return r.extensions
func (*roffRenderer) GetExtensions() blackfriday.Extensions {
return blackfriday.NoIntraEmphasis |
blackfriday.Tables |
blackfriday.FencedCode |
blackfriday.SpaceHeadings |
blackfriday.Footnotes |
blackfriday.Titleblock |
blackfriday.DefinitionLists
}
// RenderHeader handles outputting the header at document start
@ -103,7 +96,23 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering
switch node.Type {
case blackfriday.Text:
escapeSpecialChars(w, node.Literal)
// Special case: format the NAME section as required for proper whatis parsing.
// Refer to the lexgrog(1) and groff_man(7) manual pages for details.
if node.Parent != nil &&
node.Parent.Type == blackfriday.Paragraph &&
node.Parent.Prev != nil &&
node.Parent.Prev.Type == blackfriday.Heading &&
node.Parent.Prev.FirstChild != nil &&
bytes.EqualFold(node.Parent.Prev.FirstChild.Literal, []byte("NAME")) {
before, after, found := bytes.Cut(node.Literal, []byte(" - "))
escapeSpecialChars(w, before)
if found {
out(w, ` \- `)
escapeSpecialChars(w, after)
}
} else {
escapeSpecialChars(w, node.Literal)
}
case blackfriday.Softbreak:
out(w, crTag)
case blackfriday.Hardbreak:
@ -141,14 +150,25 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering
case blackfriday.Document:
break
case blackfriday.Paragraph:
// roff .PP markers break lists
if r.listDepth > 0 {
return blackfriday.GoToNext
}
if entering {
out(w, paraTag)
if r.listDepth > 0 {
// roff .PP markers break lists
if node.Prev != nil { // continued paragraph
if node.Prev.Type == blackfriday.List && node.Prev.ListFlags&blackfriday.ListTypeDefinition == 0 {
out(w, ".IP\n")
} else {
out(w, crTag)
}
}
} else if node.Prev != nil && node.Prev.Type == blackfriday.Heading {
out(w, crTag)
} else {
out(w, paraTag)
}
} else {
out(w, crTag)
if node.Next == nil || node.Next.Type != blackfriday.List {
out(w, crTag)
}
}
case blackfriday.BlockQuote:
if entering {
@ -211,6 +231,10 @@ func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, enteri
func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
openTag := listTag
closeTag := listCloseTag
if (entering && r.listDepth == 0) || (!entering && r.listDepth == 1) {
openTag = crTag
closeTag = ""
}
if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// tags for definition lists handled within Item node
openTag = ""
@ -239,23 +263,25 @@ func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering
} else if node.ListFlags&blackfriday.ListTypeTerm != 0 {
// DT (definition term): line just before DD (see below).
out(w, dtTag)
r.firstDD = true
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// DD (definition description): line that starts with ": ".
//
// We have to distinguish between the first DD and the
// subsequent ones, as there should be no vertical
// whitespace between the DT and the first DD.
if r.firstDD {
r.firstDD = false
} else {
out(w, dd2Tag)
if node.Prev != nil && node.Prev.ListFlags&(blackfriday.ListTypeTerm|blackfriday.ListTypeDefinition) == blackfriday.ListTypeDefinition {
if node.Prev.Type == blackfriday.Item &&
node.Prev.LastChild != nil &&
node.Prev.LastChild.Type == blackfriday.List &&
node.Prev.LastChild.ListFlags&blackfriday.ListTypeDefinition == 0 {
out(w, ".IP\n")
} else {
out(w, dd2Tag)
}
}
} else {
out(w, ".IP \\(bu 2\n")
}
} else {
out(w, "\n")
}
}

View File

@ -15,7 +15,7 @@ const (
MIN_PAYLOAD_SIZE = MIN_MSS_SIZE - UDP_HEADER_SIZE - SRT_HEADER_SIZE
MAX_PAYLOAD_SIZE = MAX_MSS_SIZE - UDP_HEADER_SIZE - SRT_HEADER_SIZE
MIN_PASSPHRASE_SIZE = 10
MAX_PASSPHRASE_SIZE = 79
MAX_PASSPHRASE_SIZE = 80
MAX_STREAMID_SIZE = 512
SRT_VERSION = 0x010401
)
@ -169,6 +169,9 @@ type Config struct {
// An implementation of the Logger interface
Logger Logger
// if a new IP starts sending data on an existing socket id, allow it
AllowPeerIpChange bool
}
// DefaultConfig is the default configuration for a SRT connection
@ -209,6 +212,7 @@ var defaultConfig Config = Config{
TooLatePacketDrop: true,
TransmissionType: "live",
TSBPDMode: true,
AllowPeerIpChange: false,
}
// DefaultConfig returns the default configuration for Dial and Listen.

View File

@ -265,11 +265,7 @@ func (r *receiver) periodicACK(now uint64) (ok bool, sequenceNumber circular.Num
}
minPktTsbpdTime, maxPktTsbpdTime := uint64(0), uint64(0)
ackSequenceNumber := r.lastDeliveredSequenceNumber
// Find the sequence number up until we have all in a row.
// Where the first gap is (or at the end of the list) is where we can ACK to.
ackSequenceNumber := r.lastACKSequenceNumber
e := r.packetList.Front()
if e != nil {
@ -277,49 +273,42 @@ func (r *receiver) periodicACK(now uint64) (ok bool, sequenceNumber circular.Num
minPktTsbpdTime = p.Header().PktTsbpdTime
maxPktTsbpdTime = p.Header().PktTsbpdTime
}
// If there are packets that should be delivered by now, move foward.
if p.Header().PktTsbpdTime <= now {
for e = e.Next(); e != nil; e = e.Next() {
p = e.Value.(packet.Packet)
// Find the sequence number up until we have all in a row.
// Where the first gap is (or at the end of the list) is where we can ACK to.
if p.Header().PktTsbpdTime > now {
break
}
}
for e := r.packetList.Front(); e != nil; e = e.Next() {
p := e.Value.(packet.Packet)
ackSequenceNumber = p.Header().PacketSequenceNumber
maxPktTsbpdTime = p.Header().PktTsbpdTime
if e != nil {
if e = e.Next(); e != nil {
p = e.Value.(packet.Packet)
}
}
// Skip packets that we already ACK'd.
if p.Header().PacketSequenceNumber.Lte(ackSequenceNumber) {
continue
}
// If there are packets that should have been delivered by now, move forward.
if p.Header().PktTsbpdTime <= now {
ackSequenceNumber = p.Header().PacketSequenceNumber
continue
}
// Check if the packet is the next in the row.
if p.Header().PacketSequenceNumber.Equals(ackSequenceNumber.Inc()) {
ackSequenceNumber = p.Header().PacketSequenceNumber
for e = e.Next(); e != nil; e = e.Next() {
p = e.Value.(packet.Packet)
if !p.Header().PacketSequenceNumber.Equals(ackSequenceNumber.Inc()) {
break
}
ackSequenceNumber = p.Header().PacketSequenceNumber
maxPktTsbpdTime = p.Header().PktTsbpdTime
}
maxPktTsbpdTime = p.Header().PktTsbpdTime
continue
}
ok = true
sequenceNumber = ackSequenceNumber.Inc()
// Keep track of the last ACK's sequence. with this we can faster ignore
// packets that come in that have a lower sequence number.
r.lastACKSequenceNumber = ackSequenceNumber
break
}
ok = true
sequenceNumber = ackSequenceNumber.Inc()
// Keep track of the last ACK's sequence number. With this we can faster ignore
// packets that come in late that have a lower sequence number.
r.lastACKSequenceNumber = ackSequenceNumber
r.lastPeriodicACK = now
r.nPackets = 0
@ -338,13 +327,19 @@ func (r *receiver) periodicNAK(now uint64) (ok bool, from, to circular.Number) {
// Send a periodic NAK
ackSequenceNumber := r.lastDeliveredSequenceNumber
ackSequenceNumber := r.lastACKSequenceNumber
// Send a NAK only for the first gap.
// Alternatively send a NAK for max. X gaps because the size of the NAK packet is limited.
for e := r.packetList.Front(); e != nil; e = e.Next() {
p := e.Value.(packet.Packet)
// Skip packets that we already ACK'd.
if p.Header().PacketSequenceNumber.Lte(ackSequenceNumber) {
continue
}
// If this packet is not in sequence, we stop here and report that gap.
if !p.Header().PacketSequenceNumber.Equals(ackSequenceNumber.Inc()) {
nackSequenceNumber := ackSequenceNumber.Inc()

View File

@ -7,6 +7,7 @@ import (
"github.com/datarhei/gosrt/crypto"
"github.com/datarhei/gosrt/packet"
"github.com/datarhei/gosrt/rand"
)
// ConnRequest is an incoming connection request
@ -206,6 +207,18 @@ func newConnRequest(ln *listener, p packet.Packet) *connRequest {
return nil
}
// We only support live congestion control
if cif.HasCongestionCtl && cif.CongestionCtl != "live" {
cif.HandshakeType = packet.HandshakeType(REJ_CONGESTION)
ln.log("handshake:recv:error", func() string { return "only live congestion control is supported" })
p.MarshalCIF(cif)
ln.log("handshake:send:dump", func() string { return p.Dump() })
ln.log("handshake:send:cif", func() string { return cif.String() })
ln.send(p)
return nil
}
} else {
cif.HandshakeType = packet.HandshakeType(REJ_ROGUE)
ln.log("handshake:recv:error", func() string { return fmt.Sprintf("only HSv4 and HSv5 are supported (got HSv%d)", cif.Version) })
@ -328,6 +341,23 @@ func (req *connRequest) Reject(reason RejectionReason) {
delete(req.ln.connReqs, req.socketId)
}
// generateSocketId generates an SRT SocketID that can be used for this connection
func (req *connRequest) generateSocketId() (uint32, error) {
for i := 0; i < 10; i++ {
socketId, err := rand.Uint32()
if err != nil {
return 0, fmt.Errorf("could not generate random socket id")
}
// check that the socket id is not already in use
if _, found := req.ln.conns[socketId]; !found {
return socketId, nil
}
}
return 0, fmt.Errorf("could not generate unused socketid")
}
func (req *connRequest) Accept() (Conn, error) {
if req.crypto != nil && len(req.passphrase) == 0 {
req.Reject(REJ_BADSECRET)
@ -342,7 +372,10 @@ func (req *connRequest) Accept() (Conn, error) {
}
// Create a new socket ID
socketId := uint32(time.Since(req.ln.start).Microseconds())
socketId, err := req.generateSocketId()
if err != nil {
return nil, fmt.Errorf("could not generate socket id: %w", err)
}
// Select the largest TSBPD delay advertised by the caller, but at least 120ms
recvTsbpdDelay := uint16(req.config.ReceiverLatency.Milliseconds())

View File

@ -69,6 +69,51 @@ type Conn interface {
Version() uint32
}
type rtt struct {
rtt float64 // microseconds
rttVar float64 // microseconds
lock sync.RWMutex
}
func (r *rtt) Recalculate(rtt time.Duration) {
// 4.10. Round-Trip Time Estimation
lastRTT := float64(rtt.Microseconds())
r.lock.Lock()
defer r.lock.Unlock()
r.rtt = r.rtt*0.875 + lastRTT*0.125
r.rttVar = r.rttVar*0.75 + math.Abs(r.rtt-lastRTT)*0.25
}
func (r *rtt) RTT() float64 {
r.lock.RLock()
defer r.lock.RUnlock()
return r.rtt
}
func (r *rtt) RTTVar() float64 {
r.lock.RLock()
defer r.lock.RUnlock()
return r.rttVar
}
func (r *rtt) NAKInterval() float64 {
r.lock.RLock()
defer r.lock.RUnlock()
// 4.8.2. Packet Retransmission (NAKs)
nakInterval := (r.rtt + 4*r.rttVar) / 2
if nakInterval < 20000 {
nakInterval = 20000 // 20ms
}
return nakInterval
}
type connStats struct {
headerSize uint64
pktSentACK uint64
@ -108,19 +153,16 @@ type srtConn struct {
config Config
cryptoLock sync.Mutex
crypto crypto.Crypto
keyBaseEncryption packet.PacketEncryption
kmPreAnnounceCountdown uint64
kmRefreshCountdown uint64
kmConfirmed bool
cryptoLock sync.Mutex
peerIdleTimeout *time.Timer
rtt float64 // microseconds
rttVar float64 // microseconds
nakInterval float64
rtt rtt // microseconds
ackLock sync.RWMutex
ackNumbers map[uint32]time.Time
@ -232,10 +274,10 @@ func newSRTConn(config srtConnConfig) *srtConn {
c.kmRefreshCountdown = c.config.KMRefreshRate
// 4.10. Round-Trip Time Estimation
c.rtt = float64((100 * time.Millisecond).Microseconds())
c.rttVar = float64((50 * time.Millisecond).Microseconds())
c.nakInterval = float64((20 * time.Millisecond).Microseconds())
c.rtt = rtt{
rtt: float64((100 * time.Millisecond).Microseconds()),
rttVar: float64((50 * time.Millisecond).Microseconds()),
}
c.networkQueue = make(chan packet.Packet, 1024)
@ -788,7 +830,7 @@ func (c *srtConn) handleNAK(p packet.Packet) {
// handleACKACK updates the RTT and NAK interval for the congestion control.
func (c *srtConn) handleACKACK(p packet.Packet) {
c.ackLock.RLock()
c.ackLock.Lock()
c.statisticsLock.Lock()
c.statistics.pktRecvACKACK++
@ -814,31 +856,17 @@ func (c *srtConn) handleACKACK(p packet.Packet) {
}
}
nakInterval := uint64(c.nakInterval)
c.ackLock.Unlock()
c.ackLock.RUnlock()
c.recv.SetNAKInterval(nakInterval)
c.recv.SetNAKInterval(uint64(c.rtt.NAKInterval()))
}
// recalculateRTT recalculates the RTT based on a full ACK exchange
func (c *srtConn) recalculateRTT(rtt time.Duration) {
// 4.10. Round-Trip Time Estimation
lastRTT := float64(rtt.Microseconds())
c.rtt = c.rtt*0.875 + lastRTT*0.125
c.rttVar = c.rttVar*0.75 + math.Abs(c.rtt-lastRTT)*0.25
// 4.8.2. Packet Retransmission (NAKs)
nakInterval := (c.rtt + 4*c.rttVar) / 2
if nakInterval < 20000 {
c.nakInterval = 20000 // 20ms
} else {
c.nakInterval = nakInterval
}
c.rtt.Recalculate(rtt)
c.log("connection:rtt", func() string {
return fmt.Sprintf("RTT=%.0fus RTTVar=%.0fus NAKInterval=%.0fms", c.rtt, c.rttVar, c.nakInterval/1000)
return fmt.Sprintf("RTT=%.0fus RTTVar=%.0fus NAKInterval=%.0fms", c.rtt.RTT(), c.rtt.RTTVar(), c.rtt.NAKInterval()/1000)
})
}
@ -1219,8 +1247,8 @@ func (c *srtConn) sendACK(seq circular.Number, lite bool) {
} else {
pps, bps, capacity := c.recv.PacketRate()
cif.RTT = uint32(c.rtt)
cif.RTTVar = uint32(c.rttVar)
cif.RTT = uint32(c.rtt.RTT())
cif.RTTVar = uint32(c.rtt.RTTVar())
cif.AvailableBufferSize = c.config.FC // TODO: available buffer size (packets)
cif.PacketsReceivingRate = uint32(pps) // packets receiving rate (packets/s)
cif.EstimatedLinkCapacity = uint32(capacity) // estimated link capacity (packets/s), not relevant for live mode
@ -1488,7 +1516,7 @@ func (c *srtConn) Stats(s *Statistics) {
UsPktSendPeriod: send.UsPktSndPeriod,
PktFlowWindow: uint64(c.config.FC),
PktFlightSize: send.PktFlightSize,
MsRTT: c.rtt / 1000,
MsRTT: c.rtt.RTT() / 1000,
MbpsSentRate: send.MbpsEstimatedSentBandwidth,
MbpsRecvRate: recv.MbpsEstimatedRecvBandwidth,
MbpsLinkCapacity: recv.MbpsEstimatedLinkCapacity,

View File

@ -407,6 +407,14 @@ func (ln *listener) reader(ctx context.Context) {
break
}
if !ln.config.AllowPeerIpChange {
if p.Header().Addr.String() != conn.RemoteAddr().String() {
// ignore the packet, it's not from the expected peer
// https://haivision.github.io/srt-rfc/draft-sharabayko-srt.html#name-security-considerations
break
}
}
conn.push(p)
}
}

View File

@ -33,7 +33,7 @@ const (
CTRLTYPE_SHUTDOWN CtrlType = 0x0005
CTRLTYPE_ACKACK CtrlType = 0x0006
CRTLTYPE_DROPREQ CtrlType = 0x0007 // unimplemented, sender->receiver
CRTLTYPE_PEERERROR CtrlType = 0x0008 // unimplemented, receiver->sender
CRTLTYPE_PEERERROR CtrlType = 0x0008 // unimplemented, receiver->sender (only for file transfers)
CTRLTYPE_USER CtrlType = 0x7FFF
)
@ -141,8 +141,8 @@ const (
EXTTYPE_KMRSP CtrlSubType = 4
EXTTYPE_SID CtrlSubType = 5
EXTTYPE_CONGESTION CtrlSubType = 6
EXTTYPE_FILTER CtrlSubType = 7
EXTTYPE_GROUP CtrlSubType = 8
EXTTYPE_FILTER CtrlSubType = 7 // unimplemented
EXTTYPE_GROUP CtrlSubType = 8 // unimplemented
)
func (h CtrlSubType) String() string {
@ -484,7 +484,7 @@ type CIFHandshake struct {
Version uint32 // A base protocol version number. Currently used values are 4 and 5. Values greater than 5 are reserved for future use.
EncryptionField uint16 // Block cipher family and key size. The values of this field are described in Table 2. The default value is AES-128.
ExtensionField uint16 // This field is message specific extension related to Handshake Type field. The value MUST be set to 0 except for the following cases. (1) If the handshake control packet is the INDUCTION message, this field is sent back by the Listener. (2) In the case of a CONCLUSION message, this field value should contain a combination of Extension Type values. For more details, see Section 4.3.1.
ExtensionField uint16 // This field is a message specific extension related to Handshake Type field. The value MUST be set to 0 except for the following cases. (1) If the handshake control packet is the INDUCTION message, this field is sent back by the Listener. (2) In the case of a CONCLUSION message, this field value should contain a combination of Extension Type values. For more details, see Section 4.3.1.
InitialPacketSequenceNumber circular.Number // The sequence number of the very first data packet to be sent.
MaxTransmissionUnitSize uint32 // This value is typically set to 1500, which is the default Maximum Transmission Unit (MTU) size for Ethernet, but can be less.
MaxFlowWindowSize uint32 // The value of this field is the maximum number of data packets allowed to be "in flight" (i.e. the number of sent packets for which an ACK control packet has not yet been received).
@ -493,9 +493,10 @@ type CIFHandshake struct {
SynCookie uint32 // Randomized value for processing a handshake. The value of this field is specified by the handshake message type. See Section 4.3.
PeerIP srtnet.IP // IPv4 or IPv6 address of the packet's sender. The value consists of four 32-bit fields. In the case of IPv4 addresses, fields 2, 3 and 4 are filled with zeroes.
HasHS bool
HasKM bool
HasSID bool
HasHS bool
HasKM bool
HasSID bool
HasCongestionCtl bool
// 3.2.1.1. Handshake Extension Message
SRTHS *CIFHandshakeExtension
@ -505,6 +506,9 @@ type CIFHandshake struct {
// 3.2.1.3. Stream ID Extension Message
StreamId string
// ??? Congestion Control Extension message (handshake.md #### Congestion controller)
CongestionCtl string
}
func (c CIFHandshake) String() string {
@ -537,6 +541,12 @@ func (c CIFHandshake) String() string {
fmt.Fprintf(&b, " streamId : %s\n", c.StreamId)
fmt.Fprintf(&b, "--- /SIDExt ---\n")
}
if c.HasCongestionCtl {
fmt.Fprintf(&b, "--- CongestionExt ---\n")
fmt.Fprintf(&b, " congestion : %s\n", c.CongestionCtl)
fmt.Fprintf(&b, "--- /CongestionExt ---\n")
}
}
fmt.Fprintf(&b, "--- /handshake ---")
@ -599,7 +609,7 @@ func (c *CIFHandshake) Unmarshal(data []byte) error {
if extensionType == EXTTYPE_HSREQ || extensionType == EXTTYPE_HSRSP {
// 3.2.1.1. Handshake Extension Message
if extensionLength != 12 || len(pivot) < extensionLength {
return fmt.Errorf("invalid extension length")
return fmt.Errorf("invalid extension length of %d bytes (%s)", extensionLength, extensionType.String())
}
c.HasHS = true
@ -612,7 +622,7 @@ func (c *CIFHandshake) Unmarshal(data []byte) error {
} else if extensionType == EXTTYPE_KMREQ || extensionType == EXTTYPE_KMRSP {
// 3.2.1.2. Key Material Extension Message
if len(pivot) < extensionLength {
return fmt.Errorf("invalid extension length")
return fmt.Errorf("invalid extension length of %d bytes (%s)", extensionLength, extensionType.String())
}
c.HasKM = true
@ -638,7 +648,7 @@ func (c *CIFHandshake) Unmarshal(data []byte) error {
} else if extensionType == EXTTYPE_SID {
// 3.2.1.3. Stream ID Extension Message
if extensionLength > 512 || len(pivot) < extensionLength {
return fmt.Errorf("invalid extension length")
return fmt.Errorf("invalid extension length of %d bytes (%s)", extensionLength, extensionType.String())
}
c.HasSID = true
@ -653,8 +663,30 @@ func (c *CIFHandshake) Unmarshal(data []byte) error {
}
c.StreamId = strings.TrimRight(b.String(), "\x00")
} else if extensionType == EXTTYPE_CONGESTION {
// ??? Congestion Control Extension message (handshake.md #### Congestion controller)
if extensionLength > 4 || len(pivot) < extensionLength {
return fmt.Errorf("invalid extension length of %d bytes (%s)", extensionLength, extensionType.String())
}
c.HasCongestionCtl = true
var b strings.Builder
for i := 0; i < extensionLength; i += 4 {
b.WriteByte(pivot[i+3])
b.WriteByte(pivot[i+2])
b.WriteByte(pivot[i+1])
b.WriteByte(pivot[i+0])
}
c.CongestionCtl = strings.TrimRight(b.String(), "\x00")
} else if extensionType == EXTTYPE_FILTER || extensionType == EXTTYPE_GROUP {
if len(pivot) < extensionLength {
return fmt.Errorf("invalid extension length of %d bytes (%s)", extensionLength, extensionType.String())
}
} else {
return fmt.Errorf("unimplemented extension (%d)", extensionType)
return fmt.Errorf("unknown extension (%d)", extensionType)
}
if len(pivot) > extensionLength {
@ -695,6 +727,10 @@ func (c *CIFHandshake) Marshal(w io.Writer) {
if c.HasSID {
c.ExtensionField = c.ExtensionField | 4
}
if c.HasCongestionCtl {
c.ExtensionField = c.ExtensionField | 4
}
} else {
c.EncryptionField = 0
c.ExtensionField = 2
@ -773,6 +809,33 @@ func (c *CIFHandshake) Marshal(w io.Writer) {
w.Write(buffer[:4])
}
}
if c.HasCongestionCtl && c.CongestionCtl != "live" {
congestion := bytes.NewBufferString(c.CongestionCtl)
missing := (4 - congestion.Len()%4)
if missing < 4 {
for i := 0; i < missing; i++ {
congestion.WriteByte(0)
}
}
binary.BigEndian.PutUint16(buffer[0:], EXTTYPE_CONGESTION.Value())
binary.BigEndian.PutUint16(buffer[2:], uint16(congestion.Len()/4))
w.Write(buffer[:4])
b := congestion.Bytes()
for i := 0; i < len(b); i += 4 {
buffer[0] = b[i+3]
buffer[1] = b[i+2]
buffer[2] = b[i+1]
buffer[3] = b[i+0]
w.Write(buffer[:4])
}
}
}
// 3.2.1.1.1. Handshake Extension Message Flags

View File

@ -12,7 +12,7 @@ func RandomString(length int, charset string) (string, error) {
if err != nil {
return "", err
}
b[i] = charset[int(j)]
b[i] = charset[j]
}
return string(b), nil

View File

@ -23,6 +23,8 @@ var (
Torrent = prefix([]byte("d8:announce"))
// PAR1 matches a parquet file.
Par1 = prefix([]byte{0x50, 0x41, 0x52, 0x31})
// CBOR matches a Concise Binary Object Representation https://cbor.io/
CBOR = prefix([]byte{0xD9, 0xD9, 0xF7})
)
// Java bytecode and Mach-O binaries share the same magic number.
@ -34,7 +36,7 @@ func classOrMachOFat(in []byte) bool {
return false
}
return bytes.HasPrefix(in, []byte{0xCA, 0xFE, 0xBA, 0xBE})
return binary.BigEndian.Uint32(in) == macho.MagicFat
}
// Class matches a java class file.
@ -44,7 +46,7 @@ func Class(raw []byte, limit uint32) bool {
// MachO matches Mach-O binaries format.
func MachO(raw []byte, limit uint32) bool {
if classOrMachOFat(raw) && raw[7] < 20 {
if classOrMachOFat(raw) && raw[7] < 0x14 {
return true
}
@ -156,11 +158,11 @@ func Marc(raw []byte, limit uint32) bool {
// the GL transmission Format (glTF).
// GLB uses little endian and its header structure is as follows:
//
// <-- 12-byte header -->
// | magic | version | length |
// | (uint32) | (uint32) | (uint32) |
// | \x67\x6C\x54\x46 | \x01\x00\x00\x00 | ... |
// | g l T F | 1 | ... |
// <-- 12-byte header -->
// | magic | version | length |
// | (uint32) | (uint32) | (uint32) |
// | \x67\x6C\x54\x46 | \x01\x00\x00\x00 | ... |
// | g l T F | 1 | ... |
//
// Visit [glTF specification] and [IANA glTF entry] for more details.
//
@ -172,14 +174,15 @@ var Glb = prefix([]byte("\x67\x6C\x54\x46\x02\x00\x00\x00"),
// TzIf matches a Time Zone Information Format (TZif) file.
// See more: https://tools.ietf.org/id/draft-murchison-tzdist-tzif-00.html#rfc.section.3
// Its header structure is shown below:
// +---------------+---+
// | magic (4) | <-+-- version (1)
// +---------------+---+---------------------------------------+
// | [unused - reserved for future use] (15) |
// +---------------+---------------+---------------+-----------+
// | isutccnt (4) | isstdcnt (4) | leapcnt (4) |
// +---------------+---------------+---------------+
// | timecnt (4) | typecnt (4) | charcnt (4) |
//
// +---------------+---+
// | magic (4) | <-+-- version (1)
// +---------------+---+---------------------------------------+
// | [unused - reserved for future use] (15) |
// +---------------+---------------+---------------+-----------+
// | isutccnt (4) | isstdcnt (4) | leapcnt (4) |
// +---------------+---------------+---------------+
// | timecnt (4) | typecnt (4) | charcnt (4) |
func TzIf(raw []byte, limit uint32) bool {
// File is at least 44 bytes (header size).
if len(raw) < 44 {

View File

@ -1,4 +1,4 @@
## 176 Supported MIME types
## 177 Supported MIME types
This file is automatically generated when running tests. Do not edit manually.
Extension | MIME type | Aliases
@ -79,7 +79,7 @@ Extension | MIME type | Aliases
**.avif** | image/avif | -
**.3gp** | video/3gpp | video/3gp, audio/3gpp
**.3g2** | video/3gpp2 | video/3g2, audio/3gpp2
**.mp4** | audio/mp4 | audio/x-m4a, audio/x-mp4a
**.mp4** | audio/mp4 | audio/x-mp4a
**.mqv** | video/quicktime | -
**.m4a** | audio/x-m4a | -
**.m4v** | video/x-m4v | -
@ -118,6 +118,7 @@ Extension | MIME type | Aliases
**.mobi** | application/x-mobipocket-ebook | -
**.lit** | application/x-ms-reader | -
**.bpg** | image/bpg | -
**.cbor** | application/cbor | -
**.sqlite** | application/vnd.sqlite3 | application/x-sqlite3
**.dwg** | image/vnd.dwg | image/x-dwg, application/acad, application/x-acad, application/autocad_dwg, application/dwg, application/x-dwg, application/x-autocad, drawing/dwg
**.nes** | application/vnd.nintendo.snes.rom | -
@ -162,7 +163,7 @@ Extension | MIME type | Aliases
**.xfdf** | application/vnd.adobe.xfdf | -
**.owl** | application/owl+xml | -
**.php** | text/x-php | -
**.js** | application/javascript | application/x-javascript, text/javascript
**.js** | text/javascript | application/x-javascript, application/javascript
**.lua** | text/x-lua | -
**.pl** | text/x-perl | -
**.py** | text/x-python | text/x-script.python, application/x-python

View File

@ -21,7 +21,7 @@ var root = newMIME("application/octet-stream", "",
jpm, jxs, gif, webp, exe, elf, ar, tar, xar, bz2, fits, tiff, bmp, ico, mp3,
flac, midi, ape, musePack, amr, wav, aiff, au, mpeg, quickTime, mp4, webM,
avi, flv, mkv, asf, aac, voc, m3u, rmvb, gzip, class, swf, crx, ttf, woff,
woff2, otf, ttc, eot, wasm, shx, dbf, dcm, rar, djvu, mobi, lit, bpg,
woff2, otf, ttc, eot, wasm, shx, dbf, dcm, rar, djvu, mobi, lit, bpg, cbor,
sqlite3, dwg, nes, lnk, macho, qcp, icns, hdr, mrc, mdb, accdb, zstd, cab,
rpm, xz, lzip, torrent, cpio, tzif, xcf, pat, gbr, glb, cabIS, jxr, parquet,
// Keep text last because it is the slowest check.
@ -87,8 +87,8 @@ var (
html = newMIME("text/html", ".html", magic.HTML)
php = newMIME("text/x-php", ".php", magic.Php)
rtf = newMIME("text/rtf", ".rtf", magic.Rtf).alias("application/rtf")
js = newMIME("application/javascript", ".js", magic.Js).
alias("application/x-javascript", "text/javascript")
js = newMIME("text/javascript", ".js", magic.Js).
alias("application/x-javascript", "application/javascript")
srt = newMIME("application/x-subrip", ".srt", magic.Srt).
alias("application/x-srt", "text/x-srt")
vtt = newMIME("text/vtt", ".vtt", magic.Vtt)
@ -156,7 +156,7 @@ var (
aac = newMIME("audio/aac", ".aac", magic.AAC)
voc = newMIME("audio/x-unknown", ".voc", magic.Voc)
aMp4 = newMIME("audio/mp4", ".mp4", magic.AMp4).
alias("audio/x-m4a", "audio/x-mp4a")
alias("audio/x-mp4a")
m4a = newMIME("audio/x-m4a", ".m4a", magic.M4a)
m3u = newMIME("application/vnd.apple.mpegurl", ".m3u", magic.M3u).
alias("audio/mpegurl")
@ -261,4 +261,5 @@ var (
jxr = newMIME("image/jxr", ".jxr", magic.Jxr).alias("image/vnd.ms-photo")
parquet = newMIME("application/vnd.apache.parquet", ".parquet", magic.Par1).
alias("application/x-parquet")
cbor = newMIME("application/cbor", ".cbor", magic.CBOR)
)

View File

@ -1,7 +1,7 @@
Package validator
=================
<img align="right" src="logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.22.1-green.svg)
![Project status](https://img.shields.io/badge/version-10.23.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)

View File

@ -205,6 +205,7 @@ var (
"fqdn": isFQDN,
"unique": isUnique,
"oneof": isOneOf,
"oneofci": isOneOfCI,
"html": isHTML,
"html_encoded": isHTMLEncoded,
"url_encoded": isURLEncoded,
@ -213,6 +214,7 @@ var (
"json": isJSON,
"jwt": isJWT,
"hostname_port": isHostnamePort,
"port": isPort,
"lowercase": isLowercase,
"uppercase": isUppercase,
"datetime": isDatetime,
@ -299,6 +301,23 @@ func isOneOf(fl FieldLevel) bool {
return false
}
// isOneOfCI is the validation function for validating if the current field's value is one of the provided string values (case insensitive).
func isOneOfCI(fl FieldLevel) bool {
vals := parseOneOfParam2(fl.Param())
field := fl.Field()
if field.Kind() != reflect.String {
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
v := field.String()
for _, val := range vals {
if strings.EqualFold(val, v) {
return true
}
}
return false
}
// isUnique is the validation function for validating if each array|slice|map value is unique
func isUnique(fl FieldLevel) bool {
field := fl.Field()
@ -2711,6 +2730,13 @@ func isHostnamePort(fl FieldLevel) bool {
return true
}
// IsPort validates if the current field's value represents a valid port
func isPort(fl FieldLevel) bool {
val := fl.Field().Uint()
return val >= 1 && val <= 65535
}
// isLowercase is the validation function for validating if the current field's value is a lowercase string.
func isLowercase(fl FieldLevel) bool {
field := fl.Field()

View File

@ -489,12 +489,19 @@ For strings, ints, and uints, oneof will ensure that the value
is one of the values in the parameter. The parameter should be
a list of values separated by whitespace. Values may be
strings or numbers. To match strings with spaces in them, include
the target string between single quotes.
the target string between single quotes. Kind of like an 'enum'.
Usage: oneof=red green
oneof='red green' 'blue yellow'
oneof=5 7 9
# One Of Case Insensitive
Works the same as oneof but is case insensitive and therefore only accepts strings.
Usage: oneofci=red green
oneofci='red green' 'blue yellow'
# Greater Than
For numbers, this will ensure that the value is greater than the

View File

@ -73,7 +73,7 @@ const (
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
mongodbIdRegexString = "^[a-f\\d]{24}$"
mongodbConnStringRegexString = "^mongodb(\\+srv)?:\\/\\/(([a-zA-Z\\d]+):([a-zA-Z\\d$:\\/?#\\[\\]@]+)@)?(([a-z\\d.-]+)(:[\\d]+)?)((,(([a-z\\d.-]+)(:(\\d+))?))*)?(\\/[a-zA-Z-_]{1,64})?(\\?(([a-zA-Z]+)=([a-zA-Z\\d]+))(&(([a-zA-Z\\d]+)=([a-zA-Z\\d]+))?)*)?$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|((\*|\d+)(\/|-)\d+)|\d+|\*) ?){5,7})`
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
spicedbTypeRegexString = "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$"

View File

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.go]
indent_style = tab
[{Makefile,*.mk}]
indent_style = tab
[*.nix]
indent_size = 2

4
vendor/github.com/go-viper/mapstructure/v2/.envrc generated vendored Normal file
View File

@ -0,0 +1,4 @@
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
fi
use flake . --impure

View File

@ -0,0 +1,6 @@
/.devenv/
/.direnv/
/.pre-commit-config.yaml
/bin/
/build/
/var/

View File

@ -0,0 +1,23 @@
run:
timeout: 5m
linters-settings:
gci:
sections:
- standard
- default
- prefix(github.com/go-viper/mapstructure)
golint:
min-confidence: 0
goimports:
local-prefixes: github.com/go-viper/maptstructure
linters:
disable-all: true
enable:
- gci
- gofmt
- gofumpt
- goimports
- staticcheck
# - stylecheck

View File

@ -1,3 +1,11 @@
> [!WARNING]
> As of v2 of this library, change log can be found in GitHub releases.
## 1.5.1
* Wrap errors so they're compatible with `errors.Is` and `errors.As` [GH-282]
* Fix map of slices not decoding properly in certain cases. [GH-266]
## 1.5.0
* New option `IgnoreUntaggedFields` to ignore decoding to any fields

80
vendor/github.com/go-viper/mapstructure/v2/README.md generated vendored Normal file
View File

@ -0,0 +1,80 @@
# mapstructure
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/go-viper/mapstructure/ci.yaml?branch=main&style=flat-square)](https://github.com/go-viper/mapstructure/actions?query=workflow%3ACI)
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.18-61CFDD.svg?style=flat-square)
mapstructure is a Go library for decoding generic map values to structures
and vice versa, while providing helpful error handling.
This library is most useful when decoding values from some data stream (JSON,
Gob, etc.) where you don't _quite_ know the structure of the underlying data
until you read a part of it. You can therefore read a `map[string]interface{}`
and use this library to decode it into the proper underlying native Go
structure.
## Installation
```shell
go get github.com/go-viper/mapstructure/v2
```
## Migrating from `github.com/mitchellh/mapstructure`
[@mitchehllh](https://github.com/mitchellh) announced his intent to archive some of his unmaintained projects (see [here](https://gist.github.com/mitchellh/90029601268e59a29e64e55bab1c5bdc) and [here](https://github.com/mitchellh/mapstructure/issues/349)). This is a repository achieved the "blessed fork" status.
You can migrate to this package by changing your import paths in your Go files to `github.com/go-viper/mapstructure/v2`.
The API is the same, so you don't need to change anything else.
Here is a script that can help you with the migration:
```shell
sed -i 's/github.com\/mitchellh\/mapstructure/github.com\/go-viper\/mapstructure\/v2/g' $(find . -type f -name '*.go')
```
If you need more time to migrate your code, that is absolutely fine.
Some of the latest fixes are backported to the v1 release branch of this package, so you can use the Go modules `replace` feature until you are ready to migrate:
```shell
replace github.com/mitchellh/mapstructure => github.com/go-viper/mapstructure v1.6.0
```
## Usage & Example
For usage and examples see the [documentation](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2).
The `Decode` function has examples associated with it there.
## But Why?!
Go offers fantastic standard libraries for decoding formats such as JSON.
The standard method is to have a struct pre-created, and populate that struct
from the bytes of the encoded format. This is great, but the problem is if
you have configuration or an encoding that changes slightly depending on
specific fields. For example, consider this JSON:
```json
{
"type": "person",
"name": "Mitchell"
}
```
Perhaps we can't populate a specific structure without first reading
the "type" field from the JSON. We could always do two passes over the
decoding of the JSON (reading the "type" first, and the rest later).
However, it is much simpler to just decode this into a `map[string]interface{}`
structure, read the "type" key, then use something like this library
to decode it into the proper structure.
## Credits
Mapstructure was originally created by [@mitchellh](https://github.com/mitchellh).
This is a maintained fork of the original library.
Read more about the reasons for the fork [here](https://github.com/mitchellh/mapstructure/issues/349).
## License
The project is licensed under the [MIT License](LICENSE).

File diff suppressed because it is too large Load Diff

472
vendor/github.com/go-viper/mapstructure/v2/flake.lock generated vendored Normal file
View File

@ -0,0 +1,472 @@
{
"nodes": {
"cachix": {
"inputs": {
"devenv": "devenv_2",
"flake-compat": [
"devenv",
"flake-compat"
],
"nixpkgs": [
"devenv",
"nixpkgs"
],
"pre-commit-hooks": [
"devenv",
"pre-commit-hooks"
]
},
"locked": {
"lastModified": 1712055811,
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
"owner": "cachix",
"repo": "cachix",
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat_2",
"nix": "nix_2",
"nixpkgs": "nixpkgs_2",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1717245169,
"narHash": "sha256-+mW3rTBjGU8p1THJN0lX/Dd/8FbnF+3dB+mJuSaxewE=",
"owner": "cachix",
"repo": "devenv",
"rev": "c3f9f053c077c6f88a3de5276d9178c62baa3fc3",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv_2": {
"inputs": {
"flake-compat": [
"devenv",
"cachix",
"flake-compat"
],
"nix": "nix",
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix",
"pre-commit-hooks": [
"devenv",
"cachix",
"pre-commit-hooks"
]
},
"locked": {
"lastModified": 1708704632,
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
"owner": "cachix",
"repo": "devenv",
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "python-rewrite",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1717285511,
"narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix_2": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression_2"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1692808169,
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1717284937,
"narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-regression_2": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1710695816,
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1713361204,
"narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1717112898,
"narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1692876271,
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-utils": "flake-utils_2",
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1713775815,
"narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_3"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

39
vendor/github.com/go-viper/mapstructure/v2/flake.nix generated vendored Normal file
View File

@ -0,0 +1,39 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
devenv.url = "github:cachix/devenv";
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
perSystem = { config, self', inputs', pkgs, system, ... }: rec {
devenv.shells = {
default = {
languages = {
go.enable = true;
};
pre-commit.hooks = {
nixpkgs-fmt.enable = true;
};
packages = with pkgs; [
golangci-lint
];
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
containers = pkgs.lib.mkForce { };
};
ci = devenv.shells.default;
};
};
};
}

View File

@ -0,0 +1,11 @@
package errors
import "errors"
func New(text string) error {
return errors.New(text)
}
func As(err error, target interface{}) bool {
return errors.As(err, target)
}

View File

@ -0,0 +1,9 @@
//go:build go1.20
package errors
import "errors"
func Join(errs ...error) error {
return errors.Join(errs...)
}

View File

@ -0,0 +1,61 @@
//go:build !go1.20
// Copyright 2022 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 errors
// Join returns an error that wraps the given errors.
// Any nil error values are discarded.
// Join returns nil if every value in errs is nil.
// The error formats as the concatenation of the strings obtained
// by calling the Error method of each element of errs, with a newline
// between each string.
//
// A non-nil error returned by Join implements the Unwrap() []error method.
func Join(errs ...error) error {
n := 0
for _, err := range errs {
if err != nil {
n++
}
}
if n == 0 {
return nil
}
e := &joinError{
errs: make([]error, 0, n),
}
for _, err := range errs {
if err != nil {
e.errs = append(e.errs, err)
}
}
return e
}
type joinError struct {
errs []error
}
func (e *joinError) Error() string {
// Since Join returns nil if every value in errs is nil,
// e.errs cannot be empty.
if len(e.errs) == 1 {
return e.errs[0].Error()
}
b := []byte(e.errs[0].Error())
for _, err := range e.errs[1:] {
b = append(b, '\n')
b = append(b, err.Error()...)
}
// At this point, b has at least one byte '\n'.
// return unsafe.String(&b[0], len(b))
return string(b)
}
func (e *joinError) Unwrap() []error {
return e.errs
}

View File

@ -0,0 +1,44 @@
//go:build !go1.20
package mapstructure
import "reflect"
func isComparable(v reflect.Value) bool {
k := v.Kind()
switch k {
case reflect.Invalid:
return false
case reflect.Array:
switch v.Type().Elem().Kind() {
case reflect.Interface, reflect.Array, reflect.Struct:
for i := 0; i < v.Type().Len(); i++ {
// if !v.Index(i).Comparable() {
if !isComparable(v.Index(i)) {
return false
}
}
return true
}
return v.Type().Comparable()
case reflect.Interface:
// return v.Elem().Comparable()
return isComparable(v.Elem())
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
return false
// if !v.Field(i).Comparable() {
if !isComparable(v.Field(i)) {
return false
}
}
return true
default:
return v.Type().Comparable()
}
}

View File

@ -0,0 +1,10 @@
//go:build go1.20
package mapstructure
import "reflect"
// TODO: remove once we drop support for Go <1.20
func isComparable(v reflect.Value) bool {
return v.Comparable()
}

View File

@ -281,6 +281,7 @@ Exit Code 1
| AMXBF16 | Tile computational operations on BFLOAT16 numbers |
| AMXINT8 | Tile computational operations on 8-bit integers |
| AMXFP16 | Tile computational operations on FP16 numbers |
| AMXFP8 | Tile computational operations on FP8 numbers |
| AMXTILE | Tile architecture |
| APX_F | Intel APX |
| AVX | AVX functions |

View File

@ -55,6 +55,12 @@ const (
Qualcomm
Marvell
QEMU
QNX
ACRN
SRE
Apple
lastVendor
)
@ -75,6 +81,7 @@ const (
AMXBF16 // Tile computational operations on BFLOAT16 numbers
AMXFP16 // Tile computational operations on FP16 numbers
AMXINT8 // Tile computational operations on 8-bit integers
AMXFP8 // Tile computational operations on FP8 numbers
AMXTILE // Tile architecture
APX_F // Intel APX
AVX // AVX functions
@ -296,20 +303,22 @@ const (
// CPUInfo contains information about the detected system CPU.
type CPUInfo struct {
BrandName string // Brand name reported by the CPU
VendorID Vendor // Comparable CPU vendor ID
VendorString string // Raw vendor string.
featureSet flagSet // Features of the CPU
PhysicalCores int // Number of physical processor cores in your CPU. Will be 0 if undetectable.
ThreadsPerCore int // Number of threads per physical core. Will be 1 if undetectable.
LogicalCores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable.
Family int // CPU family number
Model int // CPU model number
Stepping int // CPU stepping info
CacheLine int // Cache line size in bytes. Will be 0 if undetectable.
Hz int64 // Clock speed, if known, 0 otherwise. Will attempt to contain base clock speed.
BoostFreq int64 // Max clock speed, if known, 0 otherwise
Cache struct {
BrandName string // Brand name reported by the CPU
VendorID Vendor // Comparable CPU vendor ID
VendorString string // Raw vendor string.
HypervisorVendorID Vendor // Hypervisor vendor
HypervisorVendorString string // Raw hypervisor vendor string
featureSet flagSet // Features of the CPU
PhysicalCores int // Number of physical processor cores in your CPU. Will be 0 if undetectable.
ThreadsPerCore int // Number of threads per physical core. Will be 1 if undetectable.
LogicalCores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable.
Family int // CPU family number
Model int // CPU model number
Stepping int // CPU stepping info
CacheLine int // Cache line size in bytes. Will be 0 if undetectable.
Hz int64 // Clock speed, if known, 0 otherwise. Will attempt to contain base clock speed.
BoostFreq int64 // Max clock speed, if known, 0 otherwise
Cache struct {
L1I int // L1 Instruction Cache (per core or shared). Will be -1 if undetected
L1D int // L1 Data Cache (per core or shared). Will be -1 if undetected
L2 int // L2 Cache (per core or shared). Will be -1 if undetected
@ -318,8 +327,9 @@ type CPUInfo struct {
SGX SGXSupport
AMDMemEncryption AMDMemEncryptionSupport
AVX10Level uint8
maxFunc uint32
maxExFunc uint32
maxFunc uint32
maxExFunc uint32
}
var cpuid func(op uint32) (eax, ebx, ecx, edx uint32)
@ -503,7 +513,7 @@ func (c CPUInfo) FeatureSet() []string {
// Uses the RDTSCP instruction. The value 0 is returned
// if the CPU does not support the instruction.
func (c CPUInfo) RTCounter() uint64 {
if !c.Supports(RDTSCP) {
if !c.Has(RDTSCP) {
return 0
}
a, _, _, d := rdtscpAsm()
@ -515,13 +525,22 @@ func (c CPUInfo) RTCounter() uint64 {
// about the current cpu/core the code is running on.
// If the RDTSCP instruction isn't supported on the CPU, the value 0 is returned.
func (c CPUInfo) Ia32TscAux() uint32 {
if !c.Supports(RDTSCP) {
if !c.Has(RDTSCP) {
return 0
}
_, _, ecx, _ := rdtscpAsm()
return ecx
}
// SveLengths returns arm SVE vector and predicate lengths.
// Will return 0, 0 if SVE is not enabled or otherwise unable to detect.
func (c CPUInfo) SveLengths() (vl, pl uint64) {
if !c.Has(SVE) {
return 0, 0
}
return getVectorLength()
}
// LogicalCPU will return the Logical CPU the code is currently executing on.
// This is likely to change when the OS re-schedules the running thread
// to another CPU.
@ -781,11 +800,16 @@ func threadsPerCore() int {
_, b, _, _ := cpuidex(0xb, 0)
if b&0xffff == 0 {
if vend == AMD {
// Workaround for AMD returning 0, assume 2 if >= Zen 2
// It will be more correct than not.
// if >= Zen 2 0x8000001e EBX 15-8 bits means threads per core.
// The number of threads per core is ThreadsPerCore+1
// See PPR for AMD Family 17h Models 00h-0Fh (page 82)
fam, _, _ := familyModel()
_, _, _, d := cpuid(1)
if (d&(1<<28)) != 0 && fam >= 23 {
if maxExtendedFunction() >= 0x8000001e {
_, b, _, _ := cpuid(0x8000001e)
return int((b>>8)&0xff) + 1
}
return 2
}
}
@ -877,7 +901,9 @@ var vendorMapping = map[string]Vendor{
"GenuineTMx86": Transmeta,
"Geode by NSC": NSC,
"VIA VIA VIA ": VIA,
"KVMKVMKVMKVM": KVM,
"KVMKVMKVM": KVM,
"Linux KVM Hv": KVM,
"TCGTCGTCGTCG": QEMU,
"Microsoft Hv": MSVM,
"VMwareVMware": VMware,
"XenVMMXenVMM": XenHVM,
@ -887,6 +913,10 @@ var vendorMapping = map[string]Vendor{
"SiS SiS SiS ": SiS,
"RiseRiseRise": SiS,
"Genuine RDC": RDC,
"QNXQVMBSQG": QNX,
"ACRNACRNACRN": ACRN,
"SRESRESRESRE": SRE,
"Apple VZ": Apple,
}
func vendorID() (Vendor, string) {
@ -899,6 +929,17 @@ func vendorID() (Vendor, string) {
return vend, v
}
func hypervisorVendorID() (Vendor, string) {
// https://lwn.net/Articles/301888/
_, b, c, d := cpuid(0x40000000)
v := string(valAsString(b, c, d))
vend, ok := vendorMapping[v]
if !ok {
return VendorUnknown, v
}
return vend, v
}
func cacheLine() int {
if maxFunctionID() < 0x1 {
return 0
@ -1271,6 +1312,7 @@ func support() flagSet {
fs.setIf(ebx&(1<<31) != 0, AVX512VL)
// ecx
fs.setIf(ecx&(1<<1) != 0, AVX512VBMI)
fs.setIf(ecx&(1<<3) != 0, AMXFP8)
fs.setIf(ecx&(1<<6) != 0, AVX512VBMI2)
fs.setIf(ecx&(1<<11) != 0, AVX512VNNI)
fs.setIf(ecx&(1<<12) != 0, AVX512BITALG)

View File

@ -24,3 +24,13 @@ TEXT ·getInstAttributes(SB), 7, $0
MOVD R1, instAttrReg1+8(FP)
RET
TEXT ·getVectorLength(SB), 7, $0
WORD $0xd2800002 // mov x2, #0
WORD $0x04225022 // addvl x2, x2, #1
WORD $0xd37df042 // lsl x2, x2, #3
WORD $0xd2800003 // mov x3, #0
WORD $0x04635023 // addpl x3, x3, #1
WORD $0xd37df063 // lsl x3, x3, #3
MOVD R2, vl+0(FP)
MOVD R3, pl+8(FP)
RET

View File

@ -10,6 +10,7 @@ import "runtime"
func getMidr() (midr uint64)
func getProcFeatures() (procFeatures uint64)
func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
func getVectorLength() (vl, pl uint64)
func initCPU() {
cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
@ -24,7 +25,7 @@ func addInfo(c *CPUInfo, safe bool) {
detectOS(c)
// ARM64 disabled since it may crash if interrupt is not intercepted by OS.
if safe && !c.Supports(ARMCPUID) && runtime.GOOS != "freebsd" {
if safe && !c.Has(ARMCPUID) && runtime.GOOS != "freebsd" {
return
}
midr := getMidr()

View File

@ -10,6 +10,8 @@ func initCPU() {
cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
}
func addInfo(info *CPUInfo, safe bool) {}
func getVectorLength() (vl, pl uint64) { return 0, 0 }

View File

@ -32,7 +32,10 @@ func addInfo(c *CPUInfo, safe bool) {
c.LogicalCores = logicalCores()
c.PhysicalCores = physicalCores()
c.VendorID, c.VendorString = vendorID()
c.HypervisorVendorID, c.HypervisorVendorString = hypervisorVendorID()
c.AVX10Level = c.supportAVX10()
c.cacheSize()
c.frequencies()
}
func getVectorLength() (vl, pl uint64) { return 0, 0 }

View File

@ -15,224 +15,225 @@ func _() {
_ = x[AMXBF16-5]
_ = x[AMXFP16-6]
_ = x[AMXINT8-7]
_ = x[AMXTILE-8]
_ = x[APX_F-9]
_ = x[AVX-10]
_ = x[AVX10-11]
_ = x[AVX10_128-12]
_ = x[AVX10_256-13]
_ = x[AVX10_512-14]
_ = x[AVX2-15]
_ = x[AVX512BF16-16]
_ = x[AVX512BITALG-17]
_ = x[AVX512BW-18]
_ = x[AVX512CD-19]
_ = x[AVX512DQ-20]
_ = x[AVX512ER-21]
_ = x[AVX512F-22]
_ = x[AVX512FP16-23]
_ = x[AVX512IFMA-24]
_ = x[AVX512PF-25]
_ = x[AVX512VBMI-26]
_ = x[AVX512VBMI2-27]
_ = x[AVX512VL-28]
_ = x[AVX512VNNI-29]
_ = x[AVX512VP2INTERSECT-30]
_ = x[AVX512VPOPCNTDQ-31]
_ = x[AVXIFMA-32]
_ = x[AVXNECONVERT-33]
_ = x[AVXSLOW-34]
_ = x[AVXVNNI-35]
_ = x[AVXVNNIINT8-36]
_ = x[AVXVNNIINT16-37]
_ = x[BHI_CTRL-38]
_ = x[BMI1-39]
_ = x[BMI2-40]
_ = x[CETIBT-41]
_ = x[CETSS-42]
_ = x[CLDEMOTE-43]
_ = x[CLMUL-44]
_ = x[CLZERO-45]
_ = x[CMOV-46]
_ = x[CMPCCXADD-47]
_ = x[CMPSB_SCADBS_SHORT-48]
_ = x[CMPXCHG8-49]
_ = x[CPBOOST-50]
_ = x[CPPC-51]
_ = x[CX16-52]
_ = x[EFER_LMSLE_UNS-53]
_ = x[ENQCMD-54]
_ = x[ERMS-55]
_ = x[F16C-56]
_ = x[FLUSH_L1D-57]
_ = x[FMA3-58]
_ = x[FMA4-59]
_ = x[FP128-60]
_ = x[FP256-61]
_ = x[FSRM-62]
_ = x[FXSR-63]
_ = x[FXSROPT-64]
_ = x[GFNI-65]
_ = x[HLE-66]
_ = x[HRESET-67]
_ = x[HTT-68]
_ = x[HWA-69]
_ = x[HYBRID_CPU-70]
_ = x[HYPERVISOR-71]
_ = x[IA32_ARCH_CAP-72]
_ = x[IA32_CORE_CAP-73]
_ = x[IBPB-74]
_ = x[IBPB_BRTYPE-75]
_ = x[IBRS-76]
_ = x[IBRS_PREFERRED-77]
_ = x[IBRS_PROVIDES_SMP-78]
_ = x[IBS-79]
_ = x[IBSBRNTRGT-80]
_ = x[IBSFETCHSAM-81]
_ = x[IBSFFV-82]
_ = x[IBSOPCNT-83]
_ = x[IBSOPCNTEXT-84]
_ = x[IBSOPSAM-85]
_ = x[IBSRDWROPCNT-86]
_ = x[IBSRIPINVALIDCHK-87]
_ = x[IBS_FETCH_CTLX-88]
_ = x[IBS_OPDATA4-89]
_ = x[IBS_OPFUSE-90]
_ = x[IBS_PREVENTHOST-91]
_ = x[IBS_ZEN4-92]
_ = x[IDPRED_CTRL-93]
_ = x[INT_WBINVD-94]
_ = x[INVLPGB-95]
_ = x[KEYLOCKER-96]
_ = x[KEYLOCKERW-97]
_ = x[LAHF-98]
_ = x[LAM-99]
_ = x[LBRVIRT-100]
_ = x[LZCNT-101]
_ = x[MCAOVERFLOW-102]
_ = x[MCDT_NO-103]
_ = x[MCOMMIT-104]
_ = x[MD_CLEAR-105]
_ = x[MMX-106]
_ = x[MMXEXT-107]
_ = x[MOVBE-108]
_ = x[MOVDIR64B-109]
_ = x[MOVDIRI-110]
_ = x[MOVSB_ZL-111]
_ = x[MOVU-112]
_ = x[MPX-113]
_ = x[MSRIRC-114]
_ = x[MSRLIST-115]
_ = x[MSR_PAGEFLUSH-116]
_ = x[NRIPS-117]
_ = x[NX-118]
_ = x[OSXSAVE-119]
_ = x[PCONFIG-120]
_ = x[POPCNT-121]
_ = x[PPIN-122]
_ = x[PREFETCHI-123]
_ = x[PSFD-124]
_ = x[RDPRU-125]
_ = x[RDRAND-126]
_ = x[RDSEED-127]
_ = x[RDTSCP-128]
_ = x[RRSBA_CTRL-129]
_ = x[RTM-130]
_ = x[RTM_ALWAYS_ABORT-131]
_ = x[SBPB-132]
_ = x[SERIALIZE-133]
_ = x[SEV-134]
_ = x[SEV_64BIT-135]
_ = x[SEV_ALTERNATIVE-136]
_ = x[SEV_DEBUGSWAP-137]
_ = x[SEV_ES-138]
_ = x[SEV_RESTRICTED-139]
_ = x[SEV_SNP-140]
_ = x[SGX-141]
_ = x[SGXLC-142]
_ = x[SHA-143]
_ = x[SME-144]
_ = x[SME_COHERENT-145]
_ = x[SPEC_CTRL_SSBD-146]
_ = x[SRBDS_CTRL-147]
_ = x[SRSO_MSR_FIX-148]
_ = x[SRSO_NO-149]
_ = x[SRSO_USER_KERNEL_NO-150]
_ = x[SSE-151]
_ = x[SSE2-152]
_ = x[SSE3-153]
_ = x[SSE4-154]
_ = x[SSE42-155]
_ = x[SSE4A-156]
_ = x[SSSE3-157]
_ = x[STIBP-158]
_ = x[STIBP_ALWAYSON-159]
_ = x[STOSB_SHORT-160]
_ = x[SUCCOR-161]
_ = x[SVM-162]
_ = x[SVMDA-163]
_ = x[SVMFBASID-164]
_ = x[SVML-165]
_ = x[SVMNP-166]
_ = x[SVMPF-167]
_ = x[SVMPFT-168]
_ = x[SYSCALL-169]
_ = x[SYSEE-170]
_ = x[TBM-171]
_ = x[TDX_GUEST-172]
_ = x[TLB_FLUSH_NESTED-173]
_ = x[TME-174]
_ = x[TOPEXT-175]
_ = x[TSCRATEMSR-176]
_ = x[TSXLDTRK-177]
_ = x[VAES-178]
_ = x[VMCBCLEAN-179]
_ = x[VMPL-180]
_ = x[VMSA_REGPROT-181]
_ = x[VMX-182]
_ = x[VPCLMULQDQ-183]
_ = x[VTE-184]
_ = x[WAITPKG-185]
_ = x[WBNOINVD-186]
_ = x[WRMSRNS-187]
_ = x[X87-188]
_ = x[XGETBV1-189]
_ = x[XOP-190]
_ = x[XSAVE-191]
_ = x[XSAVEC-192]
_ = x[XSAVEOPT-193]
_ = x[XSAVES-194]
_ = x[AESARM-195]
_ = x[ARMCPUID-196]
_ = x[ASIMD-197]
_ = x[ASIMDDP-198]
_ = x[ASIMDHP-199]
_ = x[ASIMDRDM-200]
_ = x[ATOMICS-201]
_ = x[CRC32-202]
_ = x[DCPOP-203]
_ = x[EVTSTRM-204]
_ = x[FCMA-205]
_ = x[FP-206]
_ = x[FPHP-207]
_ = x[GPA-208]
_ = x[JSCVT-209]
_ = x[LRCPC-210]
_ = x[PMULL-211]
_ = x[SHA1-212]
_ = x[SHA2-213]
_ = x[SHA3-214]
_ = x[SHA512-215]
_ = x[SM3-216]
_ = x[SM4-217]
_ = x[SVE-218]
_ = x[lastID-219]
_ = x[AMXFP8-8]
_ = x[AMXTILE-9]
_ = x[APX_F-10]
_ = x[AVX-11]
_ = x[AVX10-12]
_ = x[AVX10_128-13]
_ = x[AVX10_256-14]
_ = x[AVX10_512-15]
_ = x[AVX2-16]
_ = x[AVX512BF16-17]
_ = x[AVX512BITALG-18]
_ = x[AVX512BW-19]
_ = x[AVX512CD-20]
_ = x[AVX512DQ-21]
_ = x[AVX512ER-22]
_ = x[AVX512F-23]
_ = x[AVX512FP16-24]
_ = x[AVX512IFMA-25]
_ = x[AVX512PF-26]
_ = x[AVX512VBMI-27]
_ = x[AVX512VBMI2-28]
_ = x[AVX512VL-29]
_ = x[AVX512VNNI-30]
_ = x[AVX512VP2INTERSECT-31]
_ = x[AVX512VPOPCNTDQ-32]
_ = x[AVXIFMA-33]
_ = x[AVXNECONVERT-34]
_ = x[AVXSLOW-35]
_ = x[AVXVNNI-36]
_ = x[AVXVNNIINT8-37]
_ = x[AVXVNNIINT16-38]
_ = x[BHI_CTRL-39]
_ = x[BMI1-40]
_ = x[BMI2-41]
_ = x[CETIBT-42]
_ = x[CETSS-43]
_ = x[CLDEMOTE-44]
_ = x[CLMUL-45]
_ = x[CLZERO-46]
_ = x[CMOV-47]
_ = x[CMPCCXADD-48]
_ = x[CMPSB_SCADBS_SHORT-49]
_ = x[CMPXCHG8-50]
_ = x[CPBOOST-51]
_ = x[CPPC-52]
_ = x[CX16-53]
_ = x[EFER_LMSLE_UNS-54]
_ = x[ENQCMD-55]
_ = x[ERMS-56]
_ = x[F16C-57]
_ = x[FLUSH_L1D-58]
_ = x[FMA3-59]
_ = x[FMA4-60]
_ = x[FP128-61]
_ = x[FP256-62]
_ = x[FSRM-63]
_ = x[FXSR-64]
_ = x[FXSROPT-65]
_ = x[GFNI-66]
_ = x[HLE-67]
_ = x[HRESET-68]
_ = x[HTT-69]
_ = x[HWA-70]
_ = x[HYBRID_CPU-71]
_ = x[HYPERVISOR-72]
_ = x[IA32_ARCH_CAP-73]
_ = x[IA32_CORE_CAP-74]
_ = x[IBPB-75]
_ = x[IBPB_BRTYPE-76]
_ = x[IBRS-77]
_ = x[IBRS_PREFERRED-78]
_ = x[IBRS_PROVIDES_SMP-79]
_ = x[IBS-80]
_ = x[IBSBRNTRGT-81]
_ = x[IBSFETCHSAM-82]
_ = x[IBSFFV-83]
_ = x[IBSOPCNT-84]
_ = x[IBSOPCNTEXT-85]
_ = x[IBSOPSAM-86]
_ = x[IBSRDWROPCNT-87]
_ = x[IBSRIPINVALIDCHK-88]
_ = x[IBS_FETCH_CTLX-89]
_ = x[IBS_OPDATA4-90]
_ = x[IBS_OPFUSE-91]
_ = x[IBS_PREVENTHOST-92]
_ = x[IBS_ZEN4-93]
_ = x[IDPRED_CTRL-94]
_ = x[INT_WBINVD-95]
_ = x[INVLPGB-96]
_ = x[KEYLOCKER-97]
_ = x[KEYLOCKERW-98]
_ = x[LAHF-99]
_ = x[LAM-100]
_ = x[LBRVIRT-101]
_ = x[LZCNT-102]
_ = x[MCAOVERFLOW-103]
_ = x[MCDT_NO-104]
_ = x[MCOMMIT-105]
_ = x[MD_CLEAR-106]
_ = x[MMX-107]
_ = x[MMXEXT-108]
_ = x[MOVBE-109]
_ = x[MOVDIR64B-110]
_ = x[MOVDIRI-111]
_ = x[MOVSB_ZL-112]
_ = x[MOVU-113]
_ = x[MPX-114]
_ = x[MSRIRC-115]
_ = x[MSRLIST-116]
_ = x[MSR_PAGEFLUSH-117]
_ = x[NRIPS-118]
_ = x[NX-119]
_ = x[OSXSAVE-120]
_ = x[PCONFIG-121]
_ = x[POPCNT-122]
_ = x[PPIN-123]
_ = x[PREFETCHI-124]
_ = x[PSFD-125]
_ = x[RDPRU-126]
_ = x[RDRAND-127]
_ = x[RDSEED-128]
_ = x[RDTSCP-129]
_ = x[RRSBA_CTRL-130]
_ = x[RTM-131]
_ = x[RTM_ALWAYS_ABORT-132]
_ = x[SBPB-133]
_ = x[SERIALIZE-134]
_ = x[SEV-135]
_ = x[SEV_64BIT-136]
_ = x[SEV_ALTERNATIVE-137]
_ = x[SEV_DEBUGSWAP-138]
_ = x[SEV_ES-139]
_ = x[SEV_RESTRICTED-140]
_ = x[SEV_SNP-141]
_ = x[SGX-142]
_ = x[SGXLC-143]
_ = x[SHA-144]
_ = x[SME-145]
_ = x[SME_COHERENT-146]
_ = x[SPEC_CTRL_SSBD-147]
_ = x[SRBDS_CTRL-148]
_ = x[SRSO_MSR_FIX-149]
_ = x[SRSO_NO-150]
_ = x[SRSO_USER_KERNEL_NO-151]
_ = x[SSE-152]
_ = x[SSE2-153]
_ = x[SSE3-154]
_ = x[SSE4-155]
_ = x[SSE42-156]
_ = x[SSE4A-157]
_ = x[SSSE3-158]
_ = x[STIBP-159]
_ = x[STIBP_ALWAYSON-160]
_ = x[STOSB_SHORT-161]
_ = x[SUCCOR-162]
_ = x[SVM-163]
_ = x[SVMDA-164]
_ = x[SVMFBASID-165]
_ = x[SVML-166]
_ = x[SVMNP-167]
_ = x[SVMPF-168]
_ = x[SVMPFT-169]
_ = x[SYSCALL-170]
_ = x[SYSEE-171]
_ = x[TBM-172]
_ = x[TDX_GUEST-173]
_ = x[TLB_FLUSH_NESTED-174]
_ = x[TME-175]
_ = x[TOPEXT-176]
_ = x[TSCRATEMSR-177]
_ = x[TSXLDTRK-178]
_ = x[VAES-179]
_ = x[VMCBCLEAN-180]
_ = x[VMPL-181]
_ = x[VMSA_REGPROT-182]
_ = x[VMX-183]
_ = x[VPCLMULQDQ-184]
_ = x[VTE-185]
_ = x[WAITPKG-186]
_ = x[WBNOINVD-187]
_ = x[WRMSRNS-188]
_ = x[X87-189]
_ = x[XGETBV1-190]
_ = x[XOP-191]
_ = x[XSAVE-192]
_ = x[XSAVEC-193]
_ = x[XSAVEOPT-194]
_ = x[XSAVES-195]
_ = x[AESARM-196]
_ = x[ARMCPUID-197]
_ = x[ASIMD-198]
_ = x[ASIMDDP-199]
_ = x[ASIMDHP-200]
_ = x[ASIMDRDM-201]
_ = x[ATOMICS-202]
_ = x[CRC32-203]
_ = x[DCPOP-204]
_ = x[EVTSTRM-205]
_ = x[FCMA-206]
_ = x[FP-207]
_ = x[FPHP-208]
_ = x[GPA-209]
_ = x[JSCVT-210]
_ = x[LRCPC-211]
_ = x[PMULL-212]
_ = x[SHA1-213]
_ = x[SHA2-214]
_ = x[SHA3-215]
_ = x[SHA512-216]
_ = x[SM3-217]
_ = x[SM4-218]
_ = x[SVE-219]
_ = x[lastID-220]
_ = x[firstID-0]
}
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXTILEAPX_FAVXAVX10AVX10_128AVX10_256AVX10_512AVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8AVXVNNIINT16BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBPB_BRTYPEIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBKEYLOCKERKEYLOCKERWLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSBPBSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSPEC_CTRL_SSBDSRBDS_CTRLSRSO_MSR_FIXSRSO_NOSRSO_USER_KERNEL_NOSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXFP8AMXTILEAPX_FAVXAVX10AVX10_128AVX10_256AVX10_512AVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8AVXVNNIINT16BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBPB_BRTYPEIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBKEYLOCKERKEYLOCKERWLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSBPBSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSPEC_CTRL_SSBDSRBDS_CTRLSRSO_MSR_FIXSRSO_NOSRSO_USER_KERNEL_NOSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 62, 67, 70, 75, 84, 93, 102, 106, 116, 128, 136, 144, 152, 160, 167, 177, 187, 195, 205, 216, 224, 234, 252, 267, 274, 286, 293, 300, 311, 323, 331, 335, 339, 345, 350, 358, 363, 369, 373, 382, 400, 408, 415, 419, 423, 437, 443, 447, 451, 460, 464, 468, 473, 478, 482, 486, 493, 497, 500, 506, 509, 512, 522, 532, 545, 558, 562, 573, 577, 591, 608, 611, 621, 632, 638, 646, 657, 665, 677, 693, 707, 718, 728, 743, 751, 762, 772, 779, 788, 798, 802, 805, 812, 817, 828, 835, 842, 850, 853, 859, 864, 873, 880, 888, 892, 895, 901, 908, 921, 926, 928, 935, 942, 948, 952, 961, 965, 970, 976, 982, 988, 998, 1001, 1017, 1021, 1030, 1033, 1042, 1057, 1070, 1076, 1090, 1097, 1100, 1105, 1108, 1111, 1123, 1137, 1147, 1159, 1166, 1185, 1188, 1192, 1196, 1200, 1205, 1210, 1215, 1220, 1234, 1245, 1251, 1254, 1259, 1268, 1272, 1277, 1282, 1288, 1295, 1300, 1303, 1312, 1328, 1331, 1337, 1347, 1355, 1359, 1368, 1372, 1384, 1387, 1397, 1400, 1407, 1415, 1422, 1425, 1432, 1435, 1440, 1446, 1454, 1460, 1466, 1474, 1479, 1486, 1493, 1501, 1508, 1513, 1518, 1525, 1529, 1531, 1535, 1538, 1543, 1548, 1553, 1557, 1561, 1565, 1571, 1574, 1577, 1580, 1586}
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 61, 68, 73, 76, 81, 90, 99, 108, 112, 122, 134, 142, 150, 158, 166, 173, 183, 193, 201, 211, 222, 230, 240, 258, 273, 280, 292, 299, 306, 317, 329, 337, 341, 345, 351, 356, 364, 369, 375, 379, 388, 406, 414, 421, 425, 429, 443, 449, 453, 457, 466, 470, 474, 479, 484, 488, 492, 499, 503, 506, 512, 515, 518, 528, 538, 551, 564, 568, 579, 583, 597, 614, 617, 627, 638, 644, 652, 663, 671, 683, 699, 713, 724, 734, 749, 757, 768, 778, 785, 794, 804, 808, 811, 818, 823, 834, 841, 848, 856, 859, 865, 870, 879, 886, 894, 898, 901, 907, 914, 927, 932, 934, 941, 948, 954, 958, 967, 971, 976, 982, 988, 994, 1004, 1007, 1023, 1027, 1036, 1039, 1048, 1063, 1076, 1082, 1096, 1103, 1106, 1111, 1114, 1117, 1129, 1143, 1153, 1165, 1172, 1191, 1194, 1198, 1202, 1206, 1211, 1216, 1221, 1226, 1240, 1251, 1257, 1260, 1265, 1274, 1278, 1283, 1288, 1294, 1301, 1306, 1309, 1318, 1334, 1337, 1343, 1353, 1361, 1365, 1374, 1378, 1390, 1393, 1403, 1406, 1413, 1421, 1428, 1431, 1438, 1441, 1446, 1452, 1460, 1466, 1472, 1480, 1485, 1492, 1499, 1507, 1514, 1519, 1524, 1531, 1535, 1537, 1541, 1544, 1549, 1554, 1559, 1563, 1567, 1571, 1577, 1580, 1583, 1586, 1592}
func (i FeatureID) String() string {
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {
@ -270,12 +271,17 @@ func _() {
_ = x[AMCC-23]
_ = x[Qualcomm-24]
_ = x[Marvell-25]
_ = x[lastVendor-26]
_ = x[QEMU-26]
_ = x[QNX-27]
_ = x[ACRN-28]
_ = x[SRE-29]
_ = x[Apple-30]
_ = x[lastVendor-31]
}
const _Vendor_name = "VendorUnknownIntelAMDVIATransmetaNSCKVMMSVMVMwareXenHVMBhyveHygonSiSRDCAmpereARMBroadcomCaviumDECFujitsuInfineonMotorolaNVIDIAAMCCQualcommMarvelllastVendor"
const _Vendor_name = "VendorUnknownIntelAMDVIATransmetaNSCKVMMSVMVMwareXenHVMBhyveHygonSiSRDCAmpereARMBroadcomCaviumDECFujitsuInfineonMotorolaNVIDIAAMCCQualcommMarvellQEMUQNXACRNSREApplelastVendor"
var _Vendor_index = [...]uint8{0, 13, 18, 21, 24, 33, 36, 39, 43, 49, 55, 60, 65, 68, 71, 77, 80, 88, 94, 97, 104, 112, 120, 126, 130, 138, 145, 155}
var _Vendor_index = [...]uint8{0, 13, 18, 21, 24, 33, 36, 39, 43, 49, 55, 60, 65, 68, 71, 77, 80, 88, 94, 97, 104, 112, 120, 126, 130, 138, 145, 149, 152, 156, 159, 164, 174}
func (i Vendor) String() string {
if i < 0 || i >= Vendor(len(_Vendor_index)-1) {

View File

@ -1,11 +1,11 @@
# shortuuid
[![Build Status](https://github.com/lithammer/shortuuid/workflows/CI/badge.svg)](https://github.com/lithammer/shortuuid/actions)
[![Godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/lithammer/shortuuid)
[![Godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://pkg.go.dev/github.com/lithammer/shortuuid/v4)
A Go library that generates concise, unambiguous, URL-safe UUIDs. Based on and
compatible with the Python library
[`shortuuid`](https://github.com/stochastic-technologies/shortuuid).
[`shortuuid`](https://github.com/skorokithakis/shortuuid).
Often, one needs to use non-sequential IDs in places where users will see them,
but the IDs must be as concise and easy to use as possible. shortuuid solves
@ -26,7 +26,8 @@ import (
)
func main() {
u := shortuuid.New() // KwSysDpxcBU9FNhGkn2dCf
u := shortuuid.New()
fmt.Println(u) // KwSysDpxcBU9FNhGkn2dCf
}
```

View File

@ -1,12 +1,11 @@
package shortuuid
import (
"encoding/binary"
"fmt"
"math"
"math/big"
"strings"
"github.com/google/uuid"
"math/bits"
"strings"
)
type base57 struct {
@ -14,78 +13,70 @@ type base57 struct {
alphabet alphabet
}
const (
strLen = 22
alphabetLen = 57
)
// Encode encodes uuid.UUID into a string using the most significant bits (MSB)
// first according to the alphabet.
func (b base57) Encode(u uuid.UUID) string {
var num big.Int
num.SetString(strings.Replace(u.String(), "-", "", 4), 16)
num := uint128{
binary.BigEndian.Uint64(u[8:]),
binary.BigEndian.Uint64(u[:8]),
}
var outIndexes [strLen]uint64
// Calculate encoded length.
length := math.Ceil(math.Log(math.Pow(2, 128)) / math.Log(float64(b.alphabet.Length())))
for i := strLen - 1; num.Hi > 0 || num.Lo > 0; i-- {
num, outIndexes[i] = num.quoRem64(alphabetLen)
}
return b.numToString(&num, int(length))
var sb strings.Builder
sb.Grow(strLen)
for i := 0; i < strLen; i++ {
sb.WriteRune(b.alphabet.chars[outIndexes[i]])
}
return sb.String()
}
// Decode decodes a string according to the alphabet into a uuid.UUID. If s is
// too short, its most significant bits (MSB) will be padded with 0 (zero).
func (b base57) Decode(u string) (uuid.UUID, error) {
str, err := b.stringToNum(u)
if err != nil {
return uuid.Nil, err
}
return uuid.Parse(str)
}
// numToString converts a number a string using the given alphabet.
func (b *base57) numToString(number *big.Int, padToLen int) string {
var (
out []rune
digit *big.Int
)
alphaLen := big.NewInt(b.alphabet.Length())
zero := new(big.Int)
for number.Cmp(zero) > 0 {
number, digit = new(big.Int).DivMod(number, alphaLen, new(big.Int))
out = append(out, b.alphabet.chars[digit.Int64()])
}
if padToLen > 0 {
remainder := math.Max(float64(padToLen-len(out)), 0)
out = append(out, []rune(strings.Repeat(string(b.alphabet.chars[0]), int(remainder)))...)
}
reverse(out)
return string(out)
}
// stringToNum converts a string a number using the given alphabet.
func (b *base57) stringToNum(s string) (string, error) {
n := big.NewInt(0)
func (b base57) Decode(s string) (u uuid.UUID, err error) {
var n uint128
var index int64
for _, char := range s {
n.Mul(n, big.NewInt(b.alphabet.Length()))
index, err := b.alphabet.Index(char)
index, err = b.alphabet.Index(char)
if err != nil {
return "", err
return
}
n, err = n.mulAdd64(alphabetLen, uint64(index))
if err != nil {
return
}
n.Add(n, big.NewInt(index))
}
if n.BitLen() > 128 {
return "", fmt.Errorf("number is out of range (need a 128-bit value)")
}
return fmt.Sprintf("%032x", n), nil
binary.BigEndian.PutUint64(u[:8], n.Hi)
binary.BigEndian.PutUint64(u[8:], n.Lo)
return
}
// reverse reverses a inline.
func reverse(a []rune) {
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
type uint128 struct {
Lo, Hi uint64
}
func (u uint128) quoRem64(v uint64) (q uint128, r uint64) {
q.Hi, r = bits.Div64(0, u.Hi, v)
q.Lo, r = bits.Div64(r, u.Lo, v)
return
}
func (u uint128) mulAdd64(m uint64, a uint64) (uint128, error) {
hi, lo := bits.Mul64(u.Lo, m)
p0, p1 := bits.Mul64(u.Hi, m)
lo, c0 := bits.Add64(lo, a, 0)
hi, c1 := bits.Add64(hi, p1, c0)
if p0 != 0 || c1 != 0 {
return uint128{}, fmt.Errorf("number is out of range (need a 128-bit value)")
}
return uint128{lo, hi}, nil
}

View File

@ -0,0 +1,78 @@
/*
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
* Copyright 2015-2024 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package minio
import (
"bytes"
"context"
"io"
"net/http"
"github.com/goccy/go-json"
"github.com/minio/minio-go/v7/pkg/s3utils"
)
// PromptObject performs language model inference with the prompt and referenced object as context.
// Inference is performed using a Lambda handler that can process the prompt and object.
// Currently, this functionality is limited to certain MinIO servers.
func (c *Client) PromptObject(ctx context.Context, bucketName, objectName, prompt string, opts PromptObjectOptions) (io.ReadCloser, error) {
// Input validation.
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
return nil, ErrorResponse{
StatusCode: http.StatusBadRequest,
Code: "InvalidBucketName",
Message: err.Error(),
}
}
if err := s3utils.CheckValidObjectName(objectName); err != nil {
return nil, ErrorResponse{
StatusCode: http.StatusBadRequest,
Code: "XMinioInvalidObjectName",
Message: err.Error(),
}
}
opts.AddLambdaArnToReqParams(opts.LambdaArn)
opts.SetHeader("Content-Type", "application/json")
opts.AddPromptArg("prompt", prompt)
promptReqBytes, err := json.Marshal(opts.PromptArgs)
if err != nil {
return nil, err
}
// Execute POST on bucket/object.
resp, err := c.executeMethod(ctx, http.MethodPost, requestMetadata{
bucketName: bucketName,
objectName: objectName,
queryValues: opts.toQueryValues(),
customHeader: opts.Header(),
contentSHA256Hex: sum256Hex(promptReqBytes),
contentBody: bytes.NewReader(promptReqBytes),
contentLength: int64(len(promptReqBytes)),
})
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
defer closeResponse(resp)
return nil, httpRespToErrorResponse(resp, bucketName, objectName)
}
return resp.Body, nil
}

View File

@ -0,0 +1,84 @@
/*
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
* Copyright 2015-2024 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package minio
import (
"net/http"
"net/url"
)
// PromptObjectOptions provides options to PromptObject call.
// LambdaArn is the ARN of the Prompt Lambda to be invoked.
// PromptArgs is a map of key-value pairs to be passed to the inference action on the Prompt Lambda.
// "prompt" is a reserved key and should not be used as a key in PromptArgs.
type PromptObjectOptions struct {
LambdaArn string
PromptArgs map[string]any
headers map[string]string
reqParams url.Values
}
// Header returns the http.Header representation of the POST options.
func (o PromptObjectOptions) Header() http.Header {
headers := make(http.Header, len(o.headers))
for k, v := range o.headers {
headers.Set(k, v)
}
return headers
}
// AddPromptArg Add a key value pair to the prompt arguments where the key is a string and
// the value is a JSON serializable.
func (o *PromptObjectOptions) AddPromptArg(key string, value any) {
if o.PromptArgs == nil {
o.PromptArgs = make(map[string]any)
}
o.PromptArgs[key] = value
}
// AddLambdaArnToReqParams adds the lambdaArn to the request query string parameters.
func (o *PromptObjectOptions) AddLambdaArnToReqParams(lambdaArn string) {
if o.reqParams == nil {
o.reqParams = make(url.Values)
}
o.reqParams.Add("lambdaArn", lambdaArn)
}
// SetHeader adds a key value pair to the options. The
// key-value pair will be part of the HTTP POST request
// headers.
func (o *PromptObjectOptions) SetHeader(key, value string) {
if o.headers == nil {
o.headers = make(map[string]string)
}
o.headers[http.CanonicalHeaderKey(key)] = value
}
// toQueryValues - Convert the reqParams in Options to query string parameters.
func (o *PromptObjectOptions) toQueryValues() url.Values {
urlValues := make(url.Values)
if o.reqParams != nil {
for key, values := range o.reqParams {
for _, value := range values {
urlValues.Add(key, value)
}
}
}
return urlValues
}

View File

@ -85,7 +85,10 @@ func (c *Client) PutObjectFanOut(ctx context.Context, bucket string, fanOutData
policy.SetEncryption(fanOutReq.SSE)
// Set checksum headers if any.
policy.SetChecksum(fanOutReq.Checksum)
err := policy.SetChecksum(fanOutReq.Checksum)
if err != nil {
return nil, err
}
url, formData, err := c.PresignedPostPolicy(ctx, policy)
if err != nil {

View File

@ -133,7 +133,7 @@ type Options struct {
// Global constants.
const (
libraryName = "minio-go"
libraryVersion = "v7.0.80"
libraryVersion = "v7.0.81"
)
// User Agent should always following the below style.

File diff suppressed because it is too large Load Diff

View File

@ -58,9 +58,10 @@ type WebIdentityResult struct {
// WebIdentityToken - web identity token with expiry.
type WebIdentityToken struct {
Token string
AccessToken string
Expiry int
Token string
AccessToken string
RefreshToken string
Expiry int
}
// A STSWebIdentity retrieves credentials from MinIO service, and keeps track if

View File

@ -85,7 +85,7 @@ func (p *PostPolicy) SetExpires(t time.Time) error {
// SetKey - Sets an object name for the policy based upload.
func (p *PostPolicy) SetKey(key string) error {
if strings.TrimSpace(key) == "" || key == "" {
if strings.TrimSpace(key) == "" {
return errInvalidArgument("Object name is empty.")
}
policyCond := policyCondition{
@ -118,7 +118,7 @@ func (p *PostPolicy) SetKeyStartsWith(keyStartsWith string) error {
// SetBucket - Sets bucket at which objects will be uploaded to.
func (p *PostPolicy) SetBucket(bucketName string) error {
if strings.TrimSpace(bucketName) == "" || bucketName == "" {
if strings.TrimSpace(bucketName) == "" {
return errInvalidArgument("Bucket name is empty.")
}
policyCond := policyCondition{
@ -135,7 +135,7 @@ func (p *PostPolicy) SetBucket(bucketName string) error {
// SetCondition - Sets condition for credentials, date and algorithm
func (p *PostPolicy) SetCondition(matchType, condition, value string) error {
if strings.TrimSpace(value) == "" || value == "" {
if strings.TrimSpace(value) == "" {
return errInvalidArgument("No value specified for condition")
}
@ -156,7 +156,7 @@ func (p *PostPolicy) SetCondition(matchType, condition, value string) error {
// SetTagging - Sets tagging for the object for this policy based upload.
func (p *PostPolicy) SetTagging(tagging string) error {
if strings.TrimSpace(tagging) == "" || tagging == "" {
if strings.TrimSpace(tagging) == "" {
return errInvalidArgument("No tagging specified.")
}
_, err := tags.ParseObjectXML(strings.NewReader(tagging))
@ -178,7 +178,7 @@ func (p *PostPolicy) SetTagging(tagging string) error {
// SetContentType - Sets content-type of the object for this policy
// based upload.
func (p *PostPolicy) SetContentType(contentType string) error {
if strings.TrimSpace(contentType) == "" || contentType == "" {
if strings.TrimSpace(contentType) == "" {
return errInvalidArgument("No content type specified.")
}
policyCond := policyCondition{
@ -211,7 +211,7 @@ func (p *PostPolicy) SetContentTypeStartsWith(contentTypeStartsWith string) erro
// SetContentDisposition - Sets content-disposition of the object for this policy
func (p *PostPolicy) SetContentDisposition(contentDisposition string) error {
if strings.TrimSpace(contentDisposition) == "" || contentDisposition == "" {
if strings.TrimSpace(contentDisposition) == "" {
return errInvalidArgument("No content disposition specified.")
}
policyCond := policyCondition{
@ -226,27 +226,44 @@ func (p *PostPolicy) SetContentDisposition(contentDisposition string) error {
return nil
}
// SetContentEncoding - Sets content-encoding of the object for this policy
func (p *PostPolicy) SetContentEncoding(contentEncoding string) error {
if strings.TrimSpace(contentEncoding) == "" {
return errInvalidArgument("No content encoding specified.")
}
policyCond := policyCondition{
matchType: "eq",
condition: "$Content-Encoding",
value: contentEncoding,
}
if err := p.addNewPolicy(policyCond); err != nil {
return err
}
p.formData["Content-Encoding"] = contentEncoding
return nil
}
// SetContentLengthRange - Set new min and max content length
// condition for all incoming uploads.
func (p *PostPolicy) SetContentLengthRange(min, max int64) error {
if min > max {
func (p *PostPolicy) SetContentLengthRange(minLen, maxLen int64) error {
if minLen > maxLen {
return errInvalidArgument("Minimum limit is larger than maximum limit.")
}
if min < 0 {
if minLen < 0 {
return errInvalidArgument("Minimum limit cannot be negative.")
}
if max <= 0 {
if maxLen <= 0 {
return errInvalidArgument("Maximum limit cannot be non-positive.")
}
p.contentLengthRange.min = min
p.contentLengthRange.max = max
p.contentLengthRange.min = minLen
p.contentLengthRange.max = maxLen
return nil
}
// SetSuccessActionRedirect - Sets the redirect success url of the object for this policy
// based upload.
func (p *PostPolicy) SetSuccessActionRedirect(redirect string) error {
if strings.TrimSpace(redirect) == "" || redirect == "" {
if strings.TrimSpace(redirect) == "" {
return errInvalidArgument("Redirect is empty")
}
policyCond := policyCondition{
@ -264,7 +281,7 @@ func (p *PostPolicy) SetSuccessActionRedirect(redirect string) error {
// SetSuccessStatusAction - Sets the status success code of the object for this policy
// based upload.
func (p *PostPolicy) SetSuccessStatusAction(status string) error {
if strings.TrimSpace(status) == "" || status == "" {
if strings.TrimSpace(status) == "" {
return errInvalidArgument("Status is empty")
}
policyCond := policyCondition{
@ -282,10 +299,10 @@ func (p *PostPolicy) SetSuccessStatusAction(status string) error {
// SetUserMetadata - Set user metadata as a key/value couple.
// Can be retrieved through a HEAD request or an event.
func (p *PostPolicy) SetUserMetadata(key, value string) error {
if strings.TrimSpace(key) == "" || key == "" {
if strings.TrimSpace(key) == "" {
return errInvalidArgument("Key is empty")
}
if strings.TrimSpace(value) == "" || value == "" {
if strings.TrimSpace(value) == "" {
return errInvalidArgument("Value is empty")
}
headerName := fmt.Sprintf("x-amz-meta-%s", key)
@ -304,7 +321,7 @@ func (p *PostPolicy) SetUserMetadata(key, value string) error {
// SetUserMetadataStartsWith - Set how an user metadata should starts with.
// Can be retrieved through a HEAD request or an event.
func (p *PostPolicy) SetUserMetadataStartsWith(key, value string) error {
if strings.TrimSpace(key) == "" || key == "" {
if strings.TrimSpace(key) == "" {
return errInvalidArgument("Key is empty")
}
headerName := fmt.Sprintf("x-amz-meta-%s", key)
@ -321,11 +338,29 @@ func (p *PostPolicy) SetUserMetadataStartsWith(key, value string) error {
}
// SetChecksum sets the checksum of the request.
func (p *PostPolicy) SetChecksum(c Checksum) {
func (p *PostPolicy) SetChecksum(c Checksum) error {
if c.IsSet() {
p.formData[amzChecksumAlgo] = c.Type.String()
p.formData[c.Type.Key()] = c.Encoded()
policyCond := policyCondition{
matchType: "eq",
condition: fmt.Sprintf("$%s", amzChecksumAlgo),
value: c.Type.String(),
}
if err := p.addNewPolicy(policyCond); err != nil {
return err
}
policyCond = policyCondition{
matchType: "eq",
condition: fmt.Sprintf("$%s", c.Type.Key()),
value: c.Encoded(),
}
if err := p.addNewPolicy(policyCond); err != nil {
return err
}
}
return nil
}
// SetEncryption - sets encryption headers for POST API

View File

@ -20,7 +20,7 @@ package minio
import "time"
// newRetryTimerContinous creates a timer with exponentially increasing delays forever.
func (c *Client) newRetryTimerContinous(unit, cap time.Duration, jitter float64, doneCh chan struct{}) <-chan int {
func (c *Client) newRetryTimerContinous(baseSleep, maxSleep time.Duration, jitter float64, doneCh chan struct{}) <-chan int {
attemptCh := make(chan int)
// normalize jitter to the range [0, 1.0]
@ -39,10 +39,10 @@ func (c *Client) newRetryTimerContinous(unit, cap time.Duration, jitter float64,
if attempt > maxAttempt {
attempt = maxAttempt
}
// sleep = random_between(0, min(cap, base * 2 ** attempt))
sleep := unit * time.Duration(1<<uint(attempt))
if sleep > cap {
sleep = cap
// sleep = random_between(0, min(maxSleep, base * 2 ** attempt))
sleep := baseSleep * time.Duration(1<<uint(attempt))
if sleep > maxSleep {
sleep = maxSleep
}
if jitter != NoJitter {
sleep -= time.Duration(c.random.Float64() * float64(sleep) * jitter)

View File

@ -45,7 +45,7 @@ var DefaultRetryCap = time.Second
// newRetryTimer creates a timer with exponentially increasing
// delays until the maximum retry attempts are reached.
func (c *Client) newRetryTimer(ctx context.Context, maxRetry int, unit, cap time.Duration, jitter float64) <-chan int {
func (c *Client) newRetryTimer(ctx context.Context, maxRetry int, baseSleep, maxSleep time.Duration, jitter float64) <-chan int {
attemptCh := make(chan int)
// computes the exponential backoff duration according to
@ -59,10 +59,10 @@ func (c *Client) newRetryTimer(ctx context.Context, maxRetry int, unit, cap time
jitter = MaxJitter
}
// sleep = random_between(0, min(cap, base * 2 ** attempt))
sleep := unit * time.Duration(1<<uint(attempt))
if sleep > cap {
sleep = cap
// sleep = random_between(0, min(maxSleep, base * 2 ** attempt))
sleep := baseSleep * time.Duration(1<<uint(attempt))
if sleep > maxSleep {
sleep = maxSleep
}
if jitter != NoJitter {
sleep -= time.Duration(c.random.Float64() * float64(sleep) * jitter)

View File

@ -1,46 +0,0 @@
# mapstructure [![Godoc](https://godoc.org/github.com/mitchellh/mapstructure?status.svg)](https://godoc.org/github.com/mitchellh/mapstructure)
mapstructure is a Go library for decoding generic map values to structures
and vice versa, while providing helpful error handling.
This library is most useful when decoding values from some data stream (JSON,
Gob, etc.) where you don't _quite_ know the structure of the underlying data
until you read a part of it. You can therefore read a `map[string]interface{}`
and use this library to decode it into the proper underlying native Go
structure.
## Installation
Standard `go get`:
```
$ go get github.com/mitchellh/mapstructure
```
## Usage & Example
For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure).
The `Decode` function has examples associated with it there.
## But Why?!
Go offers fantastic standard libraries for decoding formats such as JSON.
The standard method is to have a struct pre-created, and populate that struct
from the bytes of the encoded format. This is great, but the problem is if
you have configuration or an encoding that changes slightly depending on
specific fields. For example, consider this JSON:
```json
{
"type": "person",
"name": "Mitchell"
}
```
Perhaps we can't populate a specific structure without first reading
the "type" field from the JSON. We could always do two passes over the
decoding of the JSON (reading the "type" first, and the rest later).
However, it is much simpler to just decode this into a `map[string]interface{}`
structure, read the "type" key, then use something like this library
to decode it into the proper structure.

View File

@ -1,279 +0,0 @@
package mapstructure
import (
"encoding"
"errors"
"fmt"
"net"
"reflect"
"strconv"
"strings"
"time"
)
// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// Create variables here so we can reference them with the reflect pkg
var f1 DecodeHookFuncType
var f2 DecodeHookFuncKind
var f3 DecodeHookFuncValue
// Fill in the variables into this interface and the rest is done
// automatically using the reflect package.
potential := []interface{}{f1, f2, f3}
v := reflect.ValueOf(h)
vt := v.Type()
for _, raw := range potential {
pt := reflect.ValueOf(raw).Type()
if vt.ConvertibleTo(pt) {
return v.Convert(pt).Interface()
}
}
return nil
}
// DecodeHookExec executes the given decode hook. This should be used
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
// that took reflect.Kind instead of reflect.Type.
func DecodeHookExec(
raw DecodeHookFunc,
from reflect.Value, to reflect.Value) (interface{}, error) {
switch f := typedDecodeHook(raw).(type) {
case DecodeHookFuncType:
return f(from.Type(), to.Type(), from.Interface())
case DecodeHookFuncKind:
return f(from.Kind(), to.Kind(), from.Interface())
case DecodeHookFuncValue:
return f(from, to)
default:
return nil, errors.New("invalid decode hook signature")
}
}
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
// automatically composes multiple DecodeHookFuncs.
//
// The composed funcs are called in order, with the result of the
// previous transformation.
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
var err error
data := f.Interface()
newFrom := f
for _, f1 := range fs {
data, err = DecodeHookExec(f1, newFrom, t)
if err != nil {
return nil, err
}
newFrom = reflect.ValueOf(data)
}
return data, nil
}
}
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
return func(a, b reflect.Value) (interface{}, error) {
var allErrs string
var out interface{}
var err error
for _, f := range ff {
out, err = DecodeHookExec(f, a, b)
if err != nil {
allErrs += err.Error() + "\n"
continue
}
return out, nil
}
return nil, errors.New(allErrs)
}
}
// StringToSliceHookFunc returns a DecodeHookFunc that converts
// string to []string by splitting on the given sep.
func StringToSliceHookFunc(sep string) DecodeHookFunc {
return func(
f reflect.Kind,
t reflect.Kind,
data interface{}) (interface{}, error) {
if f != reflect.String || t != reflect.Slice {
return data, nil
}
raw := data.(string)
if raw == "" {
return []string{}, nil
}
return strings.Split(raw, sep), nil
}
}
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
// strings to time.Duration.
func StringToTimeDurationHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(time.Duration(5)) {
return data, nil
}
// Convert it by parsing
return time.ParseDuration(data.(string))
}
}
// StringToIPHookFunc returns a DecodeHookFunc that converts
// strings to net.IP
func StringToIPHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(net.IP{}) {
return data, nil
}
// Convert it by parsing
ip := net.ParseIP(data.(string))
if ip == nil {
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
}
return ip, nil
}
}
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
// strings to net.IPNet
func StringToIPNetHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(net.IPNet{}) {
return data, nil
}
// Convert it by parsing
_, net, err := net.ParseCIDR(data.(string))
return net, err
}
}
// StringToTimeHookFunc returns a DecodeHookFunc that converts
// strings to time.Time.
func StringToTimeHookFunc(layout string) DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(time.Time{}) {
return data, nil
}
// Convert it by parsing
return time.Parse(layout, data.(string))
}
}
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
// the decoder.
//
// Note that this is significantly different from the WeaklyTypedInput option
// of the DecoderConfig.
func WeaklyTypedHook(
f reflect.Kind,
t reflect.Kind,
data interface{}) (interface{}, error) {
dataVal := reflect.ValueOf(data)
switch t {
case reflect.String:
switch f {
case reflect.Bool:
if dataVal.Bool() {
return "1", nil
}
return "0", nil
case reflect.Float32:
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
case reflect.Int:
return strconv.FormatInt(dataVal.Int(), 10), nil
case reflect.Slice:
dataType := dataVal.Type()
elemKind := dataType.Elem().Kind()
if elemKind == reflect.Uint8 {
return string(dataVal.Interface().([]uint8)), nil
}
case reflect.Uint:
return strconv.FormatUint(dataVal.Uint(), 10), nil
}
}
return data, nil
}
func RecursiveStructToMapHookFunc() DecodeHookFunc {
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
if f.Kind() != reflect.Struct {
return f.Interface(), nil
}
var i interface{} = struct{}{}
if t.Type() != reflect.TypeOf(&i).Elem() {
return f.Interface(), nil
}
m := make(map[string]interface{})
t.Set(reflect.ValueOf(m))
return f.Interface(), nil
}
}
// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
// strings to the UnmarshalText function, when the target type
// implements the encoding.TextUnmarshaler interface
func TextUnmarshallerHookFunc() DecodeHookFuncType {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
result := reflect.New(t).Interface()
unmarshaller, ok := result.(encoding.TextUnmarshaler)
if !ok {
return data, nil
}
if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
return nil, err
}
return result, nil
}
}

View File

@ -1,50 +0,0 @@
package mapstructure
import (
"errors"
"fmt"
"sort"
"strings"
)
// Error implements the error interface and can represents multiple
// errors that occur in the course of a single decode.
type Error struct {
Errors []string
}
func (e *Error) Error() string {
points := make([]string, len(e.Errors))
for i, err := range e.Errors {
points[i] = fmt.Sprintf("* %s", err)
}
sort.Strings(points)
return fmt.Sprintf(
"%d error(s) decoding:\n\n%s",
len(e.Errors), strings.Join(points, "\n"))
}
// WrappedErrors implements the errwrap.Wrapper interface to make this
// return value more useful with the errwrap and go-multierror libraries.
func (e *Error) WrappedErrors() []error {
if e == nil {
return nil
}
result := make([]error, len(e.Errors))
for i, e := range e.Errors {
result[i] = errors.New(e)
}
return result
}
func appendErrors(errors []string, err error) []string {
switch e := err.(type) {
case *Error:
return append(errors, e.Errors...)
default:
return append(errors, e.Error())
}
}

View File

@ -7,10 +7,13 @@ import (
"time"
)
type CompareType int
// Deprecated: CompareType has only ever been for internal use and has accidentally been published since v1.6.0. Do not use it.
type CompareType = compareResult
type compareResult int
const (
compareLess CompareType = iota - 1
compareLess compareResult = iota - 1
compareEqual
compareGreater
)
@ -39,7 +42,7 @@ var (
bytesType = reflect.TypeOf([]byte{})
)
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
func compare(obj1, obj2 interface{}, kind reflect.Kind) (compareResult, bool) {
obj1Value := reflect.ValueOf(obj1)
obj2Value := reflect.ValueOf(obj2)
@ -325,7 +328,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time)
}
return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64)
if timeObj1.Before(timeObj2) {
return compareLess, true
}
if timeObj1.Equal(timeObj2) {
return compareEqual, true
}
return compareGreater, true
}
case reflect.Slice:
{
@ -345,7 +354,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte)
}
return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true
return compareResult(bytes.Compare(bytesObj1, bytesObj2)), true
}
case reflect.Uintptr:
{
@ -381,7 +390,7 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
}
// GreaterOrEqual asserts that the first element is greater than or equal to the second
@ -394,7 +403,7 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
}
// Less asserts that the first element is less than the second
@ -406,7 +415,7 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
}
// LessOrEqual asserts that the first element is less than or equal to the second
@ -419,7 +428,7 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
if h, ok := t.(tHelper); ok {
h.Helper()
}
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
}
// Positive asserts that the specified element is positive
@ -431,7 +440,7 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...)
return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...)
}
// Negative asserts that the specified element is negative
@ -443,10 +452,10 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...)
return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...)
}
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
@ -469,7 +478,7 @@ func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedCompare
return true
}
func containsValue(values []CompareType, value CompareType) bool {
func containsValue(values []compareResult, value compareResult) bool {
for _, v := range values {
if v == value {
return true

View File

@ -104,8 +104,8 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{},
return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// EqualValuesf asserts that two objects are equal or convertible to the same types
// and equal.
// EqualValuesf asserts that two objects are equal or convertible to the larger
// type and equal.
//
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
@ -186,7 +186,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
// 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")
// }, 10*time.Second, 1*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()
@ -568,6 +568,23 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
}
// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should not match.
// This is an inverse of ElementsMatch.
//
// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false
//
// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true
//
// assert.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true
func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
}
// 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.
//
@ -604,7 +621,16 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// NotErrorAsf asserts that none of the errors in err's chain matches target,
// but if so, sets target to that error value.
func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
}
// NotErrorIsf asserts that none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {

View File

@ -186,8 +186,8 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface
return EqualExportedValuesf(a.t, expected, actual, msg, args...)
}
// EqualValues asserts that two objects are equal or convertible to the same types
// and equal.
// EqualValues asserts that two objects are equal or convertible to the larger
// type and equal.
//
// a.EqualValues(uint32(123), int32(123))
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
@ -197,8 +197,8 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
return EqualValues(a.t, expected, actual, msgAndArgs...)
}
// EqualValuesf asserts that two objects are equal or convertible to the same types
// and equal.
// EqualValuesf asserts that two objects are equal or convertible to the larger
// type and equal.
//
// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted")
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
@ -336,7 +336,7 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
// 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")
// }, 10*time.Second, 1*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()
@ -361,7 +361,7 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor
// 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")
// }, 10*time.Second, 1*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()
@ -1128,6 +1128,40 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
return NotContainsf(a.t, s, contains, msg, args...)
}
// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should not match.
// This is an inverse of ElementsMatch.
//
// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false
//
// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true
//
// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true
func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotElementsMatch(a.t, listA, listB, msgAndArgs...)
}
// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should not match.
// This is an inverse of ElementsMatch.
//
// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false
//
// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true
//
// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true
func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotElementsMatchf(a.t, listA, listB, msg, args...)
}
// 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.
//
@ -1200,7 +1234,25 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
return NotEqualf(a.t, expected, actual, msg, args...)
}
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// NotErrorAs asserts that none of the errors in err's chain matches target,
// but if so, sets target to that error value.
func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotErrorAs(a.t, err, target, msgAndArgs...)
}
// NotErrorAsf asserts that none of the errors in err's chain matches target,
// but if so, sets target to that error value.
func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotErrorAsf(a.t, err, target, msg, args...)
}
// NotErrorIs asserts that none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
@ -1209,7 +1261,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface
return NotErrorIs(a.t, err, target, msgAndArgs...)
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// NotErrorIsf asserts that none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {

View File

@ -6,7 +6,7 @@ import (
)
// isOrdered checks that collection contains orderable elements.
func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool {
objKind := reflect.TypeOf(object).Kind()
if objKind != reflect.Slice && objKind != reflect.Array {
return false
@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT
// 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...)
return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
}
// IsNonIncreasing asserts that the collection is not increasing
@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
// 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...)
return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
}
// IsDecreasing asserts that the collection is decreasing
@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{})
// 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...)
return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
}
// IsNonDecreasing asserts that the collection is not decreasing
@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo
// 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...)
return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
}

View File

@ -19,7 +19,9 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/pmezard/go-difflib/difflib"
"gopkg.in/yaml.v3"
// Wrapper around gopkg.in/yaml.v3
"github.com/stretchr/testify/assert/yaml"
)
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
@ -45,6 +47,10 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
// for table driven tests.
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
// PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful
// for table driven tests.
type PanicAssertionFunc = func(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool
// Comparison is a custom function that returns true on success and false on failure
type Comparison func() (success bool)
@ -496,7 +502,13 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
h.Helper()
}
if !samePointers(expected, actual) {
same, ok := samePointers(expected, actual)
if !ok {
return Fail(t, "Both arguments must be pointers", msgAndArgs...)
}
if !same {
// both are pointers but not the same type & pointing to the same address
return Fail(t, fmt.Sprintf("Not same: \n"+
"expected: %p %#v\n"+
"actual : %p %#v", expected, expected, actual, actual), msgAndArgs...)
@ -516,7 +528,13 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
h.Helper()
}
if samePointers(expected, actual) {
same, ok := samePointers(expected, actual)
if !ok {
//fails when the arguments are not pointers
return !(Fail(t, "Both arguments must be pointers", msgAndArgs...))
}
if same {
return Fail(t, fmt.Sprintf(
"Expected and actual point to the same object: %p %#v",
expected, expected), msgAndArgs...)
@ -524,21 +542,23 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
return true
}
// samePointers compares two generic interface objects and returns whether
// they point to the same object
func samePointers(first, second interface{}) bool {
// samePointers checks if two generic interface objects are pointers of the same
// type pointing to the same object. It returns two values: same indicating if
// they are the same type and point to the same object, and ok indicating that
// both inputs are pointers.
func samePointers(first, second interface{}) (same bool, ok bool) {
firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second)
if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr {
return false
return false, false //not both are pointers
}
firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second)
if firstType != secondType {
return false
return false, true // both are pointers, but of different types
}
// compare pointer addresses
return first == second
return first == second, true
}
// formatUnequalValues takes two values of arbitrary types and returns string
@ -572,8 +592,8 @@ func truncatingFormat(data interface{}) string {
return value
}
// EqualValues asserts that two objects are equal or convertible to the same types
// and equal.
// EqualValues asserts that two objects are equal or convertible to the larger
// type and equal.
//
// assert.EqualValues(t, uint32(123), int32(123))
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
@ -615,21 +635,6 @@ func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ..
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
}
if aType.Kind() == reflect.Ptr {
aType = aType.Elem()
}
if bType.Kind() == reflect.Ptr {
bType = bType.Elem()
}
if aType.Kind() != reflect.Struct {
return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to 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 or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
}
expected = copyExportedFields(expected)
actual = copyExportedFields(actual)
@ -1170,6 +1175,39 @@ func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) stri
return msg.String()
}
// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should not match.
// This is an inverse of ElementsMatch.
//
// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false
//
// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true
//
// assert.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true
func NotElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if isEmpty(listA) && isEmpty(listB) {
return Fail(t, "listA and listB contain the same elements", msgAndArgs)
}
if !isList(t, listA, msgAndArgs...) {
return Fail(t, "listA is not a list type", msgAndArgs...)
}
if !isList(t, listB, msgAndArgs...) {
return Fail(t, "listB is not a list type", msgAndArgs...)
}
extraA, extraB := diffLists(listA, listB)
if len(extraA) == 0 && len(extraB) == 0 {
return Fail(t, "listA and listB contain the same elements", msgAndArgs)
}
return true
}
// Condition uses a Comparison to assert a complex condition.
func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
@ -1488,6 +1526,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
if err != nil {
return Fail(t, err.Error(), msgAndArgs...)
}
if math.IsNaN(actualEpsilon) {
return Fail(t, "relative error is NaN", msgAndArgs...)
}
if actualEpsilon > epsilon {
return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+
" < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...)
@ -1611,7 +1652,6 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in
// matchRegexp return true if a specified regexp matches a string.
func matchRegexp(rx interface{}, str interface{}) bool {
var r *regexp.Regexp
if rr, ok := rx.(*regexp.Regexp); ok {
r = rr
@ -1619,7 +1659,14 @@ func matchRegexp(rx interface{}, str interface{}) bool {
r = regexp.MustCompile(fmt.Sprint(rx))
}
return (r.FindStringIndex(fmt.Sprint(str)) != nil)
switch v := str.(type) {
case []byte:
return r.Match(v)
case string:
return r.MatchString(v)
default:
return r.MatchString(fmt.Sprint(v))
}
}
@ -1872,7 +1919,7 @@ var spewConfigStringerEnabled = spew.ConfigState{
MaxDepth: 10,
}
type tHelper interface {
type tHelper = interface {
Helper()
}
@ -1911,6 +1958,9 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
// CollectT implements the TestingT interface and collects all errors.
type CollectT struct {
// A slice of errors. Non-nil slice denotes a failure.
// If it's non-nil but len(c.errors) == 0, this is also a failure
// obtained by direct c.FailNow() call.
errors []error
}
@ -1919,9 +1969,10 @@ func (c *CollectT) Errorf(format string, args ...interface{}) {
c.errors = append(c.errors, fmt.Errorf(format, args...))
}
// FailNow panics.
func (*CollectT) FailNow() {
panic("Assertion failed")
// FailNow stops execution by calling runtime.Goexit.
func (c *CollectT) FailNow() {
c.fail()
runtime.Goexit()
}
// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
@ -1934,6 +1985,16 @@ func (*CollectT) Copy(TestingT) {
panic("Copy() is deprecated")
}
func (c *CollectT) fail() {
if !c.failed() {
c.errors = []error{} // Make it non-nil to mark a failure.
}
}
func (c *CollectT) failed() bool {
return c.errors != nil
}
// 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
@ -1951,14 +2012,14 @@ func (*CollectT) Copy(TestingT) {
// 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")
// }, 10*time.Second, 1*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()
}
var lastFinishedTickErrs []error
ch := make(chan []error, 1)
ch := make(chan *CollectT, 1)
timer := time.NewTimer(waitFor)
defer timer.Stop()
@ -1978,16 +2039,16 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
go func() {
collect := new(CollectT)
defer func() {
ch <- collect.errors
ch <- collect
}()
condition(collect)
}()
case errs := <-ch:
if len(errs) == 0 {
case collect := <-ch:
if !collect.failed() {
return true
}
// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
lastFinishedTickErrs = errs
lastFinishedTickErrs = collect.errors
tick = ticker.C
}
}
@ -2049,7 +2110,7 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
), msgAndArgs...)
}
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// NotErrorIs asserts that none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
@ -2090,6 +2151,24 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
), msgAndArgs...)
}
// NotErrorAs asserts that none of the errors in err's chain matches target,
// but if so, sets target to that error value.
func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !errors.As(err, target) {
return true
}
chain := buildErrorChainString(err)
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
"found: %q\n"+
"in chain: %s", target, chain,
), msgAndArgs...)
}
func buildErrorChainString(err error) string {
if err == nil {
return ""

View File

@ -0,0 +1,25 @@
//go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default
// +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default
// Package yaml is an implementation of YAML functions that calls a pluggable implementation.
//
// This implementation is selected with the testify_yaml_custom build tag.
//
// go test -tags testify_yaml_custom
//
// This implementation can be used at build time to replace the default implementation
// to avoid linking with [gopkg.in/yaml.v3].
//
// In your test package:
//
// import assertYaml "github.com/stretchr/testify/assert/yaml"
//
// func init() {
// assertYaml.Unmarshal = func (in []byte, out interface{}) error {
// // ...
// return nil
// }
// }
package yaml
var Unmarshal func(in []byte, out interface{}) error

View File

@ -0,0 +1,37 @@
//go:build !testify_yaml_fail && !testify_yaml_custom
// +build !testify_yaml_fail,!testify_yaml_custom
// Package yaml is just an indirection to handle YAML deserialization.
//
// This package is just an indirection that allows the builder to override the
// indirection with an alternative implementation of this package that uses
// another implementation of YAML deserialization. This allows to not either not
// use YAML deserialization at all, or to use another implementation than
// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]).
//
// Alternative implementations are selected using build tags:
//
// - testify_yaml_fail: [Unmarshal] always fails with an error
// - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it
// before calling any of [github.com/stretchr/testify/assert.YAMLEq] or
// [github.com/stretchr/testify/assert.YAMLEqf].
//
// Usage:
//
// go test -tags testify_yaml_fail
//
// You can check with "go list" which implementation is linked:
//
// go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml
// go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml
// go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml
//
// [PR #1120]: https://github.com/stretchr/testify/pull/1120
package yaml
import goyaml "gopkg.in/yaml.v3"
// Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal].
func Unmarshal(in []byte, out interface{}) error {
return goyaml.Unmarshal(in, out)
}

View File

@ -0,0 +1,18 @@
//go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default
// +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default
// Package yaml is an implementation of YAML functions that always fail.
//
// This implementation can be used at build time to replace the default implementation
// to avoid linking with [gopkg.in/yaml.v3]:
//
// go test -tags testify_yaml_fail
package yaml
import "errors"
var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)")
func Unmarshal([]byte, interface{}) error {
return errNotImplemented
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
{{.Comment}}
{{ replace .Comment "assert." "require."}}
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
if h, ok := t.(tHelper); ok { h.Helper() }
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return }

View File

@ -187,8 +187,8 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface
EqualExportedValuesf(a.t, expected, actual, msg, args...)
}
// EqualValues asserts that two objects are equal or convertible to the same types
// and equal.
// EqualValues asserts that two objects are equal or convertible to the larger
// type and equal.
//
// a.EqualValues(uint32(123), int32(123))
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
@ -198,8 +198,8 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
EqualValues(a.t, expected, actual, msgAndArgs...)
}
// EqualValuesf asserts that two objects are equal or convertible to the same types
// and equal.
// EqualValuesf asserts that two objects are equal or convertible to the larger
// type and equal.
//
// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted")
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
@ -337,7 +337,7 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti
// 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")
// }, 10*time.Second, 1*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()
@ -362,7 +362,7 @@ func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), w
// 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")
// }, 10*time.Second, 1*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()
@ -1129,6 +1129,40 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
NotContainsf(a.t, s, contains, msg, args...)
}
// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should not match.
// This is an inverse of ElementsMatch.
//
// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false
//
// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true
//
// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true
func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotElementsMatch(a.t, listA, listB, msgAndArgs...)
}
// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should not match.
// This is an inverse of ElementsMatch.
//
// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false
//
// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true
//
// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true
func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotElementsMatchf(a.t, listA, listB, msg, args...)
}
// 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.
//
@ -1201,7 +1235,25 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
NotEqualf(a.t, expected, actual, msg, args...)
}
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// NotErrorAs asserts that none of the errors in err's chain matches target,
// but if so, sets target to that error value.
func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotErrorAs(a.t, err, target, msgAndArgs...)
}
// NotErrorAsf asserts that none of the errors in err's chain matches target,
// but if so, sets target to that error value.
func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotErrorAsf(a.t, err, target, msg, args...)
}
// NotErrorIs asserts that none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
@ -1210,7 +1262,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface
NotErrorIs(a.t, err, target, msgAndArgs...)
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// NotErrorIsf asserts that none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {

View File

@ -6,7 +6,7 @@ type TestingT interface {
FailNow()
}
type tHelper interface {
type tHelper = interface {
Helper()
}

View File

@ -150,7 +150,7 @@ func printCommandSuggestions(commands []*Command, writer io.Writer) {
if command.Hidden {
continue
}
if strings.HasSuffix(os.Getenv("SHELL"), "zsh") {
if strings.HasSuffix(os.Getenv("0"), "zsh") {
for _, name := range command.Names() {
_, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage)
}

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