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