Damien Neil | 92f7618 | 2019-08-02 16:58:08 -0700 | [diff] [blame] | 1 | // Copyright 2019 The Go Authors. All rights reserved. |
Damien Neil | 0232edc | 2020-02-20 10:30:38 -0800 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style |
Damien Neil | 92f7618 | 2019-08-02 16:58:08 -0700 | [diff] [blame] | 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package proto |
| 6 | |
| 7 | import ( |
| 8 | "google.golang.org/protobuf/reflect/protoreflect" |
| 9 | ) |
| 10 | |
| 11 | // HasExtension reports whether an extension field is populated. |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 12 | // It returns false if m is invalid or if xt does not extend m. |
| 13 | func HasExtension(m Message, xt protoreflect.ExtensionType) bool { |
Nicolas Hillegeer | 39bbf13 | 2024-04-03 04:03:31 -0700 | [diff] [blame] | 14 | // Treat nil message interface or descriptor as an empty message; no populated |
| 15 | // fields. |
| 16 | if m == nil || xt == nil { |
Joe Tsai | 33f8c03 | 2020-04-20 17:08:19 -0700 | [diff] [blame] | 17 | return false |
| 18 | } |
| 19 | |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 20 | // As a special-case, we reports invalid or mismatching descriptors |
| 21 | // as always not being populated (since they aren't). |
Nicolas Hillegeer | 39bbf13 | 2024-04-03 04:03:31 -0700 | [diff] [blame] | 22 | mr := m.ProtoReflect() |
| 23 | xd := xt.TypeDescriptor() |
| 24 | if mr.Descriptor() != xd.ContainingMessage() { |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 25 | return false |
| 26 | } |
| 27 | |
Nicolas Hillegeer | 39bbf13 | 2024-04-03 04:03:31 -0700 | [diff] [blame] | 28 | return mr.Has(xd) |
Damien Neil | 92f7618 | 2019-08-02 16:58:08 -0700 | [diff] [blame] | 29 | } |
| 30 | |
| 31 | // ClearExtension clears an extension field such that subsequent |
Joe Tsai | f9212a8 | 2021-04-12 13:48:27 -0700 | [diff] [blame] | 32 | // [HasExtension] calls return false. |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 33 | // It panics if m is invalid or if xt does not extend m. |
| 34 | func ClearExtension(m Message, xt protoreflect.ExtensionType) { |
| 35 | m.ProtoReflect().Clear(xt.TypeDescriptor()) |
Damien Neil | 92f7618 | 2019-08-02 16:58:08 -0700 | [diff] [blame] | 36 | } |
| 37 | |
| 38 | // GetExtension retrieves the value for an extension field. |
Damien Neil | 92f7618 | 2019-08-02 16:58:08 -0700 | [diff] [blame] | 39 | // If the field is unpopulated, it returns the default value for |
Joe Tsai | 33f8c03 | 2020-04-20 17:08:19 -0700 | [diff] [blame] | 40 | // scalars and an immutable, empty value for lists or messages. |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 41 | // It panics if xt does not extend m. |
Michael Stapelberg | cbc3dd6 | 2024-05-15 13:11:52 +0200 | [diff] [blame] | 42 | func GetExtension(m Message, xt protoreflect.ExtensionType) any { |
Joe Tsai | 33f8c03 | 2020-04-20 17:08:19 -0700 | [diff] [blame] | 43 | // Treat nil message interface as an empty message; return the default. |
| 44 | if m == nil { |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 45 | return xt.InterfaceOf(xt.Zero()) |
Joe Tsai | 33f8c03 | 2020-04-20 17:08:19 -0700 | [diff] [blame] | 46 | } |
| 47 | |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 48 | return xt.InterfaceOf(m.ProtoReflect().Get(xt.TypeDescriptor())) |
Damien Neil | 92f7618 | 2019-08-02 16:58:08 -0700 | [diff] [blame] | 49 | } |
| 50 | |
| 51 | // SetExtension stores the value of an extension field. |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 52 | // 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 Stapelberg | cbc3dd6 | 2024-05-15 13:11:52 +0200 | [diff] [blame] | 54 | func SetExtension(m Message, xt protoreflect.ExtensionType, v any) { |
Joe Tsai | b57aae9 | 2020-04-22 13:49:10 -0700 | [diff] [blame] | 55 | 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 Neil | 92f7618 | 2019-08-02 16:58:08 -0700 | [diff] [blame] | 74 | } |
Joe Tsai | 33f8c03 | 2020-04-20 17:08:19 -0700 | [diff] [blame] | 75 | |
| 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 Stapelberg | cbc3dd6 | 2024-05-15 13:11:52 +0200 | [diff] [blame] | 81 | func RangeExtensions(m Message, f func(protoreflect.ExtensionType, any) bool) { |
Joe Tsai | 33f8c03 | 2020-04-20 17:08:19 -0700 | [diff] [blame] | 82 | // 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 | } |