Skip to content

Commit 90dcd10

Browse files
dearchapJuneezee
andauthored
Feature: Add required flag support (#40)
* Feature: Add required flag support * Add new required field for tests * Update README.md for required field * Update README.md for required field * Update README.md Co-authored-by: Eng Zer Jun <[email protected]> --------- Co-authored-by: Eng Zer Jun <[email protected]>
1 parent 3e89178 commit 90dcd10

11 files changed

+1194
-1143
lines changed

README.md

+8-9
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ And you can use your favorite flag or cli library!
2323

2424
## Supported libraries and features:
2525

26-
| | | Hidden | Deprecated | Short | Env |
27-
| --- | --- |:------:|:----------:|:-----:|:---:|
28-
| <ul><li>[x] [flag]</li><ul> | [example](./examples/flag/main.go) | `-` | `-` | `-` | `-` |
29-
| <ul><li>[x] [kingpin]</li></ul> | [example](./examples/kingpin/main.go) | <ul><li>[x] </li></ul> | <ul><li>[ ] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> |
30-
| <ul><li>[x] [spf13/pflag]</li></ul> | [example](./examples/pflag/main.go) | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | `-` |
31-
| <ul><li>[x] [spf13/cobra]</li></ul> | [example](./examples/cobra/main.go) | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | `-` |
32-
| <ul><li>[ ] [spf13/viper]</li><ul> | | <ul><li>[ ] </li></ul> | <ul><li>[ ] </li></ul> | <ul><li>[ ] </li></ul> | <ul><li>[ ] </li></ul> |
33-
| <ul><li>[x] [urfave/cli]</li></ul> | [example](./examples/urfave_cli/main.go) | <ul><li>[x] </li></ul> | `-` | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> |
26+
| | | Hidden | Deprecated | Short | Env | Required |
27+
| --- | --- |:------:|:----------:|:-----:|:---:|:--------:|
28+
| <ul><li>[x] [flag]</li><ul> | [example](./examples/flag/main.go) | `-` | `-` | `-` | `-` | `-` |
29+
| <ul><li>[x] [kingpin]</li></ul> | [example](./examples/kingpin/main.go) | <ul><li>[x] </li></ul> | <ul><li>[ ] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> |
30+
| <ul><li>[x] [spf13/pflag]</li></ul> | [example](./examples/pflag/main.go) | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | `-` | `-` |
31+
| <ul><li>[x] [spf13/cobra]</li></ul> | [example](./examples/cobra/main.go) | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | `-` | `-` |
32+
| <ul><li>[x] [urfave/cli]</li></ul> | [example](./examples/urfave_cli/main.go) | <ul><li>[x] </li></ul> | `-` | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> |
3433

3534
- [x] - feature is supported and implemented
3635

@@ -50,7 +49,7 @@ And you can use your favorite flag or cli library!
5049
- [x] Set usage
5150
- [x] Long and short forms
5251
- [x] Skip field
53-
- [ ] Required
52+
- [x] Required
5453
- [ ] Placeholders (by `name`)
5554
- [x] Deprecated and hidden options
5655
- [ ] Multiple ENV names

flag.go

+1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ type Flag struct {
1111
DefValue string // default value (as text); for usage message
1212
Hidden bool
1313
Deprecated bool
14+
Required bool
1415
}

gen/gcli/gcli.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ func GenerateTo(src []*sflags.Flag, dst *[]cli.Flag) {
1515
aliases = append(aliases, srcFlag.Short)
1616
}
1717
*dst = append(*dst, &cli.GenericFlag{
18-
Name: name,
19-
EnvVars: []string{srcFlag.EnvName},
20-
Aliases: aliases,
21-
Hidden: srcFlag.Hidden,
22-
Usage: srcFlag.Usage,
23-
Value: srcFlag.Value,
18+
Name: name,
19+
EnvVars: []string{srcFlag.EnvName},
20+
Aliases: aliases,
21+
Hidden: srcFlag.Hidden,
22+
Usage: srcFlag.Usage,
23+
Value: srcFlag.Value,
24+
Required: srcFlag.Required,
2425
})
2526
}
2627
}

gen/gcli/gcli_test.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package gcli
22

33
import (
44
"errors"
5+
"fmt"
56
"io"
7+
"strings"
68
"testing"
79

810
"github.com/stretchr/testify/assert"
@@ -14,6 +16,7 @@ import (
1416
type cfg1 struct {
1517
StringValue1 string
1618
StringValue2 string `flag:"string-value-two s"`
19+
StringValue3 string `flag:",required"`
1720

1821
CounterValue1 sflags.Counter
1922

@@ -35,6 +38,7 @@ func TestParse(t *testing.T) {
3538
cfg: &cfg1{
3639
StringValue1: "string_value1_value",
3740
StringValue2: "string_value2_value",
41+
StringValue3: "string_value3_value",
3842

3943
CounterValue1: 1,
4044

@@ -43,6 +47,7 @@ func TestParse(t *testing.T) {
4347
expCfg: &cfg1{
4448
StringValue1: "string_value1_value2",
4549
StringValue2: "string_value2_value2",
50+
StringValue3: "string_value3_value2",
4651

4752
CounterValue1: 3,
4853

@@ -52,6 +57,7 @@ func TestParse(t *testing.T) {
5257
args: []string{
5358
"--string-value1", "string_value1_value2",
5459
"--string-value-two", "string_value2_value2",
60+
"--string-value3", "string_value3_value2",
5561
"--counter-value1", "--counter-value1",
5662
"--string-slice-value1", "one2",
5763
"--string-slice-value1", "two2",
@@ -68,7 +74,8 @@ func TestParse(t *testing.T) {
6874
StringValue1: "string_value1_value",
6975
StringValue2: "",
7076
},
71-
args: []string{},
77+
args: []string{},
78+
expErr2: fmt.Errorf("required flag \"string-value3\" not set"),
7279
},
7380
{
7481
name: "Test cfg1 short option",
@@ -77,8 +84,10 @@ func TestParse(t *testing.T) {
7784
},
7885
expCfg: &cfg1{
7986
StringValue2: "string_value2_value2",
87+
StringValue3: "string_value3_value2",
8088
},
8189
args: []string{
90+
"--string-value3", "string_value3_value2",
8291
"-s=string_value2_value2",
8392
},
8493
},
@@ -88,12 +97,14 @@ func TestParse(t *testing.T) {
8897
expCfg: &cfg1{
8998
StringValue1: "string_value1_value2",
9099
StringValue2: "string_value2_value2",
100+
StringValue3: "string_value3_value2",
91101

92102
CounterValue1: 3,
93103
},
94104
args: []string{
95105
"--string-value1", "string_value1_value2",
96106
"--string-value-two", "string_value2_value2",
107+
"--string-value3", "string_value3_value2",
97108
"--counter-value1=2", "--counter-value1",
98109
},
99110
},
@@ -142,7 +153,7 @@ func TestParse(t *testing.T) {
142153
err = cliApp.Run(args)
143154
if test.expErr2 != nil {
144155
require.Error(t, err)
145-
require.Equal(t, test.expErr2, err)
156+
require.Equal(t, test.expErr2.Error(), strings.ToLower(err.Error()))
146157
} else {
147158
require.NoError(t, err)
148159
}

gen/gcli/gcliv3.go

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func GenerateToV3(src []*sflags.Flag, dst *[]cli.Flag) {
4848
Value: &value{
4949
v: srcFlag.Value,
5050
},
51+
Required: srcFlag.Required,
5152
})
5253
}
5354
}

gen/gcli/gcliv3_test.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package gcli
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"io"
8+
"strings"
79
"testing"
810

911
"github.com/stretchr/testify/assert"
@@ -15,6 +17,7 @@ import (
1517
type cfg2 struct {
1618
StringValue1 string
1719
StringValue2 string `flag:"string-value-two s"`
20+
StringValue3 string `flag:",required"`
1821

1922
CounterValue1 sflags.Counter
2023

@@ -36,6 +39,7 @@ func TestParseV3(t *testing.T) {
3639
cfg: &cfg2{
3740
StringValue1: "string_value1_value",
3841
StringValue2: "string_value2_value",
42+
StringValue3: "string_value3_value",
3943

4044
CounterValue1: 1,
4145

@@ -44,6 +48,7 @@ func TestParseV3(t *testing.T) {
4448
expCfg: &cfg2{
4549
StringValue1: "string_value1_value2",
4650
StringValue2: "string_value2_value2",
51+
StringValue3: "string_value3_value2",
4752

4853
CounterValue1: 3,
4954

@@ -53,6 +58,7 @@ func TestParseV3(t *testing.T) {
5358
args: []string{
5459
"--string-value1", "string_value1_value2",
5560
"--string-value-two", "string_value2_value2",
61+
"--string-value3", "string_value3_value2",
5662
"--counter-value1", "--counter-value1",
5763
"--string-slice-value1", "one2",
5864
"--string-slice-value1", "two2",
@@ -69,7 +75,8 @@ func TestParseV3(t *testing.T) {
6975
StringValue1: "string_value1_value",
7076
StringValue2: "",
7177
},
72-
args: []string{},
78+
args: []string{},
79+
expErr2: fmt.Errorf("required flag \"string-value3\" not set"),
7380
},
7481
{
7582
name: "Test cfg2 short option",
@@ -78,8 +85,10 @@ func TestParseV3(t *testing.T) {
7885
},
7986
expCfg: &cfg2{
8087
StringValue2: "string_value2_value2",
88+
StringValue3: "string_value3_value2",
8189
},
8290
args: []string{
91+
"--string-value3", "string_value3_value2",
8392
"-s=string_value2_value2",
8493
},
8594
},
@@ -89,12 +98,14 @@ func TestParseV3(t *testing.T) {
8998
expCfg: &cfg2{
9099
StringValue1: "string_value1_value2",
91100
StringValue2: "string_value2_value2",
101+
StringValue3: "string_value3_value2",
92102

93103
CounterValue1: 3,
94104
},
95105
args: []string{
96106
"--string-value1", "string_value1_value2",
97107
"--string-value-two", "string_value2_value2",
108+
"--string-value3", "string_value3_value2",
98109
"--counter-value1=2", "--counter-value1",
99110
},
100111
},
@@ -143,7 +154,7 @@ func TestParseV3(t *testing.T) {
143154
err = cmd.Run(context.Background(), args)
144155
if test.expErr2 != nil {
145156
require.Error(t, err)
146-
require.Equal(t, test.expErr2, err)
157+
require.Equal(t, test.expErr2.Error(), strings.ToLower(err.Error()))
147158
} else {
148159
require.NoError(t, err)
149160
}

gen/gkingpin/gkingpin.go

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ func GenerateTo(src []*sflags.Flag, dst flagger) {
2323
if srcFlag.Hidden {
2424
flag.Hidden()
2525
}
26+
if srcFlag.Required {
27+
flag.Required()
28+
}
2629
if srcFlag.Short != "" {
2730
r, _ := utf8.DecodeRuneInString(srcFlag.Short)
2831
if r != utf8.RuneError {

gen/gkingpin/gkingpin_test.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
type cfg1 struct {
1414
StringValue1 string
1515
StringValue2 string `flag:"string-value-two s"`
16+
StringValue3 string `flag:",required"`
1617

1718
CounterValue1 sflags.Counter
1819

@@ -34,6 +35,7 @@ func TestParse(t *testing.T) {
3435
cfg: &cfg1{
3536
StringValue1: "string_value1_value",
3637
StringValue2: "string_value2_value",
38+
StringValue3: "string_value3_value",
3739

3840
CounterValue1: 1,
3941

@@ -42,6 +44,7 @@ func TestParse(t *testing.T) {
4244
expCfg: &cfg1{
4345
StringValue1: "string_value1_value2",
4446
StringValue2: "string_value2_value2",
47+
StringValue3: "string_value3_value2",
4548

4649
CounterValue1: 3,
4750

@@ -51,6 +54,7 @@ func TestParse(t *testing.T) {
5154
args: []string{
5255
"--string-value1", "string_value1_value2",
5356
"--string-value-two", "string_value2_value2",
57+
"--string-value3", "string_value3_value2",
5458
"--counter-value1", "--counter-value1",
5559
"--string-slice-value1", "one2",
5660
"--string-slice-value1", "two2",
@@ -67,7 +71,8 @@ func TestParse(t *testing.T) {
6771
StringValue1: "string_value1_value",
6872
StringValue2: "",
6973
},
70-
args: []string{},
74+
args: []string{},
75+
expErr2: errors.New("required flag(s) '--string-value3' not provided"),
7176
},
7277
{
7378
name: "Test cfg1 short option",
@@ -76,8 +81,10 @@ func TestParse(t *testing.T) {
7681
},
7782
expCfg: &cfg1{
7883
StringValue2: "string_value2_value2",
84+
StringValue3: "string_value3_value",
7985
},
8086
args: []string{
87+
"--string-value3", "string_value3_value",
8188
"-s", "string_value2_value2",
8289
},
8390
},
@@ -87,12 +94,14 @@ func TestParse(t *testing.T) {
8794
expCfg: &cfg1{
8895
StringValue1: "string_value1_value2",
8996
StringValue2: "string_value2_value2",
97+
StringValue3: "string_value3_value2",
9098

9199
CounterValue1: 1,
92100
},
93101
args: []string{
94102
"--string-value1", "string_value1_value2",
95103
"--string-value-two", "string_value2_value2",
104+
"--string-value3", "string_value3_value2",
96105
// kingpin can't pass value for boolean arguments.
97106
//"--counter-value1", "2",
98107
"--counter-value1",

parser.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func parseFlagTag(field reflect.StructField, opt opts) *Flag {
114114
}
115115
flag.Hidden = hasOption(flagTags[1:], "hidden")
116116
flag.Deprecated = hasOption(flagTags[1:], "deprecated")
117-
117+
flag.Required = hasOption(flagTags[1:], "required")
118118
}
119119

120120
if opt.prefix != "" && !ignoreFlagPrefix {

parser_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func TestParseStruct(t *testing.T) {
2727
Name4 *string
2828
Name5 string `flag:"-"`
2929
name6 string
30+
Name7 int `flag:",required"`
3031

3132
Addr *net.TCPAddr
3233

@@ -147,6 +148,13 @@ func TestParseStruct(t *testing.T) {
147148
DefValue: "name_value4",
148149
Value: newStringValue(simpleCfg.Name4),
149150
},
151+
{
152+
Name: "name7",
153+
EnvName: "NAME7",
154+
Required: true,
155+
DefValue: "0",
156+
Value: newIntValue(&simpleCfg.Name7),
157+
},
150158
{
151159
Name: "addr",
152160
EnvName: "ADDR",
@@ -194,6 +202,13 @@ func TestParseStruct(t *testing.T) {
194202
DefValue: "name_value4",
195203
Value: newStringValue(simpleCfg.Name4),
196204
},
205+
{
206+
Name: "name7",
207+
EnvName: "PP|NAME7",
208+
Required: true,
209+
DefValue: "0",
210+
Value: newIntValue(&simpleCfg.Name7),
211+
},
197212
{
198213
Name: "addr",
199214
EnvName: "PP|ADDR",

0 commit comments

Comments
 (0)