blob: 9c5599938a70a3bf656747cad58e859a0262e29e [file] [log] [blame]
Abhay Kumara2ae5992025-11-10 14:02:24 +00001// Copyright 2020-2024 Buf Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package protoutil contains useful functions for interacting with descriptors.
16// For now these include only functions for efficiently converting descriptors
17// produced by the compiler to descriptor protos and functions for resolving
18// "features" (a core concept of Protobuf Editions).
19//
20// Despite the fact that descriptor protos are mutable, calling code should NOT
21// mutate any of the protos returned from this package. For efficiency, some
22// values returned from this package may reference internal state of a compiler
23// result, and mutating the proto could corrupt or invalidate parts of that
24// result.
25package protoutil
26
27import (
28 "google.golang.org/protobuf/proto"
29 "google.golang.org/protobuf/reflect/protodesc"
30 "google.golang.org/protobuf/reflect/protoreflect"
31 "google.golang.org/protobuf/types/descriptorpb"
32)
33
34// DescriptorProtoWrapper is a protoreflect.Descriptor that wraps an
35// underlying descriptor proto. It provides the same interface as
36// Descriptor but with one extra operation, to efficiently query for
37// the underlying descriptor proto.
38//
39// Descriptors that implement this will also implement another method
40// whose specified return type is the concrete type returned by the
41// AsProto method. The name of this method varies by the type of this
42// descriptor:
43//
44// Descriptor Type Other Method Name
45// ---------------------+------------------------------------
46// FileDescriptor | FileDescriptorProto()
47// MessageDescriptor | MessageDescriptorProto()
48// FieldDescriptor | FieldDescriptorProto()
49// OneofDescriptor | OneofDescriptorProto()
50// EnumDescriptor | EnumDescriptorProto()
51// EnumValueDescriptor | EnumValueDescriptorProto()
52// ServiceDescriptor | ServiceDescriptorProto()
53// MethodDescriptor | MethodDescriptorProto()
54//
55// For example, a DescriptorProtoWrapper that implements FileDescriptor
56// returns a *descriptorpb.FileDescriptorProto value from its AsProto
57// method and also provides a method with the following signature:
58//
59// FileDescriptorProto() *descriptorpb.FileDescriptorProto
60type DescriptorProtoWrapper interface {
61 protoreflect.Descriptor
62 // AsProto returns the underlying descriptor proto. The concrete
63 // type of the proto message depends on the type of this
64 // descriptor:
65 // Descriptor Type Proto Message Type
66 // ---------------------+------------------------------------
67 // FileDescriptor | *descriptorpb.FileDescriptorProto
68 // MessageDescriptor | *descriptorpb.DescriptorProto
69 // FieldDescriptor | *descriptorpb.FieldDescriptorProto
70 // OneofDescriptor | *descriptorpb.OneofDescriptorProto
71 // EnumDescriptor | *descriptorpb.EnumDescriptorProto
72 // EnumValueDescriptor | *descriptorpb.EnumValueDescriptorProto
73 // ServiceDescriptor | *descriptorpb.ServiceDescriptorProto
74 // MethodDescriptor | *descriptorpb.MethodDescriptorProto
75 AsProto() proto.Message
76}
77
78// ProtoFromDescriptor extracts a descriptor proto from the given "rich"
79// descriptor. For descriptors generated by the compiler, this is an
80// inexpensive and non-lossy operation. Descriptors from other sources
81// however may be expensive (to re-create a proto) and even lossy.
82func ProtoFromDescriptor(d protoreflect.Descriptor) proto.Message {
83 switch d := d.(type) {
84 case protoreflect.FileDescriptor:
85 return ProtoFromFileDescriptor(d)
86 case protoreflect.MessageDescriptor:
87 return ProtoFromMessageDescriptor(d)
88 case protoreflect.FieldDescriptor:
89 return ProtoFromFieldDescriptor(d)
90 case protoreflect.OneofDescriptor:
91 return ProtoFromOneofDescriptor(d)
92 case protoreflect.EnumDescriptor:
93 return ProtoFromEnumDescriptor(d)
94 case protoreflect.EnumValueDescriptor:
95 return ProtoFromEnumValueDescriptor(d)
96 case protoreflect.ServiceDescriptor:
97 return ProtoFromServiceDescriptor(d)
98 case protoreflect.MethodDescriptor:
99 return ProtoFromMethodDescriptor(d)
100 default:
101 // WTF??
102 if res, ok := d.(DescriptorProtoWrapper); ok {
103 return res.AsProto()
104 }
105 return nil
106 }
107}
108
109// ProtoFromFileDescriptor extracts a descriptor proto from the given "rich"
110// descriptor. For file descriptors generated by the compiler, this is an
111// inexpensive and non-lossy operation. File descriptors from other sources
112// however may be expensive (to re-create a proto) and even lossy.
113func ProtoFromFileDescriptor(d protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto {
114 if imp, ok := d.(protoreflect.FileImport); ok {
115 d = imp.FileDescriptor
116 }
117 type canProto interface {
118 FileDescriptorProto() *descriptorpb.FileDescriptorProto
119 }
120 if res, ok := d.(canProto); ok {
121 return res.FileDescriptorProto()
122 }
123 if res, ok := d.(DescriptorProtoWrapper); ok {
124 if fd, ok := res.AsProto().(*descriptorpb.FileDescriptorProto); ok {
125 return fd
126 }
127 }
128 return protodesc.ToFileDescriptorProto(d)
129}
130
131// ProtoFromMessageDescriptor extracts a descriptor proto from the given "rich"
132// descriptor. For message descriptors generated by the compiler, this is an
133// inexpensive and non-lossy operation. Message descriptors from other sources
134// however may be expensive (to re-create a proto) and even lossy.
135func ProtoFromMessageDescriptor(d protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto {
136 type canProto interface {
137 MessageDescriptorProto() *descriptorpb.DescriptorProto
138 }
139 if res, ok := d.(canProto); ok {
140 return res.MessageDescriptorProto()
141 }
142 if res, ok := d.(DescriptorProtoWrapper); ok {
143 if md, ok := res.AsProto().(*descriptorpb.DescriptorProto); ok {
144 return md
145 }
146 }
147 return protodesc.ToDescriptorProto(d)
148}
149
150// ProtoFromFieldDescriptor extracts a descriptor proto from the given "rich"
151// descriptor. For field descriptors generated by the compiler, this is an
152// inexpensive and non-lossy operation. Field descriptors from other sources
153// however may be expensive (to re-create a proto) and even lossy.
154func ProtoFromFieldDescriptor(d protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto {
155 type canProto interface {
156 FieldDescriptorProto() *descriptorpb.FieldDescriptorProto
157 }
158 if res, ok := d.(canProto); ok {
159 return res.FieldDescriptorProto()
160 }
161 if res, ok := d.(DescriptorProtoWrapper); ok {
162 if fd, ok := res.AsProto().(*descriptorpb.FieldDescriptorProto); ok {
163 return fd
164 }
165 }
166 return protodesc.ToFieldDescriptorProto(d)
167}
168
169// ProtoFromOneofDescriptor extracts a descriptor proto from the given "rich"
170// descriptor. For oneof descriptors generated by the compiler, this is an
171// inexpensive and non-lossy operation. Oneof descriptors from other sources
172// however may be expensive (to re-create a proto) and even lossy.
173func ProtoFromOneofDescriptor(d protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto {
174 type canProto interface {
175 OneofDescriptorProto() *descriptorpb.OneofDescriptorProto
176 }
177 if res, ok := d.(canProto); ok {
178 return res.OneofDescriptorProto()
179 }
180 if res, ok := d.(DescriptorProtoWrapper); ok {
181 if ood, ok := res.AsProto().(*descriptorpb.OneofDescriptorProto); ok {
182 return ood
183 }
184 }
185 return protodesc.ToOneofDescriptorProto(d)
186}
187
188// ProtoFromEnumDescriptor extracts a descriptor proto from the given "rich"
189// descriptor. For enum descriptors generated by the compiler, this is an
190// inexpensive and non-lossy operation. Enum descriptors from other sources
191// however may be expensive (to re-create a proto) and even lossy.
192func ProtoFromEnumDescriptor(d protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto {
193 type canProto interface {
194 EnumDescriptorProto() *descriptorpb.EnumDescriptorProto
195 }
196 if res, ok := d.(canProto); ok {
197 return res.EnumDescriptorProto()
198 }
199 if res, ok := d.(DescriptorProtoWrapper); ok {
200 if ed, ok := res.AsProto().(*descriptorpb.EnumDescriptorProto); ok {
201 return ed
202 }
203 }
204 return protodesc.ToEnumDescriptorProto(d)
205}
206
207// ProtoFromEnumValueDescriptor extracts a descriptor proto from the given "rich"
208// descriptor. For enum value descriptors generated by the compiler, this is an
209// inexpensive and non-lossy operation. Enum value descriptors from other sources
210// however may be expensive (to re-create a proto) and even lossy.
211func ProtoFromEnumValueDescriptor(d protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto {
212 type canProto interface {
213 EnumValueDescriptorProto() *descriptorpb.EnumValueDescriptorProto
214 }
215 if res, ok := d.(canProto); ok {
216 return res.EnumValueDescriptorProto()
217 }
218 if res, ok := d.(DescriptorProtoWrapper); ok {
219 if ed, ok := res.AsProto().(*descriptorpb.EnumValueDescriptorProto); ok {
220 return ed
221 }
222 }
223 return protodesc.ToEnumValueDescriptorProto(d)
224}
225
226// ProtoFromServiceDescriptor extracts a descriptor proto from the given "rich"
227// descriptor. For service descriptors generated by the compiler, this is an
228// inexpensive and non-lossy operation. Service descriptors from other sources
229// however may be expensive (to re-create a proto) and even lossy.
230func ProtoFromServiceDescriptor(d protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto {
231 type canProto interface {
232 ServiceDescriptorProto() *descriptorpb.ServiceDescriptorProto
233 }
234 if res, ok := d.(canProto); ok {
235 return res.ServiceDescriptorProto()
236 }
237 if res, ok := d.(DescriptorProtoWrapper); ok {
238 if sd, ok := res.AsProto().(*descriptorpb.ServiceDescriptorProto); ok {
239 return sd
240 }
241 }
242 return protodesc.ToServiceDescriptorProto(d)
243}
244
245// ProtoFromMethodDescriptor extracts a descriptor proto from the given "rich"
246// descriptor. For method descriptors generated by the compiler, this is an
247// inexpensive and non-lossy operation. Method descriptors from other sources
248// however may be expensive (to re-create a proto) and even lossy.
249func ProtoFromMethodDescriptor(d protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProto {
250 type canProto interface {
251 MethodDescriptorProto() *descriptorpb.MethodDescriptorProto
252 }
253 if res, ok := d.(canProto); ok {
254 return res.MethodDescriptorProto()
255 }
256 if res, ok := d.(DescriptorProtoWrapper); ok {
257 if md, ok := res.AsProto().(*descriptorpb.MethodDescriptorProto); ok {
258 return md
259 }
260 }
261 return protodesc.ToMethodDescriptorProto(d)
262}