Skip to content

Commit

Permalink
Feature: Add required flag support (#40)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
dearchap and Juneezee authored Nov 13, 2024
1 parent 3e89178 commit 90dcd10
Show file tree
Hide file tree
Showing 11 changed files with 1,194 additions and 1,143 deletions.
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ And you can use your favorite flag or cli library!

## Supported libraries and features:

| | | Hidden | Deprecated | Short | Env |
| --- | --- |:------:|:----------:|:-----:|:---:|
| <ul><li>[x] [flag]</li><ul> | [example](./examples/flag/main.go) | `-` | `-` | `-` | `-` |
| <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] [spf13/pflag]</li></ul> | [example](./examples/pflag/main.go) | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | <ul><li>[x] </li></ul> | `-` |
| <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> | `-` |
| <ul><li>[ ] [spf13/viper]</li><ul> | | <ul><li>[ ] </li></ul> | <ul><li>[ ] </li></ul> | <ul><li>[ ] </li></ul> | <ul><li>[ ] </li></ul> |
| <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> |
| | | Hidden | Deprecated | Short | Env | Required |
| --- | --- |:------:|:----------:|:-----:|:---:|:--------:|
| <ul><li>[x] [flag]</li><ul> | [example](./examples/flag/main.go) | `-` | `-` | `-` | `-` | `-` |
| <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> |
| <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> | `-` | `-` |
| <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> | `-` | `-` |
| <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> |

- [x] - feature is supported and implemented

Expand All @@ -50,7 +49,7 @@ And you can use your favorite flag or cli library!
- [x] Set usage
- [x] Long and short forms
- [x] Skip field
- [ ] Required
- [x] Required
- [ ] Placeholders (by `name`)
- [x] Deprecated and hidden options
- [ ] Multiple ENV names
Expand Down
1 change: 1 addition & 0 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ type Flag struct {
DefValue string // default value (as text); for usage message
Hidden bool
Deprecated bool
Required bool
}
13 changes: 7 additions & 6 deletions gen/gcli/gcli.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ func GenerateTo(src []*sflags.Flag, dst *[]cli.Flag) {
aliases = append(aliases, srcFlag.Short)
}
*dst = append(*dst, &cli.GenericFlag{
Name: name,
EnvVars: []string{srcFlag.EnvName},
Aliases: aliases,
Hidden: srcFlag.Hidden,
Usage: srcFlag.Usage,
Value: srcFlag.Value,
Name: name,
EnvVars: []string{srcFlag.EnvName},
Aliases: aliases,
Hidden: srcFlag.Hidden,
Usage: srcFlag.Usage,
Value: srcFlag.Value,
Required: srcFlag.Required,
})
}
}
Expand Down
15 changes: 13 additions & 2 deletions gen/gcli/gcli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package gcli

