Skip to content

Commit c66219f

Browse files
authored
fix: modified to inherit end opt flag after parsing until sub command (#64)
1 parent 4000aaf commit c66219f

6 files changed

+178
-33
lines changed

cmd.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ type Cmd struct {
1919
Args []string
2020
OptCfgs []OptCfg
2121

22-
opts map[string][]string
22+
opts map[string][]string
23+
isAfterEndOpt bool
2324

2425
_args []string
2526
}
@@ -40,7 +41,7 @@ func NewCmd() Cmd {
4041
return Cmd{Name: name, Args: []string{}, opts: make(map[string][]string), _args: args}
4142
}
4243

43-
func (cmd Cmd) subCmd(fromIndex int) Cmd {
44+
func (cmd Cmd) subCmd(fromIndex int, isAfterEndOpt bool) Cmd {
4445
var name string
4546
if len(cmd._args) > fromIndex {
4647
name = cmd._args[fromIndex]
@@ -51,7 +52,13 @@ func (cmd Cmd) subCmd(fromIndex int) Cmd {
5152
args = cmd._args[fromIndex+1:]
5253
}
5354

54-
return Cmd{Name: name, Args: []string{}, opts: make(map[string][]string), _args: args}
55+
return Cmd{
56+
Name: name,
57+
Args: []string{},
58+
opts: make(map[string][]string),
59+
isAfterEndOpt: isAfterEndOpt,
60+
_args: args,
61+
}
5562
}
5663

5764
// HasOpt is the method that checks whether an option with the specified name exists.

cmd_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestCmd_subCmd(t *testing.T) {
6969
cmd := NewCmd()
7070
cmd._args = []string{"--foo", "-b", "qux", "--corge"}
7171

72-
subCmd := cmd.subCmd(2)
72+
subCmd := cmd.subCmd(2, false)
7373
assert.Equal(t, subCmd.Name, "qux")
7474
assert.Equal(t, subCmd._args, []string{"--corge"})
7575
}
@@ -78,7 +78,7 @@ func TestCmd_subCmd_withNoArg(t *testing.T) {
7878
cmd := NewCmd()
7979
cmd._args = []string{"--foo", "-b", "qux"}
8080

81-
subCmd := cmd.subCmd(2)
81+
subCmd := cmd.subCmd(2, false)
8282
assert.Equal(t, subCmd.Name, "qux")
8383
assert.Equal(t, subCmd._args, []string(nil))
8484
}
@@ -87,7 +87,7 @@ func TestCmd_subCmd_empty(t *testing.T) {
8787
cmd := NewCmd()
8888
cmd._args = []string{"--foo", "-b"}
8989

90-
subCmd := cmd.subCmd(2)
90+
subCmd := cmd.subCmd(2, false)
9191
assert.Equal(t, subCmd.Name, "")
9292
assert.Equal(t, subCmd._args, []string(nil))
9393
}

parse-with.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const anyOption = "*"
3838
// The option configurations used to parsing are set into this Cmd instance, and it can be
3939
// retrieved from its field: Cmd#OptCfgs.
4040
func (cmd *Cmd) ParseWith(optCfgs []OptCfg) error {
41-
_, err := cmd.parseArgsWith(optCfgs, false)
41+
_, _, err := cmd.parseArgsWith(optCfgs, false)
4242
cmd.OptCfgs = optCfgs
4343
return err
4444
}
@@ -55,18 +55,18 @@ func (cmd *Cmd) ParseWith(optCfgs []OptCfg) error {
5555
// The option configurations used to parsing are set into this Cmd instance, and it can be
5656
// retrieved from its field: Cmd#OptCfgs.
5757
func (cmd *Cmd) ParseUntilSubCmdWith(optCfgs []OptCfg) (Cmd, error) {
58-
idx, err := cmd.parseArgsWith(optCfgs, true)
58+
idx, isAfterEndOpt, err := cmd.parseArgsWith(optCfgs, true)
5959
cmd.OptCfgs = optCfgs
6060
if idx < 0 {
6161
return Cmd{}, err
6262
}
63-
return cmd.subCmd(idx), err
63+
return cmd.subCmd(idx, isAfterEndOpt), err
6464
}
6565

6666
func (cmd *Cmd) parseArgsWith(
6767
optCfgs []OptCfg,
6868
untilFirstArg bool,
69-
) (int, error) {
69+
) (int, bool, error) {
7070

7171
const ANY_OPT = "*"
7272
hasAnyOpt := false
@@ -109,18 +109,18 @@ func (cmd *Cmd) parseArgsWith(
109109
_, exists := optMap[storeKey]
110110
if exists {
111111
e := errors.StoreKeyIsDuplicated{StoreKey: storeKey, Name: firstName}
112-
return -1, e
112+
return -1, cmd.isAfterEndOpt, e
113113
}
114114
optMap[storeKey] = EMPTY_STRUCT
115115

116116
if !cfg.HasArg {
117117
if cfg.IsArray {
118118
e := errors.ConfigIsArrayButHasNoArg{StoreKey: storeKey, Name: firstName}
119-
return -1, e
119+
return -1, cmd.isAfterEndOpt, e
120120
}
121121
if cfg.Defaults != nil {
122122
e := errors.ConfigHasDefaultsButHasNoArg{StoreKey: storeKey, Name: firstName}
123-
return -1, e
123+
return -1, cmd.isAfterEndOpt, e
124124
}
125125
}
126126

@@ -131,7 +131,7 @@ func (cmd *Cmd) parseArgsWith(
131131
_, exists := cfgMap[nm]
132132
if exists {
133133
e := errors.OptionNameIsDuplicated{StoreKey: storeKey, Name: nm}
134-
return -1, e
134+
return -1, cmd.isAfterEndOpt, e
135135
}
136136
cfgMap[nm] = i
137137
}
@@ -231,12 +231,13 @@ func (cmd *Cmd) parseArgsWith(
231231
}
232232
}
233233

234-
idx, err := parseArgs(
234+
idx, isAfterEndOpt, err := parseArgs(
235235
cmd._args,
236236
collectArgs,
237237
collectOpts,
238238
takeOptArgs,
239239
untilFirstArg,
240+
cmd.isAfterEndOpt,
240241
)
241242

242243
for _, cfg := range optCfgs {
@@ -273,5 +274,5 @@ func (cmd *Cmd) parseArgsWith(
273274
}
274275
}
275276

276-
return idx, err
277+
return idx, isAfterEndOpt, err
277278
}

parse-with_test.go

+84
Original file line numberDiff line numberDiff line change
@@ -1819,3 +1819,87 @@ func TestParseUntilSubCmdWith_oneCfgUsingValidatorAndSubCmd(t *testing.T) {
18191819
assert.Equal(t, subCmd.Name, "def")
18201820
assert.Equal(t, subCmd.Args, []string{"ghi"})
18211821
}
1822+
1823+
func TestParseUntilSubCmd_parseWithEndOptMark(t *testing.T) {
1824+
defer reset()
1825+
1826+
optCfgs0 := []cliargs.OptCfg{
1827+
cliargs.OptCfg{
1828+
Names: []string{"foo"},
1829+
},
1830+
}
1831+
optCfgs1 := []cliargs.OptCfg{
1832+
cliargs.OptCfg{
1833+
Names: []string{"bar"},
1834+
},
1835+
}
1836+
1837+
os.Args = []string{"/path/to/app", "--foo", "sub", "--", "bar", "-@"}
1838+
1839+
cmd := cliargs.NewCmd()
1840+
subCmd, err := cmd.ParseUntilSubCmdWith(optCfgs0)
1841+
1842+
assert.Nil(t, err)
1843+
assert.Equal(t, cmd.Name, "app")
1844+
assert.Equal(t, cmd.Args, []string{})
1845+
assert.Equal(t, cmd.HasOpt("foo"), true)
1846+
assert.Equal(t, cmd.OptArg("foo"), "")
1847+
assert.Equal(t, cmd.OptArgs("foo"), []string(nil))
1848+
assert.Equal(t, cmd.HasOpt("bar"), false)
1849+
assert.Equal(t, cmd.OptArg("bar"), "")
1850+
assert.Equal(t, cmd.OptArgs("bar"), []string(nil))
1851+
1852+
err = subCmd.ParseWith(optCfgs1)
1853+
1854+
assert.Nil(t, err)
1855+
assert.Equal(t, subCmd.Name, "sub")
1856+
assert.Equal(t, subCmd.Args, []string{"bar", "-@"})
1857+
assert.Equal(t, subCmd.HasOpt("foo"), false)
1858+
assert.Equal(t, subCmd.OptArg("foo"), "")
1859+
assert.Equal(t, subCmd.OptArgs("foo"), []string(nil))
1860+
assert.Equal(t, subCmd.HasOpt("bar"), false)
1861+
assert.Equal(t, subCmd.OptArg("bar"), "")
1862+
assert.Equal(t, subCmd.OptArgs("bar"), []string(nil))
1863+
}
1864+
1865+
func TestParseUntilSubCmd_parseAfterEndOptMark(t *testing.T) {
1866+
defer reset()
1867+
1868+
optCfgs0 := []cliargs.OptCfg{
1869+
cliargs.OptCfg{
1870+
Names: []string{"foo"},
1871+
},
1872+
}
1873+
optCfgs1 := []cliargs.OptCfg{
1874+
cliargs.OptCfg{
1875+
Names: []string{"bar"},
1876+
},
1877+
}
1878+
1879+
os.Args = []string{"/path/to/app", "--", "--foo", "sub", "bar", "-@"}
1880+
1881+
cmd := cliargs.NewCmd()
1882+
subCmd, err := cmd.ParseUntilSubCmdWith(optCfgs0)
1883+
1884+
assert.Nil(t, err)
1885+
assert.Equal(t, cmd.Name, "app")
1886+
assert.Equal(t, cmd.Args, []string{})
1887+
assert.Equal(t, cmd.HasOpt("foo"), false)
1888+
assert.Equal(t, cmd.OptArg("foo"), "")
1889+
assert.Equal(t, cmd.OptArgs("foo"), []string(nil))
1890+
assert.Equal(t, cmd.HasOpt("bar"), false)
1891+
assert.Equal(t, cmd.OptArg("bar"), "")
1892+
assert.Equal(t, cmd.OptArgs("bar"), []string(nil))
1893+
1894+
err = subCmd.ParseWith(optCfgs1)
1895+
1896+
assert.Nil(t, err)
1897+
assert.Equal(t, subCmd.Name, "--foo")
1898+
assert.Equal(t, subCmd.Args, []string{"sub", "bar", "-@"})
1899+
assert.Equal(t, subCmd.HasOpt("foo"), false)
1900+
assert.Equal(t, subCmd.OptArg("foo"), "")
1901+
assert.Equal(t, subCmd.OptArgs("foo"), []string(nil))
1902+
assert.Equal(t, subCmd.HasOpt("bar"), false)
1903+
assert.Equal(t, subCmd.OptArg("bar"), "")
1904+
assert.Equal(t, subCmd.OptArgs("bar"), []string(nil))
1905+
}

parse.go

+12-11
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (cmd *Cmd) Parse() error {
6060
return nil
6161
}
6262

63-
_, err := parseArgs(cmd._args, collectArgs, collectOpts, takeOptArgs, false)
63+
_, _, err := parseArgs(cmd._args, collectArgs, collectOpts, takeOptArgs, false, cmd.isAfterEndOpt)
6464
return err
6565
}
6666

@@ -84,11 +84,12 @@ func (cmd *Cmd) ParseUntilSubCmd() (Cmd, error) {
8484
return nil
8585
}
8686

87-
idx, err := parseArgs(cmd._args, collectArgs, collectOpts, takeOptArgs, true)
87+
idx, isAfterEndOpt, err := parseArgs(
88+
cmd._args, collectArgs, collectOpts, takeOptArgs, true, cmd.isAfterEndOpt)
8889
if idx < 0 {
8990
return Cmd{}, err
9091
}
91-
return cmd.subCmd(idx), err
92+
return cmd.subCmd(idx, isAfterEndOpt), err
9293
}
9394

9495
func takeOptArgs(_opt string) bool {
@@ -101,17 +102,17 @@ func parseArgs(
101102
collectOpts func(string, ...string) error,
102103
takeOptArgs func(string) bool,
103104
untilFirstArg bool,
104-
) (int, error) {
105+
isAfterEndOpt bool,
106+
) (int, bool, error) {
105107

106-
isNonOpt := false
107108
prevOptTakingArgs := ""
108109
var firstErr error = nil
109110

110111
L0:
111112
for iArg, arg := range osArgs {
112-
if isNonOpt {
113+
if isAfterEndOpt {
113114
if untilFirstArg {
114-
return iArg, firstErr
115+
return iArg, isAfterEndOpt, firstErr
115116
}
116117
collectArgs(arg)
117118

@@ -126,7 +127,7 @@ L0:
126127
}
127128
} else if strings.HasPrefix(arg, "--") {
128129
if len(arg) == 2 {
129-
isNonOpt = true
130+
isAfterEndOpt = true
130131
continue L0
131132
}
132133

@@ -179,7 +180,7 @@ L0:
179180
} else if strings.HasPrefix(arg, "-") {
180181
if len(arg) == 1 {
181182
if untilFirstArg {
182-
return iArg, firstErr
183+
return iArg, isAfterEndOpt, firstErr
183184
}
184185
collectArgs(arg)
185186
continue L0
@@ -238,11 +239,11 @@ L0:
238239

239240
} else {
240241
if untilFirstArg {
241-
return iArg, firstErr
242+
return iArg, isAfterEndOpt, firstErr
242243
}
243244
collectArgs(arg)
244245
}
245246
}
246247

247-
return -1, firstErr
248+
return -1, isAfterEndOpt, firstErr
248249
}

0 commit comments

Comments
 (0)