add command parser
This commit is contained in:
parent
cf86d63d24
commit
525a2ad966
|
@ -0,0 +1,203 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Name string
|
||||
LongName string
|
||||
ShortName string
|
||||
Description string
|
||||
OType string
|
||||
DefaultValue interface{}
|
||||
}
|
||||
type Command struct {
|
||||
Name string
|
||||
Description string
|
||||
Args []*Option
|
||||
SubCommands []*Command
|
||||
parentCommand *Command
|
||||
subcommand string
|
||||
globalOptions map[string]interface{}
|
||||
subcommandOptions map[string]interface{}
|
||||
}
|
||||
|
||||
func (c *Command) AddArg(name string, shortName string, description string, defaultValue interface{}) {
|
||||
oType := reflect.TypeOf(defaultValue).String()
|
||||
c.Args = append(c.Args, &Option{
|
||||
Name: name,
|
||||
LongName: ToKebabCase(name),
|
||||
ShortName: shortName,
|
||||
Description: description,
|
||||
OType: oType,
|
||||
DefaultValue: defaultValue,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Command) AddSubCommand(name string, description string) *Command {
|
||||
if c.parentCommand != nil {
|
||||
panic("Sub commands can only be added to top level commands")
|
||||
}
|
||||
command := &Command{
|
||||
Name: name,
|
||||
Description: description,
|
||||
parentCommand: c,
|
||||
}
|
||||
c.SubCommands = append(c.SubCommands, command)
|
||||
return command
|
||||
}
|
||||
func (c *Command) Usage() {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (c *Command) GetOption(name string) *Option {
|
||||
if len(name) > 1 {
|
||||
for _, arg := range c.Args {
|
||||
if arg.LongName == name {
|
||||
return arg
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, arg := range c.Args {
|
||||
if arg.ShortName == name {
|
||||
return arg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Command) parse(args []string) {
|
||||
if len(args) == 0 {
|
||||
if len(c.Args) != 0 || len(c.SubCommands) != 0 || c.parentCommand == nil {
|
||||
c.Usage()
|
||||
}
|
||||
if c.parentCommand != nil {
|
||||
c.parentCommand.subcommand = c.Name
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *Command) longOption(args []string) ([]string, bool, error) {
|
||||
if !strings.HasPrefix(args[0], "--") {
|
||||
return args, false, nil
|
||||
}
|
||||
optName := strings.TrimPrefix(args[0], "--")
|
||||
if len(optName) < 2 {
|
||||
return args, false, errors.New("Invalid option name")
|
||||
}
|
||||
opt := c.GetOption(optName)
|
||||
if opt != nil {
|
||||
rargs, _, err := c.getOptValue(args, opt, false)
|
||||
if err == nil {
|
||||
return rargs, true, nil
|
||||
} else {
|
||||
return args, false, err
|
||||
}
|
||||
}
|
||||
if c.parentCommand != nil {
|
||||
return c.parentCommand.longOption(args)
|
||||
}
|
||||
return args, false, errors.New("Unknown option " + args[0])
|
||||
}
|
||||
func (c *Command) shortOption(args []string) ([]string, bool, error) {
|
||||
if !strings.HasPrefix(args[0], "-") {
|
||||
return args, false, nil
|
||||
}
|
||||
optName := strings.TrimPrefix(args[0], "-")
|
||||
last := len(optName) - 1
|
||||
for i, s := range optName {
|
||||
opt := c.GetOption(string(s))
|
||||
if opt != nil {
|
||||
if i != last {
|
||||
_, _, err := c.getOptValue(args[i:], opt, true)
|
||||
if err == nil {
|
||||
return args, true, nil
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
rargs, _, err := c.getOptValue(args[i:], opt, false)
|
||||
if err == nil {
|
||||
return rargs, true, nil
|
||||
} else {
|
||||
return args, false, err
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if c.parentCommand != nil {
|
||||
opt = c.parentCommand.GetOption(string(s))
|
||||
if opt != nil {
|
||||
rargs, _, err := c.parentCommand.getOptValue(args[i:], opt, i != last)
|
||||
if err == nil {
|
||||
return rargs, true, nil
|
||||
} else {
|
||||
return args, false, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return args, false, errors.New("Unknown option " + args[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
return args, false, errors.New("Unknown option " + args[0])
|
||||
}
|
||||
|
||||
func (c *Command) getOptValue(args []string, opt *Option, isFixBool bool) ([]string, interface{}, error) {
|
||||
|
||||
paramMap := c.globalOptions
|
||||
if c.parentCommand != nil {
|
||||
paramMap = c.parentCommand.subcommandOptions
|
||||
}
|
||||
if isFixBool {
|
||||
if opt.OType != "bool" {
|
||||
return args, nil, errors.New("Invalid type for boolean option")
|
||||
}
|
||||
paramMap[opt.Name] = true
|
||||
return args, true, nil
|
||||
}
|
||||
|
||||
if opt.OType == "bool" {
|
||||
nextStr := strings.ToLower(args[1])
|
||||
if nextStr == "f" || nextStr == "false" {
|
||||
paramMap[opt.Name] = false
|
||||
return args[2:], true, nil
|
||||
} else if nextStr == "t" || nextStr == "true" {
|
||||
paramMap[opt.Name] = true
|
||||
return args[2:], true, nil
|
||||
} else {
|
||||
paramMap[opt.Name] = true
|
||||
return args[1:], true, nil
|
||||
}
|
||||
} else {
|
||||
valStr := args[1]
|
||||
switch opt.OType {
|
||||
case "int":
|
||||
val, err := strconv.Atoi(valStr)
|
||||
if err != nil {
|
||||
return args, nil, errors.New("Invalid value for option " + opt.Name)
|
||||
} else {
|
||||
paramMap[opt.Name] = val
|
||||
return args[2:], true, nil
|
||||
}
|
||||
case "string":
|
||||
paramMap[opt.Name] = valStr
|
||||
return args[2:], true, nil
|
||||
case "float64":
|
||||
val, err := strconv.ParseFloat(valStr, 64)
|
||||
if err != nil {
|
||||
return args, nil, errors.New("Invalid value for option " + opt.Name)
|
||||
} else {
|
||||
paramMap[opt.Name] = val
|
||||
return args[2:], true, nil
|
||||
}
|
||||
case "default":
|
||||
return args, nil, errors.New("Unsupported type")
|
||||
}
|
||||
}
|
||||
return args, nil, errors.New("Unsupported type")
|
||||
}
|
Loading…
Reference in New Issue