Bump github.com/spf13/viper from 1.20.1 to 1.21.0 (#28)

Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.20.1 to 1.21.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.20.1...v1.21.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-version: 1.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot]
2025-09-09 09:56:53 +03:00
committed by GitHub
parent 0f1baa900f
commit e39f887db6
94 changed files with 4391 additions and 4306 deletions

19
go.mod
View File

@@ -4,22 +4,21 @@ go 1.24.2
require ( require (
github.com/gorilla/feeds v1.2.0 github.com/gorilla/feeds v1.2.0
github.com/spf13/viper v1.20.1 github.com/spf13/viper v1.21.0
github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300
) )
require ( require (
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.14.0 // indirect github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/sys v0.32.0 // indirect golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect golang.org/x/text v0.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

40
go.sum
View File

@@ -14,36 +14,36 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 h1:XQdibLKagjdevRB6vAjVY4qbSr8rQ610YzTkWcxzxSI= github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 h1:XQdibLKagjdevRB6vAjVY4qbSr8rQ610YzTkWcxzxSI=
github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300/go.mod h1:FNa/dfN95vAYCNFrIKRrlRo+MBLbwmR9Asa5f2ljmBI= github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300/go.mod h1:FNa/dfN95vAYCNFrIKRrlRo+MBLbwmR9Asa5f2ljmBI=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -113,7 +113,7 @@ dockers:
checksum: checksum:
name_template: 'sha256sums.txt' name_template: 'sha256sums.txt'
snapshot: snapshot:
name_template: "{{ incpatch .Version }}-next" version_template: "{{ incpatch .Version }}-next"
release: release:
github: github:
owner: pelletier owner: pelletier

View File

@@ -59,7 +59,7 @@ func (d *Decoder) DisallowUnknownFields() *Decoder {
// //
// With this feature enabled, types implementing the unstable/Unmarshaler // With this feature enabled, types implementing the unstable/Unmarshaler
// interface can be decoded from any structure of the document. It allows types // interface can be decoded from any structure of the document. It allows types
// that don't have a straightfoward TOML representation to provide their own // that don't have a straightforward TOML representation to provide their own
// decoding logic. // decoding logic.
// //
// Currently, types can only decode from a single value. Tables and array tables // Currently, types can only decode from a single value. Tables and array tables

View File

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

View File

@@ -7,18 +7,21 @@ type FileType int
// FileType represents the kind of entries [Finder] can return. // FileType represents the kind of entries [Finder] can return.
const ( const (
FileTypeAll FileType = iota FileTypeAny FileType = iota
FileTypeFile FileTypeFile
FileTypeDir FileTypeDir
// Deprecated: Use [FileTypeAny] instead.
FileTypeAll = FileTypeAny
) )
func (ft FileType) matchFileInfo(info fs.FileInfo) bool { func (ft FileType) match(info fs.FileInfo) bool {
switch ft { switch ft {
case FileTypeAll: case FileTypeAny:
return true return true
case FileTypeFile: case FileTypeFile:
return !info.IsDir() return info.Mode().IsRegular()
case FileTypeDir: case FileTypeDir:
return info.IsDir() return info.IsDir()

View File

@@ -7,7 +7,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/sourcegraph/conc/iter" "github.com/sourcegraph/conc/pool"
"github.com/spf13/afero" "github.com/spf13/afero"
) )
@@ -44,70 +44,66 @@ type Finder struct {
// Find looks for files and directories in an [afero.Fs] filesystem. // Find looks for files and directories in an [afero.Fs] filesystem.
func (f Finder) Find(fsys afero.Fs) ([]string, error) { func (f Finder) Find(fsys afero.Fs) ([]string, error) {
// Arbitrary go routine limit (TODO: make this a parameter) // Arbitrary go routine limit (TODO: make this a parameter)
// pool := pool.NewWithResults[[]string]().WithMaxGoroutines(5).WithErrors().WithFirstError() p := pool.NewWithResults[[]searchResult]().WithMaxGoroutines(5).WithErrors().WithFirstError()
type searchItem struct {
path string
name string
}
var searchItems []searchItem
for _, searchPath := range f.Paths { for _, searchPath := range f.Paths {
searchPath := searchPath
for _, searchName := range f.Names { for _, searchName := range f.Names {
searchName := searchName p.Go(func() ([]searchResult, error) {
// If the name contains any glob character, perform a glob match
if strings.ContainsAny(searchName, globMatch) {
return globWalkSearch(fsys, searchPath, searchName, f.Type)
}
searchItems = append(searchItems, searchItem{searchPath, searchName}) return statSearch(fsys, searchPath, searchName, f.Type)
})
// pool.Go(func() ([]string, error) {
// // If the name contains any glob character, perform a glob match
// if strings.ContainsAny(searchName, globMatch) {
// return globWalkSearch(fsys, searchPath, searchName, f.Type)
// }
//
// return statSearch(fsys, searchPath, searchName, f.Type)
// })
} }
} }
// allResults, err := pool.Wait() searchResults, err := flatten(p.Wait())
// if err != nil {
// return nil, err
// }
allResults, err := iter.MapErr(searchItems, func(item *searchItem) ([]string, error) {
// If the name contains any glob character, perform a glob match
if strings.ContainsAny(item.name, globMatch) {
return globWalkSearch(fsys, item.path, item.name, f.Type)
}
return statSearch(fsys, item.path, item.name, f.Type)
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
var results []string // Return early if no results were found
if len(searchResults) == 0 {
for _, r := range allResults { return nil, nil
results = append(results, r...)
} }
// Sort results in alphabetical order for now results := make([]string, 0, len(searchResults))
// sort.Strings(results)
for _, searchResult := range searchResults {
results = append(results, searchResult.path)
}
return results, nil return results, nil
} }
type searchResult struct {
path string
info fs.FileInfo
}
func flatten[T any](results [][]T, err error) ([]T, error) {
if err != nil {
return nil, err
}
var flattened []T
for _, r := range results {
flattened = append(flattened, r...)
}
return flattened, nil
}
func globWalkSearch( func globWalkSearch(
fsys afero.Fs, fsys afero.Fs,
searchPath string, searchPath string,
searchName string, searchName string,
searchType FileType, searchType FileType,
) ([]string, error) { ) ([]searchResult, error) {
var results []string var results []searchResult
err := afero.Walk(fsys, searchPath, func(p string, fileInfo fs.FileInfo, err error) error { err := afero.Walk(fsys, searchPath, func(p string, fileInfo fs.FileInfo, err error) error {
if err != nil { if err != nil {
@@ -128,7 +124,7 @@ func globWalkSearch(
} }
// Skip unmatching type // Skip unmatching type
if !searchType.matchFileInfo(fileInfo) { if !searchType.match(fileInfo) {
return result return result
} }
@@ -138,7 +134,7 @@ func globWalkSearch(
} }
if match { if match {
results = append(results, p) results = append(results, searchResult{p, fileInfo})
} }
return result return result
@@ -155,7 +151,7 @@ func statSearch(
searchPath string, searchPath string,
searchName string, searchName string,
searchType FileType, searchType FileType,
) ([]string, error) { ) ([]searchResult, error) {
filePath := filepath.Join(searchPath, searchName) filePath := filepath.Join(searchPath, searchName)
fileInfo, err := fsys.Stat(filePath) fileInfo, err := fsys.Stat(filePath)
@@ -167,9 +163,9 @@ func statSearch(
} }
// Skip unmatching type // Skip unmatching type
if !searchType.matchFileInfo(fileInfo) { if !searchType.match(fileInfo) {
return nil, nil return nil, nil
} }
return []string{filePath}, nil return []searchResult{{filePath, fileInfo}}, nil
} }

View File

@@ -9,16 +9,20 @@
"devenv" "devenv"
], ],
"git-hooks": [ "git-hooks": [
"devenv" "devenv",
"git-hooks"
], ],
"nixpkgs": "nixpkgs" "nixpkgs": [
"devenv",
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1737621947, "lastModified": 1748883665,
"narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=", "narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
"owner": "cachix", "owner": "cachix",
"repo": "cachix", "repo": "cachix",
"rev": "f65a3cd5e339c223471e64c051434616e18cc4f5", "rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -34,14 +38,14 @@
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"git-hooks": "git-hooks", "git-hooks": "git-hooks",
"nix": "nix", "nix": "nix",
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1742998885, "lastModified": 1753981111,
"narHash": "sha256-xd2EwpUr+f/LLQjuKvFjXMe/fVyOAXkrKy986hMMuqs=", "narHash": "sha256-uBJOyMxOkGRmxhD2M5rbN2aV6oP1T2AKq5oBaHHC4mw=",
"owner": "cachix", "owner": "cachix",
"repo": "devenv", "repo": "devenv",
"rev": "4e56212b1781ab297b506bfca0085bb0e8ba1cfb", "rev": "d4d70df706b153b601a87ab8e81c88a0b1a373b6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -53,11 +57,11 @@
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1733328505, "lastModified": 1747046372,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -75,11 +79,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1712014858, "lastModified": 1733312601,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -93,11 +97,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1741352980, "lastModified": 1753121425,
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", "narHash": "sha256-TVcTNvOeWWk1DXljFxVRp+E0tzG1LhrVjOGGoMHuXio=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", "rev": "644e0fc48951a860279da645ba77fe4a6e814c5e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -109,7 +113,8 @@
"git-hooks": { "git-hooks": {
"inputs": { "inputs": {
"flake-compat": [ "flake-compat": [
"devenv" "devenv",
"flake-compat"
], ],
"gitignore": "gitignore", "gitignore": "gitignore",
"nixpkgs": [ "nixpkgs": [
@@ -118,11 +123,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1740849354, "lastModified": 1750779888,
"narHash": "sha256-oy33+t09FraucSZ2rZ6qnD1Y1c8azKKmQuCvF2ytUko=", "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "4a709a8ce9f8c08fa7ddb86761fe488ff7858a07", "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -153,78 +158,66 @@
"type": "github" "type": "github"
} }
}, },
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": { "nix": {
"inputs": { "inputs": {
"flake-compat": [ "flake-compat": [
"devenv" "devenv",
"flake-compat"
], ],
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"libgit2": "libgit2", "git-hooks-nix": [
"nixpkgs": "nixpkgs_2", "devenv",
"git-hooks"
],
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-23-11": [ "nixpkgs-23-11": [
"devenv" "devenv"
], ],
"nixpkgs-regression": [ "nixpkgs-regression": [
"devenv" "devenv"
],
"pre-commit-hooks": [
"devenv"
] ]
}, },
"locked": { "locked": {
"lastModified": 1741798497, "lastModified": 1752773918,
"narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=", "narHash": "sha256-dOi/M6yNeuJlj88exI+7k154z+hAhFcuB8tZktiW7rg=",
"owner": "domenkozar", "owner": "cachix",
"repo": "nix", "repo": "nix",
"rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd", "rev": "031c3cf42d2e9391eee373507d8c12e0f9606779",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "domenkozar", "owner": "cachix",
"ref": "devenv-2.24", "ref": "devenv-2.30",
"repo": "nix", "repo": "nix",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1733212471, "lastModified": 1750441195,
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", "narHash": "sha256-yke+pm+MdgRb6c0dPt8MgDhv7fcBbdjmv1ZceNTyzKg=",
"owner": "NixOS", "owner": "cachix",
"repo": "nixpkgs", "repo": "devenv-nixpkgs",
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", "rev": "0ceffe312871b443929ff3006960d29b120dc627",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "cachix",
"ref": "nixos-unstable", "ref": "rolling",
"repo": "nixpkgs", "repo": "devenv-nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1740877520, "lastModified": 1751159883,
"narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=", "narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "147dee35aab2193b174e4c0868bd80ead5ce755c", "rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -235,43 +228,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1717432640, "lastModified": 1753939845,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", "narHash": "sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1+A4GQej/lBk5y7I=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", "rev": "94def634a20494ee057c76998843c015909d6311",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1733477122,
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1742889210,
"narHash": "sha256-hw63HnwnqU3ZQfsMclLhMvOezpM7RSB0dMAtD5/sOiw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "698214a32beb4f4c8e3942372c694f40848b360d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -285,7 +246,7 @@
"inputs": { "inputs": {
"devenv": "devenv", "devenv": "devenv",
"flake-parts": "flake-parts_2", "flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_4" "nixpkgs": "nixpkgs_2"
} }
} }
}, },

View File

@@ -1,6 +1,5 @@
{ {
inputs = { inputs = {
# Revert to nixpkgs-unstable once #392713 lands there: https://nixpk.gs/pr-tracker.html?pr=392713
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.url = "github:hercules-ci/flake-parts";
devenv.url = "github:cachix/devenv"; devenv.url = "github:cachix/devenv";
@@ -20,7 +19,7 @@
perSystem = perSystem =
{ pkgs, ... }: { pkgs, ... }:
rec { {
devenv.shells = { devenv.shells = {
default = { default = {
languages = { languages = {
@@ -37,24 +36,6 @@
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
containers = pkgs.lib.mkForce { }; containers = pkgs.lib.mkForce { };
}; };
ci = devenv.shells.default;
ci_1_23 = {
imports = [ devenv.shells.ci ];
languages = {
go.package = pkgs.go_1_23;
};
};
ci_1_24 = {
imports = [ devenv.shells.ci ];
languages = {
go.package = pkgs.go_1_24;
};
};
}; };
}; };
}; };

View File

@@ -2,7 +2,7 @@ default:
just --list just --list
test: test:
go test -race -v ./... go test -count 10 -shuffle on -race -v ./...
fuzz: fuzz:
go test -race -v -fuzz=Fuzz -fuzztime=60s ./... go test -race -v -fuzz=Fuzz -fuzztime=60s ./...

24
vendor/github.com/sourcegraph/conc/Makefile generated vendored Normal file
View File

@@ -0,0 +1,24 @@
.DEFAULT_GOAL := help
GO_BIN ?= $(shell go env GOPATH)/bin
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
$(GO_BIN)/golangci-lint:
@echo "==> Installing golangci-lint within "${GO_BIN}""
@go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest
.PHONY: lint
lint: $(GO_BIN)/golangci-lint ## Run linting on Go files
@echo "==> Linting Go source files"
@golangci-lint run -v --fix -c .golangci.yml ./...
.PHONY: test
test: ## Run tests
go test -race -v ./... -coverprofile ./coverage.txt
.PHONY: bench
bench: ## Run benchmarks. See https://pkg.go.dev/cmd/go#hdr-Testing_flags
go test ./... -bench . -benchtime 5s -timeout 0 -run=XXX -cpu 1 -benchmem

View File

@@ -1,10 +0,0 @@
//go:build !go1.20
// +build !go1.20
package multierror
import "go.uber.org/multierr"
var (
Join = multierr.Combine
)

View File

@@ -1,10 +0,0 @@
//go:build go1.20
// +build go1.20
package multierror
import "errors"
var (
Join = errors.Join
)

View File

@@ -1,85 +0,0 @@
package iter
import (
"runtime"
"sync/atomic"
"github.com/sourcegraph/conc"
)
// defaultMaxGoroutines returns the default maximum number of
// goroutines to use within this package.
func defaultMaxGoroutines() int { return runtime.GOMAXPROCS(0) }
// Iterator can be used to configure the behaviour of ForEach
// and ForEachIdx. The zero value is safe to use with reasonable
// defaults.
//
// Iterator is also safe for reuse and concurrent use.
type Iterator[T any] struct {
// MaxGoroutines controls the maximum number of goroutines
// to use on this Iterator's methods.
//
// If unset, MaxGoroutines defaults to runtime.GOMAXPROCS(0).
MaxGoroutines int
}
// ForEach executes f in parallel over each element in input.
//
// It is safe to mutate the input parameter, which makes it
// possible to map in place.
//
// ForEach always uses at most runtime.GOMAXPROCS goroutines.
// It takes roughly 2µs to start up the goroutines and adds
// an overhead of roughly 50ns per element of input. For
// a configurable goroutine limit, use a custom Iterator.
func ForEach[T any](input []T, f func(*T)) { Iterator[T]{}.ForEach(input, f) }
// ForEach executes f in parallel over each element in input,
// using up to the Iterator's configured maximum number of
// goroutines.
//
// It is safe to mutate the input parameter, which makes it
// possible to map in place.
//
// It takes roughly 2µs to start up the goroutines and adds
// an overhead of roughly 50ns per element of input.
func (iter Iterator[T]) ForEach(input []T, f func(*T)) {
iter.ForEachIdx(input, func(_ int, t *T) {
f(t)
})
}
// ForEachIdx is the same as ForEach except it also provides the
// index of the element to the callback.
func ForEachIdx[T any](input []T, f func(int, *T)) { Iterator[T]{}.ForEachIdx(input, f) }
// ForEachIdx is the same as ForEach except it also provides the
// index of the element to the callback.
func (iter Iterator[T]) ForEachIdx(input []T, f func(int, *T)) {
if iter.MaxGoroutines == 0 {
// iter is a value receiver and is hence safe to mutate
iter.MaxGoroutines = defaultMaxGoroutines()
}
numInput := len(input)
if iter.MaxGoroutines > numInput {
// No more concurrent tasks than the number of input items.
iter.MaxGoroutines = numInput
}
var idx atomic.Int64
// Create the task outside the loop to avoid extra closure allocations.
task := func() {
i := int(idx.Add(1) - 1)
for ; i < numInput; i = int(idx.Add(1) - 1) {
f(i, &input[i])
}
}
var wg conc.WaitGroup
for i := 0; i < iter.MaxGoroutines; i++ {
wg.Go(task)
}
wg.Wait()
}

View File

@@ -1,65 +0,0 @@
package iter
import (
"sync"
"github.com/sourcegraph/conc/internal/multierror"
)
// Mapper is an Iterator with a result type R. It can be used to configure
// the behaviour of Map and MapErr. The zero value is safe to use with
// reasonable defaults.
//
// Mapper is also safe for reuse and concurrent use.
type Mapper[T, R any] Iterator[T]
// Map applies f to each element of input, returning the mapped result.
//
// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
// goroutine limit, use a custom Mapper.
func Map[T, R any](input []T, f func(*T) R) []R {
return Mapper[T, R]{}.Map(input, f)
}
// Map applies f to each element of input, returning the mapped result.
//
// Map uses up to the configured Mapper's maximum number of goroutines.
func (m Mapper[T, R]) Map(input []T, f func(*T) R) []R {
res := make([]R, len(input))
Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
res[i] = f(t)
})
return res
}
// MapErr applies f to each element of the input, returning the mapped result
// and a combined error of all returned errors.
//
// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
// goroutine limit, use a custom Mapper.
func MapErr[T, R any](input []T, f func(*T) (R, error)) ([]R, error) {
return Mapper[T, R]{}.MapErr(input, f)
}
// MapErr applies f to each element of the input, returning the mapped result
// and a combined error of all returned errors.
//
// Map uses up to the configured Mapper's maximum number of goroutines.
func (m Mapper[T, R]) MapErr(input []T, f func(*T) (R, error)) ([]R, error) {
var (
res = make([]R, len(input))
errMux sync.Mutex
errs error
)
Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
var err error
res[i], err = f(t)
if err != nil {
errMux.Lock()
// TODO: use stdlib errors once multierrors land in go 1.20
errs = multierror.Join(errs, err)
errMux.Unlock()
}
})
return res, errs
}

104
vendor/github.com/sourcegraph/conc/pool/context_pool.go generated vendored Normal file
View File

@@ -0,0 +1,104 @@
package pool
import (
"context"
)
// ContextPool is a pool that runs tasks that take a context.
// A new ContextPool should be created with `New().WithContext(ctx)`.
//
// The configuration methods (With*) will panic if they are used after calling
// Go() for the first time.
type ContextPool struct {
errorPool ErrorPool
ctx context.Context
cancel context.CancelFunc
cancelOnError bool
}
// Go submits a task. If it returns an error, the error will be
// collected and returned by Wait(). If all goroutines in the pool
// are busy, a call to Go() will block until the task can be started.
func (p *ContextPool) Go(f func(ctx context.Context) error) {
p.errorPool.Go(func() error {
if p.cancelOnError {
// If we are cancelling on error, then we also want to cancel if a
// panic is raised. To do this, we need to recover, cancel, and then
// re-throw the caught panic.
defer func() {
if r := recover(); r != nil {
p.cancel()
panic(r)
}
}()
}
err := f(p.ctx)
if err != nil && p.cancelOnError {
// Leaky abstraction warning: We add the error directly because
// otherwise, canceling could cause another goroutine to exit and
// return an error before this error was added, which breaks the
// expectations of WithFirstError().
p.errorPool.addErr(err)
p.cancel()
return nil
}
return err
})
}
// Wait cleans up all spawned goroutines, propagates any panics, and
// returns an error if any of the tasks errored.
func (p *ContextPool) Wait() error {
// Make sure we call cancel after pool is done to avoid memory leakage.
defer p.cancel()
return p.errorPool.Wait()
}
// WithFirstError configures the pool to only return the first error
// returned by a task. By default, Wait() will return a combined error.
// This is particularly useful for (*ContextPool).WithCancelOnError(),
// where all errors after the first are likely to be context.Canceled.
func (p *ContextPool) WithFirstError() *ContextPool {
p.panicIfInitialized()
p.errorPool.WithFirstError()
return p
}
// WithCancelOnError configures the pool to cancel its context as soon as
// any task returns an error or panics. By default, the pool's context is not
// canceled until the parent context is canceled.
//
// In this case, all errors returned from the pool after the first will
// likely be context.Canceled - you may want to also use
// (*ContextPool).WithFirstError() to configure the pool to only return
// the first error.
func (p *ContextPool) WithCancelOnError() *ContextPool {
p.panicIfInitialized()
p.cancelOnError = true
return p
}
// WithFailFast is an alias for the combination of WithFirstError and
// WithCancelOnError. By default, the errors from all tasks are returned and
// the pool's context is not canceled until the parent context is canceled.
func (p *ContextPool) WithFailFast() *ContextPool {
p.panicIfInitialized()
p.WithFirstError()
p.WithCancelOnError()
return p
}
// WithMaxGoroutines limits the number of goroutines in a pool.
// Defaults to unlimited. Panics if n < 1.
func (p *ContextPool) WithMaxGoroutines(n int) *ContextPool {
p.panicIfInitialized()
p.errorPool.WithMaxGoroutines(n)
return p
}
func (p *ContextPool) panicIfInitialized() {
p.errorPool.panicIfInitialized()
}

100
vendor/github.com/sourcegraph/conc/pool/error_pool.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
package pool
import (
"context"
"errors"
"sync"
)
// ErrorPool is a pool that runs tasks that may return an error.
// Errors are collected and returned by Wait().
//
// The configuration methods (With*) will panic if they are used after calling
// Go() for the first time.
//
// A new ErrorPool should be created using `New().WithErrors()`.
type ErrorPool struct {
pool Pool
onlyFirstError bool
mu sync.Mutex
errs []error
}
// Go submits a task to the pool. If all goroutines in the pool
// are busy, a call to Go() will block until the task can be started.
func (p *ErrorPool) Go(f func() error) {
p.pool.Go(func() {
p.addErr(f())
})
}
// Wait cleans up any spawned goroutines, propagating any panics and
// returning any errors from tasks.
func (p *ErrorPool) Wait() error {
p.pool.Wait()
errs := p.errs
p.errs = nil // reset errs
if len(errs) == 0 {
return nil
} else if p.onlyFirstError {
return errs[0]
} else {
return errors.Join(errs...)
}
}
// WithContext converts the pool to a ContextPool for tasks that should
// run under the same context, such that they each respect shared cancellation.
// For example, WithCancelOnError can be configured on the returned pool to
// signal that all goroutines should be cancelled upon the first error.
func (p *ErrorPool) WithContext(ctx context.Context) *ContextPool {
p.panicIfInitialized()
ctx, cancel := context.WithCancel(ctx)
return &ContextPool{
errorPool: p.deref(),
ctx: ctx,
cancel: cancel,
}
}
// WithFirstError configures the pool to only return the first error
// returned by a task. By default, Wait() will return a combined error.
func (p *ErrorPool) WithFirstError() *ErrorPool {
p.panicIfInitialized()
p.onlyFirstError = true
return p
}
// WithMaxGoroutines limits the number of goroutines in a pool.
// Defaults to unlimited. Panics if n < 1.
func (p *ErrorPool) WithMaxGoroutines(n int) *ErrorPool {
p.panicIfInitialized()
p.pool.WithMaxGoroutines(n)
return p
}
// deref is a helper that creates a shallow copy of the pool with the same
// settings. We don't want to just dereference the pointer because that makes
// the copylock lint angry.
func (p *ErrorPool) deref() ErrorPool {
return ErrorPool{
pool: p.pool.deref(),
onlyFirstError: p.onlyFirstError,
}
}
func (p *ErrorPool) panicIfInitialized() {
p.pool.panicIfInitialized()
}
func (p *ErrorPool) addErr(err error) {
if err != nil {
p.mu.Lock()
p.errs = append(p.errs, err)
p.mu.Unlock()
}
}

174
vendor/github.com/sourcegraph/conc/pool/pool.go generated vendored Normal file
View File

@@ -0,0 +1,174 @@
package pool
import (
"context"
"sync"
"github.com/sourcegraph/conc"
)
// New creates a new Pool.
func New() *Pool {
return &Pool{}
}
// Pool is a pool of goroutines used to execute tasks concurrently.
//
// Tasks are submitted with Go(). Once all your tasks have been submitted, you
// must call Wait() to clean up any spawned goroutines and propagate any
// panics.
//
// Goroutines are started lazily, so creating a new pool is cheap. There will
// never be more goroutines spawned than there are tasks submitted.
//
// The configuration methods (With*) will panic if they are used after calling
// Go() for the first time.
//
// Pool is efficient, but not zero cost. It should not be used for very short
// tasks. Startup and teardown come with an overhead of around 1µs, and each
// task has an overhead of around 300ns.
type Pool struct {
handle conc.WaitGroup
limiter limiter
tasks chan func()
initOnce sync.Once
}
// Go submits a task to be run in the pool. If all goroutines in the pool
// are busy, a call to Go() will block until the task can be started.
func (p *Pool) Go(f func()) {
p.init()
if p.limiter == nil {
// No limit on the number of goroutines.
select {
case p.tasks <- f:
// A goroutine was available to handle the task.
default:
// No goroutine was available to handle the task.
// Spawn a new one and send it the task.
p.handle.Go(func() {
p.worker(f)
})
}
} else {
select {
case p.limiter <- struct{}{}:
// If we are below our limit, spawn a new worker rather
// than waiting for one to become available.
p.handle.Go(func() {
p.worker(f)
})
case p.tasks <- f:
// A worker is available and has accepted the task.
return
}
}
}
// Wait cleans up spawned goroutines, propagating any panics that were
// raised by a tasks.
func (p *Pool) Wait() {
p.init()
close(p.tasks)
// After Wait() returns, reset the struct so tasks will be reinitialized on
// next use. This better matches the behavior of sync.WaitGroup
defer func() { p.initOnce = sync.Once{} }()
p.handle.Wait()
}
// MaxGoroutines returns the maximum size of the pool.
func (p *Pool) MaxGoroutines() int {
return p.limiter.limit()
}
// WithMaxGoroutines limits the number of goroutines in a pool.
// Defaults to unlimited. Panics if n < 1.
func (p *Pool) WithMaxGoroutines(n int) *Pool {
p.panicIfInitialized()
if n < 1 {
panic("max goroutines in a pool must be greater than zero")
}
p.limiter = make(limiter, n)
return p
}
// init ensures that the pool is initialized before use. This makes the
// zero value of the pool usable.
func (p *Pool) init() {
p.initOnce.Do(func() {
p.tasks = make(chan func())
})
}
// panicIfInitialized will trigger a panic if a configuration method is called
// after the pool has started any goroutines for the first time. In the case that
// new settings are needed, a new pool should be created.
func (p *Pool) panicIfInitialized() {
if p.tasks != nil {
panic("pool can not be reconfigured after calling Go() for the first time")
}
}
// WithErrors converts the pool to an ErrorPool so the submitted tasks can
// return errors.
func (p *Pool) WithErrors() *ErrorPool {
p.panicIfInitialized()
return &ErrorPool{
pool: p.deref(),
}
}
// deref is a helper that creates a shallow copy of the pool with the same
// settings. We don't want to just dereference the pointer because that makes
// the copylock lint angry.
func (p *Pool) deref() Pool {
p.panicIfInitialized()
return Pool{
limiter: p.limiter,
}
}
// WithContext converts the pool to a ContextPool for tasks that should
// run under the same context, such that they each respect shared cancellation.
// For example, WithCancelOnError can be configured on the returned pool to
// signal that all goroutines should be cancelled upon the first error.
func (p *Pool) WithContext(ctx context.Context) *ContextPool {
p.panicIfInitialized()
ctx, cancel := context.WithCancel(ctx)
return &ContextPool{
errorPool: p.WithErrors().deref(),
ctx: ctx,
cancel: cancel,
}
}
func (p *Pool) worker(initialFunc func()) {
// The only time this matters is if the task panics.
// This makes it possible to spin up new workers in that case.
defer p.limiter.release()
if initialFunc != nil {
initialFunc()
}
for f := range p.tasks {
f()
}
}
type limiter chan struct{}
func (l limiter) limit() int {
return cap(l)
}
func (l limiter) release() {
if l != nil {
<-l
}
}

View File

@@ -0,0 +1,85 @@
package pool
import (
"context"
)
// ResultContextPool is a pool that runs tasks that take a context and return a
// result. The context passed to the task will be canceled if any of the tasks
// return an error, which makes its functionality different than just capturing
// a context with the task closure.
//
// The configuration methods (With*) will panic if they are used after calling
// Go() for the first time.
type ResultContextPool[T any] struct {
contextPool ContextPool
agg resultAggregator[T]
collectErrored bool
}
// Go submits a task to the pool. If all goroutines in the pool
// are busy, a call to Go() will block until the task can be started.
func (p *ResultContextPool[T]) Go(f func(context.Context) (T, error)) {
idx := p.agg.nextIndex()
p.contextPool.Go(func(ctx context.Context) error {
res, err := f(ctx)
p.agg.save(idx, res, err != nil)
return err
})
}
// Wait cleans up all spawned goroutines, propagates any panics, and
// returns an error if any of the tasks errored.
func (p *ResultContextPool[T]) Wait() ([]T, error) {
err := p.contextPool.Wait()
results := p.agg.collect(p.collectErrored)
p.agg = resultAggregator[T]{}
return results, err
}
// WithCollectErrored configures the pool to still collect the result of a task
// even if the task returned an error. By default, the result of tasks that errored
// are ignored and only the error is collected.
func (p *ResultContextPool[T]) WithCollectErrored() *ResultContextPool[T] {
p.panicIfInitialized()
p.collectErrored = true
return p
}
// WithFirstError configures the pool to only return the first error
// returned by a task. By default, Wait() will return a combined error.
func (p *ResultContextPool[T]) WithFirstError() *ResultContextPool[T] {
p.panicIfInitialized()
p.contextPool.WithFirstError()
return p
}
// WithCancelOnError configures the pool to cancel its context as soon as
// any task returns an error. By default, the pool's context is not
// canceled until the parent context is canceled.
func (p *ResultContextPool[T]) WithCancelOnError() *ResultContextPool[T] {
p.panicIfInitialized()
p.contextPool.WithCancelOnError()
return p
}
// WithFailFast is an alias for the combination of WithFirstError and
// WithCancelOnError. By default, the errors from all tasks are returned and
// the pool's context is not canceled until the parent context is canceled.
func (p *ResultContextPool[T]) WithFailFast() *ResultContextPool[T] {
p.panicIfInitialized()
p.contextPool.WithFailFast()
return p
}
// WithMaxGoroutines limits the number of goroutines in a pool.
// Defaults to unlimited. Panics if n < 1.
func (p *ResultContextPool[T]) WithMaxGoroutines(n int) *ResultContextPool[T] {
p.panicIfInitialized()
p.contextPool.WithMaxGoroutines(n)
return p
}
func (p *ResultContextPool[T]) panicIfInitialized() {
p.contextPool.panicIfInitialized()
}

View File

@@ -0,0 +1,80 @@
package pool
import (
"context"
)
// ResultErrorPool is a pool that executes tasks that return a generic result
// type and an error. Tasks are executed in the pool with Go(), then the
// results of the tasks are returned by Wait().
//
// The order of the results is guaranteed to be the same as the order the
// tasks were submitted.
//
// The configuration methods (With*) will panic if they are used after calling
// Go() for the first time.
type ResultErrorPool[T any] struct {
errorPool ErrorPool
agg resultAggregator[T]
collectErrored bool
}
// Go submits a task to the pool. If all goroutines in the pool
// are busy, a call to Go() will block until the task can be started.
func (p *ResultErrorPool[T]) Go(f func() (T, error)) {
idx := p.agg.nextIndex()
p.errorPool.Go(func() error {
res, err := f()
p.agg.save(idx, res, err != nil)
return err
})
}
// Wait cleans up any spawned goroutines, propagating any panics and
// returning the results and any errors from tasks.
func (p *ResultErrorPool[T]) Wait() ([]T, error) {
err := p.errorPool.Wait()
results := p.agg.collect(p.collectErrored)
p.agg = resultAggregator[T]{} // reset for reuse
return results, err
}
// WithCollectErrored configures the pool to still collect the result of a task
// even if the task returned an error. By default, the result of tasks that errored
// are ignored and only the error is collected.
func (p *ResultErrorPool[T]) WithCollectErrored() *ResultErrorPool[T] {
p.panicIfInitialized()
p.collectErrored = true
return p
}
// WithContext converts the pool to a ResultContextPool for tasks that should
// run under the same context, such that they each respect shared cancellation.
// For example, WithCancelOnError can be configured on the returned pool to
// signal that all goroutines should be cancelled upon the first error.
func (p *ResultErrorPool[T]) WithContext(ctx context.Context) *ResultContextPool[T] {
p.panicIfInitialized()
return &ResultContextPool[T]{
contextPool: *p.errorPool.WithContext(ctx),
}
}
// WithFirstError configures the pool to only return the first error
// returned by a task. By default, Wait() will return a combined error.
func (p *ResultErrorPool[T]) WithFirstError() *ResultErrorPool[T] {
p.panicIfInitialized()
p.errorPool.WithFirstError()
return p
}
// WithMaxGoroutines limits the number of goroutines in a pool.
// Defaults to unlimited. Panics if n < 1.
func (p *ResultErrorPool[T]) WithMaxGoroutines(n int) *ResultErrorPool[T] {
p.panicIfInitialized()
p.errorPool.WithMaxGoroutines(n)
return p
}
func (p *ResultErrorPool[T]) panicIfInitialized() {
p.errorPool.panicIfInitialized()
}

142
vendor/github.com/sourcegraph/conc/pool/result_pool.go generated vendored Normal file
View File

@@ -0,0 +1,142 @@
package pool
import (
"context"
"sort"
"sync"
)
// NewWithResults creates a new ResultPool for tasks with a result of type T.
//
// The configuration methods (With*) will panic if they are used after calling
// Go() for the first time.
func NewWithResults[T any]() *ResultPool[T] {
return &ResultPool[T]{
pool: *New(),
}
}
// ResultPool is a pool that executes tasks that return a generic result type.
// Tasks are executed in the pool with Go(), then the results of the tasks are
// returned by Wait().
//
// The order of the results is guaranteed to be the same as the order the
// tasks were submitted.
type ResultPool[T any] struct {
pool Pool
agg resultAggregator[T]
}
// Go submits a task to the pool. If all goroutines in the pool
// are busy, a call to Go() will block until the task can be started.
func (p *ResultPool[T]) Go(f func() T) {
idx := p.agg.nextIndex()
p.pool.Go(func() {
p.agg.save(idx, f(), false)
})
}
// Wait cleans up all spawned goroutines, propagating any panics, and returning
// a slice of results from tasks that did not panic.
func (p *ResultPool[T]) Wait() []T {
p.pool.Wait()
results := p.agg.collect(true)
p.agg = resultAggregator[T]{} // reset for reuse
return results
}
// MaxGoroutines returns the maximum size of the pool.
func (p *ResultPool[T]) MaxGoroutines() int {
return p.pool.MaxGoroutines()
}
// WithErrors converts the pool to an ResultErrorPool so the submitted tasks
// can return errors.
func (p *ResultPool[T]) WithErrors() *ResultErrorPool[T] {
p.panicIfInitialized()
return &ResultErrorPool[T]{
errorPool: *p.pool.WithErrors(),
}
}
// WithContext converts the pool to a ResultContextPool for tasks that should
// run under the same context, such that they each respect shared cancellation.
// For example, WithCancelOnError can be configured on the returned pool to
// signal that all goroutines should be cancelled upon the first error.
func (p *ResultPool[T]) WithContext(ctx context.Context) *ResultContextPool[T] {
p.panicIfInitialized()
return &ResultContextPool[T]{
contextPool: *p.pool.WithContext(ctx),
}
}
// WithMaxGoroutines limits the number of goroutines in a pool.
// Defaults to unlimited. Panics if n < 1.
func (p *ResultPool[T]) WithMaxGoroutines(n int) *ResultPool[T] {
p.panicIfInitialized()
p.pool.WithMaxGoroutines(n)
return p
}
func (p *ResultPool[T]) panicIfInitialized() {
p.pool.panicIfInitialized()
}
// resultAggregator is a utility type that lets us safely append from multiple
// goroutines. The zero value is valid and ready to use.
type resultAggregator[T any] struct {
mu sync.Mutex
len int
results []T
errored []int
}
// nextIndex reserves a slot for a result. The returned value should be passed
// to save() when adding a result to the aggregator.
func (r *resultAggregator[T]) nextIndex() int {
r.mu.Lock()
defer r.mu.Unlock()
nextIdx := r.len
r.len += 1
return nextIdx
}
func (r *resultAggregator[T]) save(i int, res T, errored bool) {
r.mu.Lock()
defer r.mu.Unlock()
if i >= len(r.results) {
old := r.results
r.results = make([]T, r.len)
copy(r.results, old)
}
r.results[i] = res
if errored {
r.errored = append(r.errored, i)
}
}
// collect returns the set of aggregated results.
func (r *resultAggregator[T]) collect(collectErrored bool) []T {
if !r.mu.TryLock() {
panic("collect should not be called until all goroutines have exited")
}
if collectErrored || len(r.errored) == 0 {
return r.results
}
filtered := r.results[:0]
sort.Ints(r.errored)
for i, e := range r.errored {
if i == 0 {
filtered = append(filtered, r.results[:e]...)
} else {
filtered = append(filtered, r.results[r.errored[i-1]+1:e]...)
}
}
return filtered
}

View File

@@ -10,3 +10,6 @@ trim_trailing_whitespace = true
[*.go] [*.go]
indent_style = tab indent_style = tab
[{*.yml,*.yaml}]
indent_size = 2

View File

@@ -1,18 +1,48 @@
linters-settings: version: "2"
gci:
sections: run:
- standard timeout: 10m
- default
- prefix(github.com/spf13/afero)
linters: linters:
disable-all: true enable:
enable: - govet
- gci - ineffassign
- gofmt - misspell
- gofumpt - nolintlint
- staticcheck # - revive
- staticcheck
- unused
issues: disable:
exclude-dirs: - errcheck
- gcsfs/internal/stiface # - staticcheck
settings:
misspell:
locale: US
nolintlint:
allow-unused: false # report any unused nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
exclusions:
paths:
- gcsfs/internal/stiface
formatters:
enable:
- gci
- gofmt
- gofumpt
- goimports
- golines
settings:
gci:
sections:
- standard
- default
- localmodule
exclusions:
paths:
- gcsfs/internal/stiface

View File

@@ -1,479 +1,474 @@
![afero logo-sm](https://cloud.githubusercontent.com/assets/173412/11490338/d50e16dc-97a5-11e5-8b12-019a300d0fcb.png) <img src="https://cloud.githubusercontent.com/assets/173412/11490338/d50e16dc-97a5-11e5-8b12-019a300d0fcb.png" alt="afero logo-sm"/>
A FileSystem Abstraction System for Go
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/afero/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/afero/actions?query=workflow%3ACI)
[![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/afero?style=flat-square)](https://goreportcard.com/report/github.com/spf13/afero)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.23-61CFDD.svg?style=flat-square)
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/afero)](https://pkg.go.dev/mod/github.com/spf13/afero)
# Overview
Afero is a filesystem framework providing a simple, uniform and universal API
interacting with any filesystem, as an abstraction layer providing interfaces,
types and methods. Afero has an exceptionally clean interface and simple design
without needless constructors or initialization methods.
Afero is also a library providing a base set of interoperable backend
filesystems that make it easy to work with, while retaining all the power
and benefit of the os and ioutil packages.
Afero provides significant improvements over using the os package alone, most
notably the ability to create mock and testing filesystems without relying on the disk.
It is suitable for use in any situation where you would consider using the OS
package as it provides an additional abstraction that makes it easy to use a
memory backed file system during testing. It also adds support for the http
filesystem for full interoperability.
## Afero Features [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/afero/ci.yaml?branch=master&amp;style=flat-square)](https://github.com/spf13/afero/actions?query=workflow%3ACI)
[![GoDoc](https://pkg.go.dev/badge/mod/github.com/spf13/afero)](https://pkg.go.dev/mod/github.com/spf13/afero)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/afero)](https://goreportcard.com/report/github.com/spf13/afero)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.23-61CFDD.svg?style=flat-square")
* A single consistent API for accessing a variety of filesystems
* Interoperation between a variety of file system types
* A set of interfaces to encourage and enforce interoperability between backends
* An atomic cross platform memory backed file system
* Support for compositional (union) file systems by combining multiple file systems acting as one
* Specialized backends which modify existing filesystems (Read Only, Regexp filtered)
* A set of utility functions ported from io, ioutil & hugo to be afero aware
* Wrapper for go 1.16 filesystem abstraction `io/fs.FS`
# Using Afero # Afero: The Universal Filesystem Abstraction for Go
Afero is easy to use and easier to adopt. Afero is a powerful and extensible filesystem abstraction system for Go. It provides a single, unified API for interacting with diverse filesystems—including the local disk, memory, archives, and network storage.
A few different ways you could use Afero: Afero acts as a drop-in replacement for the standard `os` package, enabling you to write modular code that is agnostic to the underlying storage, dramatically simplifies testing, and allows for sophisticated architectural patterns through filesystem composition.
* Use the interfaces alone to define your own file system. ## Why Afero?
* Wrapper for the OS packages.
* Define different filesystems for different parts of your application.
* Use Afero for mock filesystems while testing
## Step 1: Install Afero Afero elevates filesystem interaction beyond simple file reading and writing, offering solutions for testability, flexibility, and advanced architecture.
First use go get to install the latest version of the library. 🔑 **Key Features:**
$ go get github.com/spf13/afero * **Universal API:** Write your code once. Run it against the local OS, in-memory storage, ZIP/TAR archives, or remote systems (SFTP, GCS).
* **Ultimate Testability:** Utilize `MemMapFs`, a fully concurrent-safe, read/write in-memory filesystem. Write fast, isolated, and reliable unit tests without touching the physical disk or worrying about cleanup.
* **Powerful Composition:** Afero's hidden superpower. Layer filesystems on top of each other to create sophisticated behaviors:
* **Sandboxing:** Use `CopyOnWriteFs` to create temporary scratch spaces that isolate changes from the base filesystem.
* **Caching:** Use `CacheOnReadFs` to automatically layer a fast cache (like memory) over a slow backend (like a network drive).
* **Security Jails:** Use `BasePathFs` to restrict application access to a specific subdirectory (chroot).
* **`os` Package Compatibility:** Afero mirrors the functions in the standard `os` package, making adoption and refactoring seamless.
* **`io/fs` Compatibility:** Fully compatible with the Go standard library's `io/fs` interfaces.
## Installation
```bash
go get github.com/spf13/afero
```
Next include Afero in your application.
```go ```go
import "github.com/spf13/afero" import "github.com/spf13/afero"
``` ```
## Step 2: Declare a backend ## Quick Start: The Power of Abstraction
First define a package variable and set it to a pointer to a filesystem. The core of Afero is the `afero.Fs` interface. By designing your functions to accept this interface rather than calling `os.*` functions directly, your code instantly becomes more flexible and testable.
```go
var AppFs = afero.NewMemMapFs()
or ### 1. Refactor Your Code
var AppFs = afero.NewOsFs() Change functions that rely on the `os` package to accept `afero.Fs`.
```
It is important to note that if you repeat the composite literal you
will be using a completely new and isolated filesystem. In the case of
OsFs it will still use the same underlying filesystem but will reduce
the ability to drop in other filesystems as desired.
## Step 3: Use it like you would the OS package
Throughout your application use any function and method like you normally
would.
So if my application before had:
```go
os.Open("/tmp/foo")
```
We would replace it with:
```go
AppFs.Open("/tmp/foo")
```
`AppFs` being the variable we defined above.
## List of all available functions
File System Methods Available:
```go
Chmod(name string, mode os.FileMode) : error
Chown(name string, uid, gid int) : error
Chtimes(name string, atime time.Time, mtime time.Time) : error
Create(name string) : File, error
Mkdir(name string, perm os.FileMode) : error
MkdirAll(path string, perm os.FileMode) : error
Name() : string
Open(name string) : File, error
OpenFile(name string, flag int, perm os.FileMode) : File, error
Remove(name string) : error
RemoveAll(path string) : error
Rename(oldname, newname string) : error
Stat(name string) : os.FileInfo, error
```
File Interfaces and Methods Available:
```go
io.Closer
io.Reader
io.ReaderAt
io.Seeker
io.Writer
io.WriterAt
Name() : string
Readdir(count int) : []os.FileInfo, error
Readdirnames(n int) : []string, error
Stat() : os.FileInfo, error
Sync() : error
Truncate(size int64) : error
WriteString(s string) : ret int, err error
```
In some applications it may make sense to define a new package that
simply exports the file system variable for easy access from anywhere.
## Using Afero's utility functions
Afero provides a set of functions to make it easier to use the underlying file systems.
These functions have been primarily ported from io & ioutil with some developed for Hugo.
The afero utilities support all afero compatible backends.
The list of utilities includes:
```go ```go
DirExists(path string) (bool, error) // Before: Coupled to the OS and difficult to test
Exists(path string) (bool, error) // func ProcessConfiguration(path string) error {
FileContainsBytes(filename string, subslice []byte) (bool, error) // data, err := os.ReadFile(path)
GetTempDir(subPath string) string // ...
IsDir(path string) (bool, error) // }
IsEmpty(path string) (bool, error)
ReadDir(dirname string) ([]os.FileInfo, error)
ReadFile(filename string) ([]byte, error)
SafeWriteReader(path string, r io.Reader) (err error)
TempDir(dir, prefix string) (name string, err error)
TempFile(dir, prefix string) (f File, err error)
Walk(root string, walkFn filepath.WalkFunc) error
WriteFile(filename string, data []byte, perm os.FileMode) error
WriteReader(path string, r io.Reader) (err error)
```
For a complete list see [Afero's GoDoc](https://godoc.org/github.com/spf13/afero)
They are available under two different approaches to use. You can either call import "github.com/spf13/afero"
them directly where the first parameter of each function will be the file
system, or you can declare a new `Afero`, a custom type used to bind these
functions as methods to a given filesystem.
### Calling utilities directly // After: Decoupled, flexible, and testable
func ProcessConfiguration(fs afero.Fs, path string) error {
```go // Use Afero utility functions which mirror os/ioutil
fs := new(afero.MemMapFs) data, err := afero.ReadFile(fs, path)
f, err := afero.TempFile(fs,"", "ioutil-test") // ... process the data
return err
```
### Calling via Afero
```go
fs := afero.NewMemMapFs()
afs := &afero.Afero{Fs: fs}
f, err := afs.TempFile("", "ioutil-test")
```
## Using Afero for Testing
There is a large benefit to using a mock filesystem for testing. It has a
completely blank state every time it is initialized and can be easily
reproducible regardless of OS. You could create files to your hearts content
and the file access would be fast while also saving you from all the annoying
issues with deleting temporary files, Windows file locking, etc. The MemMapFs
backend is perfect for testing.
* Much faster than performing I/O operations on disk
* Avoid security issues and permissions
* Far more control. 'rm -rf /' with confidence
* Test setup is far more easier to do
* No test cleanup needed
One way to accomplish this is to define a variable as mentioned above.
In your application this will be set to afero.NewOsFs() during testing you
can set it to afero.NewMemMapFs().
It wouldn't be uncommon to have each test initialize a blank slate memory
backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere
appropriate in my application code. This approach ensures that Tests are order
independent, with no test relying on the state left by an earlier test.
Then in my tests I would initialize a new MemMapFs for each test:
```go
func TestExist(t *testing.T) {
appFS := afero.NewMemMapFs()
// create test files and directories
appFS.MkdirAll("src/a", 0755)
afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644)
afero.WriteFile(appFS, "src/c", []byte("file c"), 0644)
name := "src/c"
_, err := appFS.Stat(name)
if os.IsNotExist(err) {
t.Errorf("file \"%s\" does not exist.\n", name)
}
} }
``` ```
# Available Backends ### 2. Usage in Production
## Operating System Native In your production environment, inject the `OsFs` backend, which wraps the standard operating system calls.
### OsFs
The first is simply a wrapper around the native OS calls. This makes it
very easy to use as all of the calls are the same as the existing OS
calls. It also makes it trivial to have your code use the OS during
operation and a mock filesystem during testing or as needed.
```go ```go
appfs := afero.NewOsFs() func main() {
appfs.MkdirAll("src/a", 0755) // Use the real OS filesystem
AppFs := afero.NewOsFs()
ProcessConfiguration(AppFs, "/etc/myapp.conf")
}
``` ```
## Memory Backed Storage ### 3. Usage in Testing
### MemMapFs In your tests, inject `MemMapFs`. This provides a blazing-fast, isolated, in-memory filesystem that requires no disk I/O and no cleanup.
Afero also provides a fully atomic memory backed filesystem perfect for use in
mocking and to speed up unnecessary disk io when persistence isnt
necessary. It is fully concurrent and will work within go routines
safely.
```go ```go
mm := afero.NewMemMapFs() func TestProcessConfiguration(t *testing.T) {
mm.MkdirAll("src/a", 0755) // Use the in-memory filesystem
AppFs := afero.NewMemMapFs()
// Pre-populate the memory filesystem for the test
configPath := "/test/config.json"
afero.WriteFile(AppFs, configPath, []byte(`{"feature": true}`), 0644)
// Run the test entirely in memory
err := ProcessConfiguration(AppFs, configPath)
if err != nil {
t.Fatal(err)
}
}
``` ```
#### InMemoryFile ## Afero's Superpower: Composition
As part of MemMapFs, Afero also provides an atomic, fully concurrent memory Afero's most unique feature is its ability to combine filesystems. This allows you to build complex behaviors out of simple components, keeping your application logic clean.
backed file implementation. This can be used in other memory backed file
systems with ease. Plans are to add a radix tree memory stored file
system using InMemoryFile.
## Network Interfaces ### Example 1: Sandboxing with Copy-on-Write
### SftpFs Create a temporary environment where an application can "modify" system files without affecting the actual disk.
Afero has experimental support for secure file transfer protocol (sftp). Which can
be used to perform file operations over a encrypted channel.
### GCSFs
Afero has experimental support for Google Cloud Storage (GCS). You can either set the
`GOOGLE_APPLICATION_CREDENTIALS_JSON` env variable to your JSON credentials or use `opts` in
`NewGcsFS` to configure access to your GCS bucket.
Some known limitations of the existing implementation:
* No Chmod support - The GCS ACL could probably be mapped to *nix style permissions but that would add another level of complexity and is ignored in this version.
* No Chtimes support - Could be simulated with attributes (gcs a/m-times are set implicitly) but that's is left for another version.
* Not thread safe - Also assumes all file operations are done through the same instance of the GcsFs. File operations between different GcsFs instances are not guaranteed to be consistent.
## Filtering Backends
### BasePathFs
The BasePathFs restricts all operations to a given path within an Fs.
The given file name to the operations on this Fs will be prepended with
the base path before calling the source Fs.
```go ```go
bp := afero.NewBasePathFs(afero.NewOsFs(), "/base/path") // 1. The base layer is the real OS, made read-only for safety.
baseFs := afero.NewReadOnlyFs(afero.NewOsFs())
// 2. The overlay layer is a temporary in-memory filesystem for changes.
overlayFs := afero.NewMemMapFs()
// 3. Combine them. Reads fall through to the base; writes only hit the overlay.
sandboxFs := afero.NewCopyOnWriteFs(baseFs, overlayFs)
// The application can now "modify" /etc/hosts, but the changes are isolated in memory.
afero.WriteFile(sandboxFs, "/etc/hosts", []byte("127.0.0.1 sandboxed-app"), 0644)
// The real /etc/hosts on disk is untouched.
``` ```
### ReadOnlyFs ### Example 2: Caching a Slow Filesystem
A thin wrapper around the source Fs providing a read only view. Improve performance by layering a fast cache (like memory) over a slow backend (like a network drive or cloud storage).
```go ```go
fs := afero.NewReadOnlyFs(afero.NewOsFs()) import "time"
_, err := fs.Create("/file.txt")
// err = syscall.EPERM // Assume 'remoteFs' is a slow backend (e.g., SFTP or GCS)
var remoteFs afero.Fs
// 'cacheFs' is a fast in-memory backend
cacheFs := afero.NewMemMapFs()
// Create the caching layer. Cache items for 5 minutes upon first read.
cachedFs := afero.NewCacheOnReadFs(remoteFs, cacheFs, 5*time.Minute)
// The first read is slow (fetches from remote, then caches)
data1, _ := afero.ReadFile(cachedFs, "data.json")
// The second read is instant (serves from memory cache)
data2, _ := afero.ReadFile(cachedFs, "data.json")
``` ```
# RegexpFs ### Example 3: Security Jails (chroot)
A filtered view on file names, any file NOT matching Restrict an application component's access to a specific subdirectory.
the passed regexp will be treated as non-existing.
Files not matching the regexp provided will not be created.
Directories are not filtered.
```go ```go
fs := afero.NewRegexpFs(afero.NewMemMapFs(), regexp.MustCompile(`\.txt$`)) osFs := afero.NewOsFs()
_, err := fs.Create("/file.html")
// err = syscall.ENOENT // Create a filesystem rooted at /home/user/public
// The application cannot access anything above this directory.
jailedFs := afero.NewBasePathFs(osFs, "/home/user/public")
// To the application, this is reading "/"
// In reality, it's reading "/home/user/public/"
dirInfo, err := afero.ReadDir(jailedFs, "/")
// Attempts to access parent directories fail
_, err = jailedFs.Open("../secrets.txt") // Returns an error
``` ```
### HttpFs ## Real-World Use Cases
Afero provides an http compatible backend which can wrap any of the existing ### Build Cloud-Agnostic Applications
backends.
The Http package requires a slightly specific version of Open which Write applications that seamlessly work with different storage backends:
returns an http.File type.
Afero provides an httpFs file system which satisfies this requirement.
Any Afero FileSystem can be used as an httpFs.
```go ```go
httpFs := afero.NewHttpFs(<ExistingFS>) type DocumentProcessor struct {
fileserver := http.FileServer(httpFs.Dir(<PATH>)) fs afero.Fs
http.Handle("/", fileserver) }
func NewDocumentProcessor(fs afero.Fs) *DocumentProcessor {
return &DocumentProcessor{fs: fs}
}
func (p *DocumentProcessor) Process(inputPath, outputPath string) error {
// This code works whether fs is local disk, cloud storage, or memory
content, err := afero.ReadFile(p.fs, inputPath)
if err != nil {
return err
}
processed := processContent(content)
return afero.WriteFile(p.fs, outputPath, processed, 0644)
}
// Use with local filesystem
processor := NewDocumentProcessor(afero.NewOsFs())
// Use with Google Cloud Storage
processor := NewDocumentProcessor(gcsFS)
// Use with in-memory filesystem for testing
processor := NewDocumentProcessor(afero.NewMemMapFs())
``` ```
## Composite Backends ### Treating Archives as Filesystems
Afero provides the ability have two filesystems (or more) act as a single Read files directly from `.zip` or `.tar` archives without unpacking them to disk first.
file system.
### CacheOnReadFs
The CacheOnReadFs will lazily make copies of any accessed files from the base
layer into the overlay. Subsequent reads will be pulled from the overlay
directly permitting the request is within the cache duration of when it was
created in the overlay.
If the base filesystem is writeable, any changes to files will be
done first to the base, then to the overlay layer. Write calls to open file
handles like `Write()` or `Truncate()` to the overlay first.
To writing files to the overlay only, you can use the overlay Fs directly (not
via the union Fs).
Cache files in the layer for the given time.Duration, a cache duration of 0
means "forever" meaning the file will not be re-requested from the base ever.
A read-only base will make the overlay also read-only but still copy files
from the base to the overlay when they're not present (or outdated) in the
caching layer.
```go ```go
base := afero.NewOsFs() import (
layer := afero.NewMemMapFs() "archive/zip"
ufs := afero.NewCacheOnReadFs(base, layer, 100 * time.Second) "github.com/spf13/afero/zipfs"
)
// Assume 'zipReader' is a *zip.Reader initialized from a file or memory
var zipReader *zip.Reader
// Create a read-only ZipFs
archiveFS := zipfs.New(zipReader)
// Read a file from within the archive using the standard Afero API
content, err := afero.ReadFile(archiveFS, "/docs/readme.md")
``` ```
### CopyOnWriteFs() ### Serving Any Filesystem over HTTP
The CopyOnWriteFs is a read only base file system with a potentially Use `HttpFs` to expose any Afero filesystem—even one created dynamically in memory—through a standard Go web server.
writeable layer on top.
Read operations will first look in the overlay and if not found there, will
serve the file from the base.
Changes to the file system will only be made in the overlay.
Any attempt to modify a file found only in the base will copy the file to the
overlay layer before modification (including opening a file with a writable
handle).
Removing and Renaming files present only in the base layer is not currently
permitted. If a file is present in the base layer and the overlay, only the
overlay will be removed/renamed.
```go ```go
base := afero.NewOsFs() import (
roBase := afero.NewReadOnlyFs(base) "net/http"
ufs := afero.NewCopyOnWriteFs(roBase, afero.NewMemMapFs()) "github.com/spf13/afero"
)
fh, _ = ufs.Create("/home/test/file2.txt") func main() {
fh.WriteString("This is a test") memFS := afero.NewMemMapFs()
fh.Close() afero.WriteFile(memFS, "index.html", []byte("<h1>Hello from Memory!</h1>"), 0644)
// Wrap the memory filesystem to make it compatible with http.FileServer.
httpFS := afero.NewHttpFs(memFS)
http.Handle("/", http.FileServer(httpFS.Dir("/")))
http.ListenAndServe(":8080", nil)
}
``` ```
In this example all write operations will only occur in memory (MemMapFs) ### Testing Made Simple
leaving the base filesystem (OsFs) untouched.
One of Afero's greatest strengths is making filesystem-dependent code easily testable:
## Desired/possible backends ```go
func SaveUserData(fs afero.Fs, userID string, data []byte) error {
filename := fmt.Sprintf("users/%s.json", userID)
return afero.WriteFile(fs, filename, data, 0644)
}
The following is a short list of possible backends we hope someone will func TestSaveUserData(t *testing.T) {
implement: // Create a clean, fast, in-memory filesystem for testing
testFS := afero.NewMemMapFs()
userData := []byte(`{"name": "John", "email": "john@example.com"}`)
err := SaveUserData(testFS, "123", userData)
if err != nil {
t.Fatalf("SaveUserData failed: %v", err)
}
// Verify the file was saved correctly
saved, err := afero.ReadFile(testFS, "users/123.json")
if err != nil {
t.Fatalf("Failed to read saved file: %v", err)
}
if string(saved) != string(userData) {
t.Errorf("Data mismatch: got %s, want %s", saved, userData)
}
}
```
* SSH **Benefits of testing with Afero:**
* S3 -**Fast** - No disk I/O, tests run in memory
- 🔄 **Reliable** - Each test starts with a clean slate
- 🧹 **No cleanup** - Memory is automatically freed
- 🔒 **Safe** - Can't accidentally modify real files
- 🏃 **Parallel** - Tests can run concurrently without conflicts
# About the project ## Backend Reference
## What's in the name | Type | Backend | Constructor | Description | Status |
| :--- | :--- | :--- | :--- | :--- |
| **Core** | **OsFs** | `afero.NewOsFs()` | Interacts with the real operating system filesystem. Use in production. | ✅ Official |
| | **MemMapFs** | `afero.NewMemMapFs()` | A fast, atomic, concurrent-safe, in-memory filesystem. Ideal for testing. | ✅ Official |
| **Composition** | **CopyOnWriteFs**| `afero.NewCopyOnWriteFs(base, overlay)` | A read-only base with a writable overlay. Ideal for sandboxing. | ✅ Official |
| | **CacheOnReadFs**| `afero.NewCacheOnReadFs(base, cache, ttl)` | Lazily caches files from a slow base into a fast layer on first read. | ✅ Official |
| | **BasePathFs** | `afero.NewBasePathFs(source, path)` | Restricts operations to a subdirectory (chroot/jail). | ✅ Official |
| | **ReadOnlyFs** | `afero.NewReadOnlyFs(source)` | Provides a read-only view, preventing any modifications. | ✅ Official |
| | **RegexpFs** | `afero.NewRegexpFs(source, regexp)` | Filters a filesystem, only showing files that match a regex. | ✅ Official |
| **Utility** | **HttpFs** | `afero.NewHttpFs(source)` | Wraps any Afero filesystem to be served via `http.FileServer`. | ✅ Official |
| **Archives** | **ZipFs** | `zipfs.New(zipReader)` | Read-only access to files within a ZIP archive. | ✅ Official |
| | **TarFs** | `tarfs.New(tarReader)` | Read-only access to files within a TAR archive. | ✅ Official |
| **Network** | **GcsFs** | `gcsfs.NewGcsFs(...)` | Google Cloud Storage backend. | ⚡ Experimental |
| | **SftpFs** | `sftpfs.New(...)` | SFTP backend. | ⚡ Experimental |
| **3rd Party Cloud** | **S3Fs** | [`fclairamb/afero-s3`](https://github.com/fclairamb/afero-s3) | Production-ready S3 backend built on official AWS SDK. | 🔹 3rd Party |
| | **MinioFs** | [`cpyun/afero-minio`](https://github.com/cpyun/afero-minio) | MinIO object storage backend with S3 compatibility. | 🔹 3rd Party |
| | **DriveFs** | [`fclairamb/afero-gdrive`](https://github.com/fclairamb/afero-gdrive) | Google Drive backend with streaming support. | 🔹 3rd Party |
| | **DropboxFs** | [`fclairamb/afero-dropbox`](https://github.com/fclairamb/afero-dropbox) | Dropbox backend with streaming support. | 🔹 3rd Party |
| **3rd Party Specialized** | **GitFs** | [`tobiash/go-gitfs`](https://github.com/tobiash/go-gitfs) | Git repository filesystem (read-only, Afero compatible). | 🔹 3rd Party |
| | **DockerFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | Docker container filesystem access. | 🔹 3rd Party |
| | **GitHubFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | GitHub repository and releases filesystem. | 🔹 3rd Party |
| | **FilterFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | Filesystem filtering with predicates. | 🔹 3rd Party |
| | **IgnoreFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | .gitignore-aware filtering filesystem. | 🔹 3rd Party |
| | **FUSEFs** | [`JakWai01/sile-fystem`](https://github.com/JakWai01/sile-fystem) | Generic FUSE implementation using any Afero backend. | 🔹 3rd Party |
Afero comes from the latin roots Ad-Facere. ## Afero vs. `io/fs` (Go 1.16+)
**"Ad"** is a prefix meaning "to". Go 1.16 introduced the `io/fs` package, which provides a standard abstraction for **read-only** filesystems.
**"Facere"** is a form of the root "faciō" making "make or do". Afero complements `io/fs` by focusing on different needs:
The literal meaning of afero is "to make" or "to do" which seems very fitting * **Use `io/fs` when:** You only need to read files and want to conform strictly to the standard library interfaces.
for a library that allows one to make files and directories and do things with them. * **Use Afero when:**
* Your application needs to **create, write, modify, or delete** files.
* You need to test complex read/write interactions (e.g., renaming, concurrent writes).
* You need advanced compositional features (Copy-on-Write, Caching, etc.).
The English word that shares the same roots as Afero is "affair". Affair shares Afero is fully compatible with `io/fs`. You can wrap any Afero filesystem to satisfy the `fs.FS` interface using `afero.NewIOFS`:
the same concept but as a noun it means "something that is made or done" or "an
object of a particular type".
It's also nice that unlike some of my other libraries (hugo, cobra, viper) it ```go
Googles very well. import "io/fs"
## Release Notes // Create an Afero filesystem (writable)
var myAferoFs afero.Fs = afero.NewMemMapFs()
See the [Releases Page](https://github.com/spf13/afero/releases). // Convert it to a standard library fs.FS (read-only view)
var myIoFs fs.FS = afero.NewIOFS(myAferoFs)
```
## Third-Party Backends & Ecosystem
The Afero community has developed numerous backends and tools that extend the library's capabilities. Below are curated, well-maintained options organized by maturity and reliability.
### Featured Community Backends
These are mature, reliable backends that we can confidently recommend for production use:
#### **Amazon S3** - [`fclairamb/afero-s3`](https://github.com/fclairamb/afero-s3)
Production-ready S3 backend built on the official AWS SDK for Go.
```go
import "github.com/fclairamb/afero-s3"
s3fs := s3.NewFs(bucket, session)
```
#### **MinIO** - [`cpyun/afero-minio`](https://github.com/cpyun/afero-minio)
MinIO object storage backend providing S3-compatible object storage with deduplication and optimization features.
```go
import "github.com/cpyun/afero-minio"
minioFs := miniofs.NewMinioFs(ctx, "minio://endpoint/bucket")
```
### Community & Specialized Backends
#### Cloud Storage
- **Google Drive** - [`fclairamb/afero-gdrive`](https://github.com/fclairamb/afero-gdrive)
Streaming support; no write-seeking or POSIX permissions; no files listing cache
- **Dropbox** - [`fclairamb/afero-dropbox`](https://github.com/fclairamb/afero-dropbox)
Streaming support; no write-seeking or POSIX permissions
#### Version Control Systems
- **Git Repositories** - [`tobiash/go-gitfs`](https://github.com/tobiash/go-gitfs)
Read-only filesystem abstraction for Git repositories. Works with bare repositories and provides filesystem view of any git reference. Uses go-git for repository access.
#### Container and Remote Systems
- **Docker Containers** - [`unmango/aferox`](https://github.com/unmango/aferox)
Access Docker container filesystems as if they were local filesystems
- **GitHub API** - [`unmango/aferox`](https://github.com/unmango/aferox)
Turn GitHub repositories, releases, and assets into browsable filesystems
#### FUSE Integration
- **Generic FUSE** - [`JakWai01/sile-fystem`](https://github.com/JakWai01/sile-fystem)
Mount any Afero filesystem as a FUSE filesystem, allowing any Afero backend to be used as a real mounted filesystem
#### Specialized Filesystems
- **FAT32 Support** - [`aligator/GoFAT`](https://github.com/aligator/GoFAT)
Pure Go FAT filesystem implementation (currently read-only)
### Interface Adapters & Utilities
**Cross-Interface Compatibility:**
- [`jfontan/go-billy-desfacer`](https://github.com/jfontan/go-billy-desfacer) - Adapter between Afero and go-billy interfaces (for go-git compatibility)
- [`Maldris/go-billy-afero`](https://github.com/Maldris/go-billy-afero) - Alternative wrapper for using Afero with go-billy
- [`c4milo/afero2billy`](https://github.com/c4milo/afero2billy) - Another Afero to billy filesystem adapter
**Working Directory Management:**
- [`carolynvs/aferox`](https://github.com/carolynvs/aferox) - Working directory-aware filesystem wrapper
**Advanced Filtering:**
- [`unmango/aferox`](https://github.com/unmango/aferox) includes multiple specialized filesystems:
- **FilterFs** - Predicate-based file filtering
- **IgnoreFs** - .gitignore-aware filtering
- **WriterFs** - Dump writes to io.Writer for debugging
#### Developer Tools & Utilities
**nhatthm Utility Suite** - Essential tools for Afero development:
- [`nhatthm/aferocopy`](https://github.com/nhatthm/aferocopy) - Copy files between any Afero filesystems
- [`nhatthm/aferomock`](https://github.com/nhatthm/aferomock) - Mocking toolkit for testing
- [`nhatthm/aferoassert`](https://github.com/nhatthm/aferoassert) - Assertion helpers for filesystem testing
### Ecosystem Showcase
**Windows Virtual Drives** - [`balazsgrill/potatodrive`](https://github.com/balazsgrill/potatodrive)
Mount any Afero filesystem as a Windows drive letter. Brilliant demonstration of Afero's power!
### Modern Asset Embedding (Go 1.16+)
Instead of third-party tools, use Go's native `//go:embed` with Afero:
```go
import (
"embed"
"github.com/spf13/afero"
)
//go:embed assets/*
var assetsFS embed.FS
func main() {
// Convert embedded files to Afero filesystem
fs := afero.FromIOFS(assetsFS)
// Use like any other Afero filesystem
content, _ := afero.ReadFile(fs, "assets/config.json")
}
```
## Contributing ## Contributing
1. Fork it We welcome contributions! The project is mature, but we are actively looking for contributors to help implement and stabilize network/cloud backends.
* 🔥 **Microsoft Azure Blob Storage**
* 🔒 **Modern Encryption Backend** - Built on secure, contemporary crypto (not legacy EncFS)
* 🐙 **Canonical go-git Adapter** - Unified solution for Git integration
* 📡 **SSH/SCP Backend** - Secure remote file operations
* Stabilization of existing experimental backends (GCS, SFTP)
To contribute:
1. Fork the repository
2. Create your feature branch (`git checkout -b my-new-feature`) 2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`) 3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`) 4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request 5. Create a new Pull Request
## Releasing ## 📄 License
As of version 1.14.0, Afero moved implementations with third-party libraries to Afero is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt) for details.
their own submodules.
Releasing a new version now requires a few steps: ## 🔗 Additional Resources
``` - [📖 Full API Documentation](https://pkg.go.dev/github.com/spf13/afero)
VERSION=X.Y.Z - [🎯 Examples Repository](https://github.com/spf13/afero/tree/master/examples)
git tag -a v$VERSION -m "Release $VERSION" - [📋 Release Notes](https://github.com/spf13/afero/releases)
git push origin v$VERSION - [❓ GitHub Discussions](https://github.com/spf13/afero/discussions)
cd gcsfs ---
go get github.com/spf13/afero@v$VERSION
go mod tidy
git commit -am "Update afero to v$VERSION"
git tag -a gcsfs/v$VERSION -m "Release gcsfs $VERSION"
git push origin gcsfs/v$VERSION
cd ..
cd sftpfs *Afero comes from the Latin roots Ad-Facere, meaning "to make" or "to do" - fitting for a library that empowers you to make and do amazing things with filesystems.*
go get github.com/spf13/afero@v$VERSION
go mod tidy
git commit -am "Update afero to v$VERSION"
git tag -a sftpfs/v$VERSION -m "Release sftpfs $VERSION"
git push origin sftpfs/v$VERSION
cd ..
git push
```
TODO: move these instructions to a Makefile or something
## Contributors
Names in no particular order:
* [spf13](https://github.com/spf13)
* [jaqx0r](https://github.com/jaqx0r)
* [mbertschler](https://github.com/mbertschler)
* [xor-gate](https://github.com/xor-gate)
## License
Afero is released under the Apache 2.0 license. See
[LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt)

View File

@@ -34,7 +34,8 @@ func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
_, err := u.base.Stat(name) _, err := u.base.Stat(name)
if err != nil { if err != nil {
if oerr, ok := err.(*os.PathError); ok { if oerr, ok := err.(*os.PathError); ok {
if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR { if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT ||
oerr.Err == syscall.ENOTDIR {
return false, nil return false, nil
} }
} }
@@ -237,7 +238,11 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File,
return u.layer.OpenFile(name, flag, perm) return u.layer.OpenFile(name, flag, perm)
} }
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist? return nil, &os.PathError{
Op: "open",
Path: name,
Err: syscall.ENOTDIR,
} // ...or os.ErrNotExist?
} }
if b { if b {
return u.base.OpenFile(name, flag, perm) return u.base.OpenFile(name, flag, perm)

View File

@@ -137,7 +137,7 @@ type readDirFile struct {
var _ fs.ReadDirFile = readDirFile{} var _ fs.ReadDirFile = readDirFile{}
func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) { func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) {
items, err := r.File.Readdir(n) items, err := r.Readdir(n)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -161,7 +161,12 @@ var _ Fs = FromIOFS{}
func (f FromIOFS) Create(name string) (File, error) { return nil, notImplemented("create", name) } func (f FromIOFS) Create(name string) (File, error) { return nil, notImplemented("create", name) }
func (f FromIOFS) Mkdir(name string, perm os.FileMode) error { return notImplemented("mkdir", name) } func (f FromIOFS) Mkdir(
name string,
perm os.FileMode,
) error {
return notImplemented("mkdir", name)
}
func (f FromIOFS) MkdirAll(path string, perm os.FileMode) error { func (f FromIOFS) MkdirAll(path string, perm os.FileMode) error {
return notImplemented("mkdirall", path) return notImplemented("mkdirall", path)

View File

@@ -19,9 +19,9 @@ import (
// Lstater is an optional interface in Afero. It is only implemented by the // Lstater is an optional interface in Afero. It is only implemented by the
// filesystems saying so. // filesystems saying so.
// It will call Lstat if the filesystem iself is, or it delegates to, the os filesystem. // It will call Lstat if the filesystem itself is, or it delegates to, the os filesystem.
// Else it will call Stat. // Else it will call Stat.
// In addtion to the FileInfo, it will return a boolean telling whether Lstat was called or not. // In addition to the FileInfo, it will return a boolean telling whether Lstat was called or not.
type Lstater interface { type Lstater interface {
LstatIfPossible(name string) (os.FileInfo, bool, error) LstatIfPossible(name string) (os.FileInfo, bool, error)
} }

View File

@@ -150,7 +150,11 @@ func (f *File) Sync() error {
func (f *File) Readdir(count int) (res []os.FileInfo, err error) { func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
if !f.fileData.dir { if !f.fileData.dir {
return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")} return nil, &os.PathError{
Op: "readdir",
Path: f.fileData.name,
Err: errors.New("not a dir"),
}
} }
var outLength int64 var outLength int64
@@ -236,7 +240,11 @@ func (f *File) Truncate(size int64) error {
return ErrFileClosed return ErrFileClosed
} }
if f.readOnly { if f.readOnly {
return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")} return &os.PathError{
Op: "truncate",
Path: f.fileData.name,
Err: errors.New("file handle is read only"),
}
} }
if size < 0 { if size < 0 {
return ErrOutOfRange return ErrOutOfRange
@@ -273,7 +281,11 @@ func (f *File) Write(b []byte) (n int, err error) {
return 0, ErrFileClosed return 0, ErrFileClosed
} }
if f.readOnly { if f.readOnly {
return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")} return 0, &os.PathError{
Op: "write",
Path: f.fileData.name,
Err: errors.New("file handle is read only"),
}
} }
n = len(b) n = len(b)
cur := atomic.LoadInt64(&f.at) cur := atomic.LoadInt64(&f.at)
@@ -285,7 +297,9 @@ func (f *File) Write(b []byte) (n int, err error) {
tail = f.fileData.data[n+int(cur):] tail = f.fileData.data[n+int(cur):]
} }
if diff > 0 { if diff > 0 {
f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...) f.fileData.data = append(
f.fileData.data,
append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...)
f.fileData.data = append(f.fileData.data, tail...) f.fileData.data = append(f.fileData.data, tail...)
} else { } else {
f.fileData.data = append(f.fileData.data[:cur], b...) f.fileData.data = append(f.fileData.data[:cur], b...)

View File

@@ -92,7 +92,8 @@ func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
func (f *UnionFile) Write(s []byte) (n int, err error) { func (f *UnionFile) Write(s []byte) (n int, err error) {
if f.Layer != nil { if f.Layer != nil {
n, err = f.Layer.Write(s) n, err = f.Layer.Write(s)
if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark? if err == nil &&
f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
_, err = f.Base.Write(s) _, err = f.Base.Write(s)
} }
return n, err return n, err
@@ -157,7 +158,7 @@ var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, err
// return a single view of the overlayed directories. // return a single view of the overlayed directories.
// At the end of the directory view, the error is io.EOF if c > 0. // At the end of the directory view, the error is io.EOF if c > 0.
func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) { func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
var merge DirsMerger = f.Merger merge := f.Merger
if merge == nil { if merge == nil {
merge = defaultUnionMergeDirsFn merge = defaultUnionMergeDirsFn
} }

View File

@@ -113,11 +113,11 @@ func GetTempDir(fs Fs, subPath string) string {
if subPath != "" { if subPath != "" {
// preserve windows backslash :-( // preserve windows backslash :-(
if FilePathSeparator == "\\" { if FilePathSeparator == "\\" {
subPath = strings.Replace(subPath, "\\", "____", -1) subPath = strings.ReplaceAll(subPath, "\\", "____")
} }
dir = dir + UnicodeSanitize((subPath)) dir = dir + UnicodeSanitize((subPath))
if FilePathSeparator == "\\" { if FilePathSeparator == "\\" {
dir = strings.Replace(dir, "____", "\\", -1) dir = strings.ReplaceAll(dir, "____", "\\")
} }
if exists, _ := Exists(fs, dir); exists { if exists, _ := Exists(fs, dir); exists {

15
vendor/github.com/spf13/cast/.editorconfig generated vendored Normal file
View File

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

39
vendor/github.com/spf13/cast/.golangci.yaml generated vendored Normal file
View File

@@ -0,0 +1,39 @@
version: "2"
run:
timeout: 10m
linters:
enable:
- errcheck
- govet
- ineffassign
- misspell
- nolintlint
# - revive
- unused
disable:
- staticcheck
settings:
misspell:
locale: US
nolintlint:
allow-unused: false # report any unused nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
formatters:
enable:
- gci
- gofmt
# - gofumpt
- goimports
# - golines
settings:
gci:
sections:
- standard
- default
- localmodule

View File

@@ -1,9 +1,9 @@
# cast # cast
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/cast/test.yaml?branch=master&style=flat-square)](https://github.com/spf13/cast/actions/workflows/test.yaml) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/cast/ci.yaml?style=flat-square)](https://github.com/spf13/cast/actions/workflows/ci.yaml)
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/cast)](https://pkg.go.dev/mod/github.com/spf13/cast) [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/spf13/cast)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/spf13/cast?style=flat-square&color=61CFDD)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cast?style=flat-square)](https://goreportcard.com/report/github.com/spf13/cast) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/spf13/cast/badge?style=flat-square)](https://deps.dev/go/github.com%252Fspf13%252Fcast)
Easy and safe casting from one type to another in Go Easy and safe casting from one type to another in Go
@@ -73,3 +73,7 @@ the code for a complete set.
var eight interface{} = 8 var eight interface{} = 8
cast.ToInt(eight) // 8 cast.ToInt(eight) // 8
cast.ToInt(nil) // 0 cast.ToInt(nil) // 0
## License
The project is licensed under the [MIT License](LICENSE).

69
vendor/github.com/spf13/cast/alias.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package cast
import (
"reflect"
"slices"
)
var kindNames = []string{
reflect.String: "string",
reflect.Bool: "bool",
reflect.Int: "int",
reflect.Int8: "int8",
reflect.Int16: "int16",
reflect.Int32: "int32",
reflect.Int64: "int64",
reflect.Uint: "uint",
reflect.Uint8: "uint8",
reflect.Uint16: "uint16",
reflect.Uint32: "uint32",
reflect.Uint64: "uint64",
reflect.Float32: "float32",
reflect.Float64: "float64",
}
var kinds = map[reflect.Kind]func(reflect.Value) any{
reflect.String: func(v reflect.Value) any { return v.String() },
reflect.Bool: func(v reflect.Value) any { return v.Bool() },
reflect.Int: func(v reflect.Value) any { return int(v.Int()) },
reflect.Int8: func(v reflect.Value) any { return int8(v.Int()) },
reflect.Int16: func(v reflect.Value) any { return int16(v.Int()) },
reflect.Int32: func(v reflect.Value) any { return int32(v.Int()) },
reflect.Int64: func(v reflect.Value) any { return v.Int() },
reflect.Uint: func(v reflect.Value) any { return uint(v.Uint()) },
reflect.Uint8: func(v reflect.Value) any { return uint8(v.Uint()) },
reflect.Uint16: func(v reflect.Value) any { return uint16(v.Uint()) },
reflect.Uint32: func(v reflect.Value) any { return uint32(v.Uint()) },
reflect.Uint64: func(v reflect.Value) any { return v.Uint() },
reflect.Float32: func(v reflect.Value) any { return float32(v.Float()) },
reflect.Float64: func(v reflect.Value) any { return v.Float() },
}
// resolveAlias attempts to resolve a named type to its underlying basic type (if possible).
//
// Pointers are expected to be indirected by this point.
func resolveAlias(i any) (any, bool) {
if i == nil {
return nil, false
}
t := reflect.TypeOf(i)
// Not a named type
if t.Name() == "" || slices.Contains(kindNames, t.Name()) {
return i, false
}
resolve, ok := kinds[t.Kind()]
if !ok { // Not a supported kind
return i, false
}
v := reflect.ValueOf(i)
return resolve(v), true
}

131
vendor/github.com/spf13/cast/basic.go generated vendored Normal file
View File

@@ -0,0 +1,131 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package cast
import (
"encoding/json"
"fmt"
"html/template"
"strconv"
"time"
)
// ToBoolE casts any value to a bool type.
func ToBoolE(i any) (bool, error) {
i, _ = indirect(i)
switch b := i.(type) {
case bool:
return b, nil
case nil:
return false, nil
case int:
return b != 0, nil
case int8:
return b != 0, nil
case int16:
return b != 0, nil
case int32:
return b != 0, nil
case int64:
return b != 0, nil
case uint:
return b != 0, nil
case uint8:
return b != 0, nil
case uint16:
return b != 0, nil
case uint32:
return b != 0, nil
case uint64:
return b != 0, nil
case float32:
return b != 0, nil
case float64:
return b != 0, nil
case time.Duration:
return b != 0, nil
case string:
return strconv.ParseBool(b)
case json.Number:
v, err := ToInt64E(b)
if err == nil {
return v != 0, nil
}
return false, fmt.Errorf(errorMsg, i, i, false)
default:
if i, ok := resolveAlias(i); ok {
return ToBoolE(i)
}
return false, fmt.Errorf(errorMsg, i, i, false)
}
}
// ToStringE casts any value to a string type.
func ToStringE(i any) (string, error) {
switch s := i.(type) {
case string:
return s, nil
case bool:
return strconv.FormatBool(s), nil
case float64:
return strconv.FormatFloat(s, 'f', -1, 64), nil
case float32:
return strconv.FormatFloat(float64(s), 'f', -1, 32), nil
case int:
return strconv.Itoa(s), nil
case int8:
return strconv.FormatInt(int64(s), 10), nil
case int16:
return strconv.FormatInt(int64(s), 10), nil
case int32:
return strconv.FormatInt(int64(s), 10), nil
case int64:
return strconv.FormatInt(s, 10), nil
case uint:
return strconv.FormatUint(uint64(s), 10), nil
case uint8:
return strconv.FormatUint(uint64(s), 10), nil
case uint16:
return strconv.FormatUint(uint64(s), 10), nil
case uint32:
return strconv.FormatUint(uint64(s), 10), nil
case uint64:
return strconv.FormatUint(s, 10), nil
case json.Number:
return s.String(), nil
case []byte:
return string(s), nil
case template.HTML:
return string(s), nil
case template.URL:
return string(s), nil
case template.JS:
return string(s), nil
case template.CSS:
return string(s), nil
case template.HTMLAttr:
return string(s), nil
case nil:
return "", nil
case fmt.Stringer:
return s.String(), nil
case error:
return s.Error(), nil
default:
if i, ok := indirect(i); ok {
return ToStringE(i)
}
if i, ok := resolveAlias(i); ok {
return ToStringE(i)
}
return "", fmt.Errorf(errorMsg, i, i, "")
}
}

222
vendor/github.com/spf13/cast/cast.go generated vendored
View File

@@ -8,169 +8,77 @@ package cast
import "time" import "time"
// ToBool casts an interface to a bool type. const errorMsg = "unable to cast %#v of type %T to %T"
func ToBool(i interface{}) bool { const errorMsgWith = "unable to cast %#v of type %T to %T: %w"
v, _ := ToBoolE(i)
return v // Basic is a type parameter constraint for functions accepting basic types.
//
// It represents the supported basic types this package can cast to.
type Basic interface {
string | bool | Number | time.Time | time.Duration
} }
// ToTime casts an interface to a time.Time type. // ToE casts any value to a [Basic] type.
func ToTime(i interface{}) time.Time { func ToE[T Basic](i any) (T, error) {
v, _ := ToTimeE(i) var t T
return v
var v any
var err error
switch any(t).(type) {
case string:
v, err = ToStringE(i)
case bool:
v, err = ToBoolE(i)
case int:
v, err = toNumberE[int](i, parseInt[int])
case int8:
v, err = toNumberE[int8](i, parseInt[int8])
case int16:
v, err = toNumberE[int16](i, parseInt[int16])
case int32:
v, err = toNumberE[int32](i, parseInt[int32])
case int64:
v, err = toNumberE[int64](i, parseInt[int64])
case uint:
v, err = toUnsignedNumberE[uint](i, parseUint[uint])
case uint8:
v, err = toUnsignedNumberE[uint8](i, parseUint[uint8])
case uint16:
v, err = toUnsignedNumberE[uint16](i, parseUint[uint16])
case uint32:
v, err = toUnsignedNumberE[uint32](i, parseUint[uint32])
case uint64:
v, err = toUnsignedNumberE[uint64](i, parseUint[uint64])
case float32:
v, err = toNumberE[float32](i, parseFloat[float32])
case float64:
v, err = toNumberE[float64](i, parseFloat[float64])
case time.Time:
v, err = ToTimeE(i)
case time.Duration:
v, err = ToDurationE(i)
}
if err != nil {
return t, err
}
return v.(T), nil
} }
func ToTimeInDefaultLocation(i interface{}, location *time.Location) time.Time { // Must is a helper that wraps a call to a cast function and panics if the error is non-nil.
v, _ := ToTimeInDefaultLocationE(i, location) func Must[T any](i any, err error) T {
return v if err != nil {
panic(err)
}
return i.(T)
} }
// ToDuration casts an interface to a time.Duration type. // To casts any value to a [Basic] type.
func ToDuration(i interface{}) time.Duration { func To[T Basic](i any) T {
v, _ := ToDurationE(i) v, _ := ToE[T](i)
return v
}
// ToFloat64 casts an interface to a float64 type.
func ToFloat64(i interface{}) float64 {
v, _ := ToFloat64E(i)
return v
}
// ToFloat32 casts an interface to a float32 type.
func ToFloat32(i interface{}) float32 {
v, _ := ToFloat32E(i)
return v
}
// ToInt64 casts an interface to an int64 type.
func ToInt64(i interface{}) int64 {
v, _ := ToInt64E(i)
return v
}
// ToInt32 casts an interface to an int32 type.
func ToInt32(i interface{}) int32 {
v, _ := ToInt32E(i)
return v
}
// ToInt16 casts an interface to an int16 type.
func ToInt16(i interface{}) int16 {
v, _ := ToInt16E(i)
return v
}
// ToInt8 casts an interface to an int8 type.
func ToInt8(i interface{}) int8 {
v, _ := ToInt8E(i)
return v
}
// ToInt casts an interface to an int type.
func ToInt(i interface{}) int {
v, _ := ToIntE(i)
return v
}
// ToUint casts an interface to a uint type.
func ToUint(i interface{}) uint {
v, _ := ToUintE(i)
return v
}
// ToUint64 casts an interface to a uint64 type.
func ToUint64(i interface{}) uint64 {
v, _ := ToUint64E(i)
return v
}
// ToUint32 casts an interface to a uint32 type.
func ToUint32(i interface{}) uint32 {
v, _ := ToUint32E(i)
return v
}
// ToUint16 casts an interface to a uint16 type.
func ToUint16(i interface{}) uint16 {
v, _ := ToUint16E(i)
return v
}
// ToUint8 casts an interface to a uint8 type.
func ToUint8(i interface{}) uint8 {
v, _ := ToUint8E(i)
return v
}
// ToString casts an interface to a string type.
func ToString(i interface{}) string {
v, _ := ToStringE(i)
return v
}
// ToStringMapString casts an interface to a map[string]string type.
func ToStringMapString(i interface{}) map[string]string {
v, _ := ToStringMapStringE(i)
return v
}
// ToStringMapStringSlice casts an interface to a map[string][]string type.
func ToStringMapStringSlice(i interface{}) map[string][]string {
v, _ := ToStringMapStringSliceE(i)
return v
}
// ToStringMapBool casts an interface to a map[string]bool type.
func ToStringMapBool(i interface{}) map[string]bool {
v, _ := ToStringMapBoolE(i)
return v
}
// ToStringMapInt casts an interface to a map[string]int type.
func ToStringMapInt(i interface{}) map[string]int {
v, _ := ToStringMapIntE(i)
return v
}
// ToStringMapInt64 casts an interface to a map[string]int64 type.
func ToStringMapInt64(i interface{}) map[string]int64 {
v, _ := ToStringMapInt64E(i)
return v
}
// ToStringMap casts an interface to a map[string]interface{} type.
func ToStringMap(i interface{}) map[string]interface{} {
v, _ := ToStringMapE(i)
return v
}
// ToSlice casts an interface to a []interface{} type.
func ToSlice(i interface{}) []interface{} {
v, _ := ToSliceE(i)
return v
}
// ToBoolSlice casts an interface to a []bool type.
func ToBoolSlice(i interface{}) []bool {
v, _ := ToBoolSliceE(i)
return v
}
// ToStringSlice casts an interface to a []string type.
func ToStringSlice(i interface{}) []string {
v, _ := ToStringSliceE(i)
return v
}
// ToIntSlice casts an interface to a []int type.
func ToIntSlice(i interface{}) []int {
v, _ := ToIntSliceE(i)
return v
}
// ToDurationSlice casts an interface to a []time.Duration type.
func ToDurationSlice(i interface{}) []time.Duration {
v, _ := ToDurationSliceE(i)
return v return v
} }

1510
vendor/github.com/spf13/cast/caste.go generated vendored

File diff suppressed because it is too large Load Diff

37
vendor/github.com/spf13/cast/indirect.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package cast
import (
"reflect"
)
// From html/template/content.go
// Copyright 2011 The Go Authors. All rights reserved.
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
func indirect(i any) (any, bool) {
if i == nil {
return nil, false
}
if t := reflect.TypeOf(i); t.Kind() != reflect.Ptr {
// Avoid creating a reflect.Value if it's not a pointer.
return i, false
}
v := reflect.ValueOf(i)
for v.Kind() == reflect.Ptr || (v.Kind() == reflect.Interface && v.Elem().Kind() == reflect.Ptr) {
if v.IsNil() {
return nil, true
}
v = v.Elem()
}
return v.Interface(), true
}

79
vendor/github.com/spf13/cast/internal/time.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
package internal
import (
"fmt"
"time"
)
//go:generate stringer -type=TimeFormatType
type TimeFormatType int
const (
TimeFormatNoTimezone TimeFormatType = iota
TimeFormatNamedTimezone
TimeFormatNumericTimezone
TimeFormatNumericAndNamedTimezone
TimeFormatTimeOnly
)
type TimeFormat struct {
Format string
Typ TimeFormatType
}
func (f TimeFormat) HasTimezone() bool {
// We don't include the formats with only named timezones, see
// https://github.com/golang/go/issues/19694#issuecomment-289103522
return f.Typ >= TimeFormatNumericTimezone && f.Typ <= TimeFormatNumericAndNamedTimezone
}
var TimeFormats = []TimeFormat{
// Keep common formats at the top.
{"2006-01-02", TimeFormatNoTimezone},
{time.RFC3339, TimeFormatNumericTimezone},
{"2006-01-02T15:04:05", TimeFormatNoTimezone}, // iso8601 without timezone
{time.RFC1123Z, TimeFormatNumericTimezone},
{time.RFC1123, TimeFormatNamedTimezone},
{time.RFC822Z, TimeFormatNumericTimezone},
{time.RFC822, TimeFormatNamedTimezone},
{time.RFC850, TimeFormatNamedTimezone},
{"2006-01-02 15:04:05.999999999 -0700 MST", TimeFormatNumericAndNamedTimezone}, // Time.String()
{"2006-01-02T15:04:05-0700", TimeFormatNumericTimezone}, // RFC3339 without timezone hh:mm colon
{"2006-01-02 15:04:05Z0700", TimeFormatNumericTimezone}, // RFC3339 without T or timezone hh:mm colon
{"2006-01-02 15:04:05", TimeFormatNoTimezone},
{time.ANSIC, TimeFormatNoTimezone},
{time.UnixDate, TimeFormatNamedTimezone},
{time.RubyDate, TimeFormatNumericTimezone},
{"2006-01-02 15:04:05Z07:00", TimeFormatNumericTimezone},
{"02 Jan 2006", TimeFormatNoTimezone},
{"2006-01-02 15:04:05 -07:00", TimeFormatNumericTimezone},
{"2006-01-02 15:04:05 -0700", TimeFormatNumericTimezone},
{time.Kitchen, TimeFormatTimeOnly},
{time.Stamp, TimeFormatTimeOnly},
{time.StampMilli, TimeFormatTimeOnly},
{time.StampMicro, TimeFormatTimeOnly},
{time.StampNano, TimeFormatTimeOnly},
}
func ParseDateWith(s string, location *time.Location, formats []TimeFormat) (d time.Time, e error) {
for _, format := range formats {
if d, e = time.Parse(format.Format, s); e == nil {
// Some time formats have a zone name, but no offset, so it gets
// put in that zone name (not the default one passed in to us), but
// without that zone's offset. So set the location manually.
if format.Typ <= TimeFormatNamedTimezone {
if location == nil {
location = time.Local
}
year, month, day := d.Date()
hour, min, sec := d.Clock()
d = time.Date(year, month, day, hour, min, sec, d.Nanosecond(), location)
}
return
}
}
return d, fmt.Errorf("unable to parse date: %s", s)
}

View File

@@ -0,0 +1,27 @@
// Code generated by "stringer -type=TimeFormatType"; DO NOT EDIT.
package internal
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[TimeFormatNoTimezone-0]
_ = x[TimeFormatNamedTimezone-1]
_ = x[TimeFormatNumericTimezone-2]
_ = x[TimeFormatNumericAndNamedTimezone-3]
_ = x[TimeFormatTimeOnly-4]
}
const _TimeFormatType_name = "TimeFormatNoTimezoneTimeFormatNamedTimezoneTimeFormatNumericTimezoneTimeFormatNumericAndNamedTimezoneTimeFormatTimeOnly"
var _TimeFormatType_index = [...]uint8{0, 20, 43, 68, 101, 119}
func (i TimeFormatType) String() string {
if i < 0 || i >= TimeFormatType(len(_TimeFormatType_index)-1) {
return "TimeFormatType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _TimeFormatType_name[_TimeFormatType_index[i]:_TimeFormatType_index[i+1]]
}

212
vendor/github.com/spf13/cast/map.go generated vendored Normal file
View File

@@ -0,0 +1,212 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package cast
import (
"encoding/json"
"fmt"
"reflect"
)
func toMapE[K comparable, V any](i any, keyFn func(any) K, valFn func(any) V) (map[K]V, error) {
m := map[K]V{}
if i == nil {
return m, fmt.Errorf(errorMsg, i, i, m)
}
switch v := i.(type) {
case map[K]V:
return v, nil
case map[K]any:
for k, val := range v {
m[k] = valFn(val)
}
return m, nil
case map[any]V:
for k, val := range v {
m[keyFn(k)] = val
}
return m, nil
case map[any]any:
for k, val := range v {
m[keyFn(k)] = valFn(val)
}
return m, nil
case string:
err := jsonStringToObject(v, &m)
return m, err
default:
return m, fmt.Errorf(errorMsg, i, i, m)
}
}
func toStringMapE[T any](i any, fn func(any) T) (map[string]T, error) {
return toMapE(i, ToString, fn)
}
// ToStringMapStringE casts any value to a map[string]string type.
func ToStringMapStringE(i any) (map[string]string, error) {
return toStringMapE(i, ToString)
}
// ToStringMapStringSliceE casts any value to a map[string][]string type.
func ToStringMapStringSliceE(i any) (map[string][]string, error) {
m := map[string][]string{}
switch v := i.(type) {
case map[string][]string:
return v, nil
case map[string][]any:
for k, val := range v {
m[ToString(k)] = ToStringSlice(val)
}
return m, nil
case map[string]string:
for k, val := range v {
m[ToString(k)] = []string{val}
}
case map[string]any:
for k, val := range v {
switch vt := val.(type) {
case []any:
m[ToString(k)] = ToStringSlice(vt)
case []string:
m[ToString(k)] = vt
default:
m[ToString(k)] = []string{ToString(val)}
}
}
return m, nil
case map[any][]string:
for k, val := range v {
m[ToString(k)] = ToStringSlice(val)
}
return m, nil
case map[any]string:
for k, val := range v {
m[ToString(k)] = ToStringSlice(val)
}
return m, nil
case map[any][]any:
for k, val := range v {
m[ToString(k)] = ToStringSlice(val)
}
return m, nil
case map[any]any:
for k, val := range v {
key, err := ToStringE(k)
if err != nil {
return m, fmt.Errorf(errorMsg, i, i, m)
}
value, err := ToStringSliceE(val)
if err != nil {
return m, fmt.Errorf(errorMsg, i, i, m)
}
m[key] = value
}
case string:
err := jsonStringToObject(v, &m)
return m, err
default:
return m, fmt.Errorf(errorMsg, i, i, m)
}
return m, nil
}
// ToStringMapBoolE casts any value to a map[string]bool type.
func ToStringMapBoolE(i any) (map[string]bool, error) {
return toStringMapE(i, ToBool)
}
// ToStringMapE casts any value to a map[string]any type.
func ToStringMapE(i any) (map[string]any, error) {
fn := func(i any) any { return i }
return toStringMapE(i, fn)
}
func toStringMapIntE[T int | int64](i any, fn func(any) T, fnE func(any) (T, error)) (map[string]T, error) {
m := map[string]T{}
if i == nil {
return nil, fmt.Errorf(errorMsg, i, i, m)
}
switch v := i.(type) {
case map[string]T:
return v, nil
case map[string]any:
for k, val := range v {
m[k] = fn(val)
}
return m, nil
case map[any]T:
for k, val := range v {
m[ToString(k)] = val
}
return m, nil
case map[any]any:
for k, val := range v {
m[ToString(k)] = fn(val)
}
return m, nil
case string:
err := jsonStringToObject(v, &m)
return m, err
}
if reflect.TypeOf(i).Kind() != reflect.Map {
return m, fmt.Errorf(errorMsg, i, i, m)
}
mVal := reflect.ValueOf(m)
v := reflect.ValueOf(i)
for _, keyVal := range v.MapKeys() {
val, err := fnE(v.MapIndex(keyVal).Interface())
if err != nil {
return m, fmt.Errorf(errorMsg, i, i, m)
}
mVal.SetMapIndex(keyVal, reflect.ValueOf(val))
}
return m, nil
}
// ToStringMapIntE casts any value to a map[string]int type.
func ToStringMapIntE(i any) (map[string]int, error) {
return toStringMapIntE(i, ToInt, ToIntE)
}
// ToStringMapInt64E casts any value to a map[string]int64 type.
func ToStringMapInt64E(i any) (map[string]int64, error) {
return toStringMapIntE(i, ToInt64, ToInt64E)
}
// jsonStringToObject attempts to unmarshall a string as JSON into
// the object passed as pointer.
func jsonStringToObject(s string, v any) error {
data := []byte(s)
return json.Unmarshal(data, v)
}

549
vendor/github.com/spf13/cast/number.go generated vendored Normal file
View File

@@ -0,0 +1,549 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package cast
import (
"encoding/json"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
var errNegativeNotAllowed = errors.New("unable to cast negative value")
type float64EProvider interface {
Float64() (float64, error)
}
type float64Provider interface {
Float64() float64
}
// Number is a type parameter constraint for functions accepting number types.
//
// It represents the supported number types this package can cast to.
type Number interface {
int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64
}
type integer interface {
int | int8 | int16 | int32 | int64
}
type unsigned interface {
uint | uint8 | uint16 | uint32 | uint64
}
type float interface {
float32 | float64
}
// ToNumberE casts any value to a [Number] type.
func ToNumberE[T Number](i any) (T, error) {
var t T
switch any(t).(type) {
case int:
return toNumberE[T](i, parseNumber[T])
case int8:
return toNumberE[T](i, parseNumber[T])
case int16:
return toNumberE[T](i, parseNumber[T])
case int32:
return toNumberE[T](i, parseNumber[T])
case int64:
return toNumberE[T](i, parseNumber[T])
case uint:
return toUnsignedNumberE[T](i, parseNumber[T])
case uint8:
return toUnsignedNumberE[T](i, parseNumber[T])
case uint16:
return toUnsignedNumberE[T](i, parseNumber[T])
case uint32:
return toUnsignedNumberE[T](i, parseNumber[T])
case uint64:
return toUnsignedNumberE[T](i, parseNumber[T])
case float32:
return toNumberE[T](i, parseNumber[T])
case float64:
return toNumberE[T](i, parseNumber[T])
default:
return 0, fmt.Errorf("unknown number type: %T", t)
}
}
// ToNumber casts any value to a [Number] type.
func ToNumber[T Number](i any) T {
v, _ := ToNumberE[T](i)
return v
}
// toNumber's semantics differ from other "to" functions.
// It returns false as the second parameter if the conversion fails.
// This is to signal other callers that they should proceed with their own conversions.
func toNumber[T Number](i any) (T, bool) {
i, _ = indirect(i)
switch s := i.(type) {
case T:
return s, true
case int:
return T(s), true
case int8:
return T(s), true
case int16:
return T(s), true
case int32:
return T(s), true
case int64:
return T(s), true
case uint:
return T(s), true
case uint8:
return T(s), true
case uint16:
return T(s), true
case uint32:
return T(s), true
case uint64:
return T(s), true
case float32:
return T(s), true
case float64:
return T(s), true
case bool:
if s {
return 1, true
}
return 0, true
case nil:
return 0, true
case time.Weekday:
return T(s), true
case time.Month:
return T(s), true
}
return 0, false
}
func toNumberE[T Number](i any, parseFn func(string) (T, error)) (T, error) {
n, ok := toNumber[T](i)
if ok {
return n, nil
}
i, _ = indirect(i)
switch s := i.(type) {
case string:
if s == "" {
return 0, nil
}
v, err := parseFn(s)
if err != nil {
return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
}
return v, nil
case json.Number:
if s == "" {
return 0, nil
}
v, err := parseFn(string(s))
if err != nil {
return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
}
return v, nil
case float64EProvider:
if _, ok := any(n).(float64); !ok {
return 0, fmt.Errorf(errorMsg, i, i, n)
}
v, err := s.Float64()
if err != nil {
return 0, fmt.Errorf(errorMsg, i, i, n)
}
return T(v), nil
case float64Provider:
if _, ok := any(n).(float64); !ok {
return 0, fmt.Errorf(errorMsg, i, i, n)
}
return T(s.Float64()), nil
default:
if i, ok := resolveAlias(i); ok {
return toNumberE(i, parseFn)
}
return 0, fmt.Errorf(errorMsg, i, i, n)
}
}
func toUnsignedNumber[T Number](i any) (T, bool, bool) {
i, _ = indirect(i)
switch s := i.(type) {
case T:
return s, true, true
case int:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case int8:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case int16:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case int32:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case int64:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case uint:
return T(s), true, true
case uint8:
return T(s), true, true
case uint16:
return T(s), true, true
case uint32:
return T(s), true, true
case uint64:
return T(s), true, true
case float32:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case float64:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case bool:
if s {
return 1, true, true
}
return 0, true, true
case nil:
return 0, true, true
case time.Weekday:
if s < 0 {
return 0, false, false
}
return T(s), true, true
case time.Month:
if s < 0 {
return 0, false, false
}
return T(s), true, true
}
return 0, true, false
}
func toUnsignedNumberE[T Number](i any, parseFn func(string) (T, error)) (T, error) {
n, valid, ok := toUnsignedNumber[T](i)
if ok {
return n, nil
}
i, _ = indirect(i)
if !valid {
return 0, errNegativeNotAllowed
}
switch s := i.(type) {
case string:
if s == "" {
return 0, nil
}
v, err := parseFn(s)
if err != nil {
return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
}
return v, nil
case json.Number:
if s == "" {
return 0, nil
}
v, err := parseFn(string(s))
if err != nil {
return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
}
return v, nil
case float64EProvider:
if _, ok := any(n).(float64); !ok {
return 0, fmt.Errorf(errorMsg, i, i, n)
}
v, err := s.Float64()
if err != nil {
return 0, fmt.Errorf(errorMsg, i, i, n)
}
if v < 0 {
return 0, errNegativeNotAllowed
}
return T(v), nil
case float64Provider:
if _, ok := any(n).(float64); !ok {
return 0, fmt.Errorf(errorMsg, i, i, n)
}
v := s.Float64()
if v < 0 {
return 0, errNegativeNotAllowed
}
return T(v), nil
default:
if i, ok := resolveAlias(i); ok {
return toUnsignedNumberE(i, parseFn)
}
return 0, fmt.Errorf(errorMsg, i, i, n)
}
}
func parseNumber[T Number](s string) (T, error) {
var t T
switch any(t).(type) {
case int:
v, err := parseInt[int](s)
return T(v), err
case int8:
v, err := parseInt[int8](s)
return T(v), err
case int16:
v, err := parseInt[int16](s)
return T(v), err
case int32:
v, err := parseInt[int32](s)
return T(v), err
case int64:
v, err := parseInt[int64](s)
return T(v), err
case uint:
v, err := parseUint[uint](s)
return T(v), err
case uint8:
v, err := parseUint[uint8](s)
return T(v), err
case uint16:
v, err := parseUint[uint16](s)
return T(v), err
case uint32:
v, err := parseUint[uint32](s)
return T(v), err
case uint64:
v, err := parseUint[uint64](s)
return T(v), err
case float32:
v, err := strconv.ParseFloat(s, 32)
return T(v), err
case float64:
v, err := strconv.ParseFloat(s, 64)
return T(v), err
default:
return 0, fmt.Errorf("unknown number type: %T", t)
}
}
func parseInt[T integer](s string) (T, error) {
v, err := strconv.ParseInt(trimDecimal(s), 0, 0)
if err != nil {
return 0, err
}
return T(v), nil
}
func parseUint[T unsigned](s string) (T, error) {
v, err := strconv.ParseUint(strings.TrimLeft(trimDecimal(s), "+"), 0, 0)
if err != nil {
return 0, err
}
return T(v), nil
}
func parseFloat[T float](s string) (T, error) {
var t T
var v any
var err error
switch any(t).(type) {
case float32:
n, e := strconv.ParseFloat(s, 32)
v = float32(n)
err = e
case float64:
n, e := strconv.ParseFloat(s, 64)
v = float64(n)
err = e
}
return v.(T), err
}
// ToFloat64E casts an interface to a float64 type.
func ToFloat64E(i any) (float64, error) {
return toNumberE[float64](i, parseFloat[float64])
}
// ToFloat32E casts an interface to a float32 type.
func ToFloat32E(i any) (float32, error) {
return toNumberE[float32](i, parseFloat[float32])
}
// ToInt64E casts an interface to an int64 type.
func ToInt64E(i any) (int64, error) {
return toNumberE[int64](i, parseInt[int64])
}
// ToInt32E casts an interface to an int32 type.
func ToInt32E(i any) (int32, error) {
return toNumberE[int32](i, parseInt[int32])
}
// ToInt16E casts an interface to an int16 type.
func ToInt16E(i any) (int16, error) {
return toNumberE[int16](i, parseInt[int16])
}
// ToInt8E casts an interface to an int8 type.
func ToInt8E(i any) (int8, error) {
return toNumberE[int8](i, parseInt[int8])
}
// ToIntE casts an interface to an int type.
func ToIntE(i any) (int, error) {
return toNumberE[int](i, parseInt[int])
}
// ToUintE casts an interface to a uint type.
func ToUintE(i any) (uint, error) {
return toUnsignedNumberE[uint](i, parseUint[uint])
}
// ToUint64E casts an interface to a uint64 type.
func ToUint64E(i any) (uint64, error) {
return toUnsignedNumberE[uint64](i, parseUint[uint64])
}
// ToUint32E casts an interface to a uint32 type.
func ToUint32E(i any) (uint32, error) {
return toUnsignedNumberE[uint32](i, parseUint[uint32])
}
// ToUint16E casts an interface to a uint16 type.
func ToUint16E(i any) (uint16, error) {
return toUnsignedNumberE[uint16](i, parseUint[uint16])
}
// ToUint8E casts an interface to a uint type.
func ToUint8E(i any) (uint8, error) {
return toUnsignedNumberE[uint8](i, parseUint[uint8])
}
func trimZeroDecimal(s string) string {
var foundZero bool
for i := len(s); i > 0; i-- {
switch s[i-1] {
case '.':
if foundZero {
return s[:i-1]
}
case '0':
foundZero = true
default:
return s
}
}
return s
}
var stringNumberRe = regexp.MustCompile(`^([-+]?\d*)(\.\d*)?$`)
// see [BenchmarkDecimal] for details about the implementation
func trimDecimal(s string) string {
if !strings.Contains(s, ".") {
return s
}
matches := stringNumberRe.FindStringSubmatch(s)
if matches != nil {
// matches[1] is the captured integer part with sign
s = matches[1]
// handle special cases
switch s {
case "-", "+":
s += "0"
case "":
s = "0"
}
return s
}
return s
}

106
vendor/github.com/spf13/cast/slice.go generated vendored Normal file
View File

@@ -0,0 +1,106 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package cast
import (
"fmt"
"reflect"
"strings"
)
// ToSliceE casts any value to a []any type.
func ToSliceE(i any) ([]any, error) {
i, _ = indirect(i)
var s []any
switch v := i.(type) {
case []any:
// TODO: use slices.Clone
return append(s, v...), nil
case []map[string]any:
for _, u := range v {
s = append(s, u)
}
return s, nil
default:
return s, fmt.Errorf(errorMsg, i, i, s)
}
}
func toSliceE[T Basic](i any) ([]T, error) {
v, ok, err := toSliceEOk[T](i)
if err != nil {
return nil, err
}
if !ok {
return nil, fmt.Errorf(errorMsg, i, i, []T{})
}
return v, nil
}
func toSliceEOk[T Basic](i any) ([]T, bool, error) {
i, _ = indirect(i)
if i == nil {
return nil, true, fmt.Errorf(errorMsg, i, i, []T{})
}
switch v := i.(type) {
case []T:
// TODO: clone slice
return v, true, nil
}
kind := reflect.TypeOf(i).Kind()
switch kind {
case reflect.Slice, reflect.Array:
s := reflect.ValueOf(i)
a := make([]T, s.Len())
for j := 0; j < s.Len(); j++ {
val, err := ToE[T](s.Index(j).Interface())
if err != nil {
return nil, true, fmt.Errorf(errorMsg, i, i, []T{})
}
a[j] = val
}
return a, true, nil
default:
return nil, false, nil
}
}
// ToStringSliceE casts any value to a []string type.
func ToStringSliceE(i any) ([]string, error) {
if a, ok, err := toSliceEOk[string](i); ok {
if err != nil {
return nil, err
}
return a, nil
}
var a []string
switch v := i.(type) {
case string:
return strings.Fields(v), nil
case any:
str, err := ToStringE(v)
if err != nil {
return nil, fmt.Errorf(errorMsg, i, i, a)
}
return []string{str}, nil
default:
return nil, fmt.Errorf(errorMsg, i, i, a)
}
}

116
vendor/github.com/spf13/cast/time.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package cast
import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/spf13/cast/internal"
)
// ToTimeE any value to a [time.Time] type.
func ToTimeE(i any) (time.Time, error) {
return ToTimeInDefaultLocationE(i, time.UTC)
}
// ToTimeInDefaultLocationE casts an empty interface to [time.Time],
// interpreting inputs without a timezone to be in the given location,
// or the local timezone if nil.
func ToTimeInDefaultLocationE(i any, location *time.Location) (tim time.Time, err error) {
i, _ = indirect(i)
switch v := i.(type) {
case time.Time:
return v, nil
case string:
return StringToDateInDefaultLocation(v, location)
case json.Number:
// Originally this used ToInt64E, but adding string float conversion broke ToTime.
// the behavior of ToTime would have changed if we continued using it.
// For now, using json.Number's own Int64 method should be good enough to preserve backwards compatibility.
v = json.Number(trimZeroDecimal(string(v)))
s, err1 := v.Int64()
if err1 != nil {
return time.Time{}, fmt.Errorf(errorMsg, i, i, time.Time{})
}
return time.Unix(s, 0), nil
case int:
return time.Unix(int64(v), 0), nil
case int32:
return time.Unix(int64(v), 0), nil
case int64:
return time.Unix(v, 0), nil
case uint:
return time.Unix(int64(v), 0), nil
case uint32:
return time.Unix(int64(v), 0), nil
case uint64:
return time.Unix(int64(v), 0), nil
case nil:
return time.Time{}, nil
default:
return time.Time{}, fmt.Errorf(errorMsg, i, i, time.Time{})
}
}
// ToDurationE casts any value to a [time.Duration] type.
func ToDurationE(i any) (time.Duration, error) {
i, _ = indirect(i)
switch s := i.(type) {
case time.Duration:
return s, nil
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
v, err := ToInt64E(s)
if err != nil {
// TODO: once there is better error handling, this should be easier
return 0, errors.New(strings.ReplaceAll(err.Error(), " int64", "time.Duration"))
}
return time.Duration(v), nil
case float32, float64, float64EProvider, float64Provider:
v, err := ToFloat64E(s)
if err != nil {
// TODO: once there is better error handling, this should be easier
return 0, errors.New(strings.ReplaceAll(err.Error(), " float64", "time.Duration"))
}
return time.Duration(v), nil
case string:
if !strings.ContainsAny(s, "nsuµmh") {
return time.ParseDuration(s + "ns")
}
return time.ParseDuration(s)
case nil:
return time.Duration(0), nil
default:
if i, ok := resolveAlias(i); ok {
return ToDurationE(i)
}
return 0, fmt.Errorf(errorMsg, i, i, time.Duration(0))
}
}
// StringToDate attempts to parse a string into a [time.Time] type using a
// predefined list of formats.
//
// If no suitable format is found, an error is returned.
func StringToDate(s string) (time.Time, error) {
return internal.ParseDateWith(s, time.UTC, internal.TimeFormats)
}
// StringToDateInDefaultLocation casts an empty interface to a [time.Time],
// interpreting inputs without a timezone to be in the given location,
// or the local timezone if nil.
func StringToDateInDefaultLocation(s string, location *time.Location) (time.Time, error) {
return internal.ParseDateWith(s, location, internal.TimeFormats)
}

View File

@@ -1,27 +0,0 @@
// Code generated by "stringer -type timeFormatType"; DO NOT EDIT.
package cast
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[timeFormatNoTimezone-0]
_ = x[timeFormatNamedTimezone-1]
_ = x[timeFormatNumericTimezone-2]
_ = x[timeFormatNumericAndNamedTimezone-3]
_ = x[timeFormatTimeOnly-4]
}
const _timeFormatType_name = "timeFormatNoTimezonetimeFormatNamedTimezonetimeFormatNumericTimezonetimeFormatNumericAndNamedTimezonetimeFormatTimeOnly"
var _timeFormatType_index = [...]uint8{0, 20, 43, 68, 101, 119}
func (i timeFormatType) String() string {
if i < 0 || i >= timeFormatType(len(_timeFormatType_index)-1) {
return "timeFormatType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _timeFormatType_name[_timeFormatType_index[i]:_timeFormatType_index[i+1]]
}

261
vendor/github.com/spf13/cast/zz_generated.go generated vendored Normal file
View File

@@ -0,0 +1,261 @@
// Code generated by cast generator. DO NOT EDIT.
package cast
import "time"
// ToBool casts any value to a(n) bool type.
func ToBool(i any) bool {
v, _ := ToBoolE(i)
return v
}
// ToString casts any value to a(n) string type.
func ToString(i any) string {
v, _ := ToStringE(i)
return v
}
// ToTime casts any value to a(n) time.Time type.
func ToTime(i any) time.Time {
v, _ := ToTimeE(i)
return v
}
// ToTimeInDefaultLocation casts any value to a(n) time.Time type.
func ToTimeInDefaultLocation(i any, location *time.Location) time.Time {
v, _ := ToTimeInDefaultLocationE(i, location)
return v
}
// ToDuration casts any value to a(n) time.Duration type.
func ToDuration(i any) time.Duration {
v, _ := ToDurationE(i)
return v
}
// ToInt casts any value to a(n) int type.
func ToInt(i any) int {
v, _ := ToIntE(i)
return v
}
// ToInt8 casts any value to a(n) int8 type.
func ToInt8(i any) int8 {
v, _ := ToInt8E(i)
return v
}
// ToInt16 casts any value to a(n) int16 type.
func ToInt16(i any) int16 {
v, _ := ToInt16E(i)
return v
}
// ToInt32 casts any value to a(n) int32 type.
func ToInt32(i any) int32 {
v, _ := ToInt32E(i)
return v
}
// ToInt64 casts any value to a(n) int64 type.
func ToInt64(i any) int64 {
v, _ := ToInt64E(i)
return v
}
// ToUint casts any value to a(n) uint type.
func ToUint(i any) uint {
v, _ := ToUintE(i)
return v
}
// ToUint8 casts any value to a(n) uint8 type.
func ToUint8(i any) uint8 {
v, _ := ToUint8E(i)
return v
}
// ToUint16 casts any value to a(n) uint16 type.
func ToUint16(i any) uint16 {
v, _ := ToUint16E(i)
return v
}
// ToUint32 casts any value to a(n) uint32 type.
func ToUint32(i any) uint32 {
v, _ := ToUint32E(i)
return v
}
// ToUint64 casts any value to a(n) uint64 type.
func ToUint64(i any) uint64 {
v, _ := ToUint64E(i)
return v
}
// ToFloat32 casts any value to a(n) float32 type.
func ToFloat32(i any) float32 {
v, _ := ToFloat32E(i)
return v
}
// ToFloat64 casts any value to a(n) float64 type.
func ToFloat64(i any) float64 {
v, _ := ToFloat64E(i)
return v
}
// ToStringMapString casts any value to a(n) map[string]string type.
func ToStringMapString(i any) map[string]string {
v, _ := ToStringMapStringE(i)
return v
}
// ToStringMapStringSlice casts any value to a(n) map[string][]string type.
func ToStringMapStringSlice(i any) map[string][]string {
v, _ := ToStringMapStringSliceE(i)
return v
}
// ToStringMapBool casts any value to a(n) map[string]bool type.
func ToStringMapBool(i any) map[string]bool {
v, _ := ToStringMapBoolE(i)
return v
}
// ToStringMapInt casts any value to a(n) map[string]int type.
func ToStringMapInt(i any) map[string]int {
v, _ := ToStringMapIntE(i)
return v
}
// ToStringMapInt64 casts any value to a(n) map[string]int64 type.
func ToStringMapInt64(i any) map[string]int64 {
v, _ := ToStringMapInt64E(i)
return v
}
// ToStringMap casts any value to a(n) map[string]any type.
func ToStringMap(i any) map[string]any {
v, _ := ToStringMapE(i)
return v
}
// ToSlice casts any value to a(n) []any type.
func ToSlice(i any) []any {
v, _ := ToSliceE(i)
return v
}
// ToBoolSlice casts any value to a(n) []bool type.
func ToBoolSlice(i any) []bool {
v, _ := ToBoolSliceE(i)
return v
}
// ToStringSlice casts any value to a(n) []string type.
func ToStringSlice(i any) []string {
v, _ := ToStringSliceE(i)
return v
}
// ToIntSlice casts any value to a(n) []int type.
func ToIntSlice(i any) []int {
v, _ := ToIntSliceE(i)
return v
}
// ToInt64Slice casts any value to a(n) []int64 type.
func ToInt64Slice(i any) []int64 {
v, _ := ToInt64SliceE(i)
return v
}
// ToUintSlice casts any value to a(n) []uint type.
func ToUintSlice(i any) []uint {
v, _ := ToUintSliceE(i)
return v
}
// ToFloat64Slice casts any value to a(n) []float64 type.
func ToFloat64Slice(i any) []float64 {
v, _ := ToFloat64SliceE(i)
return v
}
// ToDurationSlice casts any value to a(n) []time.Duration type.
func ToDurationSlice(i any) []time.Duration {
v, _ := ToDurationSliceE(i)
return v
}
// ToBoolSliceE casts any value to a(n) []bool type.
func ToBoolSliceE(i any) ([]bool, error) {
return toSliceE[bool](i)
}
// ToDurationSliceE casts any value to a(n) []time.Duration type.
func ToDurationSliceE(i any) ([]time.Duration, error) {
return toSliceE[time.Duration](i)
}
// ToIntSliceE casts any value to a(n) []int type.
func ToIntSliceE(i any) ([]int, error) {
return toSliceE[int](i)
}
// ToInt8SliceE casts any value to a(n) []int8 type.
func ToInt8SliceE(i any) ([]int8, error) {
return toSliceE[int8](i)
}
// ToInt16SliceE casts any value to a(n) []int16 type.
func ToInt16SliceE(i any) ([]int16, error) {
return toSliceE[int16](i)
}
// ToInt32SliceE casts any value to a(n) []int32 type.
func ToInt32SliceE(i any) ([]int32, error) {
return toSliceE[int32](i)
}
// ToInt64SliceE casts any value to a(n) []int64 type.
func ToInt64SliceE(i any) ([]int64, error) {
return toSliceE[int64](i)
}
// ToUintSliceE casts any value to a(n) []uint type.
func ToUintSliceE(i any) ([]uint, error) {
return toSliceE[uint](i)
}
// ToUint8SliceE casts any value to a(n) []uint8 type.
func ToUint8SliceE(i any) ([]uint8, error) {
return toSliceE[uint8](i)
}
// ToUint16SliceE casts any value to a(n) []uint16 type.
func ToUint16SliceE(i any) ([]uint16, error) {
return toSliceE[uint16](i)
}
// ToUint32SliceE casts any value to a(n) []uint32 type.
func ToUint32SliceE(i any) ([]uint32, error) {
return toSliceE[uint32](i)
}
// ToUint64SliceE casts any value to a(n) []uint64 type.
func ToUint64SliceE(i any) ([]uint64, error) {
return toSliceE[uint64](i)
}
// ToFloat32SliceE casts any value to a(n) []float32 type.
func ToFloat32SliceE(i any) ([]float32, error) {
return toSliceE[float32](i)
}
// ToFloat64SliceE casts any value to a(n) []float64 type.
func ToFloat64SliceE(i any) ([]float64, error) {
return toSliceE[float64](i)
}

View File

@@ -284,6 +284,33 @@ func main() {
} }
``` ```
### Using pflag with go test
`pflag` does not parse the shorthand versions of go test's built-in flags (i.e., those starting with `-test.`).
For more context, see issues [#63](https://github.com/spf13/pflag/issues/63) and [#238](https://github.com/spf13/pflag/issues/238) for more details.
For example, if you use pflag in your `TestMain` function and call `pflag.Parse()` after defining your custom flags, running a test like this:
```bash
go test /your/tests -run ^YourTest -v --your-test-pflags
```
will result in the `-v` flag being ignored. This happens because of the way pflag handles flag parsing, skipping over go test's built-in shorthand flags.
To work around this, you can use the `ParseSkippedFlags` function, which ensures that go test's flags are parsed separately using the standard flag package.
**Example**: You want to parse go test flags that are otherwise ignore by `pflag.Parse()`
```go
import (
goflag "flag"
flag "github.com/spf13/pflag"
)
var ip *int = flag.Int("flagname", 1234, "help message for flagname")
func main() {
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
flag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine)
flag.Parse()
}
```
## More info ## More info
You can see the full reference documentation of the pflag package You can see the full reference documentation of the pflag package

40
vendor/github.com/spf13/pflag/bool_func.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package pflag
// -- func Value
type boolfuncValue func(string) error
func (f boolfuncValue) Set(s string) error { return f(s) }
func (f boolfuncValue) Type() string { return "boolfunc" }
func (f boolfuncValue) String() string { return "" } // same behavior as stdlib 'flag' package
func (f boolfuncValue) IsBoolFlag() bool { return true }
// BoolFunc defines a func flag with specified name, callback function and usage string.
//
// The callback function will be called every time "--{name}" (or any form that matches the flag) is parsed
// on the command line.
func (f *FlagSet) BoolFunc(name string, usage string, fn func(string) error) {
f.BoolFuncP(name, "", usage, fn)
}
// BoolFuncP is like BoolFunc, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) BoolFuncP(name, shorthand string, usage string, fn func(string) error) {
var val Value = boolfuncValue(fn)
flag := f.VarPF(val, name, shorthand, usage)
flag.NoOptDefVal = "true"
}
// BoolFunc defines a func flag with specified name, callback function and usage string.
//
// The callback function will be called every time "--{name}" (or any form that matches the flag) is parsed
// on the command line.
func BoolFunc(name string, usage string, fn func(string) error) {
CommandLine.BoolFuncP(name, "", usage, fn)
}
// BoolFuncP is like BoolFunc, but accepts a shorthand letter that can be used after a single dash.
func BoolFuncP(name, shorthand string, usage string, fn func(string) error) {
CommandLine.BoolFuncP(name, shorthand, usage, fn)
}

View File

@@ -85,7 +85,7 @@ func (f *FlagSet) CountP(name, shorthand string, usage string) *int {
// Count defines a count flag with specified name, default value, and usage string. // Count defines a count flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag. // The return value is the address of an int variable that stores the value of the flag.
// A count flag will add 1 to its value evey time it is found on the command line // A count flag will add 1 to its value every time it is found on the command line
func Count(name string, usage string) *int { func Count(name string, usage string) *int {
return CommandLine.CountP(name, "", usage) return CommandLine.CountP(name, "", usage)
} }

149
vendor/github.com/spf13/pflag/errors.go generated vendored Normal file
View File

@@ -0,0 +1,149 @@
package pflag
import "fmt"
// notExistErrorMessageType specifies which flavor of "flag does not exist"
// is printed by NotExistError. This allows the related errors to be grouped
// under a single NotExistError struct without making a breaking change to
// the error message text.
type notExistErrorMessageType int
const (
flagNotExistMessage notExistErrorMessageType = iota
flagNotDefinedMessage
flagNoSuchFlagMessage
flagUnknownFlagMessage
flagUnknownShorthandFlagMessage
)
// NotExistError is the error returned when trying to access a flag that
// does not exist in the FlagSet.
type NotExistError struct {
name string
specifiedShorthands string
messageType notExistErrorMessageType
}
// Error implements error.
func (e *NotExistError) Error() string {
switch e.messageType {
case flagNotExistMessage:
return fmt.Sprintf("flag %q does not exist", e.name)
case flagNotDefinedMessage:
return fmt.Sprintf("flag accessed but not defined: %s", e.name)
case flagNoSuchFlagMessage:
return fmt.Sprintf("no such flag -%v", e.name)
case flagUnknownFlagMessage:
return fmt.Sprintf("unknown flag: --%s", e.name)
case flagUnknownShorthandFlagMessage:
c := rune(e.name[0])
return fmt.Sprintf("unknown shorthand flag: %q in -%s", c, e.specifiedShorthands)
}
panic(fmt.Errorf("unknown flagNotExistErrorMessageType: %v", e.messageType))
}
// GetSpecifiedName returns the name of the flag (without dashes) as it
// appeared in the parsed arguments.
func (e *NotExistError) GetSpecifiedName() string {
return e.name
}
// GetSpecifiedShortnames returns the group of shorthand arguments
// (without dashes) that the flag appeared within. If the flag was not in a
// shorthand group, this will return an empty string.
func (e *NotExistError) GetSpecifiedShortnames() string {
return e.specifiedShorthands
}
// ValueRequiredError is the error returned when a flag needs an argument but
// no argument was provided.
type ValueRequiredError struct {
flag *Flag
specifiedName string
specifiedShorthands string
}
// Error implements error.
func (e *ValueRequiredError) Error() string {
if len(e.specifiedShorthands) > 0 {
c := rune(e.specifiedName[0])
return fmt.Sprintf("flag needs an argument: %q in -%s", c, e.specifiedShorthands)
}
return fmt.Sprintf("flag needs an argument: --%s", e.specifiedName)
}
// GetFlag returns the flag for which the error occurred.
func (e *ValueRequiredError) GetFlag() *Flag {
return e.flag
}
// GetSpecifiedName returns the name of the flag (without dashes) as it
// appeared in the parsed arguments.
func (e *ValueRequiredError) GetSpecifiedName() string {
return e.specifiedName
}
// GetSpecifiedShortnames returns the group of shorthand arguments
// (without dashes) that the flag appeared within. If the flag was not in a
// shorthand group, this will return an empty string.
func (e *ValueRequiredError) GetSpecifiedShortnames() string {
return e.specifiedShorthands
}
// InvalidValueError is the error returned when an invalid value is used
// for a flag.
type InvalidValueError struct {
flag *Flag
value string
cause error
}
// Error implements error.
func (e *InvalidValueError) Error() string {
flag := e.flag
var flagName string
if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name)
} else {
flagName = fmt.Sprintf("--%s", flag.Name)
}
return fmt.Sprintf("invalid argument %q for %q flag: %v", e.value, flagName, e.cause)
}
// Unwrap implements errors.Unwrap.
func (e *InvalidValueError) Unwrap() error {
return e.cause
}
// GetFlag returns the flag for which the error occurred.
func (e *InvalidValueError) GetFlag() *Flag {
return e.flag
}
// GetValue returns the invalid value that was provided.
func (e *InvalidValueError) GetValue() string {
return e.value
}
// InvalidSyntaxError is the error returned when a bad flag name is passed on
// the command line.
type InvalidSyntaxError struct {
specifiedFlag string
}
// Error implements error.
func (e *InvalidSyntaxError) Error() string {
return fmt.Sprintf("bad flag syntax: %s", e.specifiedFlag)
}
// GetSpecifiedName returns the exact flag (with dashes) as it
// appeared in the parsed arguments.
func (e *InvalidSyntaxError) GetSpecifiedFlag() string {
return e.specifiedFlag
}

121
vendor/github.com/spf13/pflag/flag.go generated vendored
View File

@@ -27,23 +27,32 @@ unaffected.
Define flags using flag.String(), Bool(), Int(), etc. Define flags using flag.String(), Bool(), Int(), etc.
This declares an integer flag, -flagname, stored in the pointer ip, with type *int. This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
var ip = flag.Int("flagname", 1234, "help message for flagname") var ip = flag.Int("flagname", 1234, "help message for flagname")
If you like, you can bind the flag to a variable using the Var() functions. If you like, you can bind the flag to a variable using the Var() functions.
var flagvar int var flagvar int
func init() { func init() {
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
} }
Or you can create custom flags that satisfy the Value interface (with Or you can create custom flags that satisfy the Value interface (with
pointer receivers) and couple them to flag parsing by pointer receivers) and couple them to flag parsing by
flag.Var(&flagVal, "name", "help message for flagname") flag.Var(&flagVal, "name", "help message for flagname")
For such flags, the default value is just the initial value of the variable. For such flags, the default value is just the initial value of the variable.
After all flags are defined, call After all flags are defined, call
flag.Parse() flag.Parse()
to parse the command line into the defined flags. to parse the command line into the defined flags.
Flags may then be used directly. If you're using the flags themselves, Flags may then be used directly. If you're using the flags themselves,
they are all pointers; if you bind to variables, they're values. they are all pointers; if you bind to variables, they're values.
fmt.Println("ip has value ", *ip) fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar) fmt.Println("flagvar has value ", flagvar)
@@ -54,22 +63,26 @@ The arguments are indexed from 0 through flag.NArg()-1.
The pflag package also defines some new functions that are not in flag, The pflag package also defines some new functions that are not in flag,
that give one-letter shorthands for flags. You can use these by appending that give one-letter shorthands for flags. You can use these by appending
'P' to the name of any function that defines a flag. 'P' to the name of any function that defines a flag.
var ip = flag.IntP("flagname", "f", 1234, "help message") var ip = flag.IntP("flagname", "f", 1234, "help message")
var flagvar bool var flagvar bool
func init() { func init() {
flag.BoolVarP(&flagvar, "boolname", "b", true, "help message") flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
} }
flag.VarP(&flagval, "varname", "v", "help message") flag.VarP(&flagval, "varname", "v", "help message")
Shorthand letters can be used with single dashes on the command line. Shorthand letters can be used with single dashes on the command line.
Boolean shorthand flags can be combined with other shorthand flags. Boolean shorthand flags can be combined with other shorthand flags.
Command line flag syntax: Command line flag syntax:
--flag // boolean flags only --flag // boolean flags only
--flag=x --flag=x
Unlike the flag package, a single dash before an option means something Unlike the flag package, a single dash before an option means something
different than a double dash. Single dashes signify a series of shorthand different than a double dash. Single dashes signify a series of shorthand
letters for flags. All but the last shorthand letter must be boolean flags. letters for flags. All but the last shorthand letter must be boolean flags.
// boolean flags // boolean flags
-f -f
-abc -abc
@@ -124,12 +137,17 @@ const (
PanicOnError PanicOnError
) )
// ParseErrorsWhitelist defines the parsing errors that can be ignored // ParseErrorsAllowlist defines the parsing errors that can be ignored
type ParseErrorsWhitelist struct { type ParseErrorsAllowlist struct {
// UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags
UnknownFlags bool UnknownFlags bool
} }
// ParseErrorsWhitelist defines the parsing errors that can be ignored.
//
// Deprecated: use [ParseErrorsAllowlist] instead. This type will be removed in a future release.
type ParseErrorsWhitelist = ParseErrorsAllowlist
// NormalizedName is a flag name that has been normalized according to rules // NormalizedName is a flag name that has been normalized according to rules
// for the FlagSet (e.g. making '-' and '_' equivalent). // for the FlagSet (e.g. making '-' and '_' equivalent).
type NormalizedName string type NormalizedName string
@@ -145,8 +163,13 @@ type FlagSet struct {
// help/usage messages. // help/usage messages.
SortFlags bool SortFlags bool
// ParseErrorsWhitelist is used to configure a whitelist of errors // ParseErrorsAllowlist is used to configure an allowlist of errors
ParseErrorsWhitelist ParseErrorsWhitelist ParseErrorsAllowlist ParseErrorsAllowlist
// ParseErrorsAllowlist is used to configure an allowlist of errors.
//
// Deprecated: use [FlagSet.ParseErrorsAllowlist] instead. This field will be removed in a future release.
ParseErrorsWhitelist ParseErrorsAllowlist
name string name string
parsed bool parsed bool
@@ -381,7 +404,7 @@ func (f *FlagSet) lookup(name NormalizedName) *Flag {
func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) { func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) {
flag := f.Lookup(name) flag := f.Lookup(name)
if flag == nil { if flag == nil {
err := fmt.Errorf("flag accessed but not defined: %s", name) err := &NotExistError{name: name, messageType: flagNotDefinedMessage}
return nil, err return nil, err
} }
@@ -411,7 +434,7 @@ func (f *FlagSet) ArgsLenAtDash() int {
func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
flag := f.Lookup(name) flag := f.Lookup(name)
if flag == nil { if flag == nil {
return fmt.Errorf("flag %q does not exist", name) return &NotExistError{name: name, messageType: flagNotExistMessage}
} }
if usageMessage == "" { if usageMessage == "" {
return fmt.Errorf("deprecated message for flag %q must be set", name) return fmt.Errorf("deprecated message for flag %q must be set", name)
@@ -427,7 +450,7 @@ func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error { func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error {
flag := f.Lookup(name) flag := f.Lookup(name)
if flag == nil { if flag == nil {
return fmt.Errorf("flag %q does not exist", name) return &NotExistError{name: name, messageType: flagNotExistMessage}
} }
if usageMessage == "" { if usageMessage == "" {
return fmt.Errorf("deprecated message for flag %q must be set", name) return fmt.Errorf("deprecated message for flag %q must be set", name)
@@ -441,7 +464,7 @@ func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) erro
func (f *FlagSet) MarkHidden(name string) error { func (f *FlagSet) MarkHidden(name string) error {
flag := f.Lookup(name) flag := f.Lookup(name)
if flag == nil { if flag == nil {
return fmt.Errorf("flag %q does not exist", name) return &NotExistError{name: name, messageType: flagNotExistMessage}
} }
flag.Hidden = true flag.Hidden = true
return nil return nil
@@ -464,18 +487,16 @@ func (f *FlagSet) Set(name, value string) error {
normalName := f.normalizeFlagName(name) normalName := f.normalizeFlagName(name)
flag, ok := f.formal[normalName] flag, ok := f.formal[normalName]
if !ok { if !ok {
return fmt.Errorf("no such flag -%v", name) return &NotExistError{name: name, messageType: flagNoSuchFlagMessage}
} }
err := flag.Value.Set(value) err := flag.Value.Set(value)
if err != nil { if err != nil {
var flagName string return &InvalidValueError{
if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { flag: flag,
flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name) value: value,
} else { cause: err,
flagName = fmt.Sprintf("--%s", flag.Name)
} }
return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err)
} }
if !flag.Changed { if !flag.Changed {
@@ -501,7 +522,7 @@ func (f *FlagSet) SetAnnotation(name, key string, values []string) error {
normalName := f.normalizeFlagName(name) normalName := f.normalizeFlagName(name)
flag, ok := f.formal[normalName] flag, ok := f.formal[normalName]
if !ok { if !ok {
return fmt.Errorf("no such flag -%v", name) return &NotExistError{name: name, messageType: flagNoSuchFlagMessage}
} }
if flag.Annotations == nil { if flag.Annotations == nil {
flag.Annotations = map[string][]string{} flag.Annotations = map[string][]string{}
@@ -538,7 +559,7 @@ func (f *FlagSet) PrintDefaults() {
func (f *Flag) defaultIsZeroValue() bool { func (f *Flag) defaultIsZeroValue() bool {
switch f.Value.(type) { switch f.Value.(type) {
case boolFlag: case boolFlag:
return f.DefValue == "false" return f.DefValue == "false" || f.DefValue == ""
case *durationValue: case *durationValue:
// Beginning in Go 1.7, duration zero values are "0s" // Beginning in Go 1.7, duration zero values are "0s"
return f.DefValue == "0" || f.DefValue == "0s" return f.DefValue == "0" || f.DefValue == "0s"
@@ -551,7 +572,7 @@ func (f *Flag) defaultIsZeroValue() bool {
case *intSliceValue, *stringSliceValue, *stringArrayValue: case *intSliceValue, *stringSliceValue, *stringArrayValue:
return f.DefValue == "[]" return f.DefValue == "[]"
default: default:
switch f.Value.String() { switch f.DefValue {
case "false": case "false":
return true return true
case "<nil>": case "<nil>":
@@ -588,8 +609,10 @@ func UnquoteUsage(flag *Flag) (name string, usage string) {
name = flag.Value.Type() name = flag.Value.Type()
switch name { switch name {
case "bool": case "bool", "boolfunc":
name = "" name = ""
case "func":
name = "value"
case "float64": case "float64":
name = "float" name = "float"
case "int64": case "int64":
@@ -707,7 +730,7 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string {
switch flag.Value.Type() { switch flag.Value.Type() {
case "string": case "string":
line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
case "bool": case "bool", "boolfunc":
if flag.NoOptDefVal != "true" { if flag.NoOptDefVal != "true" {
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
} }
@@ -911,12 +934,10 @@ func VarP(value Value, name, shorthand, usage string) {
CommandLine.VarP(value, name, shorthand, usage) CommandLine.VarP(value, name, shorthand, usage)
} }
// failf prints to standard error a formatted error and usage message and // fail prints an error message and usage message to standard error and
// returns the error. // returns the error.
func (f *FlagSet) failf(format string, a ...interface{}) error { func (f *FlagSet) fail(err error) error {
err := fmt.Errorf(format, a...)
if f.errorHandling != ContinueOnError { if f.errorHandling != ContinueOnError {
fmt.Fprintln(f.Output(), err)
f.usage() f.usage()
} }
return err return err
@@ -934,9 +955,9 @@ func (f *FlagSet) usage() {
} }
} }
//--unknown (args will be empty) // --unknown (args will be empty)
//--unknown --next-flag ... (args will be --next-flag ...) // --unknown --next-flag ... (args will be --next-flag ...)
//--unknown arg ... (args will be arg ...) // --unknown arg ... (args will be arg ...)
func stripUnknownFlagValue(args []string) []string { func stripUnknownFlagValue(args []string) []string {
if len(args) == 0 { if len(args) == 0 {
//--unknown //--unknown
@@ -960,7 +981,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
a = args a = args
name := s[2:] name := s[2:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' { if len(name) == 0 || name[0] == '-' || name[0] == '=' {
err = f.failf("bad flag syntax: %s", s) err = f.fail(&InvalidSyntaxError{specifiedFlag: s})
return return
} }
@@ -974,6 +995,8 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
f.usage() f.usage()
return a, ErrHelp return a, ErrHelp
case f.ParseErrorsWhitelist.UnknownFlags: case f.ParseErrorsWhitelist.UnknownFlags:
fallthrough
case f.ParseErrorsAllowlist.UnknownFlags:
// --unknown=unknownval arg ... // --unknown=unknownval arg ...
// we do not want to lose arg in this case // we do not want to lose arg in this case
if len(split) >= 2 { if len(split) >= 2 {
@@ -982,7 +1005,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
return stripUnknownFlagValue(a), nil return stripUnknownFlagValue(a), nil
default: default:
err = f.failf("unknown flag: --%s", name) err = f.fail(&NotExistError{name: name, messageType: flagUnknownFlagMessage})
return return
} }
} }
@@ -1000,13 +1023,16 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
a = a[1:] a = a[1:]
} else { } else {
// '--flag' (arg was required) // '--flag' (arg was required)
err = f.failf("flag needs an argument: %s", s) err = f.fail(&ValueRequiredError{
flag: flag,
specifiedName: name,
})
return return
} }
err = fn(flag, value) err = fn(flag, value)
if err != nil { if err != nil {
f.failf(err.Error()) f.fail(err)
} }
return return
} }
@@ -1014,7 +1040,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) { func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) {
outArgs = args outArgs = args
if strings.HasPrefix(shorthands, "test.") { if isGotestShorthandFlag(shorthands) {
return return
} }
@@ -1029,6 +1055,8 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
err = ErrHelp err = ErrHelp
return return
case f.ParseErrorsWhitelist.UnknownFlags: case f.ParseErrorsWhitelist.UnknownFlags:
fallthrough
case f.ParseErrorsAllowlist.UnknownFlags:
// '-f=arg arg ...' // '-f=arg arg ...'
// we do not want to lose arg in this case // we do not want to lose arg in this case
if len(shorthands) > 2 && shorthands[1] == '=' { if len(shorthands) > 2 && shorthands[1] == '=' {
@@ -1039,7 +1067,11 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
outArgs = stripUnknownFlagValue(outArgs) outArgs = stripUnknownFlagValue(outArgs)
return return
default: default:
err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) err = f.fail(&NotExistError{
name: string(c),
specifiedShorthands: shorthands,
messageType: flagUnknownShorthandFlagMessage,
})
return return
} }
} }
@@ -1062,7 +1094,11 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
outArgs = args[1:] outArgs = args[1:]
} else { } else {
// '-f' (arg was required) // '-f' (arg was required)
err = f.failf("flag needs an argument: %q in -%s", c, shorthands) err = f.fail(&ValueRequiredError{
flag: flag,
specifiedName: string(c),
specifiedShorthands: shorthands,
})
return return
} }
@@ -1072,7 +1108,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
err = fn(flag, value) err = fn(flag, value)
if err != nil { if err != nil {
f.failf(err.Error()) f.fail(err)
} }
return return
} }
@@ -1135,12 +1171,12 @@ func (f *FlagSet) Parse(arguments []string) error {
} }
f.parsed = true f.parsed = true
if len(arguments) < 0 { f.args = make([]string, 0, len(arguments))
if len(arguments) == 0 {
return nil return nil
} }
f.args = make([]string, 0, len(arguments))
set := func(flag *Flag, value string) error { set := func(flag *Flag, value string) error {
return f.Set(flag.Name, value) return f.Set(flag.Name, value)
} }
@@ -1151,7 +1187,10 @@ func (f *FlagSet) Parse(arguments []string) error {
case ContinueOnError: case ContinueOnError:
return err return err
case ExitOnError: case ExitOnError:
fmt.Println(err) if err == ErrHelp {
os.Exit(0)
}
fmt.Fprintln(f.Output(), err)
os.Exit(2) os.Exit(2)
case PanicOnError: case PanicOnError:
panic(err) panic(err)
@@ -1177,6 +1216,10 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string)
case ContinueOnError: case ContinueOnError:
return err return err
case ExitOnError: case ExitOnError:
if err == ErrHelp {
os.Exit(0)
}
fmt.Fprintln(f.Output(), err)
os.Exit(2) os.Exit(2)
case PanicOnError: case PanicOnError:
panic(err) panic(err)

37
vendor/github.com/spf13/pflag/func.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
package pflag
// -- func Value
type funcValue func(string) error
func (f funcValue) Set(s string) error { return f(s) }
func (f funcValue) Type() string { return "func" }
func (f funcValue) String() string { return "" } // same behavior as stdlib 'flag' package
// Func defines a func flag with specified name, callback function and usage string.
//
// The callback function will be called every time "--{name}={value}" (or equivalent) is
// parsed on the command line, with "{value}" as an argument.
func (f *FlagSet) Func(name string, usage string, fn func(string) error) {
f.FuncP(name, "", usage, fn)
}
// FuncP is like Func, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) FuncP(name string, shorthand string, usage string, fn func(string) error) {
var val Value = funcValue(fn)
f.VarP(val, name, shorthand, usage)
}
// Func defines a func flag with specified name, callback function and usage string.
//
// The callback function will be called every time "--{name}={value}" (or equivalent) is
// parsed on the command line, with "{value}" as an argument.
func Func(name string, usage string, fn func(string) error) {
CommandLine.FuncP(name, "", usage, fn)
}
// FuncP is like Func, but accepts a shorthand letter that can be used after a single dash.
func FuncP(name, shorthand string, usage string, fn func(string) error) {
CommandLine.FuncP(name, shorthand, usage, fn)
}

View File

@@ -8,8 +8,18 @@ import (
goflag "flag" goflag "flag"
"reflect" "reflect"
"strings" "strings"
"time"
) )
// go test flags prefixes
func isGotestFlag(flag string) bool {
return strings.HasPrefix(flag, "-test.")
}
func isGotestShorthandFlag(flag string) bool {
return strings.HasPrefix(flag, "test.")
}
// flagValueWrapper implements pflag.Value around a flag.Value. The main // flagValueWrapper implements pflag.Value around a flag.Value. The main
// difference here is the addition of the Type method that returns a string // difference here is the addition of the Type method that returns a string
// name of the type. As this is generally unknown, we approximate that with // name of the type. As this is generally unknown, we approximate that with
@@ -103,3 +113,49 @@ func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) {
} }
f.addedGoFlagSets = append(f.addedGoFlagSets, newSet) f.addedGoFlagSets = append(f.addedGoFlagSets, newSet)
} }
// CopyToGoFlagSet will add all current flags to the given Go flag set.
// Deprecation remarks get copied into the usage description.
// Whenever possible, a flag gets added for which Go flags shows
// a proper type in the help message.
func (f *FlagSet) CopyToGoFlagSet(newSet *goflag.FlagSet) {
f.VisitAll(func(flag *Flag) {
usage := flag.Usage
if flag.Deprecated != "" {
usage += " (DEPRECATED: " + flag.Deprecated + ")"
}
switch value := flag.Value.(type) {
case *stringValue:
newSet.StringVar((*string)(value), flag.Name, flag.DefValue, usage)
case *intValue:
newSet.IntVar((*int)(value), flag.Name, *(*int)(value), usage)
case *int64Value:
newSet.Int64Var((*int64)(value), flag.Name, *(*int64)(value), usage)
case *uintValue:
newSet.UintVar((*uint)(value), flag.Name, *(*uint)(value), usage)
case *uint64Value:
newSet.Uint64Var((*uint64)(value), flag.Name, *(*uint64)(value), usage)
case *durationValue:
newSet.DurationVar((*time.Duration)(value), flag.Name, *(*time.Duration)(value), usage)
case *float64Value:
newSet.Float64Var((*float64)(value), flag.Name, *(*float64)(value), usage)
default:
newSet.Var(flag.Value, flag.Name, usage)
}
})
}
// ParseSkippedFlags explicitly Parses go test flags (i.e. the one starting with '-test.') with goflag.Parse(),
// since by default those are skipped by pflag.Parse().
// Typical usage example: `ParseGoTestFlags(os.Args[1:], goflag.CommandLine)`
func ParseSkippedFlags(osArgs []string, goFlagSet *goflag.FlagSet) error {
var skippedFlags []string
for _, f := range osArgs {
if isGotestFlag(f) {
skippedFlags = append(skippedFlags, f)
}
}
return goFlagSet.Parse(skippedFlags)
}

View File

@@ -73,7 +73,7 @@ func (s *ipNetSliceValue) String() string {
func ipNetSliceConv(val string) (interface{}, error) { func ipNetSliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]") val = strings.Trim(val, "[]")
// Emtpy string would cause a slice with one (empty) entry // Empty string would cause a slice with one (empty) entry
if len(val) == 0 { if len(val) == 0 {
return []net.IPNet{}, nil return []net.IPNet{}, nil
} }

View File

@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"encoding/csv" "encoding/csv"
"fmt" "fmt"
"sort"
"strings" "strings"
) )
@@ -62,8 +63,15 @@ func (s *stringToStringValue) Type() string {
} }
func (s *stringToStringValue) String() string { func (s *stringToStringValue) String() string {
keys := make([]string, 0, len(*s.value))
for k := range *s.value {
keys = append(keys, k)
}
sort.Strings(keys)
records := make([]string, 0, len(*s.value)>>1) records := make([]string, 0, len(*s.value)>>1)
for k, v := range *s.value { for _, k := range keys {
v := (*s.value)[k]
records = append(records, k+"="+v) records = append(records, k+"="+v)
} }

81
vendor/github.com/spf13/pflag/text.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package pflag
import (
"encoding"
"fmt"
"reflect"
)
// following is copied from go 1.23.4 flag.go
type textValue struct{ p encoding.TextUnmarshaler }
func newTextValue(val encoding.TextMarshaler, p encoding.TextUnmarshaler) textValue {
ptrVal := reflect.ValueOf(p)
if ptrVal.Kind() != reflect.Ptr {
panic("variable value type must be a pointer")
}
defVal := reflect.ValueOf(val)
if defVal.Kind() == reflect.Ptr {
defVal = defVal.Elem()
}
if defVal.Type() != ptrVal.Type().Elem() {
panic(fmt.Sprintf("default type does not match variable type: %v != %v", defVal.Type(), ptrVal.Type().Elem()))
}
ptrVal.Elem().Set(defVal)
return textValue{p}
}
func (v textValue) Set(s string) error {
return v.p.UnmarshalText([]byte(s))
}
func (v textValue) Get() interface{} {
return v.p
}
func (v textValue) String() string {
if m, ok := v.p.(encoding.TextMarshaler); ok {
if b, err := m.MarshalText(); err == nil {
return string(b)
}
}
return ""
}
//end of copy
func (v textValue) Type() string {
return reflect.ValueOf(v.p).Type().Name()
}
// GetText set out, which implements encoding.UnmarshalText, to the value of a flag with given name
func (f *FlagSet) GetText(name string, out encoding.TextUnmarshaler) error {
flag := f.Lookup(name)
if flag == nil {
return fmt.Errorf("flag accessed but not defined: %s", name)
}
if flag.Value.Type() != reflect.TypeOf(out).Name() {
return fmt.Errorf("trying to get %s value of flag of type %s", reflect.TypeOf(out).Name(), flag.Value.Type())
}
return out.UnmarshalText([]byte(flag.Value.String()))
}
// TextVar defines a flag with a specified name, default value, and usage string. The argument p must be a pointer to a variable that will hold the value of the flag, and p must implement encoding.TextUnmarshaler. If the flag is used, the flag value will be passed to p's UnmarshalText method. The type of the default value must be the same as the type of p.
func (f *FlagSet) TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) {
f.VarP(newTextValue(value, p), name, "", usage)
}
// TextVarP is like TextVar, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) TextVarP(p encoding.TextUnmarshaler, name, shorthand string, value encoding.TextMarshaler, usage string) {
f.VarP(newTextValue(value, p), name, shorthand, usage)
}
// TextVar defines a flag with a specified name, default value, and usage string. The argument p must be a pointer to a variable that will hold the value of the flag, and p must implement encoding.TextUnmarshaler. If the flag is used, the flag value will be passed to p's UnmarshalText method. The type of the default value must be the same as the type of p.
func TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) {
CommandLine.VarP(newTextValue(value, p), name, "", usage)
}
// TextVarP is like TextVar, but accepts a shorthand letter that can be used after a single dash.
func TextVarP(p encoding.TextUnmarshaler, name, shorthand string, value encoding.TextMarshaler, usage string) {
CommandLine.VarP(newTextValue(value, p), name, shorthand, usage)
}

124
vendor/github.com/spf13/pflag/time.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
package pflag
import (
"fmt"
"strings"
"time"
)
// TimeValue adapts time.Time for use as a flag.
type timeValue struct {
*time.Time
formats []string
}
func newTimeValue(val time.Time, p *time.Time, formats []string) *timeValue {
*p = val
return &timeValue{
Time: p,
formats: formats,
}
}
// Set time.Time value from string based on accepted formats.
func (d *timeValue) Set(s string) error {
s = strings.TrimSpace(s)
for _, f := range d.formats {
v, err := time.Parse(f, s)
if err != nil {
continue
}
*d.Time = v
return nil
}
formatsString := ""
for i, f := range d.formats {
if i > 0 {
formatsString += ", "
}
formatsString += fmt.Sprintf("`%s`", f)
}
return fmt.Errorf("invalid time format `%s` must be one of: %s", s, formatsString)
}
// Type name for time.Time flags.
func (d *timeValue) Type() string {
return "time"
}
func (d *timeValue) String() string {
if d.Time.IsZero() {
return ""
} else {
return d.Time.Format(time.RFC3339Nano)
}
}
// GetTime return the time value of a flag with the given name
func (f *FlagSet) GetTime(name string) (time.Time, error) {
flag := f.Lookup(name)
if flag == nil {
err := fmt.Errorf("flag accessed but not defined: %s", name)
return time.Time{}, err
}
if flag.Value.Type() != "time" {
err := fmt.Errorf("trying to get %s value of flag of type %s", "time", flag.Value.Type())
return time.Time{}, err
}
val, ok := flag.Value.(*timeValue)
if !ok {
return time.Time{}, fmt.Errorf("value %s is not a time", flag.Value)
}
return *val.Time, nil
}
// TimeVar defines a time.Time flag with specified name, default value, and usage string.
// The argument p points to a time.Time variable in which to store the value of the flag.
func (f *FlagSet) TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
f.TimeVarP(p, name, "", value, formats, usage)
}
// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
f.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
}
// TimeVar defines a time.Time flag with specified name, default value, and usage string.
// The argument p points to a time.Time variable in which to store the value of the flag.
func TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
CommandLine.TimeVarP(p, name, "", value, formats, usage)
}
// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
func TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
CommandLine.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
}
// Time defines a time.Time flag with specified name, default value, and usage string.
// The return value is the address of a time.Time variable that stores the value of the flag.
func (f *FlagSet) Time(name string, value time.Time, formats []string, usage string) *time.Time {
return f.TimeP(name, "", value, formats, usage)
}
// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
p := new(time.Time)
f.TimeVarP(p, name, shorthand, value, formats, usage)
return p
}
// Time defines a time.Time flag with specified name, default value, and usage string.
// The return value is the address of a time.Time variable that stores the value of the flag.
func Time(name string, value time.Time, formats []string, usage string) *time.Time {
return CommandLine.TimeP(name, "", value, formats, usage)
}
// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
func TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
return CommandLine.TimeP(name, shorthand, value, formats, usage)
}

View File

@@ -16,3 +16,6 @@ indent_style = tab
[*.nix] [*.nix]
indent_size = 2 indent_size = 2
[.golangci.yaml]
indent_size = 2

View File

@@ -1,105 +1,118 @@
run: version: "2"
timeout: 5m
linters-settings: run:
gci: timeout: 5m
sections:
- standard
- default
- prefix(github.com/spf13/viper)
gocritic:
# Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
enabled-tags:
- diagnostic
- experimental
- opinionated
- style
disabled-checks:
- importShadow
- unnamedResult
goimports:
local-prefixes: github.com/spf13/viper
linters: linters:
disable-all: true enable:
enable: - bodyclose
- bodyclose - dogsled
- dogsled - dupl
- dupl - durationcheck
- durationcheck - exhaustive
- exhaustive - gocritic
- gci - godot
- gocritic - gomoddirectives
- godot - goprintffuncname
- gofmt - govet
- gofumpt - importas
- goimports - ineffassign
- gomoddirectives - makezero
- goprintffuncname - misspell
- govet - nakedret
- importas - nilerr
- ineffassign - noctx
- makezero - nolintlint
- misspell - prealloc
- nakedret - predeclared
- nilerr - revive
- rowserrcheck
- sqlclosecheck
- staticcheck
- tparallel
- unconvert
- unparam
- unused
- wastedassign
- whitespace
# fixme
# - cyclop
# - errcheck
# - errorlint
# - exhaustivestruct
# - forbidigo
# - forcetypeassert
# - gochecknoglobals
# - gochecknoinits
# - gocognit
# - goconst
# - gocyclo
# - gosec
# - gosimple
# - ifshort
# - lll
# - nlreturn
# - paralleltest
# - scopelint
# - thelper
# - wrapcheck
# unused
# - depguard
# - goheader
# - gomodguard
# don't enable:
# - asciicheck
# - funlen
# - godox
# - goerr113
# - gomnd
# - interfacer
# - maligned
# - nestif
# - testpackage
# - wsl
exclusions:
rules:
- linters:
- errcheck
- noctx - noctx
- nolintlint path: _test.go
- prealloc presets:
- predeclared - comments
- revive - std-error-handling
- rowserrcheck
- sqlclosecheck
- staticcheck
- stylecheck
- tparallel
- typecheck
- unconvert
- unparam
- unused
- wastedassign
- whitespace
# fixme settings:
# - cyclop misspell:
# - errcheck locale: US
# - errorlint nolintlint:
# - exhaustivestruct allow-unused: false # report any unused nolint directives
# - forbidigo require-specific: false # don't require nolint directives to be specific about which linter is being skipped
# - forcetypeassert gocritic:
# - gochecknoglobals # Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
# - gochecknoinits enabled-tags:
# - gocognit - diagnostic
# - goconst - experimental
# - gocyclo - opinionated
# - gosec - style
# - gosimple disabled-checks:
# - ifshort - importShadow
# - lll - unnamedResult
# - nlreturn
# - paralleltest
# - scopelint
# - thelper
# - wrapcheck
# unused formatters:
# - depguard enable:
# - goheader - gci
# - gomodguard - gofmt
- gofumpt
- goimports
# - golines
# deprecated settings:
# - deadcode gci:
# - structcheck sections:
# - varcheck - standard
- default
# don't enable: - localmodule
# - asciicheck
# - funlen
# - godox
# - goerr113
# - gomnd
# - interfacer
# - maligned
# - nestif
# - testpackage
# - wsl

View File

@@ -12,7 +12,7 @@
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.21-61CFDD.svg?style=flat-square) ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.23-61CFDD.svg?style=flat-square)
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper) [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)
**Go configuration with fangs!** **Go configuration with fangs!**
@@ -821,7 +821,7 @@ You can use your favorite format's marshaller with the config returned by `AllSe
```go ```go
import ( import (
yaml "gopkg.in/yaml.v2" yaml "go.yaml.in/yaml/v3"
// ... // ...
) )

View File

@@ -83,6 +83,27 @@ v := viper.NewWithOptions(
) )
``` ```
### BREAKING: "github.com/mitchellh/mapstructure" depedency replaced
The original [mapstructure](https://github.com/mitchellh/mapstructure) has been [archived](https://github.com/mitchellh/mapstructure/issues/349) and was replaced with a [fork](https://github.com/go-viper/mapstructure) maintained by Viper ([#1723](https://github.com/spf13/viper/pull/1723)).
As a result, the package import path needs to be changed in cases where `mapstructure` is directly referenced in your code.
For example, when providing a custom decoder config:
```go
err := viper.Unmarshal(&appConfig, func(config *mapstructure.DecoderConfig) {
config.TagName = "yaml"
})
```
The change is fairly straightforward, just replace all occurrences of the import path `github.com/mitchellh/mapstructure` with `github.com/go-viper/mapstructure/v2`:
```diff
- import "github.com/mitchellh/mapstructure"
+ import "github.com/go-viper/mapstructure/v2"
```
### BREAKING: HCL, Java properties, INI removed from core ### BREAKING: HCL, Java properties, INI removed from core
In order to reduce third-party dependencies, Viper dropped support for the following formats from the core: In order to reduce third-party dependencies, Viper dropped support for the following formats from the core:

View File

@@ -2,30 +2,32 @@
"nodes": { "nodes": {
"cachix": { "cachix": {
"inputs": { "inputs": {
"devenv": "devenv_2", "devenv": [
"devenv"
],
"flake-compat": [ "flake-compat": [
"devenv"
],
"git-hooks": [
"devenv", "devenv",
"flake-compat" "git-hooks"
], ],
"nixpkgs": [ "nixpkgs": [
"devenv", "devenv",
"nixpkgs" "nixpkgs"
],
"pre-commit-hooks": [
"devenv",
"pre-commit-hooks"
] ]
}, },
"locked": { "locked": {
"lastModified": 1712055811, "lastModified": 1748883665,
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", "narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
"owner": "cachix", "owner": "cachix",
"repo": "cachix", "repo": "cachix",
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", "rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "cachix", "owner": "cachix",
"ref": "latest",
"repo": "cachix", "repo": "cachix",
"type": "github" "type": "github"
} }
@@ -33,52 +35,21 @@
"devenv": { "devenv": {
"inputs": { "inputs": {
"cachix": "cachix", "cachix": "cachix",
"flake-compat": "flake-compat_2", "flake-compat": "flake-compat",
"nix": "nix_2", "git-hooks": "git-hooks",
"nixpkgs": "nixpkgs_2",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1724763216,
"narHash": "sha256-oW2bwCrJpIzibCNK6zfIDaIQw765yMAuMSG2gyZfGv0=",
"owner": "cachix",
"repo": "devenv",
"rev": "1e4ef61205b9aa20fe04bf1c468b6a316281c4f1",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"devenv_2": {
"inputs": {
"flake-compat": [
"devenv",
"cachix",
"flake-compat"
],
"nix": "nix", "nix": "nix",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs"
"poetry2nix": "poetry2nix",
"pre-commit-hooks": [
"devenv",
"cachix",
"pre-commit-hooks"
]
}, },
"locked": { "locked": {
"lastModified": 1708704632, "lastModified": 1755257397,
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", "narHash": "sha256-VU+OHexL2y6y7yrpEc6bZvYYwoQg6aZK1b4YxT0yZCk=",
"owner": "cachix", "owner": "cachix",
"repo": "devenv", "repo": "devenv",
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", "rev": "6f9c3d4722aa253631644329f7bda60b1d3d1b97",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "cachix", "owner": "cachix",
"ref": "python-rewrite",
"repo": "devenv", "repo": "devenv",
"type": "github" "type": "github"
} }
@@ -86,27 +57,11 @@
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1673956053, "lastModified": 1747046372,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -116,15 +71,37 @@
} }
}, },
"flake-parts": { "flake-parts": {
"inputs": {
"nixpkgs-lib": [
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": { "inputs": {
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1722555600, "lastModified": 1754487366,
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d", "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -133,39 +110,29 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils": { "git-hooks": {
"inputs": { "inputs": {
"systems": "systems" "flake-compat": [
"devenv",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1689068808, "lastModified": 1750779888,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
"owner": "numtide", "owner": "cachix",
"repo": "flake-utils", "repo": "git-hooks.nix",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "numtide", "owner": "cachix",
"repo": "flake-utils", "repo": "git-hooks.nix",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github" "type": "github"
} }
}, },
@@ -173,7 +140,7 @@
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"devenv", "devenv",
"pre-commit-hooks", "git-hooks",
"nixpkgs" "nixpkgs"
] ]
}, },
@@ -192,165 +159,49 @@
} }
}, },
"nix": { "nix": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1712911606,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
"owner": "domenkozar",
"repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.21",
"repo": "nix",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix_2": {
"inputs": { "inputs": {
"flake-compat": [ "flake-compat": [
"devenv", "devenv",
"flake-compat" "flake-compat"
], ],
"flake-parts": "flake-parts",
"git-hooks-nix": [
"devenv",
"git-hooks"
],
"nixpkgs": [ "nixpkgs": [
"devenv", "devenv",
"nixpkgs" "nixpkgs"
], ],
"nixpkgs-regression": "nixpkgs-regression_2" "nixpkgs-23-11": [
"devenv"
],
"nixpkgs-regression": [
"devenv"
]
}, },
"locked": { "locked": {
"lastModified": 1712911606, "lastModified": 1755029779,
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", "narHash": "sha256-3+GHIYGg4U9XKUN4rg473frIVNn8YD06bjwxKS1IPrU=",
"owner": "domenkozar", "owner": "cachix",
"repo": "nix", "repo": "nix",
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", "rev": "b0972b0eee6726081d10b1199f54de6d2917f861",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "domenkozar", "owner": "cachix",
"ref": "devenv-2.21", "ref": "devenv-2.30",
"repo": "nix", "repo": "nix",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1692808169, "lastModified": 1750441195,
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", "narHash": "sha256-yke+pm+MdgRb6c0dPt8MgDhv7fcBbdjmv1ZceNTyzKg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1722555339,
"narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-regression_2": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1710695816,
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1713361204,
"narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
"owner": "cachix", "owner": "cachix",
"repo": "devenv-nixpkgs", "repo": "devenv-nixpkgs",
"rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6", "rev": "0ceffe312871b443929ff3006960d29b120dc627",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -360,13 +211,28 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_3": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1724748588, "lastModified": 1753579242,
"narHash": "sha256-NlpGA4+AIf1dKNq76ps90rxowlFXUsV9x7vK/mN37JM=", "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1755268003,
"narHash": "sha256-nNaeJjo861wFR0tjHDyCnHs1rbRtrMgxAKMoig9Sj/w=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a6292e34000dc93d43bccf78338770c1c5ec8a99", "rev": "32f313e49e42f715491e1ea7b306a87c16fe0388",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -376,94 +242,11 @@
"type": "github" "type": "github"
} }
}, },
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"devenv",
"cachix",
"devenv",
"nixpkgs"
]
},
"locked": {
"lastModified": 1692876271,
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "poetry2nix",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
"flake-utils": "flake-utils_2",
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1713775815,
"narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"devenv": "devenv", "devenv": "devenv",
"flake-parts": "flake-parts", "flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs_2"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
} }
} }
}, },

View File

@@ -7,51 +7,55 @@
devenv.url = "github:cachix/devenv"; devenv.url = "github:cachix/devenv";
}; };
outputs = inputs@{ flake-parts, ... }: outputs =
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } { flake-parts.lib.mkFlake { inherit inputs; } {
imports = [ imports = [
inputs.devenv.flakeModule inputs.devenv.flakeModule
]; ];
systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ]; systems = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
perSystem = { config, self', inputs', pkgs, system, ... }: rec { perSystem =
devenv.shells = { { pkgs, ... }:
default = { {
languages = { devenv.shells = {
go.enable = true; default = {
go.package = pkgs.go_1_23; languages = {
}; go.enable = true;
};
pre-commit.hooks = { git-hooks.hooks = {
nixpkgs-fmt.enable = true; nixpkgs-fmt.enable = true;
yamllint.enable = true; yamllint.enable = true;
}; };
packages = with pkgs; [ packages = with pkgs; [
gnumake gnumake
golangci-lint golangci-lint
yamllint yamllint
]; ];
scripts = { scripts = {
versions.exec = '' versions.exec = ''
go version go version
golangci-lint version golangci-lint version
'';
};
enterShell = ''
versions
''; '';
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
containers = pkgs.lib.mkForce { };
}; };
enterShell = ''
versions
'';
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
containers = pkgs.lib.mkForce { };
}; };
ci = devenv.shells.default;
}; };
};
}; };
} }

