1use bigdecimal::BigDecimal;
2use codespan_reporting::diagnostic::{Diagnostic, Label};
3use derive_new::new;
4use getset::Getters;
5use nu_ansi_term::Color;
6use nu_source::{
7 DbgDocBldr, DebugDocBuilder, HasFallibleSpan, PrettyDebug, Span, Spanned, SpannedItem,
8};
9use num_bigint::BigInt;
10use num_traits::ToPrimitive;
11use serde::{Deserialize, Serialize};
12use std::fmt;
13use std::ops::Range;
14
15#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
19pub enum ParseErrorReason {
20 Eof { expected: String, span: Span },
22 ExtraTokens { actual: Spanned<String> },
25 Mismatch {
27 expected: String,
28 actual: Spanned<String>,
29 },
30
31 Unclosed { delimiter: String, span: Span },
33
34 GeneralError {
36 message: String,
37 label: Spanned<String>,
38 },
39
40 ArgumentError {
43 command: Spanned<String>,
44 error: ArgumentError,
45 },
46}
47
48#[derive(Debug, Clone, Getters, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
50pub struct ParseError {
51 #[get = "pub"]
52 reason: ParseErrorReason,
53}
54
55impl ParseError {
56 pub fn unexpected_eof(expected: impl Into<String>, span: Span) -> ParseError {
58 ParseError {
59 reason: ParseErrorReason::Eof {
60 expected: expected.into(),
61 span,
62 },
63 }
64 }
65
66 pub fn extra_tokens(actual: Spanned<impl Into<String>>) -> ParseError {
68 let Spanned { span, item } = actual;
69
70 ParseError {
71 reason: ParseErrorReason::ExtraTokens {
72 actual: item.into().spanned(span),
73 },
74 }
75 }
76
77 pub fn mismatch(expected: impl Into<String>, actual: Spanned<impl Into<String>>) -> ParseError {
79 let Spanned { span, item } = actual;
80
81 ParseError {
82 reason: ParseErrorReason::Mismatch {
83 expected: expected.into(),
84 actual: item.into().spanned(span),
85 },
86 }
87 }
88
89 pub fn general_error(
91 message: impl Into<String>,
92 label: Spanned<impl Into<String>>,
93 ) -> ParseError {
94 ParseError {
95 reason: ParseErrorReason::GeneralError {
96 message: message.into(),
97 label: label.item.into().spanned(label.span),
98 },
99 }
100 }
101
102 pub fn argument_error(command: Spanned<impl Into<String>>, kind: ArgumentError) -> ParseError {
104 ParseError {
105 reason: ParseErrorReason::ArgumentError {
106 command: command.item.into().spanned(command.span),
107 error: kind,
108 },
109 }
110 }
111
112 pub fn unclosed(delimiter: String, span: Span) -> ParseError {
114 ParseError {
115 reason: ParseErrorReason::Unclosed { delimiter, span },
116 }
117 }
118}
119
120impl From<ParseError> for ShellError {
122 fn from(error: ParseError) -> ShellError {
123 match error.reason {
124 ParseErrorReason::Eof { expected, span } => ShellError::unexpected_eof(expected, span),
125 ParseErrorReason::ExtraTokens { actual } => ShellError::type_error("nothing", actual),
126 ParseErrorReason::Mismatch { actual, expected } => {
127 ShellError::type_error(expected, actual)
128 }
129 ParseErrorReason::GeneralError { message, label } => {
130 ShellError::labeled_error(&message, &label.item, &label.span)
131 }
132 ParseErrorReason::ArgumentError { command, error } => {
133 ShellError::argument_error(command, error)
134 }
135 ParseErrorReason::Unclosed { delimiter, span } => ShellError::labeled_error(
136 "Unclosed delimiter",
137 format!("expected '{}'", delimiter),
138 span,
139 ),
140 }
141 }
142}
143
144#[derive(Debug, Eq, PartialEq, Clone, Ord, Hash, PartialOrd, Serialize, Deserialize)]
149pub enum ArgumentError {
150 MissingMandatoryFlag(String),
152 MissingMandatoryPositional(String),
154 MissingValueForName(String),
156 UnexpectedArgument(Spanned<String>),
158 UnexpectedFlag(Spanned<String>),
160 InvalidExternalWord,
163 BadValue(String),
165}
166
167impl PrettyDebug for ArgumentError {
168 fn pretty(&self) -> DebugDocBuilder {
169 match self {
170 ArgumentError::MissingMandatoryFlag(flag) => {
171 DbgDocBldr::description("missing `")
172 + DbgDocBldr::description(flag)
173 + DbgDocBldr::description("` as mandatory flag")
174 }
175 ArgumentError::UnexpectedArgument(name) => {
176 DbgDocBldr::description("unexpected `")
177 + DbgDocBldr::description(&name.item)
178 + DbgDocBldr::description("` is not supported")
179 }
180 ArgumentError::UnexpectedFlag(name) => {
181 DbgDocBldr::description("unexpected `")
182 + DbgDocBldr::description(&name.item)
183 + DbgDocBldr::description("` is not supported")
184 }
185 ArgumentError::MissingMandatoryPositional(pos) => {
186 DbgDocBldr::description("missing `")
187 + DbgDocBldr::description(pos)
188 + DbgDocBldr::description("` as mandatory positional argument")
189 }
190 ArgumentError::MissingValueForName(name) => {
191 DbgDocBldr::description("missing value for flag `")
192 + DbgDocBldr::description(name)
193 + DbgDocBldr::description("`")
194 }
195 ArgumentError::InvalidExternalWord => DbgDocBldr::description("invalid word"),
196 ArgumentError::BadValue(msg) => {
197 DbgDocBldr::description("bad value `")
198 + DbgDocBldr::description(msg)
199 + DbgDocBldr::description("`")
200 }
201 }
202 }
203}
204
205#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize, Hash)]
208pub struct ShellError {
209 pub error: ProximateShellError,
210 pub notes: Vec<String>,
211}
212
213impl PrettyDebug for ShellError {
216 fn pretty(&self) -> DebugDocBuilder {
217 match &self.error {
218 ProximateShellError::SyntaxError { problem } => {
219 DbgDocBldr::error("Syntax Error")
220 + DbgDocBldr::space()
221 + DbgDocBldr::delimit("(", DbgDocBldr::description(&problem.item), ")")
222 }
223 ProximateShellError::UnexpectedEof { .. } => DbgDocBldr::error("Unexpected end"),
224 ProximateShellError::TypeError { expected, actual } => {
225 DbgDocBldr::error("Type Error")
226 + DbgDocBldr::space()
227 + DbgDocBldr::delimit(
228 "(",
229 DbgDocBldr::description("expected:")
230 + DbgDocBldr::space()
231 + DbgDocBldr::description(expected)
232 + DbgDocBldr::description(",")
233 + DbgDocBldr::space()
234 + DbgDocBldr::description("actual:")
235 + DbgDocBldr::space()
236 + DbgDocBldr::option(actual.item.as_ref().map(DbgDocBldr::description)),
237 ")",
238 )
239 }
240 ProximateShellError::MissingProperty { subpath, expr } => {
241 DbgDocBldr::error("Missing Property")
242 + DbgDocBldr::space()
243 + DbgDocBldr::delimit(
244 "(",
245 DbgDocBldr::description("expr:")
246 + DbgDocBldr::space()
247 + DbgDocBldr::description(&expr.item)
248 + DbgDocBldr::description(",")
249 + DbgDocBldr::space()
250 + DbgDocBldr::description("subpath:")
251 + DbgDocBldr::space()
252 + DbgDocBldr::description(&subpath.item),
253 ")",
254 )
255 }
256 ProximateShellError::InvalidIntegerIndex { subpath, .. } => {
257 DbgDocBldr::error("Invalid integer index")
258 + DbgDocBldr::space()
259 + DbgDocBldr::delimit(
260 "(",
261 DbgDocBldr::description("subpath:")
262 + DbgDocBldr::space()
263 + DbgDocBldr::description(&subpath.item),
264 ")",
265 )
266 }
267 ProximateShellError::MissingValue { reason, .. } => {
268 DbgDocBldr::error("Missing Value")
269 + DbgDocBldr::space()
270 + DbgDocBldr::delimit(
271 "(",
272 DbgDocBldr::description("reason:")
273 + DbgDocBldr::space()
274 + DbgDocBldr::description(reason),
275 ")",
276 )
277 }
278 ProximateShellError::ArgumentError { command, error } => {
279 DbgDocBldr::error("Argument Error")
280 + DbgDocBldr::space()
281 + DbgDocBldr::delimit(
282 "(",
283 DbgDocBldr::description("command:")
284 + DbgDocBldr::space()
285 + DbgDocBldr::description(&command.item)
286 + DbgDocBldr::description(",")
287 + DbgDocBldr::space()
288 + DbgDocBldr::description("error:")
289 + DbgDocBldr::space()
290 + error.pretty(),
291 ")",
292 )
293 }
294 ProximateShellError::RangeError {
295 kind,
296 actual_kind,
297 operation,
298 } => {
299 DbgDocBldr::error("Range Error")
300 + DbgDocBldr::space()
301 + DbgDocBldr::delimit(
302 "(",
303 DbgDocBldr::description("expected:")
304 + DbgDocBldr::space()
305 + kind.pretty()
306 + DbgDocBldr::description(",")
307 + DbgDocBldr::space()
308 + DbgDocBldr::description("actual:")
309 + DbgDocBldr::space()
310 + DbgDocBldr::description(&actual_kind.item)
311 + DbgDocBldr::description(",")
312 + DbgDocBldr::space()
313 + DbgDocBldr::description("operation:")
314 + DbgDocBldr::space()
315 + DbgDocBldr::description(operation),
316 ")",
317 )
318 }
319 ProximateShellError::Diagnostic(_) => DbgDocBldr::error("diagnostic"),
320 ProximateShellError::CoerceError { left, right } => {
321 DbgDocBldr::error("Coercion Error")
322 + DbgDocBldr::space()
323 + DbgDocBldr::delimit(
324 "(",
325 DbgDocBldr::description("left:")
326 + DbgDocBldr::space()
327 + DbgDocBldr::description(&left.item)
328 + DbgDocBldr::description(",")
329 + DbgDocBldr::space()
330 + DbgDocBldr::description("right:")
331 + DbgDocBldr::space()
332 + DbgDocBldr::description(&right.item),
333 ")",
334 )
335 }
336 ProximateShellError::UntaggedRuntimeError { reason } => {
337 DbgDocBldr::error("Unknown Error")
338 + DbgDocBldr::delimit("(", DbgDocBldr::description(reason), ")")
339 }
340 ProximateShellError::Unimplemented { reason } => {
341 DbgDocBldr::error("Unimplemented")
342 + DbgDocBldr::delimit("(", DbgDocBldr::description(reason), ")")
343 }
344 }
345 }
346}
347
348impl std::fmt::Display for ShellError {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 write!(f, "{}", self.pretty().display())
351 }
352}
353
354impl serde::de::Error for ShellError {
355 fn custom<T>(msg: T) -> Self
356 where
357 T: std::fmt::Display,
358 {
359 ShellError::untagged_runtime_error(msg.to_string())
360 }
361}
362
363impl ShellError {
364 pub fn type_error(
366 expected: impl Into<String>,
367 actual: Spanned<impl Into<String>>,
368 ) -> ShellError {
369 ProximateShellError::TypeError {
370 expected: expected.into(),
371 actual: actual.map(|i| Some(i.into())),
372 }
373 .start()
374 }
375
376 pub fn missing_property(
377 subpath: Spanned<impl Into<String>>,
378 expr: Spanned<impl Into<String>>,
379 ) -> ShellError {
380 ProximateShellError::MissingProperty {
381 subpath: subpath.map(|s| s.into()),
382 expr: expr.map(|e| e.into()),
383 }
384 .start()
385 }
386
387 pub fn missing_value(span: impl Into<Option<Span>>, reason: impl Into<String>) -> ShellError {
388 ProximateShellError::MissingValue {
389 span: span.into(),
390 reason: reason.into(),
391 }
392 .start()
393 }
394
395 pub fn invalid_integer_index(
396 subpath: Spanned<impl Into<String>>,
397 integer: impl Into<Span>,
398 ) -> ShellError {
399 ProximateShellError::InvalidIntegerIndex {
400 subpath: subpath.map(|s| s.into()),
401 integer: integer.into(),
402 }
403 .start()
404 }
405
406 pub fn untagged_runtime_error(error: impl Into<String>) -> ShellError {
407 ProximateShellError::UntaggedRuntimeError {
408 reason: error.into(),
409 }
410 .start()
411 }
412
413 pub fn unexpected_eof(expected: impl Into<String>, span: impl Into<Span>) -> ShellError {
414 ProximateShellError::UnexpectedEof {
415 expected: expected.into(),
416 span: span.into(),
417 }
418 .start()
419 }
420
421 pub fn range_error(
422 expected: impl Into<ExpectedRange>,
423 actual: &Spanned<impl fmt::Debug>,
424 operation: impl Into<String>,
425 ) -> ShellError {
426 ProximateShellError::RangeError {
427 kind: expected.into(),
428 actual_kind: format!("{:?}", actual.item).spanned(actual.span),
429 operation: operation.into(),
430 }
431 .start()
432 }
433
434 pub fn syntax_error(problem: Spanned<impl Into<String>>) -> ShellError {
435 ProximateShellError::SyntaxError {
436 problem: problem.map(|p| p.into()),
437 }
438 .start()
439 }
440
441 pub fn coerce_error(
442 left: Spanned<impl Into<String>>,
443 right: Spanned<impl Into<String>>,
444 ) -> ShellError {
445 ProximateShellError::CoerceError {
446 left: left.map(|l| l.into()),
447 right: right.map(|r| r.into()),
448 }
449 .start()
450 }
451
452 pub fn argument_error(command: Spanned<impl Into<String>>, kind: ArgumentError) -> ShellError {
453 ProximateShellError::ArgumentError {
454 command: command.map(|c| c.into()),
455 error: kind,
456 }
457 .start()
458 }
459
460 pub fn diagnostic(diagnostic: Diagnostic<usize>) -> ShellError {
461 ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start()
462 }
463
464 pub fn into_diagnostic(self) -> Diagnostic<usize> {
465 let d = match self.error {
466 ProximateShellError::MissingValue { span, reason } => {
467 let mut d = Diagnostic::bug().with_message(format!("Internal Error (missing value) :: {}", reason));
468
469 if let Some(span) = span {
470 d = d.with_labels(vec![Label::primary(0, span)]);
471 }
472
473 d
474 }
475 ProximateShellError::ArgumentError {
476 command,
477 error,
478 } => match error {
479 ArgumentError::InvalidExternalWord => Diagnostic::error().with_message("Invalid bare word for Nu command (did you intend to invoke an external command?)")
480 .with_labels(vec![Label::primary(0, command.span)]),
481 ArgumentError::UnexpectedArgument(argument) => Diagnostic::error().with_message(
482 format!(
483 "{} unexpected {}",
484 Color::Cyan.paint(&command.item),
485 Color::Green.bold().paint(&argument.item)
486 )
487 )
488 .with_labels(
489 vec![Label::primary(0, argument.span).with_message(
490 format!("unexpected argument (try {} -h)", &command.item))]
491 ),
492 ArgumentError::UnexpectedFlag(flag) => Diagnostic::error().with_message(
493 format!(
494 "{} unexpected {}",
495 Color::Cyan.paint(&command.item),
496 Color::Green.bold().paint(&flag.item)
497 ),
498 )
499 .with_labels(vec![
500 Label::primary(0, flag.span).with_message(
501 format!("unexpected flag (try {} -h)", &command.item))
502 ]),
503 ArgumentError::MissingMandatoryFlag(name) => Diagnostic::error().with_message( format!(
504 "{} requires {}{}",
505 Color::Cyan.paint(&command.item),
506 Color::Green.bold().paint("--"),
507 Color::Green.bold().paint(name)
508 ),
509 )
510 .with_labels(vec![Label::primary(0, command.span)]),
511 ArgumentError::MissingMandatoryPositional(name) => Diagnostic::error().with_message(
512 format!(
513 "{} requires {} parameter",
514 Color::Cyan.paint(&command.item),
515 Color::Green.bold().paint(name.clone())
516 ),
517 )
518 .with_labels(
519 vec![Label::primary(0, command.span).with_message(format!("requires {} parameter", name))],
520 ),
521 ArgumentError::MissingValueForName(name) => Diagnostic::error().with_message(
522 format!(
523 "{} is missing value for flag {}{}",
524 Color::Cyan.paint(&command.item),
525 Color::Green.bold().paint("--"),
526 Color::Green.bold().paint(name)
527 ),
528 )
529 .with_labels(vec![Label::primary(0, command.span)]),
530 ArgumentError::BadValue(msg) => Diagnostic::error().with_message(msg.clone()).with_labels(vec![Label::primary(0, command.span).with_message(msg)])
531 },
532 ProximateShellError::TypeError {
533 expected,
534 actual:
535 Spanned {
536 item: Some(actual),
537 span,
538 },
539 } => Diagnostic::error().with_message("Type Error").with_labels(
540 vec![Label::primary(0, span)
541 .with_message(format!("Expected {}, found {}", expected, actual))],
542 ),
543 ProximateShellError::TypeError {
544 expected,
545 actual:
546 Spanned {
547 item: None,
548 span
549 },
550 } => Diagnostic::error().with_message("Type Error")
551 .with_labels(vec![Label::primary(0, span).with_message(expected)]),
552
553 ProximateShellError::UnexpectedEof {
554 expected, span
555 } => Diagnostic::error().with_message("Unexpected end of input")
556 .with_labels(vec![Label::primary(0, span).with_message(format!("Expected {}", expected))]),
557
558 ProximateShellError::RangeError {
559 kind,
560 operation,
561 actual_kind:
562 Spanned {
563 item,
564 span
565 },
566 } => Diagnostic::error().with_message("Range Error").with_labels(
567 vec![Label::primary(0, span).with_message(format!(
568 "Expected to convert {} to {} while {}, but it was out of range",
569 item,
570 kind.display(),
571 operation
572 ))],
573 ),
574
575 ProximateShellError::SyntaxError {
576 problem:
577 Spanned {
578 span,
579 item
580 },
581 } => Diagnostic::error().with_message("Syntax Error")
582 .with_labels(vec![Label::primary(0, span).with_message(item)]),
583
584 ProximateShellError::MissingProperty { subpath, expr, .. } => {
585
586 let mut diag = Diagnostic::error().with_message("Missing property");
587
588 if subpath.span == Span::unknown() {
589 diag.message = format!("Missing property (for {})", subpath.item);
590 } else {
591 let subpath = Label::primary(0, subpath.span).with_message(subpath.item);
592 let mut labels = vec![subpath];
593
594 if expr.span != Span::unknown() {
595 let expr = Label::primary(0, expr.span).with_message(expr.item);
596 labels.push(expr);
597 }
598 diag = diag.with_labels(labels);
599 }
600
601 diag
602 }
603
604 ProximateShellError::InvalidIntegerIndex { subpath,integer } => {
605 let mut diag = Diagnostic::error().with_message("Invalid integer property");
606 let mut labels = vec![];
607 if subpath.span == Span::unknown() {
608 diag.message = format!("Invalid integer property (for {})", subpath.item)
609 } else {
610 let label = Label::primary(0, subpath.span).with_message(subpath.item);
611 labels.push(label);
612 }
613
614 labels.push(Label::secondary(0, integer).with_message("integer"));
615 diag = diag.with_labels(labels);
616
617 diag
618 }
619
620 ProximateShellError::Diagnostic(diag) => diag.diagnostic,
621 ProximateShellError::CoerceError { left, right } => {
622 Diagnostic::error().with_message("Coercion error")
623 .with_labels(vec![Label::primary(0, left.span).with_message(left.item),
624 Label::secondary(0, right.span).with_message(right.item)])
625 }
626
627 ProximateShellError::UntaggedRuntimeError { reason } => Diagnostic::error().with_message(format!("Error: {}", reason)),
628 ProximateShellError::Unimplemented { reason } => Diagnostic::error().with_message(format!("Unimplemented: {}", reason)),
629
630 };
631
632 let notes = self.notes.clone();
633 d.with_notes(notes)
634 }
635
636 pub fn labeled_error(
637 msg: impl Into<String>,
638 label: impl Into<String>,
639 span: impl Into<Span>,
640 ) -> ShellError {
641 ShellError::diagnostic(
642 Diagnostic::error()
643 .with_message(msg.into())
644 .with_labels(vec![
645 Label::primary(0, span.into()).with_message(label.into())
646 ]),
647 )
648 }
649
650 pub fn labeled_error_with_secondary(
651 msg: impl Into<String>,
652 primary_label: impl Into<String>,
653 primary_span: impl Into<Span>,
654 secondary_label: impl Into<String>,
655 secondary_span: impl Into<Span>,
656 ) -> ShellError {
657 ShellError::diagnostic(
658 Diagnostic::error()
659 .with_message(msg.into())
660 .with_labels(vec![
661 Label::primary(0, primary_span.into()).with_message(primary_label.into()),
662 Label::secondary(0, secondary_span.into()).with_message(secondary_label.into()),
663 ]),
664 )
665 }
666
667 pub fn unimplemented(title: impl Into<String>) -> ShellError {
668 ShellError::untagged_runtime_error(&format!("Unimplemented: {}", title.into()))
669 }
670
671 pub fn unexpected(title: impl Into<String>) -> ShellError {
672 ShellError::untagged_runtime_error(&format!("Unexpected: {}", title.into()))
673 }
674
675 pub fn is_unimplemented(&self) -> bool {
676 matches!(self.error, ProximateShellError::Unimplemented { .. })
677 }
678}
679
680#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Serialize, Deserialize)]
685pub enum ExpectedRange {
686 I8,
687 I16,
688 I32,
689 I64,
690 I128,
691 U8,
692 U16,
693 U32,
694 U64,
695 U128,
696 F32,
697 F64,
698 Usize,
699 Size,
700 BigInt,
701 BigDecimal,
702 Range { start: usize, end: usize },
703}
704
705impl From<Range<usize>> for ExpectedRange {
707 fn from(range: Range<usize>) -> Self {
708 ExpectedRange::Range {
709 start: range.start,
710 end: range.end,
711 }
712 }
713}
714
715impl PrettyDebug for ExpectedRange {
716 fn pretty(&self) -> DebugDocBuilder {
717 DbgDocBldr::description(match self {
718 ExpectedRange::I8 => "an 8-bit signed integer",
719 ExpectedRange::I16 => "a 16-bit signed integer",
720 ExpectedRange::I32 => "a 32-bit signed integer",
721 ExpectedRange::I64 => "a 64-bit signed integer",
722 ExpectedRange::I128 => "a 128-bit signed integer",
723 ExpectedRange::U8 => "an 8-bit unsigned integer",
724 ExpectedRange::U16 => "a 16-bit unsigned integer",
725 ExpectedRange::U32 => "a 32-bit unsigned integer",
726 ExpectedRange::U64 => "a 64-bit unsigned integer",
727 ExpectedRange::U128 => "a 128-bit unsigned integer",
728 ExpectedRange::F32 => "a 32-bit float",
729 ExpectedRange::F64 => "a 64-bit float",
730 ExpectedRange::Usize => "an list index",
731 ExpectedRange::Size => "a list offset",
732 ExpectedRange::BigDecimal => "a decimal",
733 ExpectedRange::BigInt => "an integer",
734 ExpectedRange::Range { start, end } => {
735 return DbgDocBldr::description(format!("{} to {}", start, end))
736 }
737 })
738 }
739}
740
741#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize, Hash)]
742pub enum ProximateShellError {
743 SyntaxError {
744 problem: Spanned<String>,
745 },
746 UnexpectedEof {
747 expected: String,
748 span: Span,
749 },
750 TypeError {
751 expected: String,
752 actual: Spanned<Option<String>>,
753 },
754 MissingProperty {
755 subpath: Spanned<String>,
756 expr: Spanned<String>,
757 },
758 InvalidIntegerIndex {
759 subpath: Spanned<String>,
760 integer: Span,
761 },
762 MissingValue {
763 span: Option<Span>,
764 reason: String,
765 },
766 ArgumentError {
767 command: Spanned<String>,
768 error: ArgumentError,
769 },
770 RangeError {
771 kind: ExpectedRange,
772 actual_kind: Spanned<String>,
773 operation: String,
774 },
775 Diagnostic(ShellDiagnostic),
776 CoerceError {
777 left: Spanned<String>,
778 right: Spanned<String>,
779 },
780 UntaggedRuntimeError {
781 reason: String,
782 },
783 Unimplemented {
784 reason: String,
785 },
786}
787
788impl ProximateShellError {
789 fn start(self) -> ShellError {
790 ShellError {
791 error: self,
792 notes: vec![],
793 }
794 }
795}
796
797impl HasFallibleSpan for ShellError {
798 fn maybe_span(&self) -> Option<Span> {
799 self.error.maybe_span()
800 }
801}
802
803impl HasFallibleSpan for ProximateShellError {
804 fn maybe_span(&self) -> Option<Span> {
805 Some(match self {
806 ProximateShellError::SyntaxError { problem } => problem.span,
807 ProximateShellError::UnexpectedEof { span, .. } => *span,
808 ProximateShellError::TypeError { actual, .. } => actual.span,
809 ProximateShellError::MissingProperty { subpath, .. } => subpath.span,
810 ProximateShellError::InvalidIntegerIndex { subpath, .. } => subpath.span,
811 ProximateShellError::MissingValue { span, .. } => return *span,
812 ProximateShellError::ArgumentError { command, .. } => command.span,
813 ProximateShellError::RangeError { actual_kind, .. } => actual_kind.span,
814 ProximateShellError::Diagnostic(_) => return None,
815 ProximateShellError::CoerceError { left, right } => left.span.until(right.span),
816 ProximateShellError::UntaggedRuntimeError { .. } => return None,
817 ProximateShellError::Unimplemented { .. } => return None,
818 })
819 }
820}
821
822#[derive(Debug, Clone, Serialize, Deserialize)]
823pub struct ShellDiagnostic {
824 pub diagnostic: Diagnostic<usize>,
825}
826
827impl std::hash::Hash for ShellDiagnostic {
828 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
829 self.diagnostic.severity.hash(state);
830 self.diagnostic.code.hash(state);
831 self.diagnostic.message.hash(state);
832
833 for label in &self.diagnostic.labels {
834 label.range.hash(state);
835 label.message.hash(state);
836 match label.style {
837 codespan_reporting::diagnostic::LabelStyle::Primary => 0.hash(state),
838 codespan_reporting::diagnostic::LabelStyle::Secondary => 1.hash(state),
839 }
840 }
841 }
842}
843
844impl PartialEq for ShellDiagnostic {
845 fn eq(&self, _other: &ShellDiagnostic) -> bool {
846 false
847 }
848}
849
850impl Eq for ShellDiagnostic {}
851
852impl std::cmp::PartialOrd for ShellDiagnostic {
853 fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
854 Some(std::cmp::Ordering::Less)
855 }
856}
857
858impl std::cmp::Ord for ShellDiagnostic {
859 fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
860 std::cmp::Ordering::Less
861 }
862}
863
864#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone, Serialize, Deserialize)]
865pub struct StringError {
866 title: String,
867 error: String,
868}
869
870impl std::error::Error for ShellError {}
871
872impl std::convert::From<Box<dyn std::error::Error>> for ShellError {
873 fn from(input: Box<dyn std::error::Error>) -> ShellError {
874 ShellError::untagged_runtime_error(input.to_string())
875 }
876}
877
878impl std::convert::From<std::io::Error> for ShellError {
879 fn from(input: std::io::Error) -> ShellError {
880 ShellError::untagged_runtime_error(input.to_string())
881 }
882}
883
884impl std::convert::From<std::string::FromUtf8Error> for ShellError {
885 fn from(input: std::string::FromUtf8Error) -> ShellError {
886 ShellError::untagged_runtime_error(input.to_string())
887 }
888}
889
890impl std::convert::From<std::str::Utf8Error> for ShellError {
891 fn from(input: std::str::Utf8Error) -> ShellError {
892 ShellError::untagged_runtime_error(input.to_string())
893 }
894}
895
896impl std::convert::From<serde_yaml::Error> for ShellError {
897 fn from(input: serde_yaml::Error) -> ShellError {
898 ShellError::untagged_runtime_error(format!("{:?}", input))
899 }
900}
901
902impl std::convert::From<toml::ser::Error> for ShellError {
903 fn from(input: toml::ser::Error) -> ShellError {
904 ShellError::untagged_runtime_error(format!("{:?}", input))
905 }
906}
907
908impl std::convert::From<serde_json::Error> for ShellError {
909 fn from(input: serde_json::Error) -> ShellError {
910 ShellError::untagged_runtime_error(format!("{:?}", input))
911 }
912}
913
914impl std::convert::From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
915 fn from(input: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
916 ShellError::untagged_runtime_error(format!("{:?}", input))
917 }
918}
919
920impl std::convert::From<glob::PatternError> for ShellError {
921 fn from(input: glob::PatternError) -> ShellError {
922 ShellError::untagged_runtime_error(format!("{:?}", input))
923 }
924}
925
926pub trait CoerceInto<U> {
927 fn coerce_into(self, operation: impl Into<String>) -> Result<U, ShellError>;
928}
929
930trait ToExpectedRange {
931 fn to_expected_range() -> ExpectedRange;
932}
933
934macro_rules! ranged_int {
935 ($ty:tt -> $op:tt -> $variant:tt) => {
936 impl ToExpectedRange for $ty {
937 fn to_expected_range() -> ExpectedRange {
938 ExpectedRange::$variant
939 }
940 }
941
942 impl CoerceInto<$ty> for nu_source::Tagged<BigInt> {
943 fn coerce_into(self, operation: impl Into<String>) -> Result<$ty, ShellError> {
944 self.$op().ok_or_else(|| {
945 ShellError::range_error(
946 $ty::to_expected_range(),
947 &self.item.spanned(self.tag.span),
948 operation.into(),
949 )
950 })
951 }
952 }
953
954 impl CoerceInto<$ty> for nu_source::Tagged<&BigInt> {
955 fn coerce_into(self, operation: impl Into<String>) -> Result<$ty, ShellError> {
956 self.$op().ok_or_else(|| {
957 ShellError::range_error(
958 $ty::to_expected_range(),
959 &self.item.spanned(self.tag.span),
960 operation.into(),
961 )
962 })
963 }
964 }
965 };
966}
967
968ranged_int!(u8 -> to_u8 -> U8);
969ranged_int!(u16 -> to_u16 -> U16);
970ranged_int!(u32 -> to_u32 -> U32);
971ranged_int!(u64 -> to_u64 -> U64);
972ranged_int!(i8 -> to_i8 -> I8);
973ranged_int!(i16 -> to_i16 -> I16);
974ranged_int!(i32 -> to_i32 -> I32);
975ranged_int!(i64 -> to_i64 -> I64);
976
977macro_rules! ranged_decimal {
978 ($ty:tt -> $op:tt -> $variant:tt) => {
979 impl ToExpectedRange for $ty {
980 fn to_expected_range() -> ExpectedRange {
981 ExpectedRange::$variant
982 }
983 }
984
985 impl CoerceInto<$ty> for nu_source::Tagged<BigDecimal> {
986 fn coerce_into(self, operation: impl Into<String>) -> Result<$ty, ShellError> {
987 self.$op().ok_or_else(|| {
988 ShellError::range_error(
989 $ty::to_expected_range(),
990 &self.item.spanned(self.tag.span),
991 operation.into(),
992 )
993 })
994 }
995 }
996
997 impl CoerceInto<$ty> for nu_source::Tagged<&BigDecimal> {
998 fn coerce_into(self, operation: impl Into<String>) -> Result<$ty, ShellError> {
999 self.$op().ok_or_else(|| {
1000 ShellError::range_error(
1001 $ty::to_expected_range(),
1002 &self.item.spanned(self.tag.span),
1003 operation.into(),
1004 )
1005 })
1006 }
1007 }
1008 };
1009}
1010
1011ranged_decimal!(f32 -> to_f32 -> F32);
1012ranged_decimal!(f64 -> to_f64 -> F64);