Skip to content

Commit 37bea1f

Browse files
committed
feat(Command): filter subcommands
1 parent e9b3867 commit 37bea1f

File tree

5 files changed

+134
-3
lines changed

5 files changed

+134
-3
lines changed

command.go

+48
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,54 @@ func (c *Command) Resolve(pth []string) ([]*Command, error) {
210210
return cmds, nil
211211
}
212212

213+
// FilterSubcommands returns a subset of the subcommands in this root with only the ones
214+
// passed as argument with the format: array of strings containing slash-delimited
215+
// subcommands as:
216+
// * "cmd/subcmd/subsubcmd"
217+
// * "block/get"
218+
// * "dag/put"
219+
// If a command path as "0/1/2" is specified only midway like "0/1" it is interpreted
220+
// as allowing all of its subcommands, like "0/1/*".
221+
func (c *Command) FilterSubcommands(subCommands []string) (map[string]*Command, error) {
222+
filteredRoot := make(map[string]*Command)
223+
224+
for _, s := range subCommands {
225+
cmdName := strings.Split(s, "/")
226+
cmdPath, err := c.Resolve(cmdName)
227+
if err != nil {
228+
return nil, err
229+
}
230+
// Discard the root command, we return the map of its subcommands
231+
// (just because go-ipfs consumes it that way).
232+
cmdPath = cmdPath[1:]
233+
234+
filtered := filteredRoot
235+
for i, node := range cmdPath {
236+
name := cmdName[i]
237+
if i == len(cmdPath)-1 {
238+
// Last specified path, allow any subcommand so just copy the
239+
// entire thing.
240+
filtered[name] = node
241+
} else {
242+
// More qualifiers to come in the path, only copy the entry
243+
// (if needed) with no subcommands.
244+
if _, ok := filtered[name]; !ok {
245+
filtered[name] = node.copyWithoutSubCommands()
246+
}
247+
}
248+
filtered = filtered[name].Subcommands
249+
}
250+
}
251+
252+
return filteredRoot, nil
253+
}
254+
255+
func (c *Command) copyWithoutSubCommands() *Command {
256+
copied := *c
257+
copied.Subcommands = make(map[string]*Command)
258+
return &copied
259+
}
260+
213261
// Get resolves and returns the Command addressed by path
214262
func (c *Command) Get(path []string) (*Command, error) {
215263
cmds, err := c.Resolve(path)

command_test.go

+77
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ import (
55
"errors"
66
"fmt"
77
"io"
8+
"sort"
9+
"strconv"
810
"testing"
911
"time"
12+
13+
"github.com/stretchr/testify/require"
1014
)
1115

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

134+
func fullCmdTree(numberOfSubs int, depth int) *Command {
135+
cmd := &Command{}
136+
cmd.Subcommands = make(map[string]*Command)
137+
for i := 0; i < numberOfSubs; i++ {
138+
var sub *Command
139+
if depth == 0 {
140+
sub = &Command{}
141+
} else {
142+
sub = fullCmdTree(numberOfSubs, depth-1)
143+
}
144+
cmd.Subcommands[strconv.FormatInt(int64(i), 10)] = sub
145+
}
146+
return cmd
147+
}
148+
149+
func listAllCmds(cmd *Command, prefix string) []string {
150+
if cmd.Subcommands == nil || len(cmd.Subcommands) == 0 {
151+
if prefix == "" {
152+
return []string{}
153+
}
154+
return []string{prefix}
155+
}
156+
157+
subStr := make([]string, 0)
158+
for k, s := range cmd.Subcommands {
159+
newPrefix := k
160+
if prefix != "" {
161+
newPrefix = prefix + "/" + newPrefix
162+
}
163+
subStr = append(subStr, listAllCmds(s, newPrefix)...)
164+
}
165+
sort.Strings(subStr)
166+
return subStr
167+
}
168+
169+
func TestFilterSubCommands(t *testing.T) {
170+
cmd := fullCmdTree(2, 2)
171+
all := []string{
172+
"0/0/0",
173+
"0/0/1",
174+
"0/1/0",
175+
"0/1/1",
176+
"1/0/0",
177+
"1/0/1",
178+
"1/1/0",
179+
"1/1/1"}
180+
require.Equal(t, all, listAllCmds(cmd, ""))
181+
182+
checkFilter := func(filter []string, expected []string) {
183+
filtered, err := cmd.FilterSubcommands(filter)
184+
require.NoError(t, err)
185+
require.Equal(t, expected, listAllCmds(&Command{Subcommands: filtered}, ""))
186+
}
187+
188+
checkFilter(nil, []string{})
189+
checkFilter(all, all)
190+
checkFilter([]string{"0/0/0"}, []string{"0/0/0"})
191+
checkFilter([]string{"0/0"}, []string{ // implicit subcommands
192+
"0/0/0",
193+
"0/0/1"})
194+
checkFilter([]string{"0"}, []string{ // even more implicit subcommands
195+
"0/0/0",
196+
"0/0/1",
197+
"0/1/0",
198+
"0/1/1"})
199+
checkFilter([]string{"1", "1/1"}, []string{ // redundant second filter
200+
"1/0/0",
201+
"1/0/1",
202+
"1/1/0",
203+
"1/1/1"})
204+
checkFilter([]string{"0/0/0", "1/1/1"}, []string{"0/0/0", "1/1/1"}) // independent
205+
}
206+
130207
func TestWalking(t *testing.T) {
131208
cmdA := &Command{
132209
Subcommands: map[string]*Command{

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/ipfs/go-ipfs-files v0.0.8
88
github.com/ipfs/go-log v1.0.4
99
github.com/rs/cors v1.7.0
10+
github.com/stretchr/testify v1.7.1
1011
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e
1112
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d
1213
)

go.sum

+7-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ github.com/ipfs/go-log/v2 v2.0.5 h1:fL4YI+1g5V/b1Yxr1qAiXTMg1H8z9vx/VmJxBuQMHvU=
1818
github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
1919
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
2020
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
21+
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
2122
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
2223
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
24+
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
2325
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
2426
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
2527
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -32,8 +34,9 @@ github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
3234
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
3335
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
3436
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
35-
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
3637
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
38+
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
39+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3740
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s=
3841
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g=
3942
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
@@ -68,9 +71,11 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64
6871
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
6972
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
7073
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
74+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
7175
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7276
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
73-
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
7477
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
78+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
79+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7580
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
7681
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

version.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "v0.8.1"
2+
"version": "v0.8.2"
33
}

0 commit comments

Comments
 (0)