cmd/opts/parser.go

144 lines
2.5 KiB
Go
Raw Normal View History

2023-11-11 20:49:38 -05:00
package opts
import (
"errors"
"fmt"
"strings"
)
var (
ErrInvalidShortOption = errors.New("invalid short option")
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]
// Handle regular argument
if !strings.HasPrefix(arg, "-") {
restArgs = append(restArgs, arg)
continue
}
// Handle options terminator
if arg == "--" {
restArgs = append(restArgs, args[i+1:]...)
return
}
// Handle long option
if strings.HasPrefix(arg, "--") {
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 {
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
}