use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
const UNSTRINGIFIABLE_VALUE: &str = "<unstringifiable value>";
pub trait FatalErrorLike {
fn name(&self) -> &str;
fn message(&self) -> &str;
fn stack(&self) -> Option<&str> {
None
}
}
#[derive(Clone, Copy)]
pub enum FatalValue<'a> {
Error(&'a dyn FatalErrorLike),
Display(&'a dyn fmt::Display),
Null,
Undefined,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FatalDescription {
pub name: String,
pub message: Option<String>,
}
impl FatalDescription {
pub fn render(&self) -> String {
match &self.message {
Some(message) if !message.is_empty() => format!("{}: {}", self.name, message),
_ => self.name.clone(),
}
}
}
pub fn describe_fatal(value: FatalValue<'_>) -> String {
match value {
FatalValue::Error(error) => FatalDescription {
name: if error.name().is_empty() {
"Error"
} else {
error.name()
}
.to_owned(),
message: Some(error.message().to_owned()),
}
.render(),
FatalValue::Display(value) => describe_display(value),
FatalValue::Null => "null".to_owned(),
FatalValue::Undefined => "undefined".to_owned(),
}
}
struct StringSink(String);
impl fmt::Write for StringSink {
fn write_str(&mut self, value: &str) -> fmt::Result {
self.0.push_str(value);
Ok(())
}
}
pub fn describe_display(value: &dyn fmt::Display) -> String {
let mut sink = StringSink(String::new());
match fmt::write(&mut sink, format_args!("{}", value)) {
Ok(()) => sink.0,
Err(_) => UNSTRINGIFIABLE_VALUE.to_owned(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FatalEvent {
UncaughtException,
UnhandledRejection,
}
pub type FatalEventHandler<'a> = Box<dyn for<'value> FnMut(FatalValue<'value>) + 'a>;
pub trait FatalEventTarget<'a> {
fn on(&mut self, event: FatalEvent, handler: FatalEventHandler<'a>);
}
pub struct FatalHandlerDeps<'target, T, W, E>
where
W: FnMut(&str),
E: FnMut(i32),
T: ?Sized,
{
pub target: &'target mut T,
pub write: W,
pub exit: E,
}
pub fn install_fatal_handlers<'handler, T, W, E>(deps: FatalHandlerDeps<'_, T, W, E>)
where
T: FatalEventTarget<'handler> + ?Sized,
W: FnMut(&str) + 'handler,
E: FnMut(i32) + 'handler,
{
let write = Rc::new(RefCell::new(deps.write));
let exit = Rc::new(RefCell::new(deps.exit));
let write_uncaught = Rc::clone(&write);
let exit_uncaught = Rc::clone(&exit);
deps.target.on(
FatalEvent::UncaughtException,
Box::new(move |error| {
(write_uncaught.borrow_mut())(&format!(
"[RustCodeGraph] Uncaught exception: {}\n",
describe_fatal(error)
));
(exit_uncaught.borrow_mut())(1);
}),
);
let write_rejection = Rc::clone(&write);
let exit_rejection = Rc::clone(&exit);
deps.target.on(
FatalEvent::UnhandledRejection,
Box::new(move |reason| {
(write_rejection.borrow_mut())(&format!(
"[RustCodeGraph] Unhandled rejection: {}\n",
describe_fatal(reason)
));
(exit_rejection.borrow_mut())(1);
}),
);
}