Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Required parameter to flag structs #242

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 12 additions & 4 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,22 @@ func (c Command) Run(ctx *Context) error {
return err
}

nerr := normalizeFlags(c.Flags, set)
if nerr != nil {
fmt.Fprintln(ctx.App.Writer, nerr)
if err = normalizeFlags(c.Flags, set); err != nil {
fmt.Fprintln(ctx.App.Writer, err)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
fmt.Fprintln(ctx.App.Writer)
return nerr
return err
}

if err = validateFlags(c.Flags, set); err != nil {
fmt.Fprintln(ctx.App.Writer, err)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
fmt.Fprintln(ctx.App.Writer)
return err
}

context := NewContext(ctx.App, set, ctx)

if checkCommandCompletions(context, c.Name) {
Expand Down
26 changes: 26 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ func TestCommandDoNotIgnoreFlags(t *testing.T) {
expect(t, err.Error(), "flag provided but not defined: -break")
}

func TestCommandRequiredFlagMissing(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
test := []string{"blah", "blah"}
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",
Flags: []cli.Flag{
cli.StringFlag{
Name: "break",
Required: true,
},
},
Action: func(_ *cli.Context) {},
}
err := command.Run(c)

expect(t, err.Error(), "could not locate required flag: -break")
}

func TestCommandIgnoreFlags(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
Expand Down
27 changes: 26 additions & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
name = strings.Trim(name, " ")
if visited[name] {
if ff != nil {
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
return errors.New("cannot use two forms of the same flag: " + name + " " + ff.Name)
}
ff = set.Lookup(name)
}
Expand All @@ -374,3 +374,28 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
}
return nil
}

func validateFlags(flags []Flag, set *flag.FlagSet) error {
visited := make(map[string]bool)
set.Visit(func(f *flag.Flag) {
visited[f.Name] = true
})

for _, flag := range flags {
found := false
nameParts := strings.Split(flag.getName(), ",")

for _, name := range nameParts {
name = strings.Trim(name, " ")
if found == false {
found = visited[name]
}
}

if flag.isRequired() && found == false {
return errors.New("could not locate required flag: -" + flag.getName())
}
}

return nil
}
114 changes: 80 additions & 34 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Flag interface {
// Apply Flag settings to the given flag set
Apply(*flag.FlagSet)
getName() string
isRequired() bool
}

func flagSet(name string, flags []Flag) *flag.FlagSet {
Expand Down Expand Up @@ -63,10 +64,11 @@ type Generic interface {

// GenericFlag is the flag type for types implementing Generic
type GenericFlag struct {
Name string
Value Generic
Usage string
EnvVar string
Name string
Value Generic
Usage string
EnvVar string
Required bool
}

// String returns the string representation of the generic flag to display the
Expand All @@ -76,6 +78,10 @@ func (f GenericFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s \"%v\"\t%v", prefixFor(f.Name), f.Name, f.Value, f.Usage))
}

func (f GenericFlag) isRequired() bool {
return f.Required
}

// Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
func (f GenericFlag) Apply(set *flag.FlagSet) {
Expand Down Expand Up @@ -121,10 +127,11 @@ func (f *StringSlice) Value() []string {
// StringSlice is a string flag that can be specified multiple times on the
// command-line
type StringSliceFlag struct {
Name string
Value *StringSlice
Usage string
EnvVar string
Name string
Value *StringSlice
Usage string
EnvVar string
Required bool
}

// String returns the usage
Expand All @@ -134,6 +141,10 @@ func (f StringSliceFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
}

func (f StringSliceFlag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
Expand Down Expand Up @@ -190,10 +201,11 @@ func (f *IntSlice) Value() []int {
// IntSliceFlag is an int flag that can be specified multiple times on the
// command-line
type IntSliceFlag struct {
Name string
Value *IntSlice
Usage string
EnvVar string
Name string
Value *IntSlice
Usage string
EnvVar string
Required bool
}

// String returns the usage
Expand All @@ -203,6 +215,10 @@ func (f IntSliceFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
}

func (f IntSliceFlag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
Expand Down Expand Up @@ -237,16 +253,21 @@ func (f IntSliceFlag) getName() string {

// BoolFlag is a switch that defaults to false
type BoolFlag struct {
Name string
Usage string
EnvVar string
Name string
Usage string
EnvVar string
Required bool
}

// String returns a readable representation of this value (for usage defaults)
func (f BoolFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
}

func (f BoolFlag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f BoolFlag) Apply(set *flag.FlagSet) {
val := false
Expand Down Expand Up @@ -275,16 +296,21 @@ func (f BoolFlag) getName() string {
// BoolTFlag this represents a boolean flag that is true by default, but can
// still be set to false by --some-flag=false
type BoolTFlag struct {
Name string
Usage string
EnvVar string
Name string
Usage string
EnvVar string
Required bool
}

// String returns a readable representation of this value (for usage defaults)
func (f BoolTFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
}

func (f BoolTFlag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f BoolTFlag) Apply(set *flag.FlagSet) {
val := true
Expand Down Expand Up @@ -312,10 +338,11 @@ func (f BoolTFlag) getName() string {

// StringFlag represents a flag that takes as string value
type StringFlag struct {
Name string
Value string
Usage string
EnvVar string
Name string
Value string
Usage string
EnvVar string
Required bool
}

// String returns the usage
Expand All @@ -332,6 +359,10 @@ func (f StringFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage))
}

func (f StringFlag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f StringFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
Expand All @@ -356,17 +387,22 @@ func (f StringFlag) getName() string {
// IntFlag is a flag that takes an integer
// Errors if the value provided cannot be parsed
type IntFlag struct {
Name string
Value int
Usage string
EnvVar string
Name string
Value int
Usage string
EnvVar string
Required bool
}

// String returns the usage
func (f IntFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
}

func (f IntFlag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f IntFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
Expand Down Expand Up @@ -394,17 +430,22 @@ func (f IntFlag) getName() string {
// DurationFlag is a flag that takes a duration specified in Go's duration
// format: https://golang.org/pkg/time/#ParseDuration
type DurationFlag struct {
Name string
Value time.Duration
Usage string
EnvVar string
Name string
Value time.Duration
Usage string
EnvVar string
Required bool
}

// String returns a readable representation of this value (for usage defaults)
func (f DurationFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
}

func (f DurationFlag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f DurationFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
Expand Down Expand Up @@ -432,17 +473,22 @@ func (f DurationFlag) getName() string {
// Float64Flag is a flag that takes an float value
// Errors if the value provided cannot be parsed
type Float64Flag struct {
Name string
Value float64
Usage string
EnvVar string
Name string
Value float64
Usage string
EnvVar string
Required bool
}

// String returns the usage
func (f Float64Flag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
}

func (f Float64Flag) isRequired() bool {
return f.Required
}

// Apply populates the flag given the flag set and environment
func (f Float64Flag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
Expand Down