Skip to content

Commit 2692ff7

Browse files
committed
extra arguments support
1 parent 164563a commit 2692ff7

File tree

10 files changed

+239
-19
lines changed

10 files changed

+239
-19
lines changed

README.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,22 @@ Customize argument hint with `&argparse.Option{HintInfo: "customize info"}`
199199

200200
[example](examples/sub-command)
201201

202+
#### 3. Extra special positional arguments [ >= v1.12 ]
203+
204+
When the user want to input some special arguments starting with `-` or `--` , normally the parser will warn you with `unrecognized arguments`.
205+
206+
If you want to do so, simply input any arguments after a single `--`.
207+
208+
For example: `./run -- extra1 extra2`. In the example, `extra1` and `extra2` can be anything, `./run -- --1 -x`.
209+
210+
If the last positional argument receives multiple inputs, then the positional inputs before and after `--` will all be its value.
211+
212+
```go
213+
names := parser.Strings("", "names", &argparse.Option{Positional: true})
214+
parser.parse([]string{"a", "--", "b", "c"})
215+
// names == ["a", "b", "c"]
216+
```
217+
202218
### Supported Arguments
203219

204220
#### 1. Flag
@@ -311,7 +327,7 @@ if e := parser.Parse(nil); e != nil {
311327
}
312328

