1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! Format Ruby `Exception` backtraces.
use std::error;
use std::io;
use termcolor::{ColorSpec, WriteColor};
use crate::prelude::*;
/// Format an `Exception` backtrace into an [`io::Write`] suitable for
/// displaying in a Ruby REPL.
///
/// This backtrace has the same style and formatting as one returned from the
/// `irb` command in MRI.
///
/// # Examples
///
/// Executing the following Ruby code:
///
/// ```ruby
/// def fail; raise RuntimeError, "bang!"; end
/// fail
/// ```
///
/// Results in this stack trace:
///
/// ```txt
/// Traceback (most recent call last):
/// 2: from (airb):2
/// 1: from (airb):1:in fail
/// RuntimeError (bang!)
/// ```
///
/// # Errors
///
/// If writing into the provided `out` writer fails, an error is returned.
pub fn format_repl_trace_into<W, E>(mut error: W, interp: &mut Artichoke, exc: &E) -> Result<(), Box<dyn error::Error>>
where
W: io::Write + WriteColor,
E: RubyException,
{
// reset colors
error.reset()?;
// Format backtrace if present
if let Some(backtrace) = exc.vm_backtrace(interp) {
error.set_color(ColorSpec::new().set_bold(true))?;
write!(error, "Traceback")?;
error.reset()?;
writeln!(error, " (most recent call last):")?;
for (num, frame) in backtrace.into_iter().enumerate().rev() {
write!(error, "\t{}: from ", num + 1)?;
error.write_all(frame.as_slice())?;
writeln!(error)?;
}
}
// Format exception class and message
error.set_color(ColorSpec::new().set_bold(true))?;
write!(error, "{} (", exc.name())?;
error.set_color(ColorSpec::new().set_bold(true).set_underline(true))?;
error.write_all(&exc.message())?;
error.set_color(ColorSpec::new().set_bold(true))?;
writeln!(error, ")")?;
// reset colors
error.reset()?;
Ok(())
}
/// Format an `Exception` backtrace into an [`io::Write`] suitable for
/// displaying in a Ruby CLI.
///
/// This backtrace has the same style and formatting as one returned from the
/// `ruby` command in MRI.
///
/// # Examples
///
/// Executing the following Ruby code:
///
/// ```ruby
/// def fail; raise RuntimeError, "bang!"; end
/// fail
/// ```
///
/// Results in this stack trace:
///
/// ```txt
/// Traceback (most recent call last):
/// 2: from -e:1
/// -e:1:in fail: bang! (RuntimeError)
/// ```
///
/// # Errors
///
/// If writing into the provided `out` writer fails, an error is returned.
pub fn format_cli_trace_into<W, E>(mut error: W, interp: &mut Artichoke, exc: &E) -> Result<(), Box<dyn error::Error>>
where
W: io::Write + WriteColor,
E: RubyException,
{
// reset colors
error.reset()?;
let mut top = None;
// Format backtrace if present
if let Some(backtrace) = exc.vm_backtrace(interp) {
error.set_color(ColorSpec::new().set_bold(true))?;
write!(error, "Traceback")?;
error.reset()?;
writeln!(error, " (most recent call last):")?;
let mut iter = backtrace.into_iter().enumerate();
top = iter.next();
for (num, frame) in iter.rev() {
write!(error, "\t{}: from ", num + 1)?;
error.write_all(frame.as_slice())?;
writeln!(error)?;
}
}
if let Some((_, frame)) = top {
error.write_all(frame.as_slice())?;
write!(error, ": ")?;
}
// Format exception class and message
error.set_color(ColorSpec::new().set_bold(true))?;
error.write_all(&exc.message())?;
error.set_color(ColorSpec::new().set_bold(true))?;
write!(error, " (")?;
error.set_color(ColorSpec::new().set_bold(true).set_underline(true))?;
write!(error, "{}", exc.name())?;
error.set_color(ColorSpec::new().set_bold(true))?;
writeln!(error, ")")?;
// reset colors
error.reset()?;
Ok(())
}