tagparser.go 1.76 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
package goptions

import (
	"fmt"
	"reflect"
	"regexp"
	"strings"
)

const (
	_LONG_FLAG_REGEXP     = `--[[:word:]-]+`
	_SHORT_FLAG_REGEXP    = `-[[:alnum:]]`
	_QUOTED_STRING_REGEXP = `'((?:\\'|[^\\'])+)'`
	_OPTION_REGEXP        = `([[:word:]-]+)(?:=` + _QUOTED_STRING_REGEXP + `)?`
)

var (
	optionRegexp = regexp.MustCompile(`^(` + strings.Join([]string{_SHORT_FLAG_REGEXP, _LONG_FLAG_REGEXP, _OPTION_REGEXP}, "|") + `)(?:,|$)`)
)

func parseStructField(fieldValue reflect.Value, tag string) (*Flag, error) {
	f := &Flag{
		value:        fieldValue,
		DefaultValue: fieldValue.Interface(),
		optionMeta:   make(map[string]interface{}),
	}
	for {
		tag = strings.TrimSpace(tag)
		if len(tag) == 0 {
			break
		}
		idx := optionRegexp.FindStringSubmatchIndex(tag)
		if idx == nil {
			return nil, fmt.Errorf("Could not find a valid flag definition at the beginning of \"%s\"", tag)
		}
		option := tag[idx[2]:idx[3]]

		if strings.HasPrefix(option, "--") {
			if f.Long != "" {
				return nil, fmt.Errorf("Multiple flags assigned to a member: %s", strings.Join([]string{"--" + f.Long, option}, ", "))
			}
			f.Long = option[2:]
		} else if strings.HasPrefix(option, "-") {
			if f.Short != "" {
				return nil, fmt.Errorf("Multiple flags assigned to a member: %s", strings.Join([]string{"-" + f.Short, option}, ", "))
			}
			f.Short = option[1:]
		} else {
			option := tag[idx[4]:idx[5]]
			value := ""
			if idx[6] != -1 {
				value = tag[idx[6]:idx[7]]
			}
			optionmap := optionMapForType(fieldValue.Type())
			opf, ok := optionmap[option]
			if !ok {
				return nil, fmt.Errorf("Unknown option %s", option)
			}
			err := opf(f, option, value)
			if err != nil {
				return nil, fmt.Errorf("Option %s invalid: %s", option, err)
			}
		}
		// Keep remainder
		tag = tag[idx[1]:]
	}
	return f, nil
}