Skip to content

Commit b551d34

Browse files
authored
CLOUDP-138375: [cobra2snooty] better arg detection (#21)
1 parent bcfd605 commit b551d34

File tree

3 files changed

+118
-34
lines changed

3 files changed

+118
-34
lines changed

cobra2snooty_test.go

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
// Copyright 2022 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
115
package cobra2snooty
216

317
import (
@@ -53,14 +67,12 @@ func Echo() *cobra.Command {
5367
return echoCmd
5468
}
5569
echoCmd = &cobra.Command{
56-
Use: "echo <string to echo> [test param]",
70+
Use: "echo <string to print> [test param]",
5771
Aliases: []string{"say"},
5872
Short: "Echo anything to the screen",
5973
Long: "an utterly useless command for testing",
6074
Example: "Just run root echo",
6175
Annotations: map[string]string{
62-
"args": "string to print, test param",
63-
"requiredArgs": "string to print",
6476
"string to printDesc": "A string to print",
6577
"test paramDesc": "just for testing",
6678
},
@@ -108,7 +120,7 @@ var deprecatedCmd = &cobra.Command{
108120
}
109121

110122
func TestGenDocs(t *testing.T) {
111-
// We generate on a subcommand so we have both subcommands and parents
123+
// We generate on a subcommand, so we have both subcommands and parents
112124
buf := new(bytes.Buffer)
113125
Root() // init root
114126
if err := GenDocs(Echo(), buf); err != nil {
@@ -166,7 +178,13 @@ func TestGenDocsNoTag(t *testing.T) {
166178
}
167179

168180
func TestGenTreeDocs(t *testing.T) {
169-
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
181+
c := &cobra.Command{
182+
Use: "do <arg1> [arg2]",
183+
Annotations: map[string]string{
184+
"arg1Desc": "desc",
185+
"arg2Desc": "desc",
186+
},
187+
}
170188

171189
tmpdir, err := ioutil.TempDir("", "test-gen-rst-tree")
172190
if err != nil {
@@ -212,3 +230,48 @@ func checkStringOmits(t *testing.T, got, expected string) {
212230
t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
213231
}
214232
}
233+
234+
func TestArgsRegex(t *testing.T) {
235+
t.Run("simple", func(t *testing.T) {
236+
result := argsRegex.FindAllString("<arg1> [arg2]", -1)
237+
expected := []string{"<arg1>", "[arg2]"}
238+
for i := range result {
239+
if result[i] != expected[i] {
240+
t.Fatalf("expected: %s, got: %s\n", expected[i], result[i])
241+
}
242+
}
243+
})
244+
t.Run("with spaces", func(t *testing.T) {
245+
result := argsRegex.FindAllString("<this arg1> [that arg2]", -1)
246+
expected := []string{"<this arg1>", "[that arg2]"}
247+
for i := range result {
248+
if result[i] != expected[i] {
249+
t.Fatalf("expected: %s, got: %s\n", expected[i], result[i])
250+
}
251+
}
252+
})
253+
t.Run("repeating", func(t *testing.T) {
254+
result := argsRegex.FindAllString("<arg1>... [arg2]...", -1)
255+
expected := []string{"<arg1>", "[arg2]"}
256+
for i := range result {
257+
if result[i] != expected[i] {
258+
t.Fatalf("expected: %s, got: %s\n", expected[i], result[i])
259+
}
260+
}
261+
})
262+
t.Run("empty", func(t *testing.T) {
263+
result := argsRegex.FindAllString("<> []", -1)
264+
if len(result) != 0 {
265+
t.Fatalf("expected no matches\n")
266+
}
267+
})
268+
t.Run("complex", func(t *testing.T) {
269+
result := argsRegex.FindAllString("<this arg1> <that arg2> [optional] [long option]", -1)
270+
expected := []string{"<this arg1>", "<that arg2>", "[optional]", "[long option]"}
271+
for i := range result {
272+
if result[i] != expected[i] {
273+
t.Fatalf("expected: %s, got: %s\n", expected[i], result[i])
274+
}
275+
}
276+
})
277+
}

options.go

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
1+
// Copyright 2022 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
115
package cobra2snooty
216

317
import (
418
"bytes"
519
"errors"
620
"fmt"
21+
"regexp"
722
"strings"
823

924
"github.com/spf13/cobra"
@@ -19,30 +34,31 @@ const optionsHeader = `.. list-table::
1934
- Description
2035
`
2136

22-
var ErrMissingDescription = errors.New("missing description")
37+
var (
38+
ErrMissingDescription = errors.New("missing description")
39+
argsRegex = regexp.MustCompile(`<[^>]+>|\[[^]]+]`)
40+
)
2341

2442
func printArgs(buf *bytes.Buffer, cmd *cobra.Command) error {
25-
if args, ok := cmd.Annotations["args"]; ok {
26-
buf.WriteString("Arguments\n")
27-
buf.WriteString("---------\n\n")
28-
buf.WriteString(optionsHeader)
29-
var requiredSlice []string
30-
if requiredArgs, hasRequired := cmd.Annotations["requiredArgs"]; hasRequired {
31-
requiredSlice = strings.Split(requiredArgs, ",")
32-
}
33-
34-
for _, arg := range strings.Split(args, ",") {
35-
trimmedArg := strings.TrimSpace(arg)
36-
required := stringInSlice(requiredSlice, trimmedArg)
37-
if description, hasDescription := cmd.Annotations[trimmedArg+"Desc"]; hasDescription {
38-
line := fmt.Sprintf(" * - %s\n - string\n - %v\n - %s\n", trimmedArg, required, description)
39-
buf.WriteString(line)
40-
} else {
41-
return fmt.Errorf("%w: %s - %s", ErrMissingDescription, cmd.Use, trimmedArg)
42-
}
43+
u := argsRegex.FindAllString(cmd.Use, -1)
44+
if len(u) == 0 {
45+
return nil
46+
}
47+
buf.WriteString("Arguments\n")
48+
buf.WriteString("---------\n\n")
49+
buf.WriteString(optionsHeader)
50+
for _, a := range u {
51+
value := a[1 : len(a)-1]
52+
if description, hasDescription := cmd.Annotations[value+"Desc"]; hasDescription {
53+
required := strings.HasPrefix(a, "<")
54+
line := fmt.Sprintf(" * - %s\n - string\n - %v\n - %s\n", value, required, description)
55+
buf.WriteString(line)
56+
} else {
57+
return fmt.Errorf("%w: %s - %s", ErrMissingDescription, cmd.CommandPath(), value)
4358
}
44-
buf.WriteString("\n")
4559
}
60+
buf.WriteString("\n")
61+
4662
return nil
4763
}
4864

strings.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
// Copyright 2022 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
115
package cobra2snooty
216

317
// adapted from: https://github.com/kr/text/blob/main/indent.go
@@ -15,12 +29,3 @@ func indentString(s, p string) string {
1529
}
1630
return string(res)
1731
}
18-
19-
func stringInSlice(a []string, x string) bool {
20-
for _, b := range a {
21-
if b == x {
22-
return true
23-
}
24-
}
25-
return false
26-
}

0 commit comments

Comments
 (0)