Refactor the option parsing code to use methods on a struct
This commit is contained in:
parent
34bd7544b1
commit
f4c5adc4c7
11
command.go
11
command.go
|
@ -1,6 +1,7 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -67,14 +68,10 @@ func (c *Command) Execute(args []string) {
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
restArgs, err := c.opts.Parse(args)
|
parser := opts.NewParser(args, c.opts)
|
||||||
if err != nil {
|
restArgs, err := parser.Parse()
|
||||||
c.ShowHelp()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
restArgs, err = opts.Globals().Parse(restArgs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Option error: %s\n", err.Error())
|
||||||
c.ShowHelp()
|
c.ShowHelp()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package opts
|
package opts
|
||||||
|
|
||||||
import "strconv"
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
type BoolOption struct {
|
type BoolOption struct {
|
||||||
name string
|
name string
|
||||||
|
|
242
opts/parser.go
242
opts/parser.go
|
@ -2,7 +2,6 @@ package opts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,133 +10,136 @@ var (
|
||||||
ErrCannotChainOption = errors.New("cannot chain option as it takes an argument")
|
ErrCannotChainOption = errors.New("cannot chain option as it takes an argument")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s Set) Parse(args []string) (restArgs []string, err error) {
|
type Parser struct {
|
||||||
for i := 0; i < len(args); i++ {
|
opts Set
|
||||||
arg := args[i]
|
args []string
|
||||||
|
curr int
|
||||||
|
}
|
||||||
|
|
||||||
// Handle regular argument
|
func NewParser(args []string, opts []Option) *Parser {
|
||||||
if !strings.HasPrefix(arg, "-") {
|
return &Parser{
|
||||||
|
opts: append(opts, globalOpts...),
|
||||||
|
args: args,
|
||||||
|
curr: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Parse() (restArgs []string, err error) {
|
||||||
|
for p.hasNext() {
|
||||||
|
arg := p.next()
|
||||||
|
|
||||||
|
if !strings.HasPrefix(arg, "-") { // Regular argument
|
||||||
restArgs = append(restArgs, arg)
|
restArgs = append(restArgs, arg)
|
||||||
continue
|
} else if arg == "--" { // Options terminator
|
||||||
}
|
restArgs = append(restArgs, p.restArgs()...)
|
||||||
|
|
||||||
// Handle options terminator
|
|
||||||
if arg == "--" {
|
|
||||||
restArgs = append(restArgs, args[i+1:]...)
|
|
||||||
return
|
return
|
||||||
}
|
} else if strings.HasPrefix(arg, "--") { // Long option
|
||||||
|
|
||||||
// Handle long option
|
|
||||||
if strings.HasPrefix(arg, "--") {
|
|
||||||
longName := arg[2:]
|
longName := arg[2:]
|
||||||
value := ""
|
if err = p.parseLongOption(longName); err != nil {
|
||||||
|
return
|
||||||
equals := strings.Index(longName, "=")
|
|
||||||
if equals > 0 {
|
|
||||||
longName = longName[:equals]
|
|
||||||
value = longName[equals+1:]
|
|
||||||
} else if i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") {
|
|
||||||
value = args[i+1]
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
|
} else { // Short option
|
||||||
parsed := false
|
shortNames := arg[1:]
|
||||||
for _, opt := range s {
|
if err = p.parseShortOption(shortNames); err != nil {
|
||||||
if opt.Name() != longName {
|
return
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = opt.Parse(value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !parsed {
|
|
||||||
restArgs = append(restArgs, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle short option
|
|
||||||
shortNames := arg[1:]
|
|
||||||
if len(shortNames) == 0 {
|
|
||||||
err = ErrInvalidShortOption
|
|
||||||
} else if len(shortNames) == 1 {
|
|
||||||
parsed := false
|
|
||||||
value := ""
|
|
||||||
if i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") {
|
|
||||||
value = args[i+1]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, opt := range s {
|
|
||||||
if opt.ShortName() != shortNames {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = opt.Parse(value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !parsed {
|
|
||||||
restArgs = append(restArgs, arg)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for j := 0; j < len(shortNames); j++ {
|
|
||||||
shortName := shortNames[j]
|
|
||||||
value := ""
|
|
||||||
takesValue := false
|
|
||||||
parsed := false
|
|
||||||
|
|
||||||
for _, opt := range s {
|
|
||||||
if opt.ShortName() != string(shortName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.TakesArg() {
|
|
||||||
if j > 0 {
|
|
||||||
err = ErrCannotChainOption
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
takesValue = true
|
|
||||||
value = shortNames[j+1:]
|
|
||||||
value = strings.TrimPrefix(value, "=")
|
|
||||||
if len(value) == 0 && i < len(args)-1 && !strings.HasPrefix(args[i+1], "-") {
|
|
||||||
value = args[i+1]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
j = len(shortNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed = true
|
|
||||||
|
|
||||||
if err = opt.Parse(value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !parsed {
|
|
||||||
if takesValue {
|
|
||||||
restArgs = append(restArgs, arg)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
restArgs = append(restArgs, fmt.Sprintf("-%c", shortName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) hasNext() bool {
|
||||||
|
return (p.curr + 1) < len(p.args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) next() string {
|
||||||
|
p.curr++
|
||||||
|
|
||||||
|
if p.curr >= len(p.args) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.args[p.curr]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) value() string {
|
||||||
|
p.curr++
|
||||||
|
|
||||||
|
if p.curr >= len(p.args) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := p.args[p.curr]
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) restArgs() []string {
|
||||||
|
rest := p.args[p.curr+1:]
|
||||||
|
p.curr = len(p.args)
|
||||||
|
|
||||||
|
return rest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseLongOption(longName string) error {
|
||||||
|
value := ""
|
||||||
|
|
||||||
|
equals := strings.Index(longName, "=")
|
||||||
|
if equals >= 0 {
|
||||||
|
longName = longName[:equals]
|
||||||
|
value = longName[equals+1:]
|
||||||
|
} else {
|
||||||
|
value = p.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
opt, ok := p.opts.GetByLongName(longName)
|
||||||
|
if ok {
|
||||||
|
if err := opt.Parse(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseShortOption(shortNames string) error {
|
||||||
|
if len(shortNames) == 0 {
|
||||||
|
return ErrInvalidShortOption
|
||||||
|
} else if len(shortNames) == 1 {
|
||||||
|
value := p.value()
|
||||||
|
|
||||||
|
opt, ok := p.opts.GetByShortName(shortNames)
|
||||||
|
if ok {
|
||||||
|
if err := opt.Parse(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for j := 0; j < len(shortNames); j++ {
|
||||||
|
shortName := shortNames[j]
|
||||||
|
value := ""
|
||||||
|
|
||||||
|
opt, ok := p.opts.GetByShortName(string(shortName))
|
||||||
|
if ok {
|
||||||
|
if opt.TakesArg() {
|
||||||
|
if j > 0 {
|
||||||
|
return ErrCannotChainOption
|
||||||
|
}
|
||||||
|
|
||||||
|
value = shortNames[1:]
|
||||||
|
value = strings.TrimPrefix(value, "=")
|
||||||
|
if len(value) == 0 {
|
||||||
|
value = p.value()
|
||||||
|
}
|
||||||
|
j = len(shortNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := opt.Parse(value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
18
opts/set.go
18
opts/set.go
|
@ -33,6 +33,24 @@ func (s Set) Get(name string) (Option, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Set) GetByLongName(longName string) (Option, bool) {
|
||||||
|
for _, o := range s {
|
||||||
|
if o.Name() == longName {
|
||||||
|
return o, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Set) GetByShortName(shortName string) (Option, bool) {
|
||||||
|
for _, o := range s {
|
||||||
|
if o.ShortName() == shortName {
|
||||||
|
return o, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
func (s Set) GetBool(name string) (*BoolOption, bool) {
|
func (s Set) GetBool(name string) (*BoolOption, bool) {
|
||||||
o, ok := s.Get(name)
|
o, ok := s.Get(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
Loading…
Reference in New Issue