use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use crate::generated::descriptor::field_descriptor_proto::Type as ProtoType;
use crate::generated::descriptor::{
EnumOptions, EnumValueOptions, FieldOptions, MessageOptions, MethodOptions, OneofOptions,
ServiceOptions,
};
use buffa::editions::{EnumType, FieldPresence};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MessageIndex(pub(crate) u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EnumIndex(pub(crate) u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ScalarType {
Double,
Float,
Int64,
Uint64,
Int32,
Fixed64,
Fixed32,
Bool,
String,
Bytes,
Uint32,
Sfixed32,
Sfixed64,
Sint32,
Sint64,
}
impl ScalarType {
pub fn from_proto(ty: ProtoType) -> Option<Self> {
Some(match ty {
ProtoType::TYPE_DOUBLE => Self::Double,
ProtoType::TYPE_FLOAT => Self::Float,
ProtoType::TYPE_INT64 => Self::Int64,
ProtoType::TYPE_UINT64 => Self::Uint64,
ProtoType::TYPE_INT32 => Self::Int32,
ProtoType::TYPE_FIXED64 => Self::Fixed64,
ProtoType::TYPE_FIXED32 => Self::Fixed32,
ProtoType::TYPE_BOOL => Self::Bool,
ProtoType::TYPE_STRING => Self::String,
ProtoType::TYPE_BYTES => Self::Bytes,
ProtoType::TYPE_UINT32 => Self::Uint32,
ProtoType::TYPE_SFIXED32 => Self::Sfixed32,
ProtoType::TYPE_SFIXED64 => Self::Sfixed64,
ProtoType::TYPE_SINT32 => Self::Sint32,
ProtoType::TYPE_SINT64 => Self::Sint64,
ProtoType::TYPE_MESSAGE | ProtoType::TYPE_GROUP | ProtoType::TYPE_ENUM => return None,
})
}
pub fn is_valid_map_key(self) -> bool {
!matches!(self, Self::Double | Self::Float | Self::Bytes)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SingularKind {
Scalar(ScalarType),
Enum(EnumIndex),
Message(MessageIndex),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum FieldKind {
Singular(SingularKind),
List(SingularKind),
Map {
key: ScalarType,
value: SingularKind,
},
}
#[derive(Clone, Debug)]
pub struct FieldDescriptor {
pub(crate) name: String,
pub(crate) json_name: String,
pub(crate) number: u32,
pub(crate) kind: FieldKind,
pub(crate) presence: FieldPresence,
pub(crate) packed: bool,
pub(crate) delimited: bool,
pub(crate) oneof_index: Option<u16>,
pub(crate) options: Option<Box<FieldOptions>>,
}
impl FieldDescriptor {
#[inline]
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
#[must_use]
pub fn json_name(&self) -> &str {
&self.json_name
}
#[inline]
#[must_use]
pub fn number(&self) -> u32 {
self.number
}
#[inline]
#[must_use]
pub fn kind(&self) -> FieldKind {
self.kind
}
#[inline]
#[must_use]
pub fn presence(&self) -> FieldPresence {
self.presence
}
#[inline]
#[must_use]
pub fn is_packed(&self) -> bool {
self.packed
}
#[inline]
#[must_use]
pub fn is_delimited(&self) -> bool {
self.delimited
}
#[inline]
#[must_use]
pub fn oneof_index(&self) -> Option<u16> {
self.oneof_index
}
#[inline]
#[must_use]
pub fn options(&self) -> Option<&FieldOptions> {
self.options.as_deref()
}
}
#[derive(Clone, Debug)]
pub struct MessageDescriptor {
pub(crate) full_name: String,
pub(crate) fields: Vec<FieldDescriptor>,
pub(crate) field_by_number: Vec<(u32, u16)>,
pub(crate) field_by_name: Vec<(String, u16)>,
pub(crate) oneofs: Vec<OneofDescriptor>,
pub(crate) extension_ranges: Vec<(u32, u32)>,
pub(crate) options: Option<Box<MessageOptions>>,
}
impl MessageDescriptor {
#[inline]
#[must_use]
pub fn full_name(&self) -> &str {
&self.full_name
}
#[inline]
#[must_use]
pub fn options(&self) -> Option<&MessageOptions> {
self.options.as_deref()
}
#[inline]
#[must_use]
pub fn fields(&self) -> &[FieldDescriptor] {
&self.fields
}
#[inline]
#[must_use]
pub fn oneofs(&self) -> &[OneofDescriptor] {
&self.oneofs
}
#[inline]
#[must_use]
pub fn extension_ranges(&self) -> &[(u32, u32)] {
&self.extension_ranges
}
#[must_use]
pub fn field(&self, number: u32) -> Option<&FieldDescriptor> {
let i = self
.field_by_number
.binary_search_by_key(&number, |&(n, _)| n)
.ok()?;
let (_, idx) = self.field_by_number[i];
debug_assert!(
(idx as usize) < self.fields.len(),
"field_by_number index {idx} out of bounds for {} fields",
self.fields.len()
);
self.fields.get(idx as usize)
}
#[must_use]
pub fn field_by_name(&self, name: &str) -> Option<&FieldDescriptor> {
let i = self
.field_by_name
.binary_search_by(|(n, _)| n.as_str().cmp(name))
.ok()?;
let (_, idx) = self.field_by_name[i];
self.fields.get(idx as usize)
}
#[must_use]
pub fn in_extension_range(&self, number: u32) -> bool {
self.extension_ranges
.iter()
.any(|&(start, end)| start <= number && number < end)
}
}
#[derive(Clone, Debug)]
pub struct OneofDescriptor {
pub(crate) name: String,
pub(crate) field_indices: Vec<u16>,
pub(crate) synthetic: bool,
pub(crate) options: Option<Box<OneofOptions>>,
}
impl OneofDescriptor {
#[inline]
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
#[must_use]
pub fn options(&self) -> Option<&OneofOptions> {
self.options.as_deref()
}
#[inline]
#[must_use]
pub fn field_indices(&self) -> &[u16] {
&self.field_indices
}
#[inline]
#[must_use]
pub fn is_synthetic(&self) -> bool {
self.synthetic
}
}
#[derive(Clone, Debug)]
pub struct EnumDescriptor {
pub(crate) full_name: String,
pub(crate) values: Vec<EnumValueDescriptor>,
pub(crate) enum_type: EnumType,
pub(crate) options: Option<Box<EnumOptions>>,
}
impl EnumDescriptor {
#[inline]
#[must_use]
pub fn full_name(&self) -> &str {
&self.full_name
}
#[inline]
#[must_use]
pub fn options(&self) -> Option<&EnumOptions> {
self.options.as_deref()
}
#[inline]
#[must_use]
pub fn values(&self) -> &[EnumValueDescriptor] {
&self.values
}
#[inline]
#[must_use]
pub fn enum_type(&self) -> EnumType {
self.enum_type
}
#[must_use]
pub fn value(&self, number: i32) -> Option<&EnumValueDescriptor> {
self.values.iter().find(|v| v.number == number)
}
#[must_use]
pub fn value_by_name(&self, name: &str) -> Option<&EnumValueDescriptor> {
self.values.iter().find(|v| v.name == name)
}
}
#[derive(Clone, Debug)]
pub struct EnumValueDescriptor {
pub(crate) name: String,
pub(crate) number: i32,
pub(crate) options: Option<Box<EnumValueOptions>>,
}
impl EnumValueDescriptor {
#[inline]
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
#[must_use]
pub fn number(&self) -> i32 {
self.number
}
#[inline]
#[must_use]
pub fn options(&self) -> Option<&EnumValueOptions> {
self.options.as_deref()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServiceIndex(pub(crate) u32);
#[derive(Clone, Debug)]
pub struct ServiceDescriptor {
pub(crate) full_name: String,
pub(crate) methods: Vec<MethodDescriptor>,
pub(crate) options: Option<Box<ServiceOptions>>,
}
impl ServiceDescriptor {
#[inline]
#[must_use]
pub fn full_name(&self) -> &str {
&self.full_name
}
#[inline]
#[must_use]
pub fn options(&self) -> Option<&ServiceOptions> {
self.options.as_deref()
}
#[inline]
#[must_use]
pub fn methods(&self) -> &[MethodDescriptor] {
&self.methods
}
#[must_use]
pub fn method(&self, name: &str) -> Option<&MethodDescriptor> {
self.methods.iter().find(|m| m.name == name)
}
}
#[derive(Clone, Debug)]
pub struct MethodDescriptor {
pub(crate) name: String,
pub(crate) input: MessageIndex,
pub(crate) output: MessageIndex,
pub(crate) client_streaming: bool,
pub(crate) server_streaming: bool,
pub(crate) options: Option<Box<MethodOptions>>,
}
impl MethodDescriptor {
#[inline]
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
#[must_use]
pub fn input(&self) -> MessageIndex {
self.input
}
#[inline]
#[must_use]
pub fn output(&self) -> MessageIndex {
self.output
}
#[inline]
#[must_use]
pub fn is_client_streaming(&self) -> bool {
self.client_streaming
}
#[inline]
#[must_use]
pub fn is_server_streaming(&self) -> bool {
self.server_streaming
}
#[inline]
#[must_use]
pub fn options(&self) -> Option<&MethodOptions> {
self.options.as_deref()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ExtensionIndex(pub(crate) u32);
#[derive(Clone, Debug)]
pub struct ExtensionDescriptor {
pub(crate) field: FieldDescriptor,
pub(crate) full_name: String,
pub(crate) json_key: String,
pub(crate) extendee: MessageIndex,
}
impl ExtensionDescriptor {
#[inline]
#[must_use]
pub fn field(&self) -> &FieldDescriptor {
&self.field
}
#[inline]
#[must_use]
pub fn full_name(&self) -> &str {
&self.full_name
}
#[inline]
#[must_use]
pub fn json_key(&self) -> &str {
&self.json_key
}
#[inline]
#[must_use]
pub fn extendee(&self) -> MessageIndex {
self.extendee
}
}
impl AsRef<FieldDescriptor> for ExtensionDescriptor {
fn as_ref(&self) -> &FieldDescriptor {
&self.field
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scalar_type_from_proto_scalars() {
assert_eq!(
ScalarType::from_proto(ProtoType::TYPE_INT32),
Some(ScalarType::Int32)
);
assert_eq!(
ScalarType::from_proto(ProtoType::TYPE_STRING),
Some(ScalarType::String)
);
assert_eq!(
ScalarType::from_proto(ProtoType::TYPE_SINT64),
Some(ScalarType::Sint64)
);
}
#[test]
fn scalar_type_from_proto_rejects_composites() {
assert_eq!(ScalarType::from_proto(ProtoType::TYPE_MESSAGE), None);
assert_eq!(ScalarType::from_proto(ProtoType::TYPE_GROUP), None);
assert_eq!(ScalarType::from_proto(ProtoType::TYPE_ENUM), None);
}
#[test]
fn scalar_type_map_key_validity() {
assert!(ScalarType::Int32.is_valid_map_key());
assert!(ScalarType::String.is_valid_map_key());
assert!(ScalarType::Bool.is_valid_map_key());
assert!(ScalarType::Sfixed64.is_valid_map_key());
assert!(!ScalarType::Double.is_valid_map_key());
assert!(!ScalarType::Float.is_valid_map_key());
assert!(!ScalarType::Bytes.is_valid_map_key());
}
fn scalar_field(name: &str, number: u32, ty: ScalarType) -> FieldDescriptor {
FieldDescriptor {
name: name.into(),
json_name: name.into(),
number,
kind: FieldKind::Singular(SingularKind::Scalar(ty)),
presence: FieldPresence::Implicit,
packed: false,
delimited: false,
oneof_index: None,
options: None,
}
}
fn sample_message() -> MessageDescriptor {
MessageDescriptor {
full_name: "test.Foo".into(),
fields: alloc::vec![
scalar_field("a", 1, ScalarType::Int32),
scalar_field("b", 5, ScalarType::String),
],
field_by_number: alloc::vec![(1, 0), (5, 1)],
field_by_name: alloc::vec![("a".into(), 0), ("b".into(), 1)],
oneofs: Vec::new(),
extension_ranges: alloc::vec![(100, 200), (1000, 2000)],
options: None,
}
}
#[test]
fn message_field_lookup_by_number() {
let m = sample_message();
assert_eq!(m.field(1).unwrap().name, "a");
assert_eq!(m.field(5).unwrap().name, "b");
assert!(m.field(2).is_none());
assert!(m.field(99).is_none());
}
#[test]
fn message_field_lookup_by_name() {
let m = sample_message();
assert_eq!(m.field_by_name("a").unwrap().number, 1);
assert_eq!(m.field_by_name("b").unwrap().number, 5);
assert!(m.field_by_name("c").is_none());
assert!(m.field_by_name("").is_none());
}
#[test]
fn empty_message_field_lookup() {
let m = MessageDescriptor {
full_name: "test.Empty".into(),
fields: Vec::new(),
field_by_number: Vec::new(),
field_by_name: Vec::new(),
oneofs: Vec::new(),
extension_ranges: Vec::new(),
options: None,
};
assert!(m.field(1).is_none());
assert!(m.field_by_name("anything").is_none());
assert!(!m.in_extension_range(1));
}
#[test]
fn message_extension_range_check() {
let m = sample_message();
assert!(m.in_extension_range(100));
assert!(m.in_extension_range(150));
assert!(m.in_extension_range(199));
assert!(!m.in_extension_range(200)); assert!(m.in_extension_range(1500));
assert!(!m.in_extension_range(50));
assert!(!m.in_extension_range(500));
}
#[test]
fn enum_value_lookup() {
let e = EnumDescriptor {
full_name: "test.Color".into(),
values: alloc::vec![
EnumValueDescriptor {
name: "RED".into(),
number: 0,
options: None,
},
EnumValueDescriptor {
name: "GREEN".into(),
number: 1,
options: None,
},
EnumValueDescriptor {
name: "ALIAS_RED".into(),
number: 0,
options: None,
},
],
enum_type: EnumType::Open,
options: None,
};
assert_eq!(e.value(1).unwrap().name, "GREEN");
assert_eq!(e.value(0).unwrap().name, "RED"); assert!(e.value(99).is_none());
assert_eq!(e.value_by_name("GREEN").unwrap().number, 1);
assert!(e.value_by_name("BLUE").is_none());
}
#[test]
fn field_kind_is_copy() {
let list = FieldKind::List(SingularKind::Message(MessageIndex(3)));
let copied = list;
assert_eq!(list, copied);
let map = FieldKind::Map {
key: ScalarType::String,
value: SingularKind::Enum(EnumIndex(1)),
};
match map {
FieldKind::Map { key, value } => {
assert_eq!(key, ScalarType::String);
assert_eq!(value, SingularKind::Enum(EnumIndex(1)));
}
_ => panic!(),
}
}
#[test]
fn scalar_type_from_proto_exhaustive() {
use ProtoType::*;
let all = [
(TYPE_DOUBLE, ScalarType::Double),
(TYPE_FLOAT, ScalarType::Float),
(TYPE_INT64, ScalarType::Int64),
(TYPE_UINT64, ScalarType::Uint64),
(TYPE_INT32, ScalarType::Int32),
(TYPE_FIXED64, ScalarType::Fixed64),
(TYPE_FIXED32, ScalarType::Fixed32),
(TYPE_BOOL, ScalarType::Bool),
(TYPE_STRING, ScalarType::String),
(TYPE_BYTES, ScalarType::Bytes),
(TYPE_UINT32, ScalarType::Uint32),
(TYPE_SFIXED32, ScalarType::Sfixed32),
(TYPE_SFIXED64, ScalarType::Sfixed64),
(TYPE_SINT32, ScalarType::Sint32),
(TYPE_SINT64, ScalarType::Sint64),
];
for (proto, scalar) in all {
assert_eq!(ScalarType::from_proto(proto), Some(scalar));
}
}
}