mcfg: modify WithCLITail to allow modifying the Usage description
This commit is contained in:
parent
99387d89ac
commit
507c359997
41
mcfg/cli.go
41
mcfg/cli.go
@ -16,10 +16,15 @@ import (
|
|||||||
type cliKey int
|
type cliKey int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
cliKeyTailPtr cliKey = iota
|
cliKeyTail cliKey = iota
|
||||||
cliKeySubCmdM
|
cliKeySubCmdM
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type cliTail struct {
|
||||||
|
dst *[]string
|
||||||
|
descr string
|
||||||
|
}
|
||||||
|
|
||||||
// WithCLITail returns a Context which modifies the behavior of SourceCLI's
|
// WithCLITail returns a Context which modifies the behavior of SourceCLI's
|
||||||
// Parse, if SourceCLI is used with that Context at all. Normally when SourceCLI
|
// Parse, if SourceCLI is used with that Context at all. Normally when SourceCLI
|
||||||
// encounters an unexpected Arg it will immediately return an error. This
|
// encounters an unexpected Arg it will immediately return an error. This
|
||||||
@ -27,21 +32,33 @@ const (
|
|||||||
// and all subsequent Args (i.e. the tail) should be set to the returned
|
// and all subsequent Args (i.e. the tail) should be set to the returned
|
||||||
// []string value.
|
// []string value.
|
||||||
//
|
//
|
||||||
// If multiple WithCLITail calls are used then only the latest returned pointer
|
// The descr (optional) will be appended to the "Usage" line which is printed
|
||||||
// will be filled.
|
// with the help document when "-h" is passed in.
|
||||||
func WithCLITail(ctx context.Context) (context.Context, *[]string) {
|
func WithCLITail(ctx context.Context, descr string) (context.Context, *[]string) {
|
||||||
|
if ctx.Value(cliKeyTail) != nil {
|
||||||
|
panic("WithCLITail already called in this Context")
|
||||||
|
}
|
||||||
tailPtr := new([]string)
|
tailPtr := new([]string)
|
||||||
return context.WithValue(ctx, cliKeyTailPtr, tailPtr), tailPtr
|
ctx = context.WithValue(ctx, cliKeyTail, cliTail{
|
||||||
|
dst: tailPtr,
|
||||||
|
descr: descr,
|
||||||
|
})
|
||||||
|
return ctx, tailPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
func populateCLITail(ctx context.Context, tail []string) bool {
|
func populateCLITail(ctx context.Context, tail []string) bool {
|
||||||
tailPtr, ok := ctx.Value(cliKeyTailPtr).(*[]string)
|
ct, ok := ctx.Value(cliKeyTail).(cliTail)
|
||||||
if ok {
|
if ok {
|
||||||
*tailPtr = tail
|
*ct.dst = tail
|
||||||
}
|
}
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCLITailDescr(ctx context.Context) string {
|
||||||
|
ct, _ := ctx.Value(cliKeyTail).(cliTail)
|
||||||
|
return ct.descr
|
||||||
|
}
|
||||||
|
|
||||||
type subCmd struct {
|
type subCmd struct {
|
||||||
name, descr string
|
name, descr string
|
||||||
flag *bool
|
flag *bool
|
||||||
@ -133,10 +150,9 @@ func (cli *SourceCLI) parse(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
subCmdM, _ := ctx.Value(cliKeySubCmdM).(map[string]subCmd)
|
|
||||||
|
|
||||||
printHelpAndExit := func() {
|
printHelpAndExit := func() {
|
||||||
cli.printHelp(os.Stderr, subCmdPrefix, subCmdM, pM)
|
cli.printHelp(ctx, os.Stderr, subCmdPrefix, pM)
|
||||||
os.Stderr.Sync()
|
os.Stderr.Sync()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@ -145,6 +161,7 @@ func (cli *SourceCLI) parse(
|
|||||||
// of them should have been given, in which case send the Context through
|
// of them should have been given, in which case send the Context through
|
||||||
// the callback to obtain a new one (which presumably has further config
|
// the callback to obtain a new one (which presumably has further config
|
||||||
// options the previous didn't) and call parse again.
|
// options the previous didn't) and call parse again.
|
||||||
|
subCmdM, _ := ctx.Value(cliKeySubCmdM).(map[string]subCmd)
|
||||||
if len(subCmdM) > 0 {
|
if len(subCmdM) > 0 {
|
||||||
subCmd, args, ok := cli.getSubCmd(subCmdM, args)
|
subCmd, args, ok := cli.getSubCmd(subCmdM, args)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -256,9 +273,9 @@ func (cli *SourceCLI) cliParams(params []Param) (map[string]Param, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cli *SourceCLI) printHelp(
|
func (cli *SourceCLI) printHelp(
|
||||||
|
ctx context.Context,
|
||||||
w io.Writer,
|
w io.Writer,
|
||||||
subCmdPrefix []string,
|
subCmdPrefix []string,
|
||||||
subCmdM map[string]subCmd,
|
|
||||||
pM map[string]Param,
|
pM map[string]Param,
|
||||||
) {
|
) {
|
||||||
type pEntry struct {
|
type pEntry struct {
|
||||||
@ -297,6 +314,7 @@ func (cli *SourceCLI) printHelp(
|
|||||||
subCmd
|
subCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subCmdM, _ := ctx.Value(cliKeySubCmdM).(map[string]subCmd)
|
||||||
subCmdA := make([]subCmdEntry, 0, len(subCmdM))
|
subCmdA := make([]subCmdEntry, 0, len(subCmdM))
|
||||||
for name, subCmd := range subCmdM {
|
for name, subCmd := range subCmdM {
|
||||||
subCmdA = append(subCmdA, subCmdEntry{name: name, subCmd: subCmd})
|
subCmdA = append(subCmdA, subCmdEntry{name: name, subCmd: subCmd})
|
||||||
@ -316,6 +334,9 @@ func (cli *SourceCLI) printHelp(
|
|||||||
if len(pA) > 0 {
|
if len(pA) > 0 {
|
||||||
fmt.Fprint(w, " [options]")
|
fmt.Fprint(w, " [options]")
|
||||||
}
|
}
|
||||||
|
if descr := getCLITailDescr(ctx); descr != "" {
|
||||||
|
fmt.Fprintf(w, " %s", descr)
|
||||||
|
}
|
||||||
fmt.Fprint(w, "\n\n")
|
fmt.Fprint(w, "\n\n")
|
||||||
|
|
||||||
if len(subCmdA) > 0 {
|
if len(subCmdA) > 0 {
|
||||||
|
@ -22,8 +22,7 @@ func TestSourceCLIHelp(t *T) {
|
|||||||
src := &SourceCLI{}
|
src := &SourceCLI{}
|
||||||
pM, err := src.cliParams(CollectParams(ctx))
|
pM, err := src.cliParams(CollectParams(ctx))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
subCmdM, _ := ctx.Value(cliKeySubCmdM).(map[string]subCmd)
|
src.printHelp(ctx, buf, subCmdPrefix, pM)
|
||||||
src.printHelp(buf, subCmdPrefix, subCmdM, pM)
|
|
||||||
|
|
||||||
out := buf.String()
|
out := buf.String()
|
||||||
ok := regexp.MustCompile(exp).MatchString(out)
|
ok := regexp.MustCompile(exp).MatchString(out)
|
||||||
@ -106,6 +105,31 @@ Options:
|
|||||||
--foo \(Default: 5\)
|
--foo \(Default: 5\)
|
||||||
Test int param.
|
Test int param.
|
||||||
|
|
||||||
|
$`)
|
||||||
|
|
||||||
|
ctx, _ = WithCLITail(ctx, "[arg...]")
|
||||||
|
assertHelp(ctx, nil, `^Usage: \S+ <sub-command> \[options\] \[arg\.\.\.\]
|
||||||
|
|
||||||
|
Sub-commands:
|
||||||
|
|
||||||
|
first First sub-command
|
||||||
|
second Second sub-command
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
--baz2 \(Required\)
|
||||||
|
|
||||||
|
--baz3 \(Required\)
|
||||||
|
|
||||||
|
--bar \(Flag\)
|
||||||
|
Test bool param.
|
||||||
|
|
||||||
|
--baz \(Default: "baz"\)
|
||||||
|
Test string param.
|
||||||
|
|
||||||
|
--foo \(Default: 5\)
|
||||||
|
Test int param.
|
||||||
|
|
||||||
$`)
|
$`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +228,7 @@ func TestWithCLITail(t *T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
ctx, tail := WithCLITail(ctx)
|
ctx, tail := WithCLITail(ctx, "foo")
|
||||||
_, err := Populate(ctx, &SourceCLI{Args: tc.args})
|
_, err := Populate(ctx, &SourceCLI{Args: tc.args})
|
||||||
massert.Require(t, massert.Comment(massert.All(
|
massert.Require(t, massert.Comment(massert.All(
|
||||||
massert.Nil(err),
|
massert.Nil(err),
|
||||||
@ -216,15 +240,15 @@ func TestWithCLITail(t *T) {
|
|||||||
func ExampleWithCLITail() {
|
func ExampleWithCLITail() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx, foo := WithInt(ctx, "foo", 1, "Description of foo.")
|
ctx, foo := WithInt(ctx, "foo", 1, "Description of foo.")
|
||||||
ctx, tail := WithCLITail(ctx)
|
ctx, tail := WithCLITail(ctx, "[arg...]")
|
||||||
ctx, bar := WithString(ctx, "bar", "defaultVal", "Description of bar.")
|
ctx, bar := WithString(ctx, "bar", "defaultVal", "Description of bar.")
|
||||||
|
|
||||||
_, err := Populate(ctx, &SourceCLI{
|
_, err := Populate(ctx, &SourceCLI{
|
||||||
Args: []string{"--foo=100", "BADARG", "--bar", "BAR"},
|
Args: []string{"--foo=100", "arg1", "arg2", "arg3"},
|
||||||
})
|
})
|
||||||
|
|
||||||
fmt.Printf("err:%v foo:%v bar:%v tail:%#v\n", err, *foo, *bar, *tail)
|
fmt.Printf("err:%v foo:%v bar:%v tail:%#v\n", err, *foo, *bar, *tail)
|
||||||
// Output: err:<nil> foo:100 bar:defaultVal tail:[]string{"BADARG", "--bar", "BAR"}
|
// Output: err:<nil> foo:100 bar:defaultVal tail:[]string{"arg1", "arg2", "arg3"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithCLISubCommand(t *T) {
|
func TestWithCLISubCommand(t *T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user