~rjarry/public-inbox

go-opt: spec: ignore flags after the special argument -- v2 APPLIED

Koni Marti: 1
 spec: ignore flags after the special argument --

 3 files changed, 23 insertions(+), 2 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~rjarry/public-inbox/patches/49169/mbox | git am -3
Learn more about email & git

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

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
Koni Marti <koni.marti@gmail.com> wrote: