]> git.lizzy.rs Git - rust.git/blobdiff - library/std/src/error.rs
Rollup merge of #91938 - yaahc:error-reporter, r=m-ou-se
[rust.git] / library / std / src / error.rs
index ea0c230fa42db7438021169ccac9a0dfe6d366d2..643108b88bf79da58131bacff61a3a67e616285b 100644 (file)
@@ -25,7 +25,7 @@
 use crate::borrow::Cow;
 use crate::cell;
 use crate::char;
-use crate::fmt::{self, Debug, Display};
+use crate::fmt::{self, Debug, Display, Write};
 use crate::mem::transmute;
 use crate::num;
 use crate::str;
@@ -63,7 +63,7 @@ pub trait Error: Debug + Display {
     ///
     /// #[derive(Debug)]
     /// struct SuperError {
-    ///     side: SuperErrorSideKick,
+    ///     source: SuperErrorSideKick,
     /// }
     ///
     /// impl fmt::Display for SuperError {
@@ -74,7 +74,7 @@ pub trait Error: Debug + Display {
     ///
     /// impl Error for SuperError {
     ///     fn source(&self) -> Option<&(dyn Error + 'static)> {
-    ///         Some(&self.side)
+    ///         Some(&self.source)
     ///     }
     /// }
     ///
@@ -90,7 +90,7 @@ pub trait Error: Debug + Display {
     /// impl Error for SuperErrorSideKick {}
     ///
     /// fn get_super_error() -> Result<(), SuperError> {
-    ///     Err(SuperError { side: SuperErrorSideKick })
+    ///     Err(SuperError { source: SuperErrorSideKick })
     /// }
     ///
     /// fn main() {
@@ -606,21 +606,21 @@ impl Error for time::FromSecsError {}
 
 // Copied from `any.rs`.
 impl dyn Error + 'static {
-    /// Returns `true` if the boxed type is the same as `T`
+    /// Returns `true` if the inner type is the same as `T`.
     #[stable(feature = "error_downcast", since = "1.3.0")]
     #[inline]
     pub fn is<T: Error + 'static>(&self) -> bool {
         // Get `TypeId` of the type this function is instantiated with.
         let t = TypeId::of::<T>();
 
-        // Get `TypeId` of the type in the trait object.
-        let boxed = self.type_id(private::Internal);
+        // Get `TypeId` of the type in the trait object (`self`).
+        let concrete = self.type_id(private::Internal);
 
         // Compare both `TypeId`s on equality.
-        t == boxed
+        t == concrete
     }
 
-    /// Returns some reference to the boxed value if it is of type `T`, or
+    /// Returns some reference to the inner value if it is of type `T`, or
     /// `None` if it isn't.
     #[stable(feature = "error_downcast", since = "1.3.0")]
     #[inline]
@@ -632,7 +632,7 @@ pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
         }
     }
 
-    /// Returns some mutable reference to the boxed value if it is of type `T`, or
+    /// Returns some mutable reference to the inner value if it is of type `T`, or
     /// `None` if it isn't.
     #[stable(feature = "error_downcast", since = "1.3.0")]
     #[inline]
@@ -810,3 +810,642 @@ pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>
         })
     }
 }
