Skip to content

Commit 9d68797

Browse files
committed
help: simplify implementation of help subsystem
Following changes are applied: - the main help command is implemented in a way similar to the other commands (choice between internal and external variation of the command is handled with the common handler cmd.RunModuleFunc) - help for every external command is tied to a command itself over cmd.SetHelp function rather than add separate help subcommand to the main help command (it allows to obtain help for internal and external commands uniformly) - corresponding Cobra commands are created for all available external commands rather than only for the invoked one (being combined with the uniform handling mentioned above it allows to make implementation of the internal variation of help command trivial) - external commands are also implemented over the common handler cmd.RunModuleFunc to reduce code duplication Close #1136
1 parent 22ce087 commit 9d68797

File tree

4 files changed

+46
-119
lines changed

4 files changed

+46
-119
lines changed

cli/cmd/external.go

+27-46
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
package cmd
22

33
import (
4-
"strings"
5-
6-
"github.com/apex/log"
74
"github.com/spf13/cobra"
85
"github.com/tarantool/tt/cli/modules"
9-
"github.com/tarantool/tt/cli/util"
6+
"golang.org/x/exp/slices"
107
)
118

129
// configureExternalCmd configures external commands.
13-
func configureExternalCmd(rootCmd *cobra.Command,
14-
modulesInfo *modules.ModulesInfo, forceInternal bool, args []string) {
10+
func configureExternalCmd(rootCmd *cobra.Command, modulesInfo *modules.ModulesInfo,
11+
forceInternal bool) {
1512
configureExistsCmd(rootCmd, modulesInfo, forceInternal)
16-
configureNonExistentCmd(rootCmd, modulesInfo, args)
13+
configureNonExistentCmd(rootCmd, modulesInfo)
1714
}
1815

1916
// configureExistsCmd configures an external commands
@@ -29,54 +26,38 @@ func configureExistsCmd(rootCmd *cobra.Command, modulesInfo *modules.ModulesInfo
2926

3027
// configureNonExistentCmd configures an external command that
3128
// has no internal implementation within the Tarantool CLI.
32-
func configureNonExistentCmd(rootCmd *cobra.Command,
33-
modulesInfo *modules.ModulesInfo, args []string) {
34-
// Since the user can pass flags, to determine the name of
35-
// an external command we have to take the first non-flag argument.
36-
externalCmd := args[0]
37-
for _, name := range args {
38-
if !strings.HasPrefix(name, "-") && name != "help" {
39-
externalCmd = name
40-
break
41-
}
42-
}
43-
29+
func configureNonExistentCmd(rootCmd *cobra.Command, modulesInfo *modules.ModulesInfo) {
4430
// We avoid overwriting existing commands - we should add a command only
4531
// if it doesn't have an internal implementation in Tarantool CLI.
32+
// So first collect list of internal command names.
33+
internalCmdNames := []string{"help"}
4634
for _, cmd := range rootCmd.Commands() {
47-
if cmd.Name() == externalCmd {
48-
return
49-
}
35+
internalCmdNames = append(internalCmdNames, cmd.Name())
5036
}
5137

52-
helpCmd := util.GetHelpCommand(rootCmd)
53-
externalCmdPath := rootCmd.Name() + " " + externalCmd
54-
if _, found := (*modulesInfo)[externalCmdPath]; found {
55-
rootCmd.AddCommand(newExternalCommand(modulesInfo, externalCmd,
56-
externalCmdPath, nil))
57-
helpCmd.AddCommand(newExternalCommand(modulesInfo, externalCmd, externalCmdPath,
58-
[]string{"--help"}))
38+
// Add external command only if it doesn't have an internal implementation in Tarantool CLI.
39+
for _, manifest := range *modulesInfo {
40+
if !slices.Contains(internalCmdNames, manifest.Name) {
41+
rootCmd.AddCommand(newExternalCmd(manifest))
42+
}
5943
}
6044
}
6145

62-
// newExternalCommand returns a pointer to a new external
46+
// newExternalCmd returns a pointer to a new external
6347
// command that will call modules.RunCmd.
64-
func newExternalCommand(modulesInfo *modules.ModulesInfo,
65-
cmdName, cmdPath string, addArgs []string) *cobra.Command {
66-
cmd := &cobra.Command{
67-
Use: cmdName,
68-
Run: func(cmd *cobra.Command, args []string) {
69-
if addArgs != nil {
70-
args = append(args, addArgs...)
71-
}
72-
73-
cmdCtx.Cli.ForceInternal = false
74-
if err := modules.RunCmd(&cmdCtx, cmdPath, modulesInfo, nil, args); err != nil {
75-
log.Fatalf(err.Error())
76-
}
77-
},
48+
func newExternalCmd(manifest modules.Manifest) *cobra.Command {
49+
var cmd = &cobra.Command{
50+
Use: manifest.Name,
51+
Run: RunModuleFunc(nil),
52+
DisableFlagParsing: true,
7853
}
79-
80-
cmd.DisableFlagParsing = true
54+
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
55+
help, err := modules.GetExternalModuleHelp(manifest.Name)
56+
if err != nil {
57+
cmd.PrintErrf("failed to get help for module %q: %s\n", manifest.Name, err)
58+
return
59+
}
60+
cmd.Print(help)
61+
})
8162
return cmd
8263
}

cli/cmd/help.go

+17-57
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,42 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65
"strings"
76

8-
"github.com/apex/log"
97
"github.com/spf13/cobra"
108
"github.com/tarantool/tt/cli/cmdcontext"
119
"github.com/tarantool/tt/cli/modules"
1210
"github.com/tarantool/tt/cli/util"
1311
)
1412

15-
// DefaultHelpFunc is a type of the standard built-in
16-
// cobra implementation of the help function.
17-
type DefaultHelpFunc func(*cobra.Command, []string)
18-
19-
// configureHelpCommand configures our own help module
20-
// so that it can be external as well.
21-
// If the help is called for an external module
22-
// (for example `tt help version`), we try to get the help from it.
23-
func configureHelpCommand(cmdCtx *cmdcontext.CmdCtx, rootCmd *cobra.Command) error {
13+
func configureHelpCommand(rootCmd *cobra.Command, modulesInfo *modules.ModulesInfo) error {
2414
// Add information about external modules into help template.
25-
rootCmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, getExternalCommandsString(&modulesInfo)))
26-
defaultHelp := rootCmd.HelpFunc()
27-
28-
rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
29-
cmdCtx.CommandName = cmd.Name()
30-
if len(os.Args) == 1 || util.Find(args, "-h") != -1 || util.Find(args, "--help") != -1 {
31-
defaultHelp(cmd, nil)
32-
return
33-
}
15+
rootCmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, getExternalCommandsString(modulesInfo)))
3416

35-
args = modules.GetDefaultCmdArgs("help")
36-
err := modules.RunCmd(cmdCtx, "tt help", &modulesInfo,
37-
getInternalHelpFunc(cmd, defaultHelp), args)
17+
internalHelpModule := func(cmdCtx *cmdcontext.CmdCtx, args []string) error {
18+
cmd, _, err := rootCmd.Find(args)
3819
if err != nil {
39-
log.Fatalf(err.Error())
20+
return err
4021
}
41-
})
22+
cmd.Help()
23+
return nil
24+
}
25+
26+
helpCmd := &cobra.Command{
27+
Use: "help [command]",
28+
Short: "Help about any command",
29+
Run: RunModuleFunc(internalHelpModule),
30+
}
4231

