buffa_descriptor/desc.rs
1//! Linked descriptor types for runtime reflection.
2//!
3//! These are the processed, feature-resolved form of the raw
4//! [`FileDescriptorProto`](crate::generated::descriptor::FileDescriptorProto)
5//! tree. Where the raw protos use string `type_name` references and
6//! unresolved `FeatureSet` options, these types use pool indices
7//! ([`MessageIndex`], [`EnumIndex`]) and pre-resolved edition features
8//! ([`FieldPresence`](buffa::editions::FieldPresence), `packed`, `delimited`).
9//!
10//! [`FieldKind`] flattens protobuf's orthogonal type × label × map-entry axes
11//! into a single discriminant that maps 1:1 to runtime representation — the
12//! same approach protobuf-es takes with its `fieldKind` union.
13//!
14//! These types are constructed only by [`DescriptorPool`](crate::DescriptorPool)
15//! from a `FileDescriptorSet` and are immutable thereafter. Fields are
16//! private — read them through accessor methods (`name()`, `kind()`,
17//! `full_name()`, etc.) so the pool's internal representation can evolve
18//! without breaking consumers. Mutation through `&mut` is unsupported —
19//! the pool hands out shared references only.
20//!
21//! Downstream crates can't fabricate a `MessageDescriptor` directly. Test
22//! fixtures should compile a `.proto` to a `FileDescriptorSet` and load it
23//! through `DescriptorPool::decode` — anything subtler skips the
24//! feature-resolution and validation passes and would diverge from
25//! production behavior.
26//!
27//! # Limits
28//!
29//! Field indices within a message are stored as `u16`, capping the number of
30//! fields per message at 65 535. `DescriptorPool` enforces this at
31//! construction time. Field *numbers* remain `u32` per the protobuf spec.
32
33use alloc::boxed::Box;
34use alloc::string::String;
35use alloc::vec::Vec;
36
37use crate::generated::descriptor::field_descriptor_proto::Type as ProtoType;
38use crate::generated::descriptor::{
39 EnumOptions, EnumValueOptions, FieldOptions, MessageOptions, MethodOptions, OneofOptions,
40 ServiceOptions,
41};
42use buffa::editions::{EnumType, FieldPresence};
43
44/// Index of a [`MessageDescriptor`] within its owning pool.
45///
46/// The `Ord` impl is an arbitrary but stable total order over one pool's
47/// indices (so they can key ordered collections); it is **not** a documented
48/// relationship to declaration or registration order. Comparing indices from
49/// different pools is meaningless (the same cross-pool hazard as
50/// `PartialEq`).
51#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
52pub struct MessageIndex(pub(crate) u32);
53
54/// Index of an [`EnumDescriptor`] within its owning pool.
55#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub struct EnumIndex(pub(crate) u32);
57
58/// Protobuf scalar field types.
59///
60/// This is [`field_descriptor_proto::Type`](ProtoType) minus
61/// `TYPE_MESSAGE`, `TYPE_GROUP`, and `TYPE_ENUM` — those get dedicated
62/// [`SingularKind`] variants instead of being lumped in with scalars.
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
64pub enum ScalarType {
65 Double,
66 Float,
67 Int64,
68 Uint64,
69 Int32,
70 Fixed64,
71 Fixed32,
72 Bool,
73 String,
74 Bytes,
75 Uint32,
76 Sfixed32,
77 Sfixed64,
78 Sint32,
79 Sint64,
80}
81
82impl ScalarType {
83 /// Convert a raw proto `Type` to a `ScalarType`.
84 ///
85 /// Returns `None` for `TYPE_MESSAGE`, `TYPE_GROUP`, and `TYPE_ENUM`,
86 /// which are not scalar.
87 pub fn from_proto(ty: ProtoType) -> Option<Self> {
88 Some(match ty {
89 ProtoType::TYPE_DOUBLE => Self::Double,
90 ProtoType::TYPE_FLOAT => Self::Float,
91 ProtoType::TYPE_INT64 => Self::Int64,
92 ProtoType::TYPE_UINT64 => Self::Uint64,
93 ProtoType::TYPE_INT32 => Self::Int32,
94 ProtoType::TYPE_FIXED64 => Self::Fixed64,
95 ProtoType::TYPE_FIXED32 => Self::Fixed32,
96 ProtoType::TYPE_BOOL => Self::Bool,
97 ProtoType::TYPE_STRING => Self::String,
98 ProtoType::TYPE_BYTES => Self::Bytes,
99 ProtoType::TYPE_UINT32 => Self::Uint32,
100 ProtoType::TYPE_SFIXED32 => Self::Sfixed32,
101 ProtoType::TYPE_SFIXED64 => Self::Sfixed64,
102 ProtoType::TYPE_SINT32 => Self::Sint32,
103 ProtoType::TYPE_SINT64 => Self::Sint64,
104 ProtoType::TYPE_MESSAGE | ProtoType::TYPE_GROUP | ProtoType::TYPE_ENUM => return None,
105 })
106 }
107
108 /// Whether this scalar is valid as a protobuf map key.
109 ///
110 /// Per the protobuf spec: integral types, bool, and string. Not floats,
111 /// not bytes.
112 pub fn is_valid_map_key(self) -> bool {
113 !matches!(self, Self::Double | Self::Float | Self::Bytes)
114 }
115}
116
117/// The element kind of a singular field, list element, or map value.
118///
119/// Separating this from [`FieldKind`] makes `List(List(...))` and
120/// `Map { value: Map {..} }` unrepresentable — protobuf does not allow
121/// nested repeated or map-of-map. It also keeps [`FieldKind`] `Copy`.
122#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123pub enum SingularKind {
124 /// A scalar value.
125 Scalar(ScalarType),
126 /// An enum value, referencing an enum in the pool.
127 Enum(EnumIndex),
128 /// A message value, referencing a message in the pool.
129 Message(MessageIndex),
130}
131
132/// The kind of a protobuf field, flattening type × cardinality × map-entry.
133///
134/// This discriminant maps 1:1 to the field's runtime representation.
135#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
136pub enum FieldKind {
137 /// A singular (non-repeated, non-map) field.
138 Singular(SingularKind),
139 /// A `repeated` field.
140 List(SingularKind),
141 /// A `map<K, V>` field.
142 Map {
143 /// Key type. Always integral, bool, or string per the protobuf spec.
144 key: ScalarType,
145 /// Value kind.
146 value: SingularKind,
147 },
148}
149
150/// A linked, feature-resolved field descriptor.
151///
152/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
153/// constructible by downstream crates. The fields are accessed through
154/// methods so the pool can change its internal representation without
155/// breaking consumers.
156#[derive(Clone, Debug)]
157pub struct FieldDescriptor {
158 pub(crate) name: String,
159 pub(crate) json_name: String,
160 pub(crate) number: u32,
161 pub(crate) kind: FieldKind,
162 pub(crate) presence: FieldPresence,
163 pub(crate) packed: bool,
164 pub(crate) delimited: bool,
165 pub(crate) oneof_index: Option<u16>,
166 pub(crate) enum_type: Option<EnumType>,
167 /// Raw `FieldOptions`, boxed (`None` when the field declares none).
168 pub(crate) options: Option<Box<FieldOptions>>,
169}
170
171impl FieldDescriptor {
172 /// Proto field name (as written in the `.proto` file).
173 #[inline]
174 #[must_use]
175 pub fn name(&self) -> &str {
176 &self.name
177 }
178
179 /// JSON name — lowerCamelCase unless overridden by `[json_name = ...]`.
180 #[inline]
181 #[must_use]
182 pub fn json_name(&self) -> &str {
183 &self.json_name
184 }
185
186 /// Field number, in `[1, 2^29 - 1]`.
187 #[inline]
188 #[must_use]
189 pub fn number(&self) -> u32 {
190 self.number
191 }
192
193 /// Resolved kind (scalar/enum/message/list/map).
194 #[inline]
195 #[must_use]
196 pub fn kind(&self) -> FieldKind {
197 self.kind
198 }
199
200 /// Resolved presence discipline. For `List`/`Map` kinds this is always
201 /// [`Implicit`](FieldPresence::Implicit) (repeated fields have no presence).
202 #[inline]
203 #[must_use]
204 pub fn presence(&self) -> FieldPresence {
205 self.presence
206 }
207
208 /// Whether a `List` of packable scalars uses packed wire encoding.
209 /// Meaningless for non-list or non-packable kinds.
210 #[inline]
211 #[must_use]
212 pub fn is_packed(&self) -> bool {
213 self.packed
214 }
215
216 /// Whether a `Message` kind uses delimited (group-style) wire encoding.
217 /// Meaningless for non-message kinds.
218 #[inline]
219 #[must_use]
220 pub fn is_delimited(&self) -> bool {
221 self.delimited
222 }
223
224 /// Index into the parent message's [`oneofs`](MessageDescriptor::oneofs),
225 /// if this field belongs to a oneof (including proto3 synthetic oneofs
226 /// for `optional`).
227 #[inline]
228 #[must_use]
229 pub fn oneof_index(&self) -> Option<u16> {
230 self.oneof_index
231 }
232
233 /// Effective openness for an enum-valued field, list, or map value:
234 /// whether unknown numeric values are preserved ([`Open`](EnumType::Open))
235 /// or treated as unknown fields ([`Closed`](EnumType::Closed)).
236 ///
237 /// Unlike [`EnumDescriptor::enum_type`], which reports the enum's own
238 /// declared openness, this reflects buffa's field-scoped overrides
239 /// (`open_enums_in`), so it is the value the decoders act on. Returns
240 /// `None` for fields whose value is not an enum.
241 #[inline]
242 #[must_use]
243 pub fn enum_type(&self) -> Option<EnumType> {
244 self.enum_type
245 }
246
247 /// The raw `FieldOptions` for this field, if any were declared.
248 ///
249 /// Standard options (`deprecated`, etc.) read directly off the returned
250 /// struct. **Custom options** — `[(my.pkg.opt) = ...]` — are extensions
251 /// of `google.protobuf.FieldOptions`; they survive on the returned
252 /// struct's unknown fields. To read one generically, register the
253 /// option's defining proto (and `descriptor.proto`) in the same pool,
254 /// then reflect over the options via
255 /// [`DynamicMessage::from_options`](crate::reflect::DynamicMessage::from_options):
256 ///
257 /// ```no_run
258 /// # #[cfg(feature = "reflect")] {
259 /// # use std::sync::Arc;
260 /// # use buffa_descriptor::{DescriptorPool, reflect::{DynamicMessage, ReflectMessage}};
261 /// # fn demo(pool: Arc<DescriptorPool>, field: &buffa_descriptor::FieldDescriptor) -> Option<()> {
262 /// let dyn_opts = DynamicMessage::from_options(Arc::clone(&pool), field.options()?)?;
263 /// let ext = pool.extension_by_name("my.pkg.opt")?;
264 /// let value = dyn_opts.get(ext.field());
265 /// # let _ = value; Some(())
266 /// # }
267 /// # }
268 /// ```
269 #[inline]
270 #[must_use]
271 pub fn options(&self) -> Option<&FieldOptions> {
272 self.options.as_deref()
273 }
274}
275
276/// A linked message descriptor.
277///
278/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
279/// constructible by downstream crates.
280#[derive(Clone, Debug)]
281pub struct MessageDescriptor {
282 pub(crate) full_name: String,
283 pub(crate) fields: Vec<FieldDescriptor>,
284 /// `(field_number, index_into_fields)`, sorted by field number for
285 /// binary-search lookup. Internal index; not API.
286 pub(crate) field_by_number: Vec<(u32, u16)>,
287 /// `(name, index_into_fields)`, sorted by name. Holds both the proto
288 /// name and (when distinct) the JSON name. Internal index; not API.
289 pub(crate) field_by_name: Vec<(String, u16)>,
290 pub(crate) oneofs: Vec<OneofDescriptor>,
291 pub(crate) extension_ranges: Vec<(u32, u32)>,
292 /// Raw `MessageOptions`, boxed (`None` when the message declares none).
293 pub(crate) options: Option<Box<MessageOptions>>,
294}
295
296impl MessageDescriptor {
297 /// Fully-qualified proto name without leading dot, e.g.
298 /// `google.protobuf.Timestamp`.
299 #[inline]
300 #[must_use]
301 pub fn full_name(&self) -> &str {
302 &self.full_name
303 }
304
305 /// The raw `MessageOptions` for this message, if any were declared.
306 ///
307 /// See [`FieldDescriptor::options`] for how to read custom options.
308 #[inline]
309 #[must_use]
310 pub fn options(&self) -> Option<&MessageOptions> {
311 self.options.as_deref()
312 }
313
314 /// Fields in source (declaration) order.
315 #[inline]
316 #[must_use]
317 pub fn fields(&self) -> &[FieldDescriptor] {
318 &self.fields
319 }
320
321 /// Oneof declarations, including proto3 synthetic oneofs.
322 #[inline]
323 #[must_use]
324 pub fn oneofs(&self) -> &[OneofDescriptor] {
325 &self.oneofs
326 }
327
328 /// Extension ranges `[start, end)`.
329 #[inline]
330 #[must_use]
331 pub fn extension_ranges(&self) -> &[(u32, u32)] {
332 &self.extension_ranges
333 }
334
335 /// Look up a field by its proto field number. `O(log n)`.
336 #[must_use]
337 pub fn field(&self, number: u32) -> Option<&FieldDescriptor> {
338 let i = self
339 .field_by_number
340 .binary_search_by_key(&number, |&(n, _)| n)
341 .ok()?;
342 let (_, idx) = self.field_by_number[i];
343 debug_assert!(
344 (idx as usize) < self.fields.len(),
345 "field_by_number index {idx} out of bounds for {} fields",
346 self.fields.len()
347 );
348 self.fields.get(idx as usize)
349 }
350
351 /// Look up a field by its proto field name or JSON name. `O(log n)`.
352 ///
353 /// CEL evaluators and JSON parsers both look fields up by name in a hot
354 /// loop; this is the supported path. Both the proto field name and the
355 /// camelCase JSON name resolve.
356 #[must_use]
357 pub fn field_by_name(&self, name: &str) -> Option<&FieldDescriptor> {
358 let i = self
359 .field_by_name
360 .binary_search_by(|(n, _)| n.as_str().cmp(name))
361 .ok()?;
362 let (_, idx) = self.field_by_name[i];
363 self.fields.get(idx as usize)
364 }
365
366 /// Whether `number` falls within any declared extension range.
367 #[must_use]
368 pub fn in_extension_range(&self, number: u32) -> bool {
369 self.extension_ranges
370 .iter()
371 .any(|&(start, end)| start <= number && number < end)
372 }
373}
374
375/// A oneof declaration within a message.
376///
377/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
378/// constructible by downstream crates.
379#[derive(Clone, Debug)]
380pub struct OneofDescriptor {
381 pub(crate) name: String,
382 pub(crate) field_indices: Vec<u16>,
383 pub(crate) synthetic: bool,
384 /// Raw `OneofOptions`, boxed (`None` when the oneof declares none).
385 pub(crate) options: Option<Box<OneofOptions>>,
386}
387
388impl OneofDescriptor {
389 /// Proto oneof name.
390 #[inline]
391 #[must_use]
392 pub fn name(&self) -> &str {
393 &self.name
394 }
395
396 /// The raw `OneofOptions` for this oneof, if any were declared.
397 ///
398 /// See [`FieldDescriptor::options`] for how to read custom options.
399 #[inline]
400 #[must_use]
401 pub fn options(&self) -> Option<&OneofOptions> {
402 self.options.as_deref()
403 }
404
405 /// Indices into the parent message's [`fields`](MessageDescriptor::fields)
406 /// for members of this oneof.
407 #[inline]
408 #[must_use]
409 pub fn field_indices(&self) -> &[u16] {
410 &self.field_indices
411 }
412
413 /// Whether this is a synthetic oneof generated for a proto3 `optional`
414 /// field (exactly one member, not user-declared).
415 #[inline]
416 #[must_use]
417 pub fn is_synthetic(&self) -> bool {
418 self.synthetic
419 }
420}
421
422/// A linked enum descriptor.
423///
424/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
425/// constructible by downstream crates.
426#[derive(Clone, Debug)]
427pub struct EnumDescriptor {
428 pub(crate) full_name: String,
429 pub(crate) values: Vec<EnumValueDescriptor>,
430 pub(crate) enum_type: EnumType,
431 /// Raw `EnumOptions`, boxed (`None` when the enum declares none).
432 pub(crate) options: Option<Box<EnumOptions>>,
433}
434
435impl EnumDescriptor {
436 /// Fully-qualified proto name without leading dot.
437 #[inline]
438 #[must_use]
439 pub fn full_name(&self) -> &str {
440 &self.full_name
441 }
442
443 /// The raw `EnumOptions` for this enum, if any were declared.
444 ///
445 /// See [`FieldDescriptor::options`] for how to read custom options.
446 #[inline]
447 #[must_use]
448 pub fn options(&self) -> Option<&EnumOptions> {
449 self.options.as_deref()
450 }
451
452 /// Declared values in source order.
453 #[inline]
454 #[must_use]
455 pub fn values(&self) -> &[EnumValueDescriptor] {
456 &self.values
457 }
458
459 /// Whether unknown numeric values are preserved
460 /// ([`Open`](EnumType::Open)) or treated as unknown fields
461 /// ([`Closed`](EnumType::Closed)). Resolved from edition features.
462 #[inline]
463 #[must_use]
464 pub fn enum_type(&self) -> EnumType {
465 self.enum_type
466 }
467
468 /// Look up a value by its numeric value.
469 ///
470 /// If the enum has aliases (`allow_alias = true`), returns the first
471 /// declared value with that number.
472 #[must_use]
473 pub fn value(&self, number: i32) -> Option<&EnumValueDescriptor> {
474 self.values.iter().find(|v| v.number == number)
475 }
476
477 /// Look up a value by its proto name.
478 #[must_use]
479 pub fn value_by_name(&self, name: &str) -> Option<&EnumValueDescriptor> {
480 self.values.iter().find(|v| v.name == name)
481 }
482}
483
484/// A single value within an enum.
485///
486/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
487/// constructible by downstream crates.
488#[derive(Clone, Debug)]
489pub struct EnumValueDescriptor {
490 pub(crate) name: String,
491 pub(crate) number: i32,
492 /// Raw `EnumValueOptions`, boxed (`None` when the value declares none).
493 pub(crate) options: Option<Box<EnumValueOptions>>,
494}
495
496impl EnumValueDescriptor {
497 /// Proto value name, e.g. `FOO_BAR`.
498 #[inline]
499 #[must_use]
500 pub fn name(&self) -> &str {
501 &self.name
502 }
503
504 /// Numeric value.
505 #[inline]
506 #[must_use]
507 pub fn number(&self) -> i32 {
508 self.number
509 }
510
511 /// The raw `EnumValueOptions` for this value, if any were declared.
512 ///
513 /// See [`FieldDescriptor::options`] for how to read custom options.
514 #[inline]
515 #[must_use]
516 pub fn options(&self) -> Option<&EnumValueOptions> {
517 self.options.as_deref()
518 }
519}
520
521/// Pool-local index of a registered service.
522///
523/// Same contract as [`MessageIndex`] / [`EnumIndex`]: stable for the
524/// lifetime of the pool, no cross-pool identity.
525#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
526pub struct ServiceIndex(pub(crate) u32);
527
528/// A linked service descriptor.
529///
530/// Carries the service's RPC methods. Used by gRPC server reflection,
531/// transcoding gateways that route by method, and interceptors that need to
532/// know an RPC's input/output types — the connect-rust use cases.
533///
534/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
535/// constructible by downstream crates.
536#[derive(Clone, Debug)]
537pub struct ServiceDescriptor {
538 pub(crate) full_name: String,
539 pub(crate) methods: Vec<MethodDescriptor>,
540 /// Raw `ServiceOptions`, boxed (`None` when the service declares none).
541 pub(crate) options: Option<Box<ServiceOptions>>,
542}
543
544impl ServiceDescriptor {
545 /// Fully-qualified proto name without leading dot, e.g.
546 /// `connectrpc.eliza.v1.ElizaService`.
547 #[inline]
548 #[must_use]
549 pub fn full_name(&self) -> &str {
550 &self.full_name
551 }
552
553 /// The raw `ServiceOptions` for this service, if any were declared.
554 ///
555 /// See [`FieldDescriptor::options`] for how to read custom options.
556 #[inline]
557 #[must_use]
558 pub fn options(&self) -> Option<&ServiceOptions> {
559 self.options.as_deref()
560 }
561
562 /// Methods in declaration order.
563 #[inline]
564 #[must_use]
565 pub fn methods(&self) -> &[MethodDescriptor] {
566 &self.methods
567 }
568
569 /// Look up a method by its proto name. `O(n)` over the methods slice —
570 /// services rarely have more than a dozen methods.
571 #[must_use]
572 pub fn method(&self, name: &str) -> Option<&MethodDescriptor> {
573 self.methods.iter().find(|m| m.name == name)
574 }
575}
576
577/// A linked RPC method descriptor.
578///
579/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
580/// constructible by downstream crates.
581#[derive(Clone, Debug)]
582pub struct MethodDescriptor {
583 pub(crate) name: String,
584 pub(crate) input: MessageIndex,
585 pub(crate) output: MessageIndex,
586 pub(crate) client_streaming: bool,
587 pub(crate) server_streaming: bool,
588 /// Raw `MethodOptions`, boxed (`None` when the method declares none).
589 pub(crate) options: Option<Box<MethodOptions>>,
590}
591
592impl MethodDescriptor {
593 /// Proto method name, e.g. `Say`.
594 #[inline]
595 #[must_use]
596 pub fn name(&self) -> &str {
597 &self.name
598 }
599
600 /// Pool index of the request message type.
601 #[inline]
602 #[must_use]
603 pub fn input(&self) -> MessageIndex {
604 self.input
605 }
606
607 /// Pool index of the response message type.
608 #[inline]
609 #[must_use]
610 pub fn output(&self) -> MessageIndex {
611 self.output
612 }
613
614 /// Whether the client streams multiple request messages.
615 #[inline]
616 #[must_use]
617 pub fn is_client_streaming(&self) -> bool {
618 self.client_streaming
619 }
620
621 /// Whether the server streams multiple response messages.
622 #[inline]
623 #[must_use]
624 pub fn is_server_streaming(&self) -> bool {
625 self.server_streaming
626 }
627
628 /// The raw `MethodOptions` for this method, if any were declared.
629 ///
630 /// `(google.api.http)` and other transcoding annotations live here as
631 /// custom options. See [`FieldDescriptor::options`] for how to read them.
632 #[inline]
633 #[must_use]
634 pub fn options(&self) -> Option<&MethodOptions> {
635 self.options.as_deref()
636 }
637}
638
639/// Pool-local index of a registered extension.
640///
641/// Same contract as [`MessageIndex`] / [`EnumIndex`]: stable for the
642/// lifetime of the pool, no cross-pool identity.
643#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
644pub struct ExtensionIndex(pub(crate) u32);
645
646/// A linked extension descriptor.
647///
648/// An extension is a field declared *outside* the message it belongs to —
649/// `extend Foo { optional int32 bar = 100; }` adds field 100 to `Foo` from
650/// anywhere that can see `Foo`. Structurally it is a [`FieldDescriptor`]
651/// plus the identity of the message it extends and the fully-qualified name
652/// it is registered under.
653///
654/// The contained [`field()`](Self::field) descriptor is what the
655/// [`ReflectMessage`](crate::reflect::ReflectMessage) accessors take —
656/// `msg.get(ext.field())` reads an extension exactly like a declared field.
657/// This mirrors protobuf-go, where `ExtensionDescriptor` *is* a
658/// `FieldDescriptor` and the reflective accessors don't distinguish.
659///
660/// Constructed only by [`DescriptorPool`](crate::DescriptorPool); not
661/// constructible by downstream crates.
662#[derive(Clone, Debug)]
663pub struct ExtensionDescriptor {
664 /// The field this extension adds to the extendee. `name` is the
665 /// extension's simple name; `json_name` is derived but unused (the JSON
666 /// key for an extension is the bracketed [`full_name`](Self::full_name)).
667 pub(crate) field: FieldDescriptor,
668 /// Fully-qualified registration name, e.g. `pkg.ext_name` for a
669 /// file-level extension or `pkg.Scope.ext_name` for one declared inside
670 /// a message. This is what appears in JSON `"[...]"` keys.
671 pub(crate) full_name: String,
672 /// The bracketed JSON object key, `"[<full_name>]"`. Precomputed at link
673 /// time so the JSON serializer doesn't allocate it per message.
674 pub(crate) json_key: String,
675 /// The message this extension extends.
676 pub(crate) extendee: MessageIndex,
677}
678
679impl ExtensionDescriptor {
680 /// The field this extension adds to the extendee.
681 ///
682 /// Pass this to [`ReflectMessage`](crate::reflect::ReflectMessage)
683 /// accessors: `msg.get(ext.field())`, `msg.has(ext.field())`,
684 /// `msg.set(ext.field(), value)`.
685 ///
686 /// The returned descriptor's
687 /// [`json_name()`](FieldDescriptor::json_name) is **not** the JSON key
688 /// for this extension — extensions serialize as the bracketed
689 /// [`full_name()`](Self::full_name) (`"[pkg.ext_name]"`), not as a
690 /// camelCase field name. A reflection-driven serializer must special-case
691 /// extension fields.
692 #[inline]
693 #[must_use]
694 pub fn field(&self) -> &FieldDescriptor {
695 &self.field
696 }
697
698 /// Fully-qualified registration name (the JSON `"[...]"` key without
699 /// the brackets).
700 #[inline]
701 #[must_use]
702 pub fn full_name(&self) -> &str {
703 &self.full_name
704 }
705
706 /// The JSON object key for this extension: the bracketed
707 /// [`full_name()`](Self::full_name), e.g. `"[pkg.ext_name]"`.
708 #[inline]
709 #[must_use]
710 pub fn json_key(&self) -> &str {
711 &self.json_key
712 }
713
714 /// The message this extension extends.
715 #[inline]
716 #[must_use]
717 pub fn extendee(&self) -> MessageIndex {
718 self.extendee
719 }
720}
721
722impl AsRef<FieldDescriptor> for ExtensionDescriptor {
723 /// Equivalent to [`field()`](Self::field), for generic code that accepts
724 /// "anything that is a field descriptor".
725 fn as_ref(&self) -> &FieldDescriptor {
726 &self.field
727 }
728}
729
730#[cfg(test)]
731mod tests {
732 use super::*;
733
734 #[test]
735 fn scalar_type_from_proto_scalars() {
736 assert_eq!(
737 ScalarType::from_proto(ProtoType::TYPE_INT32),
738 Some(ScalarType::Int32)
739 );
740 assert_eq!(
741 ScalarType::from_proto(ProtoType::TYPE_STRING),
742 Some(ScalarType::String)
743 );
744 assert_eq!(
745 ScalarType::from_proto(ProtoType::TYPE_SINT64),
746 Some(ScalarType::Sint64)
747 );
748 }
749
750 #[test]
751 fn scalar_type_from_proto_rejects_composites() {
752 assert_eq!(ScalarType::from_proto(ProtoType::TYPE_MESSAGE), None);
753 assert_eq!(ScalarType::from_proto(ProtoType::TYPE_GROUP), None);
754 assert_eq!(ScalarType::from_proto(ProtoType::TYPE_ENUM), None);
755 }
756
757 #[test]
758 fn scalar_type_map_key_validity() {
759 assert!(ScalarType::Int32.is_valid_map_key());
760 assert!(ScalarType::String.is_valid_map_key());
761 assert!(ScalarType::Bool.is_valid_map_key());
762 assert!(ScalarType::Sfixed64.is_valid_map_key());
763 assert!(!ScalarType::Double.is_valid_map_key());
764 assert!(!ScalarType::Float.is_valid_map_key());
765 assert!(!ScalarType::Bytes.is_valid_map_key());
766 }
767
768 fn scalar_field(name: &str, number: u32, ty: ScalarType) -> FieldDescriptor {
769 FieldDescriptor {
770 name: name.into(),
771 json_name: name.into(),
772 number,
773 kind: FieldKind::Singular(SingularKind::Scalar(ty)),
774 presence: FieldPresence::Implicit,
775 packed: false,
776 delimited: false,
777 oneof_index: None,
778 enum_type: None,
779 options: None,
780 }
781 }
782
783 fn sample_message() -> MessageDescriptor {
784 MessageDescriptor {
785 full_name: "test.Foo".into(),
786 fields: alloc::vec![
787 scalar_field("a", 1, ScalarType::Int32),
788 scalar_field("b", 5, ScalarType::String),
789 ],
790 field_by_number: alloc::vec![(1, 0), (5, 1)],
791 field_by_name: alloc::vec![("a".into(), 0), ("b".into(), 1)],
792 oneofs: Vec::new(),
793 extension_ranges: alloc::vec![(100, 200), (1000, 2000)],
794 options: None,
795 }
796 }
797
798 #[test]
799 fn message_field_lookup_by_number() {
800 let m = sample_message();
801 assert_eq!(m.field(1).unwrap().name, "a");
802 assert_eq!(m.field(5).unwrap().name, "b");
803 assert!(m.field(2).is_none());
804 assert!(m.field(99).is_none());
805 }
806
807 #[test]
808 fn message_field_lookup_by_name() {
809 let m = sample_message();
810 assert_eq!(m.field_by_name("a").unwrap().number, 1);
811 assert_eq!(m.field_by_name("b").unwrap().number, 5);
812 assert!(m.field_by_name("c").is_none());
813 assert!(m.field_by_name("").is_none());
814 }
815
816 #[test]
817 fn empty_message_field_lookup() {
818 let m = MessageDescriptor {
819 full_name: "test.Empty".into(),
820 fields: Vec::new(),
821 field_by_number: Vec::new(),
822 field_by_name: Vec::new(),
823 oneofs: Vec::new(),
824 extension_ranges: Vec::new(),
825 options: None,
826 };
827 assert!(m.field(1).is_none());
828 assert!(m.field_by_name("anything").is_none());
829 assert!(!m.in_extension_range(1));
830 }
831
832 #[test]
833 fn message_extension_range_check() {
834 let m = sample_message();
835 assert!(m.in_extension_range(100));
836 assert!(m.in_extension_range(150));
837 assert!(m.in_extension_range(199));
838 assert!(!m.in_extension_range(200)); // end is exclusive
839 assert!(m.in_extension_range(1500));
840 assert!(!m.in_extension_range(50));
841 assert!(!m.in_extension_range(500));
842 }
843
844 #[test]
845 fn enum_value_lookup() {
846 let e = EnumDescriptor {
847 full_name: "test.Color".into(),
848 values: alloc::vec![
849 EnumValueDescriptor {
850 name: "RED".into(),
851 number: 0,
852 options: None,
853 },
854 EnumValueDescriptor {
855 name: "GREEN".into(),
856 number: 1,
857 options: None,
858 },
859 EnumValueDescriptor {
860 name: "ALIAS_RED".into(),
861 number: 0,
862 options: None,
863 },
864 ],
865 enum_type: EnumType::Open,
866 options: None,
867 };
868 assert_eq!(e.value(1).unwrap().name, "GREEN");
869 assert_eq!(e.value(0).unwrap().name, "RED"); // first wins on alias
870 assert!(e.value(99).is_none());
871 assert_eq!(e.value_by_name("GREEN").unwrap().number, 1);
872 assert!(e.value_by_name("BLUE").is_none());
873 }
874
875 #[test]
876 fn field_kind_is_copy() {
877 let list = FieldKind::List(SingularKind::Message(MessageIndex(3)));
878 let copied = list;
879 assert_eq!(list, copied);
880
881 let map = FieldKind::Map {
882 key: ScalarType::String,
883 value: SingularKind::Enum(EnumIndex(1)),
884 };
885 match map {
886 FieldKind::Map { key, value } => {
887 assert_eq!(key, ScalarType::String);
888 assert_eq!(value, SingularKind::Enum(EnumIndex(1)));
889 }
890 _ => panic!(),
891 }
892 }
893
894 #[test]
895 fn scalar_type_from_proto_exhaustive() {
896 use ProtoType::*;
897 let all = [
898 (TYPE_DOUBLE, ScalarType::Double),
899 (TYPE_FLOAT, ScalarType::Float),
900 (TYPE_INT64, ScalarType::Int64),
901 (TYPE_UINT64, ScalarType::Uint64),
902 (TYPE_INT32, ScalarType::Int32),
903 (TYPE_FIXED64, ScalarType::Fixed64),
904 (TYPE_FIXED32, ScalarType::Fixed32),
905 (TYPE_BOOL, ScalarType::Bool),
906 (TYPE_STRING, ScalarType::String),
907 (TYPE_BYTES, ScalarType::Bytes),
908 (TYPE_UINT32, ScalarType::Uint32),
909 (TYPE_SFIXED32, ScalarType::Sfixed32),
910 (TYPE_SFIXED64, ScalarType::Sfixed64),
911 (TYPE_SINT32, ScalarType::Sint32),
912 (TYPE_SINT64, ScalarType::Sint64),
913 ];
914 for (proto, scalar) in all {
915 assert_eq!(ScalarType::from_proto(proto), Some(scalar));
916 }
917 }
918}