Permalink
Browse files

Merge branch 'ini'

  • Loading branch information...
2 parents 9387697 + 7c5e61b commit 413faad9b9b32094e9d8b2047939cc4fd814ac93 Jesse van den Kieboom committed Oct 23, 2012
Showing with 625 additions and 150 deletions.
  1. +27 −15 convert.go
  2. +11 −0 error.go
  3. +1 −1 example_test.go
  4. +5 −80 group.go
  5. +43 −39 group_private.go
  6. +202 −0 ini.go
  7. +87 −0 option.go
  8. +73 −0 option_private.go
  9. +105 −9 parser.go
  10. +71 −6 parser_private.go
View
@@ -32,7 +32,11 @@ func convertToString(val reflect.Value, options reflect.StructTag) string {
case reflect.String:
return val.String()
case reflect.Bool:
- return ""
+ if val.Bool() {
+ return "true"
+ } else {
+ return "false"
+ }
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
base, _ := getBase(options, 10)
return strconv.FormatInt(val.Int(), base)
@@ -73,11 +77,32 @@ func convertToString(val reflect.Value, options reflect.StructTag) string {
func convert(val string, retval reflect.Value, options reflect.StructTag) error {
tp := retval.Type()
+ // Support for time.Duration
+ if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
+ parsed, err := time.ParseDuration(val)
+
+ if err != nil {
+ return err
+ }
+
+ retval.SetInt(int64(parsed))
+ }
+
switch tp.Kind() {
case reflect.String:
retval.SetString(val)
case reflect.Bool:
- retval.SetBool(true)
+ if val == "" {
+ retval.SetBool(true)
+ } else {
+ b, err := strconv.ParseBool(val)
+
+ if err != nil {
+ return err
+ }
+
+ retval.SetBool(b)
+ }
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
base, err := getBase(options, 10)
@@ -154,19 +179,6 @@ func convert(val string, retval reflect.Value, options reflect.StructTag) error
retval.SetMapIndex(reflect.Indirect(keyval), reflect.Indirect(valueval))
}
- // Special cases
-
- // Support for time.Duration
- if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() {
- parsed, err := time.ParseDuration(val)
-
- if err != nil {
- return err
- }
-
- retval.SetInt(int64(parsed))
- }
-
return nil
}
View
@@ -13,6 +13,9 @@ const (
// Unknown flag
ErrUnknownFlag
+ // Unknown group
+ ErrUnknownGroup
+
// Failed to marshal value
ErrMarshal
@@ -47,3 +50,11 @@ func newError(tp ErrorType, message string) *Error {
Message: message,
}
}
+
+func wrapError(err error) error {
+ if _, ok := err.(*Error); !ok {
+ return newError(ErrUnknown, err.Error())
+ }
+
+ return err
+}
View
@@ -35,7 +35,7 @@ func Example() {
}
// Make some fake arguments to parse.
- args := []string {
+ args := []string{
"-vv",
"--offset=5",
"-n", "Me",
View
@@ -6,9 +6,6 @@ package flags
import (
"errors"
- "fmt"
- "reflect"
- "unicode/utf8"
)
// The provided container is not a pointer to a struct
@@ -17,51 +14,14 @@ var ErrNotPointerToStruct = errors.New("provided data is not a pointer to struct
// The provided short name is longer than a single character
var ErrShortNameTooLong = errors.New("short names can only be 1 character")
-// Option flag information. Contains a description of the option, short and
-// long name as well as a default value and whether an argument for this
-// flag is optional.
-type Option struct {
- // The short name of the option (a single character). If not 0, the
- // option flag can be 'activated' using -<ShortName>. Either ShortName
- // or LongName needs to be non-empty.
- ShortName rune
-
- // The long name of the option. If not "", the option flag can be
- // activated using --<LongName>. Either ShortName or LongName needs
- // to be non-empty.
- LongName string
-
- // The description of the option flag. This description is shown
- // automatically in the builtin help.
- Description string
-
- // The default value of the option. The default value is used when
- // the option flag is marked as having an OptionalArgument. This means
- // that when the flag is specified, but no option argument is given,
- // the value of the field this option represents will be set to
- // Default. This is only valid for non-boolean options.
- Default string
-
- // If true, specifies that the argument to an option flag is optional.
- // When no argument to the flag is specified on the command line, the
- // value of Default will be set in the field this option represents.
- // This is only valid for non-boolean options.
- OptionalArgument bool
-
- // If true, the option _must_ be specified on the command line. If the
- // option is not specified, the parser will generate an ErrRequired type
- // error.
- Required bool
-
- value reflect.Value
- options reflect.StructTag
-}
-
// An option group. The option group has a name and a set of options.
type Group struct {
// The name of the group.
Name string
+ Names map[string]*Option
+ IniNames map[string]*Option
+
// A map of long names to option option descriptions.
LongNames map[string]*Option
@@ -77,43 +37,6 @@ type Group struct {
data interface{}
}
-// Set the value of an option to the specified value. An error will be returned
-// if the specified value could not be converted to the corresponding option
-// value type.
-func (option *Option) Set(value *string) error {
- if option.isFunc() {
- return option.call(value)
- } else if value != nil {
- return convert(*value, option.value, option.options)
- } else {
- return convert("", option.value, option.options)
- }
-
- return nil
-}
-
-// Convert an option to a human friendly readable string describing the option.
-func (option *Option) String() string {
- var s string
- var short string
-
- if option.ShortName != 0 {
- data := make([]byte, utf8.RuneLen(option.ShortName))
- utf8.EncodeRune(data, option.ShortName)
- short = string(data)
-
- if len(option.LongName) != 0 {
- s = fmt.Sprintf("-%s, --%s", short, option.LongName)
- } else {
- s = fmt.Sprintf("-%s", short)
- }
- } else if len(option.LongName) != 0 {
- s = fmt.Sprintf("--%s", option.LongName)
- }
-
- return s
-}
-
// NewGroup creates a new option group with a given name and underlying data
// container. The data container is a pointer to a struct. The fields of the
// struct represent the command line options (using field tags) and their values
@@ -122,6 +45,8 @@ func (option *Option) String() string {
func NewGroup(name string, data interface{}) *Group {
ret := &Group{
Name: name,
+ Names: make(map[string]*Option),
+ IniNames: make(map[string]*Option),
LongNames: make(map[string]*Option),
ShortNames: make(map[rune]*Option),
data: data,
View
@@ -2,61 +2,56 @@ package flags
import (
"reflect"
+ "strings"
"unicode/utf8"
+ "unsafe"
)
-func (option *Option) canArgument() bool {
- if option.isBool() {
- return false
+func (g *Group) lookupByName(name string, ini bool) (*Option, string) {
+ name = strings.ToLower(name)
+
+ if ini {
+ if ret := g.IniNames[name]; ret != nil {
+ return ret, ret.options.Get("ini-name")
+ }
+
+ if ret := g.Names[name]; ret != nil {
+ return ret, ret.fieldName
+ }
}
- if option.isFunc() {
- return (option.value.Type().NumIn() > 0)
+ if ret := g.LongNames[name]; ret != nil {
+ return ret, ret.LongName
}
- return true
-}
+ if utf8.RuneCountInString(name) == 1 {
+ r, _ := utf8.DecodeRuneInString(name)
-func (option *Option) isBool() bool {
- tp := option.value.Type()
+ if ret := g.ShortNames[r]; ret != nil {
+ data := make([]byte, utf8.RuneLen(ret.ShortName))
+ utf8.EncodeRune(data, ret.ShortName)
- switch tp.Kind() {
- case reflect.Bool:
- return true
- case reflect.Slice:
- return (tp.Elem().Kind() == reflect.Bool)
+ return ret, string(data)
+ }
}
- return false
-}
-
-func (option *Option) isFunc() bool {
- return option.value.Type().Kind() == reflect.Func
+ return nil, ""
}
-func (option *Option) call(value *string) error {
- var retval []reflect.Value
-
- if value == nil {
- retval = option.value.Call(nil)
- } else {
- tp := option.value.Type().In(0)
-
- val := reflect.New(tp)
- val = reflect.Indirect(val)
-
- if err := convert(*value, val, option.options); err != nil {
- return err
+func (g *Group) storeDefaults() {
+ for _, option := range g.Options {
+ if !option.value.CanAddr() {
+ continue
}
- retval = option.value.Call([]reflect.Value{val})
- }
+ addr := option.value.UnsafeAddr()
- if len(retval) == 1 && retval[0].Type() == reflect.TypeOf((*error)(nil)).Elem() {
- return retval[0].Interface().(error)
- }
+ // Create a pointer to the current value
+ ptr := reflect.NewAt(option.value.Type(), unsafe.Pointer(addr))
- return nil
+ // Indirect the pointer to the value to make a copy
+ option.defaultValue = reflect.Indirect(ptr)
+ }
}
func (g *Group) scan() error {
@@ -125,6 +120,7 @@ func (g *Group) scan() error {
Required: required,
value: realval.Field(i),
options: field.Tag,
+ fieldName: field.Name,
}
g.Options = append(g.Options, option)
@@ -134,7 +130,15 @@ func (g *Group) scan() error {
}
if option.LongName != "" {
- g.LongNames[option.LongName] = option
+ g.LongNames[strings.ToLower(option.LongName)] = option
+ }
+
+ g.Names[strings.ToLower(field.Name)] = option
+
+ ininame := field.Tag.Get("ini-name")
+
+ if len(ininame) != 0 {
+ g.IniNames[strings.ToLower(ininame)] = option
}
}
Oops, something went wrong. Retry.

0 comments on commit 413faad

Please sign in to comment.