:pin: upgraded and vendored dependencies

This commit is contained in:
Maxim Lebedev 2023-08-05 08:50:52 +06:00
parent 98576889a2
commit 0d4843e4f9
Signed by: toby3d
GPG key ID: 1F14E25B7C119FC5
116 changed files with 59904 additions and 41212 deletions

16
go.mod
View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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