|
|
|
@ -26,16 +26,6 @@ type typePair struct {
|
|
|
|
|
dest reflect.Type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type typeNamePair struct {
|
|
|
|
|
fieldType reflect.Type
|
|
|
|
|
fieldName string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DebugLogger allows you to get debugging messages if necessary.
|
|
|
|
|
type DebugLogger interface {
|
|
|
|
|
Logf(format string, args ...interface{})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type NameFunc func(t reflect.Type) string
|
|
|
|
|
|
|
|
|
|
var DefaultNameFunc = func(t reflect.Type) string { return t.Name() }
|
|
|
|
@ -57,24 +47,12 @@ type Converter struct {
|
|
|
|
|
ignoredConversions map[typePair]struct{}
|
|
|
|
|
ignoredUntypedConversions map[typePair]struct{}
|
|
|
|
|
|
|
|
|
|
// This is a map from a source field type and name, to a list of destination
|
|
|
|
|
// field type and name.
|
|
|
|
|
structFieldDests map[typeNamePair][]typeNamePair
|
|
|
|
|
|
|
|
|
|
// Allows for the opposite lookup of structFieldDests. So that SourceFromDest
|
|
|
|
|
// copy flag also works. So this is a map of destination field name, to potential
|
|
|
|
|
// source field name and type to look for.
|
|
|
|
|
structFieldSources map[typeNamePair][]typeNamePair
|
|
|
|
|
|
|
|
|
|
// Map from an input type to a function which can apply a key name mapping
|
|
|
|
|
inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc
|
|
|
|
|
|
|
|
|
|
// Map from an input type to a set of default conversion flags.
|
|
|
|
|
inputDefaultFlags map[reflect.Type]FieldMatchingFlags
|
|
|
|
|
|
|
|
|
|
// If non-nil, will be called to print helpful debugging info. Quite verbose.
|
|
|
|
|
Debug DebugLogger
|
|
|
|
|
|
|
|
|
|
// nameFunc is called to retrieve the name of a type; this name is used for the
|
|
|
|
|
// purpose of deciding whether two types match or not (i.e., will we attempt to
|
|
|
|
|
// do a conversion). The default returns the go type name.
|
|
|
|
@ -89,8 +67,6 @@ func NewConverter(nameFn NameFunc) *Converter {
|
|
|
|
|
ignoredConversions: make(map[typePair]struct{}),
|
|
|
|
|
ignoredUntypedConversions: make(map[typePair]struct{}),
|
|
|
|
|
nameFunc: nameFn,
|
|
|
|
|
structFieldDests: make(map[typeNamePair][]typeNamePair),
|
|
|
|
|
structFieldSources: make(map[typeNamePair][]typeNamePair),
|
|
|
|
|
|
|
|
|
|
inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc),
|
|
|
|
|
inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags),
|
|
|
|
@ -138,11 +114,6 @@ type Scope interface {
|
|
|
|
|
// parameters, you'll run out of stack space before anything useful happens.
|
|
|
|
|
Convert(src, dest interface{}, flags FieldMatchingFlags) error
|
|
|
|
|
|
|
|
|
|
// SrcTags and DestTags contain the struct tags that src and dest had, respectively.
|
|
|
|
|
// If the enclosing object was not a struct, then these will contain no tags, of course.
|
|
|
|
|
SrcTag() reflect.StructTag
|
|
|
|
|
DestTag() reflect.StructTag
|
|
|
|
|
|
|
|
|
|
// Flags returns the flags with which the conversion was started.
|
|
|
|
|
Flags() FieldMatchingFlags
|
|
|
|
|
|
|
|
|
@ -206,63 +177,6 @@ type scope struct {
|
|
|
|
|
converter *Converter
|
|
|
|
|
meta *Meta
|
|
|
|
|
flags FieldMatchingFlags
|
|
|
|
|
|
|
|
|
|
// srcStack & destStack are separate because they may not have a 1:1
|
|
|
|
|
// relationship.
|
|
|
|
|
srcStack scopeStack
|
|
|
|
|
destStack scopeStack
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type scopeStackElem struct {
|
|
|
|
|
tag reflect.StructTag
|
|
|
|
|
value reflect.Value
|
|
|
|
|
key string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type scopeStack []scopeStackElem
|
|
|
|
|
|
|
|
|
|
func (s *scopeStack) pop() {
|
|
|
|
|
n := len(*s)
|
|
|
|
|
*s = (*s)[:n-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *scopeStack) push(e scopeStackElem) {
|
|
|
|
|
*s = append(*s, e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *scopeStack) top() *scopeStackElem {
|
|
|
|
|
return &(*s)[len(*s)-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s scopeStack) describe() string {
|
|
|
|
|
desc := ""
|
|
|
|
|
if len(s) > 1 {
|
|
|
|
|
desc = "(" + s[1].value.Type().String() + ")"
|
|
|
|
|
}
|
|
|
|
|
for i, v := range s {
|
|
|
|
|
if i < 2 {
|
|
|
|
|
// First layer on stack is not real; second is handled specially above.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if v.key == "" {
|
|
|
|
|
desc += fmt.Sprintf(".%v", v.value.Type())
|
|
|
|
|
} else {
|
|
|
|
|
desc += fmt.Sprintf(".%v", v.key)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return desc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Formats src & dest as indices for printing.
|
|
|
|
|
func (s *scope) setIndices(src, dest int) {
|
|
|
|
|
s.srcStack.top().key = fmt.Sprintf("[%v]", src)
|
|
|
|
|
s.destStack.top().key = fmt.Sprintf("[%v]", dest)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Formats src & dest as map keys for printing.
|
|
|
|
|
func (s *scope) setKeys(src, dest interface{}) {
|
|
|
|
|
s.srcStack.top().key = fmt.Sprintf(`["%v"]`, src)
|
|
|
|
|
s.destStack.top().key = fmt.Sprintf(`["%v"]`, dest)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert continues a conversion.
|
|
|
|
@ -270,16 +184,6 @@ func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {
|
|
|
|
|
return s.converter.Convert(src, dest, flags, s.meta)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SrcTag returns the tag of the struct containing the current source item, if any.
|
|
|
|
|
func (s *scope) SrcTag() reflect.StructTag {
|
|
|
|
|
return s.srcStack.top().tag
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DestTag returns the tag of the struct containing the current dest item, if any.
|
|
|
|
|
func (s *scope) DestTag() reflect.StructTag {
|
|
|
|
|
return s.destStack.top().tag
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Flags returns the flags with which the current conversion was started.
|
|
|
|
|
func (s *scope) Flags() FieldMatchingFlags {
|
|
|
|
|
return s.flags
|
|
|
|
@ -290,19 +194,6 @@ func (s *scope) Meta() *Meta {
|
|
|
|
|
return s.meta
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// describe prints the path to get to the current (source, dest) values.
|
|
|
|
|
func (s *scope) describe() (src, dest string) {
|
|
|
|
|
return s.srcStack.describe(), s.destStack.describe()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// error makes an error that includes information about where we were in the objects
|
|
|
|
|
// we were asked to convert.
|
|
|
|
|
func (s *scope) errorf(message string, args ...interface{}) error {
|
|
|
|
|
srcPath, destPath := s.describe()
|
|
|
|
|
where := fmt.Sprintf("converting %v to %v: ", srcPath, destPath)
|
|
|
|
|
return fmt.Errorf(where+message, args...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RegisterUntypedConversionFunc registers a function that converts between a and b by passing objects of those
|
|
|
|
|
// types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
|
|
|
|
|
// any other guarantee.
|
|
|
|
@ -388,12 +279,6 @@ func (f FieldMatchingFlags) IsSet(flag FieldMatchingFlags) bool {
|
|
|
|
|
// it is not used by Convert() other than storing it in the scope.
|
|
|
|
|
// Not safe for objects with cyclic references!
|
|
|
|
|
func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
|
|
|
|
|
return c.doConversion(src, dest, flags, meta, c.convert)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type conversionFunc func(sv, dv reflect.Value, scope *scope) error
|
|
|
|
|
|
|
|
|
|
func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error {
|
|
|
|
|
pair := typePair{reflect.TypeOf(src), reflect.TypeOf(dest)}
|
|
|
|
|
scope := &scope{
|
|
|
|
|
converter: c,
|
|
|
|
@ -421,357 +306,4 @@ func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("converting (%s) to (%s): unknown conversion", sv.Type(), dv.Type())
|
|
|
|
|
|
|
|
|
|
// TODO: Everything past this point is deprecated.
|
|
|
|
|
// Remove in 1.20 once we're sure it didn't break anything.
|
|
|
|
|
|
|
|
|
|
// Leave something on the stack, so that calls to struct tag getters never fail.
|
|
|
|
|
scope.srcStack.push(scopeStackElem{})
|
|
|
|
|
scope.destStack.push(scopeStackElem{})
|
|
|
|
|
return f(sv, dv, scope)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// callUntyped calls predefined conversion func.
|
|
|
|
|
func (c *Converter) callUntyped(sv, dv reflect.Value, f ConversionFunc, scope *scope) error {
|
|
|
|
|
if !dv.CanAddr() {
|
|
|
|
|
return scope.errorf("cant addr dest")
|
|
|
|
|
}
|
|
|
|
|
var svPointer reflect.Value
|
|
|
|
|
if sv.CanAddr() {
|
|
|
|
|
svPointer = sv.Addr()
|
|
|
|
|
} else {
|
|
|
|
|
svPointer = reflect.New(sv.Type())
|
|
|
|
|
svPointer.Elem().Set(sv)
|
|
|
|
|
}
|
|
|
|
|
dvPointer := dv.Addr()
|
|
|
|
|
return f(svPointer.Interface(), dvPointer.Interface(), scope)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// convert recursively copies sv into dv, calling an appropriate conversion function if
|
|
|
|
|
// one is registered.
|
|
|
|
|
func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {
|
|
|
|
|
dt, st := dv.Type(), sv.Type()
|
|
|
|
|
pair := typePair{st, dt}
|
|
|
|
|
|
|
|
|
|
// ignore conversions of this type
|
|
|
|
|
if _, ok := c.ignoredConversions[pair]; ok {
|
|
|
|
|
if c.Debug != nil {
|
|
|
|
|
c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert sv to dv.
|
|
|
|
|
pair = typePair{reflect.PtrTo(sv.Type()), reflect.PtrTo(dv.Type())}
|
|
|
|
|
if f, ok := c.conversionFuncs.untyped[pair]; ok {
|
|
|
|
|
return c.callUntyped(sv, dv, f, scope)
|
|
|
|
|
}
|
|
|
|
|
if f, ok := c.generatedConversionFuncs.untyped[pair]; ok {
|
|
|
|
|
return c.callUntyped(sv, dv, f, scope)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !dv.CanSet() {
|
|
|
|
|
return scope.errorf("Cannot set dest. (Tried to deep copy something with unexported fields?)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.nameFunc(dt) != c.nameFunc(st) {
|
|
|
|
|
return scope.errorf(
|
|
|
|
|
"type names don't match (%v, %v), and no conversion 'func (%v, %v) error' registered.",
|
|
|
|
|
c.nameFunc(st), c.nameFunc(dt), st, dt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch st.Kind() {
|
|
|
|
|
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
|
|
|
|
// Don't copy these via assignment/conversion!
|
|
|
|
|
default:
|
|
|
|
|
// This should handle all simple types.
|
|
|
|
|
if st.AssignableTo(dt) {
|
|
|
|
|
dv.Set(sv)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if st.ConvertibleTo(dt) {
|
|
|
|
|
dv.Set(sv.Convert(dt))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.Debug != nil {
|
|
|
|
|
c.Debug.Logf("Trying to convert '%v' to '%v'", st, dt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scope.srcStack.push(scopeStackElem{value: sv})
|
|
|
|
|
scope.destStack.push(scopeStackElem{value: dv})
|
|
|
|
|
defer scope.srcStack.pop()
|
|
|
|
|
defer scope.destStack.pop()
|
|
|
|
|
|
|
|
|
|
switch dv.Kind() {
|
|
|
|
|
case reflect.Struct:
|
|
|
|
|
return c.convertKV(toKVValue(sv), toKVValue(dv), scope)
|
|
|
|
|
case reflect.Slice:
|
|
|
|
|
if sv.IsNil() {
|
|
|
|
|
// Don't make a zero-length slice.
|
|
|
|
|
dv.Set(reflect.Zero(dt))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
|
|
|
|
|
for i := 0; i < sv.Len(); i++ {
|
|
|
|
|
scope.setIndices(i, i)
|
|
|
|
|
if err := c.convert(sv.Index(i), dv.Index(i), scope); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case reflect.Ptr:
|
|
|
|
|
if sv.IsNil() {
|
|
|
|
|
// Don't copy a nil ptr!
|
|
|
|
|
dv.Set(reflect.Zero(dt))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
dv.Set(reflect.New(dt.Elem()))
|
|
|
|
|
switch st.Kind() {
|
|
|
|
|
case reflect.Ptr, reflect.Interface:
|
|
|
|
|
return c.convert(sv.Elem(), dv.Elem(), scope)
|
|
|
|
|
default:
|
|
|
|
|
return c.convert(sv, dv.Elem(), scope)
|
|
|
|
|
}
|
|
|
|
|
case reflect.Map:
|
|
|
|
|
if sv.IsNil() {
|
|
|
|
|
// Don't copy a nil ptr!
|
|
|
|
|
dv.Set(reflect.Zero(dt))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
dv.Set(reflect.MakeMap(dt))
|
|
|
|
|
for _, sk := range sv.MapKeys() {
|
|
|
|
|
dk := reflect.New(dt.Key()).Elem()
|
|
|
|
|
if err := c.convert(sk, dk, scope); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
dkv := reflect.New(dt.Elem()).Elem()
|
|
|
|
|
scope.setKeys(sk.Interface(), dk.Interface())
|
|
|
|
|
// TODO: sv.MapIndex(sk) may return a value with CanAddr() == false,
|
|
|
|
|
// because a map[string]struct{} does not allow a pointer reference.
|
|
|
|
|
// Calling a custom conversion function defined for the map value
|
|
|
|
|
// will panic. Example is PodInfo map[string]ContainerStatus.
|
|
|
|
|
if err := c.convert(sv.MapIndex(sk), dkv, scope); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
dv.SetMapIndex(dk, dkv)
|
|
|
|
|
}
|
|
|
|
|
case reflect.Interface:
|
|
|
|
|
if sv.IsNil() {
|
|
|
|
|
// Don't copy a nil interface!
|
|
|
|
|
dv.Set(reflect.Zero(dt))
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
tmpdv := reflect.New(sv.Elem().Type()).Elem()
|
|
|
|
|
if err := c.convert(sv.Elem(), tmpdv, scope); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
dv.Set(reflect.ValueOf(tmpdv.Interface()))
|
|
|
|
|
return nil
|
|
|
|
|
default:
|
|
|
|
|
return scope.errorf("couldn't copy '%v' into '%v'; didn't understand types", st, dt)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var stringType = reflect.TypeOf("")
|
|
|
|
|
|
|
|
|
|
func toKVValue(v reflect.Value) kvValue {
|
|
|
|
|
switch v.Kind() {
|
|
|
|
|
case reflect.Struct:
|
|
|
|
|
return structAdaptor(v)
|
|
|
|
|
case reflect.Map:
|
|
|
|
|
if v.Type().Key().AssignableTo(stringType) {
|
|
|
|
|
return stringMapAdaptor(v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// kvValue lets us write the same conversion logic to work with both maps
|
|
|
|
|
// and structs. Only maps with string keys make sense for this.
|
|
|
|
|
type kvValue interface {
|
|
|
|
|
// returns all keys, as a []string.
|
|
|
|
|
keys() []string
|
|
|
|
|
// Will just return "" for maps.
|
|
|
|
|
tagOf(key string) reflect.StructTag
|
|
|
|
|
// Will return the zero Value if the key doesn't exist.
|
|
|
|
|
value(key string) reflect.Value
|
|
|
|
|
// Maps require explicit setting-- will do nothing for structs.
|
|
|
|
|
// Returns false on failure.
|
|
|
|
|
confirmSet(key string, v reflect.Value) bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type stringMapAdaptor reflect.Value
|
|
|
|
|
|
|
|
|
|
func (a stringMapAdaptor) keys() []string {
|
|
|
|
|
v := reflect.Value(a)
|
|
|
|
|
keys := make([]string, v.Len())
|
|
|
|
|
for i, v := range v.MapKeys() {
|
|
|
|
|
if v.IsNil() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
switch t := v.Interface().(type) {
|
|
|
|
|
case string:
|
|
|
|
|
keys[i] = t
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return keys
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a stringMapAdaptor) tagOf(key string) reflect.StructTag {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a stringMapAdaptor) value(key string) reflect.Value {
|
|
|
|
|
return reflect.Value(a).MapIndex(reflect.ValueOf(key))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a stringMapAdaptor) confirmSet(key string, v reflect.Value) bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type structAdaptor reflect.Value
|
|
|
|
|
|
|
|
|
|
func (a structAdaptor) keys() []string {
|
|
|
|
|
v := reflect.Value(a)
|
|
|
|
|
t := v.Type()
|
|
|
|
|
keys := make([]string, t.NumField())
|
|
|
|
|
for i := range keys {
|
|
|
|
|
keys[i] = t.Field(i).Name
|
|
|
|
|
}
|
|
|
|
|
return keys
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a structAdaptor) tagOf(key string) reflect.StructTag {
|
|
|
|
|
v := reflect.Value(a)
|
|
|
|
|
field, ok := v.Type().FieldByName(key)
|
|
|
|
|
if ok {
|
|
|
|
|
return field.Tag
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a structAdaptor) value(key string) reflect.Value {
|
|
|
|
|
v := reflect.Value(a)
|
|
|
|
|
return v.FieldByName(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a structAdaptor) confirmSet(key string, v reflect.Value) bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// convertKV can convert things that consist of key/value pairs, like structs
|
|
|
|
|
// and some maps.
|
|
|
|
|
func (c *Converter) convertKV(skv, dkv kvValue, scope *scope) error {
|
|
|
|
|
if skv == nil || dkv == nil {
|
|
|
|
|
// TODO: add keys to stack to support really understandable error messages.
|
|
|
|
|
return fmt.Errorf("Unable to convert %#v to %#v", skv, dkv)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lister := dkv
|
|
|
|
|
if scope.flags.IsSet(SourceToDest) {
|
|
|
|
|
lister = skv
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var mapping FieldMappingFunc
|
|
|
|
|
if scope.meta != nil && scope.meta.KeyNameMapping != nil {
|
|
|
|
|
mapping = scope.meta.KeyNameMapping
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, key := range lister.keys() {
|
|
|
|
|
if found, err := c.checkField(key, skv, dkv, scope); found {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
stag := skv.tagOf(key)
|
|
|
|
|
dtag := dkv.tagOf(key)
|
|
|
|
|
skey := key
|
|
|
|
|
dkey := key
|
|
|
|
|
if mapping != nil {
|
|
|
|
|
skey, dkey = scope.meta.KeyNameMapping(key, stag, dtag)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
df := dkv.value(dkey)
|
|
|
|
|
sf := skv.value(skey)
|
|
|
|
|
if !df.IsValid() || !sf.IsValid() {
|
|
|
|
|
switch {
|
|
|
|
|
case scope.flags.IsSet(IgnoreMissingFields):
|
|
|
|
|
// No error.
|
|
|
|
|
case scope.flags.IsSet(SourceToDest):
|
|
|
|
|
return scope.errorf("%v not present in dest", dkey)
|
|
|
|
|
default:
|
|
|
|
|
return scope.errorf("%v not present in src", skey)
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
scope.srcStack.top().key = skey
|
|
|
|
|
scope.srcStack.top().tag = stag
|
|
|
|
|
scope.destStack.top().key = dkey
|
|
|
|
|
scope.destStack.top().tag = dtag
|
|
|
|
|
if err := c.convert(sf, df, scope); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checkField returns true if the field name matches any of the struct
|
|
|
|
|
// field copying rules. The error should be ignored if it returns false.
|
|
|
|
|
func (c *Converter) checkField(fieldName string, skv, dkv kvValue, scope *scope) (bool, error) {
|
|
|
|
|
replacementMade := false
|
|
|
|
|
if scope.flags.IsSet(DestFromSource) {
|
|
|
|
|
df := dkv.value(fieldName)
|
|
|
|
|
if !df.IsValid() {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
destKey := typeNamePair{df.Type(), fieldName}
|
|
|
|
|
// Check each of the potential source (type, name) pairs to see if they're
|
|
|
|
|
// present in sv.
|
|
|
|
|
for _, potentialSourceKey := range c.structFieldSources[destKey] {
|
|
|
|
|
sf := skv.value(potentialSourceKey.fieldName)
|
|
|
|
|
if !sf.IsValid() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if sf.Type() == potentialSourceKey.fieldType {
|
|
|
|
|
// Both the source's name and type matched, so copy.
|
|
|
|
|
scope.srcStack.top().key = potentialSourceKey.fieldName
|
|
|
|
|
scope.destStack.top().key = fieldName
|
|
|
|
|
if err := c.convert(sf, df, scope); err != nil {
|
|
|
|
|
return true, err
|
|
|
|
|
}
|
|
|
|
|
dkv.confirmSet(fieldName, df)
|
|
|
|
|
replacementMade = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return replacementMade, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sf := skv.value(fieldName)
|
|
|
|
|
if !sf.IsValid() {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
srcKey := typeNamePair{sf.Type(), fieldName}
|
|
|
|
|
// Check each of the potential dest (type, name) pairs to see if they're
|
|
|
|
|
// present in dv.
|
|
|
|
|
for _, potentialDestKey := range c.structFieldDests[srcKey] {
|
|
|
|
|
df := dkv.value(potentialDestKey.fieldName)
|
|
|
|
|
if !df.IsValid() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if df.Type() == potentialDestKey.fieldType {
|
|
|
|
|
// Both the dest's name and type matched, so copy.
|
|
|
|
|
scope.srcStack.top().key = fieldName
|
|
|
|
|
scope.destStack.top().key = potentialDestKey.fieldName
|
|
|
|
|
if err := c.convert(sf, df, scope); err != nil {
|
|
|
|
|
return true, err
|
|
|
|
|
}
|
|
|
|
|
dkv.confirmSet(potentialDestKey.fieldName, df)
|
|
|
|
|
replacementMade = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return replacementMade, nil
|
|
|
|
|
}
|
|
|
|
|