:pin: upgraded and vendored dependencies
This commit is contained in:
parent
98576889a2
commit
0d4843e4f9
116 changed files with 59904 additions and 41212 deletions
16
go.mod
16
go.mod
|
@ -5,7 +5,7 @@ go 1.20
|
|||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/brianvoe/gofakeit/v6 v6.22.0
|
||||
github.com/caarlos0/env/v7 v7.1.0
|
||||
github.com/caarlos0/env/v9 v9.0.0
|
||||
github.com/goccy/go-json v0.10.2
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
|
@ -14,11 +14,11 @@ require (
|
|||
github.com/valyala/fasttemplate v1.2.2
|
||||
github.com/valyala/quicktemplate v1.7.0
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
|
||||
golang.org/x/text v0.11.0
|
||||
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b
|
||||
golang.org/x/text v0.12.0
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
|
||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a
|
||||
modernc.org/sqlite v1.23.1
|
||||
modernc.org/sqlite v1.25.0
|
||||
source.toby3d.me/toby3d/form v0.3.0
|
||||
willnorris.com/go/microformats v1.2.0
|
||||
)
|
||||
|
@ -43,11 +43,11 @@ require (
|
|||
github.com/valyala/fasthttp v1.48.0 // indirect
|
||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
|
||||
golang.org/x/crypto v0.10.0 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.11.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/tools v0.10.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/tools v0.11.1 // indirect
|
||||
lukechampine.com/uint128 v1.3.0 // indirect
|
||||
modernc.org/cc/v3 v3.41.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.14 // indirect
|
||||
|
|
32
go.sum
32
go.sum
|
@ -6,8 +6,8 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
|
|||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/brianvoe/gofakeit/v6 v6.22.0 h1:BzOsDot1o3cufTfOk+fWKE9nFYojyDV+XHdCWL2+uyE=
|
||||
github.com/brianvoe/gofakeit/v6 v6.22.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
|
||||
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
|
||||
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
|
||||
github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc=
|
||||
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -98,10 +98,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
|
||||
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
|
@ -115,8 +115,8 @@ golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -136,8 +136,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
@ -148,15 +148,15 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||
golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc=
|
||||
golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -184,8 +184,8 @@ modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
|
|||
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
|
||||
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
|
|
8
main.go
8
main.go
|
@ -23,7 +23,7 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
|
@ -116,11 +116,7 @@ func init() {
|
|||
flag.StringVar(&memProfilePath, "memprofile", "", "set path to saving pprof memory profile")
|
||||
flag.Parse()
|
||||
|
||||
if err := env.Parse(config, env.Options{
|
||||
Prefix: "AUTH_",
|
||||
TagName: "env",
|
||||
UseFieldNameByDefault: true,
|
||||
}); err != nil {
|
||||
if err := env.ParseWithOptions(config, env.Options{Prefix: "AUTH_"}); err != nil {
|
||||
logger.Fatalln(err)
|
||||
}
|
||||
|
||||
|
|
7
vendor/github.com/caarlos0/env/v9/.mailmap
generated
vendored
Normal file
7
vendor/github.com/caarlos0/env/v9/.mailmap
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos A Becker <caarlos0@gmail.com>
|
||||
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos A Becker <caarlos0@users.noreply.github.com>
|
||||
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos Alexandro Becker <caarlos0@gmail.com>
|
||||
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
|
||||
Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Carlos Becker <caarlos0@gmail.com>
|
||||
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
|
||||
actions-user <actions@github.com> github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
140
vendor/github.com/caarlos0/env/v7/README.md → vendor/github.com/caarlos0/env/v9/README.md
generated
vendored
140
vendor/github.com/caarlos0/env/v7/README.md → vendor/github.com/caarlos0/env/v9/README.md
generated
vendored
|
@ -2,16 +2,17 @@
|
|||
|
||||
[![Build Status](https://img.shields.io/github/actions/workflow/status/caarlos0/env/build.yml?branch=main&style=for-the-badge)](https://github.com/caarlos0/env/actions?workflow=build)
|
||||
[![Coverage Status](https://img.shields.io/codecov/c/gh/caarlos0/env.svg?logo=codecov&style=for-the-badge)](https://codecov.io/gh/caarlos0/env)
|
||||
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v7)
|
||||
[![](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/caarlos0/env/v9)
|
||||
|
||||
A simple and zero-dependencies library to parse environment variables into structs.
|
||||
A simple and zero-dependencies library to parse environment variables into
|
||||
`struct`s.
|
||||
|
||||
## Example
|
||||
|
||||
Get the module with:
|
||||
|
||||
```sh
|
||||
go get github.com/caarlos0/env/v7
|
||||
go get github.com/caarlos0/env/v9
|
||||
```
|
||||
|
||||
The usage looks like this:
|
||||
|
@ -23,7 +24,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
|
@ -33,7 +34,7 @@ type config struct {
|
|||
IsProduction bool `env:"PRODUCTION"`
|
||||
Hosts []string `env:"HOSTS" envSeparator:":"`
|
||||
Duration time.Duration `env:"DURATION"`
|
||||
TempFolder string `env:"TEMP_FOLDER" envDefault:"${HOME}/tmp" envExpand:"true"`
|
||||
TempFolder string `env:"TEMP_FOLDER,expand" envDefault:"${HOME}/tmp"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -61,7 +62,6 @@ $ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run main.go
|
|||
|
||||
- _Unexported fields_ are **ignored**
|
||||
|
||||
|
||||
## Supported types and defaults
|
||||
|
||||
Out of the box all built-in types are supported, plus a few others that
|
||||
|
@ -102,31 +102,30 @@ case of absence of it in the environment.
|
|||
By default, slice types will split the environment value on `,`; you can change
|
||||
this behavior by setting the `envSeparator` tag.
|
||||
|
||||
If you set the `envExpand` tag, environment variables (either in `${var}` or
|
||||
`$var` format) in the string will be replaced according with the actual value
|
||||
of the variable.
|
||||
|
||||
## Custom Parser Funcs
|
||||
|
||||
If you have a type that is not supported out of the box by the lib, you are able
|
||||
to use (or define) and pass custom parsers (and their associated `reflect.Type`)
|
||||
to the `env.ParseWithFuncs()` function.
|
||||
to the `env.ParseWithOptions()` function.
|
||||
|
||||
In addition to accepting a struct pointer (same as `Parse()`), this function
|
||||
also accepts a `map[reflect.Type]env.ParserFunc`.
|
||||
also accepts a `Options{}`, and you can set your custom parsers in the `FuncMap`
|
||||
field.
|
||||
|
||||
If you add a custom parser for, say `Foo`, it will also be used to parse
|
||||
`*Foo` and `[]Foo` types.
|
||||
|
||||
Check the examples in the [go doc](http://pkg.go.dev/github.com/caarlos0/env/v7)
|
||||
Check the examples in the [go doc](http://pkg.go.dev/github.com/caarlos0/env/v9)
|
||||
for more info.
|
||||
|
||||
### A note about `TextUnmarshaler` and `time.Time`
|
||||
|
||||
Env supports by default anything that implements the `TextUnmarshaler` interface.
|
||||
That includes things like `time.Time` for example.
|
||||
The upside is that depending on the format you need, you don't need to change anything.
|
||||
The downside is that if you do need time in another format, you'll need to create your own type.
|
||||
The upside is that depending on the format you need, you don't need to change
|
||||
anything.
|
||||
The downside is that if you do need time in another format, you'll need to
|
||||
create your own type.
|
||||
|
||||
Its fairly straightforward:
|
||||
|
||||
|
@ -148,8 +147,9 @@ And then you can parse `Config` with `env.Parse`.
|
|||
|
||||
## Required fields
|
||||
|
||||
The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added to ensure that some environment variable is set.
|
||||
In the example above, an error is returned if the `config` struct is changed to:
|
||||
The `env` tag option `required` (e.g., `env:"tagKey,required"`) can be added to
|
||||
ensure that some environment variable is set. In the example above, an error is
|
||||
returned if the `config` struct is changed to:
|
||||
|
||||
```go
|
||||
type config struct {
|
||||
|
@ -157,10 +157,32 @@ type config struct {
|
|||
}
|
||||
```
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> Note that being set is not the same as being empty.
|
||||
> If the variable is set, but empty, the field will have its type's default
|
||||
> value.
|
||||
> This also means that custom parser funcs will not be invoked.
|
||||
|
||||
## Expand vars
|
||||
|
||||
If you set the `expand` option, environment variables (either in `${var}` or
|
||||
`$var` format) in the string will be replaced according with the actual value
|
||||
of the variable. For example:
|
||||
|
||||
```go
|
||||
type config struct {
|
||||
SecretKey string `env:"SECRET_KEY,expand"`
|
||||
}
|
||||
```
|
||||
|
||||
This also works with `envDefault`.
|
||||
|
||||
## Not Empty fields
|
||||
|
||||
While `required` demands the environment variable to be set, it doesn't check its value.
|
||||
If you want to make sure the environment is set and not empty, you need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`).
|
||||
While `required` demands the environment variable to be set, it doesn't check
|
||||
its value. If you want to make sure the environment is set and not empty, you
|
||||
need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`).
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -186,9 +208,9 @@ type config struct {
|
|||
## From file
|
||||
|
||||
The `env` tag option `file` (e.g., `env:"tagKey,file"`) can be added
|
||||
to in order to indicate that the value of the variable shall be loaded from a file. The path of that file is given
|
||||
by the environment variable associated with it
|
||||
Example below
|
||||
in order to indicate that the value of the variable shall be loaded from a
|
||||
file.
|
||||
The path of that file is given by the environment variable associated with it:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
@ -196,13 +218,13 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Secret string `env:"SECRET,file"`
|
||||
Password string `env:"PASSWORD,file" envDefault:"/tmp/password"`
|
||||
Certificate string `env:"CERTIFICATE,file" envDefault:"${CERTIFICATE_FILE}" envExpand:"true"`
|
||||
Certificate string `env:"CERTIFICATE,file,expand" envDefault:"${CERTIFICATE_FILE}"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -237,7 +259,6 @@ It will use the field name as environment variable name.
|
|||
|
||||
Here's an example:
|
||||
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
|
@ -245,7 +266,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -256,10 +277,10 @@ type Config struct {
|
|||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{UseFieldNameByDefault: true}
|
||||
opts := env.Options{UseFieldNameByDefault: true}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
if err := env.ParseWithOptions(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -270,9 +291,11 @@ func main() {
|
|||
|
||||
### Environment
|
||||
|
||||
By setting the `Options.Environment` map you can tell `Parse` to add those `keys` and `values`
|
||||
as env vars before parsing is done. These envs are stored in the map and never actually set by `os.Setenv`.
|
||||
This option effectively makes `env` ignore the OS environment variables: only the ones provided in the option are used.
|
||||
By setting the `Options.Environment` map you can tell `Parse` to add those
|
||||
`keys` and `values` as `env` vars before parsing is done.
|
||||
These `envs` are stored in the map and never actually set by `os.Setenv`.
|
||||
This option effectively makes `env` ignore the OS environment variables: only
|
||||
the ones provided in the option are used.
|
||||
|
||||
This can make your testing scenarios a bit more clean and easy to handle.
|
||||
|
||||
|
@ -283,7 +306,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -292,12 +315,12 @@ type Config struct {
|
|||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{Environment: map[string]string{
|
||||
opts := env.Options{Environment: map[string]string{
|
||||
"PASSWORD": "MY_PASSWORD",
|
||||
}}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
if err := env.ParseWithOptions(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -308,10 +331,11 @@ func main() {
|
|||
|
||||
### Changing default tag name
|
||||
|
||||
You can change what tag name to use for setting the env vars by setting the `Options.TagName`
|
||||
variable.
|
||||
You can change what tag name to use for setting the env vars by setting the
|
||||
`Options.TagName` variable.
|
||||
|
||||
For example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
|
@ -319,7 +343,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -328,10 +352,10 @@ type Config struct {
|
|||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{TagName: "json"}
|
||||
opts := env.Options{TagName: "json"}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
if err := env.ParseWithOptions(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -353,7 +377,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -368,8 +392,8 @@ type ComplexConfig struct {
|
|||
}
|
||||
|
||||
func main() {
|
||||
cfg := ComplexConfig{}
|
||||
if err := Parse(&cfg, Options{
|
||||
cfg := &ComplexConfig{}
|
||||
opts := env.Options{
|
||||
Prefix: "T_",
|
||||
Environment: map[string]string{
|
||||
"T_FOO_HOME": "/foo",
|
||||
|
@ -377,12 +401,10 @@ func main() {
|
|||
"T_BLAH": "blahhh",
|
||||
"T_HOME": "/clean",
|
||||
},
|
||||
}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
if err := env.ParseWithOptions(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -393,7 +415,8 @@ func main() {
|
|||
|
||||
### On set hooks
|
||||
|
||||
You might want to listen to value sets and, for example, log something or do some other kind of logic.
|
||||
You might want to listen to value sets and, for example, log something or do
|
||||
some other kind of logic.
|
||||
You can do this by passing a `OnSet` option:
|
||||
|
||||
```go
|
||||
|
@ -403,7 +426,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -413,14 +436,14 @@ type Config struct {
|
|||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{
|
||||
opts := env.Options{
|
||||
OnSet: func(tag string, value interface{}, isDefault bool) {
|
||||
fmt.Printf("Set %s to %v (default? %v)\n", tag, value, isDefault)
|
||||
},
|
||||
}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
if err := env.ParseWithOptions(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -431,7 +454,8 @@ func main() {
|
|||
|
||||
## Making all fields to required
|
||||
|
||||
You can make all fields that don't have a default value be required by setting the `RequiredIfNoDef: true` in the `Options`.
|
||||
You can make all fields that don't have a default value be required by setting
|
||||
the `RequiredIfNoDef: true` in the `Options`.
|
||||
|
||||
For example
|
||||
|
||||
|
@ -442,7 +466,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -452,10 +476,10 @@ type Config struct {
|
|||
|
||||
func main() {
|
||||
cfg := &Config{}
|
||||
opts := &env.Options{RequiredIfNoDef: true}
|
||||
opts := env.Options{RequiredIfNoDef: true}
|
||||
|
||||
// Load env vars.
|
||||
if err := env.Parse(cfg, opts); err != nil {
|
||||
if err := env.ParseWithOptions(cfg, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -466,8 +490,10 @@ func main() {
|
|||
|
||||
## Defaults from code
|
||||
|
||||
You may define default value also in code, by initialising the config data before it's filled by `env.Parse`.
|
||||
Default values defined as struct tags will overwrite existing values during Parse.
|
||||
You may define default value also in code, by initialising the config data
|
||||
before it's filled by `env.Parse`.
|
||||
Default values defined as struct tags will overwrite existing values during
|
||||
Parse.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
@ -476,7 +502,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -509,7 +535,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/caarlos0/env/v7"
|
||||
"github.com/caarlos0/env/v9"
|
||||
)
|
||||
|
||||
type Config struct {
|
144
vendor/github.com/caarlos0/env/v7/env.go → vendor/github.com/caarlos0/env/v9/env.go
generated
vendored
144
vendor/github.com/caarlos0/env/v7/env.go → vendor/github.com/caarlos0/env/v9/env.go
generated
vendored
|
@ -117,72 +117,60 @@ type Options struct {
|
|||
// name by default if the `env` key is missing.
|
||||
UseFieldNameByDefault bool
|
||||
|
||||
// Sets to true if we have already configured once.
|
||||
configured bool
|
||||
// Custom parse functions for different types.
|
||||
FuncMap map[reflect.Type]ParserFunc
|
||||
}
|
||||
|
||||
// configure will do the basic configurations and defaults.
|
||||
func configure(opts []Options) []Options {
|
||||
// If we have already configured the first item
|
||||
// of options will have been configured set to true.
|
||||
if len(opts) > 0 && opts[0].configured {
|
||||
return opts
|
||||
}
|
||||
|
||||
// Created options with defaults.
|
||||
opt := Options{
|
||||
func defaultOptions() Options {
|
||||
return Options{
|
||||
TagName: "env",
|
||||
Environment: toMap(os.Environ()),
|
||||
configured: true,
|
||||
FuncMap: defaultTypeParsers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over all opts structs and set
|
||||
// to opt if value is not default/empty.
|
||||
for _, item := range opts {
|
||||
if item.Environment != nil {
|
||||
opt.Environment = item.Environment
|
||||
}
|
||||
if item.TagName != "" {
|
||||
opt.TagName = item.TagName
|
||||
}
|
||||
if item.OnSet != nil {
|
||||
opt.OnSet = item.OnSet
|
||||
}
|
||||
if item.Prefix != "" {
|
||||
opt.Prefix = item.Prefix
|
||||
}
|
||||
opt.UseFieldNameByDefault = item.UseFieldNameByDefault
|
||||
opt.RequiredIfNoDef = item.RequiredIfNoDef
|
||||
func customOptions(opt Options) Options {
|
||||
defOpts := defaultOptions()
|
||||
if opt.TagName == "" {
|
||||
opt.TagName = defOpts.TagName
|
||||
}
|
||||
|
||||
return []Options{opt}
|
||||
if opt.Environment == nil {
|
||||
opt.Environment = defOpts.Environment
|
||||
}
|
||||
if opt.FuncMap == nil {
|
||||
opt.FuncMap = map[reflect.Type]ParserFunc{}
|
||||
}
|
||||
for k, v := range defOpts.FuncMap {
|
||||
opt.FuncMap[k] = v
|
||||
}
|
||||
return opt
|
||||
}
|
||||
|
||||
func getOnSetFn(opts []Options) OnSetFn {
|
||||
return opts[0].OnSet
|
||||
}
|
||||
|
||||
// getTagName returns the tag name.
|
||||
func getTagName(opts []Options) string {
|
||||
return opts[0].TagName
|
||||
}
|
||||
|
||||
// getEnvironment returns the environment map.
|
||||
func getEnvironment(opts []Options) map[string]string {
|
||||
return opts[0].Environment
|
||||
func optionsWithEnvPrefix(field reflect.StructField, opts Options) Options {
|
||||
return Options{
|
||||
Environment: opts.Environment,
|
||||
TagName: opts.TagName,
|
||||
RequiredIfNoDef: opts.RequiredIfNoDef,
|
||||
OnSet: opts.OnSet,
|
||||
Prefix: opts.Prefix + field.Tag.Get("envPrefix"),
|
||||
UseFieldNameByDefault: opts.UseFieldNameByDefault,
|
||||
FuncMap: opts.FuncMap,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses a struct containing `env` tags and loads its values from
|
||||
// environment variables.
|
||||
func Parse(v interface{}, opts ...Options) error {
|
||||
return ParseWithFuncs(v, map[reflect.Type]ParserFunc{}, opts...)
|
||||
func Parse(v interface{}) error {
|
||||
return parseInternal(v, defaultOptions())
|
||||
}
|
||||
|
||||
// ParseWithFuncs is the same as `Parse` except it also allows the user to pass
|
||||
// in custom parsers.
|
||||
func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...Options) error {
|
||||
opts = configure(opts)
|
||||
// Parse parses a struct containing `env` tags and loads its values from
|
||||
// environment variables.
|
||||
func ParseWithOptions(v interface{}, opts Options) error {
|
||||
return parseInternal(v, customOptions(opts))
|
||||
}
|
||||
|
||||
func parseInternal(v interface{}, opts Options) error {
|
||||
ptrRef := reflect.ValueOf(v)
|
||||
if ptrRef.Kind() != reflect.Ptr {
|
||||
return newAggregateError(NotStructPtrError{})
|
||||
|
@ -191,15 +179,10 @@ func ParseWithFuncs(v interface{}, funcMap map[reflect.Type]ParserFunc, opts ...
|
|||
if ref.Kind() != reflect.Struct {
|
||||
return newAggregateError(NotStructPtrError{})
|
||||
}
|
||||
parsers := defaultTypeParsers()
|
||||
for k, v := range funcMap {
|
||||
parsers[k] = v
|
||||
}
|
||||
|
||||
return doParse(ref, parsers, opts)
|
||||
return doParse(ref, opts)
|
||||
}
|
||||
|
||||
func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Options) error {
|
||||
func doParse(ref reflect.Value, opts Options) error {
|
||||
refType := ref.Type()
|
||||
|
||||
var agrErr AggregateError
|
||||
|
@ -208,7 +191,7 @@ func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Opti
|
|||
refField := ref.Field(i)
|
||||
refTypeField := refType.Field(i)
|
||||
|
||||
if err := doParseField(refField, refTypeField, funcMap, opts); err != nil {
|
||||
if err := doParseField(refField, refTypeField, opts); err != nil {
|
||||
if val, ok := err.(AggregateError); ok {
|
||||
agrErr.Errors = append(agrErr.Errors, val.Errors...)
|
||||
} else {
|
||||
|
@ -224,15 +207,15 @@ func doParse(ref reflect.Value, funcMap map[reflect.Type]ParserFunc, opts []Opti
|
|||
return agrErr
|
||||
}
|
||||
|
||||
func doParseField(refField reflect.Value, refTypeField reflect.StructField, funcMap map[reflect.Type]ParserFunc, opts []Options) error {
|
||||
func doParseField(refField reflect.Value, refTypeField reflect.StructField, opts Options) error {
|
||||
if !refField.CanSet() {
|
||||
return nil
|
||||
}
|
||||
if reflect.Ptr == refField.Kind() && !refField.IsNil() {
|
||||
return ParseWithFuncs(refField.Interface(), funcMap, optsWithPrefix(refTypeField, opts)...)
|
||||
return parseInternal(refField.Interface(), optionsWithEnvPrefix(refTypeField, opts))
|
||||
}
|
||||
if reflect.Struct == refField.Kind() && refField.CanAddr() && refField.Type().Name() == "" {
|
||||
return ParseWithFuncs(refField.Addr().Interface(), funcMap, optsWithPrefix(refTypeField, opts)...)
|
||||
return parseInternal(refField.Addr().Interface(), optionsWithEnvPrefix(refTypeField, opts))
|
||||
}
|
||||
value, err := get(refTypeField, opts)
|
||||
if err != nil {
|
||||
|
@ -240,11 +223,11 @@ func doParseField(refField reflect.Value, refTypeField reflect.StructField, func
|
|||
}
|
||||
|
||||
if value != "" {
|
||||
return set(refField, refTypeField, value, funcMap)
|
||||
return set(refField, refTypeField, value, opts.FuncMap)
|
||||
}
|
||||
|
||||
if reflect.Struct == refField.Kind() {
|
||||
return doParse(refField, funcMap, optsWithPrefix(refTypeField, opts))
|
||||
return doParse(refField, optionsWithEnvPrefix(refTypeField, opts))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -263,20 +246,20 @@ func toEnvName(input string) string {
|
|||
return string(output)
|
||||
}
|
||||
|
||||
func get(field reflect.StructField, opts []Options) (val string, err error) {
|
||||
func get(field reflect.StructField, opts Options) (val string, err error) {
|
||||
var exists bool
|
||||
var isDefault bool
|
||||
var loadFile bool
|
||||
var unset bool
|
||||
var notEmpty bool
|
||||
var expand bool
|
||||
|
||||
required := opts[0].RequiredIfNoDef
|
||||
prefix := opts[0].Prefix
|
||||
ownKey, tags := parseKeyForOption(field.Tag.Get(getTagName(opts)))
|
||||
if ownKey == "" && opts[0].UseFieldNameByDefault {
|
||||
required := opts.RequiredIfNoDef
|
||||
ownKey, tags := parseKeyForOption(field.Tag.Get(opts.TagName))
|
||||
if ownKey == "" && opts.UseFieldNameByDefault {
|
||||
ownKey = toEnvName(field.Name)
|
||||
}
|
||||
key := prefix + ownKey
|
||||
|
||||
for _, tag := range tags {
|
||||
switch tag {
|
||||
case "":
|
||||
|
@ -289,13 +272,17 @@ func get(field reflect.StructField, opts []Options) (val string, err error) {
|
|||
unset = true
|
||||
case "notEmpty":
|
||||
notEmpty = true
|
||||
case "expand":
|
||||
expand = true
|
||||
default:
|
||||
return "", newNoSupportedTagOptionError(tag)
|
||||
}
|
||||
}
|
||||
expand := strings.EqualFold(field.Tag.Get("envExpand"), "true")
|
||||
|
||||
prefix := opts.Prefix
|
||||
key := prefix + ownKey
|
||||
defaultValue, defExists := field.Tag.Lookup("envDefault")
|
||||
val, exists, isDefault = getOr(key, defaultValue, defExists, getEnvironment(opts))
|
||||
val, exists, isDefault = getOr(key, defaultValue, defExists, opts.Environment)
|
||||
|
||||
if expand {
|
||||
val = os.ExpandEnv(val)
|
||||
|
@ -321,8 +308,10 @@ func get(field reflect.StructField, opts []Options) (val string, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
if onSetFn := getOnSetFn(opts); onSetFn != nil {
|
||||
onSetFn(key, val, isDefault)
|
||||
if opts.OnSet != nil {
|
||||
if ownKey != "" {
|
||||
opts.OnSet(key, val, isDefault)
|
||||
}
|
||||
}
|
||||
return val, err
|
||||
}
|
||||
|
@ -529,12 +518,3 @@ func parseTextUnmarshalers(field reflect.Value, data []string, sf reflect.Struct
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func optsWithPrefix(field reflect.StructField, opts []Options) []Options {
|
||||
subOpts := make([]Options, len(opts))
|
||||
copy(subOpts, opts)
|
||||
if prefix := field.Tag.Get("envPrefix"); prefix != "" {
|
||||
subOpts[0].Prefix += prefix
|
||||
}
|
||||
return subOpts
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
//go:build !windows
|
||||
|
||||
package env
|
||||
|
||||
|
@ -9,7 +8,9 @@ func toMap(env []string) map[string]string {
|
|||
r := map[string]string{}
|
||||
for _, e := range env {
|
||||
p := strings.SplitN(e, "=", 2)
|
||||
r[p[0]] = p[1]
|
||||
if len(p) == 2 {
|
||||
r[p[0]] = p[1]
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
//go:build windows
|
||||
|
||||
package env
|
||||
|
||||
import "strings"
|
||||
|
@ -19,7 +21,9 @@ func toMap(env []string) map[string]string {
|
|||
p[0] = "=" + p[0]
|
||||
}
|
||||
|
||||
r[p[0]] = p[1]
|
||||
if len(p) == 2 {
|
||||
r[p[0]] = p[1]
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
|
@ -89,7 +89,7 @@ func (e NoParserError) Error() string {
|
|||
}
|
||||
|
||||
// This error occurs when the given tag is not supported
|
||||
// In-built supported tags: "", "file", "required", "unset", "notEmpty", "envDefault", "envExpand", "envSeparator"
|
||||
// In-built supported tags: "", "file", "required", "unset", "notEmpty", "expand", "envDefault", "envSeparator"
|
||||
// How to create a custom tag: https://github.com/caarlos0/env#changing-default-tag-name
|
||||
type NoSupportedTagOptionError struct {
|
||||
Tag string
|
44
vendor/golang.org/x/exp/slices/cmp.go
generated
vendored
Normal file
44
vendor/golang.org/x/exp/slices/cmp.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2023 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 slices
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
|
||||
// min is a version of the predeclared function from the Go 1.21 release.
|
||||
func min[T constraints.Ordered](a, b T) T {
|
||||
if a < b || isNaN(a) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// max is a version of the predeclared function from the Go 1.21 release.
|
||||
func max[T constraints.Ordered](a, b T) T {
|
||||
if a > b || isNaN(a) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// cmpLess is a copy of cmp.Less from the Go 1.21 release.
|
||||
func cmpLess[T constraints.Ordered](x, y T) bool {
|
||||
return (isNaN(x) && !isNaN(y)) || x < y
|
||||
}
|
||||
|
||||
// cmpCompare is a copy of cmp.Compare from the Go 1.21 release.
|
||||
func cmpCompare[T constraints.Ordered](x, y T) int {
|
||||
xNaN := isNaN(x)
|
||||
yNaN := isNaN(y)
|
||||
if xNaN && yNaN {
|
||||
return 0
|
||||
}
|
||||
if xNaN || x < y {
|
||||
return -1
|
||||
}
|
||||
if yNaN || x > y {
|
||||
return +1
|
||||
}
|
||||
return 0
|
||||
}
|
353
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
353
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
|
@ -3,23 +3,20 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package slices defines various functions useful with slices of any type.
|
||||
// Unless otherwise specified, these functions all apply to the elements
|
||||
// of a slice at index 0 <= i < len(s).
|
||||
//
|
||||
// Note that the less function in IsSortedFunc, SortFunc, SortStableFunc requires a
|
||||
// strict weak ordering (https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings),
|
||||
// or the sorting may fail to sort correctly. A common case is when sorting slices of
|
||||
// floating-point numbers containing NaN values.
|
||||
package slices
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// Equal reports whether two slices are equal: the same length and all
|
||||
// elements equal. If the lengths are different, Equal returns false.
|
||||
// Otherwise, the elements are compared in increasing index order, and the
|
||||
// comparison stops at the first unequal pair.
|
||||
// Floating point NaNs are not considered equal.
|
||||
func Equal[E comparable](s1, s2 []E) bool {
|
||||
func Equal[S ~[]E, E comparable](s1, s2 S) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
|
@ -31,12 +28,12 @@ func Equal[E comparable](s1, s2 []E) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// EqualFunc reports whether two slices are equal using a comparison
|
||||
// EqualFunc reports whether two slices are equal using an equality
|
||||
// function on each pair of elements. If the lengths are different,
|
||||
// EqualFunc returns false. Otherwise, the elements are compared in
|
||||
// increasing index order, and the comparison stops at the first index
|
||||
// for which eq returns false.
|
||||
func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool {
|
||||
func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
|
@ -49,45 +46,37 @@ func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Compare compares the elements of s1 and s2.
|
||||
// The elements are compared sequentially, starting at index 0,
|
||||
// Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair
|
||||
// of elements. The elements are compared sequentially, starting at index 0,
|
||||
// until one element is not equal to the other.
|
||||
// The result of comparing the first non-matching elements is returned.
|
||||
// If both slices are equal until one of them ends, the shorter slice is
|
||||
// considered less than the longer one.
|
||||
// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
|
||||
// Comparisons involving floating point NaNs are ignored.
|
||||
func Compare[E constraints.Ordered](s1, s2 []E) int {
|
||||
s2len := len(s2)
|
||||
func Compare[S ~[]E, E constraints.Ordered](s1, s2 S) int {
|
||||
for i, v1 := range s1 {
|
||||
if i >= s2len {
|
||||
if i >= len(s2) {
|
||||
return +1
|
||||
}
|
||||
v2 := s2[i]
|
||||
switch {
|
||||
case v1 < v2:
|
||||
return -1
|
||||
case v1 > v2:
|
||||
return +1
|
||||
if c := cmpCompare(v1, v2); c != 0 {
|
||||
return c
|
||||
}
|
||||
}
|
||||
if len(s1) < s2len {
|
||||
if len(s1) < len(s2) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CompareFunc is like Compare but uses a comparison function
|
||||
// on each pair of elements. The elements are compared in increasing
|
||||
// index order, and the comparisons stop after the first time cmp
|
||||
// returns non-zero.
|
||||
// CompareFunc is like [Compare] but uses a custom comparison function on each
|
||||
// pair of elements.
|
||||
// The result is the first non-zero result of cmp; if cmp always
|
||||
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
|
||||
// and +1 if len(s1) > len(s2).
|
||||
func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
|
||||
s2len := len(s2)
|
||||
func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int {
|
||||
for i, v1 := range s1 {
|
||||
if i >= s2len {
|
||||
if i >= len(s2) {
|
||||
return +1
|
||||
}
|
||||
v2 := s2[i]
|
||||
|
@ -95,7 +84,7 @@ func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
|
|||
return c
|
||||
}
|
||||
}
|
||||
if len(s1) < s2len {
|
||||
if len(s1) < len(s2) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
|
@ -103,7 +92,7 @@ func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
|
|||
|
||||
// Index returns the index of the first occurrence of v in s,
|
||||
// or -1 if not present.
|
||||
func Index[E comparable](s []E, v E) int {
|
||||
func Index[S ~[]E, E comparable](s S, v E) int {
|
||||
for i := range s {
|
||||
if v == s[i] {
|
||||
return i
|
||||
|
@ -114,7 +103,7 @@ func Index[E comparable](s []E, v E) int {
|
|||
|
||||
// IndexFunc returns the first index i satisfying f(s[i]),
|
||||
// or -1 if none do.
|
||||
func IndexFunc[E any](s []E, f func(E) bool) int {
|
||||
func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
|
||||
for i := range s {
|
||||
if f(s[i]) {
|
||||
return i
|
||||
|
@ -124,39 +113,104 @@ func IndexFunc[E any](s []E, f func(E) bool) int {
|
|||
}
|
||||
|
||||
// Contains reports whether v is present in s.
|
||||
func Contains[E comparable](s []E, v E) bool {
|
||||
func Contains[S ~[]E, E comparable](s S, v E) bool {
|
||||
return Index(s, v) >= 0
|
||||
}
|
||||
|
||||
// ContainsFunc reports whether at least one
|
||||
// element e of s satisfies f(e).
|
||||
func ContainsFunc[E any](s []E, f func(E) bool) bool {
|
||||
func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool {
|
||||
return IndexFunc(s, f) >= 0
|
||||
}
|
||||
|
||||
// Insert inserts the values v... into s at index i,
|
||||
// returning the modified slice.
|
||||
// In the returned slice r, r[i] == v[0].
|
||||
// The elements at s[i:] are shifted up to make room.
|
||||
// In the returned slice r, r[i] == v[0],
|
||||
// and r[i+len(v)] == value originally at r[i].
|
||||
// Insert panics if i is out of range.
|
||||
// This function is O(len(s) + len(v)).
|
||||
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
|
||||
tot := len(s) + len(v)
|
||||
if tot <= cap(s) {
|
||||
s2 := s[:tot]
|
||||
copy(s2[i+len(v):], s[i:])
|
||||
m := len(v)
|
||||
if m == 0 {
|
||||
return s
|
||||
}
|
||||
n := len(s)
|
||||
if i == n {
|
||||
return append(s, v...)
|
||||
}
|
||||
if n+m > cap(s) {
|
||||
// Use append rather than make so that we bump the size of
|
||||
// the slice up to the next storage class.
|
||||
// This is what Grow does but we don't call Grow because
|
||||
// that might copy the values twice.
|
||||
s2 := append(s[:i], make(S, n+m-i)...)
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+m:], s[i:])
|
||||
return s2
|
||||
}
|
||||
s2 := make(S, tot)
|
||||
copy(s2, s[:i])
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+len(v):], s[i:])
|
||||
return s2
|
||||
s = s[:n+m]
|
||||
|
||||
// before:
|
||||
// s: aaaaaaaabbbbccccccccdddd
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// after:
|
||||
// s: aaaaaaaavvvvbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
//
|
||||
// a are the values that don't move in s.
|
||||
// v are the values copied in from v.
|
||||
// b and c are the values from s that are shifted up in index.
|
||||
// d are the values that get overwritten, never to be seen again.
|
||||
|
||||
if !overlaps(v, s[i+m:]) {
|
||||
// Easy case - v does not overlap either the c or d regions.
|
||||
// (It might be in some of a or b, or elsewhere entirely.)
|
||||
// The data we copy up doesn't write to v at all, so just do it.
|
||||
|
||||
copy(s[i+m:], s[i:])
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaabbbbbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// Note the b values are duplicated.
|
||||
|
||||
copy(s[i:], v)
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaavvvvbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// That's the result we want.
|
||||
return s
|
||||
}
|
||||
|
||||
// The hard case - v overlaps c or d. We can't just shift up
|
||||
// the data because we'd move or clobber the values we're trying
|
||||
// to insert.
|
||||
// So instead, write v on top of d, then rotate.
|
||||
copy(s[n:], v)
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaabbbbccccccccvvvv
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
|
||||
rotateRight(s[i:], m)
|
||||
|
||||
// Now we have
|
||||
// s: aaaaaaaavvvvbbbbcccccccc
|
||||
// ^ ^ ^ ^
|
||||
// i i+m n n+m
|
||||
// That's the result we want.
|
||||
return s
|
||||
}
|
||||
|
||||
// Delete removes the elements s[i:j] from s, returning the modified slice.
|
||||
// Delete panics if s[i:j] is not a valid slice of s.
|
||||
// Delete modifies the contents of the slice s; it does not create a new slice.
|
||||
// Delete is O(len(s)-j), so if many items must be deleted, it is better to
|
||||
// make a single call deleting them all together than to delete one at a time.
|
||||
// Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those
|
||||
|
@ -168,22 +222,113 @@ func Delete[S ~[]E, E any](s S, i, j int) S {
|
|||
return append(s[:i], s[j:]...)
|
||||
}
|
||||
|
||||
// DeleteFunc removes any elements from s for which del returns true,
|
||||
// returning the modified slice.
|
||||
// When DeleteFunc removes m elements, it might not modify the elements
|
||||
// s[len(s)-m:len(s)]. If those elements contain pointers you might consider
|
||||
// zeroing those elements so that objects they reference can be garbage
|
||||
// collected.
|
||||
func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
|
||||
i := IndexFunc(s, del)
|
||||
if i == -1 {
|
||||
return s
|
||||
}
|
||||
// Don't start copying elements until we find one to delete.
|
||||
for j := i + 1; j < len(s); j++ {
|
||||
if v := s[j]; !del(v) {
|
||||
s[i] = v
|
||||
i++
|
||||
}
|
||||
}
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
// Replace replaces the elements s[i:j] by the given v, and returns the
|
||||
// modified slice. Replace panics if s[i:j] is not a valid slice of s.
|
||||
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
|
||||
_ = s[i:j] // verify that i:j is a valid subslice
|
||||
|
||||
if i == j {
|
||||
return Insert(s, i, v...)
|
||||
}
|
||||
if j == len(s) {
|
||||
return append(s[:i], v...)
|
||||
}
|
||||
|
||||
tot := len(s[:i]) + len(v) + len(s[j:])
|
||||
if tot <= cap(s) {
|
||||
s2 := s[:tot]
|
||||
copy(s2[i+len(v):], s[j:])
|
||||
if tot > cap(s) {
|
||||
// Too big to fit, allocate and copy over.
|
||||
s2 := append(s[:i], make(S, tot-i)...) // See Insert
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+len(v):], s[j:])
|
||||
return s2
|
||||
}
|
||||
s2 := make(S, tot)
|
||||
copy(s2, s[:i])
|
||||
copy(s2[i:], v)
|
||||
copy(s2[i+len(v):], s[j:])
|
||||
return s2
|
||||
|
||||
r := s[:tot]
|
||||
|
||||
if i+len(v) <= j {
|
||||
// Easy, as v fits in the deleted portion.
|
||||
copy(r[i:], v)
|
||||
if i+len(v) != j {
|
||||
copy(r[i+len(v):], s[j:])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// We are expanding (v is bigger than j-i).
|
||||
// The situation is something like this:
|
||||
// (example has i=4,j=8,len(s)=16,len(v)=6)
|
||||
// s: aaaaxxxxbbbbbbbbyy
|
||||
// ^ ^ ^ ^
|
||||
// i j len(s) tot
|
||||
// a: prefix of s
|
||||
// x: deleted range
|
||||
// b: more of s
|
||||
// y: area to expand into
|
||||
|
||||
if !overlaps(r[i+len(v):], v) {
|
||||
// Easy, as v is not clobbered by the first copy.
|
||||
copy(r[i+len(v):], s[j:])
|
||||
copy(r[i:], v)
|
||||
return r
|
||||
}
|
||||
|
||||
// This is a situation where we don't have a single place to which
|
||||
// we can copy v. Parts of it need to go to two different places.
|
||||
// We want to copy the prefix of v into y and the suffix into x, then
|
||||
// rotate |y| spots to the right.
|
||||
//
|
||||
// v[2:] v[:2]
|
||||
// | |
|
||||
// s: aaaavvvvbbbbbbbbvv
|
||||
// ^ ^ ^ ^
|
||||
// i j len(s) tot
|
||||
//
|
||||
// If either of those two destinations don't alias v, then we're good.
|
||||
y := len(v) - (j - i) // length of y portion
|
||||
|
||||
if !overlaps(r[i:j], v) {
|
||||
copy(r[i:j], v[y:])
|
||||
copy(r[len(s):], v[:y])
|
||||
rotateRight(r[i:], y)
|
||||
return r
|
||||
}
|
||||
if !overlaps(r[len(s):], v) {
|
||||
copy(r[len(s):], v[:y])
|
||||
copy(r[i:j], v[y:])
|
||||
rotateRight(r[i:], y)
|
||||
return r
|
||||
}
|
||||
|
||||
// Now we know that v overlaps both x and y.
|
||||
// That means that the entirety of b is *inside* v.
|
||||
// So we don't need to preserve b at all; instead we
|
||||
// can copy v first, then copy the b part of v out of
|
||||
// v to the right destination.
|
||||
k := startIdx(v, s[j:])
|
||||
copy(r[i:], v)
|
||||
copy(r[i+len(v):], r[i+k:])
|
||||
return r
|
||||
}
|
||||
|
||||
// Clone returns a copy of the slice.
|
||||
|
@ -198,7 +343,8 @@ func Clone[S ~[]E, E any](s S) S {
|
|||
|
||||
// Compact replaces consecutive runs of equal elements with a single copy.
|
||||
// This is like the uniq command found on Unix.
|
||||
// Compact modifies the contents of the slice s; it does not create a new slice.
|
||||
// Compact modifies the contents of the slice s and returns the modified slice,
|
||||
// which may have a smaller length.
|
||||
// When Compact discards m elements in total, it might not modify the elements
|
||||
// s[len(s)-m:len(s)]. If those elements contain pointers you might consider
|
||||
// zeroing those elements so that objects they reference can be garbage collected.
|
||||
|
@ -218,7 +364,8 @@ func Compact[S ~[]E, E comparable](s S) S {
|
|||
return s[:i]
|
||||
}
|
||||
|
||||
// CompactFunc is like Compact but uses a comparison function.
|
||||
// CompactFunc is like [Compact] but uses an equality function to compare elements.
|
||||
// For runs of elements that compare equal, CompactFunc keeps the first one.
|
||||
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
|
||||
if len(s) < 2 {
|
||||
return s
|
||||
|
@ -256,3 +403,97 @@ func Grow[S ~[]E, E any](s S, n int) S {
|
|||
func Clip[S ~[]E, E any](s S) S {
|
||||
return s[:len(s):len(s)]
|
||||
}
|
||||
|
||||
// Rotation algorithm explanation:
|
||||
//
|
||||
// rotate left by 2
|
||||
// start with
|
||||
// 0123456789
|
||||
// split up like this
|
||||
// 01 234567 89
|
||||
// swap first 2 and last 2
|
||||
// 89 234567 01
|
||||
// join first parts
|
||||
// 89234567 01
|
||||
// recursively rotate first left part by 2
|
||||
// 23456789 01
|
||||
// join at the end
|
||||
// 2345678901
|
||||
//
|
||||
// rotate left by 8
|
||||
// start with
|
||||
// 0123456789
|
||||
// split up like this
|
||||
// 01 234567 89
|
||||
// swap first 2 and last 2
|
||||
// 89 234567 01
|
||||
// join last parts
|
||||
// 89 23456701
|
||||
// recursively rotate second part left by 6
|
||||
// 89 01234567
|
||||
// join at the end
|
||||
// 8901234567
|
||||
|
||||
// TODO: There are other rotate algorithms.
|
||||
// This algorithm has the desirable property that it moves each element exactly twice.
|
||||
// The triple-reverse algorithm is simpler and more cache friendly, but takes more writes.
|
||||
// The follow-cycles algorithm can be 1-write but it is not very cache friendly.
|
||||
|
||||
// rotateLeft rotates b left by n spaces.
|
||||
// s_final[i] = s_orig[i+r], wrapping around.
|
||||
func rotateLeft[E any](s []E, r int) {
|
||||
for r != 0 && r != len(s) {
|
||||
if r*2 <= len(s) {
|
||||
swap(s[:r], s[len(s)-r:])
|
||||
s = s[:len(s)-r]
|
||||
} else {
|
||||
swap(s[:len(s)-r], s[r:])
|
||||
s, r = s[len(s)-r:], r*2-len(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
func rotateRight[E any](s []E, r int) {
|
||||
rotateLeft(s, len(s)-r)
|
||||
}
|
||||
|
||||
// swap swaps the contents of x and y. x and y must be equal length and disjoint.
|
||||
func swap[E any](x, y []E) {
|
||||
for i := 0; i < len(x); i++ {
|
||||
x[i], y[i] = y[i], x[i]
|
||||
}
|
||||
}
|
||||
|
||||
// overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap.
|
||||
func overlaps[E any](a, b []E) bool {
|
||||
if len(a) == 0 || len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
elemSize := unsafe.Sizeof(a[0])
|
||||
if elemSize == 0 {
|
||||
return false
|
||||
}
|
||||
// TODO: use a runtime/unsafe facility once one becomes available. See issue 12445.
|
||||
// Also see crypto/internal/alias/alias.go:AnyOverlap
|
||||
return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) &&
|
||||
uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1)
|
||||
}
|
||||
|