blob: f70d233f1f9bd8454ff40199e68a25706a543233 [file] [log] [blame]
use std::ops::Deref;
use protobuf::reflect::EnumDescriptor;
use protobuf::reflect::EnumValueDescriptor;
use protobuf::reflect::FieldDescriptor;
use protobuf::reflect::FileDescriptor;
use protobuf::reflect::MessageDescriptor;
use protobuf::reflect::OneofDescriptor;
use protobuf_parse::ProtobufAbsPath;
use protobuf_parse::ProtobufAbsPathRef;
use protobuf_parse::ProtobufIdentRef;
use protobuf_parse::ProtobufRelPath;
use protobuf_parse::ProtobufRelPathRef;
use crate::customize::Customize;
use crate::gen::field::rust_field_name_for_protobuf_field_name;
use crate::gen::file_and_mod::FileAndMod;
use crate::gen::map::map_entry;
use crate::gen::message::message_name_to_nested_mod_name;
use crate::gen::paths::proto_path_to_rust_mod;
use crate::gen::rust::ident::RustIdent;
use crate::gen::rust::ident_with_path::RustIdentWithPath;
use crate::gen::rust::rel_path::RustRelativePath;
use crate::gen::strx::capitalize;
pub(crate) struct RootScope<'a> {
pub file_descriptors: &'a [FileDescriptor],
}
impl<'a> RootScope<'a> {
fn packages(&'a self) -> Vec<FileScope<'a>> {
self.file_descriptors
.iter()
.map(|fd| FileScope {
file_descriptor: fd,
})
.collect()
}
// find enum by fully qualified name
pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> {
match self.find_message_or_enum(fqn) {
MessageOrEnumWithScope::Enum(e) => e,
_ => panic!("not an enum: {}", fqn),
}
}
// find message by fully qualified name
pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> {
match self.find_message_or_enum(fqn) {
MessageOrEnumWithScope::Message(m) => m,
_ => panic!("not a message: {}", fqn),
}
}
// find message or enum by fully qualified name
pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> {
assert!(!fqn.is_root());
self.packages()
.into_iter()
.flat_map(|p| p.find_message_or_enum_abs(fqn))
.next()
.expect(&format!("enum not found by name: {}", fqn))
}
}
#[derive(Clone, Debug)]
pub(crate) struct FileScope<'a> {
pub file_descriptor: &'a FileDescriptor,
}
impl<'a> Deref for FileScope<'a> {
type Target = FileDescriptor;
fn deref(&self) -> &Self::Target {
self.file_descriptor
}
}
impl<'a> FileScope<'a> {
fn package(&self) -> ProtobufAbsPath {
ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor)
}
pub fn to_scope(&self) -> Scope<'a> {
Scope {
file_scope: self.clone(),
path: Vec::new(),
}
}
fn find_message_or_enum(
&self,
name: &ProtobufRelPathRef,
) -> Option<MessageOrEnumWithScope<'a>> {
self.find_messages_and_enums()
.into_iter()
.filter(|e| e.protobuf_name_to_package().as_ref() == name)
.next()
}
fn find_message_or_enum_abs(
&self,
name: &ProtobufAbsPathRef,
) -> Option<MessageOrEnumWithScope<'a>> {
let name = name.to_owned();
match name.remove_prefix(&self.package()) {
Some(rem) => self.find_message_or_enum(&rem),
None => None,
}
}
// find all enums in given file descriptor
pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> {
let mut r = Vec::new();
self.to_scope().walk_scopes(|scope| {
r.extend(scope.enums());
});
r
}
/// Find all messages in given file descriptor
pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> {
let mut r = Vec::new();
self.to_scope().walk_scopes(|scope| {
r.extend(scope.messages());
});
r
}
/// Find all messages in given file descriptor, except map messages
pub fn find_messages_except_map(&self) -> Vec<MessageWithScope<'a>> {
self.find_messages()
.into_iter()
.filter(|m| !m.is_map())
.collect()
}
/// find all messages and enums in given file descriptor
pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
let mut r = Vec::new();
self.to_scope().walk_scopes(|scope| {
r.extend(scope.messages_and_enums());
});
r
}
}
#[derive(Clone, Debug)]
pub(crate) struct Scope<'a> {
pub file_scope: FileScope<'a>,
pub path: Vec<MessageDescriptor>,
}
impl<'a> Scope<'a> {
pub(crate) fn file_descriptor(&self) -> FileDescriptor {
self.file_scope.file_descriptor.clone()
}
// get message descriptors in this scope
fn message_descriptors(&self) -> Vec<MessageDescriptor> {
if self.path.is_empty() {
self.file_scope.file_descriptor.messages().collect()
} else {
self.path.last().unwrap().nested_messages().collect()
}
}
// get enum descriptors in this scope
fn enum_descriptors(&self) -> Vec<EnumDescriptor> {
if self.path.is_empty() {
self.file_scope.file_descriptor.enums().collect()
} else {
self.path.last().unwrap().nested_enums().collect()
}
}
// get messages with attached scopes in this scope
pub fn messages(&self) -> Vec<MessageWithScope<'a>> {
self.message_descriptors()
.into_iter()
.map(|message| MessageWithScope {
scope: self.clone(),
message,
})
.collect()
}
// get enums with attached scopes in this scope
pub fn enums(&self) -> Vec<EnumWithScope<'a>> {
self.enum_descriptors()
.into_iter()
.map(|en| EnumWithScope {
scope: self.clone(),
en,
})
.collect()
}
// get messages and enums with attached scopes in this scope
pub fn messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
self.messages()
.into_iter()
.map(|m| MessageOrEnumWithScope::Message(m))
.chain(
self.enums()
.into_iter()
.map(|m| MessageOrEnumWithScope::Enum(m)),
)
.collect()
}
// nested scopes, i. e. scopes of nested messages
fn nested_scopes(&self) -> Vec<Scope<'a>> {
self.message_descriptors()
.into_iter()
.map(|m| {
let mut nested = self.clone();
nested.path.push(m);
nested
})
.collect()
}
fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
(*callback)(self);
for nested in self.nested_scopes() {
nested.walk_scopes_impl(callback);
}
}
// apply callback for this scope and all nested scopes
fn walk_scopes<F>(&self, mut callback: F)
where
F: FnMut(&Scope<'a>),
{
self.walk_scopes_impl(&mut callback);
}
pub fn rust_path_to_file(&self) -> RustRelativePath {
RustRelativePath::from_idents(
self.path
.iter()
.map(|m| message_name_to_nested_mod_name(m.name())),
)
}
pub fn path_str(&self) -> String {
let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect();
v.join(".")
}
pub fn prefix(&self) -> String {
let path_str = self.path_str();
if path_str.is_empty() {
path_str
} else {
format!("{}.", path_str)
}
}
pub fn protobuf_path_to_file(&self) -> ProtobufRelPath {
ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name())))
}
pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath {
let mut r = self.file_scope.package();
r.push_relative(&self.protobuf_path_to_file());
r
}
pub fn file_and_mod(&self, customize: Customize) -> FileAndMod {
FileAndMod {
file: self.file_scope.file_descriptor.proto().name().to_owned(),
relative_mod: self.rust_path_to_file(),
customize,
}
}
}
pub(crate) trait WithScope<'a> {
fn scope(&self) -> &Scope<'a>;
fn file_descriptor(&self) -> FileDescriptor {
self.scope().file_descriptor()
}
// message or enum name
fn name(&self) -> &ProtobufIdentRef;
fn name_to_package(&self) -> String {
let mut r = self.scope().prefix();
r.push_str(&self.name());
r
}
fn protobuf_name_to_package(&self) -> ProtobufRelPath {
let r = self.scope().protobuf_path_to_file();
r.append_ident(ProtobufIdentRef::new(self.name()))
}
/// Return absolute name starting with dot
fn name_absolute(&self) -> ProtobufAbsPath {
let mut path = self.scope().protobuf_absolute_path();
path.push_simple(self.name());
path
}
// rust type name of this descriptor
fn rust_name(&self) -> RustIdent {
let rust_name = capitalize(&self.name());
RustIdent::new(&rust_name)
}
fn rust_name_to_file(&self) -> RustIdentWithPath {
self.scope()
.rust_path_to_file()
.into_path()
.with_ident(self.rust_name())
}
// fully-qualified name of this type
fn rust_name_with_file(&self) -> RustIdentWithPath {
let mut r = self.rust_name_to_file();
r.prepend_ident(proto_path_to_rust_mod(
self.scope().file_descriptor().name(),
));
r
}
}
#[derive(Clone, Debug)]
pub(crate) struct MessageWithScope<'a> {
pub scope: Scope<'a>,
pub message: MessageDescriptor,
}
impl<'a> WithScope<'a> for MessageWithScope<'a> {
fn scope(&self) -> &Scope<'a> {
&self.scope
}
fn name(&self) -> &ProtobufIdentRef {
ProtobufIdentRef::new(self.message.name())
}
}
impl<'a> MessageWithScope<'a> {
pub fn into_scope(mut self) -> Scope<'a> {
self.scope.path.push(self.message);
self.scope
}
pub fn to_scope(&self) -> Scope<'a> {
self.clone().into_scope()
}
pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
self.message
.fields()
.into_iter()
.map(|field| FieldWithContext {
field,
message: self.clone(),
})
.collect()
}
pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
self.message
.oneofs()
.into_iter()
.map(|oneof| OneofWithContext {
message: self.clone(),
oneof,
})
.collect()
}
pub fn mod_name(&self) -> RustIdent {
message_name_to_nested_mod_name(self.message.name())
}
/// This message is a special message which is a map.
pub fn is_map(&self) -> bool {
map_entry(self).is_some()
}
}
#[derive(Clone, Debug)]
pub(crate) struct EnumWithScope<'a> {
pub scope: Scope<'a>,
pub en: EnumDescriptor,
}
impl<'a> EnumWithScope<'a> {
pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
self.en
.values()
.into_iter()
.map(|v| EnumValueWithContext {
en: self.clone(),
proto: v,
})
.collect()
}
// find enum value by protobuf name
pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
self.values()
.into_iter()
.find(|v| v.proto.proto().name() == name)
.unwrap()
}
}
#[derive(Clone, Debug)]
pub(crate) struct EnumValueWithContext<'a> {
pub en: EnumWithScope<'a>,
pub proto: EnumValueDescriptor,
}
impl<'a> EnumValueWithContext<'a> {
pub fn rust_name(&self) -> RustIdent {
// TODO: camel case or something.
RustIdent::new(self.proto.name())
}
}
impl<'a> WithScope<'a> for EnumWithScope<'a> {
fn scope(&self) -> &Scope<'a> {
&self.scope
}
fn name(&self) -> &ProtobufIdentRef {
ProtobufIdentRef::new(self.en.name())
}
}
pub(crate) enum MessageOrEnumWithScope<'a> {
Message(MessageWithScope<'a>),
Enum(EnumWithScope<'a>),
}
impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
fn scope(&self) -> &Scope<'a> {
match self {
MessageOrEnumWithScope::Message(m) => m.scope(),
MessageOrEnumWithScope::Enum(e) => e.scope(),
}
}
fn name(&self) -> &ProtobufIdentRef {
match self {
MessageOrEnumWithScope::Message(m) => m.name(),
MessageOrEnumWithScope::Enum(e) => e.name(),
}
}
}
#[derive(Clone)]
pub(crate) struct FieldWithContext<'a> {
pub field: FieldDescriptor,
pub message: MessageWithScope<'a>,
}
impl<'a> Deref for FieldWithContext<'a> {
type Target = FieldDescriptor;
fn deref(&self) -> &Self::Target {
&self.field
}
}
impl<'a> FieldWithContext<'a> {
pub fn is_oneof(&self) -> bool {
self.field.containing_oneof().is_some()
}
pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
match self.field.containing_oneof() {
Some(oneof) => Some(OneofWithContext {
message: self.message.clone(),
oneof,
}),
None => None,
}
}
}
#[derive(Clone)]
pub(crate) struct OneofVariantWithContext<'a> {
pub oneof: &'a OneofWithContext<'a>,
pub field: FieldDescriptor,
}
#[derive(Clone)]
pub(crate) struct OneofWithContext<'a> {
pub oneof: OneofDescriptor,
pub message: MessageWithScope<'a>,
}
impl<'a> OneofWithContext<'a> {
pub fn field_name(&'a self) -> RustIdent {
return rust_field_name_for_protobuf_field_name(self.oneof.name());
}
// rust type name of enum
pub fn rust_name(&self) -> RustIdentWithPath {
let type_name = RustIdent::from(capitalize(self.oneof.name()));
self.message
.to_scope()
.rust_path_to_file()
.into_path()
.with_ident(type_name)
}
pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
self.message
.fields()
.into_iter()
.filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof))
.map(|f| OneofVariantWithContext {
oneof: self,
field: f.field,
})
.collect()
}
}