mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-02 16:29:21 +00:00
Make default conversion behavior available to conversion funcs
This commit is contained in:
parent
22a241aa57
commit
2da1865fc2
@ -78,6 +78,10 @@ type Scope interface {
|
|||||||
// parameters, you'll run out of stack space before anything useful happens.
|
// parameters, you'll run out of stack space before anything useful happens.
|
||||||
Convert(src, dest interface{}, flags FieldMatchingFlags) error
|
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.
|
// 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.
|
// If the enclosing object was not a struct, then these will contain no tags, of course.
|
||||||
SrcTag() reflect.StructTag
|
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)
|
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.
|
// SrcTag returns the tag of the struct containing the current source item, if any.
|
||||||
func (s *scope) SrcTag() reflect.StructTag {
|
func (s *scope) SrcTag() reflect.StructTag {
|
||||||
return s.srcStack.top().tag
|
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.
|
// it is not used by Convert() other than storing it in the scope.
|
||||||
// Not safe for objects with cyclic references!
|
// Not safe for objects with cyclic references!
|
||||||
func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
|
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)
|
dv, err := EnforcePtr(dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// Leave something on the stack, so that calls to struct tag getters never fail.
|
||||||
s.srcStack.push(scopeStackElem{})
|
s.srcStack.push(scopeStackElem{})
|
||||||
s.destStack.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.
|
// 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.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) {
|
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))
|
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"
|
"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) {
|
func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
||||||
type A struct {
|
type A struct {
|
||||||
Foo string
|
Foo string
|
||||||
@ -86,7 +127,6 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.Convert(&A{}, &C{}, 0, nil)
|
err = c.Convert(&A{}, &C{}, 0, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("unexpected non-error")
|
t.Errorf("unexpected non-error")
|
||||||
|
Loading…
Reference in New Issue
Block a user