commplete Command style

This commit is contained in:
程广 2024-11-16 15:15:27 +08:00
parent 9f26d1cf29
commit e20ed14191
3 changed files with 172 additions and 18 deletions

19
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"args":[
"-a",
"ssd"
]
}
]
}

View File

@ -2,9 +2,12 @@ package command
import ( import (
"errors" "errors"
"fmt"
"os"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"text/template"
) )
type Option struct { type Option struct {
@ -15,17 +18,48 @@ type Option struct {
OType string OType string
DefaultValue interface{} DefaultValue interface{}
} }
var UsageTemplate, _ = template.New("Usage").Parse(`Usage:
{{if .ParentCommand }} {{.ParentCommand.Name}} {{end}} {{.Name}} [OPTIONS] [ARGS]
{{ if .Description }}
Description:
{{.Description}}
{{ end }}
{{ if .Args }}
Options:
{{range .Args}}
-{{.ShortName}}|--{{.LongName}} {{.OType}} {{.Description}} default:{{.DefaultValue}}
{{end}}
{{ end }}
{{ if .SubCommands }}
Sub Commands:
{{range .SubCommands}}
{{.Name}} {{.Description}}
{{end}}
{{end}}
`)
type Command struct { type Command struct {
Name string Name string
Description string Description string
Args []*Option Args []*Option
SubCommands []*Command SubCommands []*Command
parentCommand *Command ParentCommand *Command
subcommand string subcommand string
globalOptions map[string]interface{} globalOptions map[string]interface{}
subcommandOptions map[string]interface{} subcommandOptions map[string]interface{}
remainArgs []string
} }
func (c *Command) GetGlobalOptions() map[string]interface{} {
return c.globalOptions
}
func (c *Command) GetSubCommandOptions() map[string]interface{} {
return c.subcommandOptions
}
func (c *Command) GetSubCommand() string {
return c.subcommand
}
func (c *Command) AddArg(name string, shortName string, description string, defaultValue interface{}) { func (c *Command) AddArg(name string, shortName string, description string, defaultValue interface{}) {
oType := reflect.TypeOf(defaultValue).String() oType := reflect.TypeOf(defaultValue).String()
c.Args = append(c.Args, &Option{ c.Args = append(c.Args, &Option{
@ -39,19 +73,19 @@ func (c *Command) AddArg(name string, shortName string, description string, defa
} }
func (c *Command) AddSubCommand(name string, description string) *Command { func (c *Command) AddSubCommand(name string, description string) *Command {
if c.parentCommand != nil { if c.ParentCommand != nil {
panic("Sub commands can only be added to top level commands") panic("Sub commands can only be added to top level commands")
} }
command := &Command{ command := &Command{
Name: name, Name: name,
Description: description, Description: description,
parentCommand: c, ParentCommand: c,
} }
c.SubCommands = append(c.SubCommands, command) c.SubCommands = append(c.SubCommands, command)
return command return command
} }
func (c *Command) Usage() { func (c *Command) Usage() {
panic("not implemented") UsageTemplate.Execute(os.Stdout, c)
} }
func (c *Command) GetOption(name string) *Option { func (c *Command) GetOption(name string) *Option {
if len(name) > 1 { if len(name) > 1 {
@ -68,17 +102,91 @@ func (c *Command) GetOption(name string) *Option {
} }
return nil return nil
} }
func (c *Command) parseError(errMsg string) {
func (c *Command) parse(args []string) { fmt.Println(errMsg)
os.Exit(1)
}
func (c *Command) showHelpWithOption(args []string) {
if len(args) > 0 && (args[0] == "-h" || args[0] == "--help") {
c.Usage()
os.Exit(0)
}
}
func (c *Command) showSubCommandHelp(args []string) {
if len(args) == 2 && args[0] == "help" {
cmd := c.findSubcommand(args[1])
if cmd == nil {
c.parseError("Unknown subcommand " + args[1])
}
cmd.Usage()
os.Exit(0)
}
}
func (c *Command) Parse(args []string) ([]string, error) {
c.showHelpWithOption(args)
c.showSubCommandHelp(args)
if len(args) == 0 { if len(args) == 0 {
if len(c.Args) != 0 || len(c.SubCommands) != 0 || c.parentCommand == nil { if len(c.Args) != 0 || len(c.SubCommands) != 0 || c.ParentCommand == nil {
c.Usage() c.Usage()
} }
if c.parentCommand != nil { if c.ParentCommand != nil {
c.parentCommand.subcommand = c.Name c.ParentCommand.subcommand = c.Name
} }
} }
vargs := args
for {
if len(vargs) == 0 {
break
}
var parsed bool
var err error
vargs, parsed, err = c.longOption(vargs)
if err != nil {
c.parseError(err.Error())
}
if parsed {
continue
}
vargs, parsed, err = c.shortOption(vargs)
if err != nil {
c.parseError(err.Error())
}
if parsed {
continue
}
if len(c.SubCommands) > 0 {
cmd := c.findSubcommand(args[0])
if cmd != nil {
c.subcommand = cmd.Name
vargs = vargs[1:]
vargs, err = cmd.Parse(vargs)
if err != nil {
c.parseError(err.Error())
}
} else {
c.parseError("Unknown subcommand " + args[0])
}
} else {
if c.ParentCommand != nil {
c.ParentCommand.remainArgs = append(c.ParentCommand.remainArgs, vargs[0])
} else {
c.remainArgs = append(c.remainArgs, vargs[0])
}
vargs = vargs[1:]
}
}
return vargs, nil
}
func (c *Command) findSubcommand(cmd string) *Command {
for _, subCmd := range c.SubCommands {
if subCmd.Name == cmd {
return subCmd
}
}
return nil
} }
func (c *Command) longOption(args []string) ([]string, bool, error) { func (c *Command) longOption(args []string) ([]string, bool, error) {
@ -98,8 +206,8 @@ func (c *Command) longOption(args []string) ([]string, bool, error) {
return args, false, err return args, false, err
} }
} }
if c.parentCommand != nil { if c.ParentCommand != nil {
return c.parentCommand.longOption(args) return c.ParentCommand.longOption(args)
} }
return args, false, errors.New("Unknown option " + args[0]) return args, false, errors.New("Unknown option " + args[0])
} }
@ -129,10 +237,10 @@ func (c *Command) shortOption(args []string) ([]string, bool, error) {
} }
} else { } else {
if c.parentCommand != nil { if c.ParentCommand != nil {
opt = c.parentCommand.GetOption(string(s)) opt = c.ParentCommand.GetOption(string(s))
if opt != nil { if opt != nil {
rargs, _, err := c.parentCommand.getOptValue(args[i:], opt, i != last) rargs, _, err := c.ParentCommand.getOptValue(args[i:], opt, i != last)
if err == nil { if err == nil {
return rargs, true, nil return rargs, true, nil
} else { } else {
@ -150,8 +258,8 @@ func (c *Command) shortOption(args []string) ([]string, bool, error) {
func (c *Command) getOptValue(args []string, opt *Option, isFixBool bool) ([]string, interface{}, error) { func (c *Command) getOptValue(args []string, opt *Option, isFixBool bool) ([]string, interface{}, error) {
paramMap := c.globalOptions paramMap := c.globalOptions
if c.parentCommand != nil { if c.ParentCommand != nil {
paramMap = c.parentCommand.subcommandOptions paramMap = c.ParentCommand.subcommandOptions
} }
if isFixBool { if isFixBool {
if opt.OType != "bool" { if opt.OType != "bool" {
@ -196,8 +304,18 @@ func (c *Command) getOptValue(args []string, opt *Option, isFixBool bool) ([]str
return args[2:], true, nil return args[2:], true, nil
} }
case "default": case "default":
return args, nil, errors.New("Unsupported type") return args, nil, errors.New("unsupported type")
} }
} }
return args, nil, errors.New("Unsupported type") return args, nil, errors.New("unsupported type")
}
func NewCommand(name string, desc string) *Command {
return &Command{
Name: name,
Description: desc,
globalOptions: map[string]interface{}{},
subcommandOptions: map[string]interface{}{},
remainArgs: []string{},
}
} }

17
testcmd/main.go Normal file
View File

@ -0,0 +1,17 @@
package main
import (
"fmt"
"os"
"git.pyer.club/kingecg/command"
)
func main() {
var cmd = command.NewCommand("test", "test command")
cmd.AddArg("arg1", "a", "arg1 description", "default value")
cmd.AddArg("arg2", "b", "arg2 description", "default value")
cmd.AddSubCommand("sub1", "sub1 description")
cmd.Parse(os.Args[1:])
fmt.Println(cmd.GetGlobalOptions())
}