313329
if *path != "" {
314-
if read, e := ioutil.ReadFile(*path); e == nil {
330+
if read, e := os.ReadFile(*path); e == nil {
315331
fmt.Println(string(read))
316332
}
317333
}
@@ -1046,4 +1062,5 @@ feel free to add different use cases
10461062
9. [batch create arguments](examples/batch-create-arguments)
10471063
9. [argument inherit](examples/inherit)
10481064
9. [color support](examples/colorful)
1065+
9. [extra arguments](examples/extra-arguments)
10491066

args.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type arg struct {
1313
short string
1414
full string
1515
target interface{}
16-
assigned bool
16+
assigned bool // whether the argument is parsed
1717
Option
1818
}
1919

docs/cn.md

+21-2
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,24 @@ options:
193193

194194
[example](../examples/sub-command)
195195

196+
#### 3. 额外的特殊位置参数 [ >= v1.12 ]
197+
198+
当用户想要输入一些以 `-``--` 开头的一些入参,通常解析器会警告你 `未知的参数`
199+
200+
如果的确想要这样做,只需要把入参放到 `--` 之后。
201+
202+
比如: `./run -- extra1 extra2`,在栗子里,`extra1``extra2` 可以是任何东西,比如:
203+
204+
`./run -- --1 -x`
205+
206+
如果最后一个位置参数接受多个入参,那么用户在 `--` 前后的位置入参都会当作这个位置参数的值。
207+
208+
```go
209+
names := parser.Strings("", "names", &argparse.Option{Positional: true})
210+
parser.parse([]string{"a", "--", "b", "c"})
211+
// names == ["a", "b", "c"]
212+
```
213+
196214
### 支持的参数
197215

198216
#### 1. 标记参数
@@ -305,7 +323,7 @@ if e := parser.Parse(nil); e != nil {
305323
}
306324

307325
if *path != "" {
308-
if read, e := ioutil.ReadFile(*path); e == nil {
326+
if read, e := os.ReadFile(*path); e == nil {
309327
fmt.Println(string(read))
310328
}
311329
}
@@ -1037,4 +1055,5 @@ type Option struct {
10371055
9. [参数分组](../examples/argument-groups)
10381056
9. [批量创建参数](../examples/batch-create-arguments)
10391057
9. [参数继承](../examples/inherit)
1040-
9. [颜色支持](../examples/colorful)
1058+
9. [颜色支持](../examples/colorful)
1059+
9. [额外特殊参数](../examples/extra-arguments)

examples/any-type-action/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package main
77

88
import (
99
"fmt"
10-
"io/ioutil"
10+
"os"
1111
"strconv"
1212

1313
"github.com/hellflame/argparse"
@@ -30,7 +30,7 @@ func main() {
3030
}})
3131

3232
parser.String("", "b", &argparse.Option{Action: func(args []string) error {
33-
raw, e := ioutil.ReadFile(args[0])
33+
raw, e := os.ReadFile(args[0])
3434
if e != nil {
3535
return e
3636
}

examples/customzed-types/main.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ package main
1111

1212
import (
1313
"fmt"
14-
"io/ioutil"
1514
"net/url"
1615
"os"
1716

@@ -46,7 +45,7 @@ func main() {
4645
}
4746

4847
if *path != "" {
49-
if read, e := ioutil.ReadFile(*path); e == nil {
48+
if read, e := os.ReadFile(*path); e == nil {
5049
fmt.Println(string(read))
5150
}
5251
}

examples/extra-arguments/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*
2+
!*.go
3+
!.gitignore

examples/extra-arguments/main.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//go:build ignore
2+
3+
// this is show case for extra arguments after --
4+
//
5+
// if there are argument value happen to start with - or --, it may be regard as unknown arguments.
6+
// in this scenario, you can input any input after a --, eg: program -- extra1 extra2
7+
package main
8+
9+
import (
10+
"fmt"
11+
"os"
12+
"strings"
13+
14+
"github.com/hellflame/argparse"
15+
)
16+
17+
func main() {
18+
// example:
19+
// go run main.go x y -- --x --y
20+
parser := argparse.NewParser("basic", "this is a basic program", nil)
21+
names := parser.Strings("", "names", &argparse.Option{Positional: true})
22+
if e := parser.Parse(nil); e != nil {
23+
switch e {
24+
case argparse.BreakAfterHelpError:
25+
os.Exit(1)
26+
default:
27+
fmt.Println(e.Error())
28+
}
29+
return
30+
}
31+
fmt.Printf("hello %s\n", strings.Join(*names, ", "))
32+
}

parse.go

+65-8
Original file line numberDiff line numberDiff line change
@@ -465,14 +465,36 @@ fi
465465
`, p.formatBashCompletionScript(), p.formatZshCompletionScript())
466466
}
467467

468+
// find extra positional arguments
469+
func findExtraPositionalArgs(args []string) (idx int, remains []string) {
470+
startRemain := false
471+
remainMark := "--"
472+
for i, a := range args {
473+
if startRemain {
474+
remains = append(remains, a)
475+
}
476+
if !startRemain && a == remainMark {
477+
startRemain = true
478+
idx = i
479+
}
480+
}
481+
return
482+
}
483+
468484
// Parse will parse given args to bind to any registered arguments
469485
//
470486
// args: set nil to use os.Args[1:] by default
471487
func (p *Parser) Parse(args []string) error {
472488
if args == nil {
473489
args = os.Args[1:]
474490
}
475-
if len(args) == 0 {
491+
extraIdx, remains := findExtraPositionalArgs(args)
492+
hasExtra := len(remains) > 0
493+
if hasExtra || extraIdx > 0 {
494+
args = args[:extraIdx]
495+
}
496+
497+
if len(args) == 0 && !hasExtra {
476498
if p.config.DefaultAction != nil {
477499
p.config.DefaultAction()
478500
} else if !p.config.DisableDefaultShowHelp {
@@ -488,43 +510,53 @@ func (p *Parser) Parse(args []string) error {
488510
lastPositionArgIndex := 0
489511
registeredPositionsLength := len(p.positionArgs)
490512
for len(args) > 0 {
513+
// iterate user input args
491514
sign := args[0]
492515
if arg, ok := p.entryMap[sign]; ok {
493516
if arg.isFlag {
494517
_ = arg.parseValue(nil)
495518
args = args[1:]
496519
} else {
497-
var tillNext []string // find user inputs before next registered optional argument
520+
// find user inputs before next registered optional argument
521+
var tillNext []string
498522
for _, a := range args[1:] {
499523
if _, isEntry := p.entryMap[a]; !isEntry {
500524
tillNext = append(tillNext, a)
501525
} else {
502526
break
503527
}
504528
}
505-
if len(tillNext) == 0 { // argument takes at least one input as argument, but there is 0
529+
// argument takes at least one input as argument, but there is 0
530+
if len(tillNext) == 0 {
506531
return fmt.Errorf("argument %s expect argument",
507532
strings.Join(arg.getWatchers(), "/"))
508533
}
509-
if arg.multi { // if argument takes more than one arguments, it will take all user input before next registered argument, and proceed 'args' parsing to next registered argument
534+
// if argument takes more than one arguments,
535+
// it will take all user input before next registered argument,
536+
// and proceed 'args' parsing to next registered argument
537+
if arg.multi {
510538
e := arg.parseValue(tillNext)
511539
if e != nil {
512540
return e
513541
}
514542
args = args[len(tillNext)+1:]
515-
} else { // then the argument takes only one argument, and proceed the left arguments for positional argument parsing
543+
} else {
544+
// then the argument takes only one argument
516545
e := arg.parseValue(tillNext[0:1])
517546
if e != nil {
518547
return e
519548
}
549+
// proceed the left arguments for positional argument parsing
520550
args = args[2:]
521551
}
522552
}
523553
} else {
524-
if registeredPositionsLength > lastPositionArgIndex { // while there is unparsed positional argument
554+
// while there is unparsed positional argument
555+
if registeredPositionsLength > lastPositionArgIndex {
525556
arg := p.positionArgs[lastPositionArgIndex]
526557
lastPositionArgIndex += 1
527-
var tillNext []string // find user inputs before next registered optional argument
558+
// find user inputs before next registered optional argument
559+
var tillNext []string
528560
for _, a := range args {
529561
if _, isEntry := p.entryMap[a]; !isEntry {
530562
tillNext = append(tillNext, a)
@@ -533,10 +565,13 @@ func (p *Parser) Parse(args []string) error {
533565
}
534566
}
535567
if arg.multi {
536-
e := arg.parseValue(tillNext)
568+
// if any multi-type positional required,
569+
// extra arguments with be regard as part of it
570+
e := arg.parseValue(append(tillNext, remains...))
537571
if e != nil {
538572
return e
539573
}
574+
remains = []string{}
540575
args = args[len(tillNext):]
541576
} else {
542577
e := arg.parseValue(tillNext[0:1])
@@ -579,6 +614,28 @@ func (p *Parser) Parse(args []string) error {
579614
fmt.Println(p.FormatCompletionScript())
580615
return BreakAfterShellScriptError
581616
}
617+
618+
// apply extra arguments for only positional
619+
if len(remains) > 0 {
620+
for _, arg := range p.positionArgs {
621+
if arg.assigned {
622+
continue
623+
}
624+
if arg.multi {
625+
e := arg.parseValue(remains)
626+
if e != nil {
627+
return e
628+
}
629+
} else {
630+
e := arg.parseValue(remains[0:1])
631+
if e != nil {
632+
return e
633+
}
634+
remains = remains[1:]
635+
}
636+
}
637+
}
638+
582639
entries := append(p.entries, p.positionArgs...)
583640
for _, arg := range entries { // check Required & set Default value
584641
if !arg.assigned && arg.Default != "" {

0 commit comments

Comments
 (0)