extension.go

  1// 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 (
  8	"google.golang.org/protobuf/reflect/protoreflect"
  9)
 10
 11// HasExtension reports whether an extension field is populated.
 12// It returns false if m is invalid or if xt does not extend m.
 13func HasExtension(m Message, xt protoreflect.ExtensionType) bool {
 14	// Treat nil message interface or descriptor as an empty message; no populated
 15	// fields.
 16	if m == nil || xt == nil {
 17		return false
 18	}
 19
 20	// As a special-case, we reports invalid or mismatching descriptors
 21	// as always not being populated (since they aren't).
 22	mr := m.ProtoReflect()
 23	xd := xt.TypeDescriptor()
 24	if mr.Descriptor() != xd.ContainingMessage() {
 25		return false
 26	}
 27
 28	return mr.Has(xd)
 29}
 30
 31// ClearExtension clears an extension field such that subsequent
 32// [HasExtension] calls return false.
 33// 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())
 36}
 37
 38// GetExtension retrieves the value for an extension field.
 39// If the field is unpopulated, it returns the default value for
 40// scalars and an immutable, empty value for lists or messages.
 41// It panics if xt does not extend m.
 42//
 43// The type of the value is dependent on the field type of the extension.
 44// For extensions generated by protoc-gen-go, the Go type is as follows:
 45//
 46//	โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•คโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
 47//	โ•‘ Go type           โ”‚ Protobuf kind           โ•‘
 48//	โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
 49//	โ•‘ bool              โ”‚ bool                    โ•‘
 50//	โ•‘ int32             โ”‚ int32, sint32, sfixed32 โ•‘
 51//	โ•‘ int64             โ”‚ int64, sint64, sfixed64 โ•‘
 52//	โ•‘ uint32            โ”‚ uint32, fixed32         โ•‘
 53//	โ•‘ uint64            โ”‚ uint64, fixed64         โ•‘
 54//	โ•‘ float32           โ”‚ float                   โ•‘
 55//	โ•‘ float64           โ”‚ double                  โ•‘
 56//	โ•‘ string            โ”‚ string                  โ•‘
 57//	โ•‘ []byte            โ”‚ bytes                   โ•‘
 58//	โ•‘ protoreflect.Enum โ”‚ enum                    โ•‘
 59//	โ•‘ proto.Message     โ”‚ message, group          โ•‘
 60//	โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•งโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 61//
 62// The protoreflect.Enum and proto.Message types are the concrete Go type
 63// associated with the named enum or message. Repeated fields are represented
 64// using a Go slice of the base element type.
 65//
 66// If a generated extension descriptor variable is directly passed to
 67// GetExtension, then the call should be followed immediately by a
 68// type assertion to the expected output value. For example:
 69//
 70//	mm := proto.GetExtension(m, foopb.E_MyExtension).(*foopb.MyMessage)
 71//
 72// This pattern enables static analysis tools to verify that the asserted type
 73// matches the Go type associated with the extension field and
 74// also enables a possible future migration to a type-safe extension API.
 75//
 76// Since singular messages are the most common extension type, the pattern of
 77// calling HasExtension followed by GetExtension may be simplified to:
 78//
 79//	if mm := proto.GetExtension(m, foopb.E_MyExtension).(*foopb.MyMessage); mm != nil {
 80//	    ... // make use of mm
 81//	}
 82//
 83// The mm variable is non-nil if and only if HasExtension reports true.
 84func GetExtension(m Message, xt protoreflect.ExtensionType) any {
 85	// Treat nil message interface as an empty message; return the default.
 86	if m == nil {
 87		return xt.InterfaceOf(xt.Zero())
 88	}
 89
 90	return xt.InterfaceOf(m.ProtoReflect().Get(xt.TypeDescriptor()))
 91}
 92
 93// SetExtension stores the value of an extension field.
 94// It panics if m is invalid, xt does not extend m, or if type of v
 95// is invalid for the specified extension field.
 96//
 97// The type of the value is dependent on the field type of the extension.
 98// For extensions generated by protoc-gen-go, the Go type is as follows:
 99//
100//	โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•คโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
101//	โ•‘ Go type           โ”‚ Protobuf kind           โ•‘
102//	โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
103//	โ•‘ bool              โ”‚ bool                    โ•‘
104//	โ•‘ int32             โ”‚ int32, sint32, sfixed32 โ•‘
105//	โ•‘ int64             โ”‚ int64, sint64, sfixed64 โ•‘
106//	โ•‘ uint32            โ”‚ uint32, fixed32         โ•‘
107//	โ•‘ uint64            โ”‚ uint64, fixed64         โ•‘
108//	โ•‘ float32           โ”‚ float                   โ•‘
109//	โ•‘ float64           โ”‚ double                  โ•‘
110//	โ•‘ string            โ”‚ string                  โ•‘
111//	โ•‘ []byte            โ”‚ bytes                   โ•‘
112//	โ•‘ protoreflect.Enum โ”‚ enum                    โ•‘
113//	โ•‘ proto.Message     โ”‚ message, group          โ•‘
114//	โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•งโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
115//
116// The protoreflect.Enum and proto.Message types are the concrete Go type
117// associated with the named enum or message. Repeated fields are represented
118// using a Go slice of the base element type.
119//
120// If a generated extension descriptor variable is directly passed to
121// SetExtension (e.g., foopb.E_MyExtension), then the value should be a
122// concrete type that matches the expected Go type for the extension descriptor
123// so that static analysis tools can verify type correctness.
124// This also enables a possible future migration to a type-safe extension API.
125func SetExtension(m Message, xt protoreflect.ExtensionType, v any) {
126	xd := xt.TypeDescriptor()
127	pv := xt.ValueOf(v)
128
129	// Specially treat an invalid list, map, or message as clear.
130	isValid := true
131	switch {
132	case xd.IsList():
133		isValid = pv.List().IsValid()
134	case xd.IsMap():
135		isValid = pv.Map().IsValid()
136	case xd.Message() != nil:
137		isValid = pv.Message().IsValid()
138	}
139	if !isValid {
140		m.ProtoReflect().Clear(xd)
141		return
142	}
143
144	m.ProtoReflect().Set(xd, pv)
145}
146
147// RangeExtensions iterates over every populated extension field in m in an
148// undefined order, calling f for each extension type and value encountered.
149// It returns immediately if f returns false.
150// While iterating, mutating operations may only be performed
151// on the current extension field.
152func RangeExtensions(m Message, f func(protoreflect.ExtensionType, any) bool) {
153	// Treat nil message interface as an empty message; nothing to range over.
154	if m == nil {
155		return
156	}
157
158	m.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
159		if fd.IsExtension() {
160			xt := fd.(protoreflect.ExtensionTypeDescriptor).Type()
161			vi := xt.InterfaceOf(v)
162			return f(xt, vi)
163		}
164		return true
165	})
166}