View File

@@ -1,6 +1,6 @@
package yaml package yaml
import "gopkg.in/yaml.v3" import "go.yaml.in/yaml/v3"
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding. // Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
type Codec struct{} type Codec struct{}

View File

@@ -219,7 +219,10 @@ func (v *Viper) watchKeyValueConfigOnChannel() error {
for { for {
b := <-rc b := <-rc
reader := bytes.NewReader(b.Value) reader := bytes.NewReader(b.Value)
v.unmarshalReader(reader, v.kvstore) err := v.unmarshalReader(reader, v.kvstore)
if err != nil {
v.logger.Error(fmt.Errorf("failed to unmarshal remote config: %w", err).Error())
}
} }
}(respc) }(respc)
return nil return nil

View File

@@ -174,10 +174,7 @@ func parseSizeInBytes(sizeStr string) uint {
} }
} }
size := cast.ToInt(sizeStr) size := max(cast.ToInt(sizeStr), 0)
if size < 0 {
size = 0
}
return safeMul(uint(size), multiplier) return safeMul(uint(size), multiplier)
} }

View File

@@ -376,7 +376,12 @@ func (v *Viper) WatchConfig() {
} }
} }
}() }()
watcher.Add(configDir) err = watcher.Add(configDir)
if err != nil {
v.logger.Error(fmt.Sprintf("failed to add watcher: %s", err))
initWG.Done()
return
}
initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on... initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on...
eventsWG.Wait() // now, wait for event loop to end in this go-routine... eventsWG.Wait() // now, wait for event loop to end in this go-routine...
}() }()
@@ -1181,11 +1186,26 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) any {
s = strings.TrimSuffix(s, "]") s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s) res, _ := readAsCSV(s)
return res return res
case "boolSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToBoolSlice(res)
case "intSlice": case "intSlice":
s := strings.TrimPrefix(flag.ValueString(), "[") s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]") s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s) res, _ := readAsCSV(s)
return cast.ToIntSlice(res) return cast.ToIntSlice(res)
case "uintSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToUintSlice(res)
case "float64Slice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToFloat64Slice(res)
case "durationSlice": case "durationSlice":
s := strings.TrimPrefix(flag.ValueString(), "[") s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]") s = strings.TrimSuffix(s, "]")
@@ -1268,11 +1288,26 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) any {
s = strings.TrimSuffix(s, "]") s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s) res, _ := readAsCSV(s)
return res return res
case "boolSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToBoolSlice(res)
case "intSlice": case "intSlice":
s := strings.TrimPrefix(flag.ValueString(), "[") s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]") s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s) res, _ := readAsCSV(s)
return cast.ToIntSlice(res) return cast.ToIntSlice(res)
case "uintSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToUintSlice(res)
case "float64Slice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToFloat64Slice(res)
case "stringToString": case "stringToString":
return stringToStringConv(flag.ValueString()) return stringToStringConv(flag.ValueString())
case "stringToInt": case "stringToInt":
@@ -1670,7 +1705,10 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(in) _, err := buf.ReadFrom(in)
if err != nil {
return fmt.Errorf("failed to read configuration from input: %w", err)
}
// TODO: remove this once SupportedExts is deprecated/removed // TODO: remove this once SupportedExts is deprecated/removed
if !slices.Contains(SupportedExts, format) { if !slices.Contains(SupportedExts, format) {

View File

@@ -1,15 +0,0 @@
coverage:
range: 80..100
round: down
precision: 2
status:
project: # measuring the overall project coverage
default: # context, you can create multiple ones with custom titles
enabled: yes # must be yes|true to enable this status
target: 100 # specify the target coverage for each commit status
# option: "auto" (must increase from parent commit or pull request base)
# option: "X%" a static target percentage to hit
if_not_found: success # if parent is not found report status as success, error, or failure
if_ci_failed: error # if ci fails report status as success, error, or failure

View File

@@ -1,4 +0,0 @@
/vendor
cover.html
cover.out
/bin

View File

@@ -1,95 +0,0 @@
Releases
========
v1.11.0 (2023-03-28)
====================
- `Errors` now supports any error that implements multiple-error
interface.
- Add `Every` function to allow checking if all errors in the chain
satisfies `errors.Is` against the target error.
v1.10.0 (2023-03-08)
====================
- Comply with Go 1.20's multiple-error interface.
- Drop Go 1.18 support.
Per the support policy, only Go 1.19 and 1.20 are supported now.
- Drop all non-test external dependencies.
v1.9.0 (2022-12-12)
===================
- Add `AppendFunc` that allow passsing functions to similar to
`AppendInvoke`.
- Bump up yaml.v3 dependency to 3.0.1.
v1.8.0 (2022-02-28)
===================
- `Combine`: perform zero allocations when there are no errors.
v1.7.0 (2021-05-06)
===================
- Add `AppendInvoke` to append into errors from `defer` blocks.
v1.6.0 (2020-09-14)
===================
- Actually drop library dependency on development-time tooling.
v1.5.0 (2020-02-24)
===================
- Drop library dependency on development-time tooling.
v1.4.0 (2019-11-04)
===================
- Add `AppendInto` function to more ergonomically build errors inside a
loop.
v1.3.0 (2019-10-29)
===================
- Switch to Go modules.
v1.2.0 (2019-09-26)
===================
- Support extracting and matching against wrapped errors with `errors.As`
and `errors.Is`.
v1.1.0 (2017-06-30)
===================
- Added an `Errors(error) []error` function to extract the underlying list of
errors for a multierr error.
v1.0.0 (2017-05-31)
===================
No changes since v0.2.0. This release is committing to making no breaking
changes to the current API in the 1.X series.
v0.2.0 (2017-04-11)
===================
- Repeatedly appending to the same error is now faster due to fewer
allocations.
v0.1.0 (2017-31-03)
===================
- Initial release

View File

@@ -1,19 +0,0 @@
Copyright (c) 2017-2021 Uber Technologies, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

38
vendor/go.uber.org/multierr/Makefile generated vendored
View File

@@ -1,38 +0,0 @@
# Directory to put `go install`ed binaries in.
export GOBIN ?= $(shell pwd)/bin
GO_FILES := $(shell \
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
-o -name '*.go' -print | cut -b3-)
.PHONY: build
build:
go build ./...
.PHONY: test
test:
go test -race ./...
.PHONY: gofmt
gofmt:
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
@gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false)
.PHONY: golint
golint:
@cd tools && go install golang.org/x/lint/golint
@$(GOBIN)/golint ./...
.PHONY: staticcheck
staticcheck:
@cd tools && go install honnef.co/go/tools/cmd/staticcheck
@$(GOBIN)/staticcheck ./...
.PHONY: lint
lint: gofmt golint staticcheck
.PHONY: cover
cover:
go test -race -coverprofile=cover.out -coverpkg=./... -v ./...
go tool cover -html=cover.out -o cover.html

View File

@@ -1,43 +0,0 @@
# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
`multierr` allows combining one or more Go `error`s together.
## Features
- **Idiomatic**:
multierr follows best practices in Go, and keeps your code idiomatic.
- It keeps the underlying error type hidden,
allowing you to deal in `error` values exclusively.
- It provides APIs to safely append into an error from a `defer` statement.
- **Performant**:
multierr is optimized for performance:
- It avoids allocations where possible.
- It utilizes slice resizing semantics to optimize common cases
like appending into the same error object from a loop.
- **Interoperable**:
multierr interoperates with the Go standard library's error APIs seamlessly:
- The `errors.Is` and `errors.As` functions *just work*.
- **Lightweight**:
multierr comes with virtually no dependencies.
## Installation
```bash
go get -u go.uber.org/multierr@latest
```
## Status
Stable: No breaking changes will be made before 2.0.
-------------------------------------------------------------------------------
Released under the [MIT License].
[MIT License]: LICENSE.txt
[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr
[doc]: https://pkg.go.dev/go.uber.org/multierr
[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml
[cov]: https://codecov.io/gh/uber-go/multierr

646
vendor/go.uber.org/multierr/error.go generated vendored
View File

@@ -1,646 +0,0 @@
// Copyright (c) 2017-2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Package multierr allows combining one or more errors together.
//
// # Overview
//
// Errors can be combined with the use of the Combine function.
//
// multierr.Combine(
// reader.Close(),
// writer.Close(),
// conn.Close(),
// )
//
// If only two errors are being combined, the Append function may be used
// instead.
//
// err = multierr.Append(reader.Close(), writer.Close())
//
// The underlying list of errors for a returned error object may be retrieved
// with the Errors function.
//
// errors := multierr.Errors(err)
// if len(errors) > 0 {
// fmt.Println("The following errors occurred:", errors)
// }
//
// # Appending from a loop
//
// You sometimes need to append into an error from a loop.
//
// var err error
// for _, item := range items {
// err = multierr.Append(err, process(item))
// }
//
// Cases like this may require knowledge of whether an individual instance
// failed. This usually requires introduction of a new variable.
//
// var err error
// for _, item := range items {
// if perr := process(item); perr != nil {
// log.Warn("skipping item", item)
// err = multierr.Append(err, perr)
// }
// }
//
// multierr includes AppendInto to simplify cases like this.
//
// var err error
// for _, item := range items {
// if multierr.AppendInto(&err, process(item)) {
// log.Warn("skipping item", item)
// }
// }
//
// This will append the error into the err variable, and return true if that
// individual error was non-nil.
//
// See [AppendInto] for more information.
//
// # Deferred Functions
//
// Go makes it possible to modify the return value of a function in a defer
// block if the function was using named returns. This makes it possible to
// record resource cleanup failures from deferred blocks.
//
// func sendRequest(req Request) (err error) {
// conn, err := openConnection()
// if err != nil {
// return err
// }
// defer func() {
// err = multierr.Append(err, conn.Close())
// }()
// // ...
// }
//
// multierr provides the Invoker type and AppendInvoke function to make cases
// like the above simpler and obviate the need for a closure. The following is
// roughly equivalent to the example above.
//
// func sendRequest(req Request) (err error) {
// conn, err := openConnection()
// if err != nil {
// return err
// }
// defer multierr.AppendInvoke(&err, multierr.Close(conn))
// // ...
// }
//
// See [AppendInvoke] and [Invoker] for more information.
//
// NOTE: If you're modifying an error from inside a defer, you MUST use a named
// return value for that function.
//
// # Advanced Usage
//
// Errors returned by Combine and Append MAY implement the following
// interface.
//
// type errorGroup interface {
// // Returns a slice containing the underlying list of errors.
// //
// // This slice MUST NOT be modified by the caller.
// Errors() []error
// }
//
// Note that if you need access to list of errors behind a multierr error, you
// should prefer using the Errors function. That said, if you need cheap
// read-only access to the underlying errors slice, you can attempt to cast
// the error to this interface. You MUST handle the failure case gracefully
// because errors returned by Combine and Append are not guaranteed to
// implement this interface.
//
// var errors []error
// group, ok := err.(errorGroup)
// if ok {
// errors = group.Errors()
// } else {
// errors = []error{err}
// }
package multierr // import "go.uber.org/multierr"
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
"sync"
"sync/atomic"
)
var (
// Separator for single-line error messages.
_singlelineSeparator = []byte("; ")
// Prefix for multi-line messages
_multilinePrefix = []byte("the following errors occurred:")
// Prefix for the first and following lines of an item in a list of
// multi-line error messages.
//
// For example, if a single item is:
//
// foo
// bar
//
// It will become,
//
// - foo
// bar
_multilineSeparator = []byte("\n - ")
_multilineIndent = []byte(" ")
)
// _bufferPool is a pool of bytes.Buffers.
var _bufferPool = sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
}
type errorGroup interface {
Errors() []error
}
// Errors returns a slice containing zero or more errors that the supplied
// error is composed of. If the error is nil, a nil slice is returned.
//
// err := multierr.Append(r.Close(), w.Close())
// errors := multierr.Errors(err)
//
// If the error is not composed of other errors, the returned slice contains
// just the error that was passed in.
//
// Callers of this function are free to modify the returned slice.
func Errors(err error) []error {
return extractErrors(err)
}
// multiError is an error that holds one or more errors.
//
// An instance of this is guaranteed to be non-empty and flattened. That is,
// none of the errors inside multiError are other multiErrors.
//
// multiError formats to a semi-colon delimited list of error messages with
// %v and with a more readable multi-line format with %+v.
type multiError struct {
copyNeeded atomic.Bool
errors []error
}
// Errors returns the list of underlying errors.
//
// This slice MUST NOT be modified.
func (merr *multiError) Errors() []error {
if merr == nil {
return nil
}
return merr.errors
}
func (merr *multiError) Error() string {
if merr == nil {
return ""
}
buff := _bufferPool.Get().(*bytes.Buffer)
buff.Reset()
merr.writeSingleline(buff)
result := buff.String()
_bufferPool.Put(buff)
return result
}
// Every compares every error in the given err against the given target error
// using [errors.Is], and returns true only if every comparison returned true.
func Every(err error, target error) bool {
for _, e := range extractErrors(err) {
if !errors.Is(e, target) {
return false
}
}
return true
}
func (merr *multiError) Format(f fmt.State, c rune) {
if c == 'v' && f.Flag('+') {
merr.writeMultiline(f)
} else {
merr.writeSingleline(f)
}
}
func (merr *multiError) writeSingleline(w io.Writer) {
first := true
for _, item := range merr.errors {
if first {
first = false
} else {
w.Write(_singlelineSeparator)
}
io.WriteString(w, item.Error())
}
}
func (merr *multiError) writeMultiline(w io.Writer) {
w.Write(_multilinePrefix)
for _, item := range merr.errors {
w.Write(_multilineSeparator)
writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
}
}
// Writes s to the writer with the given prefix added before each line after
// the first.
func writePrefixLine(w io.Writer, prefix []byte, s string) {
first := true
for len(s) > 0 {
if first {
first = false
} else {
w.Write(prefix)
}
idx := strings.IndexByte(s, '\n')
if idx < 0 {
idx = len(s) - 1
}
io.WriteString(w, s[:idx+1])
s = s[idx+1:]
}
}
type inspectResult struct {
// Number of top-level non-nil errors
Count int
// Total number of errors including multiErrors
Capacity int
// Index of the first non-nil error in the list. Value is meaningless if
// Count is zero.
FirstErrorIdx int
// Whether the list contains at least one multiError
ContainsMultiError bool
}
// Inspects the given slice of errors so that we can efficiently allocate
// space for it.
func inspect(errors []error) (res inspectResult) {
first := true
for i, err := range errors {
if err == nil {
continue
}
res.Count++
if first {
first = false
res.FirstErrorIdx = i
}
if merr, ok := err.(*multiError); ok {
res.Capacity += len(merr.errors)
res.ContainsMultiError = true
} else {
res.Capacity++
}
}
return
}
// fromSlice converts the given list of errors into a single error.
func fromSlice(errors []error) error {
// Don't pay to inspect small slices.
switch len(errors) {
case 0:
return nil
case 1:
return errors[0]
}
res := inspect(errors)
switch res.Count {
case 0:
return nil
case 1:
// only one non-nil entry
return errors[res.FirstErrorIdx]
case len(errors):
if !res.ContainsMultiError {
// Error list is flat. Make a copy of it
// Otherwise "errors" escapes to the heap
// unconditionally for all other cases.
// This lets us optimize for the "no errors" case.
out := append(([]error)(nil), errors...)
return &multiError{errors: out}
}
}
nonNilErrs := make([]error, 0, res.Capacity)
for _, err := range errors[res.FirstErrorIdx:] {
if err == nil {
continue
}
if nested, ok := err.(*multiError); ok {
nonNilErrs = append(nonNilErrs, nested.errors...)
} else {
nonNilErrs = append(nonNilErrs, err)
}
}
return &multiError{errors: nonNilErrs}
}
// Combine combines the passed errors into a single error.
//
// If zero arguments were passed or if all items are nil, a nil error is
// returned.
//
// Combine(nil, nil) // == nil
//
// If only a single error was passed, it is returned as-is.
//
// Combine(err) // == err
//
// Combine skips over nil arguments so this function may be used to combine
// together errors from operations that fail independently of each other.
//
// multierr.Combine(
// reader.Close(),
// writer.Close(),
// pipe.Close(),
// )
//
// If any of the passed errors is a multierr error, it will be flattened along
// with the other errors.
//
// multierr.Combine(multierr.Combine(err1, err2), err3)
// // is the same as
// multierr.Combine(err1, err2, err3)
//
// The returned error formats into a readable multi-line error message if
// formatted with %+v.
//
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
func Combine(errors ...error) error {
return fromSlice(errors)
}
// Append appends the given errors together. Either value may be nil.
//
// This function is a specialization of Combine for the common case where
// there are only two errors.
//
// err = multierr.Append(reader.Close(), writer.Close())
//
// The following pattern may also be used to record failure of deferred
// operations without losing information about the original error.
//
// func doSomething(..) (err error) {
// f := acquireResource()
// defer func() {
// err = multierr.Append(err, f.Close())
// }()
//
// Note that the variable MUST be a named return to append an error to it from
// the defer statement. See also [AppendInvoke].
func Append(left error, right error) error {
switch {
case left == nil:
return right
case right == nil:
return left
}
if _, ok := right.(*multiError); !ok {
if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
// Common case where the error on the left is constantly being
// appended to.
errs := append(l.errors, right)
return &multiError{errors: errs}
} else if !ok {
// Both errors are single errors.
return &multiError{errors: []error{left, right}}
}
}
// Either right or both, left and right, are multiErrors. Rely on usual
// expensive logic.
errors := [2]error{left, right}
return fromSlice(errors[0:])
}
// AppendInto appends an error into the destination of an error pointer and
// returns whether the error being appended was non-nil.
//
// var err error
// multierr.AppendInto(&err, r.Close())
// multierr.AppendInto(&err, w.Close())
//
// The above is equivalent to,
//
// err := multierr.Append(r.Close(), w.Close())
//
// As AppendInto reports whether the provided error was non-nil, it may be
// used to build a multierr error in a loop more ergonomically. For example:
//
// var err error
// for line := range lines {
// var item Item
// if multierr.AppendInto(&err, parse(line, &item)) {
// continue
// }
// items = append(items, item)
// }
//
// Compare this with a version that relies solely on Append:
//
// var err error
// for line := range lines {
// var item Item
// if parseErr := parse(line, &item); parseErr != nil {
// err = multierr.Append(err, parseErr)
// continue
// }
// items = append(items, item)
// }
func AppendInto(into *error, err error) (errored bool) {
if into == nil {
// We panic if 'into' is nil. This is not documented above
// because suggesting that the pointer must be non-nil may
// confuse users into thinking that the error that it points
// to must be non-nil.
panic("misuse of multierr.AppendInto: into pointer must not be nil")
}
if err == nil {
return false
}
*into = Append(*into, err)
return true
}
// Invoker is an operation that may fail with an error. Use it with
// AppendInvoke to append the result of calling the function into an error.
// This allows you to conveniently defer capture of failing operations.
//
// See also, [Close] and [Invoke].
type Invoker interface {
Invoke() error
}
// Invoke wraps a function which may fail with an error to match the Invoker
// interface. Use it to supply functions matching this signature to
// AppendInvoke.
//
// For example,
//
// func processReader(r io.Reader) (err error) {
// scanner := bufio.NewScanner(r)
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
// for scanner.Scan() {
// // ...
// }
// // ...
// }
//
// In this example, the following line will construct the Invoker right away,
// but defer the invocation of scanner.Err() until the function returns.
//
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
//
// Note that the error you're appending to from the defer statement MUST be a
// named return.
type Invoke func() error
// Invoke calls the supplied function and returns its result.
func (i Invoke) Invoke() error { return i() }
// Close builds an Invoker that closes the provided io.Closer. Use it with
// AppendInvoke to close io.Closers and append their results into an error.
//
// For example,
//
// func processFile(path string) (err error) {
// f, err := os.Open(path)
// if err != nil {
// return err
// }
// defer multierr.AppendInvoke(&err, multierr.Close(f))
// return processReader(f)
// }
//
// In this example, multierr.Close will construct the Invoker right away, but
// defer the invocation of f.Close until the function returns.
//
// defer multierr.AppendInvoke(&err, multierr.Close(f))
//
// Note that the error you're appending to from the defer statement MUST be a
// named return.
func Close(closer io.Closer) Invoker {
return Invoke(closer.Close)
}
// AppendInvoke appends the result of calling the given Invoker into the
// provided error pointer. Use it with named returns to safely defer
// invocation of fallible operations until a function returns, and capture the
// resulting errors.
//
// func doSomething(...) (err error) {
// // ...
// f, err := openFile(..)
// if err != nil {
// return err
// }
//
// // multierr will call f.Close() when this function returns and
// // if the operation fails, its append its error into the
// // returned error.
// defer multierr.AppendInvoke(&err, multierr.Close(f))
//
// scanner := bufio.NewScanner(f)
// // Similarly, this scheduled scanner.Err to be called and
// // inspected when the function returns and append its error
// // into the returned error.
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
//
// // ...
// }
//
// NOTE: If used with a defer, the error variable MUST be a named return.
//
// Without defer, AppendInvoke behaves exactly like AppendInto.
//
// err := // ...
// multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
//
// // ...is roughly equivalent to...
//
// err := // ...
// multierr.AppendInto(&err, foo())
//
// The advantage of the indirection introduced by Invoker is to make it easy
// to defer the invocation of a function. Without this indirection, the
// invoked function will be evaluated at the time of the defer block rather
// than when the function returns.
//
// // BAD: This is likely not what the caller intended. This will evaluate
// // foo() right away and append its result into the error when the
// // function returns.
// defer multierr.AppendInto(&err, foo())
//
// // GOOD: This will defer invocation of foo unutil the function returns.
// defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
//
// multierr provides a few Invoker implementations out of the box for
// convenience. See [Invoker] for more information.
func AppendInvoke(into *error, invoker Invoker) {
AppendInto(into, invoker.Invoke())
}
// AppendFunc is a shorthand for [AppendInvoke].
// It allows using function or method value directly
// without having to wrap it into an [Invoker] interface.
//
// func doSomething(...) (err error) {
// w, err := startWorker(...)
// if err != nil {
// return err
// }
//
// // multierr will call w.Stop() when this function returns and
// // if the operation fails, it appends its error into the
// // returned error.
// defer multierr.AppendFunc(&err, w.Stop)
// }
func AppendFunc(into *error, fn func() error) {
AppendInvoke(into, Invoke(fn))
}

View File

@@ -1,48 +0,0 @@
// Copyright (c) 2017-2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//go:build go1.20
// +build go1.20
package multierr
// Unwrap returns a list of errors wrapped by this multierr.
func (merr *multiError) Unwrap() []error {
return merr.Errors()
}
type multipleErrors interface {
Unwrap() []error
}
func extractErrors(err error) []error {
if err == nil {
return nil
}
// check if the given err is an Unwrapable error that
// implements multipleErrors interface.
eg, ok := err.(multipleErrors)
if !ok {
return []error{err}
}
return append(([]error)(nil), eg.Unwrap()...)
}

View File

@@ -1,79 +0,0 @@
// Copyright (c) 2017-2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//go:build !go1.20
// +build !go1.20
package multierr
import "errors"
// Versions of Go before 1.20 did not support the Unwrap() []error method.
// This provides a similar behavior by implementing the Is(..) and As(..)
// methods.
// See the errors.Join proposal for details:
// https://github.com/golang/go/issues/53435
// As attempts to find the first error in the error list that matches the type
// of the value that target points to.
//
// This function allows errors.As to traverse the values stored on the
// multierr error.
func (merr *multiError) As(target interface{}) bool {
for _, err := range merr.Errors() {
if errors.As(err, target) {
return true
}
}
return false
}
// Is attempts to match the provided error against errors in the error list.
//
// This function allows errors.Is to traverse the values stored on the
// multierr error.
func (merr *multiError) Is(target error) bool {
for _, err := range merr.Errors() {
if errors.Is(err, target) {
return true
}
}
return false
}
func extractErrors(err error) []error {
if err == nil {
return nil
}
// Note that we're casting to multiError, not errorGroup. Our contract is
// that returned errors MAY implement errorGroup. Errors, however, only
// has special behavior for multierr-specific error objects.
//
// This behavior can be expanded in the future but I think it's prudent to
// start with as little as possible in terms of contract and possibility
// of misuse.
eg, ok := err.(*multiError)
if !ok {
return []error{err}
}
return append(([]error)(nil), eg.Errors()...)
}

