From 2c3bf9599001e129a5b2bd56ca7c46c7bac32469 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Fri, 19 May 2023 14:09:20 +0200 Subject: [PATCH] Add simple CLI helper Signed-off-by: Knut Ahlers --- cli/cli.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 cli/cli.go diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000..9b402e1 --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,89 @@ +package cli + +import ( + "errors" + "fmt" + "os" + "sort" + "strings" + "sync" +) + +type ( + // Registry contains a collection of commands to be executed + Registry struct { + cmds map[string]RegistryEntry + sync.Mutex + } + + // Entry defines a sub-command with its parameters, description and + // run function to be called when this command is executed + RegistryEntry struct { + Description string + Name string + Params []string + Run func([]string) error + } +) + +// ErrHelpCalled is returned from the Call function if the given +// command is not found and the help function was executed +var ErrHelpCalled = errors.New("help called") + +// New creates a new Registry +func New() *Registry { + return &Registry{ + cmds: make(map[string]RegistryEntry), + } +} + +// Add adds a new command to the Registry +func (c *Registry) Add(e RegistryEntry) { + c.Lock() + defer c.Unlock() + + c.cmds[e.Name] = e +} + +// Call executes the matchign command from the given arguments +func (c *Registry) Call(args []string) error { + c.Lock() + defer c.Unlock() + + cmdEntry := c.cmds[args[0]] + if cmdEntry.Name != args[0] { + c.help() + return ErrHelpCalled + } + + return cmdEntry.Run(args) +} + +func (c *Registry) help() { + // Called from Call, does not need lock + + var ( + maxCmdLen int + cmds []RegistryEntry + ) + + for name := range c.cmds { + entry := c.cmds[name] + if l := len(entry.commandDisplay()); l > maxCmdLen { + maxCmdLen = l + } + cmds = append(cmds, entry) + } + + sort.Slice(cmds, func(i, j int) bool { return cmds[i].Name < cmds[j].Name }) + + tpl := fmt.Sprintf(" %%-%ds %%s\n", maxCmdLen) + fmt.Fprintln(os.Stdout, "Supported sub-commands are:") + for _, cmd := range cmds { + fmt.Fprintf(os.Stdout, tpl, cmd.commandDisplay(), cmd.Description) + } +} + +func (c RegistryEntry) commandDisplay() string { + return strings.Join(append([]string{c.Name}, c.Params...), " ") +}