blob: a04cdd2c6e4790f110b07bde6e4003fccf8de09e [file] [log] [blame]
package flags
import (
"reflect"
"sort"
"strings"
"unicode/utf8"
"unsafe"
)
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.tag.Get("ini-name")
}
if ret := g.Names[name]; ret != nil {
return ret, ret.Field.Name
}
}
if ret := g.LongNames[name]; ret != nil {
return ret, ret.LongName
}
if utf8.RuneCountInString(name) == 1 {
r, _ := utf8.DecodeRuneInString(name)
if ret := g.ShortNames[r]; ret != nil {
data := make([]byte, utf8.RuneLen(ret.ShortName))
utf8.EncodeRune(data, ret.ShortName)
return ret, string(data)
}
}
return nil, ""
}
func (g *Group) storeDefaults() {
for _, option := range g.Options {
// First. empty out the value
if len(option.Default) > 0 {
option.clear()
}
for _, d := range option.Default {
option.Set(&d)
}
if !option.Value.CanSet() {
continue
}
option.defaultValue = reflect.ValueOf(option.Value.Interface())
}
}
func (g *Group) scanStruct(realval reflect.Value, sfield *reflect.StructField) error {
stype := realval.Type()
if sfield != nil {
mtag := newMultiTag(string(sfield.Tag))
groupName := mtag.Get("group")
commandName := mtag.Get("command")
name := mtag.Get("name")
description := mtag.Get("description")
iscommand := false
if len(commandName) != 0 {
iscommand = true
if len(name) != 0 {
groupName = name
} else if len(commandName) != 0 {
groupName = commandName
}
}
if len(groupName) != 0 {
ptrval := reflect.NewAt(realval.Type(), unsafe.Pointer(realval.UnsafeAddr()))
newGroup := NewGroup(groupName, ptrval.Interface())
if iscommand {
newGroup.IsCommand = true
g.Commands[commandName] = newGroup
}
newGroup.LongDescription = description
g.EmbeddedGroups = append(g.EmbeddedGroups, newGroup)
return g.Error
}
}
for i := 0; i < stype.NumField(); i++ {
field := stype.Field(i)
// PkgName is set only for non-exported fields, which we ignore
if field.PkgPath != "" {
continue
}
mtag := newMultiTag(string(field.Tag))
// Skip fields with the no-flag tag
if mtag.Get("no-flag") != "" {
continue
}
// Dive deep into structs or pointers to structs
kind := field.Type.Kind()
if kind == reflect.Struct {
err := g.scanStruct(realval.Field(i), &field)
if err != nil {
return err
}
} else if kind == reflect.Ptr &&
field.Type.Elem().Kind() == reflect.Struct &&
!realval.Field(i).IsNil() {
err := g.scanStruct(reflect.Indirect(realval.Field(i)),
&field)
if err != nil {
return err
}
}
longname := mtag.Get("long")
shortname := mtag.Get("short")
if longname == "" && shortname == "" {
continue
}
short := rune(0)
rc := utf8.RuneCountInString(shortname)
if rc > 1 {
return ErrShortNameTooLong
} else if rc == 1 {
short, _ = utf8.DecodeRuneInString(shortname)
}
description := mtag.Get("description")
def := mtag.GetMany("default")
optionalValue := mtag.GetMany("optional-value")
valueName := mtag.Get("value-name")
defaultMask := mtag.Get("default-mask")
optional := (mtag.Get("optional") != "")
required := (mtag.Get("required") != "")
option := &Option{
Description: description,
ShortName: short,
LongName: longname,
Default: def,
OptionalArgument: optional,
OptionalValue: optionalValue,
Required: required,
Field: field,
Value: realval.Field(i),
ValueName: valueName,
tag: mtag,
defaultMask: defaultMask,
}
g.Options = append(g.Options, option)
if option.ShortName != 0 {
g.ShortNames[option.ShortName] = option
}
if option.LongName != "" {
g.LongNames[strings.ToLower(option.LongName)] = option
}
g.Names[strings.ToLower(field.Name)] = option
ininame := mtag.Get("ini-name")
if len(ininame) != 0 {
g.IniNames[strings.ToLower(ininame)] = option
}
}
return nil
}
func (g *Group) each(index int, cb func(int, *Group)) int {
cb(index, g)
index++
for _, group := range g.EmbeddedGroups {
group.each(index, cb)
index++
}
return index
}
func (g *Group) eachCommand(cb func(string, *Group)) {
cmds := make([]string, len(g.Commands))
i := 0
for k, _ := range g.Commands {
cmds[i] = k
}
sort.Strings(cmds)
for _, k := range cmds {
cb(k, g.Commands[k])
}
}
func (g *Group) scan() error {
// Get all the public fields in the data struct
ptrval := reflect.ValueOf(g.data)
if ptrval.Type().Kind() != reflect.Ptr {
panic(ErrNotPointerToStruct)
}
stype := ptrval.Type().Elem()
if stype.Kind() != reflect.Struct {
panic(ErrNotPointerToStruct)
}
realval := reflect.Indirect(ptrval)
return g.scanStruct(realval, nil)
}