#![warn(missing_docs)]
pub use call_trace::trace_with;
pub use call_trace::Trace;
pub use call_trace::CallContext;
use std::cell::RefCell;
use std::rc::Rc;
pub type Callback = Rc<dyn Fn(&mut Context, Event)>;
pub struct Context {
callback: Option<Callback>,
stack: Vec<CallContext>,
}
impl Context {
fn new() -> Self {
let mut r = Context {
callback: None,
stack: Vec::new(),
};
r.register_callback(|_| Self::default_callback);
r
}
pub fn default_callback(ctx: &mut Context, event: Event) {
match event {
Event::Call => {
eprintln!("[{}:{}] => {}()", ctx.top().file, ctx.top().line, ctx.top().fn_name);
}
Event::Return => {
eprintln!("[{}:{}] <= {}()", ctx.top().file, ctx.top().line, ctx.top().fn_name);
}
}
}
pub fn stack(&self) -> &[CallContext] {
&self.stack
}
pub fn top(&self) -> &CallContext {
self.stack.last().expect("something went wrong with the callstack")
}
pub fn register_callback<F, G>(&mut self, f: F)
where F: FnOnce(Option<Callback>) -> G,
G: Fn(&mut Context, Event) + 'static
{
self.callback = Some(Rc::new(f(self.callback.take())));
}
pub fn unregister_callback(&mut self) -> Option<Callback> {
self.callback.take()
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Event {
Call,
Return,
}
thread_local! {
static CONTEXT: RefCell<Context> = RefCell::new(Context::new());
}
pub fn thread_access_with<T, F: FnOnce(&mut Context) -> T>(f: F) -> T {
CONTEXT.with(move |ctx| f(&mut ctx.borrow_mut()))
}
pub fn thread_register_callback<F, G>(f: F)
where F: FnOnce(Option<Callback>) -> G,
G: Fn(&mut Context, Event) + 'static
{
thread_access_with(move |ctx| {
ctx.register_callback(f)
})
}
pub fn thread_unregister_callback() -> Option<Callback> {
thread_access_with(move |ctx| {
ctx.unregister_callback()
})
}
fn on_event(cctx: &CallContext, event: Event) {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
if let Event::Call = event {
ctx.stack.push(cctx.clone());
}
let ctx = &mut *ctx;
if let Some(cb) = &ctx.callback {
let cb: Callback = cb.clone();
cb(ctx, event);
}
if let Event::Return = event {
ctx.stack.pop().expect("something went wrong with the call stack");
}
})
}
pub struct Tls;
impl Trace for Tls {
fn on_pre(&mut self, ctx: &CallContext) {
on_event(&ctx, Event::Call);
}
fn on_post(&mut self, ctx: &CallContext) {
on_event(&ctx, Event::Return);
}
}