forked from TrueCloudLab/rclone
cmd: Implement specialised help for flags and backends - fixes #2541
Instead of showing all flags/backends all the time, you can type rclone help flags rclone help flags <regexp> rclone help backends rclone help backend <name>
This commit is contained in:
parent
1557287c64
commit
14128656db
1 changed files with 119 additions and 8 deletions
127
cmd/help.go
127
cmd/help.go
|
@ -2,7 +2,10 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
"github.com/ncw/rclone/fs/config/configflags"
|
"github.com/ncw/rclone/fs/config/configflags"
|
||||||
|
@ -16,7 +19,7 @@ import (
|
||||||
// Root is the main rclone command
|
// Root is the main rclone command
|
||||||
var Root = &cobra.Command{
|
var Root = &cobra.Command{
|
||||||
Use: "rclone",
|
Use: "rclone",
|
||||||
Short: "Sync files and directories to and from local and remote object stores - " + fs.Version,
|
Short: "Show help for rclone commands, flags and backends.",
|
||||||
Long: `
|
Long: `
|
||||||
Rclone syncs files to and from cloud storage providers as well as
|
Rclone syncs files to and from cloud storage providers as well as
|
||||||
mounting them, listing them in lots of different ways.
|
mounting them, listing them in lots of different ways.
|
||||||
|
@ -36,17 +39,55 @@ var helpCommand = &cobra.Command{
|
||||||
Use: "help",
|
Use: "help",
|
||||||
Short: Root.Short,
|
Short: Root.Short,
|
||||||
Long: Root.Long,
|
Long: Root.Long,
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
Root.SetOutput(os.Stdout)
|
||||||
|
_ = Root.Usage()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to filter the flags with
|
||||||
|
var flagsRe *regexp.Regexp
|
||||||
|
|
||||||
// Show the flags
|
// Show the flags
|
||||||
var helpFlags = &cobra.Command{
|
var helpFlags = &cobra.Command{
|
||||||
Use: "flags",
|
Use: "flags [<regexp to match>]",
|
||||||
Short: "Show the global flags for rclone",
|
Short: "Show the global flags for rclone",
|
||||||
Run: func(command *cobra.Command, args []string) {
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
re, err := regexp.Compile(args[0])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to compile flags regexp: %v", err)
|
||||||
|
}
|
||||||
|
flagsRe = re
|
||||||
|
}
|
||||||
|
Root.SetOutput(os.Stdout)
|
||||||
_ = command.Usage()
|
_ = command.Usage()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show the backends
|
||||||
|
var helpBackends = &cobra.Command{
|
||||||
|
Use: "backends",
|
||||||
|
Short: "List the backends available",
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
showBackends()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show a single backend
|
||||||
|
var helpBackend = &cobra.Command{
|
||||||
|
Use: "backend <name>",
|
||||||
|
Short: "List full info about a backend",
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
Root.SetOutput(os.Stdout)
|
||||||
|
_ = command.Usage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
showBackend(args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// runRoot implements the main rclone command with no subcommands
|
// runRoot implements the main rclone command with no subcommands
|
||||||
func runRoot(cmd *cobra.Command, args []string) {
|
func runRoot(cmd *cobra.Command, args []string) {
|
||||||
if version {
|
if version {
|
||||||
|
@ -81,12 +122,15 @@ func setupRootCommand(rootCmd *cobra.Command) {
|
||||||
return cmd.CalledAs() != "flags"
|
return cmd.CalledAs() != "flags"
|
||||||
})
|
})
|
||||||
cobra.AddTemplateFunc("showLocalFlags", func(cmd *cobra.Command) bool {
|
cobra.AddTemplateFunc("showLocalFlags", func(cmd *cobra.Command) bool {
|
||||||
return cmd.CalledAs() != "rclone"
|
// Don't show local flags (which are the global ones on the root) on "rclone" and
|
||||||
|
// "rclone help" (which shows the global help)
|
||||||
|
return cmd.CalledAs() != "rclone" && cmd.CalledAs() != ""
|
||||||
})
|
})
|
||||||
cobra.AddTemplateFunc("backendFlags", func(cmd *cobra.Command, include bool) *pflag.FlagSet {
|
cobra.AddTemplateFunc("backendFlags", func(cmd *cobra.Command, include bool) *pflag.FlagSet {
|
||||||
backendFlagSet := pflag.NewFlagSet("Backend Flags", pflag.ExitOnError)
|
backendFlagSet := pflag.NewFlagSet("Backend Flags", pflag.ExitOnError)
|
||||||
cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||||
if _, ok := backendFlags[flag.Name]; ok == include {
|
matched := flagsRe == nil || flagsRe.MatchString(flag.Name)
|
||||||
|
if _, ok := backendFlags[flag.Name]; matched && ok == include {
|
||||||
backendFlagSet.AddFlag(flag)
|
backendFlagSet.AddFlag(flag)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -101,8 +145,8 @@ func setupRootCommand(rootCmd *cobra.Command) {
|
||||||
|
|
||||||
rootCmd.AddCommand(helpCommand)
|
rootCmd.AddCommand(helpCommand)
|
||||||
helpCommand.AddCommand(helpFlags)
|
helpCommand.AddCommand(helpFlags)
|
||||||
// rootCmd.AddCommand(helpBackend)
|
helpCommand.AddCommand(helpBackends)
|
||||||
// rootCmd.AddCommand(helpBackends)
|
helpCommand.AddCommand(helpBackend)
|
||||||
|
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
|
|
||||||
|
@ -133,7 +177,74 @@ Backend Flags:
|
||||||
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
||||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}
|
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}
|
||||||
|
|
||||||
Use "{{.CommandPath}} [command] --help" for more information about a command.
|
Use "rclone [command] --help" for more information about a command.
|
||||||
Use "rclone help flags" for more information about global flags.
|
Use "rclone help flags" for to see the global flags.
|
||||||
Use "rclone help backends" for a list of supported services.
|
Use "rclone help backends" for a list of supported services.
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// show all the backends
|
||||||
|
func showBackends() {
|
||||||
|
fmt.Printf("All rclone backends:\n\n")
|
||||||
|
for _, backend := range fs.Registry {
|
||||||
|
fmt.Printf(" %-12s %s\n", backend.Prefix, backend.Description)
|
||||||
|
}
|
||||||
|
fmt.Printf("\nTo see more info about a particular backend use:\n")
|
||||||
|
fmt.Printf(" rclone help backend <name>\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func quoteString(v interface{}) string {
|
||||||
|
switch v.(type) {
|
||||||
|
case string:
|
||||||
|
return fmt.Sprintf("%q", v)
|
||||||
|
}
|
||||||
|
return fmt.Sprint(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// show a single backend
|
||||||
|
func showBackend(name string) {
|
||||||
|
backend, err := fs.Find(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
var standardOptions, advancedOptions fs.Options
|
||||||
|
done := map[string]struct{}{}
|
||||||
|
for _, opt := range backend.Options {
|
||||||
|
// Skip if done already (eg with Provider options)
|
||||||
|
if _, doneAlready := done[opt.Name]; doneAlready {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if opt.Advanced {
|
||||||
|
advancedOptions = append(advancedOptions, opt)
|
||||||
|
} else {
|
||||||
|
standardOptions = append(standardOptions, opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
optionsType := "standard"
|
||||||
|
for _, opts := range []fs.Options{standardOptions, advancedOptions} {
|
||||||
|
if len(opts) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("### %s Options\n\n", strings.Title(optionsType))
|
||||||
|
fmt.Printf("Here are the %s options specific to %s (%s).\n\n", optionsType, backend.Name, backend.Description)
|
||||||
|
optionsType = "advanced"
|
||||||
|
for _, opt := range opts {
|
||||||
|
done[opt.Name] = struct{}{}
|
||||||
|
fmt.Printf("#### --%s\n\n", opt.FlagName(backend.Prefix))
|
||||||
|
fmt.Printf("%s\n\n", opt.Help)
|
||||||
|
fmt.Printf("- Config: %s\n", opt.Name)
|
||||||
|
fmt.Printf("- Env Var: %s\n", opt.EnvVarName(backend.Prefix))
|
||||||
|
fmt.Printf("- Type: %s\n", opt.Type())
|
||||||
|
fmt.Printf("- Default: %s\n", quoteString(opt.GetValue()))
|
||||||
|
if len(opt.Examples) > 0 {
|
||||||
|
fmt.Printf("- Examples:\n")
|
||||||
|
for _, ex := range opt.Examples {
|
||||||
|
fmt.Printf(" - %s\n", quoteString(ex.Value))
|
||||||
|
for _, line := range strings.Split(ex.Help, "\n") {
|
||||||
|
fmt.Printf(" - %s\n", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue