blob: d248f29284684d60850d5f7a6d9ffd512d8a7eae [file] [log] [blame]
Damien Neil92f76182019-08-02 16:58:08 -07001// Copyright 2019 The Go Authors. All rights reserved.
Damien Neil0232edc2020-02-20 10:30:38 -08002// Use of this source code is governed by a BSD-style
Damien Neil92f76182019-08-02 16:58:08 -07003// license that can be found in the LICENSE file.
4
5package proto
6
7import (
8 "google.golang.org/protobuf/reflect/protoreflect"
9)
10
11// HasExtension reports whether an extension field is populated.
Joe Tsaib57aae92020-04-22 13:49:10 -070012// It returns false if m is invalid or if xt does not extend m.
13func HasExtension(m Message, xt protoreflect.ExtensionType) bool {
Nicolas Hillegeer39bbf132024-04-03 04:03:31 -070014 // Treat nil message interface or descriptor as an empty message; no populated
15 // fields.
16 if m == nil || xt == nil {
Joe Tsai33f8c032020-04-20 17:08:19 -070017 return false
18 }
19
Joe Tsaib57aae92020-04-22 13:49:10 -070020 // As a special-case, we reports invalid or mismatching descriptors
21 // as always not being populated (since they aren't).
Nicolas Hillegeer39bbf132024-04-03 04:03:31 -070022 mr := m.ProtoReflect()
23 xd := xt.TypeDescriptor()
24 if mr.Descriptor() != xd.ContainingMessage() {
Joe Tsaib57aae92020-04-22 13:49:10 -070025 return false
26 }
27
Nicolas Hillegeer39bbf132024-04-03 04:03:31 -070028 return mr.Has(xd)
Damien Neil92f76182019-08-02 16:58:08 -070029}
30
31// ClearExtension clears an extension field such that subsequent
Joe Tsaif9212a82021-04-12 13:48:27 -070032// [HasExtension] calls return false.
Joe Tsaib57aae92020-04-22 13:49:10 -070033// It panics if m is invalid or if xt does not extend m.
34func ClearExtension(m Message, xt protoreflect.ExtensionType) {
35 m.ProtoReflect().Clear(xt.TypeDescriptor())
Damien Neil92f76182019-08-02 16:58:08 -070036}
37
38// GetExtension retrieves the value for an extension field.
Damien Neil92f76182019-08-02 16:58:08 -070039// If the field is unpopulated, it returns the default value for
Joe Tsai33f8c032020-04-20 17:08:19 -070040// scalars and an immutable, empty value for lists or messages.
Joe Tsaib57aae92020-04-22 13:49:10 -070041// It panics if xt does not extend m.
Michael Stapelbergcbc3dd62024-05-15 13:11:52 +020042func GetExtension(m Message, xt protoreflect.ExtensionType) any {
Joe Tsai33f8c032020-04-20 17:08:19 -070043 // Treat nil message interface as an empty message; return the default.
44 if m == nil {
Joe Tsaib57aae92020-04-22 13:49:10 -070045 return xt.InterfaceOf(xt.Zero())
Joe Tsai33f8c032020-04-20 17:08:19 -070046 }
47
Joe Tsaib57aae92020-04-22 13:49:10 -070048 return xt.InterfaceOf(m.ProtoReflect().Get(xt.TypeDescriptor()))
Damien Neil92f76182019-08-02 16:58:08 -070049}
50
51// SetExtension stores the value of an extension field.
Joe Tsaib57aae92020-04-22 13:49:10 -070052// It panics if m is invalid, xt does not extend m, or if type of v
53// is invalid for the specified extension field.
Michael Stapelbergcbc3dd62024-05-15 13:11:52 +020054func SetExtension(m Message, xt protoreflect.ExtensionType, v any) {
Joe Tsaib57aae92020-04-22 13:49:10 -070055 xd := xt.TypeDescriptor()
56 pv := xt.ValueOf(v)
57
58 // Specially treat an invalid list, map, or message as clear.
59 isValid := true
60 switch {
61 case xd.IsList():
62 isValid = pv.List().IsValid()
63 case xd.IsMap():
64 isValid = pv.Map().IsValid()
65 case xd.Message() != nil:
66 isValid = pv.Message().IsValid()
67 }
68 if !isValid {
69 m.ProtoReflect().Clear(xd)
70 return
71 }
72
73 m.ProtoReflect().Set(xd, pv)
Damien Neil92f76182019-08-02 16:58:08 -070074}
Joe Tsai33f8c032020-04-20 17:08:19 -070075
76// RangeExtensions iterates over every populated extension field in m in an
77// undefined order, calling f for each extension type and value encountered.
78// It returns immediately if f returns false.
79// While iterating, mutating operations may only be performed
80// on the current extension field.
Michael Stapelbergcbc3dd62024-05-15 13:11:52 +020081func RangeExtensions(m Message, f func(protoreflect.ExtensionType, any) bool) {
Joe Tsai33f8c032020-04-20 17:08:19 -070082 // Treat nil message interface as an empty message; nothing to range over.
83 if m == nil {
84 return
85 }
86
87 m.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
88 if fd.IsExtension() {
89 xt := fd.(protoreflect.ExtensionTypeDescriptor).Type()
90 vi := xt.InterfaceOf(v)
91 return f(xt, vi)
92 }
93 return true
94 })
95}