From bd97edec87a0541321c6e2529150e315ee11cd8b Mon Sep 17 00:00:00 2001 From: Alex Flint Date: Fri, 3 May 2019 16:08:29 -0700 Subject: [PATCH] add Parser.Subcommand and Parser.SubcommandNames --- parse_test.go | 9 +++++++-- subcommand.go | 37 +++++++++++++++++++++++++++++++++++++ subcommand_test.go | 34 +++++++++++++++++++++++++--------- 3 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 subcommand.go diff --git a/parse_test.go b/parse_test.go index 00b0875..c2544fd 100644 --- a/parse_test.go +++ b/parse_test.go @@ -19,15 +19,20 @@ func setenv(t *testing.T, name, val string) { } func parse(cmdline string, dest interface{}) error { + _, err := pparse(cmdline, dest) + return err +} + +func pparse(cmdline string, dest interface{}) (*Parser, error) { p, err := NewParser(Config{}, dest) if err != nil { - return err + return nil, err } var parts []string if len(cmdline) > 0 { parts = strings.Split(cmdline, " ") } - return p.Parse(parts) + return p, p.Parse(parts) } func TestString(t *testing.T) { diff --git a/subcommand.go b/subcommand.go new file mode 100644 index 0000000..b73e933 --- /dev/null +++ b/subcommand.go @@ -0,0 +1,37 @@ +package arg + +// Subcommand returns the user struct for the subcommand selected by +// the command line arguments most recently processed by the parser. +// The return value is always a pointer to a struct. If no subcommand +// was specified then it returns the top-level arguments struct. If +// no command line arguments have been processed by this parser then it +// returns nil. +func (p *Parser) Subcommand() interface{} { + if p.lastCmd == nil || p.lastCmd.parent == nil { + return nil + } + return p.readable(p.lastCmd.dest).Interface() +} + +// SubcommandNames returns the sequence of subcommands specified by the +// user. If no subcommands were given then it returns an empty slice. +func (p *Parser) SubcommandNames() []string { + if p.lastCmd == nil { + return nil + } + + // make a list of ancestor commands + var ancestors []string + cur := p.lastCmd + for cur.parent != nil { // we want to exclude the root + ancestors = append(ancestors, cur.name) + cur = cur.parent + } + + // reverse the list + out := make([]string, len(ancestors)) + for i := 0; i < len(ancestors); i++ { + out[i] = ancestors[len(ancestors)-i-1] + } + return out +} diff --git a/subcommand_test.go b/subcommand_test.go index fdc3ff4..c34ab01 100644 --- a/subcommand_test.go +++ b/subcommand_test.go @@ -41,9 +41,11 @@ func TestMinimalSubcommand(t *testing.T) { var args struct { List *listCmd `arg:"subcommand"` } - err := parse("list", &args) + p, err := pparse("list", &args) require.NoError(t, err) assert.NotNil(t, args.List) + assert.Equal(t, args.List, p.Subcommand()) + assert.Equal(t, []string{"list"}, p.SubcommandNames()) } func TestNoSuchSubcommand(t *testing.T) { @@ -52,7 +54,7 @@ func TestNoSuchSubcommand(t *testing.T) { var args struct { List *listCmd `arg:"subcommand"` } - err := parse("invalid", &args) + _, err := pparse("invalid", &args) assert.Error(t, err) } @@ -62,9 +64,11 @@ func TestNamedSubcommand(t *testing.T) { var args struct { List *listCmd `arg:"subcommand:ls"` } - err := parse("ls", &args) + p, err := pparse("ls", &args) require.NoError(t, err) assert.NotNil(t, args.List) + assert.Equal(t, args.List, p.Subcommand()) + assert.Equal(t, []string{"ls"}, p.SubcommandNames()) } func TestEmptySubcommand(t *testing.T) { @@ -73,9 +77,11 @@ func TestEmptySubcommand(t *testing.T) { var args struct { List *listCmd `arg:"subcommand"` } - err := parse("", &args) + p, err := pparse("", &args) require.NoError(t, err) assert.Nil(t, args.List) + assert.Nil(t, p.Subcommand()) + assert.Empty(t, p.SubcommandNames()) } func TestTwoSubcommands(t *testing.T) { @@ -87,10 +93,12 @@ func TestTwoSubcommands(t *testing.T) { Get *getCmd `arg:"subcommand"` List *listCmd `arg:"subcommand"` } - err := parse("list", &args) + p, err := pparse("list", &args) require.NoError(t, err) assert.Nil(t, args.Get) assert.NotNil(t, args.List) + assert.Equal(t, args.List, p.Subcommand()) + assert.Equal(t, []string{"list"}, p.SubcommandNames()) } func TestSubcommandsWithOptions(t *testing.T) { @@ -185,35 +193,43 @@ func TestNestedSubcommands(t *testing.T) { { var args root - err := parse("grandparent parent child", &args) + p, err := pparse("grandparent parent child", &args) require.NoError(t, err) require.NotNil(t, args.Grandparent) require.NotNil(t, args.Grandparent.Parent) require.NotNil(t, args.Grandparent.Parent.Child) + assert.Equal(t, args.Grandparent.Parent.Child, p.Subcommand()) + assert.Equal(t, []string{"grandparent", "parent", "child"}, p.SubcommandNames()) } { var args root - err := parse("grandparent parent", &args) + p, err := pparse("grandparent parent", &args) require.NoError(t, err) require.NotNil(t, args.Grandparent) require.NotNil(t, args.Grandparent.Parent) require.Nil(t, args.Grandparent.Parent.Child) + assert.Equal(t, args.Grandparent.Parent, p.Subcommand()) + assert.Equal(t, []string{"grandparent", "parent"}, p.SubcommandNames()) } { var args root - err := parse("grandparent", &args) + p, err := pparse("grandparent", &args) require.NoError(t, err) require.NotNil(t, args.Grandparent) require.Nil(t, args.Grandparent.Parent) + assert.Equal(t, args.Grandparent, p.Subcommand()) + assert.Equal(t, []string{"grandparent"}, p.SubcommandNames()) } { var args root - err := parse("", &args) + p, err := pparse("", &args) require.NoError(t, err) require.Nil(t, args.Grandparent) + assert.Nil(t, p.Subcommand()) + assert.Empty(t, p.SubcommandNames()) } }