mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 21:16:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			443 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| #![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);
 | |
| }
 | 
