blob: 838d5b0aac87eaf5d00d3997ad57c66cf473c074 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package conversion
18
19import (
20 "fmt"
21 "reflect"
22)
23
24type typePair struct {
25 source reflect.Type
26 dest reflect.Type
27}
28
29type typeNamePair struct {
30 fieldType reflect.Type
31 fieldName string
32}
33
34// DebugLogger allows you to get debugging messages if necessary.
35type DebugLogger interface {
36 Logf(format string, args ...interface{})
37}
38
39type NameFunc func(t reflect.Type) string
40
41var DefaultNameFunc = func(t reflect.Type) string { return t.Name() }
42
43// ConversionFunc converts the object a into the object b, reusing arrays or objects
44// or pointers if necessary. It should return an error if the object cannot be converted
45// or if some data is invalid. If you do not wish a and b to share fields or nested
46// objects, you must copy a before calling this function.
47type ConversionFunc func(a, b interface{}, scope Scope) error
48
49// Converter knows how to convert one type to another.
50type Converter struct {
51 // Map from the conversion pair to a function which can
52 // do the conversion.
53 conversionFuncs ConversionFuncs
54 generatedConversionFuncs ConversionFuncs
55
56 // Set of conversions that should be treated as a no-op
57 ignoredConversions map[typePair]struct{}
58 ignoredUntypedConversions map[typePair]struct{}
59
60 // This is a map from a source field type and name, to a list of destination
61 // field type and name.
62 structFieldDests map[typeNamePair][]typeNamePair
63
64 // Allows for the opposite lookup of structFieldDests. So that SourceFromDest
65 // copy flag also works. So this is a map of destination field name, to potential
66 // source field name and type to look for.
67 structFieldSources map[typeNamePair][]typeNamePair
68
69 // Map from an input type to a function which can apply a key name mapping
70 inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc
71
72 // Map from an input type to a set of default conversion flags.
73 inputDefaultFlags map[reflect.Type]FieldMatchingFlags
74
75 // If non-nil, will be called to print helpful debugging info. Quite verbose.
76 Debug DebugLogger
77
78 // nameFunc is called to retrieve the name of a type; this name is used for the
79 // purpose of deciding whether two types match or not (i.e., will we attempt to
80 // do a conversion). The default returns the go type name.
81 nameFunc func(t reflect.Type) string
82}
83
84// NewConverter creates a new Converter object.
85func NewConverter(nameFn NameFunc) *Converter {
86 c := &Converter{
87 conversionFuncs: NewConversionFuncs(),
88 generatedConversionFuncs: NewConversionFuncs(),
89 ignoredConversions: make(map[typePair]struct{}),
90 ignoredUntypedConversions: make(map[typePair]struct{}),
91 nameFunc: nameFn,
92 structFieldDests: make(map[typeNamePair][]typeNamePair),
93 structFieldSources: make(map[typeNamePair][]typeNamePair),
94
95 inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc),
96 inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags),
97 }
98 c.RegisterUntypedConversionFunc(
99 (*[]byte)(nil), (*[]byte)(nil),
100 func(a, b interface{}, s Scope) error {
101 return Convert_Slice_byte_To_Slice_byte(a.(*[]byte), b.(*[]byte), s)
102 },
103 )
104 return c
105}
106
107// WithConversions returns a Converter that is a copy of c but with the additional
108// fns merged on top.
109func (c *Converter) WithConversions(fns ConversionFuncs) *Converter {
110 copied := *c
111 copied.conversionFuncs = c.conversionFuncs.Merge(fns)
112 return &copied
113}
114
115// DefaultMeta returns the conversion FieldMappingFunc and meta for a given type.
116func (c *Converter) DefaultMeta(t reflect.Type) (FieldMatchingFlags, *Meta) {
117 return c.inputDefaultFlags[t], &Meta{
118 KeyNameMapping: c.inputFieldMappingFuncs[t],
119 }
120}
121
122// Convert_Slice_byte_To_Slice_byte prevents recursing into every byte
123func Convert_Slice_byte_To_Slice_byte(in *[]byte, out *[]byte, s Scope) error {
124 if *in == nil {
125 *out = nil
126 return nil
127 }
128 *out = make([]byte, len(*in))
129 copy(*out, *in)
130 return nil
131}
132
133// Scope is passed to conversion funcs to allow them to continue an ongoing conversion.
134// If multiple converters exist in the system, Scope will allow you to use the correct one
135// from a conversion function--that is, the one your conversion function was called by.
136type Scope interface {
137 // Call Convert to convert sub-objects. Note that if you call it with your own exact
138 // parameters, you'll run out of stack space before anything useful happens.
139 Convert(src, dest interface{}, flags FieldMatchingFlags) error
140
141 // SrcTags and DestTags contain the struct tags that src and dest had, respectively.
142 // If the enclosing object was not a struct, then these will contain no tags, of course.
143 SrcTag() reflect.StructTag
144 DestTag() reflect.StructTag
145
146 // Flags returns the flags with which the conversion was started.
147 Flags() FieldMatchingFlags
148
149 // Meta returns any information originally passed to Convert.
150 Meta() *Meta
151}
152
153// FieldMappingFunc can convert an input field value into different values, depending on
154// the value of the source or destination struct tags.
155type FieldMappingFunc func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string)
156
157func NewConversionFuncs() ConversionFuncs {
158 return ConversionFuncs{
159 untyped: make(map[typePair]ConversionFunc),
160 }
161}
162
163type ConversionFuncs struct {
164 untyped map[typePair]ConversionFunc
165}
166
167// AddUntyped adds the provided conversion function to the lookup table for the types that are
168// supplied as a and b. a and b must be pointers or an error is returned. This method overwrites
169// previously defined functions.
170func (c ConversionFuncs) AddUntyped(a, b interface{}, fn ConversionFunc) error {
171 tA, tB := reflect.TypeOf(a), reflect.TypeOf(b)
172 if tA.Kind() != reflect.Ptr {
173 return fmt.Errorf("the type %T must be a pointer to register as an untyped conversion", a)
174 }
175 if tB.Kind() != reflect.Ptr {
176 return fmt.Errorf("the type %T must be a pointer to register as an untyped conversion", b)
177 }
178 c.untyped[typePair{tA, tB}] = fn
179 return nil
180}
181
182// Merge returns a new ConversionFuncs that contains all conversions from
183// both other and c, with other conversions taking precedence.
184func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs {
185 merged := NewConversionFuncs()
186 for k, v := range c.untyped {
187 merged.untyped[k] = v
188 }
189 for k, v := range other.untyped {
190 merged.untyped[k] = v
191 }
192 return merged
193}
194
195// Meta is supplied by Scheme, when it calls Convert.
196type Meta struct {
197 // KeyNameMapping is an optional function which may map the listed key (field name)
198 // into a source and destination value.
199 KeyNameMapping FieldMappingFunc
200 // Context is an optional field that callers may use to pass info to conversion functions.
201 Context interface{}
202}
203
204// scope contains information about an ongoing conversion.
205type scope struct {
206 converter *Converter
207 meta *Meta
208 flags FieldMatchingFlags
209
210 // srcStack & destStack are separate because they may not have a 1:1
211 // relationship.
212 srcStack scopeStack
213 destStack scopeStack
214}
215
216type scopeStackElem struct {
217 tag reflect.StructTag
218 value reflect.Value
219 key string
220}
221
222type scopeStack []scopeStackElem
223
224func (s *scopeStack) pop() {
225 n := len(*s)
226 *s = (*s)[:n-1]
227}
228
229func (s *scopeStack) push(e scopeStackElem) {
230 *s = append(*s, e)
231}
232
233func (s *scopeStack) top() *scopeStackElem {
234 return &(*s)[len(*s)-1]
235}
236
237func (s scopeStack) describe() string {
238 desc := ""
239 if len(s) > 1 {
240 desc = "(" + s[1].value.Type().String() + ")"
241 }
242 for i, v := range s {
243 if i < 2 {
244 // First layer on stack is not real; second is handled specially above.
245 continue
246 }
247 if v.key == "" {
248 desc += fmt.Sprintf(".%v", v.value.Type())
249 } else {
250 desc += fmt.Sprintf(".%v", v.key)
251 }
252 }
253 return desc
254}
255
256// Formats src & dest as indices for printing.
257func (s *scope) setIndices(src, dest int) {
258 s.srcStack.top().key = fmt.Sprintf("[%v]", src)
259 s.destStack.top().key = fmt.Sprintf("[%v]", dest)
260}
261
262// Formats src & dest as map keys for printing.
263func (s *scope) setKeys(src, dest interface{}) {
264 s.srcStack.top().key = fmt.Sprintf(`["%v"]`, src)
265 s.destStack.top().key = fmt.Sprintf(`["%v"]`, dest)
266}
267
268// Convert continues a conversion.
269func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {
270 return s.converter.Convert(src, dest, flags, s.meta)
271}
272
273// SrcTag returns the tag of the struct containing the current source item, if any.
274func (s *scope) SrcTag() reflect.StructTag {
275 return s.srcStack.top().tag
276}
277
278// DestTag returns the tag of the struct containing the current dest item, if any.
279func (s *scope) DestTag() reflect.StructTag {
280 return s.destStack.top().tag
281}
282
283// Flags returns the flags with which the current conversion was started.
284func (s *scope) Flags() FieldMatchingFlags {
285 return s.flags
286}
287
288// Meta returns the meta object that was originally passed to Convert.
289func (s *scope) Meta() *Meta {
290 return s.meta
291}
292
293// describe prints the path to get to the current (source, dest) values.
294func (s *scope) describe() (src, dest string) {
295 return s.srcStack.describe(), s.destStack.describe()
296}
297
298// error makes an error that includes information about where we were in the objects
299// we were asked to convert.
300func (s *scope) errorf(message string, args ...interface{}) error {
301 srcPath, destPath := s.describe()
302 where := fmt.Sprintf("converting %v to %v: ", srcPath, destPath)
303 return fmt.Errorf(where+message, args...)
304}
305
306// Verifies whether a conversion function has a correct signature.
307func verifyConversionFunctionSignature(ft reflect.Type) error {
308 if ft.Kind() != reflect.Func {
309 return fmt.Errorf("expected func, got: %v", ft)
310 }
311 if ft.NumIn() != 3 {
312 return fmt.Errorf("expected three 'in' params, got: %v", ft)
313 }
314 if ft.NumOut() != 1 {
315 return fmt.Errorf("expected one 'out' param, got: %v", ft)
316 }
317 if ft.In(0).Kind() != reflect.Ptr {
318 return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft)
319 }
320 if ft.In(1).Kind() != reflect.Ptr {
321 return fmt.Errorf("expected pointer arg for 'in' param 1, got: %v", ft)
322 }
323 scopeType := Scope(nil)
324 if e, a := reflect.TypeOf(&scopeType).Elem(), ft.In(2); e != a {
325 return fmt.Errorf("expected '%v' arg for 'in' param 2, got '%v' (%v)", e, a, ft)
326 }
327 var forErrorType error
328 // This convolution is necessary, otherwise TypeOf picks up on the fact
329 // that forErrorType is nil.
330 errorType := reflect.TypeOf(&forErrorType).Elem()
331 if ft.Out(0) != errorType {
332 return fmt.Errorf("expected error return, got: %v", ft)
333 }
334 return nil
335}
336
337// RegisterUntypedConversionFunc registers a function that converts between a and b by passing objects of those
338// types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
339// any other guarantee.
340func (c *Converter) RegisterUntypedConversionFunc(a, b interface{}, fn ConversionFunc) error {
341 return c.conversionFuncs.AddUntyped(a, b, fn)
342}
343
344// RegisterGeneratedUntypedConversionFunc registers a function that converts between a and b by passing objects of those
345// types to the provided function. The function *must* accept objects of a and b - this machinery will not enforce
346// any other guarantee.
347func (c *Converter) RegisterGeneratedUntypedConversionFunc(a, b interface{}, fn ConversionFunc) error {
348 return c.generatedConversionFuncs.AddUntyped(a, b, fn)
349}
350
351// RegisterIgnoredConversion registers a "no-op" for conversion, where any requested
352// conversion between from and to is ignored.
353func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error {
354 typeFrom := reflect.TypeOf(from)
355 typeTo := reflect.TypeOf(to)
356 if reflect.TypeOf(from).Kind() != reflect.Ptr {
357 return fmt.Errorf("expected pointer arg for 'from' param 0, got: %v", typeFrom)
358 }
359 if typeTo.Kind() != reflect.Ptr {
360 return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo)
361 }
362 c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{}
363 c.ignoredUntypedConversions[typePair{typeFrom, typeTo}] = struct{}{}
364 return nil
365}
366
367// RegisterInputDefaults registers a field name mapping function, used when converting
368// from maps to structs. Inputs to the conversion methods are checked for this type and a mapping
369// applied automatically if the input matches in. A set of default flags for the input conversion
370// may also be provided, which will be used when no explicit flags are requested.
371func (c *Converter) RegisterInputDefaults(in interface{}, fn FieldMappingFunc, defaultFlags FieldMatchingFlags) error {
372 fv := reflect.ValueOf(in)
373 ft := fv.Type()
374 if ft.Kind() != reflect.Ptr {
375 return fmt.Errorf("expected pointer 'in' argument, got: %v", ft)
376 }
377 c.inputFieldMappingFuncs[ft] = fn
378 c.inputDefaultFlags[ft] = defaultFlags
379 return nil
380}
381
382// FieldMatchingFlags contains a list of ways in which struct fields could be
383// copied. These constants may be | combined.
384type FieldMatchingFlags int
385
386const (
387 // Loop through destination fields, search for matching source
388 // field to copy it from. Source fields with no corresponding
389 // destination field will be ignored. If SourceToDest is
390 // specified, this flag is ignored. If neither is specified,
391 // or no flags are passed, this flag is the default.
392 DestFromSource FieldMatchingFlags = 0
393 // Loop through source fields, search for matching dest field
394 // to copy it into. Destination fields with no corresponding
395 // source field will be ignored.
396 SourceToDest FieldMatchingFlags = 1 << iota
397 // Don't treat it as an error if the corresponding source or
398 // dest field can't be found.
399 IgnoreMissingFields
400 // Don't require type names to match.
401 AllowDifferentFieldTypeNames
402)
403
404// IsSet returns true if the given flag or combination of flags is set.
405func (f FieldMatchingFlags) IsSet(flag FieldMatchingFlags) bool {
406 if flag == DestFromSource {
407 // The bit logic doesn't work on the default value.
408 return f&SourceToDest != SourceToDest
409 }
410 return f&flag == flag
411}
412
413// Convert will translate src to dest if it knows how. Both must be pointers.
414// If no conversion func is registered and the default copying mechanism
415// doesn't work on this type pair, an error will be returned.
416// Read the comments on the various FieldMatchingFlags constants to understand
417// what the 'flags' parameter does.
418// 'meta' is given to allow you to pass information to conversion functions,
419// it is not used by Convert() other than storing it in the scope.
420// Not safe for objects with cyclic references!
421func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
422 return c.doConversion(src, dest, flags, meta, c.convert)
423}
424
425type conversionFunc func(sv, dv reflect.Value, scope *scope) error
426
427func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error {
428 pair := typePair{reflect.TypeOf(src), reflect.TypeOf(dest)}
429 scope := &scope{
430 converter: c,
431 flags: flags,
432 meta: meta,
433 }
434
435 // ignore conversions of this type
436 if _, ok := c.ignoredUntypedConversions[pair]; ok {
437 return nil
438 }
439 if fn, ok := c.conversionFuncs.untyped[pair]; ok {
440 return fn(src, dest, scope)
441 }
442 if fn, ok := c.generatedConversionFuncs.untyped[pair]; ok {
443 return fn(src, dest, scope)
444 }
445
446 dv, err := EnforcePtr(dest)
447 if err != nil {
448 return err
449 }
450 sv, err := EnforcePtr(src)
451 if err != nil {
452 return err
453 }
454 return fmt.Errorf("converting (%s) to (%s): unknown conversion", sv.Type(), dv.Type())
455
456 // TODO: Everything past this point is deprecated.
457 // Remove in 1.20 once we're sure it didn't break anything.
458
459 // Leave something on the stack, so that calls to struct tag getters never fail.
460 scope.srcStack.push(scopeStackElem{})
461 scope.destStack.push(scopeStackElem{})
462 return f(sv, dv, scope)
463}
464
465// callUntyped calls predefined conversion func.
466func (c *Converter) callUntyped(sv, dv reflect.Value, f ConversionFunc, scope *scope) error {
467 if !dv.CanAddr() {
468 return scope.errorf("cant addr dest")
469 }
470 var svPointer reflect.Value
471 if sv.CanAddr() {
472 svPointer = sv.Addr()
473 } else {
474 svPointer = reflect.New(sv.Type())
475 svPointer.Elem().Set(sv)
476 }
477 dvPointer := dv.Addr()
478 return f(svPointer.Interface(), dvPointer.Interface(), scope)
479}
480
481// convert recursively copies sv into dv, calling an appropriate conversion function if
482// one is registered.
483func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {
484 dt, st := dv.Type(), sv.Type()
485 pair := typePair{st, dt}
486
487 // ignore conversions of this type
488 if _, ok := c.ignoredConversions[pair]; ok {
489 if c.Debug != nil {
490 c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt)
491 }
492 return nil
493 }
494
495 // Convert sv to dv.
496 pair = typePair{reflect.PtrTo(sv.Type()), reflect.PtrTo(dv.Type())}
497 if f, ok := c.conversionFuncs.untyped[pair]; ok {
498 return c.callUntyped(sv, dv, f, scope)
499 }
500 if f, ok := c.generatedConversionFuncs.untyped[pair]; ok {
501 return c.callUntyped(sv, dv, f, scope)
502 }
503
504 if !dv.CanSet() {
505 return scope.errorf("Cannot set dest. (Tried to deep copy something with unexported fields?)")
506 }
507
508 if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.nameFunc(dt) != c.nameFunc(st) {
509 return scope.errorf(
510 "type names don't match (%v, %v), and no conversion 'func (%v, %v) error' registered.",
511 c.nameFunc(st), c.nameFunc(dt), st, dt)
512 }
513
514 switch st.Kind() {
515 case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
516 // Don't copy these via assignment/conversion!
517 default:
518 // This should handle all simple types.
519 if st.AssignableTo(dt) {
520 dv.Set(sv)
521 return nil
522 }
523 if st.ConvertibleTo(dt) {
524 dv.Set(sv.Convert(dt))
525 return nil
526 }
527 }
528
529 if c.Debug != nil {
530 c.Debug.Logf("Trying to convert '%v' to '%v'", st, dt)
531 }
532
533 scope.srcStack.push(scopeStackElem{value: sv})
534 scope.destStack.push(scopeStackElem{value: dv})
535 defer scope.srcStack.pop()
536 defer scope.destStack.pop()
537
538 switch dv.Kind() {
539 case reflect.Struct:
540 return c.convertKV(toKVValue(sv), toKVValue(dv), scope)
541 case reflect.Slice:
542 if sv.IsNil() {
543 // Don't make a zero-length slice.
544 dv.Set(reflect.Zero(dt))
545 return nil
546 }
547 dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
548 for i := 0; i < sv.Len(); i++ {
549 scope.setIndices(i, i)
550 if err := c.convert(sv.Index(i), dv.Index(i), scope); err != nil {
551 return err
552 }
553 }
554 case reflect.Ptr:
555 if sv.IsNil() {
556 // Don't copy a nil ptr!
557 dv.Set(reflect.Zero(dt))
558 return nil
559 }
560 dv.Set(reflect.New(dt.Elem()))
561 switch st.Kind() {
562 case reflect.Ptr, reflect.Interface:
563 return c.convert(sv.Elem(), dv.Elem(), scope)
564 default:
565 return c.convert(sv, dv.Elem(), scope)
566 }
567 case reflect.Map:
568 if sv.IsNil() {
569 // Don't copy a nil ptr!
570 dv.Set(reflect.Zero(dt))
571 return nil
572 }
573 dv.Set(reflect.MakeMap(dt))
574 for _, sk := range sv.MapKeys() {
575 dk := reflect.New(dt.Key()).Elem()
576 if err := c.convert(sk, dk, scope); err != nil {
577 return err
578 }
579 dkv := reflect.New(dt.Elem()).Elem()
580 scope.setKeys(sk.Interface(), dk.Interface())
581 // TODO: sv.MapIndex(sk) may return a value with CanAddr() == false,
582 // because a map[string]struct{} does not allow a pointer reference.
583 // Calling a custom conversion function defined for the map value
584 // will panic. Example is PodInfo map[string]ContainerStatus.
585 if err := c.convert(sv.MapIndex(sk), dkv, scope); err != nil {
586 return err
587 }
588 dv.SetMapIndex(dk, dkv)
589 }
590 case reflect.Interface:
591 if sv.IsNil() {
592 // Don't copy a nil interface!
593 dv.Set(reflect.Zero(dt))
594 return nil
595 }
596 tmpdv := reflect.New(sv.Elem().Type()).Elem()
597 if err := c.convert(sv.Elem(), tmpdv, scope); err != nil {
598 return err
599 }
600 dv.Set(reflect.ValueOf(tmpdv.Interface()))
601 return nil
602 default:
603 return scope.errorf("couldn't copy '%v' into '%v'; didn't understand types", st, dt)
604 }
605 return nil
606}
607
608var stringType = reflect.TypeOf("")
609
610func toKVValue(v reflect.Value) kvValue {
611 switch v.Kind() {
612 case reflect.Struct:
613 return structAdaptor(v)
614 case reflect.Map:
615 if v.Type().Key().AssignableTo(stringType) {
616 return stringMapAdaptor(v)
617 }
618 }
619
620 return nil
621}
622
623// kvValue lets us write the same conversion logic to work with both maps
624// and structs. Only maps with string keys make sense for this.
625type kvValue interface {
626 // returns all keys, as a []string.
627 keys() []string
628 // Will just return "" for maps.
629 tagOf(key string) reflect.StructTag
630 // Will return the zero Value if the key doesn't exist.
631 value(key string) reflect.Value
632 // Maps require explicit setting-- will do nothing for structs.
633 // Returns false on failure.
634 confirmSet(key string, v reflect.Value) bool
635}
636
637type stringMapAdaptor reflect.Value
638
639func (a stringMapAdaptor) len() int {
640 return reflect.Value(a).Len()
641}
642
643func (a stringMapAdaptor) keys() []string {
644 v := reflect.Value(a)
645 keys := make([]string, v.Len())
646 for i, v := range v.MapKeys() {
647 if v.IsNil() {
648 continue
649 }
650 switch t := v.Interface().(type) {
651 case string:
652 keys[i] = t
653 }
654 }
655 return keys
656}
657
658func (a stringMapAdaptor) tagOf(key string) reflect.StructTag {
659 return ""
660}
661
662func (a stringMapAdaptor) value(key string) reflect.Value {
663 return reflect.Value(a).MapIndex(reflect.ValueOf(key))
664}
665
666func (a stringMapAdaptor) confirmSet(key string, v reflect.Value) bool {
667 return true
668}
669
670type structAdaptor reflect.Value
671
672func (a structAdaptor) len() int {
673 v := reflect.Value(a)
674 return v.Type().NumField()
675}
676
677func (a structAdaptor) keys() []string {
678 v := reflect.Value(a)
679 t := v.Type()
680 keys := make([]string, t.NumField())
681 for i := range keys {
682 keys[i] = t.Field(i).Name
683 }
684 return keys
685}
686
687func (a structAdaptor) tagOf(key string) reflect.StructTag {
688 v := reflect.Value(a)
689 field, ok := v.Type().FieldByName(key)
690 if ok {
691 return field.Tag
692 }
693 return ""
694}
695
696func (a structAdaptor) value(key string) reflect.Value {
697 v := reflect.Value(a)
698 return v.FieldByName(key)
699}
700
701func (a structAdaptor) confirmSet(key string, v reflect.Value) bool {
702 return true
703}
704
705// convertKV can convert things that consist of key/value pairs, like structs
706// and some maps.
707func (c *Converter) convertKV(skv, dkv kvValue, scope *scope) error {
708 if skv == nil || dkv == nil {
709 // TODO: add keys to stack to support really understandable error messages.
710 return fmt.Errorf("Unable to convert %#v to %#v", skv, dkv)
711 }
712
713 lister := dkv
714 if scope.flags.IsSet(SourceToDest) {
715 lister = skv
716 }
717
718 var mapping FieldMappingFunc
719 if scope.meta != nil && scope.meta.KeyNameMapping != nil {
720 mapping = scope.meta.KeyNameMapping
721 }
722
723 for _, key := range lister.keys() {
724 if found, err := c.checkField(key, skv, dkv, scope); found {
725 if err != nil {
726 return err
727 }
728 continue
729 }
730 stag := skv.tagOf(key)
731 dtag := dkv.tagOf(key)
732 skey := key
733 dkey := key
734 if mapping != nil {
735 skey, dkey = scope.meta.KeyNameMapping(key, stag, dtag)
736 }
737
738 df := dkv.value(dkey)
739 sf := skv.value(skey)
740 if !df.IsValid() || !sf.IsValid() {
741 switch {
742 case scope.flags.IsSet(IgnoreMissingFields):
743 // No error.
744 case scope.flags.IsSet(SourceToDest):
745 return scope.errorf("%v not present in dest", dkey)
746 default:
747 return scope.errorf("%v not present in src", skey)
748 }
749 continue
750 }
751 scope.srcStack.top().key = skey
752 scope.srcStack.top().tag = stag
753 scope.destStack.top().key = dkey
754 scope.destStack.top().tag = dtag
755 if err := c.convert(sf, df, scope); err != nil {
756 return err
757 }
758 }
759 return nil
760}
761
762// checkField returns true if the field name matches any of the struct
763// field copying rules. The error should be ignored if it returns false.
764func (c *Converter) checkField(fieldName string, skv, dkv kvValue, scope *scope) (bool, error) {
765 replacementMade := false
766 if scope.flags.IsSet(DestFromSource) {
767 df := dkv.value(fieldName)
768 if !df.IsValid() {
769 return false, nil
770 }
771 destKey := typeNamePair{df.Type(), fieldName}
772 // Check each of the potential source (type, name) pairs to see if they're
773 // present in sv.
774 for _, potentialSourceKey := range c.structFieldSources[destKey] {
775 sf := skv.value(potentialSourceKey.fieldName)
776 if !sf.IsValid() {
777 continue
778 }
779 if sf.Type() == potentialSourceKey.fieldType {
780 // Both the source's name and type matched, so copy.
781 scope.srcStack.top().key = potentialSourceKey.fieldName
782 scope.destStack.top().key = fieldName
783 if err := c.convert(sf, df, scope); err != nil {
784 return true, err
785 }
786 dkv.confirmSet(fieldName, df)
787 replacementMade = true
788 }
789 }
790 return replacementMade, nil
791 }
792
793 sf := skv.value(fieldName)
794 if !sf.IsValid() {
795 return false, nil
796 }
797 srcKey := typeNamePair{sf.Type(), fieldName}
798 // Check each of the potential dest (type, name) pairs to see if they're
799 // present in dv.
800 for _, potentialDestKey := range c.structFieldDests[srcKey] {
801 df := dkv.value(potentialDestKey.fieldName)
802 if !df.IsValid() {
803 continue
804 }
805 if df.Type() == potentialDestKey.fieldType {
806 // Both the dest's name and type matched, so copy.
807 scope.srcStack.top().key = fieldName
808 scope.destStack.top().key = potentialDestKey.fieldName
809 if err := c.convert(sf, df, scope); err != nil {
810 return true, err
811 }
812 dkv.confirmSet(potentialDestKey.fieldName, df)
813 replacementMade = true
814 }
815 }
816 return replacementMade, nil
817}