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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
@ -67,14 +68,10 @@ func (c *Command) Execute(args []string) {
|
|||
args = args[1:]
|
||||
}
|
||||
|
||||
restArgs, err := c.opts.Parse(args)
|
||||
if err != nil {
|
||||
c.ShowHelp()
|
||||
return
|
||||
}
|
||||
|
||||
restArgs, err = opts.Globals().Parse(restArgs)
|
||||
parser := opts.NewParser(args, c.opts)
|
||||
restArgs, err := parser.Parse()
|
||||
if err != nil {
|
||||
fmt.Printf("Option error: %s\n", err.Error())
|
||||
c.ShowHelp()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package opts
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type BoolOption struct {
|
||||
name string
|
||||
|
|
236
opts/parser.go
236
opts/parser.go
|
@ -2,7 +2,6 @@ package opts
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -11,133 +10,136 @@ var (
|
|||
ErrCannotChainOption = errors.New("cannot chain option as it takes an argument")
|
||||
)
|
||||
|
||||
func (s Set) Parse(args []string) (restArgs []string, err error) {
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
type Parser struct {
|
||||
opts Set
|
||||
args []string
|
||||
curr int
|
||||
}
|
||||
|
||||
// Handle regular argument
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
func NewParser(args []string, opts []Option) *Parser {
|
||||
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)
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle options terminator
|
||||
if arg == "--" {
|
||||
restArgs = append(restArgs, args[i+1:]...)
|
||||
} else if arg == "--" { // Options terminator
|
||||
restArgs = append(restArgs, p.restArgs()...)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle long option
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
} else if strings.HasPrefix(arg, "--") { // Long option
|
||||
longName := arg[2:]
|
||||
value := ""
|
||||
|
||||
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++
|
||||
}
|
||||
|
||||
parsed := false
|
||||
for _, opt := range s {
|
||||
if opt.Name() != longName {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = opt.Parse(value); err != nil {
|
||||
if err = p.parseLongOption(longName); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
parsed = true
|
||||
break
|
||||
}
|
||||
|
||||
if !parsed {
|
||||
restArgs = append(restArgs, arg)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle short option
|
||||
} else { // 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 {
|
||||
if err = p.parseShortOption(shortNames); 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
o, ok := s.Get(name)
|
||||
if !ok {
|
||||
|
|
Loading…
Reference in New Issue