v/cli/cli.go

117 lines
2.3 KiB
Go

package cli
import (
"os"
"slices"
"strings"
logger "v/logger"
state "v/state"
)
type Flags struct {
AddPath bool
NoCache bool
Verbose bool
RawOutput bool
}
// Represents a CLI invocation.
// Must be initialized with commands via AddCommand before running
// with Run.
type CLI struct {
Namespaces map[string]Namespace
Metadata map[string]string
}
func (c *CLI) AddNamespace(namespace Namespace) *CLI {
if c.Namespaces == nil {
c.Namespaces = map[string]Namespace{}
}
c.Namespaces[namespace.Label] = namespace
return c
}
func (c *CLI) ListNamespaces() []string {
labels := []string{}
for key := range c.Namespaces {
labels = append(labels, key)
}
slices.Sort(labels)
return labels
}
// Executes one of the registered commands if any match the provided
// user arguments.
func (c CLI) Run(args []string, currentState state.State) error {
flags := collectFlags(args)
if flags.Verbose {
logger.DebugLogger.SetOutput(os.Stdout)
}
if len(args) == 0 {
c.Help()
return nil
}
action := args[0]
if action == "help" {
c.Help()
return nil
}
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 _, namespaceLabel := range c.ListNamespaces() {
namespace := c.Namespaces[namespaceLabel]
for _, commandLabel := range namespace.ListCommands() {
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>.
func collectFlags(args []string) Flags {
collected := Flags{}
for _, arg := range args {
if !strings.HasPrefix(arg, "--") {
continue
}
switch arg {
case "--verbose":
collected.Verbose = true
case "--no-cache":
collected.NoCache = true
case "--add-path":
collected.AddPath = true
case "--raw":
collected.RawOutput = true
}
}
return collected
}