use std::{fmt::Write, path::Path};
const DEPENDENCY_FILE_PREFIXES: &[&str] = &[
"/rustc/",
"src/libstd/",
"src/libpanic_unwind/",
"src/libtest/",
];
const DEPENDENCY_FILE_CONTAINS: &[&str] = &["/.cargo/registry/src/"];
const DEPENDENCY_SYM_PREFIXES: &[&str] = &[
"std::",
"core::",
"witcher::error::",
"<witcher::error::",
"witcher::backtrace::",
"backtrace::backtrace::",
"_rust_begin_unwind",
"color_traceback::",
"__rust_",
"___rust_",
"__pthread",
"_main",
"main",
"__scrt_common_main_seh",
"BaseThreadInitThunk",
"_start",
"__libc_start_main",
"start_thread",
"__GI__",
];
const DEPENDENCY_SYM_CONTAINS: &[&str] = &["as witcher::wrapper::Wrapper"];
pub(crate) fn new() -> Vec<Frame> {
let bt = backtrace::Backtrace::new();
bt.frames()
.iter()
.flat_map(|x| x.symbols())
.map(|sym| Frame {
symbol: match sym.name() {
Some(name) => format!("{:#}", name),
None => String::from("<unknown>"),
},
filename: simple_path(sym.filename()),
lineno: sym.lineno(),
column: sym.colno(),
})
.collect()
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Frame {
pub symbol: String, pub filename: String, pub lineno: Option<u32>, pub column: Option<u32>, }
impl Frame {
pub fn is_dependency(&self) -> bool {
if DEPENDENCY_SYM_PREFIXES.iter().any(|x| self.symbol.starts_with(x))
|| DEPENDENCY_SYM_CONTAINS.iter().any(|x| self.symbol.contains(x))
|| DEPENDENCY_FILE_PREFIXES.iter().any(|x| self.filename.starts_with(x))
|| DEPENDENCY_FILE_CONTAINS.iter().any(|x| self.filename.contains(x))
{
return true;
}
false
}
}
fn simple_path(filename: Option<&Path>) -> String {
let mut f = String::new();
if let Some(file) = filename {
let cwd = std::env::current_dir();
if let Ok(cwd) = &cwd {
if let Ok(suffix) = file.strip_prefix(cwd) {
write!(f, "{}", suffix.display()).omit();
return f;
}
}
write!(f, "{}", file.display()).omit();
} else {
write!(f, "<unknown>").omit();
}
f
}
trait Omit {
fn omit(&self);
}
impl Omit for std::fmt::Result {
fn omit(&self) {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frame_equality() {
let mut frame1 = Frame {
symbol: String::from("symbol"),
filename: String::from("filename"),
lineno: Some(1),
column: Some(2),
};
let frame2 = Frame {
symbol: String::from("symbol"),
filename: String::from("filename"),
lineno: Some(1),
column: Some(2),
};
assert_eq!(true, frame1 == frame2);
assert_eq!(frame1, frame2);
frame1.lineno = Some(3);
assert_eq!(true, frame1 != frame2);
assert_ne!(frame1, frame2);
}
#[test]
fn test_simple_path() {
let cwd = std::env::current_dir().unwrap();
assert_eq!("foo", simple_path(Some(Path::new(&cwd).join("foo").as_ref())));
assert_eq!("foobar", simple_path(Some(Path::new(&cwd).join("foobar").as_ref())));
assert_eq!("/rustc/123/src/libstd/foobar", simple_path(Some(Path::new("/rustc/123/src/libstd").join("foobar").as_ref())));
assert_eq!("<unknown>", simple_path(None));
}
#[test]
fn test_omit() {
let mut w = String::new();
write!(&mut w, "foobar").omit();
}
}