Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Proposal: built in support for protobuf wrapper types #207

@zaquestion

Description

@zaquestion

Hiya, firstly, thank you for this project, of the bunch out there offering similar functionality this package is the only one I found that really does the job right and offered enough configuration to handle edge cases or quirks of my particular needs.

One such need involved merging protobuf's wrapper types
Ref: https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/wrappers.proto

These types are really handy for go projects as the allow differentiating between "not set" and zero values in api parameters.

Representation in Go for BoolValue

type BoolValue struct {

	// The [bool](https://pkg.go.dev/builtin#bool) value.
	Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
	// contains filtered or unexported fields
}

I was running into an issue with mergo.Merge when merging a BoolValue field. Both the source and dest fields had the field set (non nil). However the underlying Value in the destination was false whereas the source was true. Mergo understandably thought the right action was to overwrite Value with true being a non-zero value (as it went to merge at the field level of the struct), however the presence of a non-nil wrapper type in the destination means it was explicitly set and shouldn't be overwritten.

mergo Transformers to the rescue! Thankfully y'all have a great mechanism for overriding this behavior and just as I thought I was screwed (proto.Merge is garbage btw), I was able to write a pretty simply transformer that resolved the issue. This ticket is mostly to say thanks for the great project and to see if adding something like the below transformer to the project would be useful.

type wrapperspbTransformer struct{}

func (t wrapperspbTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
	switch typ {
	case reflect.TypeOf(&wrapperspb.BoolValue{}),
		reflect.TypeOf(&wrapperspb.StringValue{}),
		reflect.TypeOf(&wrapperspb.BytesValue{}),
		reflect.TypeOf(&wrapperspb.DoubleValue{}),
		reflect.TypeOf(&wrapperspb.FloatValue{}),
		reflect.TypeOf(&wrapperspb.Int64Value{}),
		reflect.TypeOf(&wrapperspb.UInt64Value{}),
		reflect.TypeOf(&wrapperspb.Int32Value{}),
		reflect.TypeOf(&wrapperspb.UInt32Value{}):
		return func(dst, src reflect.Value) error {
			// only overwrite a wrapper type if the destination
			// nil. This prevents mergo from recursing into the
			// wrapper type itself and attempting to merge against
			// the primative `value` field, which means that zero
			// values would get overwritten despite being
			// explicitly set through the destination wrapper.
			//
			// Basically this transformer forces wrapperpb types to merge correctly
			if dst.CanSet() && dst.IsNil() {
				dst.Set(src)
			}
			return nil
		}
	default:
		return nil
	}
}

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions