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}