Skip to main content

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}