feat(cli): separate python commands into own namespace

This commit is contained in:
Marc 2024-01-26 00:27:46 -05:00
parent b6d8cc534d
commit d1200b81d1
Signed by: marc
GPG key ID: 048E042F22B5DC79
2 changed files with 65 additions and 32 deletions

View file

@ -25,27 +25,40 @@ type Command struct {
// Must be initialized with commands via AddCommand before running
// with Run.
type CLI struct {
// Commands in enumeration order.
OrderedCommands []string
// Command metadata entries.
Commands map[string]Command
Namespaces map[string]Namespace
Metadata map[string]string
}
type Namespace struct {
Label string
Commands map[string]Command
OrderedCommands []string
}
func (c *CLI) AddNamespace(namespace Namespace) *CLI {
if c.Namespaces == nil {
c.Namespaces = map[string]Namespace{}
}
c.Namespaces[namespace.Label] = namespace
return c
}
// Registers a command.
// This specifies a label that is used to route the user input to
// the right command, a handler that is called when the label is used,
// and usage/description details that get included in autogenerated help messaging.
func (c CLI) AddCommand(label string, handler func([]string, Flags, state.State) error, usage string, description string) CLI {
if c.Commands == nil {
c.Commands = map[string]Command{}
c.OrderedCommands = []string{}
func (n *Namespace) AddCommand(label string, handler func([]string, Flags, state.State) error, usage string, description string) *Namespace {
if n.Commands == nil {
n.Commands = map[string]Command{}
n.OrderedCommands = []string{}
}
c.OrderedCommands = append(c.OrderedCommands, label)
c.Commands[label] = Command{Label: label, Handler: handler, Usage: usage, Description: description}
n.OrderedCommands = append(n.OrderedCommands, label)
n.Commands[label] = Command{Label: label, Handler: handler, Usage: usage, Description: description}
return c
return n
}
// Executes one of the registered commands if any match the provided
@ -56,26 +69,38 @@ func (c CLI) Run(args []string, currentState state.State) error {
return nil
}
command := args[0]
action := args[0]
if command == "help" {
if action == "help" {
c.Help()
return nil
}
flags := collectFlags(args)
return c.Commands[command].Handler(args, flags, currentState)
namespace, isNamespace := c.Namespaces[action]
if isNamespace {
action = args[1]
return namespace.Commands[action].Handler(args[1:], flags, currentState)
}
rootNamespace := c.Namespaces[""]
return rootNamespace.Commands[action].Handler(args, flags, currentState)
}
// Prints autogenerated help documentation specifying command usage
// and descriptions based on registered commands (see: AddCommand).
func (c CLI) Help() {
logger.InfoLogger.Printf("v: A simple version manager. (v%s)\n---", c.Metadata["Version"])
for _, commandLabel := range c.OrderedCommands {
command := c.Commands[commandLabel]
for _, namespace := range c.Namespaces {
for _, commandLabel := range namespace.OrderedCommands {
command := namespace.Commands[commandLabel]
logger.InfoLogger.Printf("\033[1m%-30s\033[0m%s\n", command.Usage, command.Description)
}
}
}
// Traverses input arguments and extracts flags of
// the form --<flag-label>.

38
v.go
View file

@ -18,27 +18,35 @@ func main() {
args := os.Args[1:]
currentState := state.ReadState()
root := cli.Namespace{Label: ""}
root.AddCommand(
"init", Initialize, "v init", "Initializes the v state.",
)
pythonCommands := cli.Namespace{Label: "python"}
pythonCommands.AddCommand(
"install", python.InstallPython, "v python install <version>", "Downloads, builds and installs a new version of Python.",
).AddCommand(
"uninstall", python.UninstallPython, "v python uninstall <version>", "Uninstalls the given Python version.",
).AddCommand(
"use", python.Use, "v python use <version>", "Selects which Python version to use.",
).AddCommand(
"ls", python.ListVersions, "v python ls", "Lists the installed Python versions.",
).AddCommand(
"version", python.CurrentVersion, "v python version", "Prints the current version and its source.",
).AddCommand(
"which", python.Which, "v python which", "Prints the path to the current Python version.",
)
cli := cli.CLI{
Metadata: map[string]string{
"Version": Version,
},
}
err := cli.AddCommand(
"install", python.InstallPython, "v install <version>", "Downloads, builds and installs a new version of Python.",
).AddCommand(
"uninstall", python.UninstallPython, "v uninstall <version>", "Uninstalls the given Python version.",
).AddCommand(
"use", python.Use, "v use <version>", "Selects which Python version to use.",
).AddCommand(
"ls", python.ListVersions, "v ls", "Lists the installed Python versions.",
).AddCommand(
"version", python.CurrentVersion, "v version", "Prints the current version and its source.",
).AddCommand(
"which", python.Which, "v which", "Prints the path to the current Python version.",
).AddCommand(
"init", Initialize, "v init", "Initializes the v state.",
).Run(args, currentState)
cli.AddNamespace(root).AddNamespace(pythonCommands)
err := cli.Run(args, currentState)
if err != nil {
panic(err)