import (
"errors"
"fmt"
"io"
"strings"
"testing"

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

CounterValue1 sflags.Counter

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

CounterValue1: 1,

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

CounterValue1: 3,

Expand All @@ -52,6 +57,7 @@ func TestParse(t *testing.T) {
args: []string{
"--string-value1", "string_value1_value2",
"--string-value-two", "string_value2_value2",
"--string-value3", "string_value3_value2",
"--counter-value1", "--counter-value1",
"--string-slice-value1", "one2",
"--string-slice-value1", "two2",
Expand All @@ -68,7 +74,8 @@ func TestParse(t *testing.T) {
StringValue1: "string_value1_value",
StringValue2: "",
},
args: []string{},
args: []string{},
expErr2: fmt.Errorf("required flag \"string-value3\" not set"),
},
{
name: "Test cfg1 short option",
Expand All @@ -77,8 +84,10 @@ func TestParse(t *testing.T) {
},
expCfg: &cfg1{
StringValue2: "string_value2_value2",
StringValue3: "string_value3_value2",
},
args: []string{
"--string-value3", "string_value3_value2",
"-s=string_value2_value2",
},
},
Expand All @@ -88,12 +97,14 @@ func TestParse(t *testing.T) {
expCfg: &cfg1{
StringValue1: "string_value1_value2",
StringValue2: "string_value2_value2",
StringValue3: "string_value3_value2",

CounterValue1: 3,
},
args: []string{
"--string-value1", "string_value1_value2",
"--string-value-two", "string_value2_value2",
"--string-value3", "string_value3_value2",
"--counter-value1=2", "--counter-value1",
},
},
Expand Down Expand Up @@ -142,7 +153,7 @@ func TestParse(t *testing.T) {
err = cliApp.Run(args)
if test.expErr2 != nil {
require.Error(t, err)
require.Equal(t, test.expErr2, err)
require.Equal(t, test.expErr2.Error(), strings.ToLower(err.Error()))
} else {
require.NoError(t, err)
}
Expand Down
1 change: 1 addition & 0 deletions gen/gcli/gcliv3.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func GenerateToV3(src []*sflags.Flag, dst *[]cli.Flag) {
Value: &value{
v: srcFlag.Value,
},
Required: srcFlag.Required,
})
}
}
Expand Down
15 changes: 13 additions & 2 deletions gen/gcli/gcliv3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package gcli
import (
"context"
"errors"
"fmt"
"io"
"strings"
"testing"

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

CounterValue1 sflags.Counter

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

CounterValue1: 1,

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

CounterValue1: 3,

Expand All @@ -53,6 +58,7 @@ func TestParseV3(t *testing.T) {
args: []string{
"--string-value1", "string_value1_value2",
"--string-value-two", "string_value2_value2",
"--string-value3", "string_value3_value2",
"--counter-value1", "--counter-value1",
"--string-slice-value1", "one2",
"--string-slice-value1", "two2",
Expand All @@ -69,7 +75,8 @@ func TestParseV3(t *testing.T) {
StringValue1: "string_value1_value",
StringValue2: "",
},
args: []string{},
args: []string{},
expErr2: fmt.Errorf("required flag \"string-value3\" not set"),
},
{
name: "Test cfg2 short option",
Expand All @@ -78,8 +85,10 @@ func TestParseV3(t *testing.T) {
},
expCfg: &cfg2{
StringValue2: "string_value2_value2",
StringValue3: "string_value3_value2",
},
args: []string{
"--string-value3", "string_value3_value2",
"-s=string_value2_value2",
},
},
Expand All @@ -89,12 +98,14 @@ func TestParseV3(t *testing.T) {
expCfg: &cfg2{
StringValue1: "string_value1_value2",
StringValue2: "string_value2_value2",
StringValue3: "string_value3_value2",

CounterValue1: 3,
},
args: []string{
"--string-value1", "string_value1_value2",
"--string-value-two", "string_value2_value2",
"--string-value3", "string_value3_value2",
"--counter-value1=2", "--counter-value1",
},
},
Expand Down Expand Up @@ -143,7 +154,7 @@ func TestParseV3(t *testing.T) {
err = cmd.Run(context.Background(), args)
if test.expErr2 != nil {
require.Error(t, err)
require.Equal(t, test.expErr2, err)
require.Equal(t, test.expErr2.Error(), strings.ToLower(err.Error()))
} else {
require.NoError(t, err)
}
Expand Down
3 changes: 3 additions & 0 deletions gen/gkingpin/gkingpin.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ func GenerateTo(src []*sflags.Flag, dst flagger) {
if srcFlag.Hidden {
flag.Hidden()
}
if srcFlag.Required {
flag.Required()
}
if srcFlag.Short != "" {
r, _ := utf8.DecodeRuneInString(srcFlag.Short)
if r != utf8.RuneError {
Expand Down
11 changes: 10 additions & 1 deletion gen/gkingpin/gkingpin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type cfg1 struct {
StringValue1 string
StringValue2 string `flag:"string-value-two s"`
StringValue3 string `flag:",required"`

CounterValue1 sflags.Counter

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

CounterValue1: 1,

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

CounterValue1: 3,

Expand All @@ -51,6 +54,7 @@ func TestParse(t *testing.T) {
args: []string{
"--string-value1", "string_value1_value2",
"--string-value-two", "string_value2_value2",
"--string-value3", "string_value3_value2",
"--counter-value1", "--counter-value1",
"--string-slice-value1", "one2",
"--string-slice-value1", "two2",
Expand All @@ -67,7 +71,8 @@ func TestParse(t *testing.T) {
StringValue1: "string_value1_value",
StringValue2: "",
},
args: []string{},
args: []string{},
expErr2: errors.New("required flag(s) '--string-value3' not provided"),
},
{
name: "Test cfg1 short option",
Expand All @@ -76,8 +81,10 @@ func TestParse(t *testing.T) {
},
expCfg: &cfg1{
StringValue2: "string_value2_value2",
StringValue3: "string_value3_value",
},
args: []string{
"--string-value3", "string_value3_value",
"-s", "string_value2_value2",
},
},
Expand All @@ -87,12 +94,14 @@ func TestParse(t *testing.T) {
expCfg: &cfg1{
StringValue1: "string_value1_value2",
StringValue2: "string_value2_value2",
StringValue3: "string_value3_value2",

CounterValue1: 1,
},
args: []string{
"--string-value1", "string_value1_value2",
"--string-value-two", "string_value2_value2",
"--string-value3", "string_value3_value2",
// kingpin can't pass value for boolean arguments.
//"--counter-value1", "2",
"--counter-value1",
Expand Down
2 changes: 1 addition & 1 deletion parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func parseFlagTag(field reflect.StructField, opt opts) *Flag {
}
flag.Hidden = hasOption(flagTags[1:], "hidden")
flag.Deprecated = hasOption(flagTags[1:], "deprecated")

flag.Required = hasOption(flagTags[1:], "required")
}

if opt.prefix != "" && !ignoreFlagPrefix {
Expand Down
15 changes: 15 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func TestParseStruct(t *testing.T) {
Name4 *string
Name5 string `flag:"-"`
name6 string
Name7 int `flag:",required"`

Addr *net.TCPAddr

Expand Down Expand Up @@ -147,6 +148,13 @@ func TestParseStruct(t *testing.T) {
DefValue: "name_value4",
Value: newStringValue(simpleCfg.Name4),
},
{
Name: "name7",
EnvName: "NAME7",
Required: true,
DefValue: "0",
Value: newIntValue(&simpleCfg.Name7),
},
{
Name: "addr",
EnvName: "ADDR",
Expand Down Expand Up @@ -194,6 +202,13 @@ func TestParseStruct(t *testing.T) {
DefValue: "name_value4",
Value: newStringValue(simpleCfg.Name4),
},
{
Name: "name7",
EnvName: "PP|NAME7",
Required: true,
DefValue: "0",
Value: newIntValue(&simpleCfg.Name7),
},
{
Name: "addr",
EnvName: "PP|ADDR",
Expand Down
Loading

0 comments on commit 90dcd10

Please sign in to comment.