1use std::{
2 fmt,
3 ops::{Range, RangeInclusive},
4};
5
6use crate::descriptor::{FileDescriptorInner, FileIndex};
7
8pub struct DescriptorError {
10 errors: Box<[DescriptorErrorKind]>,
11 #[cfg(feature = "miette")]
12 source: Option<miette::NamedSource<String>>,
13}
14
15#[derive(Debug)]
16pub(super) enum DescriptorErrorKind {
17 MissingRequiredField {
18 label: Label,
19 },
20 UnknownSyntax {
21 syntax: String,
22 found: Label,
23 },
24 DuplicateFileName {
25 name: String,
26 },
27 FileNotFound {
28 name: String,
29 found: Label,
30 },
31 InvalidImportIndex,
32 InvalidOneofIndex,
33 DuplicateName {
34 name: String,
35 first: Label,
36 second: Label,
37 },
38 DuplicateFieldNumber {
39 number: u32,
40 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
41 first: Label,
42 second: Label,
43 },
44 DuplicateFieldJsonName {
45 name: String,
46 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
47 first: Label,
48 second: Label,
49 },
50 DuplicateFieldCamelCaseName {
51 first_name: String,
52 second_name: String,
53 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
54 first: Label,
55 second: Label,
56 },
57 InvalidFieldNumber {
58 number: i32,
59 found: Label,
60 },
61 FieldNumberInReservedRange {
62 number: i32,
63 range: Range<i32>,
64 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
65 defined: Label,
66 found: Label,
67 },
68 FieldNumberInExtensionRange {
69 number: i32,
70 range: Range<i32>,
71 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
72 defined: Label,
73 found: Label,
74 },
75 ExtensionNumberOutOfRange {
76 number: i32,
77 message: String,
78 found: Label,
79 },
80 NameNotFound {
81 name: String,
82 found: Label,
83 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
84 help: Option<String>,
85 },
86 NameShadowed {
87 name: String,
88 shadowed_name: String,
89 found: Label,
90 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
91 help: Option<String>,
92 },
93 InvalidType {
94 name: String,
95 expected: String,
96 found: Label,
97 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
98 defined: Label,
99 },
100 InvalidFieldDefault {
101 value: String,
102 kind: String,
103 found: Label,
104 },
105 EmptyEnum {
106 found: Label,
107 },
108 InvalidProto3EnumDefault {
109 found: Label,
110 },
111 DuplicateEnumNumber {
112 number: i32,
113 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
114 first: Label,
115 second: Label,
116 },
117 EnumNumberInReservedRange {
118 number: i32,
119 range: RangeInclusive<i32>,
120 #[cfg_attr(not(feature = "miette"), allow(dead_code))]
121 defined: Label,
122 found: Label,
123 },
124 OptionNotFound {
125 name: String,
126 found: Label,
127 },
128 InvalidOptionType {
129 name: String,
130 ty: String,
131 value: String,
132 is_last: bool,
133 found: Label,
134 },
135 InvalidOptionExtendee {
136 name: String,
137 expected_extendee: String,
138 actual_extendee: String,
139 found: Label,
140 },
141 #[cfg(feature = "text-format")]
142 InvalidMessageOption {
143 name: String,
144 ty: String,
145 found: Label,
146 err: crate::text_format::ParseError,
147 },
148 DuplicateOption {
149 name: String,
150 found: Label,
151 },
152 DecodeFileDescriptorSet {
153 err: prost::DecodeError,
154 },
155}
156
157#[derive(Debug)]
158pub(super) struct Label {
159 file: String,
160 path: Box<[i32]>,
161 span: Option<[i32; 4]>,
162 #[cfg(feature = "miette")]
163 message: String,
164 #[cfg(feature = "miette")]
165 resolved: Option<miette::SourceSpan>,
166}
167
168impl DescriptorError {
169 pub(super) fn new(errors: Vec<DescriptorErrorKind>) -> DescriptorError {
170 debug_assert!(!errors.is_empty());
171 DescriptorError {
172 errors: errors.into(),
173 #[cfg(feature = "miette")]
174 source: None,
175 }
176 }
177
178 pub fn file(&self) -> Option<&str> {
180 self.first().label().map(|l| l.file.as_str())
181 }
182
183 pub fn line(&self) -> Option<usize> {
188 self.first()
189 .label()
190 .and_then(|l| l.span)
191 .map(|s| s[0] as usize)
192 }
193
194 pub fn column(&self) -> Option<usize> {
199 self.first()
200 .label()
201 .and_then(|l| l.span)
202 .map(|s| s[1] as usize)
203 }
204
205 pub fn path(&self) -> Option<&[i32]> {
209 self.first().label().map(|l| l.path.as_ref())
210 }
211
212 #[cfg(feature = "miette")]
213 #[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
214 pub fn with_source_code(mut self, source: &str) -> Self {
218 if let Some(file) = self.file() {
219 let file = file.to_owned();
220
221 self.source = Some(miette::NamedSource::new(&file, source.to_owned()));
222 for error in self.errors.as_mut() {
223 error.add_source_code(&file, source);
224 }
225 }
226 self
227 }
228
229 fn first(&self) -> &DescriptorErrorKind {
230 &self.errors[0]
231 }
232}
233
234impl std::error::Error for DescriptorError {
235 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
236 self.first().source()
237 }
238}
239
240impl fmt::Display for DescriptorError {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 self.first().fmt(f)
243 }
244}
245
246impl fmt::Debug for DescriptorError {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 if let Some(file) = self.file() {
249 write!(f, "{file}:")?;
250 if let (Some(line), Some(column)) = (self.line(), self.column()) {
251 write!(f, "{}:{}:", line + 1, column + 1)?;
252 }
253 write!(f, " ")?;
254 }
255
256 write!(f, "{self}")
257 }
258}
259
260#[cfg(feature = "miette")]
261#[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
262impl miette::Diagnostic for DescriptorError {
263 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
264 self.first().code()
265 }
266
267 fn severity(&self) -> Option<miette::Severity> {
268 self.first().severity()
269 }
270
271 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
272 self.first().help()
273 }
274
275 fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
276 self.first().url()
277 }
278
279 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
280 match &self.source {
281 Some(source) => Some(source),
282 None => None,
283 }
284 }
285
286 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
287 self.first().labels()
288 }
289
290 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
291 if self.errors.len() > 1 {
292 Some(Box::new(
293 self.errors
294 .iter()
295 .map(|e| e as &dyn miette::Diagnostic)
296 .skip(1),
297 ))
298 } else {
299 None
300 }
301 }
302
303 fn diagnostic_source(&self) -> Option<&dyn miette::Diagnostic> {
304 self.first().diagnostic_source()
305 }
306}
307
308impl DescriptorErrorKind {
309 fn label(&self) -> Option<&Label> {
310 match self {
311 DescriptorErrorKind::MissingRequiredField { label } => Some(label),
312 DescriptorErrorKind::UnknownSyntax { found, .. } => Some(found),
313 DescriptorErrorKind::DuplicateFileName { .. } => None,
314 DescriptorErrorKind::FileNotFound { found, .. } => Some(found),
315 DescriptorErrorKind::InvalidImportIndex => None,
316 DescriptorErrorKind::InvalidOneofIndex => None,
317 DescriptorErrorKind::DuplicateName { second, .. } => Some(second),
318 DescriptorErrorKind::DuplicateFieldNumber { second, .. } => Some(second),
319 DescriptorErrorKind::DuplicateFieldJsonName { second, .. } => Some(second),
320 DescriptorErrorKind::DuplicateFieldCamelCaseName { second, .. } => Some(second),
321 DescriptorErrorKind::InvalidFieldNumber { found, .. } => Some(found),
322 DescriptorErrorKind::FieldNumberInReservedRange { found, .. } => Some(found),
323 DescriptorErrorKind::FieldNumberInExtensionRange { found, .. } => Some(found),
324 DescriptorErrorKind::ExtensionNumberOutOfRange { found, .. } => Some(found),
325 DescriptorErrorKind::NameNotFound { found, .. } => Some(found),
326 DescriptorErrorKind::NameShadowed { found, .. } => Some(found),
327 DescriptorErrorKind::InvalidType { found, .. } => Some(found),
328 DescriptorErrorKind::InvalidFieldDefault { found, .. } => Some(found),
329 DescriptorErrorKind::EmptyEnum { found } => Some(found),
330 DescriptorErrorKind::InvalidProto3EnumDefault { found } => Some(found),
331 DescriptorErrorKind::DuplicateEnumNumber { second, .. } => Some(second),
332 DescriptorErrorKind::EnumNumberInReservedRange { found, .. } => Some(found),
333 DescriptorErrorKind::OptionNotFound { found, .. } => Some(found),
334 DescriptorErrorKind::InvalidOptionType { found, .. } => Some(found),
335 DescriptorErrorKind::InvalidOptionExtendee { found, .. } => Some(found),
336 #[cfg(feature = "text-format")]
337 DescriptorErrorKind::InvalidMessageOption { found, .. } => Some(found),
338 DescriptorErrorKind::DuplicateOption { found, .. } => Some(found),
339 DescriptorErrorKind::DecodeFileDescriptorSet { .. } => None,
340 }
341 }
342
343 #[cfg(feature = "miette")]
344 fn add_source_code(&mut self, file: &str, source: &str) {
345 match self {
346 DescriptorErrorKind::MissingRequiredField { label } => {
347 label.resolve_span(file, source);
348 }
349 DescriptorErrorKind::UnknownSyntax { found, .. } => {
350 found.resolve_span(file, source);
351 }
352 DescriptorErrorKind::DuplicateFileName { .. } => {}
353 DescriptorErrorKind::FileNotFound { found, .. } => {
354 found.resolve_span(file, source);
355 }
356 DescriptorErrorKind::InvalidImportIndex => {}
357 DescriptorErrorKind::InvalidOneofIndex => {}
358 DescriptorErrorKind::DuplicateName { first, second, .. } => {
359 first.resolve_span(file, source);
360 second.resolve_span(file, source);
361 }
362 DescriptorErrorKind::DuplicateFieldNumber { first, second, .. } => {
363 first.resolve_span(file, source);
364 second.resolve_span(file, source);
365 }
366 DescriptorErrorKind::DuplicateFieldJsonName { first, second, .. } => {
367 first.resolve_span(file, source);
368 second.resolve_span(file, source);
369 }
370 DescriptorErrorKind::DuplicateFieldCamelCaseName { first, second, .. } => {
371 first.resolve_span(file, source);
372 second.resolve_span(file, source);
373 }
374 DescriptorErrorKind::InvalidFieldNumber { found, .. } => {
375 found.resolve_span(file, source);
376 }
377 DescriptorErrorKind::FieldNumberInReservedRange { defined, found, .. } => {
378 defined.resolve_span(file, source);
379 found.resolve_span(file, source);
380 }
381 DescriptorErrorKind::FieldNumberInExtensionRange { defined, found, .. } => {
382 defined.resolve_span(file, source);
383 found.resolve_span(file, source);
384 }
385 DescriptorErrorKind::ExtensionNumberOutOfRange { found, .. } => {
386 found.resolve_span(file, source);
387 }
388 DescriptorErrorKind::NameNotFound { found, .. } => {
389 found.resolve_span(file, source);
390 }
391 DescriptorErrorKind::NameShadowed { found, .. } => {
392 found.resolve_span(file, source);
393 }
394 DescriptorErrorKind::InvalidType { found, defined, .. } => {
395 found.resolve_span(file, source);
396 defined.resolve_span(file, source);
397 }
398 DescriptorErrorKind::InvalidFieldDefault { found, .. } => {
399 found.resolve_span(file, source);
400 }
401 DescriptorErrorKind::EmptyEnum { found } => {
402 found.resolve_span(file, source);
403 }
404 DescriptorErrorKind::InvalidProto3EnumDefault { found } => {
405 found.resolve_span(file, source);
406 }
407 DescriptorErrorKind::DuplicateEnumNumber { first, second, .. } => {
408 first.resolve_span(file, source);
409 second.resolve_span(file, source);
410 }
411 DescriptorErrorKind::EnumNumberInReservedRange { defined, found, .. } => {
412 found.resolve_span(file, source);
413 defined.resolve_span(file, source);
414 }
415 DescriptorErrorKind::OptionNotFound { found, .. } => {
416 found.resolve_span(file, source);
417 }
418 DescriptorErrorKind::InvalidOptionType { found, .. } => {
419 found.resolve_span(file, source);
420 }
421 DescriptorErrorKind::InvalidOptionExtendee { found, .. } => {
422 found.resolve_span(file, source);
423 }
424 #[cfg(feature = "text-format")]
425 DescriptorErrorKind::InvalidMessageOption { found, .. } => {
426 found.resolve_span(file, source);
427 }
428 DescriptorErrorKind::DuplicateOption { found, .. } => {
429 found.resolve_span(file, source);
430 }
431 DescriptorErrorKind::DecodeFileDescriptorSet { .. } => {}
432 }
433 }
434}
435
436impl std::error::Error for DescriptorErrorKind {
437 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
438 match self {
439 DescriptorErrorKind::DecodeFileDescriptorSet { err } => Some(err),
440 #[cfg(feature = "text-format")]
441 DescriptorErrorKind::InvalidMessageOption { err, .. } => Some(err),
442 _ => None,
443 }
444 }
445}
446
447impl fmt::Display for DescriptorErrorKind {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 match self {
450 DescriptorErrorKind::MissingRequiredField { label } => {
451 write!(f, "missing required field at {:?}", label.path)
452 }
453 DescriptorErrorKind::UnknownSyntax { syntax, .. } => {
454 write!(f, "unknown syntax '{syntax}'")
455 }
456 DescriptorErrorKind::DuplicateFileName { name, .. } => {
457 write!(f, "a different file named '{name}' has already been added")
458 }
459 DescriptorErrorKind::FileNotFound { name, .. } => {
460 write!(f, "imported file '{name}' has not been added")
461 }
462 DescriptorErrorKind::InvalidImportIndex => {
463 write!(f, "invalid import index")
464 }
465 DescriptorErrorKind::InvalidOneofIndex => {
466 write!(f, "invalid oneof index")
467 }
468 DescriptorErrorKind::DuplicateName {
469 name,
470 first,
471 second,
472 } => {
473 if first.file == second.file {
474 write!(f, "name '{name}' is defined twice")
475 } else {
476 write!(
477 f,
478 "name '{}' is already defined in file '{}'",
479 name, first.file
480 )
481 }
482 }
483 DescriptorErrorKind::DuplicateFieldNumber { number, .. } => {
484 write!(f, "field number '{number}' is already used")
485 }
486 DescriptorErrorKind::DuplicateFieldJsonName { name, .. } => {
487 write!(f, "a field with JSON name '{name}' is already defined")
488 }
489 DescriptorErrorKind::DuplicateFieldCamelCaseName {
490 first_name,
491 second_name,
492 ..
493 } => {
494 write!(
495 f,
496 "camel-case name of field '{first_name}' conflicts with field '{second_name}'"
497 )
498 }
499 DescriptorErrorKind::InvalidFieldNumber { number, .. } => {
500 write!(f, "invalid field number '{number}'")
501 }
502 DescriptorErrorKind::FieldNumberInReservedRange { number, range, .. } => {
503 write!(
504 f,
505 "field number '{}' conflicts with reserved range '{} to {}'",
506 number,
507 range.start,
508 range.end - 1
509 )
510 }
511 DescriptorErrorKind::FieldNumberInExtensionRange { number, range, .. } => {
512 write!(
513 f,
514 "field number '{}' conflicts with extension range '{} to {}'",
515 number,
516 range.start,
517 range.end - 1
518 )
519 }
520 DescriptorErrorKind::ExtensionNumberOutOfRange {
521 number, message, ..
522 } => {
523 write!(
524 f,
525 "message '{message}' does not define '{number}' as an extension number"
526 )
527 }
528 DescriptorErrorKind::NameNotFound { name, .. } => {
529 write!(f, "name '{name}' is not defined")
530 }
531 DescriptorErrorKind::NameShadowed {
532 name,
533 shadowed_name,
534 ..
535 } => {
536 write!(
537 f,
538 "'{name}' resolves to '{shadowed_name}', which is not defined",
539 )
540 }
541 DescriptorErrorKind::InvalidType { name, expected, .. } => {
542 write!(f, "'{name}' is not {expected}")
543 }
544 DescriptorErrorKind::InvalidFieldDefault { value, kind, .. } => {
545 write!(f, "invalid default value '{value}' for type '{kind}'")
546 }
547 DescriptorErrorKind::EmptyEnum { .. } => {
548 write!(f, "enums must have at least one value")
549 }
550 DescriptorErrorKind::InvalidProto3EnumDefault { .. } => {
551 write!(f, "the first value for proto3 enums must be 0")
552 }
553 DescriptorErrorKind::DuplicateEnumNumber { number, .. } => {
554 write!(f, "enum number '{number}' has already been used")
555 }
556 DescriptorErrorKind::EnumNumberInReservedRange { number, range, .. } => {
557 write!(
558 f,
559 "enum number '{}' conflicts with reserved range '{} to {}'",
560 number,
561 range.start(),
562 range.end()
563 )
564 }
565 DescriptorErrorKind::OptionNotFound { name, .. } => {
566 write!(f, "option field '{name}' is not defined")
567 }
568 DescriptorErrorKind::InvalidOptionType {
569 name,
570 ty,
571 value,
572 is_last,
573 ..
574 } => {
575 if *is_last {
576 write!(
577 f,
578 "expected a value of type '{ty}' for option '{name}', but found '{value}'"
579 )
580 } else {
581 write!(
582 f,
583 "cannot set field for option '{name}' value of type '{ty}'"
584 )
585 }
586 }
587 DescriptorErrorKind::InvalidOptionExtendee {
588 name,
589 expected_extendee,
590 actual_extendee,
591 ..
592 } => {
593 write!(
594 f,
595 "expected an extension to type '{expected_extendee}', but '{name}' extends '{actual_extendee}'"
596 )
597 }
598 #[cfg(feature = "text-format")]
599 DescriptorErrorKind::InvalidMessageOption { name, ty, .. } => {
600 write!(f, "invalid value of type '{ty}' for option '{name}'")
601 }
602 DescriptorErrorKind::DuplicateOption { name, .. } => {
603 write!(f, "option field '{name}' has already been set")
604 }
605 DescriptorErrorKind::DecodeFileDescriptorSet { .. } => {
606 write!(f, "failed to decode file descriptor set")
607 }
608 }
609 }
610}
611
612#[cfg(feature = "miette")]
613#[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
614impl miette::Diagnostic for DescriptorErrorKind {
615 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
616 use crate::descriptor::{RESERVED_MESSAGE_FIELD_NUMBERS, VALID_MESSAGE_FIELD_NUMBERS};
617
618 match self {
619 DescriptorErrorKind::MissingRequiredField { .. } => None,
620 DescriptorErrorKind::UnknownSyntax { .. } => {
621 Some(Box::new("valid values are 'proto2' and 'proto3'"))
622 }
623 DescriptorErrorKind::DuplicateFileName { .. } => None,
624 DescriptorErrorKind::FileNotFound { .. } => None,
625 DescriptorErrorKind::InvalidImportIndex => None,
626 DescriptorErrorKind::InvalidOneofIndex => None,
627 DescriptorErrorKind::DuplicateName { .. } => None,
628 DescriptorErrorKind::DuplicateFieldNumber { .. } => None,
629 DescriptorErrorKind::InvalidFieldNumber { number, .. } => {
630 if !VALID_MESSAGE_FIELD_NUMBERS.contains(number) {
631 Some(Box::new(format!(
632 "field numbers must be between {} and {}",
633 VALID_MESSAGE_FIELD_NUMBERS.start,
634 VALID_MESSAGE_FIELD_NUMBERS.end - 1
635 )))
636 } else if RESERVED_MESSAGE_FIELD_NUMBERS.contains(number) {
637 Some(Box::new(format!(
638 "field numbers {} to {} are reserved",
639 RESERVED_MESSAGE_FIELD_NUMBERS.start,
640 RESERVED_MESSAGE_FIELD_NUMBERS.end - 1
641 )))
642 } else {
643 None
644 }
645 }
646 DescriptorErrorKind::FieldNumberInReservedRange { .. } => None,
647 DescriptorErrorKind::FieldNumberInExtensionRange { .. } => None,
648 DescriptorErrorKind::DuplicateFieldJsonName { .. } => None,
649 DescriptorErrorKind::DuplicateFieldCamelCaseName { .. } => None,
650 DescriptorErrorKind::NameNotFound { help, .. }
651 | DescriptorErrorKind::NameShadowed { help, .. } => help
652 .as_ref()
653 .map(|h| -> Box<dyn fmt::Display> { Box::new(h.clone()) }),
654 DescriptorErrorKind::InvalidType { .. } => None,
655 DescriptorErrorKind::InvalidFieldDefault { .. } => None,
656 DescriptorErrorKind::EmptyEnum { .. } => None,
657 DescriptorErrorKind::InvalidProto3EnumDefault { .. } => None,
658 DescriptorErrorKind::DuplicateEnumNumber { .. } => Some(Box::new(
659 "set the 'allow_alias' option allow re-using enum numbers",
660 )),
661 DescriptorErrorKind::EnumNumberInReservedRange { .. } => None,
662 DescriptorErrorKind::OptionNotFound { .. } => None,
663 DescriptorErrorKind::InvalidOptionType { .. } => None,
664 DescriptorErrorKind::InvalidOptionExtendee { .. } => None,
665 #[cfg(feature = "text-format")]
666 DescriptorErrorKind::InvalidMessageOption { .. } => None,
667 DescriptorErrorKind::DuplicateOption { .. } => None,
668 DescriptorErrorKind::DecodeFileDescriptorSet { .. } => None,
669 DescriptorErrorKind::ExtensionNumberOutOfRange { .. } => None,
670 }
671 }
672
673 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
674 let mut spans = Vec::new();
675 match self {
676 DescriptorErrorKind::MissingRequiredField { label } => spans.extend(label.to_span()),
677 DescriptorErrorKind::UnknownSyntax { found: defined, .. } => {
678 spans.extend(defined.to_span());
679 }
680 DescriptorErrorKind::DuplicateFileName { .. } => {}
681 DescriptorErrorKind::FileNotFound { found, .. } => {
682 spans.extend(found.to_span());
683 }
684 DescriptorErrorKind::InvalidImportIndex => {}
685 DescriptorErrorKind::InvalidOneofIndex => {}
686 DescriptorErrorKind::DuplicateName { first, second, .. } => {
687 spans.extend(first.to_span());
688 spans.extend(second.to_span());
689 }
690 DescriptorErrorKind::DuplicateFieldNumber { first, second, .. } => {
691 spans.extend(first.to_span());
692 spans.extend(second.to_span());
693 }
694 DescriptorErrorKind::DuplicateFieldJsonName { first, second, .. } => {
695 spans.extend(first.to_span());
696 spans.extend(second.to_span());
697 }
698 DescriptorErrorKind::DuplicateFieldCamelCaseName { first, second, .. } => {
699 spans.extend(first.to_span());
700 spans.extend(second.to_span());
701 }
702 DescriptorErrorKind::NameNotFound { found, .. }
703 | DescriptorErrorKind::NameShadowed { found, .. } => {
704 spans.extend(found.to_span());
705 }
706 DescriptorErrorKind::InvalidFieldNumber { found, .. } => {
707 spans.extend(found.to_span());
708 }
709 DescriptorErrorKind::FieldNumberInReservedRange { defined, found, .. } => {
710 spans.extend(defined.to_span());
711 spans.extend(found.to_span());
712 }
713 DescriptorErrorKind::FieldNumberInExtensionRange { defined, found, .. } => {
714 spans.extend(defined.to_span());
715 spans.extend(found.to_span());
716 }
717 DescriptorErrorKind::ExtensionNumberOutOfRange { found, .. } => {
718 spans.extend(found.to_span());
719 }
720 DescriptorErrorKind::InvalidType { found, defined, .. } => {
721 spans.extend(found.to_span());
722 spans.extend(defined.to_span());
723 }
724 DescriptorErrorKind::InvalidFieldDefault { found, .. } => {
725 spans.extend(found.to_span());
726 }
727 DescriptorErrorKind::EmptyEnum { found } => {
728 spans.extend(found.to_span());
729 }
730 DescriptorErrorKind::InvalidProto3EnumDefault { found, .. } => {
731 spans.extend(found.to_span());
732 }
733 DescriptorErrorKind::DuplicateEnumNumber { first, second, .. } => {
734 spans.extend(first.to_span());
735 spans.extend(second.to_span());
736 }
737 DescriptorErrorKind::EnumNumberInReservedRange { defined, found, .. } => {
738 spans.extend(found.to_span());
739 spans.extend(defined.to_span());
740 }
741 DescriptorErrorKind::OptionNotFound { found, .. } => {
742 spans.extend(found.to_span());
743 }
744 DescriptorErrorKind::InvalidOptionType { found, .. } => {
745 spans.extend(found.to_span());
746 }
747 DescriptorErrorKind::InvalidOptionExtendee { found, .. } => {
748 spans.extend(found.to_span());
749 }
750 #[cfg(feature = "text-format")]
751 DescriptorErrorKind::InvalidMessageOption { found, .. } => {
752 spans.extend(found.to_span());
753 }
754 DescriptorErrorKind::DuplicateOption { found, .. } => {
755 spans.extend(found.to_span());
756 }
757 DescriptorErrorKind::DecodeFileDescriptorSet { .. } => {}
758 }
759 if spans.is_empty() {
760 None
761 } else {
762 Some(Box::new(spans.into_iter()))
763 }
764 }
765
766 fn diagnostic_source(&self) -> Option<&dyn miette::Diagnostic> {
767 match self {
768 #[cfg(feature = "text-format")]
769 DescriptorErrorKind::InvalidMessageOption { err, .. } => Some(err),
770 _ => None,
771 }
772 }
773}
774
775impl Label {
776 pub fn new(
777 files: &[FileDescriptorInner],
778 #[cfg_attr(not(feature = "miette"), allow(unused_variables))] message: impl ToString,
779 file: FileIndex,
780 path: Box<[i32]>,
781 ) -> Self {
782 let file = &files[file as usize].raw;
783
784 let span = file
785 .source_code_info
786 .as_ref()
787 .and_then(|s| s.location.iter().find(|l| *l.path == *path))
788 .and_then(|l| match *l.span {
789 [start_line, start_col, end_col] => {
790 Some([start_line, start_col, start_line, end_col])
791 }
792 [start_line, start_col, end_line, end_col] => {
793 Some([start_line, start_col, end_line, end_col])
794 }
795 _ => None,
796 });
797
798 Label {
799 file: file.name().to_owned(),
800 span,
801 path,
802 #[cfg(feature = "miette")]
803 message: message.to_string(),
804 #[cfg(feature = "miette")]
805 resolved: None,
806 }
807 }
808
809 #[cfg(feature = "miette")]
810 pub fn resolve_span(&mut self, file: &str, source: &str) {
811 if file == self.file {
812 if let Some([start_line, start_col, end_line, end_col]) = self.span {
813 let start = miette::SourceOffset::from_location(
814 source,
815 start_line.saturating_add(1) as _,
816 start_col.saturating_add(1) as _,
817 )
818 .offset();
819 let end = miette::SourceOffset::from_location(
820 source,
821 end_line.saturating_add(1) as _,
822 end_col.saturating_add(1) as _,
823 )
824 .offset();
825 self.resolved = Some(miette::SourceSpan::from(start..end));
826 }
827 }
828 }
829
830 #[cfg(feature = "miette")]
831 fn to_span(&self) -> Option<miette::LabeledSpan> {
832 match self.resolved {
833 Some(span) if !span.is_empty() => Some(miette::LabeledSpan::new_with_span(
834 Some(self.message.clone()),
835 span,
836 )),
837 _ => None,
838 }
839 }
840}