4332
// Add valid arguments for completion.
44-
helpCmd := util.GetHelpCommand(rootCmd)
45-
for name := range modulesInfo {
46-
helpCmd.ValidArgs = append(helpCmd.ValidArgs, name)
33+
for _, subCmd := range rootCmd.Commands() {
34+
helpCmd.ValidArgs = append(helpCmd.ValidArgs, subCmd.Name())
4735
}
4836

37+
rootCmd.SetHelpCommand(helpCmd)
4938
return nil
5039
}
5140

52-
// getInternalHelpFunc returns a internal implementation of help module.
53-
func getInternalHelpFunc(cmd *cobra.Command, help DefaultHelpFunc) modules.InternalFunc {
54-
return func(cmdCtx *cmdcontext.CmdCtx, args []string) error {
55-
switch manifest, found := modulesInfo[cmd.CommandPath()]; {
56-
// Cases when we have to run the "default" help:
57-
// - `tt help` and no external help module.
58-
// It looks strange: if we type the command `tt help`,
59-
// the call to cmd.Name() returns `tt` and I don’t know
60-
// what is the reason. If there is an external help module,
61-
// then we also cannot be here (see the code below) and it
62-
// is enough to check cmd.Name() == "tt".
63-
// - `tt help -I`
64-
// - `tt --help`, `tt -h` or `tt` (look code above).
65-
case cmd.Name() == "tt", !found:
66-
help(cmd, nil)
67-
// We make a call to the external module (if it exists) with the `--help` flag.
68-
default:
69-
helpMsg, err := modules.GetExternalModuleHelp(manifest.Main)
70-
if err != nil {
71-
return err
72-
}
73-
74-
cmd.Print(helpMsg)
75-
}
76-
77-
return nil
78-
}
79-
}
80-
8141
// getExternalCommandsString returns a pretty string
8242
// of descriptions for external modules.
8343
func getExternalCommandsString(modulesInfo *modules.ModulesInfo) string {

cli/cmd/root.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ After that tt will be able to manage the application using 'replicaset_example'
228228

229229
// Setup decoration for Command's error messages.
230230
rootCmd.SetErr(&logErrorWriterDecorator{rootCmd.ErrOrStderr(), logHandler})
231-
rootCmd.InitDefaultHelpCmd()
232231

233232
return rootCmd
234233
}
@@ -314,12 +313,10 @@ func InitRoot() {
314313

315314
// External commands must be configured in a special way.
316315
// This is necessary, for example, so that we can pass arguments to these commands.
317-
if len(os.Args) > 1 {
318-
configureExternalCmd(rootCmd, &modulesInfo, cmdCtx.Cli.ForceInternal, os.Args[1:])
319-
}
316+
configureExternalCmd(rootCmd, &modulesInfo, cmdCtx.Cli.ForceInternal)
320317

321318
// Configure help command.
322-
err = configureHelpCommand(&cmdCtx, rootCmd)
319+
err = configureHelpCommand(rootCmd, &modulesInfo)
323320
if err != nil {
324321
log.Fatalf("Failed to set up help command: %s", err)
325322
}

cli/util/util.go

-11
Original file line numberDiff line numberDiff line change
@@ -155,17 +155,6 @@ func ParseYAML(path string) (map[string]interface{}, error) {
155155
return raw, nil
156156
}
157157

158-
// GetHelpCommand returns the help command for the passed cmd argument.
159-
func GetHelpCommand(cmd *cobra.Command) *cobra.Command {
160-
for _, subcmd := range cmd.Commands() {
161-
if subcmd.Name() == "help" {
162-
return subcmd
163-
}
164-
}
165-
166-
return nil
167-
}
168-
169158
// GetHomeDir returns current home directory.
170159
func GetHomeDir() (string, error) {
171160
usr, err := user.Current()

0 commit comments

Comments
 (0)