1 use std::iter; 2 3 use crate::model; 4 use crate::model::WithLoc; 5 use crate::protobuf_path::ProtobufPath; 6 use crate::pure::convert::WithFullName; 7 use crate::FileDescriptorPair; 8 use crate::ProtobufAbsPath; 9 use crate::ProtobufAbsPathRef; 10 use crate::ProtobufIdent; 11 use crate::ProtobufIdentRef; 12 use crate::ProtobufRelPath; 13 use crate::ProtobufRelPathRef; 14 15 #[derive(thiserror::Error, Debug)] 16 enum TypeResolverError { 17 #[error("object is not found by path: {0}")] 18 NotFoundByAbsPath(ProtobufAbsPath), 19 #[error("object is not found by path `{0}` in scope `{1}`")] 20 NotFoundByRelPath(ProtobufRelPath, ProtobufAbsPath), 21 } 22 23 pub(crate) enum MessageOrEnum<'a> { 24 Message(&'a model::Message), 25 Enum(&'a model::Enumeration), 26 } 27 28 impl MessageOrEnum<'_> { _descriptor_type(&self) -> protobuf::descriptor::field_descriptor_proto::Type29 fn _descriptor_type(&self) -> protobuf::descriptor::field_descriptor_proto::Type { 30 match *self { 31 MessageOrEnum::Message(..) => { 32 protobuf::descriptor::field_descriptor_proto::Type::TYPE_MESSAGE 33 } 34 MessageOrEnum::Enum(..) => { 35 protobuf::descriptor::field_descriptor_proto::Type::TYPE_ENUM 36 } 37 } 38 } 39 } 40 41 #[derive(Clone)] 42 enum LookupScope<'a> { 43 File(&'a model::FileDescriptor), 44 Message(&'a model::Message, ProtobufAbsPath), 45 } 46 47 impl<'a> LookupScope<'a> { current_path(&self) -> ProtobufAbsPath48 fn current_path(&self) -> ProtobufAbsPath { 49 match self { 50 LookupScope::File(f) => f.package.clone(), 51 LookupScope::Message(_, p) => p.clone(), 52 } 53 } 54 messages(&self) -> &'a [model::WithLoc<model::Message>]55 fn messages(&self) -> &'a [model::WithLoc<model::Message>] { 56 match self { 57 &LookupScope::File(file) => &file.messages, 58 &LookupScope::Message(messasge, _) => &messasge.messages, 59 } 60 } 61 find_message(&self, simple_name: &ProtobufIdentRef) -> Option<&'a model::Message>62 fn find_message(&self, simple_name: &ProtobufIdentRef) -> Option<&'a model::Message> { 63 self.messages() 64 .into_iter() 65 .find(|m| m.t.name == simple_name.as_str()) 66 .map(|m| &m.t) 67 } 68 enums(&self) -> &'a [WithLoc<model::Enumeration>]69 fn enums(&self) -> &'a [WithLoc<model::Enumeration>] { 70 match self { 71 &LookupScope::File(file) => &file.enums, 72 &LookupScope::Message(messasge, _) => &messasge.enums, 73 } 74 } 75 members(&self) -> Vec<(ProtobufIdent, MessageOrEnum<'a>)>76 fn members(&self) -> Vec<(ProtobufIdent, MessageOrEnum<'a>)> { 77 let mut r = Vec::new(); 78 r.extend( 79 self.enums() 80 .into_iter() 81 .map(|e| (ProtobufIdent::from(&e.name[..]), MessageOrEnum::Enum(e))), 82 ); 83 r.extend(self.messages().into_iter().map(|m| { 84 ( 85 ProtobufIdent::from(&m.t.name[..]), 86 MessageOrEnum::Message(&m.t), 87 ) 88 })); 89 r 90 } 91 find_member(&self, simple_name: &ProtobufIdentRef) -> Option<MessageOrEnum<'a>>92 fn find_member(&self, simple_name: &ProtobufIdentRef) -> Option<MessageOrEnum<'a>> { 93 self.members() 94 .into_iter() 95 .filter_map(|(member_name, message_or_enum)| { 96 if member_name.as_ref() == simple_name { 97 Some(message_or_enum) 98 } else { 99 None 100 } 101 }) 102 .next() 103 } 104 find_message_or_enum( &self, path: &ProtobufRelPathRef, ) -> Option<WithFullName<MessageOrEnum<'a>>>105 pub(crate) fn find_message_or_enum( 106 &self, 107 path: &ProtobufRelPathRef, 108 ) -> Option<WithFullName<MessageOrEnum<'a>>> { 109 let current_path = self.current_path(); 110 let (first, rem) = match path.split_first_rem() { 111 Some(x) => x, 112 None => return None, 113 }; 114 115 if rem.is_empty() { 116 match self.find_member(first) { 117 Some(message_or_enum) => { 118 let mut result_path = current_path.clone(); 119 result_path.push_simple(first); 120 Some(WithFullName { 121 full_name: result_path, 122 t: message_or_enum, 123 }) 124 } 125 None => None, 126 } 127 } else { 128 match self.find_message(first) { 129 Some(message) => { 130 let mut message_path = current_path.clone(); 131 message_path.push_simple(ProtobufIdentRef::new(&message.name)); 132 let message_scope = LookupScope::Message(message, message_path); 133 message_scope.find_message_or_enum(rem) 134 } 135 None => None, 136 } 137 } 138 } 139 } 140 141 pub(crate) struct TypeResolver<'a> { 142 pub(crate) current_file: &'a model::FileDescriptor, 143 pub(crate) deps: &'a [FileDescriptorPair], 144 } 145 146 impl<'a> TypeResolver<'a> { all_files(&self) -> Vec<&'a model::FileDescriptor>147 pub(crate) fn all_files(&self) -> Vec<&'a model::FileDescriptor> { 148 iter::once(self.current_file) 149 .chain(self.deps.iter().map(|p| &p.parsed)) 150 .collect() 151 } 152 find_message_or_enum_by_abs_name( &self, absolute_path: &ProtobufAbsPath, ) -> anyhow::Result<WithFullName<MessageOrEnum<'a>>>153 pub(crate) fn find_message_or_enum_by_abs_name( 154 &self, 155 absolute_path: &ProtobufAbsPath, 156 ) -> anyhow::Result<WithFullName<MessageOrEnum<'a>>> { 157 for file in self.all_files() { 158 if let Some(relative) = absolute_path.remove_prefix(&file.package) { 159 if let Some(w) = LookupScope::File(file).find_message_or_enum(&relative) { 160 return Ok(w); 161 } 162 } 163 } 164 165 return Err(TypeResolverError::NotFoundByAbsPath(absolute_path.clone()).into()); 166 } 167 resolve_message_or_enum( &self, scope: &ProtobufAbsPathRef, name: &ProtobufPath, ) -> anyhow::Result<WithFullName<MessageOrEnum>>168 pub(crate) fn resolve_message_or_enum( 169 &self, 170 scope: &ProtobufAbsPathRef, 171 name: &ProtobufPath, 172 ) -> anyhow::Result<WithFullName<MessageOrEnum>> { 173 match name { 174 ProtobufPath::Abs(name) => Ok(self.find_message_or_enum_by_abs_name(&name)?), 175 ProtobufPath::Rel(name) => { 176 // find message or enum in current package 177 for p in scope.self_and_parents() { 178 let mut fq = p.to_owned(); 179 fq.push_relative(&name); 180 if let Ok(me) = self.find_message_or_enum_by_abs_name(&fq) { 181 return Ok(me); 182 } 183 } 184 185 Err(TypeResolverError::NotFoundByRelPath(name.clone(), scope.to_owned()).into()) 186 } 187 } 188 } 189 } 190