blob: 4081c4a962710a3b2b9788f7e72476518230503a [file] [log] [blame]
use std::any::TypeId;
use std::fmt;
use std::fmt::Formatter;
use std::hash::Hash;
use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::enums::Enum;
use crate::reflect::enums::generated::GeneratedEnumDescriptor;
use crate::reflect::file::index::EnumIndices;
use crate::reflect::file::FileDescriptorImpl;
use crate::reflect::FileDescriptor;
use crate::reflect::MessageDescriptor;
use crate::EnumFull;
pub(crate) mod generated;
/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumValueDescriptor {
pub(crate) enum_descriptor: EnumDescriptor,
pub(crate) index: usize,
}
fn _assert_send_sync() {
fn _assert_send_sync<T: Send + Sync>() {}
_assert_send_sync::<EnumValueDescriptor>();
}
impl fmt::Debug for EnumValueDescriptor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EnumValueDescriptor")
.field("enum_descriptor", &self.enum_descriptor)
.field("name", &self.name())
.finish()
}
}
impl fmt::Display for EnumValueDescriptor {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.enum_descriptor, self.name())
}
}
impl EnumValueDescriptor {
pub(crate) fn new(enum_descriptor: EnumDescriptor, index: usize) -> EnumValueDescriptor {
EnumValueDescriptor {
enum_descriptor,
index,
}
}
/// `.proto` object which declared this value.
pub fn proto(&self) -> &EnumValueDescriptorProto {
&self.enum_descriptor.proto().value[self.index]
}
/// Name of enum variant as specified in proto file
pub fn name(&self) -> &str {
self.proto().name()
}
/// Fully qualified enum value name: fully qualified enum name followed by value name.
pub fn full_name(&self) -> String {
self.to_string()
}
/// `i32` value of the enum variant
pub fn value(&self) -> i32 {
self.proto().number()
}
/// Get descriptor of enum holding this value.
pub fn enum_descriptor(&self) -> &EnumDescriptor {
&self.enum_descriptor
}
/// Convert this value descriptor into proper enum object.
///
/// ```
/// # use protobuf::well_known_types::struct_::NullValue;
/// # use protobuf::EnumFull;
/// # use protobuf::reflect::EnumValueDescriptor;
///
/// # if !cfg!(miri) {
/// let value: EnumValueDescriptor = NullValue::NULL_VALUE.descriptor();
/// let null: Option<NullValue> = value.cast();
/// assert_eq!(Some(NullValue::NULL_VALUE), null);
/// # }
/// ```
pub fn cast<E: EnumFull>(&self) -> Option<E> {
if self.enum_descriptor != E::enum_descriptor() {
return None;
}
E::from_i32(self.value())
}
}
/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumDescriptor {
file_descriptor: FileDescriptor,
index: usize,
}
impl fmt::Display for EnumDescriptor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.full_name())
}
}
impl fmt::Debug for EnumDescriptor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EnumDescriptor")
.field("full_name", &self.full_name())
.finish_non_exhaustive()
}
}
impl EnumDescriptor {
pub(crate) fn new(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
EnumDescriptor {
file_descriptor,
index,
}
}
fn get_impl(&self) -> EnumDescriptorImplRef {
match &self.file_descriptor.imp {
FileDescriptorImpl::Generated(g) => {
EnumDescriptorImplRef::Generated(&g.enums[self.index])
}
FileDescriptorImpl::Dynamic(..) => EnumDescriptorImplRef::Dynamic,
}
}
/// Descriptor objects which defined this enum.
pub fn proto(&self) -> &EnumDescriptorProto {
&self.index_entry().proto
}
/// Enum name as given in `.proto` file
pub fn name(&self) -> &str {
self.proto().name()
}
fn index_entry(&self) -> &EnumIndices {
self.file_descriptor.enum_indices(self.index)
}
/// Fully qualified protobuf name of enum
pub fn full_name(&self) -> &str {
&self.index_entry().full_name
}
/// Name relative to the package where the message is declared.
pub fn name_to_package(&self) -> &str {
&self.index_entry().name_to_package
}
/// Get `EnumDescriptor` object for given enum type
pub fn for_type<E: EnumFull>() -> EnumDescriptor {
E::enum_descriptor()
}
/// Get a message containing this message, or `None` if this message is declared at file level.
pub fn enclosing_message(&self) -> Option<MessageDescriptor> {
self.index_entry()
.enclosing_message
.map(|i| MessageDescriptor::new(self.file_descriptor.clone(), i))
}
/// This enum values
pub fn values<'a>(&'a self) -> impl Iterator<Item = EnumValueDescriptor> + 'a {
let value_len = self.proto().value.len();
(0..value_len).map(move |index| EnumValueDescriptor {
enum_descriptor: self.clone(),
index,
})
}
/// Find enum variant by name
pub fn value_by_name(&self, name: &str) -> Option<EnumValueDescriptor> {
let index = *self.file_descriptor.common().enums[self.index]
.index_by_name
.get(name)?;
Some(EnumValueDescriptor {
enum_descriptor: self.clone(),
index,
})
}
/// Find enum variant by number
pub fn value_by_number(&self, number: i32) -> Option<EnumValueDescriptor> {
let index = *self.file_descriptor.common().enums[self.index]
.index_by_number
.get(&number)?;
Some(self.value_by_index(index))
}
/// Get enum variant by index (as declared in `.proto` file).
pub fn value_by_index(&self, index: usize) -> EnumValueDescriptor {
assert!(index < self.proto().value.len());
EnumValueDescriptor {
enum_descriptor: self.clone(),
index,
}
}
/// Default enum value (first variant).
pub fn default_value(&self) -> EnumValueDescriptor {
EnumValueDescriptor {
enum_descriptor: self.clone(),
index: 0,
}
}
/// Find enum variant by number or return default (first) enum value
pub fn value_by_number_or_default(&self, number: i32) -> EnumValueDescriptor {
self.value_by_number(number)
.unwrap_or_else(|| self.default_value())
}
/// Check if this enum descriptor corresponds given enum type
///
/// ```
/// # use protobuf::EnumFull;
/// # use protobuf::descriptor::field_descriptor_proto::Label;
/// # use protobuf::reflect::EnumDescriptor;
///
/// # if !cfg!(miri) {
/// let descriptor: EnumDescriptor = Label::enum_descriptor();
///
/// assert!(descriptor.is::<Label>())
/// }
/// ```
pub fn is<E: Enum>(&self) -> bool {
match self.get_impl() {
EnumDescriptorImplRef::Generated(g) => g.type_id == TypeId::of::<E>(),
EnumDescriptorImplRef::Dynamic => false,
}
}
}
enum EnumDescriptorImplRef {
Generated(&'static GeneratedEnumDescriptor),
Dynamic,
}
#[cfg(test)]
mod test {
use crate::descriptor::field_descriptor_proto::Label;
use crate::descriptor::field_descriptor_proto::Type;
use crate::descriptor::FieldDescriptorProto;
use crate::well_known_types::struct_::NullValue;
use crate::EnumFull;
use crate::MessageFull;
#[test]
#[cfg_attr(miri, ignore)] // Too slow on Miri.
fn enclosing_message() {
assert_eq!(
Some(FieldDescriptorProto::descriptor()),
Type::enum_descriptor().enclosing_message()
);
assert_eq!(None, NullValue::enum_descriptor().enclosing_message());
}
#[test]
#[cfg_attr(miri, ignore)] // Too slow on Miri.
fn to_string() {
assert_eq!(
"google.protobuf.FieldDescriptorProto.Label",
Label::enum_descriptor().to_string()
);
assert_eq!(
"google.protobuf.FieldDescriptorProto.Label",
Label::enum_descriptor().full_name()
);
assert_eq!(
"google.protobuf.FieldDescriptorProto.Label.LABEL_REPEATED",
Label::LABEL_REPEATED.descriptor().to_string()
);
assert_eq!(
"google.protobuf.FieldDescriptorProto.Label.LABEL_REPEATED",
Label::LABEL_REPEATED.descriptor().full_name()
);
}
}