github.com/bakjos/protoreflect@v1.9.2/dynamic/merge.go (about)

     1  package dynamic
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  
     7  	"github.com/golang/protobuf/proto"
     8  
     9  	"github.com/bakjos/protoreflect/desc"
    10  )
    11  
    12  // Merge merges the given source message into the given destination message. Use
    13  // use this instead of proto.Merge when one or both of the messages might be a
    14  // a dynamic message. If there is a problem merging the messages, such as the
    15  // two messages having different types, then this method will panic (just as
    16  // proto.Merges does).
    17  func Merge(dst, src proto.Message) {
    18  	if dm, ok := dst.(*Message); ok {
    19  		if err := dm.MergeFrom(src); err != nil {
    20  			panic(err.Error())
    21  		}
    22  	} else if dm, ok := src.(*Message); ok {
    23  		if err := dm.MergeInto(dst); err != nil {
    24  			panic(err.Error())
    25  		}
    26  	} else {
    27  		proto.Merge(dst, src)
    28  	}
    29  }
    30  
    31  // TryMerge merges the given source message into the given destination message.
    32  // You can use this instead of proto.Merge when one or both of the messages
    33  // might be a dynamic message. Unlike proto.Merge, this method will return an
    34  // error on failure instead of panic'ing.
    35  func TryMerge(dst, src proto.Message) error {
    36  	if dm, ok := dst.(*Message); ok {
    37  		if err := dm.MergeFrom(src); err != nil {
    38  			return err
    39  		}
    40  	} else if dm, ok := src.(*Message); ok {
    41  		if err := dm.MergeInto(dst); err != nil {
    42  			return err
    43  		}
    44  	} else {
    45  		// proto.Merge panics on bad input, so we first verify
    46  		// inputs and return error instead of panic
    47  		out := reflect.ValueOf(dst)
    48  		if out.IsNil() {
    49  			return errors.New("proto: nil destination")
    50  		}
    51  		in := reflect.ValueOf(src)
    52  		if in.Type() != out.Type() {
    53  			return errors.New("proto: type mismatch")
    54  		}
    55  		proto.Merge(dst, src)
    56  	}
    57  	return nil
    58  }
    59  
    60  func mergeField(m *Message, fd *desc.FieldDescriptor, val interface{}) error {
    61  	rv := reflect.ValueOf(val)
    62  
    63  	if fd.IsMap() && rv.Kind() == reflect.Map {
    64  		return mergeMapField(m, fd, rv)
    65  	}
    66  
    67  	if fd.IsRepeated() && rv.Kind() == reflect.Slice && rv.Type() != typeOfBytes {
    68  		for i := 0; i < rv.Len(); i++ {
    69  			e := rv.Index(i)
    70  			if e.Kind() == reflect.Interface && !e.IsNil() {
    71  				e = e.Elem()
    72  			}
    73  			if err := m.addRepeatedField(fd, e.Interface()); err != nil {
    74  				return err
    75  			}
    76  		}
    77  		return nil
    78  	}
    79  
    80  	if fd.IsRepeated() {
    81  		return m.addRepeatedField(fd, val)
    82  	} else if fd.GetMessageType() == nil {
    83  		return m.setField(fd, val)
    84  	}
    85  
    86  	// it's a message type, so we want to merge contents
    87  	var err error
    88  	if val, err = validFieldValue(fd, val); err != nil {
    89  		return err
    90  	}
    91  
    92  	existing, _ := m.doGetField(fd, true)
    93  	if existing != nil && !reflect.ValueOf(existing).IsNil() {
    94  		return TryMerge(existing.(proto.Message), val.(proto.Message))
    95  	}
    96  
    97  	// no existing message, so just set field
    98  	m.internalSetField(fd, val)
    99  	return nil
   100  }