171
vendor/go.yaml.in/yaml/v3/README.md generated vendored Normal file
View File

@@ -0,0 +1,171 @@
go.yaml.in/yaml
===============
YAML Support for the Go Language
## Introduction
The `yaml` package enables [Go](https://go.dev/) programs to comfortably encode
and decode [YAML](https://yaml.org/) values.
It was originally developed within [Canonical](https://www.canonical.com) as
part of the [juju](https://juju.ubuntu.com) project, and is based on a pure Go
port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) C library to
parse and generate YAML data quickly and reliably.
## Project Status
This project started as a fork of the extremely popular [go-yaml](
https://github.com/go-yaml/yaml/)
project, and is being maintained by the official [YAML organization](
https://github.com/yaml/).
The YAML team took over ongoing maintenance and development of the project after
discussion with go-yaml's author, @niemeyer, following his decision to
[label the project repository as "unmaintained"](
https://github.com/go-yaml/yaml/blob/944c86a7d2/README.md) in April 2025.
We have put together a team of dedicated maintainers including representatives
of go-yaml's most important downstream projects.
We will strive to earn the trust of the various go-yaml forks to switch back to
this repository as their upstream.
Please [contact us](https://cloud-native.slack.com/archives/C08PPAT8PS7) if you
would like to contribute or be involved.
## Compatibility
The `yaml` package supports most of YAML 1.2, but preserves some behavior from
1.1 for backwards compatibility.
Specifically, v3 of the `yaml` package:
* Supports YAML 1.1 bools (`yes`/`no`, `on`/`off`) as long as they are being
decoded into a typed bool value.
Otherwise they behave as a string.
Booleans in YAML 1.2 are `true`/`false` only.
* Supports octals encoded and decoded as `0777` per YAML 1.1, rather than
`0o777` as specified in YAML 1.2, because most parsers still use the old
format.
Octals in the `0o777` format are supported though, so new files work.
* Does not support base-60 floats.
These are gone from YAML 1.2, and were actually never supported by this
package as it's clearly a poor choice.
## Installation and Usage
The import path for the package is *go.yaml.in/yaml/v3*.
To install it, run:
```bash
go get go.yaml.in/yaml/v3
```
## API Documentation
See: <https://pkg.go.dev/go.yaml.in/yaml/v3>
## API Stability
The package API for yaml v3 will remain stable as described in [gopkg.in](
https://gopkg.in).
## Example
```go
package main
import (
"fmt"
"log"
"go.yaml.in/yaml/v3"
)
var data = `
a: Easy!
b:
c: 2
d: [3, 4]
`
// Note: struct fields must be public in order for unmarshal to
// correctly populate the data.
type T struct {
A string
B struct {
RenamedC int `yaml:"c"`
D []int `yaml:",flow"`
}
}
func main() {
t := T{}
err := yaml.Unmarshal([]byte(data), &t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t:\n%v\n\n", t)
d, err := yaml.Marshal(&t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t dump:\n%s\n\n", string(d))
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m:\n%v\n\n", m)
d, err = yaml.Marshal(&m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m dump:\n%s\n\n", string(d))
}
```
This example will generate the following output:
```
--- t:
{Easy! {2 [3 4]}}
--- t dump:
a: Easy!
b:
c: 2
d: [3, 4]
--- m:
map[a:Easy! b:map[c:2 d:[3 4]]]
--- m dump:
a: Easy!
b:
c: 2
d:
- 3
- 4
```
## License
The yaml package is licensed under the MIT and Apache License 2.0 licenses.
Please see the LICENSE file for details.

View File

@@ -1,17 +1,17 @@
// //
// Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov // Copyright (c) 2006-2010 Kirill Simonov
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in // this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to // the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do // of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions: // so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

View File

@@ -832,10 +832,10 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
if d.unmarshal(n.Content[i], k) { if d.unmarshal(n.Content[i], k) {
if mergedFields != nil { if mergedFields != nil {
ki := k.Interface() ki := k.Interface()
if mergedFields[ki] { if d.getPossiblyUnhashableKey(mergedFields, ki) {
continue continue
} }
mergedFields[ki] = true d.setPossiblyUnhashableKey(mergedFields, ki, true)
} }
kkind := k.Kind() kkind := k.Kind()
if kkind == reflect.Interface { if kkind == reflect.Interface {
@@ -956,6 +956,24 @@ func failWantMap() {
failf("map merge requires map or sequence of maps as the value") failf("map merge requires map or sequence of maps as the value")
} }
func (d *decoder) setPossiblyUnhashableKey(m map[interface{}]bool, key interface{}, value bool) {
defer func() {
if err := recover(); err != nil {
failf("%v", err)
}
}()
m[key] = value
}
func (d *decoder) getPossiblyUnhashableKey(m map[interface{}]bool, key interface{}) bool {
defer func() {
if err := recover(); err != nil {
failf("%v", err)
}
}()
return m[key]
}
func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
mergedFields := d.mergedFields mergedFields := d.mergedFields
if mergedFields == nil { if mergedFields == nil {
@@ -963,7 +981,7 @@ func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
for i := 0; i < len(parent.Content); i += 2 { for i := 0; i < len(parent.Content); i += 2 {
k := reflect.New(ifaceType).Elem() k := reflect.New(ifaceType).Elem()
if d.unmarshal(parent.Content[i], k) { if d.unmarshal(parent.Content[i], k) {
d.mergedFields[k.Interface()] = true d.setPossiblyUnhashableKey(d.mergedFields, k.Interface(), true)
} }
} }
} }

View File

@@ -162,10 +162,9 @@ func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool {
// Check if we need to accumulate more events before emitting. // Check if we need to accumulate more events before emitting.
// //
// We accumulate extra // We accumulate extra
// - 1 event for DOCUMENT-START // - 1 event for DOCUMENT-START
// - 2 events for SEQUENCE-START // - 2 events for SEQUENCE-START
// - 3 events for MAPPING-START // - 3 events for MAPPING-START
//
func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool {
if emitter.events_head == len(emitter.events) { if emitter.events_head == len(emitter.events) {
return true return true
@@ -226,7 +225,7 @@ func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_
} }
// Increase the indentation level. // Increase the indentation level.
func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { func yaml_emitter_increase_indent_compact(emitter *yaml_emitter_t, flow, indentless bool, compact_seq bool) bool {
emitter.indents = append(emitter.indents, emitter.indent) emitter.indents = append(emitter.indents, emitter.indent)
if emitter.indent < 0 { if emitter.indent < 0 {
if flow { if flow {
@@ -241,7 +240,14 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool
emitter.indent += 2 emitter.indent += 2
} else { } else {
// Everything else aligns to the chosen indentation. // Everything else aligns to the chosen indentation.
emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent) emitter.indent = emitter.best_indent * ((emitter.indent + emitter.best_indent) / emitter.best_indent)
if compact_seq {
// The value compact_seq passed in is almost always set to `false` when this function is called,
// except when we are dealing with sequence nodes. So this gets triggered to subtract 2 only when we
// are increasing the indent to account for sequence nodes, which will be correct because we need to
// subtract 2 to account for the - at the beginning of the sequence node.
emitter.indent = emitter.indent - 2
}
} }
} }
return true return true
@@ -478,6 +484,18 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END")
} }
// yaml_emitter_increase_indent preserves the original signature and delegates to
// yaml_emitter_increase_indent_compact without compact-sequence indentation
func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool {
return yaml_emitter_increase_indent_compact(emitter, flow, indentless, false)
}
// yaml_emitter_process_line_comment preserves the original signature and delegates to
// yaml_emitter_process_line_comment_linebreak passing false for linebreak
func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool {
return yaml_emitter_process_line_comment_linebreak(emitter, false)
}
// Expect the root node. // Expect the root node.
func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
@@ -728,7 +746,16 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e
// Expect a block item node. // Expect a block item node.
func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
if first { if first {
if !yaml_emitter_increase_indent(emitter, false, false) { // emitter.mapping context tells us if we are currently in a mapping context.
// emiiter.column tells us which column we are in in the yaml output. 0 is the first char of the column.
// emitter.indentation tells us if the last character was an indentation character.
// emitter.compact_sequence_indent tells us if '- ' is considered part of the indentation for sequence elements.
// So, `seq` means that we are in a mapping context, and we are either at the first char of the column or
// the last character was not an indentation character, and we consider '- ' part of the indentation
// for sequence elements.
seq := emitter.mapping_context && (emitter.column == 0 || !emitter.indention) &&
emitter.compact_sequence_indent
if !yaml_emitter_increase_indent_compact(emitter, false, false, seq) {
return false return false
} }
} }
@@ -1144,8 +1171,15 @@ func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool {
} }
// Write an line comment. // Write an line comment.
func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { func yaml_emitter_process_line_comment_linebreak(emitter *yaml_emitter_t, linebreak bool) bool {
if len(emitter.line_comment) == 0 { if len(emitter.line_comment) == 0 {
// The next 3 lines are needed to resolve an issue with leading newlines
// See https://github.com/go-yaml/yaml/issues/755
// When linebreak is set to true, put_break will be called and will add
// the needed newline.
if linebreak && !put_break(emitter) {
return false
}
return true return true
} }
if !emitter.whitespace { if !emitter.whitespace {
@@ -1894,7 +1928,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
if !yaml_emitter_write_block_scalar_hints(emitter, value) { if !yaml_emitter_write_block_scalar_hints(emitter, value) {
return false return false
} }
if !yaml_emitter_process_line_comment(emitter) { if !yaml_emitter_process_line_comment_linebreak(emitter, true) {
return false return false
} }
//emitter.indention = true //emitter.indention = true
@@ -1931,7 +1965,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
if !yaml_emitter_write_block_scalar_hints(emitter, value) { if !yaml_emitter_write_block_scalar_hints(emitter, value) {
return false return false
} }
if !yaml_emitter_process_line_comment(emitter) { if !yaml_emitter_process_line_comment_linebreak(emitter, true) {
return false return false
} }

View File

@@ -227,7 +227,8 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool
// Parse the production: // Parse the production:
// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
// ************ //
// ************
func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -249,9 +250,12 @@ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t)
// Parse the productions: // Parse the productions:
// implicit_document ::= block_node DOCUMENT-END* // implicit_document ::= block_node DOCUMENT-END*
// * //
// *
//
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
// ************************* //
// *************************
func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool {
token := peek_token(parser) token := peek_token(parser)
@@ -356,8 +360,8 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
// Parse the productions: // Parse the productions:
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
// ***********
// //
// ***********
func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -379,9 +383,10 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event
// Parse the productions: // Parse the productions:
// implicit_document ::= block_node DOCUMENT-END* // implicit_document ::= block_node DOCUMENT-END*
// *************
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
// //
// *************
//
// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -428,30 +433,41 @@ func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t)
// Parse the productions: // Parse the productions:
// block_node_or_indentless_sequence ::= // block_node_or_indentless_sequence ::=
// ALIAS //
// ***** // ALIAS
// | properties (block_content | indentless_block_sequence)? // *****
// ********** * // | properties (block_content | indentless_block_sequence)?
// | block_content | indentless_block_sequence // ********** *
// * // | block_content | indentless_block_sequence
// *
//
// block_node ::= ALIAS // block_node ::= ALIAS
// ***** //
// | properties block_content? // *****
// ********** * // | properties block_content?
// | block_content // ********** *
// * // | block_content
// *
//
// flow_node ::= ALIAS // flow_node ::= ALIAS
// ***** //
// | properties flow_content? // *****
// ********** * // | properties flow_content?
// | flow_content // ********** *
// * // | flow_content
// *
//
// properties ::= TAG ANCHOR? | ANCHOR TAG? // properties ::= TAG ANCHOR? | ANCHOR TAG?
// ************************* //
// *************************
//
// block_content ::= block_collection | flow_collection | SCALAR // block_content ::= block_collection | flow_collection | SCALAR
// ****** //
// ******
//
// flow_content ::= flow_collection | SCALAR // flow_content ::= flow_collection | SCALAR
// ****** //
// ******
func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool {
//defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)()
@@ -682,8 +698,8 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
// Parse the productions: // Parse the productions:
// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
// ******************** *********** * *********
// //
// ******************** *********** * *********
func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first { if first {
token := peek_token(parser) token := peek_token(parser)
@@ -740,7 +756,8 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
// Parse the productions: // Parse the productions:
// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // indentless_sequence ::= (BLOCK-ENTRY block_node?)+
// *********** * //
// *********** *
func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -805,14 +822,14 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
// Parse the productions: // Parse the productions:
// block_mapping ::= BLOCK-MAPPING_START // block_mapping ::= BLOCK-MAPPING_START
// *******************
// ((KEY block_node_or_indentless_sequence?)?
// *** *
// (VALUE block_node_or_indentless_sequence?)?)*
// //
// BLOCK-END // *******************
// ********* // ((KEY block_node_or_indentless_sequence?)?
// *** *
// (VALUE block_node_or_indentless_sequence?)?)*
// //
// BLOCK-END
// *********
func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first { if first {
token := peek_token(parser) token := peek_token(parser)
@@ -881,13 +898,11 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
// Parse the productions: // Parse the productions:
// block_mapping ::= BLOCK-MAPPING_START // block_mapping ::= BLOCK-MAPPING_START
// //
// ((KEY block_node_or_indentless_sequence?)? // ((KEY block_node_or_indentless_sequence?)?
//
// (VALUE block_node_or_indentless_sequence?)?)*
// ***** *
// BLOCK-END
//
// //
// (VALUE block_node_or_indentless_sequence?)?)*
// ***** *
// BLOCK-END
func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -915,16 +930,18 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev
// Parse the productions: // Parse the productions:
// flow_sequence ::= FLOW-SEQUENCE-START // flow_sequence ::= FLOW-SEQUENCE-START
// *******************
// (flow_sequence_entry FLOW-ENTRY)*
// * **********
// flow_sequence_entry?
// *
// FLOW-SEQUENCE-END
// *****************
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// *
// //
// *******************
// (flow_sequence_entry FLOW-ENTRY)*
// * **********
// flow_sequence_entry?
// *
// FLOW-SEQUENCE-END
// *****************
//
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
//
// *
func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first { if first {
token := peek_token(parser) token := peek_token(parser)
@@ -987,11 +1004,10 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev
return true return true
} }
//
// Parse the productions: // Parse the productions:
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// *** *
// //
// *** *
func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -1011,8 +1027,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, ev
// Parse the productions: // Parse the productions:
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// ***** *
// //
// ***** *
func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -1035,8 +1051,8 @@ func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t,
// Parse the productions: // Parse the productions:
// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// *
// //
// *
func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {
@@ -1053,16 +1069,17 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev
// Parse the productions: // Parse the productions:
// flow_mapping ::= FLOW-MAPPING-START // flow_mapping ::= FLOW-MAPPING-START
// ******************
// (flow_mapping_entry FLOW-ENTRY)*
// * **********
// flow_mapping_entry?
// ******************
// FLOW-MAPPING-END
// ****************
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// * *** *
// //
// ******************
// (flow_mapping_entry FLOW-ENTRY)*
// * **********
// flow_mapping_entry?
// ******************
// FLOW-MAPPING-END
// ****************
//
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// - *** *
func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
if first { if first {
token := peek_token(parser) token := peek_token(parser)
@@ -1128,8 +1145,7 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event
// Parse the productions: // Parse the productions:
// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
// * ***** * // - ***** *
//
func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool {
token := peek_token(parser) token := peek_token(parser)
if token == nil { if token == nil {

View File

@@ -1,17 +1,17 @@
// //
// Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov // Copyright (c) 2006-2010 Kirill Simonov
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in // this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to // the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do // of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions: // so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

View File

@@ -1614,11 +1614,11 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. // Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token.
// //
// Scope: // Scope:
// %YAML 1.1 # a comment \n
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// %TAG !yaml! tag:yaml.org,2002: \n
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// //
// %YAML 1.1 # a comment \n
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// %TAG !yaml! tag:yaml.org,2002: \n
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool {
// Eat '%'. // Eat '%'.
start_mark := parser.mark start_mark := parser.mark
@@ -1719,11 +1719,11 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool
// Scan the directive name. // Scan the directive name.
// //
// Scope: // Scope:
// %YAML 1.1 # a comment \n
// ^^^^
// %TAG !yaml! tag:yaml.org,2002: \n
// ^^^
// //
// %YAML 1.1 # a comment \n
// ^^^^
// %TAG !yaml! tag:yaml.org,2002: \n
// ^^^
func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool {
// Consume the directive name. // Consume the directive name.
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@@ -1758,8 +1758,9 @@ func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark
// Scan the value of VERSION-DIRECTIVE. // Scan the value of VERSION-DIRECTIVE.
// //
// Scope: // Scope:
// %YAML 1.1 # a comment \n //
// ^^^^^^ // %YAML 1.1 # a comment \n
// ^^^^^^
func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool {
// Eat whitespaces. // Eat whitespaces.
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@@ -1797,10 +1798,11 @@ const max_number_length = 2
// Scan the version number of VERSION-DIRECTIVE. // Scan the version number of VERSION-DIRECTIVE.
// //
// Scope: // Scope:
// %YAML 1.1 # a comment \n //
// ^ // %YAML 1.1 # a comment \n
// %YAML 1.1 # a comment \n // ^
// ^ // %YAML 1.1 # a comment \n
// ^
func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool {
// Repeat while the next character is digit. // Repeat while the next character is digit.
@@ -1834,9 +1836,9 @@ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark
// Scan the value of a TAG-DIRECTIVE token. // Scan the value of a TAG-DIRECTIVE token.
// //
// Scope: // Scope:
// %TAG !yaml! tag:yaml.org,2002: \n
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// //
// %TAG !yaml! tag:yaml.org,2002: \n
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool {
var handle_value, prefix_value []byte var handle_value, prefix_value []byte
@@ -2847,7 +2849,7 @@ func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t
continue continue
} }
if parser.buffer[parser.buffer_pos+peek] == '#' { if parser.buffer[parser.buffer_pos+peek] == '#' {
seen := parser.mark.index+peek seen := parser.mark.index + peek
for { for {
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false return false
@@ -2876,7 +2878,7 @@ func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t
parser.comments = append(parser.comments, yaml_comment_t{ parser.comments = append(parser.comments, yaml_comment_t{
token_mark: token_mark, token_mark: token_mark,
start_mark: start_mark, start_mark: start_mark,
line: text, line: text,
}) })
} }
return true return true
@@ -2910,7 +2912,7 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
// the foot is the line below it. // the foot is the line below it.
var foot_line = -1 var foot_line = -1
if scan_mark.line > 0 { if scan_mark.line > 0 {
foot_line = parser.mark.line-parser.newlines+1 foot_line = parser.mark.line - parser.newlines + 1
if parser.newlines == 0 && parser.mark.column > 1 { if parser.newlines == 0 && parser.mark.column > 1 {
foot_line++ foot_line++
} }
@@ -2996,7 +2998,7 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo
recent_empty = false recent_empty = false
// Consume until after the consumed comment line. // Consume until after the consumed comment line.
seen := parser.mark.index+peek seen := parser.mark.index + peek
for { for {
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false return false

View File

@@ -1,17 +1,17 @@
// //
// Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov // Copyright (c) 2006-2010 Kirill Simonov
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in // this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to // the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do // of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions: // so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

View File

@@ -17,8 +17,7 @@
// //
// Source code and other details for the project are available at GitHub: // Source code and other details for the project are available at GitHub:
// //
// https://github.com/go-yaml/yaml // https://github.com/yaml/go-yaml
//
package yaml package yaml
import ( import (
@@ -75,16 +74,15 @@ type Marshaler interface {
// //
// For example: // For example:
// //
// type T struct { // type T struct {
// F int `yaml:"a,omitempty"` // F int `yaml:"a,omitempty"`
// B int // B int
// } // }
// var t T // var t T
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
// //
// See the documentation of Marshal for the format of tags and a list of // See the documentation of Marshal for the format of tags and a list of
// supported tag options. // supported tag options.
//
func Unmarshal(in []byte, out interface{}) (err error) { func Unmarshal(in []byte, out interface{}) (err error) {
return unmarshal(in, out, false) return unmarshal(in, out, false)
} }
@@ -185,36 +183,35 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) {
// //
// The field tag format accepted is: // The field tag format accepted is:
// //
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
// //
// The following flags are currently supported: // The following flags are currently supported:
// //
// omitempty Only include the field if it's not set to the zero // omitempty Only include the field if it's not set to the zero
// value for the type or to empty slices or maps. // value for the type or to empty slices or maps.
// Zero valued structs will be omitted if all their public // Zero valued structs will be omitted if all their public
// fields are zero, unless they implement an IsZero // fields are zero, unless they implement an IsZero
// method (see the IsZeroer interface type), in which // method (see the IsZeroer interface type), in which
// case the field will be excluded if IsZero returns true. // case the field will be excluded if IsZero returns true.
// //
// flow Marshal using a flow style (useful for structs, // flow Marshal using a flow style (useful for structs,
// sequences and maps). // sequences and maps).
// //
// inline Inline the field, which must be a struct or a map, // inline Inline the field, which must be a struct or a map,
// causing all of its fields or keys to be processed as if // causing all of its fields or keys to be processed as if
// they were part of the outer struct. For maps, keys must // they were part of the outer struct. For maps, keys must
// not conflict with the yaml keys of other struct fields. // not conflict with the yaml keys of other struct fields.
// //
// In addition, if the key is "-", the field is ignored. // In addition, if the key is "-", the field is ignored.
// //
// For example: // For example:
// //
// type T struct { // type T struct {
// F int `yaml:"a,omitempty"` // F int `yaml:"a,omitempty"`
// B int // B int
// } // }
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
//
func Marshal(in interface{}) (out []byte, err error) { func Marshal(in interface{}) (out []byte, err error) {
defer handleErr(&err) defer handleErr(&err)
e := newEncoder() e := newEncoder()
@@ -278,6 +275,16 @@ func (e *Encoder) SetIndent(spaces int) {
e.encoder.indent = spaces e.encoder.indent = spaces
} }
// CompactSeqIndent makes it so that '- ' is considered part of the indentation.
func (e *Encoder) CompactSeqIndent() {
e.encoder.emitter.compact_sequence_indent = true
}
// DefaultSeqIndent makes it so that '- ' is not considered part of the indentation.
func (e *Encoder) DefaultSeqIndent() {
e.encoder.emitter.compact_sequence_indent = false
}
// Close closes the encoder by writing any remaining data. // Close closes the encoder by writing any remaining data.
// It does not write a stream terminating string "...". // It does not write a stream terminating string "...".
func (e *Encoder) Close() (err error) { func (e *Encoder) Close() (err error) {
@@ -358,22 +365,21 @@ const (
// //
// For example: // For example:
// //
// var person struct { // var person struct {
// Name string // Name string
// Address yaml.Node // Address yaml.Node
// } // }
// err := yaml.Unmarshal(data, &person) // err := yaml.Unmarshal(data, &person)
// //
// Or by itself: // Or by itself:
// //
// var person Node // var person Node
// err := yaml.Unmarshal(data, &person) // err := yaml.Unmarshal(data, &person)
//
type Node struct { type Node struct {
// Kind defines whether the node is a document, a mapping, a sequence, // Kind defines whether the node is a document, a mapping, a sequence,
// a scalar value, or an alias to another node. The specific data type of // a scalar value, or an alias to another node. The specific data type of
// scalar nodes may be obtained via the ShortTag and LongTag methods. // scalar nodes may be obtained via the ShortTag and LongTag methods.
Kind Kind Kind Kind
// Style allows customizing the apperance of the node in the tree. // Style allows customizing the apperance of the node in the tree.
Style Style Style Style
@@ -421,7 +427,6 @@ func (n *Node) IsZero() bool {
n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
} }
// LongTag returns the long form of the tag that indicates the data type for // LongTag returns the long form of the tag that indicates the data type for
// the node. If the Tag field isn't explicitly defined, one will be computed // the node. If the Tag field isn't explicitly defined, one will be computed
// based on the node properties. // based on the node properties.

View File

@@ -438,7 +438,9 @@ type yaml_document_t struct {
// The number of written bytes should be set to the size_read variable. // The number of written bytes should be set to the size_read variable.
// //
// [in,out] data A pointer to an application data specified by // [in,out] data A pointer to an application data specified by
// yaml_parser_set_input(). //
// yaml_parser_set_input().
//
// [out] buffer The buffer to write the data from the source. // [out] buffer The buffer to write the data from the source.
// [in] size The size of the buffer. // [in] size The size of the buffer.
// [out] size_read The actual number of bytes read from the source. // [out] size_read The actual number of bytes read from the source.
@@ -639,7 +641,6 @@ type yaml_parser_t struct {
} }
type yaml_comment_t struct { type yaml_comment_t struct {
scan_mark yaml_mark_t // Position where scanning for comments started scan_mark yaml_mark_t // Position where scanning for comments started
token_mark yaml_mark_t // Position after which tokens will be associated with this comment token_mark yaml_mark_t // Position after which tokens will be associated with this comment
start_mark yaml_mark_t // Position of '#' comment mark start_mark yaml_mark_t // Position of '#' comment mark
@@ -659,13 +660,14 @@ type yaml_comment_t struct {
// @a buffer to the output. // @a buffer to the output.
// //
// @param[in,out] data A pointer to an application data specified by // @param[in,out] data A pointer to an application data specified by
// yaml_emitter_set_output(). //
// yaml_emitter_set_output().
//
// @param[in] buffer The buffer with bytes to be written. // @param[in] buffer The buffer with bytes to be written.
// @param[in] size The size of the buffer. // @param[in] size The size of the buffer.
// //
// @returns On success, the handler should return @c 1. If the handler failed, // @returns On success, the handler should return @c 1. If the handler failed,
// the returned value should be @c 0. // the returned value should be @c 0.
//
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
type yaml_emitter_state_t int type yaml_emitter_state_t int
@@ -742,6 +744,8 @@ type yaml_emitter_t struct {
indent int // The current indentation level. indent int // The current indentation level.
compact_sequence_indent bool // Is '- ' is considered part of the indentation for sequence elements?
flow_level int // The current flow level. flow_level int // The current flow level.
root_context bool // Is it the document root context? root_context bool // Is it the document root context?

View File

@@ -1,17 +1,17 @@
// //
// Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov // Copyright (c) 2006-2010 Kirill Simonov
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in // this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to // the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do // of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions: // so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software. // copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -137,8 +137,8 @@ func is_crlf(b []byte, i int) bool {
func is_breakz(b []byte, i int) bool { func is_breakz(b []byte, i int) bool {
//return is_break(b, i) || is_z(b, i) //return is_break(b, i) || is_z(b, i)
return ( return (
// is_break: // is_break:
b[i] == '\r' || // CR (#xD) b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA) b[i] == '\n' || // LF (#xA)
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
@@ -151,8 +151,8 @@ func is_breakz(b []byte, i int) bool {
func is_spacez(b []byte, i int) bool { func is_spacez(b []byte, i int) bool {
//return is_space(b, i) || is_breakz(b, i) //return is_space(b, i) || is_breakz(b, i)
return ( return (
// is_space: // is_space:
b[i] == ' ' || b[i] == ' ' ||
// is_breakz: // is_breakz:
b[i] == '\r' || // CR (#xD) b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA) b[i] == '\n' || // LF (#xA)
@@ -166,8 +166,8 @@ func is_spacez(b []byte, i int) bool {
func is_blankz(b []byte, i int) bool { func is_blankz(b []byte, i int) bool {
//return is_blank(b, i) || is_breakz(b, i) //return is_blank(b, i) || is_breakz(b, i)
return ( return (
// is_blank: // is_blank:
b[i] == ' ' || b[i] == '\t' || b[i] == ' ' || b[i] == '\t' ||
// is_breakz: // is_breakz:
b[i] == '\r' || // CR (#xD) b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA) b[i] == '\n' || // LF (#xA)

150
vendor/gopkg.in/yaml.v3/README.md generated vendored
View File

@@ -1,150 +0,0 @@
# YAML support for the Go language
Introduction
------------
The yaml package enables Go programs to comfortably encode and decode YAML
values. It was developed within [Canonical](https://www.canonical.com) as
part of the [juju](https://juju.ubuntu.com) project, and is based on a
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
C library to parse and generate YAML data quickly and reliably.
Compatibility
-------------
The yaml package supports most of YAML 1.2, but preserves some behavior
from 1.1 for backwards compatibility.
Specifically, as of v3 of the yaml package:
- YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being
decoded into a typed bool value. Otherwise they behave as a string. Booleans
in YAML 1.2 are _true/false_ only.
- Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_
as specified in YAML 1.2, because most parsers still use the old format.
Octals in the _0o777_ format are supported though, so new files work.
- Does not support base-60 floats. These are gone from YAML 1.2, and were
actually never supported by this package as it's clearly a poor choice.
and offers backwards
compatibility with YAML 1.1 in some cases.
1.2, including support for
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
implemented, and base-60 floats from YAML 1.1 are purposefully not
supported since they're a poor design and are gone in YAML 1.2.
Installation and usage
----------------------
The import path for the package is *gopkg.in/yaml.v3*.
To install it, run:
go get gopkg.in/yaml.v3
API documentation
-----------------
If opened in a browser, the import path itself leads to the API documentation:
- [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3)
API stability
-------------
The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in).
License
-------
The yaml package is licensed under the MIT and Apache License 2.0 licenses.
Please see the LICENSE file for details.
Example
-------
```Go
package main
import (
"fmt"
"log"
"gopkg.in/yaml.v3"
)
var data = `
a: Easy!
b:
c: 2
d: [3, 4]
`
// Note: struct fields must be public in order for unmarshal to
// correctly populate the data.
type T struct {
A string
B struct {
RenamedC int `yaml:"c"`
D []int `yaml:",flow"`
}
}
func main() {
t := T{}
err := yaml.Unmarshal([]byte(data), &t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t:\n%v\n\n", t)
d, err := yaml.Marshal(&t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t dump:\n%s\n\n", string(d))
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m:\n%v\n\n", m)
d, err = yaml.Marshal(&m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m dump:\n%s\n\n", string(d))
}
```
This example will generate the following output:
```
--- t:
{Easy! {2 [3 4]}}
--- t dump:
a: Easy!
b:
c: 2
d: [3, 4]
--- m:
map[a:Easy! b:map[c:2 d:[3 4]]]
--- m dump:
a: Easy!
b:
c: 2
d:
- 3
- 4
```

35
vendor/modules.txt vendored
View File

@@ -9,35 +9,35 @@ github.com/go-viper/mapstructure/v2/internal/errors
# github.com/gorilla/feeds v1.2.0 # github.com/gorilla/feeds v1.2.0
## explicit; go 1.20 ## explicit; go 1.20
github.com/gorilla/feeds github.com/gorilla/feeds
# github.com/pelletier/go-toml/v2 v2.2.3 # github.com/pelletier/go-toml/v2 v2.2.4
## explicit; go 1.21.0 ## explicit; go 1.21.0
github.com/pelletier/go-toml/v2 github.com/pelletier/go-toml/v2
github.com/pelletier/go-toml/v2/internal/characters github.com/pelletier/go-toml/v2/internal/characters
github.com/pelletier/go-toml/v2/internal/danger github.com/pelletier/go-toml/v2/internal/danger
github.com/pelletier/go-toml/v2/internal/tracker github.com/pelletier/go-toml/v2/internal/tracker
github.com/pelletier/go-toml/v2/unstable github.com/pelletier/go-toml/v2/unstable
# github.com/sagikazarmark/locafero v0.9.0 # github.com/sagikazarmark/locafero v0.11.0
## explicit; go 1.23.0 ## explicit; go 1.23.0
github.com/sagikazarmark/locafero github.com/sagikazarmark/locafero
# github.com/sourcegraph/conc v0.3.0 # github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8
## explicit; go 1.19 ## explicit; go 1.20
github.com/sourcegraph/conc github.com/sourcegraph/conc
github.com/sourcegraph/conc/internal/multierror
github.com/sourcegraph/conc/iter
github.com/sourcegraph/conc/panics github.com/sourcegraph/conc/panics
# github.com/spf13/afero v1.14.0 github.com/sourcegraph/conc/pool
# github.com/spf13/afero v1.15.0
## explicit; go 1.23.0 ## explicit; go 1.23.0
github.com/spf13/afero github.com/spf13/afero
github.com/spf13/afero/internal/common github.com/spf13/afero/internal/common
github.com/spf13/afero/mem github.com/spf13/afero/mem
# github.com/spf13/cast v1.7.1 # github.com/spf13/cast v1.10.0
## explicit; go 1.19 ## explicit; go 1.21.0
github.com/spf13/cast github.com/spf13/cast
# github.com/spf13/pflag v1.0.6 github.com/spf13/cast/internal
# github.com/spf13/pflag v1.0.10
## explicit; go 1.12 ## explicit; go 1.12
github.com/spf13/pflag github.com/spf13/pflag
# github.com/spf13/viper v1.20.1 # github.com/spf13/viper v1.21.0
## explicit; go 1.21.0 ## explicit; go 1.23.0
github.com/spf13/viper github.com/spf13/viper
github.com/spf13/viper/internal/encoding/dotenv github.com/spf13/viper/internal/encoding/dotenv
github.com/spf13/viper/internal/encoding/json github.com/spf13/viper/internal/encoding/json
@@ -51,14 +51,14 @@ github.com/subosito/gotenv
## explicit ## explicit
github.com/tcolgate/mp3 github.com/tcolgate/mp3
github.com/tcolgate/mp3/internal/data github.com/tcolgate/mp3/internal/data
# go.uber.org/multierr v1.11.0 # go.yaml.in/yaml/v3 v3.0.4
## explicit; go 1.19 ## explicit; go 1.16
go.uber.org/multierr go.yaml.in/yaml/v3
# golang.org/x/sys v0.32.0 # golang.org/x/sys v0.32.0
## explicit; go 1.23.0 ## explicit; go 1.23.0
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
# golang.org/x/text v0.24.0 # golang.org/x/text v0.28.0
## explicit; go 1.23.0 ## explicit; go 1.23.0
golang.org/x/text/encoding golang.org/x/text/encoding
golang.org/x/text/encoding/internal golang.org/x/text/encoding/internal
@@ -68,6 +68,3 @@ golang.org/x/text/internal/utf8internal
golang.org/x/text/runes golang.org/x/text/runes
golang.org/x/text/transform golang.org/x/text/transform
golang.org/x/text/unicode/norm golang.org/x/text/unicode/norm
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3