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