sequence.go raw

   1  package arg
   2  
   3  import (
   4  	"fmt"
   5  	"reflect"
   6  	"strings"
   7  
   8  	scalar "github.com/alexflint/go-scalar"
   9  )
  10  
  11  // setSliceOrMap parses a sequence of strings into a slice or map. If clear is
  12  // true then any values already in the slice or map are first removed.
  13  func setSliceOrMap(dest reflect.Value, values []string, clear bool) error {
  14  	if !dest.CanSet() {
  15  		return fmt.Errorf("field is not writable")
  16  	}
  17  
  18  	t := dest.Type()
  19  	if t.Kind() == reflect.Ptr {
  20  		dest = dest.Elem()
  21  		t = t.Elem()
  22  	}
  23  
  24  	switch t.Kind() {
  25  	case reflect.Slice:
  26  		return setSlice(dest, values, clear)
  27  	case reflect.Map:
  28  		return setMap(dest, values, clear)
  29  	default:
  30  		return fmt.Errorf("setSliceOrMap cannot insert values into a %v", t)
  31  	}
  32  }
  33  
  34  // setSlice parses a sequence of strings and inserts them into a slice. If clear
  35  // is true then any values already in the slice are removed.
  36  func setSlice(dest reflect.Value, values []string, clear bool) error {
  37  	var ptr bool
  38  	elem := dest.Type().Elem()
  39  	if elem.Kind() == reflect.Ptr && !elem.Implements(textUnmarshalerType) {
  40  		ptr = true
  41  		elem = elem.Elem()
  42  	}
  43  
  44  	// clear the slice in case default values exist
  45  	if clear && !dest.IsNil() {
  46  		dest.SetLen(0)
  47  	}
  48  
  49  	// parse the values one-by-one
  50  	for _, s := range values {
  51  		v := reflect.New(elem)
  52  		if err := scalar.ParseValue(v.Elem(), s); err != nil {
  53  			return err
  54  		}
  55  		if !ptr {
  56  			v = v.Elem()
  57  		}
  58  		dest.Set(reflect.Append(dest, v))
  59  	}
  60  	return nil
  61  }
  62  
  63  // setMap parses a sequence of name=value strings and inserts them into a map.
  64  // If clear is true then any values already in the map are removed.
  65  func setMap(dest reflect.Value, values []string, clear bool) error {
  66  	// determine the key and value type
  67  	var keyIsPtr bool
  68  	keyType := dest.Type().Key()
  69  	if keyType.Kind() == reflect.Ptr && !keyType.Implements(textUnmarshalerType) {
  70  		keyIsPtr = true
  71  		keyType = keyType.Elem()
  72  	}
  73  
  74  	var valIsPtr bool
  75  	valType := dest.Type().Elem()
  76  	if valType.Kind() == reflect.Ptr && !valType.Implements(textUnmarshalerType) {
  77  		valIsPtr = true
  78  		valType = valType.Elem()
  79  	}
  80  
  81  	// clear the slice in case default values exist
  82  	if clear && !dest.IsNil() {
  83  		for _, k := range dest.MapKeys() {
  84  			dest.SetMapIndex(k, reflect.Value{})
  85  		}
  86  	}
  87  
  88  	// allocate the map if it is not allocated
  89  	if dest.IsNil() {
  90  		dest.Set(reflect.MakeMap(dest.Type()))
  91  	}
  92  
  93  	// parse the values one-by-one
  94  	for _, s := range values {
  95  		// split at the first equals sign
  96  		pos := strings.Index(s, "=")
  97  		if pos == -1 {
  98  			return fmt.Errorf("cannot parse %q into a map, expected format key=value", s)
  99  		}
 100  
 101  		// parse the key
 102  		k := reflect.New(keyType)
 103  		if err := scalar.ParseValue(k.Elem(), s[:pos]); err != nil {
 104  			return err
 105  		}
 106  		if !keyIsPtr {
 107  			k = k.Elem()
 108  		}
 109  
 110  		// parse the value
 111  		v := reflect.New(valType)
 112  		if err := scalar.ParseValue(v.Elem(), s[pos+1:]); err != nil {
 113  			return err
 114  		}
 115  		if !valIsPtr {
 116  			v = v.Elem()
 117  		}
 118  
 119  		// add it to the map
 120  		dest.SetMapIndex(k, v)
 121  	}
 122  	return nil
 123  }
 124