// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "google.golang.org/protobuf/reflect/protoreflect" ) // HasExtension reports whether an extension field is populated. // It returns false if m is invalid or if xt does not extend m. func HasExtension(m Message, xt protoreflect.ExtensionType) bool { // Treat nil message interface or descriptor as an empty message; no populated // fields. if m == nil || xt == nil { return false } // As a special-case, we reports invalid or mismatching descriptors // as always not being populated (since they aren't). mr := m.ProtoReflect() xd := xt.TypeDescriptor() if mr.Descriptor() != xd.ContainingMessage() { return false } return mr.Has(xd) } // ClearExtension clears an extension field such that subsequent // [HasExtension] calls return false. // It panics if m is invalid or if xt does not extend m. func ClearExtension(m Message, xt protoreflect.ExtensionType) { m.ProtoReflect().Clear(xt.TypeDescriptor()) } // GetExtension retrieves the value for an extension field. // If the field is unpopulated, it returns the default value for // scalars and an immutable, empty value for lists or messages. // It panics if xt does not extend m. // // The type of the value is dependent on the field type of the extension. // For extensions generated by protoc-gen-go, the Go type is as follows: // // ╔═══════════════════╤═════════════════════════╗ // ║ Go type │ Protobuf kind ║ // ╠═══════════════════╪═════════════════════════╣ // ║ bool │ bool ║ // ║ int32 │ int32, sint32, sfixed32 ║ // ║ int64 │ int64, sint64, sfixed64 ║ // ║ uint32 │ uint32, fixed32 ║ // ║ uint64 │ uint64, fixed64 ║ // ║ float32 │ float ║ // ║ float64 │ double ║ // ║ string │ string ║ // ║ []byte │ bytes ║ // ║ protoreflect.Enum │ enum ║ // ║ proto.Message │ message, group ║ // ╚═══════════════════╧═════════════════════════╝ // // The protoreflect.Enum and proto.Message types are the concrete Go type // associated with the named enum or message. Repeated fields are represented // using a Go slice of the base element type. // // If a generated extension descriptor variable is directly passed to // GetExtension, then the call should be followed immediately by a // type assertion to the expected output value. For example: // // mm := proto.GetExtension(m, foopb.E_MyExtension).(*foopb.MyMessage) // // This pattern enables static analysis tools to verify that the asserted type // matches the Go type associated with the extension field and // also enables a possible future migration to a type-safe extension API. // // Since singular messages are the most common extension type, the pattern of // calling HasExtension followed by GetExtension may be simplified to: // // if mm := proto.GetExtension(m, foopb.E_MyExtension).(*foopb.MyMessage); mm != nil { // ... // make use of mm // } // // The mm variable is non-nil if and only if HasExtension reports true. func GetExtension(m Message, xt protoreflect.ExtensionType) any { // Treat nil message interface as an empty message; return the default. if m == nil { return xt.InterfaceOf(xt.Zero()) } return xt.InterfaceOf(m.ProtoReflect().Get(xt.TypeDescriptor())) } // SetExtension stores the value of an extension field. // It panics if m is invalid, xt does not extend m, or if type of v // is invalid for the specified extension field. // // The type of the value is dependent on the field type of the extension. // For extensions generated by protoc-gen-go, the Go type is as follows: // // ╔═══════════════════╤═════════════════════════╗ // ║ Go type │ Protobuf kind ║ // ╠═══════════════════╪═════════════════════════╣ // ║ bool │ bool ║ // ║ int32 │ int32, sint32, sfixed32 ║ // ║ int64 │ int64, sint64, sfixed64 ║ // ║ uint32 │ uint32, fixed32 ║ // ║ uint64 │ uint64, fixed64 ║ // ║ float32 │ float ║ // ║ float64 │ double ║ // ║ string │ string ║ // ║ []byte │ bytes ║ // ║ protoreflect.Enum │ enum ║ // ║ proto.Message │ message, group ║ // ╚═══════════════════╧═════════════════════════╝ // // The protoreflect.Enum and proto.Message types are the concrete Go type // associated with the named enum or message. Repeated fields are represented // using a Go slice of the base element type. // // If a generated extension descriptor variable is directly passed to // SetExtension (e.g., foopb.E_MyExtension), then the value should be a // concrete type that matches the expected Go type for the extension descriptor // so that static analysis tools can verify type correctness. // This also enables a possible future migration to a type-safe extension API. func SetExtension(m Message, xt protoreflect.ExtensionType, v any) { xd := xt.TypeDescriptor() pv := xt.ValueOf(v) // Specially treat an invalid list, map, or message as clear. isValid := true switch { case xd.IsList(): isValid = pv.List().IsValid() case xd.IsMap(): isValid = pv.Map().IsValid() case xd.Message() != nil: isValid = pv.Message().IsValid() } if !isValid { m.ProtoReflect().Clear(xd) return } m.ProtoReflect().Set(xd, pv) } // RangeExtensions iterates over every populated extension field in m in an // undefined order, calling f for each extension type and value encountered. // It returns immediately if f returns false. // While iterating, mutating operations may only be performed // on the current extension field. func RangeExtensions(m Message, f func(protoreflect.ExtensionType, any) bool) { // Treat nil message interface as an empty message; nothing to range over. if m == nil { return } m.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { if fd.IsExtension() { xt := fd.(protoreflect.ExtensionTypeDescriptor).Type() vi := xt.InterfaceOf(v) return f(xt, vi) } return true }) }