diff --git a/file.go b/file.go index 101624d1a..50a40581d 100644 --- a/file.go +++ b/file.go @@ -1,5 +1,3 @@ -//go:build !viper_finder - package viper import ( @@ -7,12 +5,62 @@ import ( "os" "path/filepath" + "github.com/sagikazarmark/locafero" "github.com/spf13/afero" ) +// ExperimentalFinder tells Viper to use the new Finder interface for finding configuration files. +func ExperimentalFinder() Option { + return optionFunc(func(v *Viper) { + v.experimentalFinder = true + }) +} + +// Search for a config file. +func (v *Viper) findConfigFile() (string, error) { + finder := v.finder + + if finder == nil && v.experimentalFinder { + 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, + } + } + + if finder != nil { + return v.findConfigFileWithFinder(finder) + } + + return v.findConfigFileOld() +} + +func (v *Viper) findConfigFileWithFinder(finder Finder) (string, error) { + results, err := finder.Find(v.fs) + if err != nil { + return "", err + } + + if len(results) == 0 { + return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} + } + + // We call clean on the final result to ensure that the path is in its canonical form. + // This is mostly for consistent path handling and to make sure tests pass. + return results[0], nil +} + // Search all configPaths for any config file. // Returns the first path that exists (and is a config file). -func (v *Viper) findConfigFile() (string, error) { +func (v *Viper) findConfigFileOld() (string, error) { v.logger.Info("searching for config in paths", "paths", v.configPaths) for _, cp := range v.configPaths { diff --git a/file_finder.go b/file_finder.go deleted file mode 100644 index c8736213e..000000000 --- a/file_finder.go +++ /dev/null @@ -1,42 +0,0 @@ -//go:build viper_finder - -package viper - -import ( - "fmt" - - "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 := v.finder - - if finder == nil { - 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, - } - } - - results, err := finder.Find(v.fs) - if err != nil { - return "", err - } - - if len(results) == 0 { - return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} - } - - return results[0], nil -} diff --git a/finder_example_test.go b/finder_example_test.go index 76c5f5751..8f094520f 100644 --- a/finder_example_test.go +++ b/finder_example_test.go @@ -1,5 +1,3 @@ -//go:build viper_finder - package viper_test import ( diff --git a/go.mod b/go.mod index 2cc6b08a5..76fe4c615 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/magiconair/properties v1.8.7 github.com/pelletier/go-toml/v2 v2.2.2 github.com/sagikazarmark/crypt v0.20.0 - github.com/sagikazarmark/locafero v0.5.0 + github.com/sagikazarmark/locafero v0.6.0 github.com/spf13/afero v1.11.0 github.com/spf13/cast v1.6.0 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 5b1e70096..1d84624fd 100644 --- a/go.sum +++ b/go.sum @@ -246,8 +246,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.20.0 h1:aMLNL5RDUyk+/Bmoql3iYbv6C75/H0qvs6CWHzRGo6Y= github.com/sagikazarmark/crypt v0.20.0/go.mod h1:ojgvzJOpJkRhzSrycMyk8cgctQgyfZN/d2/dVh4U68c= -github.com/sagikazarmark/locafero v0.5.0 h1:zXz2JnQDgE5gDg0R9ThkNT0orQzm47i8IuO6hk6XSYY= -github.com/sagikazarmark/locafero v0.5.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= 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= diff --git a/internal/features/finder.go b/internal/features/finder.go new file mode 100644 index 000000000..983ea3a9d --- /dev/null +++ b/internal/features/finder.go @@ -0,0 +1,5 @@ +//go:build viper_finder + +package features + +const Finder = true diff --git a/internal/features/finder_default.go b/internal/features/finder_default.go new file mode 100644 index 000000000..89bcb06ee --- /dev/null +++ b/internal/features/finder_default.go @@ -0,0 +1,5 @@ +//go:build !viper_finder + +package features + +const Finder = false diff --git a/viper.go b/viper.go index 472fd5962..3f9d16405 100644 --- a/viper.go +++ b/viper.go @@ -194,6 +194,8 @@ type Viper struct { // TODO: should probably be protected with a mutex encoderRegistry *encoding.EncoderRegistry decoderRegistry *encoding.DecoderRegistry + + experimentalFinder bool } // New returns an initialized Viper instance. @@ -216,6 +218,8 @@ func New() *Viper { v.resetEncoding() + v.experimentalFinder = features.Finder + return v } diff --git a/viper_test.go b/viper_test.go index 33e6cc7e7..184f0b320 100644 --- a/viper_test.go +++ b/viper_test.go @@ -22,6 +22,7 @@ import ( "github.com/fsnotify/fsnotify" "github.com/go-viper/mapstructure/v2" + "github.com/sagikazarmark/locafero" "github.com/spf13/afero" "github.com/spf13/cast" "github.com/spf13/pflag" @@ -363,6 +364,54 @@ func TestGetConfigFile(t *testing.T) { // are not considered assert.Error(t, err) }) + + t.Run("experimental finder", func(t *testing.T) { + fs := afero.NewMemMapFs() + + err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777) + require.NoError(t, err) + + _, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml")) + require.NoError(t, err) + + v := NewWithOptions(ExperimentalFinder()) + + v.SetFs(fs) + + v.AddConfigPath("/etc/viper") + + filename, err := v.getConfigFile() + assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), testutil.AbsFilePath(t, filename)) + assert.NoError(t, err) + }) + + t.Run("finder", func(t *testing.T) { + fs := afero.NewMemMapFs() + + err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777) + require.NoError(t, err) + + _, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml")) + require.NoError(t, err) + + finder := locafero.Finder{ + Paths: []string{testutil.AbsFilePath(t, "/etc/viper")}, + Names: locafero.NameWithExtensions("config", SupportedExts...), + Type: locafero.FileTypeFile, + } + + v := NewWithOptions(WithFinder(finder)) + + v.SetFs(fs) + + // These should be ineffective + v.AddConfigPath("/etc/something_else") + v.SetConfigName("not-config") + + filename, err := v.getConfigFile() + assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), testutil.AbsFilePath(t, filename)) + assert.NoError(t, err) + }) } func TestReadInConfig(t *testing.T) { @@ -415,6 +464,65 @@ func TestReadInConfig(t *testing.T) { assert.Equal(t, "value", v.Get("key")) }) + + t.Run("find file with experimental finder", func(t *testing.T) { + fs := afero.NewMemMapFs() + + err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777) + require.NoError(t, err) + + file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml")) + require.NoError(t, err) + + _, err = file.WriteString(`key: value`) + require.NoError(t, err) + + file.Close() + + v := NewWithOptions(ExperimentalFinder()) + + v.SetFs(fs) + v.AddConfigPath("/etc/viper") + + err = v.ReadInConfig() + require.NoError(t, err) + + assert.Equal(t, "value", v.Get("key")) + }) + + t.Run("find file using a finder", func(t *testing.T) { + fs := afero.NewMemMapFs() + + err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777) + require.NoError(t, err) + + file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml")) + require.NoError(t, err) + + _, err = file.WriteString(`key: value`) + require.NoError(t, err) + + file.Close() + + finder := locafero.Finder{ + Paths: []string{testutil.AbsFilePath(t, "/etc/viper")}, + Names: locafero.NameWithExtensions("config", SupportedExts...), + Type: locafero.FileTypeFile, + } + + v := NewWithOptions(WithFinder(finder)) + + v.SetFs(fs) + + // These should be ineffective + v.AddConfigPath("/etc/something_else") + v.SetConfigName("not-config") + + err = v.ReadInConfig() + require.NoError(t, err) + + assert.Equal(t, "value", v.Get("key")) + }) } func TestDefault(t *testing.T) {