blob: 45bfc9b7331b767221db99cefa1a8f8b579d2a77 [file] [log] [blame]
Joe Tsai2fc306a2019-06-20 03:01:22 -07001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package proto
6
7import "google.golang.org/protobuf/reflect/protoreflect"
8
9// Merge merges src into dst, which must be messages with the same descriptor.
10//
11// Populated scalar fields in src are copied to dst, while populated
12// singular messages in src are merged into dst by recursively calling Merge.
13// The elements of every list field in src is appended to the corresponded
14// list fields in dst. The entries of every map field in src is copied into
15// the corresponding map field in dst, possibly replacing existing entries.
16// The unknown fields of src are appended to the unknown fields of dst.
17func Merge(dst, src Message) {
18 mergeMessage(dst.ProtoReflect(), src.ProtoReflect())
19}
20
21func mergeMessage(dst, src protoreflect.Message) {
22 if dst.Descriptor() != src.Descriptor() {
23 panic("descriptor mismatch")
24 }
25
26 src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
27 switch {
28 case fd.IsList():
29 mergeList(dst.Mutable(fd).List(), v.List(), fd)
30 case fd.IsMap():
31 mergeMap(dst.Mutable(fd).Map(), v.Map(), fd.MapValue())
32 case fd.Message() != nil:
33 mergeMessage(dst.Mutable(fd).Message(), v.Message())
34 case fd.Kind() == protoreflect.BytesKind:
35 dst.Set(fd, cloneBytes(v))
36 default:
37 dst.Set(fd, v)
38 }
39 return true
40 })
41
42 dst.SetUnknown(append(dst.GetUnknown(), src.GetUnknown()...))
43}
44
45func mergeList(dst, src protoreflect.List, fd protoreflect.FieldDescriptor) {
46 for i := 0; i < src.Len(); i++ {
47 switch v := src.Get(i); {
48 case fd.Message() != nil:
Damien Neild91c4222019-09-04 10:46:00 -070049 dstv := dst.NewElement()
50 mergeMessage(dstv.Message(), v.Message())
51 dst.Append(dstv)
Joe Tsai2fc306a2019-06-20 03:01:22 -070052 case fd.Kind() == protoreflect.BytesKind:
53 dst.Append(cloneBytes(v))
54 default:
55 dst.Append(v)
56 }
57 }
58}
59
60func mergeMap(dst, src protoreflect.Map, fd protoreflect.FieldDescriptor) {
61 src.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
62 switch {
63 case fd.Message() != nil:
Damien Neild91c4222019-09-04 10:46:00 -070064 dstv := dst.NewValue()
65 mergeMessage(dstv.Message(), v.Message())
66 dst.Set(k, dstv) // may replace existing entry
Joe Tsai2fc306a2019-06-20 03:01:22 -070067 case fd.Kind() == protoreflect.BytesKind:
68 dst.Set(k, cloneBytes(v))
69 default:
70 dst.Set(k, v)
71 }
72 return true
73 })
74}
75
76func cloneBytes(v protoreflect.Value) protoreflect.Value {
Joe Tsai84177c92019-09-17 13:38:48 -070077 return protoreflect.ValueOfBytes(append([]byte{}, v.Bytes()...))
Joe Tsai2fc306a2019-06-20 03:01:22 -070078}