chinampa/pkg/command/command.go

154 lines
4.0 KiB
Go
Raw Permalink Normal View History

2022-12-19 03:04:34 +00:00
// Copyright © 2022 Roberto Hidalgo <chinampa@un.rob.mx>
2022-12-31 05:53:24 +00:00
// SPDX-License-Identifier: Apache-2.0
2022-12-19 03:04:34 +00:00
package command
import (
"fmt"
2022-12-31 05:53:24 +00:00
"strconv"
2022-12-19 03:04:34 +00:00
"strings"
2023-03-20 06:15:53 +00:00
"git.rob.mx/nidito/chinampa/pkg/logger"
2022-12-29 19:05:58 +00:00
"git.rob.mx/nidito/chinampa/pkg/runtime"
2022-12-19 03:04:34 +00:00
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
2023-03-20 06:15:53 +00:00
var log = logger.Sub("chinampa:command")
2022-12-19 03:04:34 +00:00
type HelpFunc func(printLinks bool) string
type Action func(cmd *Command) error
type Command struct {
2023-03-19 01:50:46 +00:00
Path []string `json:"path" yaml:"path"`
2022-12-19 03:04:34 +00:00
// Summary is a short description of a command, on supported shells this is part of the autocomplete prompt
Summary string `json:"summary" yaml:"summary" validate:"required"`
// Description is a long form explanation of how a command works its magic. Markdown is supported
Description string `json:"description" yaml:"description" validate:"required"`
// A list of arguments for a command
Arguments Arguments `json:"arguments" yaml:"arguments" validate:"dive"`
// A map of option names to option definitions
Options Options `json:"options" yaml:"options" validate:"dive"`
HelpFunc HelpFunc `json:"-" yaml:"-"`
// The action to take upon running
2022-12-31 05:53:24 +00:00
Action Action `json:"-" yaml:"-"`
2022-12-19 03:04:34 +00:00
runtimeFlags *pflag.FlagSet
2022-12-31 05:53:24 +00:00
Cobra *cobra.Command `json:"-" yaml:"-"`
// Meta stores application specific stuff
Meta any `json:"meta" yaml:"meta"`
Hidden bool `json:"-" yaml:"-"`
2022-12-19 03:04:34 +00:00
}
2022-12-29 19:05:58 +00:00
func (cmd *Command) IsRoot() bool {
return cmd.FullName() == runtime.Executable
}
2022-12-19 03:04:34 +00:00
func (cmd *Command) SetBindings() *Command {
ptr := cmd
for _, opt := range cmd.Options {
opt.Command = ptr
if opt.Validates() {
opt.Values.command = ptr
}
}
for _, arg := range cmd.Arguments {
arg.Command = ptr
if arg.Validates() {
arg.Values.command = ptr
}
}
return ptr
}
func (cmd *Command) Name() string {
return cmd.Path[len(cmd.Path)-1]
}
func (cmd *Command) FullName() string {
return strings.Join(cmd.Path, " ")
}
func (cmd *Command) FlagSet() *pflag.FlagSet {
if cmd.runtimeFlags == nil {
fs := pflag.NewFlagSet(strings.Join(cmd.Path, " "), pflag.ContinueOnError)
fs.SortFlags = false
fs.Usage = func() {}
for name, opt := range cmd.Options {
switch opt.Type {
case ValueTypeBoolean:
def := false
if opt.Default != nil {
def = opt.Default.(bool)
}
2022-12-23 05:29:58 +00:00
fs.BoolP(name, opt.ShortName, def, opt.Description)
2022-12-31 05:53:24 +00:00
case ValueTypeInt:
def := -1
if opt.Default != nil {
switch val := opt.Default.(type) {
case int:
def = opt.Default.(int)
case string:
casted, err := strconv.Atoi(val)
if err != nil {
2023-03-20 06:15:53 +00:00
log.Warnf("Could not parse default with value <%s> as integer for option <%s>", val, name)
2022-12-31 05:53:24 +00:00
}
def = casted
}
}
fs.IntP(name, opt.ShortName, def, opt.Description)
2022-12-19 03:04:34 +00:00
case ValueTypeDefault, ValueTypeString:
opt.Type = ValueTypeString
def := ""
if opt.Default != nil {
def = fmt.Sprintf("%s", opt.Default)
}
2022-12-23 05:29:58 +00:00
fs.StringP(name, opt.ShortName, def, opt.Description)
2022-12-19 03:04:34 +00:00
default:
// ignore flag
2023-03-20 06:15:53 +00:00
log.Warnf("Ignoring unknown option type <%s> for option <%s>", opt.Type, name)
2022-12-19 03:04:34 +00:00
continue
}
}
2023-04-04 00:13:42 +00:00
2022-12-19 03:04:34 +00:00
cmd.runtimeFlags = fs
}
return cmd.runtimeFlags
}
func (cmd *Command) ParseInput(cc *cobra.Command, args []string) error {
if err := cmd.Arguments.Parse(args); err != nil {
return err
}
2022-12-19 03:04:34 +00:00
skipValidation, _ := cc.Flags().GetBool("skip-validation")
cmd.Options.Parse(cc.Flags())
if !skipValidation {
2023-03-20 06:15:53 +00:00
log.Debug("Validating arguments")
2022-12-19 03:04:34 +00:00
if err := cmd.Arguments.AreValid(); err != nil {
return err
}
2023-03-20 06:15:53 +00:00
log.Debug("Validating flags")
2022-12-19 03:04:34 +00:00
if err := cmd.Options.AreValid(); err != nil {
2023-03-20 06:15:53 +00:00
log.Debugf("Invalid flags for %s: %s", cmd.FullName(), err)
2022-12-19 03:04:34 +00:00
return err
}
}
return nil
}
func (cmd *Command) Run(cc *cobra.Command, args []string) error {
2023-03-20 06:15:53 +00:00
log.Debugf("running command %s", cmd.FullName())
2022-12-19 03:04:34 +00:00
if err := cmd.ParseInput(cc, args); err != nil {
2023-03-20 06:15:53 +00:00
log.Debugf("Parsing input to command %s failed: %s", cmd.FullName(), err)
2022-12-19 03:04:34 +00:00
return err
}
return cmd.Action(cmd)
2022-12-19 03:04:34 +00:00
}
func (cmd *Command) SetCobra(cc *cobra.Command) {
cmd.Cobra = cc
}