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

fix: --long= should not consume the next argument #139

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 11 additions & 17 deletions flag_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,8 @@ func (fs *FlagSet) parseShortFlag(arg string, args []string) ([]string, error) {
}

func (fs *FlagSet) parseLongFlag(arg string, args []string) ([]string, error) {
var (
name string
value string
)

if equals := strings.IndexRune(arg, '='); equals > 0 {
arg, value = arg[:equals], arg[equals+1:]
}

name = strings.TrimPrefix(arg, "--")
name, value, eqFound := strings.Cut(arg, "=")
name = strings.TrimPrefix(name, "--")

f := fs.findLongFlag(name)
if f == nil {
Expand All @@ -264,22 +256,24 @@ func (fs *FlagSet) parseLongFlag(arg string, args []string) ([]string, error) {
}
}

if value == "" {
if eqFound && f.isBoolFlag && value == "" {
value = "true" // `--debug=` amounts to `--debug=true`
}

if value == "" && !eqFound {
switch {
case f.isBoolFlag:
value = "true" // `-b` or `--foo` default to true
value = "true" // `--foo` defaults to true
if len(args) > 0 {
if _, err := strconv.ParseBool(args[0]); err == nil {
value = args[0] // `-b true` or `--foo false` should also work
value = args[0] // `--foo false` should also work
args = args[1:]
}
}
Comment on lines 267 to 272

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also something strange with the code (the code that already exists)

Let's assume --foo expects a string

--foo leads to an error
--foo true leads to "true"
--foo=1 leads to "1"
--foo=false leads to "false"
--foo= leads to ""

But if --foo is a boolean

--foo false
--foo whatever
--foo 0
--foo 2
--foo=2

Leads to this

--foo=false
--foo whatever
--foo=false
--foo=true 2
an error

This behavior is strange to me, but I'm unsure how other libraries parsing flags do

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference between --foo 2 and --foo=2 is...not great. That is probably why flag in Go's standard library restricts the form --flag arg to non-boolean flags.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be in favor of that here as well, but I think @peterbourgon wants to keep boolean parsing as is. (Also, that would probably be a very breaking API change now. Don't know how that plays with v4 being in beta.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ccoVeille As a side note, flag in the standard library has a parallel inconsistency but for string flags.

	fs := flag.NewFlagSet("whatever", flag.ContinueOnError)
	cfg := struct {
		config  string
		// ...
	}{}
	fs.StringVar(&cfg.config, "config", "", "Use this configuration file")
	// ...
whatever -config=foo   # No error; config is set to "foo"
whatever -config foo   # No error; config is set to "foo"
whatever -config ""    # No error; config is set to ""
whatever -config=""    # No error; config is set to ""
whatever -config       # Error, namely "flag needs an argument: -config"
whatever -config=      # No error; config is set to ""

I think that the last two should return the same error, but they do not. I'm guessing this is because the parser would need to do extra work (not much but some) to detect the difference between -config= and -config="". I doubt anything can be done about it now (breaking API change), but I think it was a mistake.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, the behavior of the stdlib is fine here. I might look a bit odd, but I'm fine with it.

It has its logic.

About ff lib, I don't know really. You are fixing a bug with the boolean flag after all, so fixing the bug is somehow already breaking something that was broken 😄, by fixing it.

While your fix is fine, I think the issue of removing the random behavior of boolean flag with a parameter should be considered, at least to have a library that behave like other libraries.

I don't think it was intended, I would remove it. But, except that your PR is fine, you are fixing the behavior with --foo= so nothing about --foo 0

So for me it can be merged as is

case !f.isBoolFlag && len(args) > 0:
case len(args) > 0:
value, args = args[0], args[1:]
case !f.isBoolFlag && len(args) <= 0:
return nil, fmt.Errorf("missing value")
default:
panic("unreachable")
return nil, fmt.Errorf("missing value")
}
}

Expand Down
1 change: 1 addition & 0 deletions flag_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func TestFlagSet_Bool(t *testing.T) {
{args: []string{"--help"}, wantX: false, wantY: true, wantErr: ff.ErrHelp},
{args: []string{"--xflag", "-h"}, wantX: true, wantY: true, wantErr: ff.ErrHelp},
{args: []string{"-y", "--help"}, wantX: false, wantY: false, wantErr: ff.ErrHelp},
{args: []string{"--xflag=", "--help"}, wantX: true, wantY: true, wantErr: ff.ErrHelp},
} {
t.Run(strings.Join(test.args, " "), func(t *testing.T) {
fs := ff.NewFlagSet(t.Name())
Expand Down
6 changes: 6 additions & 0 deletions parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ func TestParse_FlagSet(t *testing.T) {
Args: []string{`--str`, `foo`, `--help`, `-b`},
Want: fftest.Vars{S: "foo", B: false, WantParseErrorIs: ff.ErrHelp},
},
{
Name: "--str= -a",
Constructors: []fftest.Constructor{fftest.CoreConstructor},
Args: []string{`--str=`, `-a`},
Want: fftest.Vars{S: "", A: true},
},
{
Name: "-s foo -f 1.23",
Constructors: []fftest.Constructor{fftest.CoreConstructor},
Expand Down