mirror of
https://github.com/tmedwards/tweego.git
synced 2025-07-05 14:10:27 -04:00
Initial Bitbucket→GitHub migration commit, based on release v2.0.0.
This commit is contained in:
commit
57e1aa52ff
36 changed files with 5026 additions and 0 deletions
300
internal/option/option.go
Normal file
300
internal/option/option.go
Normal file
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
option (a simple command-line option parser for Go)
|
||||
|
||||
Copyright © 2014–2018 Thomas Michael Edwards. All rights reserved.
|
||||
Use of this source code is governed by a Simplified BSD License which
|
||||
can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
// Package option implements simple command-line option parsing.
|
||||
package option
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// OptionTerminator is the string, when seen on the command line, which terminates further option processing.
|
||||
const OptionTerminator = "--"
|
||||
|
||||
// OptionTypeMap is the map of recognized type abbreviations to types.
|
||||
var OptionTypeMap = map[string]string{"s": "string", "i": "int", "u": "uint", "f": "float", "b": "bool"}
|
||||
|
||||
// Config is {TODO}.
|
||||
type Config struct {
|
||||
Name string
|
||||
Definition string
|
||||
Flags int
|
||||
//Default interface{}
|
||||
}
|
||||
|
||||
// Options is {TODO}.
|
||||
type Options struct {
|
||||
Definitions []Config
|
||||
}
|
||||
|
||||
/*
|
||||
// NewOptions is {TODO}.
|
||||
func NewOptions(options ...Config) Options {
|
||||
return Options{options}
|
||||
}
|
||||
*/
|
||||
|
||||
// NewParser returns {TODO}.
|
||||
func NewParser() Options {
|
||||
return Options{}
|
||||
}
|
||||
|
||||
// Add adds a new option definition.
|
||||
func (optDef *Options) Add(name, def string /*, flags int*/) {
|
||||
optDef.Definitions = append(optDef.Definitions, Config{name, def, 0 /*flags*/})
|
||||
}
|
||||
|
||||
type optionDefinition struct {
|
||||
name string
|
||||
wantsValue bool
|
||||
valueType string
|
||||
repeatable bool
|
||||
flags int
|
||||
}
|
||||
type optionMap map[string]optionDefinition
|
||||
|
||||
func (optDef Options) buildOptionMap() optionMap {
|
||||
optMap := make(optionMap)
|
||||
for _, def := range optDef.Definitions {
|
||||
if def.Definition != "" {
|
||||
names, opts := parseDefinition(def.Definition)
|
||||
for i := range names {
|
||||
opts[i].name = def.Name
|
||||
opts[i].flags = def.Flags
|
||||
optMap[names[i]] = opts[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return optMap
|
||||
}
|
||||
|
||||
func parseDefinition(optSpec string) ([]string, []optionDefinition) {
|
||||
var (
|
||||
names []string
|
||||
defs []optionDefinition
|
||||
)
|
||||
for _, def := range strings.Split(optSpec, "|") {
|
||||
if i := strings.LastIndex(def, "="); i != -1 {
|
||||
// value receiving option
|
||||
names = append(names, def[:i])
|
||||
optDef := optionDefinition{wantsValue: true}
|
||||
valueType := def[i+1:]
|
||||
if valueType == "s+" || valueType == "i+" || valueType == "u+" || valueType == "f+" {
|
||||
// special case: value receiving + repeatable
|
||||
optDef.repeatable = true
|
||||
optDef.valueType = OptionTypeMap[valueType[:1]]
|
||||
} else if _, ok := OptionTypeMap[valueType]; ok {
|
||||
// normal cases
|
||||
optDef.valueType = OptionTypeMap[valueType]
|
||||
} else {
|
||||
// what type now?
|
||||
panic(fmt.Errorf("Cannot parse value type %q in option specification %q.", valueType, optSpec))
|
||||
}
|
||||
defs = append(defs, optDef)
|
||||
} else if i := strings.LastIndex(def, "+"); i != -1 {
|
||||
// repeatable unsigned integer option
|
||||
names = append(names, def[:i])
|
||||
defs = append(defs, optionDefinition{
|
||||
repeatable: true,
|
||||
valueType: OptionTypeMap["u"],
|
||||
})
|
||||
} else {
|
||||
// void/empty option
|
||||
names = append(names, def)
|
||||
defs = append(defs, optionDefinition{})
|
||||
}
|
||||
}
|
||||
return names, defs
|
||||
}
|
||||
|
||||
// ParsedOptionsMap is {TODO}.
|
||||
type ParsedOptionsMap map[string]interface{}
|
||||
|
||||
// ParseCommandLine returns {TODO}.
|
||||
func (optDef Options) ParseCommandLine() (ParsedOptionsMap, []string, error) {
|
||||
return optDef.Parse(os.Args[1:])
|
||||
}
|
||||
|
||||
// Parse returns {TODO}.
|
||||
func (optDef Options) Parse(args []string) (ParsedOptionsMap, []string, error) {
|
||||
var (
|
||||
passThrough []string
|
||||
err error
|
||||
)
|
||||
options := make(ParsedOptionsMap)
|
||||
|
||||
optMap := optDef.buildOptionMap()
|
||||
|
||||
for i, argc := 0, len(args); i < argc; i++ {
|
||||
var (
|
||||
name string
|
||||
)
|
||||
sz := len(args[i])
|
||||
if sz > 1 && args[i][0] == '-' {
|
||||
// could be an option, try to parse it
|
||||
if eqPos := strings.Index(args[i], "="); eqPos != -1 {
|
||||
// with bundled value
|
||||
name = args[i][:eqPos]
|
||||
if opt, ok := optMap[name]; ok {
|
||||
if opt.wantsValue {
|
||||
if value, err := convertType(args[i][eqPos+1:], opt.valueType); err == nil {
|
||||
if opt.repeatable {
|
||||
if _, ok := options[opt.name]; !ok {
|
||||
switch opt.valueType {
|
||||
case "string":
|
||||
options[opt.name] = make([]string, 0, 4)
|
||||
case "int":
|
||||
options[opt.name] = make([]int, 0, 4)
|
||||
case "uint":
|
||||
options[opt.name] = make([]uint, 0, 4)
|
||||
case "float":
|
||||
options[opt.name] = make([]float64, 0, 4)
|
||||
}
|
||||
}
|
||||
switch opt.valueType {
|
||||
case "string":
|
||||
options[opt.name] = append(options[opt.name].([]string), value.(string))
|
||||
case "int":
|
||||
options[opt.name] = append(options[opt.name].([]int), value.(int))
|
||||
case "uint":
|
||||
options[opt.name] = append(options[opt.name].([]uint), value.(uint))
|
||||
case "float":
|
||||
options[opt.name] = append(options[opt.name].([]float64), value.(float64))
|
||||
}
|
||||
} else {
|
||||
options[opt.name] = value
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Option %q %s.", name, err.Error())
|
||||
break
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Option %q does not take a value.", name)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Unknown option %q.", name)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// without bundled value
|
||||
name = args[i]
|
||||
if name == OptionTerminator {
|
||||
// processing terminated, pass any remaining arguments on through
|
||||
passThrough = append(passThrough, args[i+1:]...)
|
||||
break
|
||||
}
|
||||
if opt, ok := optMap[name]; ok {
|
||||
if opt.wantsValue {
|
||||
i++
|
||||
if i < argc {
|
||||
if value, err := convertType(args[i], opt.valueType); err == nil {
|
||||
if opt.repeatable {
|
||||
if _, ok := options[opt.name]; !ok {
|
||||
switch opt.valueType {
|
||||
case "string":
|
||||
options[opt.name] = make([]string, 0, 4)
|
||||
case "int":
|
||||
options[opt.name] = make([]int, 0, 4)
|
||||
case "uint":
|
||||
options[opt.name] = make([]uint, 0, 4)
|
||||
case "float":
|
||||
options[opt.name] = make([]float64, 0, 4)
|
||||
}
|
||||
}
|
||||
switch opt.valueType {
|
||||
case "string":
|
||||
options[opt.name] = append(options[opt.name].([]string), value.(string))
|
||||
case "int":
|
||||
options[opt.name] = append(options[opt.name].([]int), value.(int))
|
||||
case "uint":
|
||||
options[opt.name] = append(options[opt.name].([]uint), value.(uint))
|
||||
case "float":
|
||||
options[opt.name] = append(options[opt.name].([]float64), value.(float64))
|
||||
}
|
||||
} else {
|
||||
options[opt.name] = value
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Option %q %s.", name, err.Error())
|
||||
break
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Option %q requires a value.", name)
|
||||
break
|
||||
}
|
||||
} else if opt.repeatable {
|
||||
if _, ok := options[opt.name]; ok {
|
||||
options[opt.name] = options[opt.name].(uint) + 1
|
||||
} else {
|
||||
options[opt.name] = 1
|
||||
}
|
||||
} else {
|
||||
options[opt.name] = true
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Unknown option %q.", name)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// not an option, pass it through
|
||||
passThrough = append(passThrough, args[i])
|
||||
}
|
||||
}
|
||||
return options, passThrough, err
|
||||
}
|
||||
|
||||
func convertType(original, targetType string) (interface{}, error) {
|
||||
var (
|
||||
value interface{}
|
||||
err error
|
||||
)
|
||||
switch targetType {
|
||||
|
||||
case "string":
|
||||
value = original
|
||||
|
||||
case "int":
|
||||
var tmp int64
|
||||
if tmp, err = strconv.ParseInt(original, 10, 0); err != nil {
|
||||
err = fmt.Errorf("Cannot interpret value %q as an integer: %s.", original, err.Error())
|
||||
break
|
||||
}
|
||||
value = int(tmp)
|
||||
|
||||
case "uint":
|
||||
var tmp uint64
|
||||
if tmp, err = strconv.ParseUint(original, 10, 0); err != nil {
|
||||
err = fmt.Errorf("Cannot interpret value %q as an unsigned integer: %s.", original, err.Error())
|
||||
break
|
||||
}
|
||||
value = uint(tmp)
|
||||
|
||||
case "float":
|
||||
var tmp float64
|
||||
if tmp, err = strconv.ParseFloat(original, 64); err != nil {
|
||||
err = fmt.Errorf("Cannot interpret value %q as a floating-point number: %s.", original, err.Error())
|
||||
break
|
||||
}
|
||||
value = tmp
|
||||
|
||||
case "bool":
|
||||
var tmp bool
|
||||
if tmp, err = strconv.ParseBool(original); err != nil {
|
||||
err = fmt.Errorf("Cannot interpret value %q as a boolean: %s.", original, err.Error())
|
||||
break
|
||||
}
|
||||
value = bool(tmp)
|
||||
|
||||
}
|
||||
return value, err
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue