in internal/kernel/conversions.go [137:225]
func (c *Client) CastPtrToRef(dataVal reflect.Value) interface{} {
if !dataVal.IsValid() {
// dataVal is a 0-value, meaning we have no value available... We return
// this to JavaScript as a "null" value.
return nil
}
if (dataVal.Kind() == reflect.Interface || dataVal.Kind() == reflect.Ptr) && dataVal.IsNil() {
return nil
}
// In case we got a time.Time value (or pointer to one).
if wireDate, isDate := castPtrToDate(dataVal); isDate {
return wireDate
}
switch dataVal.Kind() {
case reflect.Map:
result := api.WireMap{MapData: make(map[string]interface{})}
iter := dataVal.MapRange()
for iter.Next() {
key := iter.Key().String()
val := iter.Value()
result.MapData[key] = c.CastPtrToRef(val)
}
return result
case reflect.Interface, reflect.Ptr:
if valref, valHasRef := c.FindObjectRef(dataVal); valHasRef {
return valref
}
// In case we got a pointer to a map, slice, enum, ...
if elem := reflect.Indirect(dataVal.Elem()); elem.Kind() != reflect.Struct {
return c.CastPtrToRef(elem)
}
if dataVal.Elem().Kind() == reflect.Struct {
elemVal := dataVal.Elem()
if fields, fqn, isStruct := c.Types().StructFields(elemVal.Type()); isStruct {
data := make(map[string]interface{})
for _, field := range fields {
fieldVal := elemVal.FieldByIndex(field.Index)
if (fieldVal.Kind() == reflect.Ptr || fieldVal.Kind() == reflect.Interface) && fieldVal.IsNil() {
// If there is the "field" tag, and it's "required", then panic since the value is nil.
if requiredOrOptional, found := field.Tag.Lookup("field"); found && requiredOrOptional == "required" {
panic(fmt.Sprintf("Field %v.%v is required, but has nil value", field.Type, field.Name))
}
continue
}
key := field.Tag.Get("json")
data[key] = c.CastPtrToRef(fieldVal)
}
return api.WireStruct{
StructDescriptor: api.StructDescriptor{
FQN: fqn,
Fields: data,
},
}
}
} else if dataVal.Elem().Kind() == reflect.Ptr {
// Typically happens when a struct pointer is passed into an interface{}
// typed API (such as a place where a union is accepted).
elemVal := dataVal.Elem()
return c.CastPtrToRef(elemVal)
}
if ref, err := c.ManageObject(dataVal); err != nil {
panic(err)
} else {
return ref
}
case reflect.Slice:
refs := make([]interface{}, dataVal.Len())
for i := 0; i < dataVal.Len(); i++ {
refs[i] = c.CastPtrToRef(dataVal.Index(i))
}
return refs
case reflect.String:
if enumRef, isEnumRef := c.Types().TryRenderEnumRef(dataVal); isEnumRef {
return enumRef
}
}
return dataVal.Interface()
}