Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If any messages do not have a subfilter then don't introspect them. #40

Merged
merged 4 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ func structToStruct(filter FieldFilter, src, dst *reflect.Value, userOptions *op
return errors.Errorf("dst type is %s, expected: %s ", dst.Type(), "*any.Any")
}

// If subfilter is empty then copy the entire any without any unmarshalling.
if filter.IsEmpty() && !userOptions.UnmarshalAllAny {
dst.Set(*src)
break
}

srcProto, err := srcAny.UnmarshalNew()
if err != nil {
return errors.WithStack(err)
Expand Down Expand Up @@ -250,6 +256,12 @@ type options struct {
// It is called before copying the data from source to destination allowing custom processing.
// If the visitor function returns true the visited field is skipped.
MapVisitor mapVisitor

// UnmarshalAllAny is used to indicate unmarshal all any fields. Default to true to keep backward compatibility.
//
// If an any field is encountered and this flag is not set, it will only Unmarshal it if there is a subfilter for that field.
// If set it will always Unmarshal all any fields
UnmarshalAllAny bool
}

// mapVisitor is called for every filtered field in structToMap.
Expand Down Expand Up @@ -293,9 +305,18 @@ func WithMapVisitor(visitor mapVisitor) Option {
}
}

func WithUnmarshalAllAny(unmarshal bool) Option {
return func(o *options) {
o.UnmarshalAllAny = unmarshal
}
}

func newDefaultOptions() *options {
// set default CopyListSize is func which return src.Len()
return &options{CopyListSize: func(src *reflect.Value) int { return src.Len() }}
return &options{
CopyListSize: func(src *reflect.Value) int { return src.Len() },
UnmarshalAllAny: true,
}
}

// fieldName gets the field name according to the field's tag, or gets StructField.Name default when the field's tag is empty.
Expand Down Expand Up @@ -334,7 +355,7 @@ func structToMap(filter FieldFilter, src, dst reflect.Value, userOptions *option
}
srcType := src.Type()
for i := 0; i < src.NumField(); i++ {
srcName := fieldName(userOptions.SrcTag, srcType.Field(i))
srcName := fieldName(userOptions.SrcTag, srcType.Field(i))
if !isExported(srcType.Field(i)) {
// Unexported fields can not be copied.
continue
Expand Down
37 changes: 37 additions & 0 deletions copy_proto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,43 @@ func TestStructToStruct_NonProtoFail(t *testing.T) {
assert.NotNil(t, err)
}

func TestStructToStruct_UnknownAny(t *testing.T) {
getUsers := func() (*testproto.User, *testproto.User) {
user1 := &testproto.User{
Details: []*anypb.Any{
{
TypeUrl: "example.com/example/UnknownType",
Value: []byte("unknown"),
},
},
}
user2 := &testproto.User{}
return user1, user2
}

userWithUnknown, emptyUser := getUsers()

mask := fieldmask_utils.MaskFromString("Details")

err := fieldmask_utils.StructToStruct(mask, userWithUnknown, emptyUser, fieldmask_utils.WithUnmarshalAllAny(false))
assert.NoError(t, err)
assert.Equal(t, userWithUnknown.Details, emptyUser.Details)

userWithUnknown, emptyUser = getUsers()

err = fieldmask_utils.StructToStruct(mask, emptyUser, userWithUnknown, fieldmask_utils.WithUnmarshalAllAny(false))
assert.NoError(t, err)
assert.Equal(t, userWithUnknown.Details, emptyUser.Details)

userWithUnknown, emptyUser = getUsers()

err = fieldmask_utils.StructToStruct(mask, userWithUnknown, emptyUser)
assert.Equal(t, "proto:\u00a0not found", err.Error())

mask = fieldmask_utils.MaskFromString("Details{Id}")
err = fieldmask_utils.StructToStruct(mask, userWithUnknown, emptyUser, fieldmask_utils.WithUnmarshalAllAny(false))
assert.Equal(t, "proto:\u00a0not found", err.Error())
artificial-aidan marked this conversation as resolved.
Show resolved Hide resolved
artificial-aidan marked this conversation as resolved.
Show resolved Hide resolved
}
func TestStructToMap_Success(t *testing.T) {
userDst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString(
Expand Down