...

Source file src/github.com/thoas/go-funk/assign.go

Documentation: github.com/thoas/go-funk

     1  package funk
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  )
     9  
    10  // Set assigns in at path with value val. i.e. in.path = val
    11  // in accepts types of ptr to struct, ptr to variable, slice and ptr to slice.
    12  // Along the path, interface{} is supported and nil ptr is initialized to ptr to zero value
    13  // of the type until the variable to be set is obtained.
    14  // It returns errors when encountering along the path unknown types, uninitialized
    15  // interface{} or interface{} containing struct directly (not ptr to struct).
    16  //
    17  // Slice is resolved the same way in funk.Get(), by traversing each element of the slice,
    18  // so that each element of the slice's corresponding field are going to be set to the same provided val.
    19  // If Set is called on slice with empty path "", it behaves the same as funk.Fill()
    20  //
    21  // If in is well formed, i.e. do not expect above descripted errors to happen, funk.MustSet()
    22  // is a short hand wrapper to discard error return
    23  func Set(in interface{}, val interface{}, path string) error {
    24  	if in == nil {
    25  		return errors.New("Cannot Set nil")
    26  	}
    27  	parts := []string{}
    28  	if path != "" {
    29  		parts = strings.Split(path, ".")
    30  	}
    31  	return setByParts(in, val, parts)
    32  }
    33  
    34  // we need this layer to handle interface{} type
    35  func setByParts(in interface{}, val interface{}, parts []string) error {
    36  
    37  	if in == nil {
    38  		// nil interface can happen during traversing the path
    39  		return errors.New("Cannot traverse nil/uninitialized interface{}")
    40  	}
    41  
    42  	inValue := reflect.ValueOf(in)
    43  	inKind := inValue.Type().Kind()
    44  
    45  	// Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set.
    46  	// I.e. it is not CanAddr() or CanSet()
    47  	// So we require in interface{} to be a ptr, slice or array
    48  	if inKind == reflect.Ptr {
    49  		inValue = inValue.Elem() // if it is ptr we set its content not ptr its self
    50  	} else if inKind != reflect.Array && inKind != reflect.Slice {
    51  		return fmt.Errorf("Type %s not supported by Set", inValue.Type().String())
    52  	}
    53  
    54  	return set(inValue, reflect.ValueOf(val), parts)
    55  }
    56  
    57  // traverse inValue using path in parts and set the dst to be setValue
    58  func set(inValue reflect.Value, setValue reflect.Value, parts []string) error {
    59  
    60  	// traverse the path to get the inValue we need to set
    61  	i := 0
    62  	for i < len(parts) {
    63  
    64  		kind := inValue.Kind()
    65  
    66  		switch kind {
    67  		case reflect.Invalid:
    68  			// do not expect this case to happen
    69  			return errors.New("nil pointer found along the path")
    70  		case reflect.Struct:
    71  			fValue := inValue.FieldByName(parts[i])
    72  			if !fValue.IsValid() {
    73  				return fmt.Errorf("field name %v is not found in struct %v", parts[i], inValue.Type().String())
    74  			}
    75  			if !fValue.CanSet() {
    76  				return fmt.Errorf("field name %v is not exported in struct %v", parts[i], inValue.Type().String())
    77  			}
    78  			inValue = fValue
    79  			i++
    80  		case reflect.Slice | reflect.Array:
    81  			// set all its elements
    82  			length := inValue.Len()
    83  			for j := 0; j < length; j++ {
    84  				err := set(inValue.Index(j), setValue, parts[i:])
    85  				if err != nil {
    86  					return err
    87  				}
    88  			}
    89  			return nil
    90  		case reflect.Ptr:
    91  			// only traverse down one level
    92  			if inValue.IsNil() {
    93  				// we initialize nil ptr to ptr to zero value of the type
    94  				// and continue traversing
    95  				inValue.Set(reflect.New(inValue.Type().Elem()))
    96  			}
    97  			// traverse the ptr until it is not pointer any more or is nil again
    98  			inValue = redirectValue(inValue)
    99  		case reflect.Interface:
   100  			// Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set.
   101  			// I.e. it is not CanAddr() or CanSet(). This is why setByParts has a nil ptr check.
   102  			// we treat this as a new call to setByParts, and it will do proper check of the types
   103  			return setByParts(inValue.Interface(), setValue.Interface(), parts[i:])
   104  		default:
   105  			return fmt.Errorf("kind %v in path %v is not supported", kind, parts[i])
   106  		}
   107  
   108  	}
   109  	// here inValue holds the value we need to set
   110  
   111  	// interface{} can be set to any val
   112  	// other types we ensure the type matches
   113  	if inValue.Kind() != setValue.Kind() && inValue.Kind() != reflect.Interface {
   114  		return fmt.Errorf("cannot set target of type %v with type %v", inValue.Kind(), setValue.Kind())
   115  	}
   116  	inValue.Set(setValue)
   117  
   118  	return nil
   119  }
   120  
   121  // MustSet is functionally the same as Set.
   122  // It panics instead of returning error.
   123  // It is safe to use if the in value is well formed.
   124  func MustSet(in interface{}, val interface{}, path string) {
   125  	err := Set(in, val, path)
   126  	if err != nil {
   127  		panic(err)
   128  	}
   129  }
   130  

View as plain text