~rjarry/public-inbox

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH go-opt v2] spec: ignore flags after the special argument --

Details
Message ID
<20240204075645.128639-1-koni.marti@gmail.com>
DKIM signature
pass
Download raw message
Patch: +23 -2
Ignore flags after the special option delimiter argument ('--') and
interprete them as non-flag, positional arguments as getopt(3) does.

Example:

	type Foo struct {
		Force bool          `opt:"-f"`
		Name  string        `opt:"name"`
	}

Current behavior:

	$ foo -- -f
	main.Foo{Force:true, Name:"--"}

Expected behavior with this patch:

	$ foo -- -f
	main.Foo{Force:false, Name:"-f"}

and

	$ foo -f -- -f
	main.Foo{Force:true, Name:"-f"}

Signed-off-by: Koni Marti <koni.marti@gmail.com>
---
v1->v2:

 * match getopt(3) behavior: end option-scanning after -- and treat rest
   as positionals

 README.md    |  3 +++
 spec.go      | 14 ++++++++++++--
 spec_test.go |  8 ++++++++
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index b1478f4..4652a95 100644
--- a/README.md
+++ b/README.md
@@ -277,3 +277,6 @@ Short flags can be combined like with `getopt(3)`:

* Flags with no value: `-abc` is equivalent to `-a -b -c`
* Flags with a value (options): `-j9` is equivalent to `-j 9`

The special argument `--` forces an end to the flag parsing. The remaining
arguments are interpreted as positional arguments (see `getopt(3)`).
diff --git a/spec.go b/spec.go
index 323dffa..d4960d1 100644
--- a/spec.go
+++ b/spec.go
@@ -82,6 +82,8 @@ var (
	positionalRe = regexp.MustCompile(`^([a-zA-Z][\w-]*)$`)
)

const optionDelim = "--"

// Interpret all struct fields to a list of option specs
func NewCmdSpec(name string, v any) *CmdSpec {
	typ := reflect.TypeOf(v)
@@ -412,6 +414,7 @@ func (c *CmdSpec) parseArgs(args *Args) []*ArgError {
		argErrors = append(argErrors, &ArgError{kind, s, detail})
	}
	positionals := c.positionals
	ignoreFlags := false
	c.seen = nil
	args.Shift(1) // skip command name
	i := 1
@@ -420,7 +423,7 @@ func (c *CmdSpec) parseArgs(args *Args) []*ArgError {
		arg := args.Arg(0)

		switch {
		case c.getLongFlag(arg) != nil:
		case c.getLongFlag(arg) != nil && !ignoreFlags:
			if cur != nil {
				fail(missingValue, cur.spec, nil)
			}
@@ -443,7 +446,7 @@ func (c *CmdSpec) parseArgs(args *Args) []*ArgError {
				cur = nil
			}

		case c.getShortFlag(arg) != nil:
		case c.getShortFlag(arg) != nil && !ignoreFlags:
			if cur != nil {
				fail(missingValue, cur.spec, nil)
			}
@@ -486,6 +489,7 @@ func (c *CmdSpec) parseArgs(args *Args) []*ArgError {
				cur = nil
				goto next
			}

			for len(positionals) > 0 {
				spec := &c.opts[positionals[0]]
				positionals = positionals[1:]
@@ -494,6 +498,12 @@ func (c *CmdSpec) parseArgs(args *Args) []*ArgError {
					break
				}
			}

			if arg == optionDelim {
				ignoreFlags = true
				goto next
			}

			if cur == nil {
				fail(unexpectedArg, nil, errors.New(arg))
				goto next
diff --git a/spec_test.go b/spec_test.go
index 1d0b6de..4673ed0 100644
--- a/spec_test.go
+++ b/spec_test.go
@@ -92,6 +92,14 @@ func TestArgsToStruct(t *testing.T) {
				Name:    "n a m e",
			},
		},
		{
			cmdline: `bar -j3 -- -j7`,
			expected: OptionStruct{
				Jobs:  3,
				Delay: 0.5,
				Name:  "-j7",
			},
		},
	}

	for _, v := range vectors {
-- 
2.43.0

Applied: [PATCH go-opt v2] spec: ignore flags after the special argument --

Details
Message ID
<170703940436.400525.2384298413800389354@ringo>
In-Reply-To
<20240204075645.128639-1-koni.marti@gmail.com> (view parent)
DKIM signature
pass
Download raw message
Koni Marti <koni.marti@gmail.com> wrote:
> Ignore flags after the special option delimiter argument ('--') and
> interprete them as non-flag, positional arguments as getopt(3) does.
>
> Example:
>
> 	type Foo struct {
> 		Force bool          `opt:"-f"`
> 		Name  string        `opt:"name"`
> 	}
>
> Current behavior:
>
> 	$ foo -- -f
> 	main.Foo{Force:true, Name:"--"}
>
> Expected behavior with this patch:
>
> 	$ foo -- -f
> 	main.Foo{Force:false, Name:"-f"}
>
> and
>
> 	$ foo -f -- -f
> 	main.Foo{Force:true, Name:"-f"}
>
> Signed-off-by: Koni Marti <koni.marti@gmail.com>
> ---
> v1->v2:
>
>  * match getopt(3) behavior: end option-scanning after -- and treat rest
>    as positionals

Acked-by: Robin Jarry <robin@jarry.cc>

Applied, thanks.

To 
   cb392dfc9d62..4bd5fff1499e  main -> main
Reply to thread Export thread (mbox)