Skip to content

feat(Command): filter subcommands #231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,54 @@ func (c *Command) Resolve(pth []string) ([]*Command, error) {
return cmds, nil
}

// FilterSubcommands returns a subset of the subcommands in this root with only the ones
// passed as argument with the format: array of strings containing slash-delimited
// subcommands as:
// * "cmd/subcmd/subsubcmd"
// * "block/get"
// * "dag/put"
// If a command path as "0/1/2" is specified only midway like "0/1" it is interpreted
// as allowing all of its subcommands, like "0/1/*".
func (c *Command) FilterSubcommands(subCommands []string) (map[string]*Command, error) {
filteredRoot := make(map[string]*Command)

for _, s := range subCommands {
cmdName := strings.Split(s, "/")
cmdPath, err := c.Resolve(cmdName)
if err != nil {
return nil, err
}
// Discard the root command, we return the map of its subcommands
// (just because go-ipfs consumes it that way).
cmdPath = cmdPath[1:]

filtered := filteredRoot
for i, node := range cmdPath {
name := cmdName[i]
if i == len(cmdPath)-1 {
// Last specified path, allow any subcommand so just copy the
// entire thing.
filtered[name] = node
} else {
// More qualifiers to come in the path, only copy the entry
// (if needed) with no subcommands.
if _, ok := filtered[name]; !ok {
filtered[name] = node.copyWithoutSubCommands()
}
}
filtered = filtered[name].Subcommands
}
}

return filteredRoot, nil
}

func (c *Command) copyWithoutSubCommands() *Command {
copied := *c
copied.Subcommands = make(map[string]*Command)
return &copied
}

// Get resolves and returns the Command addressed by path
func (c *Command) Get(path []string) (*Command, error) {
cmds, err := c.Resolve(path)
Expand Down
77 changes: 77 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import (
"errors"
"fmt"
"io"
"sort"
"strconv"
"testing"
"time"

"github.com/stretchr/testify/require"
)

// NOTE: helpers nopCloser, testEmitter, noop and writeCloser are defined in helpers_test.go
Expand Down Expand Up @@ -127,6 +131,79 @@ func TestResolving(t *testing.T) {
}
}

func fullCmdTree(numberOfSubs int, depth int) *Command {
cmd := &Command{}
cmd.Subcommands = make(map[string]*Command)
for i := 0; i < numberOfSubs; i++ {
var sub *Command
if depth == 0 {
sub = &Command{}
} else {
sub = fullCmdTree(numberOfSubs, depth-1)
}
cmd.Subcommands[strconv.FormatInt(int64(i), 10)] = sub
}
return cmd
}

func listAllCmds(cmd *Command, prefix string) []string {
if cmd.Subcommands == nil || len(cmd.Subcommands) == 0 {
if prefix == "" {
return []string{}
}
return []string{prefix}
}

subStr := make([]string, 0)
for k, s := range cmd.Subcommands {
newPrefix := k
if prefix != "" {
newPrefix = prefix + "/" + newPrefix
}
subStr = append(subStr, listAllCmds(s, newPrefix)...)
}
sort.Strings(subStr)
return subStr
}

func TestFilterSubCommands(t *testing.T) {
cmd := fullCmdTree(2, 2)
all := []string{
"0/0/0",
"0/0/1",
"0/1/0",
"0/1/1",
"1/0/0",
"1/0/1",
"1/1/0",
"1/1/1"}
require.Equal(t, all, listAllCmds(cmd, ""))

checkFilter := func(filter []string, expected []string) {
filtered, err := cmd.FilterSubcommands(filter)
require.NoError(t, err)
require.Equal(t, expected, listAllCmds(&Command{Subcommands: filtered}, ""))
}

checkFilter(nil, []string{})
checkFilter(all, all)
checkFilter([]string{"0/0/0"}, []string{"0/0/0"})
checkFilter([]string{"0/0"}, []string{ // implicit subcommands
"0/0/0",
"0/0/1"})
checkFilter([]string{"0"}, []string{ // even more implicit subcommands
"0/0/0",
"0/0/1",
"0/1/0",
"0/1/1"})
checkFilter([]string{"1", "1/1"}, []string{ // redundant second filter
"1/0/0",
"1/0/1",
"1/1/0",
"1/1/1"})
checkFilter([]string{"0/0/0", "1/1/1"}, []string{"0/0/0", "1/1/1"}) // independent
}

func TestWalking(t *testing.T) {
cmdA := &Command{
Subcommands: map[string]*Command{
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/ipfs/go-ipfs-files v0.0.8
github.com/ipfs/go-log v1.0.4
github.com/rs/cors v1.7.0
github.com/stretchr/testify v1.7.1
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d
)
9 changes: 7 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ github.com/ipfs/go-log/v2 v2.0.5 h1:fL4YI+1g5V/b1Yxr1qAiXTMg1H8z9vx/VmJxBuQMHvU=
github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
Expand All @@ -32,8 +34,9 @@ github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s=
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
Expand Down Expand Up @@ -68,9 +71,11 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "v0.8.1"
"version": "v0.8.2"
}