| #![feature(error_generic_member_access, error_reporter)] | |
| use std::backtrace::Backtrace; | |
| use std::error::{Error, Report, Request}; | |
| use std::fmt; | |
| #[derive(Debug, PartialEq)] | |
| struct A; | |
| #[derive(Debug, PartialEq)] | |
| struct B; | |
| impl fmt::Display for A { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "A") | |
| } | |
| } | |
| impl fmt::Display for B { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "B") | |
| } | |
| } | |
| impl Error for A {} | |
| impl Error for B {} | |
| #[test] | |
| fn downcasting() { | |
| let mut a = A; | |
| let a = &mut a as &mut (dyn Error + 'static); | |
| assert_eq!(a.downcast_ref::<A>(), Some(&A)); | |
| assert_eq!(a.downcast_ref::<B>(), None); | |
| assert_eq!(a.downcast_mut::<A>(), Some(&mut A)); | |
| assert_eq!(a.downcast_mut::<B>(), None); | |
| let a: Box<dyn Error> = Box::new(A); | |
| match a.downcast::<B>() { | |
| Ok(..) => panic!("expected error"), | |
| Err(e) => assert_eq!(*e.downcast::<A>().unwrap(), A), | |
| } | |
| } | |
| #[derive(Debug)] | |
| struct SuperError { | |
| source: SuperErrorSideKick, | |
| } | |
| impl fmt::Display for SuperError { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "SuperError is here!") | |
| } | |
| } | |
| impl Error for SuperError { | |
| fn source(&self) -> Option<&(dyn Error + 'static)> { | |
| Some(&self.source) | |
| } | |
| } | |
| #[derive(Debug)] | |
| struct SuperErrorSideKick; | |
| impl fmt::Display for SuperErrorSideKick { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "SuperErrorSideKick is here!") | |
| } | |
| } | |
| impl Error for SuperErrorSideKick {} | |
| #[test] | |
| fn single_line_formatting() { | |
| let error = SuperError { source: SuperErrorSideKick }; | |
| let report = Report::new(&error); | |
| let actual = report.to_string(); | |
| let expected = String::from("SuperError is here!: SuperErrorSideKick is here!"); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn multi_line_formatting() { | |
| let error = SuperError { source: SuperErrorSideKick }; | |
| let report = Report::new(&error).pretty(true); | |
| let actual = report.to_string(); | |
| let expected = String::from( | |
| "\ | |
| SuperError is here! | |
| Caused by: | |
| SuperErrorSideKick is here!", | |
| ); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn error_with_no_sources_formats_single_line_correctly() { | |
| let report = Report::new(SuperErrorSideKick); | |
| let actual = report.to_string(); | |
| let expected = String::from("SuperErrorSideKick is here!"); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn error_with_no_sources_formats_multi_line_correctly() { | |
| let report = Report::new(SuperErrorSideKick).pretty(true); | |
| let actual = report.to_string(); | |
| let expected = String::from("SuperErrorSideKick is here!"); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn error_with_backtrace_outputs_correctly_with_one_source() { | |
| let trace = Backtrace::force_capture(); | |
| let expected = format!( | |
| "\ | |
| The source of the error | |
| Caused by: | |
| Error with backtrace | |
| Stack backtrace: | |
| {}", | |
| trace | |
| ); | |
| let error = GenericError::new("Error with backtrace"); | |
| let mut error = GenericError::new_with_source("The source of the error", error); | |
| error.backtrace = Some(trace); | |
| let report = Report::new(error).pretty(true).show_backtrace(true); | |
| println!("Error: {report}"); | |
| assert_eq!(expected.trim_end(), report.to_string()); | |
| } | |
| #[test] | |
| fn error_with_backtrace_outputs_correctly_with_two_sources() { | |
| let trace = Backtrace::force_capture(); | |
| let expected = format!( | |
| "\ | |
| Error with two sources | |
| Caused by: | |
| 0: The source of the error | |
| 1: Error with backtrace | |
| Stack backtrace: | |
| {}", | |
| trace | |
| ); | |
| let mut error = GenericError::new("Error with backtrace"); | |
| error.backtrace = Some(trace); | |
| let error = GenericError::new_with_source("The source of the error", error); | |
| let error = GenericError::new_with_source("Error with two sources", error); | |
| let report = Report::new(error).pretty(true).show_backtrace(true); | |
| println!("Error: {report}"); | |
| assert_eq!(expected.trim_end(), report.to_string()); | |
| } | |
| #[derive(Debug)] | |
| struct GenericError<D> { | |
| message: D, | |
| backtrace: Option<Backtrace>, | |
| source: Option<Box<dyn Error + 'static>>, | |
| } | |
| impl<D> GenericError<D> { | |
| fn new(message: D) -> GenericError<D> { | |
| Self { message, backtrace: None, source: None } | |
| } | |
| fn new_with_source<E>(message: D, source: E) -> GenericError<D> | |
| where | |
| E: Error + 'static, | |
| { | |
| let source: Box<dyn Error + 'static> = Box::new(source); | |
| let source = Some(source); | |
| GenericError { message, backtrace: None, source } | |
| } | |
| } | |
| impl<D> fmt::Display for GenericError<D> | |
| where | |
| D: fmt::Display, | |
| { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| fmt::Display::fmt(&self.message, f) | |
| } | |
| } | |
| impl<D> Error for GenericError<D> | |
| where | |
| D: fmt::Debug + fmt::Display, | |
| { | |
| fn source(&self) -> Option<&(dyn Error + 'static)> { | |
| self.source.as_deref() | |
| } | |
| fn provide<'a>(&'a self, req: &mut Request<'a>) { | |
| self.backtrace.as_ref().map(|bt| req.provide_ref::<Backtrace>(bt)); | |
| } | |
| } | |
| #[test] | |
| fn error_formats_single_line_with_rude_display_impl() { | |
| #[derive(Debug)] | |
| struct MyMessage; | |
| impl fmt::Display for MyMessage { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str("line 1\nline 2")?; | |
| f.write_str("\nline 3\nline 4\n")?; | |
| f.write_str("line 5\nline 6")?; | |
| Ok(()) | |
| } | |
| } | |
| let error = GenericError::new(MyMessage); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let report = Report::new(error); | |
| let expected = "\ | |
| line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6: line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6: line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6: line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6"; | |
| let actual = report.to_string(); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn error_formats_multi_line_with_rude_display_impl() { | |
| #[derive(Debug)] | |
| struct MyMessage; | |
| impl fmt::Display for MyMessage { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str("line 1\nline 2")?; | |
| f.write_str("\nline 3\nline 4\n")?; | |
| f.write_str("line 5\nline 6")?; | |
| Ok(()) | |
| } | |
| } | |
| let error = GenericError::new(MyMessage); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let report = Report::new(error).pretty(true); | |
| let expected = "line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6 | |
| Caused by: | |
| 0: line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6 | |
| 1: line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6 | |
| 2: line 1 | |
| line 2 | |
| line 3 | |
| line 4 | |
| line 5 | |
| line 6"; | |
| let actual = report.to_string(); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn errors_that_start_with_newline_formats_correctly() { | |
| #[derive(Debug)] | |
| struct MyMessage; | |
| impl fmt::Display for MyMessage { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str("\nThe message\n") | |
| } | |
| } | |
| let error = GenericError::new(MyMessage); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let report = Report::new(error).pretty(true); | |
| let expected = " | |
| The message | |
| Caused by: | |
| 0: \ | |
| \n The message | |
| \ | |
| \n 1: \ | |
| \n The message | |
| "; | |
| let actual = report.to_string(); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() { | |
| #[derive(Debug)] | |
| struct MyMessage; | |
| impl fmt::Display for MyMessage { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str("The message")?; | |
| f.write_str(" goes on")?; | |
| f.write_str(" and on.") | |
| } | |
| } | |
| let error = GenericError::new(MyMessage); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let report = Report::new(error).pretty(true); | |
| let expected = "\ | |
| The message goes on and on. | |
| Caused by: | |
| 0: The message goes on and on. | |
| 1: The message goes on and on."; | |
| let actual = report.to_string(); | |
| println!("{actual}"); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn errors_with_string_interpolation_formats_correctly() { | |
| #[derive(Debug)] | |
| struct MyMessage(usize); | |
| impl fmt::Display for MyMessage { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "Got an error code: ({}). ", self.0)?; | |
| write!(f, "What would you like to do in response?") | |
| } | |
| } | |
| let error = GenericError::new(MyMessage(10)); | |
| let error = GenericError::new_with_source(MyMessage(20), error); | |
| let report = Report::new(error).pretty(true); | |
| let expected = "\ | |
| Got an error code: (20). What would you like to do in response? | |
| Caused by: | |
| Got an error code: (10). What would you like to do in response?"; | |
| let actual = report.to_string(); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn empty_lines_mid_message() { | |
| #[derive(Debug)] | |
| struct MyMessage; | |
| impl fmt::Display for MyMessage { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str("line 1\n\nline 2") | |
| } | |
| } | |
| let error = GenericError::new(MyMessage); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let report = Report::new(error).pretty(true); | |
| let expected = "\ | |
| line 1 | |
| line 2 | |
| Caused by: | |
| 0: line 1 | |
| \ | |
| \n line 2 | |
| 1: line 1 | |
| \ | |
| \n line 2"; | |
| let actual = report.to_string(); | |
| assert_eq!(expected, actual); | |
| } | |
| #[test] | |
| fn only_one_source() { | |
| #[derive(Debug)] | |
| struct MyMessage; | |
| impl fmt::Display for MyMessage { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str("line 1\nline 2") | |
| } | |
| } | |
| let error = GenericError::new(MyMessage); | |
| let error = GenericError::new_with_source(MyMessage, error); | |
| let report = Report::new(error).pretty(true); | |
| let expected = "\ | |
| line 1 | |
| line 2 | |
| Caused by: | |
| line 1 | |
| line 2"; | |
| let actual = report.to_string(); | |
| assert_eq!(expected, actual); | |
| } |