1mod names;
2mod options;
3mod resolve;
4mod visit;
5
6use core::fmt;
7use std::{
8 borrow::Cow,
9 collections::{HashMap, HashSet},
10 iter,
11 sync::Arc,
12};
13
14use crate::{
15 descriptor::{
16 error::{DescriptorErrorKind, Label},
17 to_index,
18 types::FileDescriptorProto,
19 Definition, DefinitionKind, DescriptorPoolInner, EnumIndex, ExtensionIndex,
20 FileDescriptorInner, FileIndex, MessageIndex, ServiceIndex,
21 },
22 DescriptorError, DescriptorPool,
23};
24
25#[derive(Clone, Copy)]
26struct DescriptorPoolOffsets {
27 file: FileIndex,
28 message: MessageIndex,
29 enum_: EnumIndex,
30 service: ServiceIndex,
31 extension: ExtensionIndex,
32}
33
34#[derive(Copy, Clone, Debug)]
35enum ResolveNameFilter {
36 Message,
37 Extension,
38 FieldType,
39}
40
41enum ResolveNameResult<'a, 'b> {
42 Found {
43 name: Cow<'b, str>,
44 def: &'a Definition,
45 },
46 InvalidType {
47 name: Cow<'b, str>,
48 def: &'a Definition,
49 filter: ResolveNameFilter,
50 },
51 NotImported {
52 name: Cow<'b, str>,
53 file: FileIndex,
54 },
55 Shadowed {
56 name: Cow<'b, str>,
57 shadowed_name: Cow<'b, str>,
58 },
59 NotFound,
60}
61
62impl DescriptorPoolOffsets {
63 fn new(pool: &DescriptorPoolInner) -> Self {
64 DescriptorPoolOffsets {
65 file: to_index(pool.files.len()),
66 message: to_index(pool.messages.len()),
67 enum_: to_index(pool.enums.len()),
68 service: to_index(pool.services.len()),
69 extension: to_index(pool.extensions.len()),
70 }
71 }
72
73 fn rollback(&self, pool: &mut DescriptorPoolInner) {
74 pool.files.truncate(self.file as usize);
75 pool.messages.truncate(self.message as usize);
76 pool.enums.truncate(self.enum_ as usize);
77 pool.extensions.truncate(self.extension as usize);
78 pool.services.truncate(self.service as usize);
79 pool.names.retain(|name, definition| match definition.kind {
80 DefinitionKind::Package => pool.files.iter().any(|f| {
81 f.prost.package().starts_with(name.as_ref())
82 && matches!(
83 f.prost.package().as_bytes().get(name.len()),
84 None | Some(&b'.')
85 )
86 }),
87 DefinitionKind::Message(message)
88 | DefinitionKind::Field(message)
89 | DefinitionKind::Oneof(message) => message < self.message,
90 DefinitionKind::Service(service) | DefinitionKind::Method(service) => {
91 service < self.service
92 }
93 DefinitionKind::Enum(enum_) | DefinitionKind::EnumValue(enum_) => enum_ < self.enum_,
94 DefinitionKind::Extension(extension) => extension < self.extension,
95 });
96 pool.file_names.retain(|_, &mut file| file < self.file);
97 for message in &mut pool.messages {
98 message.extensions.retain(|&message| message < self.message);
99 }
100 }
101}
102
103impl DescriptorPool {
104 pub(crate) fn build_files<I>(&mut self, files: I) -> Result<(), DescriptorError>
105 where
106 I: IntoIterator<Item = FileDescriptorProto>,
107 {
108 let offsets = DescriptorPoolOffsets::new(&self.inner);
109
110 let deduped_files: Vec<_> = files
111 .into_iter()
112 .filter(|f| !self.inner.file_names.contains_key(f.name()))
113 .collect();
114
115 let result = self.build_files_deduped(offsets, &deduped_files);
116 if result.is_err() {
117 debug_assert_eq!(Arc::strong_count(&self.inner), 1);
118 offsets.rollback(Arc::get_mut(&mut self.inner).unwrap());
119 }
120
121 result
122 }
123
124 fn build_files_deduped(
125 &mut self,
126 offsets: DescriptorPoolOffsets,
127 deduped_files: &[FileDescriptorProto],
128 ) -> Result<(), DescriptorError> {
129 if deduped_files.is_empty() {
130 return Ok(());
131 }
132
133 let inner = Arc::make_mut(&mut self.inner);
134
135 inner.collect_names(offsets, deduped_files)?;
136
137 inner.resolve_names(offsets, deduped_files)?;
138
139 self.resolve_options(offsets, deduped_files)?;
140
141 debug_assert_eq!(Arc::strong_count(&self.inner), 1);
142 let inner = Arc::get_mut(&mut self.inner).unwrap();
143 for file in &mut inner.files[offsets.file as usize..] {
144 file.prost = file.raw.to_prost();
145 }
146
147 Ok(())
148 }
149}
150
151impl ResolveNameFilter {
152 fn is_match(&self, def: &DefinitionKind) -> bool {
153 matches!(
154 (self, def),
155 (ResolveNameFilter::Message, DefinitionKind::Message(_))
156 | (ResolveNameFilter::Extension, DefinitionKind::Extension(_))
157 | (
158 ResolveNameFilter::FieldType,
159 DefinitionKind::Message(_) | DefinitionKind::Enum(_),
160 )
161 )
162 }
163}
164
165impl fmt::Display for ResolveNameFilter {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 match self {
168 ResolveNameFilter::Message => f.write_str("a message type"),
169 ResolveNameFilter::Extension => f.write_str("an extension"),
170 ResolveNameFilter::FieldType => f.write_str("a message or enum type"),
171 }
172 }
173}
174
175impl<'a, 'b> ResolveNameResult<'a, 'b> {
176 fn new(
177 dependencies: &HashSet<FileIndex>,
178 names: &'a HashMap<Box<str>, Definition>,
179 name: impl Into<Cow<'b, str>>,
180 filter: ResolveNameFilter,
181 ) -> Self {
182 let name = name.into();
183 if let Some(def) = names.get(name.as_ref()) {
184 if !dependencies.contains(&def.file) {
185 ResolveNameResult::NotImported {
186 name,
187 file: def.file,
188 }
189 } else if !filter.is_match(&def.kind) {
190 ResolveNameResult::InvalidType { name, def, filter }
191 } else {
192 ResolveNameResult::Found { name, def }
193 }
194 } else {
195 ResolveNameResult::NotFound
196 }
197 }
198
199 fn into_owned(self) -> ResolveNameResult<'a, 'static> {
200 match self {
201 ResolveNameResult::Found { name, def } => ResolveNameResult::Found {
202 name: Cow::Owned(name.into_owned()),
203 def,
204 },
205 ResolveNameResult::InvalidType { name, def, filter } => {
206 ResolveNameResult::InvalidType {
207 name: Cow::Owned(name.into_owned()),
208 def,
209 filter,
210 }
211 }
212 ResolveNameResult::NotImported { name, file } => ResolveNameResult::NotImported {
213 name: Cow::Owned(name.into_owned()),
214 file,
215 },
216 ResolveNameResult::Shadowed {
217 name,
218 shadowed_name,
219 } => ResolveNameResult::Shadowed {
220 name: Cow::Owned(name.into_owned()),
221 shadowed_name: Cow::Owned(shadowed_name.into_owned()),
222 },
223 ResolveNameResult::NotFound => ResolveNameResult::NotFound,
224 }
225 }
226
227 fn is_found(&self) -> bool {
228 matches!(self, ResolveNameResult::Found { .. })
229 }
230
231 #[allow(clippy::result_large_err)]
232 fn into_result(
233 self,
234 orig_name: impl Into<String>,
235 files: &[FileDescriptorInner],
236 found_file: FileIndex,
237 found_path1: &[i32],
238 found_path2: &[i32],
239 ) -> Result<(Cow<'b, str>, &'a Definition), DescriptorErrorKind> {
240 match self {
241 ResolveNameResult::Found { name, def } => Ok((name, def)),
242 ResolveNameResult::InvalidType { name, def, filter } => {
243 Err(DescriptorErrorKind::InvalidType {
244 name: name.into_owned(),
245 expected: filter.to_string(),
246 found: Label::new(
247 files,
248 "found here",
249 found_file,
250 join_path(found_path1, found_path2),
251 ),
252 defined: Label::new(files, "defined here", def.file, def.path.clone()),
253 })
254 }
255 ResolveNameResult::NotImported { name, file } => {
256 let root_name = files[found_file as usize].raw.name();
257 let dep_name = files[file as usize].raw.name();
258 Err(DescriptorErrorKind::NameNotFound {
259 found: Label::new(
260 files,
261 "found here",
262 found_file,
263 join_path(found_path1, found_path2),
264 ),
265 help: Some(format!(
266 "'{}' is defined in '{}', which is not imported by '{}'",
267 name, dep_name, root_name
268 )),
269 name: name.into_owned(),
270 })
271 }
272 ResolveNameResult::NotFound => Err(DescriptorErrorKind::NameNotFound {
273 name: orig_name.into(),
274 found: Label::new(
275 files,
276 "found here",
277 found_file,
278 join_path(found_path1, found_path2),
279 ),
280 help: None,
281 }),
282 ResolveNameResult::Shadowed { name, shadowed_name } => Err(DescriptorErrorKind::NameShadowed {
283 found: Label::new(
284 files,
285 "found here",
286 found_file,
287 join_path(found_path1, found_path2),
288 ),
289 help: Some(format!(
290 "The innermost scope is searched first in name resolution. Consider using a leading '.' (i.e., '.{name}') to start from the outermost scope.",
291 )),
292 name: name.into_owned(),
293 shadowed_name: shadowed_name.into_owned(),
294 }),
295 }
296 }
297}
298
299fn to_json_name(name: &str) -> String {
300 let mut result = String::with_capacity(name.len());
301 let mut uppercase_next = false;
302
303 for ch in name.chars() {
304 if ch == '_' {
305 uppercase_next = true
306 } else if uppercase_next {
307 result.push(ch.to_ascii_uppercase());
308 uppercase_next = false;
309 } else {
310 result.push(ch);
311 }
312 }
313
314 result
315}
316
317fn resolve_name<'a, 'b>(
318 dependencies: &HashSet<FileIndex>,
319 names: &'a HashMap<Box<str>, Definition>,
320 scope: &str,
321 name: &'b str,
322 filter: ResolveNameFilter,
323) -> ResolveNameResult<'a, 'b> {
324 match name.strip_prefix('.') {
325 Some(full_name) => ResolveNameResult::new(dependencies, names, full_name, filter),
326 None if scope.is_empty() => ResolveNameResult::new(dependencies, names, name, filter),
327 None => resolve_relative_name(dependencies, names, scope, name, filter),
328 }
329}
330
331fn resolve_relative_name<'a, 'b>(
332 dependencies: &HashSet<FileIndex>,
333 names: &'a HashMap<Box<str>, Definition>,
334 scope: &str,
335 relative_name: &'b str,
336 filter: ResolveNameFilter,
337) -> ResolveNameResult<'a, 'b> {
338 let mut err = ResolveNameResult::NotFound;
339 let relative_first_part = relative_name.split('.').next().unwrap_or_default();
340
341 for candidate_parent in resolve_relative_candidate_parents(scope) {
342 let candidate = match candidate_parent {
343 "" => Cow::Borrowed(relative_first_part),
344 _ => Cow::Owned(format!("{}.{}", candidate_parent, relative_first_part)),
345 };
346
347 if relative_first_part.len() == relative_name.len() {
348 let res = ResolveNameResult::new(dependencies, names, candidate, filter);
350 if res.is_found() {
351 return res.into_owned();
352 } else if matches!(err, ResolveNameResult::NotFound) {
353 err = res;
354 }
355 } else {
356 match names.get(candidate.as_ref()) {
358 Some(def) if def.kind.is_parent() => {
359 let candidate_full = match candidate_parent {
360 "" => Cow::Borrowed(relative_name),
361 _ => Cow::Owned(format!("{}.{}", candidate_parent, relative_name)),
362 };
363
364 let res =
365 ResolveNameResult::new(dependencies, names, candidate_full.clone(), filter);
366 if matches!(res, ResolveNameResult::NotFound) {
367 return ResolveNameResult::Shadowed {
368 name: Cow::Borrowed(relative_name),
369 shadowed_name: candidate_full,
370 };
371 } else {
372 return res;
373 }
374 }
375 _ => continue,
376 }
377 }
378 }
379
380 err.into_owned()
381}
382
383fn resolve_relative_candidate_parents(scope: &str) -> impl Iterator<Item = &str> {
384 iter::once(scope)
385 .chain(scope.rmatch_indices('.').map(move |(i, _)| &scope[..i]))
386 .chain(iter::once(""))
387}
388
389fn join_path(path1: &[i32], path2: &[i32]) -> Box<[i32]> {
390 let mut path = Vec::with_capacity(path1.len() + path2.len());
391 path.extend_from_slice(path1);
392 path.extend_from_slice(path2);
393 path.into_boxed_slice()
394}