github.com/jhump/protoreflect@v1.16.0/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/jhump/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 }