From bd44aba1141b4109ba2228cb466be63ae45b6766 Mon Sep 17 00:00:00 2001 From: Hennik Hunsaker Date: Wed, 15 Mar 2023 16:15:59 -0600 Subject: [PATCH] Refactor to support proper types; add tests --- altsrc/default_input_source.go | 6 +- altsrc/detect_input_source.go | 38 +-- altsrc/detect_input_source_test.go | 314 ++++++++++++++++++ altsrc/flag.go | 68 ++-- altsrc/json_source_context.go | 20 +- altsrc/map_input_source.go | 4 +- altsrc/toml_file_loader.go | 10 +- altsrc/yaml_file_loader.go | 10 +- app.go | 9 +- godoc-current.txt | 167 +++++----- ...urce_context.go => input_source_context.go | 8 +- testdata/godoc-v2.x.txt | 167 +++++----- 12 files changed, 567 insertions(+), 254 deletions(-) create mode 100644 altsrc/detect_input_source_test.go rename altsrc/input_source_context.go => input_source_context.go (87%) diff --git a/altsrc/default_input_source.go b/altsrc/default_input_source.go index 7fda719be8..dc058940fd 100644 --- a/altsrc/default_input_source.go +++ b/altsrc/default_input_source.go @@ -1,6 +1,8 @@ package altsrc -// defaultInputSource creates a default InputSourceContext. -func defaultInputSource() (InputSourceContext, error) { +import "github.com/urfave/cli/v2" + +// defaultInputSource creates a default cli.InputSourceContext. +func defaultInputSource() (cli.InputSourceContext, error) { return &MapInputSource{file: "", valueMap: map[interface{}]interface{}{}}, nil } diff --git a/altsrc/detect_input_source.go b/altsrc/detect_input_source.go index fcdad6f305..d0f02d1273 100644 --- a/altsrc/detect_input_source.go +++ b/altsrc/detect_input_source.go @@ -3,12 +3,13 @@ package altsrc import ( "fmt" "path/filepath" + "sort" "github.com/urfave/cli/v2" ) // defaultSources is a read-only map, making it concurrency-safe -var defaultSources = map[string]func(string) func(*cli.Context) (InputSourceContext, error){ +var defaultSources = map[string]func(string) func(*cli.Context) (cli.InputSourceContext, error){ ".conf": NewTomlSourceFromFlagFunc, ".json": NewJSONSourceFromFlagFunc, ".toml": NewTomlSourceFromFlagFunc, @@ -16,9 +17,9 @@ var defaultSources = map[string]func(string) func(*cli.Context) (InputSourceCont ".yml": NewYamlSourceFromFlagFunc, } -// DetectNewSourceFromFlagFunc creates a new InputSourceContext from a provided flag name and source context. -func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (InputSourceContext, error) { - return func(cCtx *cli.Context) (InputSourceContext, error) { +// DetectNewSourceFromFlagFunc creates a new cli.InputSourceContext from a provided flag name and source context. +func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (cli.InputSourceContext, error) { + return func(cCtx *cli.Context) (cli.InputSourceContext, error) { detectableSources := cCtx.App.GetDetectableSources() if fileFullPath := cCtx.String(flagFileName); fileFullPath != "" { @@ -28,17 +29,7 @@ func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (InputS // Check if the App contains a handler for this extension first, allowing it to override the defaults if handler, ok := detectableSources[fileExt]; ok { - source, err := handler(flagFileName)(cCtx) - if err != nil { - return nil, err - } - - switch source := source.(type) { - case InputSourceContext: - return source, nil - default: - typeError = fmt.Errorf("Unable to parse config file. The type handler for %s is incorrectly implemented.", fileExt) - } + return handler(flagFileName)(cCtx) } // Fall back to the default sources implemented by the library itself @@ -57,17 +48,10 @@ func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (InputS } } -func detectableExtensions(detectableSources map[string]func(string) func(*cli.Context) (interface{}, error)) []string { - detectLen := len(detectableSources) - defaultLen := len(defaultSources) - - largerLen := defaultLen - if detectLen > defaultLen { - largerLen = detectLen - } - - // The App might override some file extensions, so set size to fit the larger of the two lists, with capacity for both in case it's needed - extensions := make([]string, largerLen, detectLen+defaultLen) +func detectableExtensions(detectableSources map[string]func(string) func(*cli.Context) (cli.InputSourceContext, error)) []string { + // We don't preallocate because this generates empty space in the output + // It's less efficient, but this is for error messaging only at the moment + var extensions []string for ext := range detectableSources { extensions = append(extensions, ext) @@ -79,5 +63,7 @@ func detectableExtensions(detectableSources map[string]func(string) func(*cli.Co } } + sort.Strings(extensions) + return extensions } diff --git a/altsrc/detect_input_source_test.go b/altsrc/detect_input_source_test.go new file mode 100644 index 0000000000..2b4361ed08 --- /dev/null +++ b/altsrc/detect_input_source_test.go @@ -0,0 +1,314 @@ +package altsrc + +import ( + "flag" + "fmt" + "os" + "testing" + + "github.com/urfave/cli/v2" +) + +func TestDetectsConfCorrectly(t *testing.T) { + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.conf", []byte("test = 15"), 0666) + defer os.Remove("current.conf") + test := []string{"test-cmd", "--load", "current.conf"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestDetectsJsonCorrectly(t *testing.T) { + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.json", []byte("{\"test\":15}"), 0666) + defer os.Remove("current.json") + test := []string{"test-cmd", "--load", "current.json"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestDetectsTomlCorrectly(t *testing.T) { + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.toml", []byte("test = 15"), 0666) + defer os.Remove("current.toml") + test := []string{"test-cmd", "--load", "current.toml"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestDetectsYamlCorrectly(t *testing.T) { + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.yaml", []byte("test: 15"), 0666) + defer os.Remove("current.yaml") + test := []string{"test-cmd", "--load", "current.yaml"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestDetectsYmlCorrectly(t *testing.T) { + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.yml", []byte("test: 15"), 0666) + defer os.Remove("current.yml") + test := []string{"test-cmd", "--load", "current.yml"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestHandlesCustomTypeCorrectly(t *testing.T) { + app := &cli.App{} + app.RegisterDetectableSource(".custom", NewYamlSourceFromFlagFunc) + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.custom", []byte("test: 15"), 0666) + defer os.Remove("current.custom") + test := []string{"test-cmd", "--load", "current.custom"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestAllowsOverrides(t *testing.T) { + app := &cli.App{} + app.RegisterDetectableSource(".conf", NewYamlSourceFromFlagFunc) + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.conf", []byte("test: 15"), 0666) + defer os.Remove("current.conf") + test := []string{"test-cmd", "--load", "current.conf"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestFailsOnUnrocegnized(t *testing.T) { + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.fake", []byte("test: 15"), 0666) + defer os.Remove("current.fake") + test := []string{"test-cmd", "--load", "current.fake"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 0) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, fmt.Errorf("Unable to create input source with context: inner error: \n'Unable to determine config file type from extension.\nMust be one of [.conf .json .toml .yaml .yml]'")) +} + +func TestSilentNoOpWithoutFlag(t *testing.T) { + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.conf", []byte("test = 15"), 0666) + defer os.Remove("current.conf") + test := []string{"test-cmd"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 0) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} + +func TestLoadDefaultConfig(t *testing.T) { + t.Skip("Check parent implementation for default values to get this working") + + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + _ = os.WriteFile("current.conf", []byte("test = 15"), 0666) + defer os.Remove("current.conf") + test := []string{"test-cmd"} + _ = set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load", Value: "current.conf"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, DetectNewSourceFromFlagFunc("load")) + err := command.Run(c, test...) + + expect(t, err, nil) +} diff --git a/altsrc/flag.go b/altsrc/flag.go index 084ba3f9e1..a9ae5bd888 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -13,14 +13,14 @@ import ( // allows a value to be set on the existing parsed flags. type FlagInputSourceExtension interface { cli.Flag - ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error } // ApplyInputSourceValues iterates over all provided flags and // executes ApplyInputSourceValue on flags implementing the // FlagInputSourceExtension interface to initialize these flags // to an alternate input source. -func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error { +func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext cli.InputSourceContext, flags []cli.Flag) error { for _, f := range flags { inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension) if isType { @@ -34,10 +34,10 @@ func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceCon return nil } -// InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new +// InitInputSource is used to to setup an cli.InputSourceContext on a cli.Command Before method. It will create a new // input source based on the func provided. If there is no error it will then apply the new input source to any flags // that are supported by the input source -func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc { +func InitInputSource(flags []cli.Flag, createInputSource func() (cli.InputSourceContext, error)) cli.BeforeFunc { return func(cCtx *cli.Context) error { inputSource, err := createInputSource() if err != nil { @@ -48,10 +48,10 @@ func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceCont } } -// InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new +// InitInputSourceWithContext is used to to setup an cli.InputSourceContext on a cli.Command Before method. It will create a new // input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is // no error it will then apply the new input source to any flags that are supported by the input source -func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc { +func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (cli.InputSourceContext, error)) cli.BeforeFunc { return func(cCtx *cli.Context) error { inputSource, err := createInputSource(cCtx) if err != nil { @@ -63,12 +63,12 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *c } // ApplyInputSourceValue applies a generic value to the flagSet if required -func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.GenericFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Generic(name) @@ -87,12 +87,12 @@ func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo } // ApplyInputSourceValue applies a StringSlice value to the flagSet if required -func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.StringSliceFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.StringSlice(name) @@ -119,12 +119,12 @@ func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSour } // ApplyInputSourceValue applies a IntSlice value if required -func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.IntSliceFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.IntSlice(name) @@ -150,12 +150,12 @@ func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC } // ApplyInputSourceValue applies a Int64Slice value if required -func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.Int64SliceFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Int64Slice(name) @@ -181,12 +181,12 @@ func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourc } // ApplyInputSourceValue applies a Float64Slice value if required -func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.Float64SliceFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Float64Slice(name) @@ -212,12 +212,12 @@ func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSou } // ApplyInputSourceValue applies a Bool value to the flagSet if required -func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.BoolFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Bool(name) @@ -232,12 +232,12 @@ func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte } // ApplyInputSourceValue applies a String value to the flagSet if required -func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.StringFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.String(name) @@ -252,12 +252,12 @@ func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCon } // ApplyInputSourceValue applies a Path value to the flagSet if required -func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.PathFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.String(name) @@ -282,12 +282,12 @@ func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte } // ApplyInputSourceValue applies a int value to the flagSet if required -func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.IntFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Int(name) @@ -301,12 +301,12 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex return nil } -func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.Int64Flag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Int64(name) @@ -320,12 +320,12 @@ func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCont return nil } -func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.UintFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Uint(name) @@ -339,12 +339,12 @@ func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte return nil } -func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.Uint64Flag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Uint64(name) @@ -359,12 +359,12 @@ func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCon } // ApplyInputSourceValue applies a Duration value to the flagSet if required -func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.DurationFlag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Duration(name) @@ -379,12 +379,12 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC } // ApplyInputSourceValue applies a Float64 value to the flagSet if required -func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { +func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error { if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { return nil } for _, name := range f.Float64Flag.Names() { - if !isc.isSet(name) { + if !isc.IsSet(name) { continue } value, err := isc.Float64(name) diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index aef357b7be..6f9f36dca6 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -11,11 +11,11 @@ import ( ) // NewJSONSourceFromFlagFunc returns a func that takes a cli.Context -// and returns an InputSourceContext suitable for retrieving config +// and returns an cli.InputSourceContext suitable for retrieving config // variables from a file containing JSON data with the file name defined // by the given flag. -func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) { - return func(cCtx *cli.Context) (InputSourceContext, error) { +func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (cli.InputSourceContext, error) { + return func(cCtx *cli.Context) (cli.InputSourceContext, error) { if cCtx.IsSet(flag) { return NewJSONSourceFromFile(cCtx.String(flag)) } @@ -24,10 +24,10 @@ func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceCon } } -// NewJSONSourceFromFile returns an InputSourceContext suitable for +// NewJSONSourceFromFile returns an cli.InputSourceContext suitable for // retrieving config variables from a file (or url) containing JSON // data. -func NewJSONSourceFromFile(f string) (InputSourceContext, error) { +func NewJSONSourceFromFile(f string) (cli.InputSourceContext, error) { data, err := loadDataFrom(f) if err != nil { return nil, err @@ -36,9 +36,9 @@ func NewJSONSourceFromFile(f string) (InputSourceContext, error) { return NewJSONSource(data) } -// NewJSONSourceFromReader returns an InputSourceContext suitable for +// NewJSONSourceFromReader returns an cli.InputSourceContext suitable for // retrieving config variables from an io.Reader that returns JSON data. -func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) { +func NewJSONSourceFromReader(r io.Reader) (cli.InputSourceContext, error) { data, err := io.ReadAll(r) if err != nil { return nil, err @@ -46,9 +46,9 @@ func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) { return NewJSONSource(data) } -// NewJSONSource returns an InputSourceContext suitable for retrieving +// NewJSONSource returns an cli.InputSourceContext suitable for retrieving // config variables from raw JSON data. -func NewJSONSource(data []byte) (InputSourceContext, error) { +func NewJSONSource(data []byte) (cli.InputSourceContext, error) { var deserialized map[string]interface{} if err := json.Unmarshal(data, &deserialized); err != nil { return nil, err @@ -286,7 +286,7 @@ func (x *jsonSource) Bool(name string) (bool, error) { return v, nil } -func (x *jsonSource) isSet(name string) bool { +func (x *jsonSource) IsSet(name string) bool { _, err := x.getValue(name) return err == nil } diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index abd989f0d4..a579bf0abd 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli/v2" ) -// MapInputSource implements InputSourceContext to return +// MapInputSource implements cli.InputSourceContext to return // data from the map that is loaded. type MapInputSource struct { file string @@ -461,7 +461,7 @@ func (fsm *MapInputSource) Bool(name string) (bool, error) { return false, nil } -func (fsm *MapInputSource) isSet(name string) bool { +func (fsm *MapInputSource) IsSet(name string) bool { if _, exists := fsm.valueMap[name]; exists { return exists } diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index dfc9b7b1c8..07fe3cb547 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -74,8 +74,8 @@ type tomlSourceContext struct { FilePath string } -// NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. -func NewTomlSourceFromFile(file string) (InputSourceContext, error) { +// NewTomlSourceFromFile creates a new TOML cli.InputSourceContext from a filepath. +func NewTomlSourceFromFile(file string) (cli.InputSourceContext, error) { tsc := &tomlSourceContext{FilePath: file} var results tomlMap = tomlMap{} if err := readCommandToml(tsc.FilePath, &results); err != nil { @@ -84,9 +84,9 @@ func NewTomlSourceFromFile(file string) (InputSourceContext, error) { return &MapInputSource{file: file, valueMap: results.Map}, nil } -// NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. -func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) { - return func(cCtx *cli.Context) (InputSourceContext, error) { +// NewTomlSourceFromFlagFunc creates a new TOML cli.InputSourceContext from a provided flag name and source context. +func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (cli.InputSourceContext, error) { + return func(cCtx *cli.Context) (cli.InputSourceContext, error) { if cCtx.IsSet(flagFileName) { filePath := cCtx.String(flagFileName) return NewTomlSourceFromFile(filePath) diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index 391069541f..b0675d6b1e 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -18,8 +18,8 @@ type yamlSourceContext struct { FilePath string } -// NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. -func NewYamlSourceFromFile(file string) (InputSourceContext, error) { +// NewYamlSourceFromFile creates a new Yaml cli.InputSourceContext from a filepath. +func NewYamlSourceFromFile(file string) (cli.InputSourceContext, error) { ysc := &yamlSourceContext{FilePath: file} var results map[interface{}]interface{} err := readCommandYaml(ysc.FilePath, &results) @@ -30,9 +30,9 @@ func NewYamlSourceFromFile(file string) (InputSourceContext, error) { return &MapInputSource{file: file, valueMap: results}, nil } -// NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context. -func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) { - return func(cCtx *cli.Context) (InputSourceContext, error) { +// NewYamlSourceFromFlagFunc creates a new Yaml cli.InputSourceContext from a provided flag name and source context. +func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (cli.InputSourceContext, error) { + return func(cCtx *cli.Context) (cli.InputSourceContext, error) { if filePath := cCtx.String(flagFileName); filePath != "" { return NewYamlSourceFromFile(filePath) } diff --git a/app.go b/app.go index 05a3b0097d..1fad5c2080 100644 --- a/app.go +++ b/app.go @@ -123,7 +123,7 @@ type App struct { didSetup bool separator separatorSpec - detectableSources map[string]func(string) func(*Context) (interface{}, error) + detectableSources map[string]func(string) func(*Context) (InputSourceContext, error) rootCommand *Command } @@ -305,12 +305,15 @@ func (a *App) useShortOptionHandling() bool { } // RegisterDetectableSource lets developers add support for their own altsrc filetypes to the autodetection list. -func (a *App) RegisterDetectableSource(extension string, handler func(string) func(*Context) (interface{}, error)) { +func (a *App) RegisterDetectableSource(extension string, handler func(string) func(*Context) (InputSourceContext, error)) { + if a.detectableSources == nil { + a.detectableSources = make(map[string]func(string) func(*Context) (InputSourceContext, error)) + } a.detectableSources[extension] = handler } // GetDetectableSources is used internally to get the list of registered sources. -func (a *App) GetDetectableSources() map[string]func(string) func(*Context) (interface{}, error) { +func (a *App) GetDetectableSources() map[string]func(string) func(*Context) (InputSourceContext, error) { return a.detectableSources } diff --git a/godoc-current.txt b/godoc-current.txt index f232899983..b06d45766b 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -345,11 +345,11 @@ func (a *App) Command(name string) *Command Command returns the named command on App. Returns nil if the command does not exist -func (a *App) GetDetectableSources() map[string]func(string) func(*Context) (interface{}, error) +func (a *App) GetDetectableSources() map[string]func(string) func(*Context) (InputSourceContext, error) GetDetectableSources is used internally to get the list of registered sources. -func (a *App) RegisterDetectableSource(extension string, handler func(string) func(*Context) (interface{}, error)) +func (a *App) RegisterDetectableSource(extension string, handler func(string) func(*Context) (InputSourceContext, error)) RegisterDetectableSource lets developers add support for their own altsrc filetypes to the autodetection list. @@ -1200,6 +1200,31 @@ func (f *GenericFlag) String() string func (f *GenericFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +type InputSourceContext interface { + Source() string + + Int(name string) (int, error) + Int64(name string) (int64, error) + Uint(name string) (uint, error) + Uint64(name string) (uint64, error) + Duration(name string) (time.Duration, error) + Float64(name string) (float64, error) + String(name string) (string, error) + StringSlice(name string) ([]string, error) + IntSlice(name string) ([]int, error) + Int64Slice(name string) ([]int64, error) + Float64Slice(name string) ([]float64, error) + Generic(name string) (Generic, error) + Bool(name string) (bool, error) + + IsSet(name string) bool +} + InputSourceContext is an interface used to allow other input sources to be + implemented as needed. + + Source returns an identifier for the input source. In case of file source it + should return path to the file. + type Int64Flag struct { Name string @@ -2345,39 +2370,60 @@ package altsrc // import "github.com/urfave/cli/v2/altsrc" FUNCTIONS -func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error +func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext cli.InputSourceContext, flags []cli.Flag) error ApplyInputSourceValues iterates over all provided flags and executes ApplyInputSourceValue on flags implementing the FlagInputSourceExtension interface to initialize these flags to an alternate input source. -func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (InputSourceContext, error) - DetectNewSourceFromFlagFunc creates a new InputSourceContext from a provided - flag name and source context. +func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (cli.InputSourceContext, error) + DetectNewSourceFromFlagFunc creates a new cli.InputSourceContext from a + provided flag name and source context. -func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc - InitInputSource is used to to setup an InputSourceContext on a cli.Command - Before method. It will create a new input source based on the func provided. - If there is no error it will then apply the new input source to any flags - that are supported by the input source +func InitInputSource(flags []cli.Flag, createInputSource func() (cli.InputSourceContext, error)) cli.BeforeFunc + InitInputSource is used to to setup an cli.InputSourceContext on a + cli.Command Before method. It will create a new input source based on the + func provided. If there is no error it will then apply the new input source + to any flags that are supported by the input source -func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc - InitInputSourceWithContext is used to to setup an InputSourceContext on - a cli.Command Before method. It will create a new input source based on - the func provided with potentially using existing cli.Context values to +func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (cli.InputSourceContext, error)) cli.BeforeFunc + InitInputSourceWithContext is used to to setup an cli.InputSourceContext + on a cli.Command Before method. It will create a new input source based + on the func provided with potentially using existing cli.Context values to initialize itself. If there is no error it will then apply the new input source to any flags that are supported by the input source -func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) +func NewJSONSource(data []byte) (cli.InputSourceContext, error) + NewJSONSource returns an cli.InputSourceContext suitable for retrieving + config variables from raw JSON data. + +func NewJSONSourceFromFile(f string) (cli.InputSourceContext, error) + NewJSONSourceFromFile returns an cli.InputSourceContext suitable for + retrieving config variables from a file (or url) containing JSON data. + +func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (cli.InputSourceContext, error) NewJSONSourceFromFlagFunc returns a func that takes a cli.Context and - returns an InputSourceContext suitable for retrieving config variables from - a file containing JSON data with the file name defined by the given flag. + returns an cli.InputSourceContext suitable for retrieving config variables + from a file containing JSON data with the file name defined by the given + flag. -func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) - NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a +func NewJSONSourceFromReader(r io.Reader) (cli.InputSourceContext, error) + NewJSONSourceFromReader returns an cli.InputSourceContext suitable for + retrieving config variables from an io.Reader that returns JSON data. + +func NewTomlSourceFromFile(file string) (cli.InputSourceContext, error) + NewTomlSourceFromFile creates a new TOML cli.InputSourceContext from a + filepath. + +func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (cli.InputSourceContext, error) + NewTomlSourceFromFlagFunc creates a new TOML cli.InputSourceContext from a provided flag name and source context. -func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) - NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a +func NewYamlSourceFromFile(file string) (cli.InputSourceContext, error) + NewYamlSourceFromFile creates a new Yaml cli.InputSourceContext from a + filepath. + +func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (cli.InputSourceContext, error) + NewYamlSourceFromFlagFunc creates a new Yaml cli.InputSourceContext from a provided flag name and source context. @@ -2397,7 +2443,7 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped BoolFlag.Apply -func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Bool value to the flagSet if required type DurationFlag struct { @@ -2414,12 +2460,12 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped DurationFlag.Apply -func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Duration value to the flagSet if required type FlagInputSourceExtension interface { cli.Flag - ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error } FlagInputSourceExtension is an extension interface of cli.Flag that allows a value to be set on the existing parsed flags. @@ -2438,7 +2484,7 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Float64Flag.Apply -func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Float64 value to the flagSet if required type Float64SliceFlag struct { @@ -2455,7 +2501,7 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Float64SliceFlag.Apply -func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Float64Slice value if required type GenericFlag struct { @@ -2472,52 +2518,9 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped GenericFlag.Apply -func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a generic value to the flagSet if required -type InputSourceContext interface { - Source() string - - Int(name string) (int, error) - Int64(name string) (int64, error) - Uint(name string) (uint, error) - Uint64(name string) (uint64, error) - Duration(name string) (time.Duration, error) - Float64(name string) (float64, error) - String(name string) (string, error) - StringSlice(name string) ([]string, error) - IntSlice(name string) ([]int, error) - Int64Slice(name string) ([]int64, error) - Float64Slice(name string) ([]float64, error) - Generic(name string) (cli.Generic, error) - Bool(name string) (bool, error) - - // Has unexported methods. -} - InputSourceContext is an interface used to allow other input sources to be - implemented as needed. - - Source returns an identifier for the input source. In case of file source it - should return path to the file. - -func NewJSONSource(data []byte) (InputSourceContext, error) - NewJSONSource returns an InputSourceContext suitable for retrieving config - variables from raw JSON data. - -func NewJSONSourceFromFile(f string) (InputSourceContext, error) - NewJSONSourceFromFile returns an InputSourceContext suitable for retrieving - config variables from a file (or url) containing JSON data. - -func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) - NewJSONSourceFromReader returns an InputSourceContext suitable for - retrieving config variables from an io.Reader that returns JSON data. - -func NewTomlSourceFromFile(file string) (InputSourceContext, error) - NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. - -func NewYamlSourceFromFile(file string) (InputSourceContext, error) - NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. - type Int64Flag struct { *cli.Int64Flag // Has unexported fields. @@ -2532,7 +2535,7 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64Flag.Apply -func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error type Int64SliceFlag struct { *cli.Int64SliceFlag @@ -2548,7 +2551,7 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64SliceFlag.Apply -func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Int64Slice value if required type IntFlag struct { @@ -2565,7 +2568,7 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped IntFlag.Apply -func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a int value to the flagSet if required type IntSliceFlag struct { @@ -2582,13 +2585,13 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped IntSliceFlag.Apply -func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a IntSlice value if required type MapInputSource struct { // Has unexported fields. } - MapInputSource implements InputSourceContext to return data from the map + MapInputSource implements cli.InputSourceContext to return data from the map that is loaded. func NewMapInputSource(file string, valueMap map[interface{}]interface{}) *MapInputSource @@ -2625,6 +2628,8 @@ func (fsm *MapInputSource) Int64Slice(name string) ([]int64, error) func (fsm *MapInputSource) IntSlice(name string) ([]int, error) IntSlice returns an []int from the map if it exists otherwise returns nil +func (fsm *MapInputSource) IsSet(name string) bool + func (fsm *MapInputSource) Source() string Source returns the path of the source file @@ -2656,7 +2661,7 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped PathFlag.Apply -func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Path value to the flagSet if required type StringFlag struct { @@ -2673,7 +2678,7 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped StringFlag.Apply -func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a String value to the flagSet if required type StringSliceFlag struct { @@ -2690,7 +2695,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped StringSliceFlag.Apply -func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a StringSlice value to the flagSet if required type Uint64Flag struct { @@ -2707,7 +2712,7 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Uint64Flag.Apply -func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error type UintFlag struct { *cli.UintFlag @@ -2723,5 +2728,5 @@ func (f *UintFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped UintFlag.Apply -func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error diff --git a/altsrc/input_source_context.go b/input_source_context.go similarity index 87% rename from altsrc/input_source_context.go rename to input_source_context.go index 8e6293aeb0..856d564501 100644 --- a/altsrc/input_source_context.go +++ b/input_source_context.go @@ -1,9 +1,7 @@ -package altsrc +package cli import ( "time" - - "github.com/urfave/cli/v2" ) // InputSourceContext is an interface used to allow @@ -25,8 +23,8 @@ type InputSourceContext interface { IntSlice(name string) ([]int, error) Int64Slice(name string) ([]int64, error) Float64Slice(name string) ([]float64, error) - Generic(name string) (cli.Generic, error) + Generic(name string) (Generic, error) Bool(name string) (bool, error) - isSet(name string) bool + IsSet(name string) bool } diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index f232899983..b06d45766b 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -345,11 +345,11 @@ func (a *App) Command(name string) *Command Command returns the named command on App. Returns nil if the command does not exist -func (a *App) GetDetectableSources() map[string]func(string) func(*Context) (interface{}, error) +func (a *App) GetDetectableSources() map[string]func(string) func(*Context) (InputSourceContext, error) GetDetectableSources is used internally to get the list of registered sources. -func (a *App) RegisterDetectableSource(extension string, handler func(string) func(*Context) (interface{}, error)) +func (a *App) RegisterDetectableSource(extension string, handler func(string) func(*Context) (InputSourceContext, error)) RegisterDetectableSource lets developers add support for their own altsrc filetypes to the autodetection list. @@ -1200,6 +1200,31 @@ func (f *GenericFlag) String() string func (f *GenericFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +type InputSourceContext interface { + Source() string + + Int(name string) (int, error) + Int64(name string) (int64, error) + Uint(name string) (uint, error) + Uint64(name string) (uint64, error) + Duration(name string) (time.Duration, error) + Float64(name string) (float64, error) + String(name string) (string, error) + StringSlice(name string) ([]string, error) + IntSlice(name string) ([]int, error) + Int64Slice(name string) ([]int64, error) + Float64Slice(name string) ([]float64, error) + Generic(name string) (Generic, error) + Bool(name string) (bool, error) + + IsSet(name string) bool +} + InputSourceContext is an interface used to allow other input sources to be + implemented as needed. + + Source returns an identifier for the input source. In case of file source it + should return path to the file. + type Int64Flag struct { Name string @@ -2345,39 +2370,60 @@ package altsrc // import "github.com/urfave/cli/v2/altsrc" FUNCTIONS -func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error +func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext cli.InputSourceContext, flags []cli.Flag) error ApplyInputSourceValues iterates over all provided flags and executes ApplyInputSourceValue on flags implementing the FlagInputSourceExtension interface to initialize these flags to an alternate input source. -func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (InputSourceContext, error) - DetectNewSourceFromFlagFunc creates a new InputSourceContext from a provided - flag name and source context. +func DetectNewSourceFromFlagFunc(flagFileName string) func(*cli.Context) (cli.InputSourceContext, error) + DetectNewSourceFromFlagFunc creates a new cli.InputSourceContext from a + provided flag name and source context. -func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc - InitInputSource is used to to setup an InputSourceContext on a cli.Command - Before method. It will create a new input source based on the func provided. - If there is no error it will then apply the new input source to any flags - that are supported by the input source +func InitInputSource(flags []cli.Flag, createInputSource func() (cli.InputSourceContext, error)) cli.BeforeFunc + InitInputSource is used to to setup an cli.InputSourceContext on a + cli.Command Before method. It will create a new input source based on the + func provided. If there is no error it will then apply the new input source + to any flags that are supported by the input source -func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc - InitInputSourceWithContext is used to to setup an InputSourceContext on - a cli.Command Before method. It will create a new input source based on - the func provided with potentially using existing cli.Context values to +func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (cli.InputSourceContext, error)) cli.BeforeFunc + InitInputSourceWithContext is used to to setup an cli.InputSourceContext + on a cli.Command Before method. It will create a new input source based + on the func provided with potentially using existing cli.Context values to initialize itself. If there is no error it will then apply the new input source to any flags that are supported by the input source -func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) +func NewJSONSource(data []byte) (cli.InputSourceContext, error) + NewJSONSource returns an cli.InputSourceContext suitable for retrieving + config variables from raw JSON data. + +func NewJSONSourceFromFile(f string) (cli.InputSourceContext, error) + NewJSONSourceFromFile returns an cli.InputSourceContext suitable for + retrieving config variables from a file (or url) containing JSON data. + +func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (cli.InputSourceContext, error) NewJSONSourceFromFlagFunc returns a func that takes a cli.Context and - returns an InputSourceContext suitable for retrieving config variables from - a file containing JSON data with the file name defined by the given flag. + returns an cli.InputSourceContext suitable for retrieving config variables + from a file containing JSON data with the file name defined by the given + flag. -func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) - NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a +func NewJSONSourceFromReader(r io.Reader) (cli.InputSourceContext, error) + NewJSONSourceFromReader returns an cli.InputSourceContext suitable for + retrieving config variables from an io.Reader that returns JSON data. + +func NewTomlSourceFromFile(file string) (cli.InputSourceContext, error) + NewTomlSourceFromFile creates a new TOML cli.InputSourceContext from a + filepath. + +func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (cli.InputSourceContext, error) + NewTomlSourceFromFlagFunc creates a new TOML cli.InputSourceContext from a provided flag name and source context. -func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) - NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a +func NewYamlSourceFromFile(file string) (cli.InputSourceContext, error) + NewYamlSourceFromFile creates a new Yaml cli.InputSourceContext from a + filepath. + +func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (cli.InputSourceContext, error) + NewYamlSourceFromFlagFunc creates a new Yaml cli.InputSourceContext from a provided flag name and source context. @@ -2397,7 +2443,7 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped BoolFlag.Apply -func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Bool value to the flagSet if required type DurationFlag struct { @@ -2414,12 +2460,12 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped DurationFlag.Apply -func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Duration value to the flagSet if required type FlagInputSourceExtension interface { cli.Flag - ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error } FlagInputSourceExtension is an extension interface of cli.Flag that allows a value to be set on the existing parsed flags. @@ -2438,7 +2484,7 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Float64Flag.Apply -func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Float64 value to the flagSet if required type Float64SliceFlag struct { @@ -2455,7 +2501,7 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Float64SliceFlag.Apply -func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Float64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Float64Slice value if required type GenericFlag struct { @@ -2472,52 +2518,9 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped GenericFlag.Apply -func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a generic value to the flagSet if required -type InputSourceContext interface { - Source() string - - Int(name string) (int, error) - Int64(name string) (int64, error) - Uint(name string) (uint, error) - Uint64(name string) (uint64, error) - Duration(name string) (time.Duration, error) - Float64(name string) (float64, error) - String(name string) (string, error) - StringSlice(name string) ([]string, error) - IntSlice(name string) ([]int, error) - Int64Slice(name string) ([]int64, error) - Float64Slice(name string) ([]float64, error) - Generic(name string) (cli.Generic, error) - Bool(name string) (bool, error) - - // Has unexported methods. -} - InputSourceContext is an interface used to allow other input sources to be - implemented as needed. - - Source returns an identifier for the input source. In case of file source it - should return path to the file. - -func NewJSONSource(data []byte) (InputSourceContext, error) - NewJSONSource returns an InputSourceContext suitable for retrieving config - variables from raw JSON data. - -func NewJSONSourceFromFile(f string) (InputSourceContext, error) - NewJSONSourceFromFile returns an InputSourceContext suitable for retrieving - config variables from a file (or url) containing JSON data. - -func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) - NewJSONSourceFromReader returns an InputSourceContext suitable for - retrieving config variables from an io.Reader that returns JSON data. - -func NewTomlSourceFromFile(file string) (InputSourceContext, error) - NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. - -func NewYamlSourceFromFile(file string) (InputSourceContext, error) - NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. - type Int64Flag struct { *cli.Int64Flag // Has unexported fields. @@ -2532,7 +2535,7 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64Flag.Apply -func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Int64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error type Int64SliceFlag struct { *cli.Int64SliceFlag @@ -2548,7 +2551,7 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Int64SliceFlag.Apply -func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Int64SliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Int64Slice value if required type IntFlag struct { @@ -2565,7 +2568,7 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped IntFlag.Apply -func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a int value to the flagSet if required type IntSliceFlag struct { @@ -2582,13 +2585,13 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped IntSliceFlag.Apply -func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a IntSlice value if required type MapInputSource struct { // Has unexported fields. } - MapInputSource implements InputSourceContext to return data from the map + MapInputSource implements cli.InputSourceContext to return data from the map that is loaded. func NewMapInputSource(file string, valueMap map[interface{}]interface{}) *MapInputSource @@ -2625,6 +2628,8 @@ func (fsm *MapInputSource) Int64Slice(name string) ([]int64, error) func (fsm *MapInputSource) IntSlice(name string) ([]int, error) IntSlice returns an []int from the map if it exists otherwise returns nil +func (fsm *MapInputSource) IsSet(name string) bool + func (fsm *MapInputSource) Source() string Source returns the path of the source file @@ -2656,7 +2661,7 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped PathFlag.Apply -func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a Path value to the flagSet if required type StringFlag struct { @@ -2673,7 +2678,7 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped StringFlag.Apply -func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a String value to the flagSet if required type StringSliceFlag struct { @@ -2690,7 +2695,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped StringSliceFlag.Apply -func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error ApplyInputSourceValue applies a StringSlice value to the flagSet if required type Uint64Flag struct { @@ -2707,7 +2712,7 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped Uint64Flag.Apply -func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *Uint64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error type UintFlag struct { *cli.UintFlag @@ -2723,5 +2728,5 @@ func (f *UintFlag) Apply(set *flag.FlagSet) error Apply saves the flagSet for later usage calls, then calls the wrapped UintFlag.Apply -func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +func (f *UintFlag) ApplyInputSourceValue(cCtx *cli.Context, isc cli.InputSourceContext) error