+
+/// An error reporter that print's an error and its sources.
+///
+/// Report also exposes configuration options for formatting the error chain, either entirely on a
+/// single line, or in multi-line format with each cause in the error chain on a new line.
+///
+/// `Report` only requires that the wrapped error implements `Error`. It doesn't require that the
+/// wrapped error be `Send`, `Sync`, or `'static`.
+///
+/// # Examples
+///
+/// ```rust
+/// #![feature(error_reporter)]
+/// use std::error::{Error, Report};
+/// use std::fmt;
+///
+/// #[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 {}
+///
+/// fn get_super_error() -> Result<(), SuperError> {
+///     Err(SuperError { source: SuperErrorSideKick })
+/// }
+///
+/// fn main() {
+///     match get_super_error() {
+///         Err(e) => println!("Error: {}", Report::new(e)),
+///         _ => println!("No error"),
+///     }
+/// }
+/// ```
+///
+/// This example produces the following output:
+///
+/// ```console
+/// Error: SuperError is here!: SuperErrorSideKick is here!
+/// ```
+///
+/// ## Output consistency
+///
+/// Report prints the same output via `Display` and `Debug`, so it works well with
+/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`:
+///
+/// ```should_panic
+/// #![feature(error_reporter)]
+/// use std::error::Report;
+/// # use std::error::Error;
+/// # use std::fmt;
+/// # #[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 {}
+/// # fn get_super_error() -> Result<(), SuperError> {
+/// #     Err(SuperError { source: SuperErrorSideKick })
+/// # }
+///
+/// get_super_error().map_err(Report::new).unwrap();
+/// ```
+///
+/// This example produces the following output:
+///
+/// ```console
+/// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40
+/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+/// ```
+///
+/// ## Return from `main`
+///
+/// `Report` also implements `From` for all types that implement [`Error`], this when combined with
+/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned
+/// from `main`.
+///
+/// ```should_panic
+/// #![feature(error_reporter)]
+/// use std::error::Report;
+/// # use std::error::Error;
+/// # use std::fmt;
+/// # #[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 {}
+/// # fn get_super_error() -> Result<(), SuperError> {
+/// #     Err(SuperError { source: SuperErrorSideKick })
+/// # }
+///
+/// fn main() -> Result<(), Report> {
+///     get_super_error()?;
+///     Ok(())
+/// }
+/// ```
+///
+/// This example produces the following output:
+///
+/// ```console
+/// Error: SuperError is here!: SuperErrorSideKick is here!
+/// ```
+///
+/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line
+/// output format, if you want to make sure your `Report`s are pretty printed and include backtrace
+/// you will need to manually convert and enable those flags.
+///
+/// ```should_panic
+/// #![feature(error_reporter)]
+/// use std::error::Report;
+/// # use std::error::Error;
+/// # use std::fmt;
+/// # #[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 {}
+/// # fn get_super_error() -> Result<(), SuperError> {
+/// #     Err(SuperError { source: SuperErrorSideKick })
+/// # }
+///
+/// fn main() -> Result<(), Report> {
+///     get_super_error()
+///         .map_err(Report::from)
+///         .map_err(|r| r.pretty(true).show_backtrace(true))?;
+///     Ok(())
+/// }
+/// ```
+///
+/// This example produces the following output:
+///
+/// ```console
+/// Error: SuperError is here!
+///
+/// Caused by:
+///       SuperErrorSideKick is here!
+/// ```
+#[unstable(feature = "error_reporter", issue = "90172")]
+pub struct Report<E = Box<dyn Error>> {
+    /// The error being reported.
+    error: E,
+    /// Whether a backtrace should be included as part of the report.
+    show_backtrace: bool,
+    /// Whether the report should be pretty-printed.
+    pretty: bool,
+}
+
+impl<E> Report<E>
+where
+    Report<E>: From<E>,
+{
+    /// Create a new `Report` from an input error.
+    #[unstable(feature = "error_reporter", issue = "90172")]
+    pub fn new(error: E) -> Report<E> {
+        Self::from(error)
+    }
+}
+
+impl<E> Report<E> {
+    /// Enable pretty-printing the report across multiple lines.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(error_reporter)]
+    /// use std::error::Report;
+    /// # use std::error::Error;
+    /// # use std::fmt;
+    /// # #[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 {}
+    ///
+    /// let error = SuperError { source: SuperErrorSideKick };
+    /// let report = Report::new(error).pretty(true);
+    /// eprintln!("Error: {:?}", report);
+    /// ```
+    ///
+    /// This example produces the following output:
+    ///
+    /// ```console
+    /// Error: SuperError is here!
+    ///
+    /// Caused by:
+    ///       SuperErrorSideKick is here!
+    /// ```
+    ///
+    /// When there are multiple source errors the causes will be numbered in order of iteration
+    /// starting from the outermost error.
+    ///
+    /// ```rust
+    /// #![feature(error_reporter)]
+    /// use std::error::Report;
+    /// # use std::error::Error;
+    /// # use std::fmt;
+    /// # #[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 {
+    /// #     source: SuperErrorSideKickSideKick,
+    /// # }
+    /// # impl fmt::Display for SuperErrorSideKick {
+    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    /// #         write!(f, "SuperErrorSideKick is here!")
+    /// #     }
+    /// # }
+    /// # impl Error for SuperErrorSideKick {
+    /// #     fn source(&self) -> Option<&(dyn Error + 'static)> {
+    /// #         Some(&self.source)
+    /// #     }
+    /// # }
+    /// # #[derive(Debug)]
+    /// # struct SuperErrorSideKickSideKick;
+    /// # impl fmt::Display for SuperErrorSideKickSideKick {
+    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    /// #         write!(f, "SuperErrorSideKickSideKick is here!")
+    /// #     }
+    /// # }
+    /// # impl Error for SuperErrorSideKickSideKick { }
+    ///
+    /// let source = SuperErrorSideKickSideKick;
+    /// let source = SuperErrorSideKick { source };
+    /// let error = SuperError { source };
+    /// let report = Report::new(error).pretty(true);
+    /// eprintln!("Error: {:?}", report);
+    /// ```
+    ///
+    /// This example produces the following output:
+    ///
+    /// ```console
+    /// Error: SuperError is here!
+    ///
+    /// Caused by:
+    ///    0: SuperErrorSideKick is here!
+    ///    1: SuperErrorSideKickSideKick is here!
+    /// ```
+    #[unstable(feature = "error_reporter", issue = "90172")]
+    pub fn pretty(mut self, pretty: bool) -> Self {
+        self.pretty = pretty;
+        self
+    }
+
+    /// Display backtrace if available when using pretty output format.
+    ///
+    /// # Examples
+    ///
+    /// **Note**: Report will search for the first `Backtrace` it can find starting from the
+    /// outermost error. In this example it will display the backtrace from the second error in the
+    /// chain, `SuperErrorSideKick`.
+    ///
+    /// ```rust
+    /// #![feature(error_reporter)]
+    /// #![feature(backtrace)]
+    /// # use std::error::Error;
+    /// # use std::fmt;
+    /// use std::error::Report;
+    /// use std::backtrace::Backtrace;
+    ///
+    /// # #[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 {
+    ///     backtrace: Backtrace,
+    /// }
+    ///
+    /// impl SuperErrorSideKick {
+    ///     fn new() -> SuperErrorSideKick {
+    ///         SuperErrorSideKick { backtrace: Backtrace::force_capture() }
+    ///     }
+    /// }
+    ///
+    /// impl Error for SuperErrorSideKick {
+    ///     fn backtrace(&self) -> Option<&Backtrace> {
+    ///         Some(&self.backtrace)
+    ///     }
+    /// }
+    ///
+    /// // The rest of the example is unchanged ...
+    /// # impl fmt::Display for SuperErrorSideKick {
+    /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    /// #         write!(f, "SuperErrorSideKick is here!")
+    /// #     }
+    /// # }
+    ///
+    /// let source = SuperErrorSideKick::new();
+    /// let error = SuperError { source };
+    /// let report = Report::new(error).pretty(true).show_backtrace(true);
+    /// eprintln!("Error: {:?}", report);
+    /// ```
+    ///
+    /// This example produces something similar to the following output:
+    ///
+    /// ```console
+    /// Error: SuperError is here!
+    ///
+    /// Caused by:
+    ///       SuperErrorSideKick is here!
+    ///
+    /// Stack backtrace:
+    ///    0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new
+    ///    1: rust_out::main::_doctest_main_src_error_rs_1158_0
+    ///    2: rust_out::main
+    ///    3: core::ops::function::FnOnce::call_once
+    ///    4: std::sys_common::backtrace::__rust_begin_short_backtrace
+    ///    5: std::rt::lang_start::{{closure}}
+    ///    6: std::panicking::try
+    ///    7: std::rt::lang_start_internal
+    ///    8: std::rt::lang_start
+    ///    9: main
+    ///   10: __libc_start_main
+    ///   11: _start
+    /// ```
+    #[unstable(feature = "error_reporter", issue = "90172")]
+    pub fn show_backtrace(mut self, show_backtrace: bool) -> Self {
+        self.show_backtrace = show_backtrace;
+        self
+    }
+}
+
+impl<E> Report<E>
+where
+    E: Error,
+{
+    fn backtrace(&self) -> Option<&Backtrace> {
+        // have to grab the backtrace on the first error directly since that error may not be
+        // 'static
+        let backtrace = self.error.backtrace();
+        let backtrace = backtrace.or_else(|| {
+            self.error
+                .source()
+                .map(|source| source.chain().find_map(|source| source.backtrace()))
+                .flatten()
+        });
+        backtrace
+    }
+
+    /// Format the report as a single line.
+    #[unstable(feature = "error_reporter", issue = "90172")]
+    fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.error)?;
+
+        let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain);
+
+        for cause in sources {
+            write!(f, ": {}", cause)?;
+        }
+
+        Ok(())
+    }
+
+    /// Format the report as multiple lines, with each error cause on its own line.
+    #[unstable(feature = "error_reporter", issue = "90172")]
+    fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let error = &self.error;
+
+        write!(f, "{}", error)?;
+
+        if let Some(cause) = error.source() {
+            write!(f, "\n\nCaused by:")?;
+
+            let multiple = cause.source().is_some();
+
+            for (ind, error) in cause.chain().enumerate() {
+                writeln!(f)?;
+                let mut indented = Indented { inner: f };
+                if multiple {
+                    write!(indented, "{: >4}: {}", ind, error)?;
+                } else {
+                    write!(indented, "      {}", error)?;
+                }
+            }
+        }
+
+        if self.show_backtrace {
+            let backtrace = self.backtrace();
+
+            if let Some(backtrace) = backtrace {
+                let backtrace = backtrace.to_string();
+
+                f.write_str("\n\nStack backtrace:\n")?;
+                f.write_str(backtrace.trim_end())?;
+            }
+        }
+
+        Ok(())
+    }
+}
+
+impl Report<Box<dyn Error>> {
+    fn backtrace(&self) -> Option<&Backtrace> {
+        // have to grab the backtrace on the first error directly since that error may not be
+        // 'static
+        let backtrace = self.error.backtrace();
+        let backtrace = backtrace.or_else(|| {
+            self.error
+                .source()
+                .map(|source| source.chain().find_map(|source| source.backtrace()))
+                .flatten()
+        });
+        backtrace
+    }
+
+    /// Format the report as a single line.
+    #[unstable(feature = "error_reporter", issue = "90172")]
+    fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.error)?;
+
+        let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain);
+
+        for cause in sources {
+            write!(f, ": {}", cause)?;
+        }
+
+        Ok(())
+    }
+
+    /// Format the report as multiple lines, with each error cause on its own line.
+    #[unstable(feature = "error_reporter", issue = "90172")]
+    fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let error = &self.error;
+
+        write!(f, "{}", error)?;
+
+        if let Some(cause) = error.source() {
+            write!(f, "\n\nCaused by:")?;
+
+            let multiple = cause.source().is_some();
+
+            for (ind, error) in cause.chain().enumerate() {
+                writeln!(f)?;
+                let mut indented = Indented { inner: f };
+                if multiple {
+                    write!(indented, "{: >4}: {}", ind, error)?;
+                } else {
+                    write!(indented, "      {}", error)?;
+                }
+            }
+        }
+
+        if self.show_backtrace {
+            let backtrace = self.backtrace();
+
+            if let Some(backtrace) = backtrace {
+                let backtrace = backtrace.to_string();
+
+                f.write_str("\n\nStack backtrace:\n")?;
+                f.write_str(backtrace.trim_end())?;
+            }
+        }
+
+        Ok(())
+    }
+}
+
+#[unstable(feature = "error_reporter", issue = "90172")]
+impl<E> From<E> for Report<E>
+where
+    E: Error,
+{
+    fn from(error: E) -> Self {
+        Report { error, show_backtrace: false, pretty: false }
+    }
+}
+
+#[unstable(feature = "error_reporter", issue = "90172")]
+impl<'a, E> From<E> for Report<Box<dyn Error + 'a>>
+where
+    E: Error + 'a,
+{
+    fn from(error: E) -> Self {
+        let error = box error;
+        Report { error, show_backtrace: false, pretty: false }
+    }
+}
+
+#[unstable(feature = "error_reporter", issue = "90172")]
+impl<E> fmt::Display for Report<E>
+where
+    E: Error,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
+    }
+}
+
+#[unstable(feature = "error_reporter", issue = "90172")]
+impl fmt::Display for Report<Box<dyn Error>> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
+    }
+}
+
+// This type intentionally outputs the same format for `Display` and `Debug`for
+// situations where you unwrap a `Report` or return it from main.
+#[unstable(feature = "error_reporter", issue = "90172")]
+impl<E> fmt::Debug for Report<E>
+where
+    Report<E>: fmt::Display,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(self, f)
+    }
+}
+
+/// Wrapper type for indenting the inner source.
+struct Indented<'a, D> {
+    inner: &'a mut D,
+}
+
+impl<T> Write for Indented<'_, T>
+where
+    T: Write,
+{
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        for (i, line) in s.split('\n').enumerate() {
+            if i > 0 {
+                self.inner.write_char('\n')?;
+                self.inner.write_str("      ")?;
+            }
+
+            self.inner.write_str(line)?;
+        }
+
+        Ok(())
+    }
+}