github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/base/fill/struct.go (about)

     1  // Package fill assigns arbitrary values to struct fields using reflection.
     2  // "fill" is case-insensitive, and obeys the "json" field tag if present.
     3  // It's primary use is to support decoding data from a number of serialization
     4  // formats (JSON,YAML,CBOR) into an intermediate map[string]interface{} value
     5  // which can then be used to "fill" arbitrary struct values
     6  package fill
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  	"strings"
    12  	"time"
    13  )
    14  
    15  // Struct fills in the values of an arbitrary structure using an already deserialized
    16  // map of nested data. Fields names are case-insensitive. Unknown fields are treated as an
    17  // error, *unless* the output structure implements the ArbitrarySetter interface.
    18  func Struct(fields map[string]interface{}, output interface{}) error {
    19  	target := reflect.ValueOf(output)
    20  	if target.Kind() == reflect.Ptr {
    21  		target = target.Elem()
    22  	}
    23  	collector := NewErrorCollector()
    24  	putFieldsToTargetStruct(fields, target, collector)
    25  	return collector.AsSingleError()
    26  }
    27  
    28  // ArbitrarySetter should be implemented by structs that can store arbitrary fields in a private map.
    29  type ArbitrarySetter interface {
    30  	SetArbitrary(string, interface{}) error
    31  }
    32  
    33  var (
    34  	// timeObj and strObj are used for reflect.TypeOf
    35  	timeObj time.Time
    36  	strObj  string
    37  	byteObj byte
    38  )
    39  
    40  // putFieldsToTargetStruct iterates over the fields in the target struct, and assigns each
    41  // field the value from the `fields` map. Recursively call this for an sub structures. Field
    42  // names are treated as case-insensitive. Return any errors found during this process, or nil if
    43  // there are no errors.
    44  func putFieldsToTargetStruct(fields map[string]interface{}, target reflect.Value, collector *ErrorCollector) {
    45  	if target.Kind() != reflect.Struct {
    46  		collector.Add(fmt.Errorf("can only assign fields to a struct"))
    47  		return
    48  	}
    49  
    50  	// Collect real key names used by the `fields` map.
    51  	realKeys := make([]string, 0)
    52  	for k := range fields {
    53  		realKeys = append(realKeys, k)
    54  	}
    55  	// Handle case-insensitivity by building a map from lowercase keys to real keys.
    56  	caseMap := make(map[string]string)
    57  	for i := 0; i < len(realKeys); i++ {
    58  		realKey := realKeys[i]
    59  		lowerKey := strings.ToLower(realKey)
    60  		caseMap[lowerKey] = realKey
    61  	}
    62  
    63  	// Keep track of which keys have been used from the `fields` map
    64  	usedKeys := make(map[string]bool)
    65  
    66  	for i := 0; i < target.NumField(); i++ {
    67  		// Lowercase the key in order to make matching case-insensitive.
    68  		fieldName := target.Type().Field(i).Name
    69  		lowerName := strings.ToLower(fieldName)
    70  		fieldTag := target.Type().Field(i).Tag
    71  		if fieldTag != "" && fieldTag.Get("json") != "" {
    72  			jsonName := fieldTag.Get("json")
    73  			pos := strings.Index(jsonName, ",")
    74  			if pos != -1 {
    75  				jsonName = jsonName[:pos]
    76  			}
    77  			lowerName = strings.ToLower(jsonName)
    78  		}
    79  
    80  		val, ok := fields[caseMap[lowerName]]
    81  		if !ok {
    82  			// Nothing to assign to this field, go to next.
    83  			continue
    84  		}
    85  		usedKeys[caseMap[lowerName]] = true
    86  		if val == nil {
    87  			// Don't try and assign a nil value.
    88  			continue
    89  		}
    90  
    91  		collector.PushField(fieldName)
    92  		putValueToPlace(val, target.Field(i), collector)
    93  		collector.PopField()
    94  	}
    95  
    96  	// If the target struct is able, assign unknown keys to it.
    97  	arbitrarySetter := getArbitrarySetter(target)
    98  
    99  	// Iterate over keys in the `fields` data, see if there were any keys that were not stored in
   100  	// the target struct.
   101  	for i := 0; i < len(realKeys); i++ {
   102  		k := realKeys[i]
   103  		if _, ok := usedKeys[k]; !ok {
   104  			// If target struct allows storing unknown keys to a map of arbitrary data.
   105  			if arbitrarySetter != nil {
   106  				arbitrarySetter.SetArbitrary(k, fields[k])
   107  				continue
   108  			}
   109  			// Otherwise, unknown fields are an error.
   110  			collector.Add(fmt.Errorf("at \"%s\": not found in struct %s", k, target.Type()))
   111  		}
   112  	}
   113  }
   114  
   115  // putValueToPlace stores the val at the place, recusively if necessary
   116  func putValueToPlace(val interface{}, place reflect.Value, collector *ErrorCollector) {
   117  	switch place.Kind() {
   118  	case reflect.Struct:
   119  		// Specially handle time.Time, represented as a string, which needs to be parsed.
   120  		if place.Type() == reflect.TypeOf(timeObj) {
   121  			timeText, ok := val.(string)
   122  			if ok {
   123  				ts, err := time.Parse(time.RFC3339, timeText)
   124  				if err != nil {
   125  					err = fmt.Errorf("could not parse time: \"%s\"", timeText)
   126  					collector.Add(err)
   127  				} else {
   128  					place.Set(reflect.ValueOf(ts))
   129  				}
   130  				return
   131  			}
   132  			err := &FieldError{Want: "time", Got: reflect.TypeOf(val).Name(), Val: val}
   133  			collector.Add(err)
   134  			return
   135  		}
   136  		// Struct must be assigned from a map.
   137  		component := toStringMap(val)
   138  		if component == nil {
   139  			collector.Add(fmt.Errorf("could not convert to map[string]"))
   140  			return
   141  		}
   142  		// Recursion to handle sub-component.
   143  		putFieldsToTargetStruct(component, place, collector)
   144  	case reflect.Map:
   145  		if val == nil {
   146  			// If map is nil, nothing more to do.
   147  			return
   148  		}
   149  		ms, ok := val.(map[string]interface{})
   150  		if ok {
   151  			// Special case map[string]string, convert values to strings.
   152  			if place.Type().Elem() == reflect.TypeOf(strObj) {
   153  				strmap := make(map[string]string)
   154  				for k, v := range ms {
   155  					strmap[k] = fmt.Sprintf("%s", v)
   156  				}
   157  				place.Set(reflect.ValueOf(strmap))
   158  				return
   159  			}
   160  			if place.CanSet() {
   161  				place.Set(reflect.ValueOf(ms))
   162  			}
   163  			return
   164  		}
   165  		mi, ok := val.(map[interface{}]interface{})
   166  		if ok {
   167  			// Special case map[string]string, convert values to strings.
   168  			if place.Type().Elem() == reflect.TypeOf(strObj) {
   169  				strmap := make(map[string]string)
   170  				for k, v := range mi {
   171  					strmap[fmt.Sprintf("%s", k)] = fmt.Sprintf("%s", v)
   172  				}
   173  				place.Set(reflect.ValueOf(strmap))
   174  				return
   175  			}
   176  			if place.CanSet() {
   177  				place.Set(reflect.ValueOf(ensureMapsHaveStringKeys(mi)))
   178  			}
   179  			return
   180  		}
   181  		// Error due to not being able to convert.
   182  		collector.Add(&FieldError{Want: "map", Got: reflect.TypeOf(val).Name(), Val: val})
   183  		return
   184  	case reflect.Slice:
   185  		if val == nil {
   186  			// If slice is nil, nothing more to do.
   187  			return
   188  		}
   189  
   190  		if place.Type().Elem() == reflect.TypeOf(byteObj) {
   191  			// Special behavior for raw binary data, either a byte array or a string.
   192  			// TODO(dlong): Look into if this is needed for reflect.Array. If yes, add
   193  			// functionality and tests, if no, document why not.
   194  			byteSlice, ok := val.([]byte)
   195  			if ok {
   196  				place.SetBytes(byteSlice)
   197  				return
   198  			}
   199  			text, ok := val.(string)
   200  			if ok {
   201  				place.SetBytes([]byte(text))
   202  				return
   203  			}
   204  			collector.Add(fmt.Errorf("need type byte slice, value %v", val))
   205  			return
   206  		}
   207  
   208  		slice, ok := val.([]interface{})
   209  		if !ok {
   210  			collector.Add(fmt.Errorf("need type slice, value %v", val))
   211  			return
   212  		}
   213  		// Get size of type of the slice to deserialize.
   214  		size := len(slice)
   215  		sliceType := place.Type().Elem()
   216  		// Construct a new, empty slice of the same size.
   217  		create := reflect.MakeSlice(reflect.SliceOf(sliceType), size, size)
   218  		// Fill in each element.
   219  		for i := 0; i < size; i++ {
   220  			elem := reflect.Indirect(reflect.New(sliceType))
   221  			collector.PushField(fmt.Sprintf("%d", i))
   222  			putValueToPlace(slice[i], elem, collector)
   223  			collector.PopField()
   224  			create.Index(i).Set(elem)
   225  		}
   226  		place.Set(create)
   227  		return
   228  	case reflect.Array:
   229  		if val == nil {
   230  			// If slice is nil, nothing more to do.
   231  			return
   232  		}
   233  		slice, ok := val.([]interface{})
   234  		if !ok {
   235  			collector.Add(fmt.Errorf("need type array, value %s", val))
   236  			return
   237  		}
   238  		// Get size of type of the slice to deserialize.
   239  		size := len(slice)
   240  		targetElem := place.Type().Elem()
   241  		targetSize := place.Type().Len()
   242  		if size != targetSize {
   243  			collector.Add(fmt.Errorf("need array of size %d, got size %d", targetSize, size))
   244  			return
   245  		}
   246  		// Construct array of appropriate size and type.
   247  		arrayType := reflect.ArrayOf(targetSize, targetElem)
   248  		create := reflect.New(arrayType).Elem()
   249  		// Fill in each element.
   250  		for i := 0; i < size; i++ {
   251  			elem := reflect.Indirect(reflect.New(targetElem))
   252  			putValueToPlace(slice[i], elem, collector)
   253  			create.Index(i).Set(elem)
   254  		}
   255  		place.Set(create)
   256  		return
   257  	case reflect.Ptr:
   258  		if val == nil {
   259  			// If pointer is nil, nothing more to do.
   260  			return
   261  		}
   262  		// Allocate a new pointer for the sub-component to be filled in.
   263  		alloc := reflect.New(place.Type().Elem())
   264  		place.Set(alloc)
   265  		inner := alloc.Elem()
   266  		putValueToPlace(val, inner, collector)
   267  		return
   268  	default:
   269  		collector.Add(putValueToUnit(val, place))
   270  		return
   271  	}
   272  }
   273  
   274  // putValueToUnit stores the val at the place, as long as it is a unitary (non-compound) type
   275  func putValueToUnit(val interface{}, place reflect.Value) error {
   276  	switch place.Kind() {
   277  	case reflect.Int:
   278  		num, ok := val.(int)
   279  		if ok {
   280  			place.SetInt(int64(num))
   281  			return nil
   282  		}
   283  		numFloat, ok := val.(float64)
   284  		if ok {
   285  			place.SetInt(int64(numFloat))
   286  			return nil
   287  		}
   288  		return &FieldError{Want: "int", Got: reflect.TypeOf(val).Name(), Val: val}
   289  	case reflect.Int64:
   290  		num, ok := val.(int)
   291  		if ok {
   292  			place.SetInt(int64(num))
   293  			return nil
   294  		}
   295  		num64, ok := val.(int64)
   296  		if ok {
   297  			place.SetInt(num64)
   298  			return nil
   299  		}
   300  		float64, ok := val.(float64)
   301  		if ok {
   302  			place.SetInt(int64(float64))
   303  			return nil
   304  		}
   305  		return &FieldError{Want: "int64", Got: reflect.TypeOf(val).Name(), Val: val}
   306  	case reflect.Uint64:
   307  		num, ok := val.(uint)
   308  		if ok {
   309  			place.SetUint(uint64(num))
   310  			return nil
   311  		}
   312  		num64, ok := val.(uint64)
   313  		if ok {
   314  			place.SetUint(num64)
   315  			return nil
   316  		}
   317  		float64, ok := val.(float64)
   318  		if ok {
   319  			place.SetUint(uint64(float64))
   320  			return nil
   321  		}
   322  		return &FieldError{Want: "uint64", Got: reflect.TypeOf(val).Name(), Val: val}
   323  	case reflect.Float64:
   324  		num, ok := val.(int)
   325  		if ok {
   326  			place.SetFloat(float64(num))
   327  			return nil
   328  		}
   329  		numFloat, ok := val.(float64)
   330  		if ok {
   331  			place.SetFloat(numFloat)
   332  			return nil
   333  		}
   334  		return &FieldError{Want: "float64", Got: reflect.TypeOf(val).Name(), Val: val}
   335  	case reflect.String:
   336  		text, ok := val.(string)
   337  		if ok {
   338  			place.SetString(text)
   339  			return nil
   340  		}
   341  		return &FieldError{Want: "string", Got: reflect.TypeOf(val).Name(), Val: val}
   342  	case reflect.Bool:
   343  		b, ok := val.(bool)
   344  		if ok {
   345  			place.SetBool(b)
   346  			return nil
   347  		}
   348  		return &FieldError{Want: "bool", Got: reflect.TypeOf(val).Name(), Val: val}
   349  	case reflect.Interface:
   350  		imap, ok := val.(map[interface{}]interface{})
   351  		if ok {
   352  			place.Set(reflect.ValueOf(ensureMapsHaveStringKeys(imap)))
   353  			return nil
   354  		}
   355  		place.Set(reflect.ValueOf(val))
   356  		return nil
   357  	default:
   358  		return fmt.Errorf("unknown kind %s", place.Kind())
   359  	}
   360  }
   361  
   362  // toStringMap converts the input to a map[string] if able. This is needed because, while JSON
   363  // correctly deserializes sub structures to map[string], YAML instead deserializes to
   364  // map[interface{}]interface{}, so we need to manually convert this case to map[string].
   365  func toStringMap(obj interface{}) map[string]interface{} {
   366  	m, ok := obj.(map[string]interface{})
   367  	if ok {
   368  		return m
   369  	}
   370  	imap, ok := obj.(map[interface{}]interface{})
   371  	if ok {
   372  		return ensureMapsHaveStringKeys(imap)
   373  	}
   374  	return nil
   375  }
   376  
   377  // ensureMapsHaveStringKeys will recursively convert map's key to be strings. This will allow us
   378  // to serialize back into JSON.
   379  func ensureMapsHaveStringKeys(imap map[interface{}]interface{}) map[string]interface{} {
   380  	build := make(map[string]interface{})
   381  	for k, v := range imap {
   382  		switch x := v.(type) {
   383  		case map[interface{}]interface{}:
   384  			v = ensureMapsHaveStringKeys(x)
   385  		case []interface{}:
   386  			for i, elem := range x {
   387  				if inner, ok := elem.(map[interface{}]interface{}); ok {
   388  					x[i] = ensureMapsHaveStringKeys(inner)
   389  				}
   390  			}
   391  		}
   392  		build[fmt.Sprintf("%s", k)] = v
   393  	}
   394  	return build
   395  }
   396  
   397  // getArbitrarySetter returns a ArbitrarySetter if the target implements it.
   398  func getArbitrarySetter(target reflect.Value) ArbitrarySetter {
   399  	if !target.CanAddr() {
   400  		return nil
   401  	}
   402  	ptr := target.Addr()
   403  	iface := ptr.Interface()
   404  	if setter, ok := iface.(ArbitrarySetter); ok {
   405  		return setter
   406  	}
   407  	return nil
   408  }