219 lines
4.9 KiB
Go
219 lines
4.9 KiB
Go
package arg
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
scalar "github.com/alexflint/go-scalar"
|
|
)
|
|
|
|
// setSliceOrMap parses a sequence of strings into a slice or map. The slice or
|
|
// map is always cleared first.
|
|
func setSliceOrMap(dest reflect.Value, values []string) error {
|
|
if !dest.CanSet() {
|
|
return fmt.Errorf("field is not writable")
|
|
}
|
|
|
|
t := dest.Type()
|
|
if t.Kind() == reflect.Ptr {
|
|
dest = dest.Elem()
|
|
t = t.Elem()
|
|
}
|
|
|
|
switch t.Kind() {
|
|
case reflect.Slice:
|
|
return setSlice(dest, values)
|
|
case reflect.Map:
|
|
return setMap(dest, values)
|
|
default:
|
|
return fmt.Errorf("cannot insert multiple values into a %v", t)
|
|
}
|
|
}
|
|
|
|
// setSlice parses a sequence of strings and inserts them into a slice. The
|
|
// slice is cleared first.
|
|
func setSlice(dest reflect.Value, values []string) error {
|
|
var ptr bool
|
|
elem := dest.Type().Elem()
|
|
if elem.Kind() == reflect.Ptr && !elem.Implements(textUnmarshalerType) {
|
|
ptr = true
|
|
elem = elem.Elem()
|
|
}
|
|
|
|
// clear the slice in case default values exist
|
|
if !dest.IsNil() {
|
|
dest.SetLen(0)
|
|
}
|
|
|
|
// parse the values one-by-one
|
|
for _, s := range values {
|
|
v := reflect.New(elem)
|
|
if err := scalar.ParseValue(v.Elem(), s); err != nil {
|
|
return err
|
|
}
|
|
if !ptr {
|
|
v = v.Elem()
|
|
}
|
|
dest.Set(reflect.Append(dest, v))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// setMap parses a sequence of name=value strings and inserts them into a map.
|
|
// The map is always cleared first.
|
|
func setMap(dest reflect.Value, values []string) error {
|
|
// determine the key and value type
|
|
var keyIsPtr bool
|
|
keyType := dest.Type().Key()
|
|
if keyType.Kind() == reflect.Ptr && !keyType.Implements(textUnmarshalerType) {
|
|
keyIsPtr = true
|
|
keyType = keyType.Elem()
|
|
}
|
|
|
|
var valIsPtr bool
|
|
valType := dest.Type().Elem()
|
|
if valType.Kind() == reflect.Ptr && !valType.Implements(textUnmarshalerType) {
|
|
valIsPtr = true
|
|
valType = valType.Elem()
|
|
}
|
|
|
|
// clear the slice in case default values exist
|
|
if !dest.IsNil() {
|
|
for _, k := range dest.MapKeys() {
|
|
dest.SetMapIndex(k, reflect.Value{})
|
|
}
|
|
}
|
|
|
|
// allocate the map if it is not allocated
|
|
if dest.IsNil() {
|
|
dest.Set(reflect.MakeMap(dest.Type()))
|
|
}
|
|
|
|
// parse the values one-by-one
|
|
for _, s := range values {
|
|
// split at the first equals sign
|
|
pos := strings.Index(s, "=")
|
|
if pos == -1 {
|
|
return fmt.Errorf("cannot parse %q into a map, expected format key=value", s)
|
|
}
|
|
|
|
// parse the key
|
|
k := reflect.New(keyType)
|
|
if err := scalar.ParseValue(k.Elem(), s[:pos]); err != nil {
|
|
return err
|
|
}
|
|
if !keyIsPtr {
|
|
k = k.Elem()
|
|
}
|
|
|
|
// parse the value
|
|
v := reflect.New(valType)
|
|
if err := scalar.ParseValue(v.Elem(), s[pos+1:]); err != nil {
|
|
return err
|
|
}
|
|
if !valIsPtr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
// add it to the map
|
|
dest.SetMapIndex(k, v)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// appendSliceOrMap parses a string and appends it to an existing slice or map.
|
|
func appendToSliceOrMap(dest reflect.Value, value string) error {
|
|
if !dest.CanSet() {
|
|
return fmt.Errorf("field is not writable")
|
|
}
|
|
|
|
t := dest.Type()
|
|
if t.Kind() == reflect.Ptr {
|
|
dest = dest.Elem()
|
|
t = t.Elem()
|
|
}
|
|
|
|
switch t.Kind() {
|
|
case reflect.Slice:
|
|
return appendToSlice(dest, value)
|
|
case reflect.Map:
|
|
return appendToMap(dest, value)
|
|
default:
|
|
return fmt.Errorf("cannot insert multiple values into a %v", t)
|
|
}
|
|
}
|
|
|
|
// appendSlice parses a string and appends the result into a slice.
|
|
func appendToSlice(dest reflect.Value, s string) error {
|
|
var ptr bool
|
|
elem := dest.Type().Elem()
|
|
if elem.Kind() == reflect.Ptr && !elem.Implements(textUnmarshalerType) {
|
|
ptr = true
|
|
elem = elem.Elem()
|
|
}
|
|
|
|
// parse the value and append
|
|
v := reflect.New(elem)
|
|
if err := scalar.ParseValue(v.Elem(), s); err != nil {
|
|
return err
|
|
}
|
|
if !ptr {
|
|
v = v.Elem()
|
|
}
|
|
dest.Set(reflect.Append(dest, v))
|
|
return nil
|
|
}
|
|
|
|
// appendToMap parses a name=value string and inserts it into a map.
|
|
// If clear is true then any values already in the map are removed.
|
|
func appendToMap(dest reflect.Value, s string) error {
|
|
// determine the key and value type
|
|
var keyIsPtr bool
|
|
keyType := dest.Type().Key()
|
|
if keyType.Kind() == reflect.Ptr && !keyType.Implements(textUnmarshalerType) {
|
|
keyIsPtr = true
|
|
keyType = keyType.Elem()
|
|
}
|
|
|
|
var valIsPtr bool
|
|
valType := dest.Type().Elem()
|
|
if valType.Kind() == reflect.Ptr && !valType.Implements(textUnmarshalerType) {
|
|
valIsPtr = true
|
|
valType = valType.Elem()
|
|
}
|
|
|
|
// allocate the map if it is not allocated
|
|
if dest.IsNil() {
|
|
dest.Set(reflect.MakeMap(dest.Type()))
|
|
}
|
|
|
|
// split at the first equals sign
|
|
pos := strings.Index(s, "=")
|
|
if pos == -1 {
|
|
return fmt.Errorf("cannot parse %q into a map, expected format key=value", s)
|
|
}
|
|
|
|
// parse the key
|
|
k := reflect.New(keyType)
|
|
if err := scalar.ParseValue(k.Elem(), s[:pos]); err != nil {
|
|
return err
|
|
}
|
|
if !keyIsPtr {
|
|
k = k.Elem()
|
|
}
|
|
|
|
// parse the value
|
|
v := reflect.New(valType)
|
|
if err := scalar.ParseValue(v.Elem(), s[pos+1:]); err != nil {
|
|
return err
|
|
}
|
|
if !valIsPtr {
|
|
v = v.Elem()
|
|
}
|
|
|
|
// add it to the map
|
|
dest.SetMapIndex(k, v)
|
|
return nil
|
|
}
|