Skip to main content

buffa_reflect/
message.rs

1//! [`MessageDescriptor`] — handle to a single proto message in a pool.
2
3use buffa_descriptor::generated::descriptor::DescriptorProto;
4
5use crate::{
6    field::FieldDescriptor,
7    file::FileDescriptor,
8    oneof::OneofDescriptor,
9    pool::{DescriptorPool, MessageIndex},
10};
11
12/// Handle to one message in a [`DescriptorPool`].
13#[derive(Clone, Debug)]
14pub struct MessageDescriptor {
15    pub(crate) pool: DescriptorPool,
16    pub(crate) index: MessageIndex,
17}
18
19impl MessageDescriptor {
20    /// Fully-qualified name (`<package>.<...>.<MessageName>`, no leading
21    /// dot).
22    #[must_use]
23    pub fn full_name(&self) -> &str {
24        &self.pool.inner.messages[self.index as usize].full_name
25    }
26
27    /// Leaf name (the part after the last `.`).
28    #[must_use]
29    pub fn name(&self) -> &str {
30        &self.pool.inner.messages[self.index as usize].name
31    }
32
33    /// Owning file.
34    #[must_use]
35    pub fn parent_file(&self) -> FileDescriptor {
36        let entry = &self.pool.inner.messages[self.index as usize];
37        FileDescriptor {
38            pool: self.pool.clone(),
39            index: entry.file,
40        }
41    }
42
43    /// Containing message, when this message is nested.
44    #[must_use]
45    pub fn parent_message(&self) -> Option<MessageDescriptor> {
46        let entry = &self.pool.inner.messages[self.index as usize];
47        entry.parent.map(|idx| MessageDescriptor {
48            pool: self.pool.clone(),
49            index: idx,
50        })
51    }
52
53    /// All declared fields, in proto declaration order.
54    pub fn fields(&self) -> impl ExactSizeIterator<Item = FieldDescriptor> + '_ {
55        let pool = self.pool.clone();
56        let owner = self.index;
57        let len = self.pool.inner.messages[owner as usize].fields.len() as u32;
58        (0..len).map(move |fi| FieldDescriptor {
59            pool: pool.clone(),
60            message: owner,
61            index: fi,
62        })
63    }
64
65    /// All oneof declarations, in declaration order. Includes synthetic
66    /// oneofs introduced by proto3 `optional`; filter with
67    /// [`OneofDescriptor::is_synthetic`] when only user-authored oneofs
68    /// are wanted.
69    pub fn oneofs(&self) -> impl ExactSizeIterator<Item = OneofDescriptor> + '_ {
70        let pool = self.pool.clone();
71        let owner = self.index;
72        let len = self.pool.inner.messages[owner as usize].oneofs.len() as u32;
73        (0..len).map(move |oi| OneofDescriptor {
74            pool: pool.clone(),
75            message: owner,
76            index: oi,
77        })
78    }
79
80    /// Lookup a field by its proto name.
81    #[must_use]
82    pub fn get_field_by_name(&self, name: &str) -> Option<FieldDescriptor> {
83        let entry = &self.pool.inner.messages[self.index as usize];
84        entry.by_name.get(name).map(|fi| FieldDescriptor {
85            pool: self.pool.clone(),
86            message: self.index,
87            index: *fi,
88        })
89    }
90
91    /// Lookup a field by its JSON name.
92    #[must_use]
93    pub fn get_field_by_json_name(&self, name: &str) -> Option<FieldDescriptor> {
94        let entry = &self.pool.inner.messages[self.index as usize];
95        entry.by_json_name.get(name).map(|fi| FieldDescriptor {
96            pool: self.pool.clone(),
97            message: self.index,
98            index: *fi,
99        })
100    }
101
102    /// Lookup a field by its tag number.
103    #[must_use]
104    pub fn get_field_by_number(&self, number: u32) -> Option<FieldDescriptor> {
105        let entry = &self.pool.inner.messages[self.index as usize];
106        entry.by_number.get(&number).map(|fi| FieldDescriptor {
107            pool: self.pool.clone(),
108            message: self.index,
109            index: *fi,
110        })
111    }
112
113    /// True iff this is a `map<K,V>` entry message synthesized by protoc.
114    #[must_use]
115    pub fn is_map_entry(&self) -> bool {
116        self.pool.inner.messages[self.index as usize].is_map_entry
117    }
118
119    /// Raw [`DescriptorProto`] backing this descriptor (for source-info,
120    /// proto2 default values, options, etc.).
121    #[must_use]
122    pub fn descriptor_proto(&self) -> &DescriptorProto {
123        let entry = &self.pool.inner.messages[self.index as usize];
124        let file = &self.pool.inner.files[entry.file as usize];
125        crate::pool_build::resolve_message_proto(&file.proto, &entry.proto_path)
126    }
127}
128
129impl PartialEq for MessageDescriptor {
130    fn eq(&self, other: &Self) -> bool {
131        std::sync::Arc::ptr_eq(&self.pool.inner, &other.pool.inner) && self.index == other.index
132    }
133}
134
135impl Eq for MessageDescriptor {}