package dynamic import ( "errors" "reflect" "github.com/golang/protobuf/proto" "github.com/jhump/protoreflect/desc" ) // Merge merges the given source message into the given destination message. Use // use this instead of proto.Merge when one or both of the messages might be a // a dynamic message. If there is a problem merging the messages, such as the // two messages having different types, then this method will panic (just as // proto.Merges does). func Merge(dst, src proto.Message) { if dm, ok := dst.(*Message); ok { if err := dm.MergeFrom(src); err != nil { panic(err.Error()) } } else if dm, ok := src.(*Message); ok { if err := dm.MergeInto(dst); err != nil { panic(err.Error()) } } else { proto.Merge(dst, src) } } // TryMerge merges the given source message into the given destination message. // You can use this instead of proto.Merge when one or both of the messages // might be a dynamic message. Unlike proto.Merge, this method will return an // error on failure instead of panic'ing. func TryMerge(dst, src proto.Message) error { if dm, ok := dst.(*Message); ok { if err := dm.MergeFrom(src); err != nil { return err } } else if dm, ok := src.(*Message); ok { if err := dm.MergeInto(dst); err != nil { return err } } else { // proto.Merge panics on bad input, so we first verify // inputs and return error instead of panic out := reflect.ValueOf(dst) if out.IsNil() { return errors.New("proto: nil destination") } in := reflect.ValueOf(src) if in.Type() != out.Type() { return errors.New("proto: type mismatch") } proto.Merge(dst, src) } return nil } func mergeField(m *Message, fd *desc.FieldDescriptor, val interface{}) error { rv := reflect.ValueOf(val) if fd.IsMap() && rv.Kind() == reflect.Map { for _, k := range rv.MapKeys() { if k.Kind() == reflect.Interface && !k.IsNil() { k = k.Elem() } v := rv.MapIndex(k) if v.Kind() == reflect.Interface && !v.IsNil() { v = v.Elem() } if err := m.putMapField(fd, k.Interface(), v.Interface()); err != nil { return err } } return nil } if fd.IsRepeated() && rv.Kind() == reflect.Slice && rv.Type() != typeOfBytes { for i := 0; i < rv.Len(); i++ { e := rv.Index(i) if e.Kind() == reflect.Interface && !e.IsNil() { e = e.Elem() } if err := m.addRepeatedField(fd, e.Interface()); err != nil { return err } } return nil } if fd.IsRepeated() { return m.addRepeatedField(fd, val) } else if fd.GetMessageType() == nil { return m.setField(fd, val) } // it's a message type, so we want to merge contents var err error if val, err = validFieldValue(fd, val); err != nil { return err } existing, _ := m.doGetField(fd, true) if existing != nil && !reflect.ValueOf(existing).IsNil() { return TryMerge(existing.(proto.Message), val.(proto.Message)) } // no existing message, so just set field m.internalSetField(fd, val) return nil }