mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-14 23:17:15 +00:00
78 lines
2.4 KiB
Go
78 lines
2.4 KiB
Go
|
package google
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
// Convert between two types by converting to/from JSON. Intended to switch
|
||
|
// between multiple API versions, as they are strict supersets of one another.
|
||
|
// item and out are pointers to structs
|
||
|
func Convert(item, out interface{}) error {
|
||
|
bytes, err := json.Marshal(item)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
err = json.Unmarshal(bytes, out)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Converting between maps and structs only occurs when autogenerated resources convert the result
|
||
|
// of an HTTP request. Those results do not contain omitted fields, so no need to set them.
|
||
|
if _, ok := item.(map[string]interface{}); !ok {
|
||
|
setOmittedFields(item, out)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func setOmittedFields(item, out interface{}) {
|
||
|
// Both inputs must be pointers, see https://blog.golang.org/laws-of-reflection:
|
||
|
// "To modify a reflection object, the value must be settable."
|
||
|
iVal := reflect.ValueOf(item).Elem()
|
||
|
oVal := reflect.ValueOf(out).Elem()
|
||
|
|
||
|
// Loop through all the fields of the struct to look for omitted fields and nested fields
|
||
|
for i := 0; i < iVal.NumField(); i++ {
|
||
|
iField := iVal.Field(i)
|
||
|
if isEmptyValue(iField) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
fieldInfo := iVal.Type().Field(i)
|
||
|
oField := oVal.FieldByName(fieldInfo.Name)
|
||
|
|
||
|
// Only look at fields that exist in the output struct
|
||
|
if !oField.IsValid() {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// If the field contains a 'json:"="' tag, then it was omitted from the Marshal/Unmarshal
|
||
|
// call and needs to be added back in.
|
||
|
if fieldInfo.Tag.Get("json") == "-" {
|
||
|
oField.Set(iField)
|
||
|
}
|
||
|
|
||
|
// If this field is a struct, *struct, []struct, or []*struct, recurse.
|
||
|
if iField.Kind() == reflect.Struct {
|
||
|
setOmittedFields(iField.Addr().Interface(), oField.Addr().Interface())
|
||
|
}
|
||
|
if iField.Kind() == reflect.Ptr && iField.Type().Elem().Kind() == reflect.Struct {
|
||
|
setOmittedFields(iField.Interface(), oField.Interface())
|
||
|
}
|
||
|
if iField.Kind() == reflect.Slice && iField.Type().Elem().Kind() == reflect.Struct {
|
||
|
for j := 0; j < iField.Len(); j++ {
|
||
|
setOmittedFields(iField.Index(j).Addr().Interface(), oField.Index(j).Addr().Interface())
|
||
|
}
|
||
|
}
|
||
|
if iField.Kind() == reflect.Slice && iField.Type().Elem().Kind() == reflect.Ptr &&
|
||
|
iField.Type().Elem().Elem().Kind() == reflect.Struct {
|
||
|
for j := 0; j < iField.Len(); j++ {
|
||
|
setOmittedFields(iField.Index(j).Interface(), oField.Index(j).Interface())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|