use failure::{Error, Fail};
use regex::Regex;
use crate::backtrace_support::{demangle_symbol, error_typename, filename, strip_symbol};
use crate::hub::Hub;
use crate::internals::Uuid;
use crate::protocol::{Event, Exception, Frame, Level, Stacktrace};
lazy_static::lazy_static! {
static ref MODULE_SPLIT_RE: Regex = Regex::new(r"^(.*)::(.*?)$").unwrap();
static ref FRAME_RE: Regex = Regex::new(
r#"(?xm)
^
[\ ]*(?:\d+:)[\ ]* # leading frame number
(?:
(?P<addr_oldsyntax>0x[a-f0-9]+) # addr
[\ ]-[\ ]
(?P<symbol_oldsyntax>[^\r\n]+)
|
(?P<symbol>[^\r\n]+)
\((?P<addr>0x[a-f0-9]+)\) # addr
)
(?:
\r?\n
[\ \t]+at[\ ]
(?P<path>[^\r\n]+?)
(?::(?P<lineno>\d+))?
)?
$
"#
)
.unwrap();
}
fn parse_stacktrace(bt: &str) -> Option<Stacktrace> {
let frames = FRAME_RE
.captures_iter(&bt)
.map(|captures| {
let abs_path = captures.name("path").map(|m| m.as_str().to_string());
let filename = abs_path.as_ref().map(|p| filename(p));
let real_symbol = captures
.name("symbol")
.map_or_else(|| &captures["symbol_oldsyntax"], |m| m.as_str())
.to_string();
let symbol = strip_symbol(&real_symbol);
let function = demangle_symbol(symbol);
Frame {
symbol: if symbol != function {
Some(symbol.into())
} else {
None
},
function: Some(function),
instruction_addr: Some(
captures
.name("addr")
.map_or_else(|| &captures["addr_oldsyntax"], |m| m.as_str())
.parse()
.unwrap(),
),
abs_path,
filename,
lineno: captures
.name("lineno")
.map(|x| x.as_str().parse::<u64>().unwrap()),
..Default::default()
}
})
.collect();
Stacktrace::from_frames_reversed(frames)
}
fn fail_typename<F: Fail + ?Sized>(f: &F) -> (Option<String>, String) {
if let Some(name) = f.name() {
if let Some(caps) = MODULE_SPLIT_RE.captures(name) {
(Some(caps[1].to_string()), caps[2].to_string())
} else {
(None, name.to_string())
}
} else {
(None, error_typename(f))
}
}
pub fn exception_from_single_fail<F: Fail + ?Sized>(
f: &F,
bt: Option<&failure::Backtrace>,
) -> Exception {
let (module, ty) = fail_typename(f);
Exception {
ty,
module,
value: Some(f.to_string()),
stacktrace: bt
.map(|backtrace| backtrace.to_string())
.and_then(|x| parse_stacktrace(&x)),
..Default::default()
}
}
pub fn event_from_error(err: &failure::Error) -> Event<'static> {
let mut exceptions = vec![];
for (idx, cause) in err.iter_chain().enumerate() {
let bt = match cause.backtrace() {
Some(bt) => Some(bt),
None if idx == 0 => Some(err.backtrace()),
None => None,
};
exceptions.push(exception_from_single_fail(cause, bt));
}
exceptions.reverse();
Event {
exception: exceptions.into(),
level: Level::Error,
..Default::default()
}
}
pub fn event_from_fail<F: Fail + ?Sized>(fail: &F) -> Event<'static> {
let mut exceptions = vec![exception_from_single_fail(fail, fail.backtrace())];
let mut ptr: Option<&dyn Fail> = None;
while let Some(cause) = ptr.map(Fail::cause).unwrap_or_else(|| fail.cause()) {
exceptions.push(exception_from_single_fail(cause, cause.backtrace()));
ptr = Some(cause);
}
exceptions.reverse();
Event {
exception: exceptions.into(),
level: Level::Error,
..Default::default()
}
}
pub fn capture_error(err: &Error) -> Uuid {
Hub::with_active(|hub| hub.capture_error(err))
}
pub fn capture_fail<F: Fail + ?Sized>(fail: &F) -> Uuid {
Hub::with_active(|hub| hub.capture_fail(fail))
}
pub trait FailureHubExt {
fn capture_error(&self, err: &Error) -> Uuid;
fn capture_fail<F: Fail + ?Sized>(&self, fail: &F) -> Uuid;
}
impl FailureHubExt for Hub {
fn capture_error(&self, err: &Error) -> Uuid {
self.capture_event(event_from_error(err))
}
fn capture_fail<F: Fail + ?Sized>(&self, fail: &F) -> Uuid {
self.capture_event(event_from_fail(fail))
}
}