mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 07:20:13 +00:00
Merge pull request #3223 from liggitt/default_conversion
Make default conversion behavior available to conversion funcs
This commit is contained in:
commit
4b61dc84cb
@ -78,6 +78,10 @@ type Scope interface {
|
||||
// parameters, you'll run out of stack space before anything useful happens.
|
||||
Convert(src, dest interface{}, flags FieldMatchingFlags) error
|
||||
|
||||
// DefaultConvert performs the default conversion, without calling a conversion func
|
||||
// on the current stack frame. This makes it safe to call from a conversion func.
|
||||
DefaultConvert(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
|
||||
@ -167,6 +171,12 @@ func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {
|
||||
return s.converter.Convert(src, dest, flags, s.meta)
|
||||
}
|
||||
|
||||
// DefaultConvert continues a conversion, performing a default conversion (no conversion func)
|
||||
// for the current stack frame.
|
||||
func (s *scope) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags) error {
|
||||
return s.converter.DefaultConvert(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
|
||||
@ -296,6 +306,24 @@ 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)
|
||||
}
|
||||
|
||||
// DefaultConvert will translate src to dest if it knows how. Both must be pointers.
|
||||
// No conversion func is used. If the default copying mechanism
|
||||
// doesn't work on this type pair, an error will be returned.
|
||||
// Read the comments on the various FieldMatchingFlags constants to understand
|
||||
// what the 'flags' parameter does.
|
||||
// 'meta' is given to allow you to pass information to conversion functions,
|
||||
// it is not used by DefaultConvert() other than storing it in the scope.
|
||||
// Not safe for objects with cyclic references!
|
||||
func (c *Converter) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
|
||||
return c.doConversion(src, dest, flags, meta, c.defaultConvert)
|
||||
}
|
||||
|
||||
type conversionFunc func(sv, dv reflect.Value, scope *scope) error
|
||||
|
||||
func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error {
|
||||
dv, err := EnforcePtr(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -315,7 +343,7 @@ func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, met
|
||||
// Leave something on the stack, so that calls to struct tag getters never fail.
|
||||
s.srcStack.push(scopeStackElem{})
|
||||
s.destStack.push(scopeStackElem{})
|
||||
return c.convert(sv, dv, s)
|
||||
return f(sv, dv, s)
|
||||
}
|
||||
|
||||
// callCustom calls 'custom' with sv & dv. custom must be a conversion function.
|
||||
@ -358,6 +386,14 @@ func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {
|
||||
return c.callCustom(sv, dv, fv, scope)
|
||||
}
|
||||
|
||||
return c.defaultConvert(sv, dv, scope)
|
||||
}
|
||||
|
||||
// defaultConvert recursively copies sv into dv. no conversion function is called
|
||||
// for the current stack frame (but conversion functions may be called for nested objects)
|
||||
func (c *Converter) defaultConvert(sv, dv reflect.Value, scope *scope) error {
|
||||
dt, st := dv.Type(), sv.Type()
|
||||
|
||||
if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.NameFunc(dt) != c.NameFunc(st) {
|
||||
return scope.error("type names don't match (%v, %v)", c.NameFunc(st), c.NameFunc(dt))
|
||||
}
|
||||
|
@ -25,6 +25,47 @@ import (
|
||||
"github.com/google/gofuzz"
|
||||
)
|
||||
|
||||
func TestConverter_DefaultConvert(t *testing.T) {
|
||||
type A struct {
|
||||
Foo string
|
||||
Baz int
|
||||
}
|
||||
type B struct {
|
||||
Bar string
|
||||
Baz int
|
||||
}
|
||||
c := NewConverter()
|
||||
c.Debug = t
|
||||
c.NameFunc = func(t reflect.Type) string { return "MyType" }
|
||||
|
||||
// Ensure conversion funcs can call DefaultConvert to get default behavior,
|
||||
// then fixup remaining fields manually
|
||||
err := c.Register(func(in *A, out *B, s Scope) error {
|
||||
if err := s.DefaultConvert(in, out, IgnoreMissingFields); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Bar = in.Foo
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
x := A{"hello, intrepid test reader!", 3}
|
||||
y := B{}
|
||||
|
||||
err = c.Convert(&x, &y, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if e, a := x.Foo, y.Bar; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := x.Baz, y.Baz; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
||||
type A struct {
|
||||
Foo string
|
||||
@ -86,7 +127,6 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
err = c.Convert(&A{}, &C{}, 0, nil)
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
|
Loading…
Reference in New Issue
Block a user