diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bce54e83e..05e48a0ea 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -45,6 +45,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] go: ['1.19', '1.20', '1.21'] + tags: ['', 'finder'] steps: - name: Checkout repository @@ -56,11 +57,11 @@ jobs: go-version: ${{ matrix.go }} - name: Test - run: go test -race -v ./... + run: go test -race -v -tags '${{ matrix.tags }}' ./... if: runner.os != 'Windows' - name: Test (without race detector) - run: go test -v ./... + run: go test -v -tags '${{ matrix.tags }}' ./... if: runner.os == 'Windows' lint: diff --git a/fs.go b/fs.go deleted file mode 100644 index ecb1769e5..000000000 --- a/fs.go +++ /dev/null @@ -1,65 +0,0 @@ -//go:build go1.16 && finder -// +build go1.16,finder - -package viper - -import ( - "errors" - "io/fs" - "path" -) - -type finder struct { - paths []string - fileNames []string - extensions []string - - withoutExtension bool -} - -func (f finder) Find(fsys fs.FS) (string, error) { - for _, searchPath := range f.paths { - for _, fileName := range f.fileNames { - for _, extension := range f.extensions { - filePath := path.Join(searchPath, fileName+"."+extension) - - ok, err := fileExists(fsys, filePath) - if err != nil { - return "", err - } - - if ok { - return filePath, nil - } - } - - if f.withoutExtension { - filePath := path.Join(searchPath, fileName) - - ok, err := fileExists(fsys, filePath) - if err != nil { - return "", err - } - - if ok { - return filePath, nil - } - } - } - } - - return "", nil -} - -func fileExists(fsys fs.FS, filePath string) (bool, error) { - fileInfo, err := fs.Stat(fsys, filePath) - if err == nil { - return !fileInfo.IsDir(), nil - } - - if errors.Is(err, fs.ErrNotExist) { - return false, nil - } - - return false, err -} diff --git a/fs_test.go b/fs_test.go deleted file mode 100644 index 071e3461d..000000000 --- a/fs_test.go +++ /dev/null @@ -1,100 +0,0 @@ -//go:build go1.16 && finder -// +build go1.16,finder - -package viper - -import ( - "io/fs" - "testing" - "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestFinder(t *testing.T) { - t.Parallel() - - fsys := fstest.MapFS{ - "home/user/.config": &fstest.MapFile{}, - "home/user/config.json": &fstest.MapFile{}, - "home/user/config.yaml": &fstest.MapFile{}, - "home/user/data.json": &fstest.MapFile{}, - "etc/config/.config": &fstest.MapFile{}, - "etc/config/a_random_file.txt": &fstest.MapFile{}, - "etc/config/config.json": &fstest.MapFile{}, - "etc/config/config.yaml": &fstest.MapFile{}, - "etc/config/config.xml": &fstest.MapFile{}, - } - - testCases := []struct { - name string - fsys func() fs.FS - finder finder - result string - }{ - { - name: "find file", - fsys: func() fs.FS { return fsys }, - finder: finder{ - paths: []string{"etc/config"}, - fileNames: []string{"config"}, - extensions: []string{"json"}, - }, - result: "etc/config/config.json", - }, - { - name: "file not found", - fsys: func() fs.FS { return fsys }, - finder: finder{ - paths: []string{"var/config"}, - fileNames: []string{"config"}, - extensions: []string{"json"}, - }, - result: "", - }, - { - name: "empty search params", - fsys: func() fs.FS { return fsys }, - finder: finder{}, - result: "", - }, - { - name: "precedence", - fsys: func() fs.FS { return fsys }, - finder: finder{ - paths: []string{"var/config", "home/user", "etc/config"}, - fileNames: []string{"aconfig", "config"}, - extensions: []string{"zml", "xml", "json"}, - }, - result: "home/user/config.json", - }, - { - name: "without extension", - fsys: func() fs.FS { return fsys }, - finder: finder{ - paths: []string{"var/config", "home/user", "etc/config"}, - fileNames: []string{".config"}, - extensions: []string{"zml", "xml", "json"}, - - withoutExtension: true, - }, - result: "home/user/.config", - }, - } - - for _, testCase := range testCases { - testCase := testCase - - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - - fsys := testCase.fsys() - - result, err := testCase.finder.Find(fsys) - require.NoError(t, err) - - assert.Equal(t, testCase.result, result) - }) - } -} diff --git a/go.mod b/go.mod index 44a52c0b4..b235ae022 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/pelletier/go-toml/v2 v2.1.0 github.com/sagikazarmark/crypt v0.14.0 + github.com/sagikazarmark/locafero v0.3.0 github.com/sagikazarmark/slog-shim v0.1.0 github.com/spf13/afero v1.9.5 github.com/spf13/cast v1.5.1 @@ -58,13 +59,14 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect go.etcd.io/etcd/api/v3 v3.5.9 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect go.etcd.io/etcd/client/v2 v2.305.9 // indirect go.etcd.io/etcd/client/v3 v3.5.9 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.8.0 // indirect + go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect diff --git a/go.sum b/go.sum index 2c3e0b5eb..415661a61 100644 --- a/go.sum +++ b/go.sum @@ -330,12 +330,16 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.14.0 h1:+QD5vjd6aZd6moHuIRVL+uJO7fkhiRjMz3ldbZQY5go= github.com/sagikazarmark/crypt v0.14.0/go.mod h1:JuxBJUNXrVOAywrtQNTZpOeTgcL1Az5qM7jKVDOifig= +github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= +github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= @@ -390,8 +394,8 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/viper_go1_15.go b/viper_go1_15.go index 19a771cbd..faa9ea2ea 100644 --- a/viper_go1_15.go +++ b/viper_go1_15.go @@ -1,5 +1,5 @@ -//go:build !go1.16 || !finder -// +build !go1.16 !finder +//go:build !finder +// +build !finder package viper diff --git a/viper_go1_16.go b/viper_go1_16.go index e10172fa3..cfadc052f 100644 --- a/viper_go1_16.go +++ b/viper_go1_16.go @@ -1,32 +1,39 @@ -//go:build go1.16 && finder -// +build go1.16,finder +//go:build finder +// +build finder package viper import ( "fmt" - "github.com/spf13/afero" + "github.com/sagikazarmark/locafero" ) // Search all configPaths for any config file. // Returns the first path that exists (and is a config file). func (v *Viper) findConfigFile() (string, error) { - finder := finder{ - paths: v.configPaths, - fileNames: []string{v.configName}, - extensions: SupportedExts, - withoutExtension: v.configType != "", + var names []string + + if v.configType != "" { + names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...) + } else { + names = locafero.NameWithExtensions(v.configName, SupportedExts...) + } + + finder := locafero.Finder{ + Paths: v.configPaths, + Names: names, + Type: locafero.FileTypeFile, } - file, err := finder.Find(afero.NewIOFS(v.fs)) + results, err := finder.Find(v.fs) if err != nil { return "", err } - if file == "" { + if len(results) == 0 { return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} } - return file, nil + return results[0], nil }