protobuf/reflect/enums/
mod.rs

1use std::any::TypeId;
2use std::fmt;
3use std::fmt::Formatter;
4use std::hash::Hash;
5
6use crate::descriptor::EnumDescriptorProto;
7use crate::descriptor::EnumValueDescriptorProto;
8use crate::enums::Enum;
9use crate::reflect::enums::generated::GeneratedEnumDescriptor;
10use crate::reflect::file::index::EnumIndices;
11use crate::reflect::file::FileDescriptorImpl;
12use crate::reflect::FileDescriptor;
13use crate::reflect::MessageDescriptor;
14use crate::EnumFull;
15
16pub(crate) mod generated;
17
18/// Description for enum variant.
19///
20/// Used in reflection.
21#[derive(Clone, Eq, PartialEq, Hash)]
22pub struct EnumValueDescriptor {
23    pub(crate) enum_descriptor: EnumDescriptor,
24    pub(crate) index: usize,
25}
26
27fn _assert_send_sync() {
28    fn _assert_send_sync<T: Send + Sync>() {}
29    _assert_send_sync::<EnumValueDescriptor>();
30}
31
32impl fmt::Debug for EnumValueDescriptor {
33    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34        f.debug_struct("EnumValueDescriptor")
35            .field("enum_descriptor", &self.enum_descriptor)
36            .field("name", &self.name())
37            .finish()
38    }
39}
40
41impl fmt::Display for EnumValueDescriptor {
42    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
43        write!(f, "{}.{}", self.enum_descriptor, self.name())
44    }
45}
46
47impl EnumValueDescriptor {
48    pub(crate) fn new(enum_descriptor: EnumDescriptor, index: usize) -> EnumValueDescriptor {
49        EnumValueDescriptor {
50            enum_descriptor,
51            index,
52        }
53    }
54
55    /// `.proto` object which declared this value.
56    pub fn proto(&self) -> &EnumValueDescriptorProto {
57        &self.enum_descriptor.proto().value[self.index]
58    }
59
60    /// Name of enum variant as specified in proto file
61    pub fn name(&self) -> &str {
62        self.proto().name()
63    }
64
65    /// Fully qualified enum value name: fully qualified enum name followed by value name.
66    pub fn full_name(&self) -> String {
67        self.to_string()
68    }
69
70    /// `i32` value of the enum variant
71    pub fn value(&self) -> i32 {
72        self.proto().number()
73    }
74
75    /// Get descriptor of enum holding this value.
76    pub fn enum_descriptor(&self) -> &EnumDescriptor {
77        &self.enum_descriptor
78    }
79
80    /// Convert this value descriptor into proper enum object.
81    ///
82    /// ```
83    /// # use protobuf::well_known_types::struct_::NullValue;
84    /// # use protobuf::EnumFull;
85    /// # use protobuf::reflect::EnumValueDescriptor;
86    ///
87    /// # if !cfg!(miri) {
88    /// let value: EnumValueDescriptor = NullValue::NULL_VALUE.descriptor();
89    /// let null: Option<NullValue> = value.cast();
90    /// assert_eq!(Some(NullValue::NULL_VALUE), null);
91    /// # }
92    /// ```
93    pub fn cast<E: EnumFull>(&self) -> Option<E> {
94        if self.enum_descriptor != E::enum_descriptor() {
95            return None;
96        }
97        E::from_i32(self.value())
98    }
99}
100
101/// Dynamic representation of enum type.
102///
103/// Can be used in reflective operations.
104#[derive(Clone, Eq, PartialEq, Hash)]
105pub struct EnumDescriptor {
106    file_descriptor: FileDescriptor,
107    index: usize,
108}
109
110impl fmt::Display for EnumDescriptor {
111    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112        write!(f, "{}", self.full_name())
113    }
114}
115
116impl fmt::Debug for EnumDescriptor {
117    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118        f.debug_struct("EnumDescriptor")
119            .field("full_name", &self.full_name())
120            .finish_non_exhaustive()
121    }
122}
123
124impl EnumDescriptor {
125    pub(crate) fn new(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
126        EnumDescriptor {
127            file_descriptor,
128            index,
129        }
130    }
131
132    fn get_impl(&self) -> EnumDescriptorImplRef {
133        match &self.file_descriptor.imp {
134            FileDescriptorImpl::Generated(g) => {
135                EnumDescriptorImplRef::Generated(&g.enums[self.index])
136            }
137            FileDescriptorImpl::Dynamic(..) => EnumDescriptorImplRef::Dynamic,
138        }
139    }
140
141    /// Descriptor objects which defined this enum.
142    pub fn proto(&self) -> &EnumDescriptorProto {
143        &self.index_entry().proto
144    }
145
146    /// Enum name as given in `.proto` file
147    pub fn name(&self) -> &str {
148        self.proto().name()
149    }
150
151    fn index_entry(&self) -> &EnumIndices {
152        self.file_descriptor.enum_indices(self.index)
153    }
154
155    /// Fully qualified protobuf name of enum
156    pub fn full_name(&self) -> &str {
157        &self.index_entry().full_name
158    }
159
160    /// Name relative to the package where the message is declared.
161    pub fn name_to_package(&self) -> &str {
162        &self.index_entry().name_to_package
163    }
164
165    /// Get `EnumDescriptor` object for given enum type
166    pub fn for_type<E: EnumFull>() -> EnumDescriptor {
167        E::enum_descriptor()
168    }
169
170    /// Get a message containing this message, or `None` if this message is declared at file level.
171    pub fn enclosing_message(&self) -> Option<MessageDescriptor> {
172        self.index_entry()
173            .enclosing_message
174            .map(|i| MessageDescriptor::new(self.file_descriptor.clone(), i))
175    }
176
177    /// This enum values
178    pub fn values<'a>(&'a self) -> impl Iterator<Item = EnumValueDescriptor> + 'a {
179        let value_len = self.proto().value.len();
180        (0..value_len).map(move |index| EnumValueDescriptor {
181            enum_descriptor: self.clone(),
182            index,
183        })
184    }
185
186    /// Find enum variant by name
187    pub fn value_by_name(&self, name: &str) -> Option<EnumValueDescriptor> {
188        let index = *self.file_descriptor.common().enums[self.index]
189            .index_by_name
190            .get(name)?;
191        Some(EnumValueDescriptor {
192            enum_descriptor: self.clone(),
193            index,
194        })
195    }
196
197    /// Find enum variant by number
198    pub fn value_by_number(&self, number: i32) -> Option<EnumValueDescriptor> {
199        let index = *self.file_descriptor.common().enums[self.index]
200            .index_by_number
201            .get(&number)?;
202        Some(self.value_by_index(index))
203    }
204
205    /// Get enum variant by index (as declared in `.proto` file).
206    pub fn value_by_index(&self, index: usize) -> EnumValueDescriptor {
207        assert!(index < self.proto().value.len());
208        EnumValueDescriptor {
209            enum_descriptor: self.clone(),
210            index,
211        }
212    }
213
214    /// Default enum value (first variant).
215    pub fn default_value(&self) -> EnumValueDescriptor {
216        EnumValueDescriptor {
217            enum_descriptor: self.clone(),
218            index: 0,
219        }
220    }
221
222    /// Find enum variant by number or return default (first) enum value
223    pub fn value_by_number_or_default(&self, number: i32) -> EnumValueDescriptor {
224        self.value_by_number(number)
225            .unwrap_or_else(|| self.default_value())
226    }
227
228    /// Check if this enum descriptor corresponds given enum type
229    ///
230    /// ```
231    /// # use protobuf::EnumFull;
232    /// # use protobuf::descriptor::field_descriptor_proto::Label;
233    /// # use protobuf::reflect::EnumDescriptor;
234    ///
235    /// # if !cfg!(miri) {
236    /// let descriptor: EnumDescriptor = Label::enum_descriptor();
237    ///
238    /// assert!(descriptor.is::<Label>())
239    /// }
240    /// ```
241    pub fn is<E: Enum>(&self) -> bool {
242        match self.get_impl() {
243            EnumDescriptorImplRef::Generated(g) => g.type_id == TypeId::of::<E>(),
244            EnumDescriptorImplRef::Dynamic => false,
245        }
246    }
247}
248
249enum EnumDescriptorImplRef {
250    Generated(&'static GeneratedEnumDescriptor),
251    Dynamic,
252}
253
254#[cfg(test)]
255mod test {
256    use crate::descriptor::field_descriptor_proto::Label;
257    use crate::descriptor::field_descriptor_proto::Type;
258    use crate::descriptor::FieldDescriptorProto;
259    use crate::well_known_types::struct_::NullValue;
260    use crate::EnumFull;
261    use crate::MessageFull;
262
263    #[test]
264    #[cfg_attr(miri, ignore)] // Too slow on Miri.
265    fn enclosing_message() {
266        assert_eq!(
267            Some(FieldDescriptorProto::descriptor()),
268            Type::enum_descriptor().enclosing_message()
269        );
270        assert_eq!(None, NullValue::enum_descriptor().enclosing_message());
271    }
272
273    #[test]
274    #[cfg_attr(miri, ignore)] // Too slow on Miri.
275    fn to_string() {
276        assert_eq!(
277            "google.protobuf.FieldDescriptorProto.Label",
278            Label::enum_descriptor().to_string()
279        );
280        assert_eq!(
281            "google.protobuf.FieldDescriptorProto.Label",
282            Label::enum_descriptor().full_name()
283        );
284        assert_eq!(
285            "google.protobuf.FieldDescriptorProto.Label.LABEL_REPEATED",
286            Label::LABEL_REPEATED.descriptor().to_string()
287        );
288        assert_eq!(
289            "google.protobuf.FieldDescriptorProto.Label.LABEL_REPEATED",
290            Label::LABEL_REPEATED.descriptor().full_name()
291        );
292    }
293}