From fbf5c725e354824b5c8cddfa579153fa71223baf Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 17 Nov 2024 08:21:46 -0500 Subject: [PATCH 1/7] Feat:(issue_10) Move providers into separate paths --- altsrc.go | 6 +- altsrc_test.go | 4 +- examples_test.go | 151 ------------------ file_source_cache.go | 17 +- go.mod | 23 +-- go.sum | 18 +-- json/examples_test.go | 65 ++++++++ json/go.mod | 20 +++ json/go.sum | 12 ++ .../json_value_source.go | 9 +- .../json_value_source_test.go | 19 ++- internal/package.go => testutl.go | 2 +- toml/examples_test.go | 65 ++++++++ toml/go.mod | 18 +++ toml/go.sum | 14 ++ toml_map.go => toml/toml_map.go | 2 +- .../toml_value_source.go | 12 +- .../toml_value_source_test.go | 12 ++ yaml/examples_test.go | 65 ++++++++ yaml/go.mod | 17 ++ yaml/go.sum | 12 ++ .../yaml_value_source.go | 18 +-- .../yaml_value_source_test.go | 14 +- 23 files changed, 387 insertions(+), 208 deletions(-) delete mode 100644 examples_test.go create mode 100644 json/examples_test.go create mode 100644 json/go.mod create mode 100644 json/go.sum rename json_value_source.go => json/json_value_source.go (56%) rename json_value_source_test.go => json/json_value_source_test.go (65%) rename internal/package.go => testutl.go (96%) create mode 100644 toml/examples_test.go create mode 100644 toml/go.mod create mode 100644 toml/go.sum rename toml_map.go => toml/toml_map.go (95%) rename toml_value_source.go => toml/toml_value_source.go (80%) rename toml_value_source_test.go => toml/toml_value_source_test.go (74%) create mode 100644 yaml/examples_test.go create mode 100644 yaml/go.mod create mode 100644 yaml/go.sum rename yaml_value_source.go => yaml/yaml_value_source.go (77%) rename yaml_value_source_test.go => yaml/yaml_value_source_test.go (73%) diff --git a/altsrc.go b/altsrc.go index 321902d..81a84b8 100644 --- a/altsrc.go +++ b/altsrc.go @@ -45,7 +45,7 @@ func tracef(format string, a ...any) { ) } -func readURI(uriString string) ([]byte, error) { +func ReadURI(uriString string) ([]byte, error) { u, err := url.Parse(uriString) if err != nil { return nil, err @@ -73,10 +73,10 @@ func readURI(uriString string) ([]byte, error) { return nil, fmt.Errorf("%[1]w: unable to determine how to load from %[2]q", Err, uriString) } -// nestedVal checks if the name has '.' delimiters. +// NestedVal checks if the name has '.' delimiters. // If so, it tries to traverse the tree by the '.' delimited sections to find // a nested value for the key. -func nestedVal(name string, tree map[any]any) (any, bool) { +func NestedVal(name string, tree map[any]any) (any, bool) { if sections := strings.Split(name, "."); len(sections) > 1 { node := tree for _, section := range sections[:len(sections)-1] { diff --git a/altsrc_test.go b/altsrc_test.go index 812a8ee..e502894 100644 --- a/altsrc_test.go +++ b/altsrc_test.go @@ -3,8 +3,6 @@ package altsrc import ( "context" "time" - - "github.com/urfave/cli-altsrc/v3/internal" ) var ( @@ -12,6 +10,6 @@ var ( ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - return internal.MustTestdataDir(ctx) + return MustTestdataDir(ctx) }() ) diff --git a/examples_test.go b/examples_test.go deleted file mode 100644 index dc6e0c2..0000000 --- a/examples_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package altsrc_test - -import ( - "context" - "fmt" - "os" - "path/filepath" - "time" - - altsrc "github.com/urfave/cli-altsrc/v3" - "github.com/urfave/cli-altsrc/v3/internal" - "github.com/urfave/cli/v3" -) - -var ( - testdataDir = func() string { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - - return internal.MustTestdataDir(ctx) - }() -) - -func ExampleYAML() { - configFiles := []string{ - filepath.Join(testdataDir, "config.yaml"), - filepath.Join(testdataDir, "alt-config.yaml"), - } - - app := &cli.Command{ - Name: "greet", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Sources: altsrc.YAML("greet.name", configFiles...), - }, - &cli.IntFlag{ - Name: "enthusiasm", - Aliases: []string{"!"}, - Sources: altsrc.YAML("greet.enthusiasm", configFiles...), - }, - }, - Action: func(cCtx *cli.Context) error { - punct := "" - if cCtx.Int("enthusiasm") > 9000 { - punct = "!" - } - - fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cCtx.String("name"), punct) - - return nil - }, - } - - // Simulating os.Args - os.Args = []string{"greet"} - - if err := app.Run(context.Background(), os.Args); err != nil { - fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) - } - - // Output: - // Hello, Berry! -} - -func ExampleJSON() { - configFiles := []string{ - filepath.Join(testdataDir, "config.json"), - filepath.Join(testdataDir, "alt-config.json"), - } - - app := &cli.Command{ - Name: "greet", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Sources: altsrc.JSON("greet.name", configFiles...), - }, - &cli.IntFlag{ - Name: "enthusiasm", - Aliases: []string{"!"}, - Sources: altsrc.JSON("greet.enthusiasm", configFiles...), - }, - }, - Action: func(cCtx *cli.Context) error { - punct := "" - if cCtx.Int("enthusiasm") > 9000 { - punct = "!" - } - - fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cCtx.String("name"), punct) - - return nil - }, - } - - // Simulating os.Args - os.Args = []string{"greet"} - - if err := app.Run(context.Background(), os.Args); err != nil { - fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) - } - - // Output: - // Hello, Berry! -} - -func ExampleTOML() { - configFiles := []string{ - filepath.Join(testdataDir, "config.toml"), - filepath.Join(testdataDir, "alt-config.toml"), - } - - app := &cli.Command{ - Name: "greet", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Sources: altsrc.TOML("greet.name", configFiles...), - }, - &cli.IntFlag{ - Name: "enthusiasm", - Aliases: []string{"!"}, - Sources: altsrc.TOML("greet.enthusiasm", configFiles...), - }, - }, - Action: func(cCtx *cli.Context) error { - punct := "" - if cCtx.Int("enthusiasm") > 9000 { - punct = "!" - } - - fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cCtx.String("name"), punct) - - return nil - }, - } - - // Simulating os.Args - os.Args = []string{"greet"} - - if err := app.Run(context.Background(), os.Args); err != nil { - fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) - } - - // Output: - // Hello, Berry! -} diff --git a/file_source_cache.go b/file_source_cache.go index eeacb8f..a7bd64f 100644 --- a/file_source_cache.go +++ b/file_source_cache.go @@ -1,12 +1,19 @@ package altsrc -type fileSourceCache[T any] struct { +type FileSourceCache[T any] struct { file string m *T f func(string, any) error } -func (fsc *fileSourceCache[T]) Get() T { +func NewFileSourceCache[T any](file string, f func(string, any) error) *FileSourceCache[T] { + return &FileSourceCache[T]{ + file: file, + f: f, + } +} + +func (fsc *FileSourceCache[T]) Get() T { if fsc.m == nil { res := new(T) if err := fsc.f(fsc.file, res); err == nil { @@ -25,6 +32,8 @@ func (fsc *fileSourceCache[T]) Get() T { return *fsc.m } -type mapAnyAnyFileSourceCache = fileSourceCache[map[any]any] +type MapAnyAnyFileSourceCache = FileSourceCache[map[any]any] -type tomlMapFileSourceCache = fileSourceCache[tomlMap] +func NewMapAnyAnyFileSourceCache(file string, f func(string, any) error) *MapAnyAnyFileSourceCache { + return NewFileSourceCache[map[any]any](file, f) +} diff --git a/go.mod b/go.mod index c939294..e4dc22e 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,21 @@ module github.com/urfave/cli-altsrc/v3 -go 1.18 +go 1.23.2 require ( - github.com/BurntSushi/toml v1.3.2 - github.com/stretchr/testify v1.8.4 - github.com/urfave/cli/v3 v3.0.0-alpha4 - gopkg.in/yaml.v3 v3.0.1 + github.com/urfave/cli-altsrc/json v0.0.1 + github.com/urfave/cli-altsrc/toml v0.0.1 + github.com/urfave/cli-altsrc/yaml v0.0.1 + github.com/urfave/cli/v3 v3.0.0-alpha9.3 ) require ( - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/urfave/cli-altsrc/yaml v0.0.1 => ./yaml/ + +replace github.com/urfave/cli-altsrc/json v0.0.1 => ./json/ + +replace github.com/urfave/cli-altsrc/toml v0.0.1 => ./toml/ diff --git a/go.sum b/go.sum index 462cae7..e1c2e72 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,13 @@ -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/urfave/cli/v3 v3.0.0-alpha4 h1:RJFGIs3mcalmc2YgliDh0Pa4l79S+Dqdz7cW8Fcp7Rg= -github.com/urfave/cli/v3 v3.0.0-alpha4/go.mod h1:ZFqSEHhze0duJACOdz43I5IcnKhf4RoTlOoUMBUggOI= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v3 v3.0.0-alpha9.3 h1:RfQlgUHMRxDMwEEmGsrHd+mXYJpWpXlcJM8w86cpjGs= +github.com/urfave/cli/v3 v3.0.0-alpha9.3/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/json/examples_test.go b/json/examples_test.go new file mode 100644 index 0000000..28e64e3 --- /dev/null +++ b/json/examples_test.go @@ -0,0 +1,65 @@ +package json_test + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + json "github.com/urfave/cli-altsrc/json" + altsrc "github.com/urfave/cli-altsrc/v3" + "github.com/urfave/cli/v3" +) + +var ( + testdataDir = func() string { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + return altsrc.MustTestdataDir(ctx) + }() +) + +func ExampleJSON() { + configFiles := []string{ + filepath.Join(testdataDir, "config.json"), + filepath.Join(testdataDir, "alt-config.json"), + } + + app := &cli.Command{ + Name: "greet", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Sources: json.JSON("greet.name", configFiles...), + }, + &cli.IntFlag{ + Name: "enthusiasm", + Aliases: []string{"!"}, + Sources: json.JSON("greet.enthusiasm", configFiles...), + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + punct := "" + if cmd.Int("enthusiasm") > 9000 { + punct = "!" + } + + fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cmd.String("name"), punct) + + return nil + }, + } + + // Simulating os.Args + os.Args = []string{"greet"} + + if err := app.Run(context.Background(), os.Args); err != nil { + fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) + } + + // Output: + // Hello, Berry! +} diff --git a/json/go.mod b/json/go.mod new file mode 100644 index 0000000..77efb49 --- /dev/null +++ b/json/go.mod @@ -0,0 +1,20 @@ +module github.com/urfave/cli-altsrc/json + +go 1.23.2 + +require ( + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli-altsrc/yaml v0.0.1 + github.com/urfave/cli/v3 v3.0.0-alpha9.3 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/urfave/cli-altsrc/v3 v3.0.0-alpha9.3 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/urfave/cli-altsrc/yaml v0.0.1 => ../yaml + +replace github.com/urfave/cli-altsrc/v3 v3.0.0-alpha9.3 => ../ diff --git a/json/go.sum b/json/go.sum new file mode 100644 index 0000000..3d44f9d --- /dev/null +++ b/json/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v3 v3.0.0-alpha9.3 h1:RfQlgUHMRxDMwEEmGsrHd+mXYJpWpXlcJM8w86cpjGs= +github.com/urfave/cli/v3 v3.0.0-alpha9.3/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/json_value_source.go b/json/json_value_source.go similarity index 56% rename from json_value_source.go rename to json/json_value_source.go index 33ab202..4236602 100644 --- a/json_value_source.go +++ b/json/json_value_source.go @@ -1,9 +1,12 @@ -package altsrc +package json -import "github.com/urfave/cli/v3" +import ( + yaml "github.com/urfave/cli-altsrc/yaml" + "github.com/urfave/cli/v3" +) // JSON is a helper function that wraps the YAML helper function // and loads via yaml.Unmarshal func JSON(key string, paths ...string) cli.ValueSourceChain { - return YAML(key, paths...) + return yaml.YAML(key, paths...) } diff --git a/json_value_source_test.go b/json/json_value_source_test.go similarity index 65% rename from json_value_source_test.go rename to json/json_value_source_test.go index 507f1e4..3cbc5c2 100644 --- a/json_value_source_test.go +++ b/json/json_value_source_test.go @@ -1,10 +1,23 @@ -package altsrc +package json import ( + "context" "path/filepath" "testing" + "time" "github.com/stretchr/testify/require" + altsrc "github.com/urfave/cli-altsrc/v3" + yaml "github.com/urfave/cli-altsrc/yaml" +) + +var ( + testdataDir = func() string { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + return altsrc.MustTestdataDir(ctx) + }() ) func TestJSON(t *testing.T) { @@ -13,7 +26,7 @@ func TestJSON(t *testing.T) { configPath := filepath.Join(testdataDir, "test_config.json") altConfigPath := filepath.Join(testdataDir, "test_alt_config.json") - vsc := YAML( + vsc := yaml.YAML( "water_fountain.water", "/dev/null/nonexistent.json", configPath, @@ -23,7 +36,7 @@ func TestJSON(t *testing.T) { r.Equal("false", v) r.True(ok) - yvs := vsc.Chain[0].(*yamlValueSource) + yvs := vsc.Chain[0] r.Equal("yaml file \"/dev/null/nonexistent.json\" at key \"water_fountain.water\"", yvs.String()) r.Equal("&yamlValueSource{file:\"/dev/null/nonexistent.json\",keyPath:\"water_fountain.water\"}", yvs.GoString()) } diff --git a/internal/package.go b/testutl.go similarity index 96% rename from internal/package.go rename to testutl.go index a1390a4..6cc8200 100644 --- a/internal/package.go +++ b/testutl.go @@ -1,4 +1,4 @@ -package internal +package altsrc import ( "context" diff --git a/toml/examples_test.go b/toml/examples_test.go new file mode 100644 index 0000000..e39a1f4 --- /dev/null +++ b/toml/examples_test.go @@ -0,0 +1,65 @@ +package altsrc_test + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + toml "github.com/urfave/cli-altsrc/toml" + altsrc "github.com/urfave/cli-altsrc/v3" + "github.com/urfave/cli/v3" +) + +var ( + testdataDir = func() string { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + return altsrc.MustTestdataDir(ctx) + }() +) + +func ExampleTOML() { + configFiles := []string{ + filepath.Join(testdataDir, "config.toml"), + filepath.Join(testdataDir, "alt-config.toml"), + } + + app := &cli.Command{ + Name: "greet", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Sources: toml.TOML("greet.name", configFiles...), + }, + &cli.IntFlag{ + Name: "enthusiasm", + Aliases: []string{"!"}, + Sources: toml.TOML("greet.enthusiasm", configFiles...), + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + punct := "" + if cmd.Int("enthusiasm") > 9000 { + punct = "!" + } + + fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cmd.String("name"), punct) + + return nil + }, + } + + // Simulating os.Args + os.Args = []string{"greet"} + + if err := app.Run(context.Background(), os.Args); err != nil { + fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) + } + + // Output: + // Hello, Berry! +} diff --git a/toml/go.mod b/toml/go.mod new file mode 100644 index 0000000..81721c7 --- /dev/null +++ b/toml/go.mod @@ -0,0 +1,18 @@ +module github.com/urfave/cli-altsrc/toml + +go 1.23.2 + +require ( + github.com/BurntSushi/toml v1.4.0 + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli/v3 v3.0.0-alpha9.3 + github.com/urfave/cli-altsrc/v3 v3.0.0-alpha9 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/urfave/cli-altsrc/v3 v3.0.0-alpha9 => ../ \ No newline at end of file diff --git a/toml/go.sum b/toml/go.sum new file mode 100644 index 0000000..e1c2e72 --- /dev/null +++ b/toml/go.sum @@ -0,0 +1,14 @@ +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v3 v3.0.0-alpha9.3 h1:RfQlgUHMRxDMwEEmGsrHd+mXYJpWpXlcJM8w86cpjGs= +github.com/urfave/cli/v3 v3.0.0-alpha9.3/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/toml_map.go b/toml/toml_map.go similarity index 95% rename from toml_map.go rename to toml/toml_map.go index ec14047..661fe4d 100644 --- a/toml_map.go +++ b/toml/toml_map.go @@ -62,7 +62,7 @@ func unmarshalMap(i any) (ret map[any]any, err error) { case reflect.Array, reflect.Slice: ret[key] = val.([]any) default: - return nil, fmt.Errorf("%[1]w: unsupported type %#[2]v", Err, v.Kind()) + return nil, fmt.Errorf("%[1]w: unsupported type %#[2]v", err, v.Kind()) } } return ret, nil diff --git a/toml_value_source.go b/toml/toml_value_source.go similarity index 80% rename from toml_value_source.go rename to toml/toml_value_source.go index 2bb1a1a..27e84df 100644 --- a/toml_value_source.go +++ b/toml/toml_value_source.go @@ -4,9 +4,12 @@ import ( "fmt" "github.com/BurntSushi/toml" + altsrc "github.com/urfave/cli-altsrc/v3" "github.com/urfave/cli/v3" ) +type tomlMapFileSourceCache = altsrc.FileSourceCache[tomlMap] + // TOML is a helper function to encapsulate a number of // tomlValueSource together as a cli.ValueSourceChain func TOML(key string, paths ...string) cli.ValueSourceChain { @@ -18,10 +21,7 @@ func TOML(key string, paths ...string) cli.ValueSourceChain { &tomlValueSource{ file: path, key: key, - tmc: tomlMapFileSourceCache{ - file: path, - f: tomlUnmarshalFile, - }, + tmc: *altsrc.NewFileSourceCache[tomlMap](path, tomlUnmarshalFile), }, ) } @@ -37,7 +37,7 @@ type tomlValueSource struct { } func (tvs *tomlValueSource) Lookup() (string, bool) { - if v, ok := nestedVal(tvs.key, tvs.tmc.Get().Map); ok { + if v, ok := altsrc.NestedVal(tvs.key, tvs.tmc.Get().Map); ok { return fmt.Sprintf("%[1]v", v), ok } @@ -53,7 +53,7 @@ func (tvs *tomlValueSource) GoString() string { } func tomlUnmarshalFile(filePath string, container any) error { - b, err := readURI(filePath) + b, err := altsrc.ReadURI(filePath) if err != nil { return err } diff --git a/toml_value_source_test.go b/toml/toml_value_source_test.go similarity index 74% rename from toml_value_source_test.go rename to toml/toml_value_source_test.go index 53023b3..e4ed744 100644 --- a/toml_value_source_test.go +++ b/toml/toml_value_source_test.go @@ -1,10 +1,22 @@ package altsrc import ( + "context" "path/filepath" "testing" + "time" "github.com/stretchr/testify/require" + altsrc "github.com/urfave/cli-altsrc/v3" +) + +var ( + testdataDir = func() string { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + return altsrc.MustTestdataDir(ctx) + }() ) func TestTOML(t *testing.T) { diff --git a/yaml/examples_test.go b/yaml/examples_test.go new file mode 100644 index 0000000..56979af --- /dev/null +++ b/yaml/examples_test.go @@ -0,0 +1,65 @@ +package yaml_test + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + altsrc "github.com/urfave/cli-altsrc/v3" + yaml "github.com/urfave/cli-altsrc/yaml" + "github.com/urfave/cli/v3" +) + +var ( + testdataDir = func() string { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + return altsrc.MustTestdataDir(ctx) + }() +) + +func ExampleYAML() { + configFiles := []string{ + filepath.Join(testdataDir, "config.yaml"), + filepath.Join(testdataDir, "alt-config.yaml"), + } + + app := &cli.Command{ + Name: "greet", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Sources: yaml.YAML("greet.name", configFiles...), + }, + &cli.IntFlag{ + Name: "enthusiasm", + Aliases: []string{"!"}, + Sources: yaml.YAML("greet.enthusiasm", configFiles...), + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + punct := "" + if cmd.Int("enthusiasm") > 9000 { + punct = "!" + } + + fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cmd.String("name"), punct) + + return nil + }, + } + + // Simulating os.Args + os.Args = []string{"greet"} + + if err := app.Run(context.Background(), os.Args); err != nil { + fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) + } + + // Output: + // Hello, Berry! +} diff --git a/yaml/go.mod b/yaml/go.mod new file mode 100644 index 0000000..78132f0 --- /dev/null +++ b/yaml/go.mod @@ -0,0 +1,17 @@ +module github.com/urfave/cli-altsrc/yaml + +go 1.23.2 + +require ( + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli-altsrc/v3 v3.0.0-alpha2 + github.com/urfave/cli/v3 v3.0.0-alpha9.3 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) + +replace github.com/urfave/cli-altsrc/v3 v3.0.0-alpha2 => ../ diff --git a/yaml/go.sum b/yaml/go.sum new file mode 100644 index 0000000..3d44f9d --- /dev/null +++ b/yaml/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v3 v3.0.0-alpha9.3 h1:RfQlgUHMRxDMwEEmGsrHd+mXYJpWpXlcJM8w86cpjGs= +github.com/urfave/cli/v3 v3.0.0-alpha9.3/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/yaml_value_source.go b/yaml/yaml_value_source.go similarity index 77% rename from yaml_value_source.go rename to yaml/yaml_value_source.go index 3736915..90e48c7 100644 --- a/yaml_value_source.go +++ b/yaml/yaml_value_source.go @@ -1,8 +1,9 @@ -package altsrc +package yaml import ( "fmt" + altsrc "github.com/urfave/cli-altsrc/v3" "github.com/urfave/cli/v3" "gopkg.in/yaml.v3" ) @@ -16,12 +17,9 @@ func YAML(key string, paths ...string) cli.ValueSourceChain { vsc.Chain = append( vsc.Chain, &yamlValueSource{ - file: path, - key: key, - maafsc: mapAnyAnyFileSourceCache{ - file: path, - f: yamlUnmarshalFile, - }, + file: path, + key: key, + maafsc: altsrc.NewMapAnyAnyFileSourceCache(path, yamlUnmarshalFile), }, ) } @@ -33,11 +31,11 @@ type yamlValueSource struct { file string key string - maafsc mapAnyAnyFileSourceCache + maafsc *altsrc.MapAnyAnyFileSourceCache } func (yvs *yamlValueSource) Lookup() (string, bool) { - if v, ok := nestedVal(yvs.key, yvs.maafsc.Get()); ok { + if v, ok := altsrc.NestedVal(yvs.key, yvs.maafsc.Get()); ok { return fmt.Sprintf("%[1]v", v), ok } @@ -53,7 +51,7 @@ func (yvs *yamlValueSource) GoString() string { } func yamlUnmarshalFile(filePath string, container any) error { - b, err := readURI(filePath) + b, err := altsrc.ReadURI(filePath) if err != nil { return err } diff --git a/yaml_value_source_test.go b/yaml/yaml_value_source_test.go similarity index 73% rename from yaml_value_source_test.go rename to yaml/yaml_value_source_test.go index cdc94c0..bb3b1a8 100644 --- a/yaml_value_source_test.go +++ b/yaml/yaml_value_source_test.go @@ -1,10 +1,22 @@ -package altsrc +package yaml import ( + "context" "path/filepath" "testing" + "time" "github.com/stretchr/testify/require" + altsrc "github.com/urfave/cli-altsrc/v3" +) + +var ( + testdataDir = func() string { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + return altsrc.MustTestdataDir(ctx) + }() ) func TestYAML(t *testing.T) { From 7d007577c7b130327b1aa7d39e01b6d379efaf6e Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 17 Nov 2024 09:05:05 -0500 Subject: [PATCH 2/7] Update Example in README.md --- README.md | 67 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 9a8dcb4..e38aa61 100644 --- a/README.md +++ b/README.md @@ -13,41 +13,44 @@ features which are otherwise not used throughout [`urfave/cli/v3`]. ### Example ```go -configFiles := []string{ - filepath.Join(testdataDir, "config.yaml"), - filepath.Join(testdataDir, "alt-config.yaml"), -} - -app := &cli.Command{ - Name: "greet", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Sources: altsrc.YAML("greet.name", configFiles...), + configFiles := []string{ + filepath.Join(testdataDir, "config.yaml"), + filepath.Join(testdataDir, "alt-config.yaml"), + } + + app := &cli.Command{ + Name: "greet", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Sources: yaml.YAML("greet.name", configFiles...), + }, + &cli.IntFlag{ + Name: "enthusiasm", + Aliases: []string{"!"}, + Sources: yaml.YAML("greet.enthusiasm", configFiles...), + }, }, - &cli.IntFlag{ - Name: "enthusiasm", - Aliases: []string{"!"}, - Sources: altsrc.YAML("greet.enthusiasm", configFiles...), - }, - }, - Action: func(cCtx *cli.Context) error { - punct := "" - if cCtx.Int("enthusiasm") > 9000 { - punct = "!" - } + Action: func(ctx context.Context, cmd *cli.Command) error { + punct := "" + if cmd.Int("enthusiasm") > 9000 { + punct = "!" + } + + fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cmd.String("name"), punct) - fmt.Fprintf(os.Stdout, "Hello, %[1]v%[2]v\n", cCtx.String("name"), punct) + return nil + }, + } - return nil - }, -} + // Simulating os.Args + os.Args = []string{"greet"} -// Simulating os.Args -os.Args = []string{"greet"} + if err := app.Run(context.Background(), os.Args); err != nil { + fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) + } -if err := app.Run(context.Background(), os.Args); err != nil { - fmt.Fprintf(os.Stdout, "OH NO: %[1]v\n", err) -} + // Output: + // Hello, Berry! ``` From 85a488c0f4adefc7952670305ef289bc2baf4825 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 17 Nov 2024 10:27:19 -0500 Subject: [PATCH 3/7] Move read uri in file source --- file_source_cache.go | 22 ++++++++++++---------- toml/toml_value_source.go | 15 +-------------- yaml/yaml_value_source.go | 15 +-------------- 3 files changed, 14 insertions(+), 38 deletions(-) diff --git a/file_source_cache.go b/file_source_cache.go index a7bd64f..8799c10 100644 --- a/file_source_cache.go +++ b/file_source_cache.go @@ -1,25 +1,27 @@ package altsrc type FileSourceCache[T any] struct { - file string - m *T - f func(string, any) error + file string + m *T + unmarshaller func([]byte, any) error } -func NewFileSourceCache[T any](file string, f func(string, any) error) *FileSourceCache[T] { +func NewFileSourceCache[T any](file string, f func([]byte, any) error) *FileSourceCache[T] { return &FileSourceCache[T]{ - file: file, - f: f, + file: file, + unmarshaller: f, } } func (fsc *FileSourceCache[T]) Get() T { if fsc.m == nil { res := new(T) - if err := fsc.f(fsc.file, res); err == nil { - fsc.m = res - } else { + if b, err := ReadURI(fsc.file); err != nil { + tracef("failed to read uri %[1]q: %[2]v", fsc.file, err) + } else if err := fsc.unmarshaller(b, res); err != nil { tracef("failed to unmarshal from file %[1]q: %[2]v", fsc.file, err) + } else { + fsc.m = res } } @@ -34,6 +36,6 @@ func (fsc *FileSourceCache[T]) Get() T { type MapAnyAnyFileSourceCache = FileSourceCache[map[any]any] -func NewMapAnyAnyFileSourceCache(file string, f func(string, any) error) *MapAnyAnyFileSourceCache { +func NewMapAnyAnyFileSourceCache(file string, f func([]byte, any) error) *MapAnyAnyFileSourceCache { return NewFileSourceCache[map[any]any](file, f) } diff --git a/toml/toml_value_source.go b/toml/toml_value_source.go index 27e84df..1159c3b 100644 --- a/toml/toml_value_source.go +++ b/toml/toml_value_source.go @@ -21,7 +21,7 @@ func TOML(key string, paths ...string) cli.ValueSourceChain { &tomlValueSource{ file: path, key: key, - tmc: *altsrc.NewFileSourceCache[tomlMap](path, tomlUnmarshalFile), + tmc: *altsrc.NewFileSourceCache[tomlMap](path, toml.Unmarshal), }, ) } @@ -51,16 +51,3 @@ func (tvs *tomlValueSource) String() string { func (tvs *tomlValueSource) GoString() string { return fmt.Sprintf("&tomlValueSource{file:%[1]q,keyPath:%[2]q}", tvs.file, tvs.key) } - -func tomlUnmarshalFile(filePath string, container any) error { - b, err := altsrc.ReadURI(filePath) - if err != nil { - return err - } - - if err := toml.Unmarshal(b, container); err != nil { - return err - } - - return nil -} diff --git a/yaml/yaml_value_source.go b/yaml/yaml_value_source.go index 90e48c7..9af6f84 100644 --- a/yaml/yaml_value_source.go +++ b/yaml/yaml_value_source.go @@ -19,7 +19,7 @@ func YAML(key string, paths ...string) cli.ValueSourceChain { &yamlValueSource{ file: path, key: key, - maafsc: altsrc.NewMapAnyAnyFileSourceCache(path, yamlUnmarshalFile), + maafsc: altsrc.NewMapAnyAnyFileSourceCache(path, yaml.Unmarshal), }, ) } @@ -49,16 +49,3 @@ func (yvs *yamlValueSource) String() string { func (yvs *yamlValueSource) GoString() string { return fmt.Sprintf("&yamlValueSource{file:%[1]q,keyPath:%[2]q}", yvs.file, yvs.key) } - -func yamlUnmarshalFile(filePath string, container any) error { - b, err := altsrc.ReadURI(filePath) - if err != nil { - return err - } - - if err := yaml.Unmarshal(b, container); err != nil { - return err - } - - return nil -} From bc89bf297cd7e8c6c96d96ba735dbf2d159f5419 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 17 Nov 2024 10:27:55 -0500 Subject: [PATCH 4/7] Rename read URI --- altsrc.go | 2 +- file_source_cache.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/altsrc.go b/altsrc.go index 81a84b8..7703c89 100644 --- a/altsrc.go +++ b/altsrc.go @@ -45,7 +45,7 @@ func tracef(format string, a ...any) { ) } -func ReadURI(uriString string) ([]byte, error) { +func readURI(uriString string) ([]byte, error) { u, err := url.Parse(uriString) if err != nil { return nil, err diff --git a/file_source_cache.go b/file_source_cache.go index 8799c10..ecb384f 100644 --- a/file_source_cache.go +++ b/file_source_cache.go @@ -16,7 +16,7 @@ func NewFileSourceCache[T any](file string, f func([]byte, any) error) *FileSour func (fsc *FileSourceCache[T]) Get() T { if fsc.m == nil { res := new(T) - if b, err := ReadURI(fsc.file); err != nil { + if b, err := readURI(fsc.file); err != nil { tracef("failed to read uri %[1]q: %[2]v", fsc.file, err) } else if err := fsc.unmarshaller(b, res); err != nil { tracef("failed to unmarshal from file %[1]q: %[2]v", fsc.file, err) From 1ef912d5e724513d4ba3133274b7dd1531d7f4fa Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 17 Nov 2024 10:30:00 -0500 Subject: [PATCH 5/7] change to using altsrc.Err --- toml/toml_map.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/toml/toml_map.go b/toml/toml_map.go index 661fe4d..58232b3 100644 --- a/toml/toml_map.go +++ b/toml/toml_map.go @@ -3,6 +3,9 @@ package altsrc import ( "fmt" "reflect" + + altsrc "github.com/urfave/cli-altsrc/toml" + altsrc "github.com/urfave/cli-altsrc/v3" ) type tomlMap struct { @@ -62,7 +65,7 @@ func unmarshalMap(i any) (ret map[any]any, err error) { case reflect.Array, reflect.Slice: ret[key] = val.([]any) default: - return nil, fmt.Errorf("%[1]w: unsupported type %#[2]v", err, v.Kind()) + return nil, fmt.Errorf("%[1]w: unsupported type %#[2]v", altsrc.Err, v.Kind()) } } return ret, nil From 1b2a328245db3d5550326338f6168c2745be40d4 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 17 Nov 2024 11:47:15 -0500 Subject: [PATCH 6/7] Add tests --- altsrc_test.go | 128 ++++++++++++++++++++ go.mod | 10 +- go.sum | 4 - toml/examples_test.go | 2 +- toml/toml_map.go | 3 +- toml/toml_value_source.go | 2 +- toml/toml_value_source_test.go | 2 +- file_source_cache.go => uri_source_cache.go | 14 +-- uri_source_cache_test.go | 84 +++++++++++++ 9 files changed, 226 insertions(+), 23 deletions(-) rename file_source_cache.go => uri_source_cache.go (56%) create mode 100644 uri_source_cache_test.go diff --git a/altsrc_test.go b/altsrc_test.go index e502894..da28f94 100644 --- a/altsrc_test.go +++ b/altsrc_test.go @@ -2,7 +2,10 @@ package altsrc import ( "context" + "testing" "time" + + "github.com/stretchr/testify/assert" ) var ( @@ -13,3 +16,128 @@ var ( return MustTestdataDir(ctx) }() ) + +func TestNestedVal(t *testing.T) { + tests := []struct { + name string + key string + m map[any]any + val any + b bool + }{ + { + name: "No map no key", + }, + { + name: "No map with key", + key: "foo", + }, + { + name: "Empty map no key", + m: map[any]any{}, + }, + { + name: "Empty map with key", + key: "foo", + m: map[any]any{}, + }, + { + name: "Level 1 no key", + key: ".foob", + m: map[any]any{ + "foo": 10, + }, + }, + { + name: "Level 2", + key: "foo.bar", + m: map[any]any{ + "foo": map[any]any{ + "bar": 10, + }, + }, + val: 10, + b: true, + }, + { + name: "Level 2 invalid key", + key: "foo.bar1", + m: map[any]any{ + "foo": map[any]any{ + "bar": 10, + }, + }, + }, + { + name: "Level 3 no entry", + key: "foo.bar.t", + m: map[any]any{ + "foo": map[any]any{ + "bar": "sss", + }, + }, + }, + { + name: "Level 3", + key: "foo.bar.t", + m: map[any]any{ + "foo": map[any]any{ + "bar": map[any]any{ + "t": "sss", + }, + }, + }, + val: "sss", + b: true, + }, + { + name: "Level 3 invalid key", + key: "foo.bar.t", + m: map[any]any{ + "foo": map[any]any{ + "bar": map[any]any{ + "t1": 10, + }, + }, + }, + }, + { + name: "Level 4 no entry", + key: "foo.bar.t.gh", + m: map[any]any{ + "foo": map[any]any{ + "bar": map[any]any{ + "t1": 10, + }, + }, + }, + }, + { + name: "Level 4 slice entry", + key: "foo.bar.t.gh", + m: map[any]any{ + "foo": map[any]any{ + "bar": map[any]any{ + "t": map[any]any{ + "gh": []int{10}, + }, + }, + }, + }, + val: []int{10}, + b: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + val, b := NestedVal(test.key, test.m) + if !test.b { + assert.False(t, b) + } else { + assert.True(t, b) + assert.Equal(t, val, test.val) + } + }) + } +} diff --git a/go.mod b/go.mod index e4dc22e..a9c38ab 100644 --- a/go.mod +++ b/go.mod @@ -2,15 +2,11 @@ module github.com/urfave/cli-altsrc/v3 go 1.23.2 -require ( - github.com/urfave/cli-altsrc/json v0.0.1 - github.com/urfave/cli-altsrc/toml v0.0.1 - github.com/urfave/cli-altsrc/yaml v0.0.1 - github.com/urfave/cli/v3 v3.0.0-alpha9.3 -) +require github.com/stretchr/testify v1.9.0 require ( - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e1c2e72..60ce688 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,9 @@ -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli/v3 v3.0.0-alpha9.3 h1:RfQlgUHMRxDMwEEmGsrHd+mXYJpWpXlcJM8w86cpjGs= -github.com/urfave/cli/v3 v3.0.0-alpha9.3/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/toml/examples_test.go b/toml/examples_test.go index e39a1f4..d9cd138 100644 --- a/toml/examples_test.go +++ b/toml/examples_test.go @@ -1,4 +1,4 @@ -package altsrc_test +package toml_test import ( "context" diff --git a/toml/toml_map.go b/toml/toml_map.go index 58232b3..f70b8ac 100644 --- a/toml/toml_map.go +++ b/toml/toml_map.go @@ -1,10 +1,9 @@ -package altsrc +package toml import ( "fmt" "reflect" - altsrc "github.com/urfave/cli-altsrc/toml" altsrc "github.com/urfave/cli-altsrc/v3" ) diff --git a/toml/toml_value_source.go b/toml/toml_value_source.go index 1159c3b..994abcd 100644 --- a/toml/toml_value_source.go +++ b/toml/toml_value_source.go @@ -1,4 +1,4 @@ -package altsrc +package toml import ( "fmt" diff --git a/toml/toml_value_source_test.go b/toml/toml_value_source_test.go index e4ed744..6d17e76 100644 --- a/toml/toml_value_source_test.go +++ b/toml/toml_value_source_test.go @@ -1,4 +1,4 @@ -package altsrc +package toml import ( "context" diff --git a/file_source_cache.go b/uri_source_cache.go similarity index 56% rename from file_source_cache.go rename to uri_source_cache.go index ecb384f..fdbf1ee 100644 --- a/file_source_cache.go +++ b/uri_source_cache.go @@ -1,19 +1,19 @@ package altsrc -type FileSourceCache[T any] struct { +type URISourceCache[T any] struct { file string m *T unmarshaller func([]byte, any) error } -func NewFileSourceCache[T any](file string, f func([]byte, any) error) *FileSourceCache[T] { - return &FileSourceCache[T]{ +func NewURISourceCache[T any](file string, f func([]byte, any) error) *URISourceCache[T] { + return &URISourceCache[T]{ file: file, unmarshaller: f, } } -func (fsc *FileSourceCache[T]) Get() T { +func (fsc *URISourceCache[T]) Get() T { if fsc.m == nil { res := new(T) if b, err := readURI(fsc.file); err != nil { @@ -34,8 +34,8 @@ func (fsc *FileSourceCache[T]) Get() T { return *fsc.m } -type MapAnyAnyFileSourceCache = FileSourceCache[map[any]any] +type MapAnyAnyURISourceCache = URISourceCache[map[any]any] -func NewMapAnyAnyFileSourceCache(file string, f func([]byte, any) error) *MapAnyAnyFileSourceCache { - return NewFileSourceCache[map[any]any](file, f) +func NewMapAnyAnyURISourceCache(file string, f func([]byte, any) error) *MapAnyAnyURISourceCache { + return NewURISourceCache[map[any]any](file, f) } diff --git a/uri_source_cache_test.go b/uri_source_cache_test.go new file mode 100644 index 0000000..e859d77 --- /dev/null +++ b/uri_source_cache_test.go @@ -0,0 +1,84 @@ +package altsrc + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestURISourceCache(t *testing.T) { + tests := []struct { + name string + path string + contents string + unmarshaller func([]byte, any) error + m map[any]any + }{ + { + name: "no file", + }, + { + name: "invalid file", + path: "/path/to/sssss/junk/file", + }, + { + name: "invalid format", + contents: "junkddddd", + unmarshaller: func(b []byte, a any) error { + return fmt.Errorf("invalid format") + }, + }, + { + name: "invalid format return nil", + contents: "junkddddd", + unmarshaller: func(b []byte, a any) error { + if m, ok := a.(*map[any]any); !ok { + return fmt.Errorf("not correct map") + } else { + *m = nil + } + return nil + }, + }, + { + name: "valid format", + contents: "foo.bar.z=1", + unmarshaller: func(b []byte, a any) error { + if m, ok := a.(*map[any]any); !ok { + return fmt.Errorf("not correct map") + } else { + *m = map[any]any{} + (*m)["foo"] = map[any]any{ + "bar": map[any]any{ + "z": 1, + }, + } + } + return nil + }, + m: map[any]any{ + "foo": map[any]any{ + "bar": map[any]any{ + "z": 1, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + path := test.path + if test.contents != "" { + fp, err := os.CreateTemp(t.TempDir(), test.name) + assert.NoError(t, err) + path = fp.Name() + defer fp.Close() + } + sc := NewMapAnyAnyURISourceCache(path, test.unmarshaller) + assert.Equal(t, test.m, sc.Get()) + }) + } +} From 6bc58c0dfc95c998b427b57593faab06661b9644 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 17 Nov 2024 14:19:56 -0500 Subject: [PATCH 7/7] Add more tests --- altsrc.go | 31 ------------------------ uri_source_cache.go | 38 +++++++++++++++++++++++++++++ uri_source_cache_test.go | 52 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 31 deletions(-) diff --git a/altsrc.go b/altsrc.go index 7703c89..3f3a92d 100644 --- a/altsrc.go +++ b/altsrc.go @@ -3,9 +3,6 @@ package altsrc import ( "errors" "fmt" - "io" - "net/http" - "net/url" "os" "runtime" "strings" @@ -45,34 +42,6 @@ func tracef(format string, a ...any) { ) } -func readURI(uriString string) ([]byte, error) { - u, err := url.Parse(uriString) - if err != nil { - return nil, err - } - - if u.Host != "" { // i have a host, now do i support the scheme? - switch u.Scheme { - case "http", "https": - res, err := http.Get(uriString) - if err != nil { - return nil, err - } - return io.ReadAll(res.Body) - default: - return nil, fmt.Errorf("%[1]w: scheme of %[2]q is unsupported", Err, uriString) - } - } else if u.Path != "" || - (runtime.GOOS == "windows" && strings.Contains(u.String(), "\\")) { - if _, notFoundFileErr := os.Stat(uriString); notFoundFileErr != nil { - return nil, fmt.Errorf("%[1]w: cannot read from %[2]q because it does not exist", Err, uriString) - } - return os.ReadFile(uriString) - } - - return nil, fmt.Errorf("%[1]w: unable to determine how to load from %[2]q", Err, uriString) -} - // NestedVal checks if the name has '.' delimiters. // If so, it tries to traverse the tree by the '.' delimited sections to find // a nested value for the key. diff --git a/uri_source_cache.go b/uri_source_cache.go index fdbf1ee..5075f38 100644 --- a/uri_source_cache.go +++ b/uri_source_cache.go @@ -1,5 +1,43 @@ package altsrc +import ( + "fmt" + "io" + "net/http" + "net/url" + "os" + "runtime" + "strings" +) + +func readURI(uriString string) ([]byte, error) { + u, err := url.Parse(uriString) + if err != nil { + return nil, err + } + + if u.Host != "" { // i have a host, now do i support the scheme? + switch u.Scheme { + case "http", "https": + res, err := http.Get(uriString) + if err != nil { + return nil, err + } + return io.ReadAll(res.Body) + default: + return nil, fmt.Errorf("%[1]w: scheme of %[2]q is unsupported", Err, uriString) + } + } else if u.Path != "" || + (runtime.GOOS == "windows" && strings.Contains(u.String(), "\\")) { + if _, notFoundFileErr := os.Stat(uriString); notFoundFileErr != nil { + return nil, fmt.Errorf("%[1]w: cannot read from %[2]q because it does not exist", Err, uriString) + } + return os.ReadFile(uriString) + } + + return nil, fmt.Errorf("%[1]w: unable to determine how to load from %[2]q", Err, uriString) +} + type URISourceCache[T any] struct { file string m *T diff --git a/uri_source_cache_test.go b/uri_source_cache_test.go index e859d77..a41c23a 100644 --- a/uri_source_cache_test.go +++ b/uri_source_cache_test.go @@ -3,11 +3,63 @@ package altsrc import ( "fmt" "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" ) +func TestReadURI(t *testing.T) { + tests := []struct { + name string + path string + err string + }{ + { + name: "Empty path", + err: fmt.Sprintf("%s: unable to determine how to load from \"\"", Err), + }, + { + name: "Invalid file path", + path: filepath.Join(testdataDir, "action.zzzz"), + err: fmt.Sprintf("%s: cannot read from", Err), + }, + { + name: "valid file path", + path: filepath.Join(testdataDir, "alt-config.json"), + }, + { + name: "Unknown URI", + path: "ftp://github.com", + err: "is unsupported", + }, + { + name: "Invalid http URL", + path: "http://foo", + err: "no such host", + }, + { + name: "valid http URL", + path: "http://github.com", + }, + { + name: "valid https URL", + path: "https://github.com", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := readURI(test.path) + if test.err != "" { + assert.ErrorContains(t, err, test.err) + } else { + assert.NoError(t, err) + } + }) + } +} + func TestURISourceCache(t *testing.T) { tests := []struct { name string