use std::fmt::{Debug, Write};
use std::sync::atomic::{AtomicU64, Ordering};
use indextree::{Arena, NodeId};
use itertools::Itertools;
use parking_lot::{Mutex, MutexGuard};
use crate::root::current_context;
use crate::Span;
#[derive(Debug, Clone)]
struct SpanNode {
span: Span,
start_time: coarsetime::Instant,
}
impl SpanNode {
fn new(span: Span) -> Self {
Self {
span,
start_time: coarsetime::Instant::now(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ContextId(u64);
#[derive(Debug, Clone)]
pub struct Tree {
arena: Arena<SpanNode>,
root: NodeId,
current: NodeId,
}
impl std::fmt::Display for Tree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt_node(
f: &mut std::fmt::Formatter<'_>,
arena: &Arena<SpanNode>,
node: NodeId,
depth: usize,
current: NodeId,
) -> std::fmt::Result {
f.write_str(&" ".repeat(depth * 2))?;
let inner = arena[node].get();
f.write_str(inner.span.as_str())?;
let elapsed: std::time::Duration = inner.start_time.elapsed().into();
write!(
f,
" [{}{:.3?}]",
if depth > 0 && elapsed.as_secs() >= 10 {
"!!! "
} else {
""
},
elapsed
)?;
if depth > 0 && node == current {
f.write_str(" <== current")?;
}
f.write_char('\n')?;
for child in node
.children(arena)
.sorted_by_key(|&id| arena[id].get().start_time)
{
fmt_node(f, arena, child, depth + 1, current)?;
}
Ok(())
}
fmt_node(f, &self.arena, self.root, 0, self.current)?;
for node in self.arena.iter().filter(|n| !n.is_removed()) {
let id = self.arena.get_node_id(node).unwrap();
if id == self.root {
continue;
}
if node.parent().is_none() {
writeln!(f, "[Detached {id}]")?;
fmt_node(f, &self.arena, id, 1, self.current)?;
}
}
Ok(())
}
}
impl Tree {
#[cfg(test)]
pub(crate) fn active_node_count(&self) -> usize {
self.arena.iter().filter(|n| !n.is_removed()).count()
}
#[cfg(test)]
pub(crate) fn detached_node_count(&self) -> usize {
self.arena
.iter()
.filter(|n| {
!n.is_removed()
&& n.parent().is_none()
&& self.arena.get_node_id(n).unwrap() != self.root
})
.count()
}
pub(crate) fn push(&mut self, span: Span) -> NodeId {
let child = self.arena.new_node(SpanNode::new(span));
self.current.prepend(child, &mut self.arena);
self.current = child;
child
}
pub(crate) fn step_in(&mut self, child: NodeId) {
if !self.current.children(&self.arena).contains(&child) {
self.current.prepend(child, &mut self.arena);
}
self.current = child;
}
pub(crate) fn pop(&mut self) {
let parent = self.arena[self.current]
.parent()
.expect("the root node should not be popped");
self.remove_and_detach(self.current);
self.current = parent;
}
pub(crate) fn step_out(&mut self) {
let parent = self.arena[self.current]
.parent()
.expect("the root node should not be stepped out");
self.current = parent;
}
pub(crate) fn remove_and_detach(&mut self, node: NodeId) {
node.detach(&mut self.arena);
node.remove(&mut self.arena);
}
pub(crate) fn current(&self) -> NodeId {
self.current
}
}
#[derive(Debug)]
pub(crate) struct TreeContext {
id: ContextId,
verbose: bool,
tree: Mutex<Tree>,
}
impl TreeContext {
pub(crate) fn new(root_span: Span, verbose: bool) -> Self {
static ID: AtomicU64 = AtomicU64::new(0);
let id = ID.fetch_add(1, Ordering::Relaxed);
let mut arena = Arena::new();
let root = arena.new_node(SpanNode::new(root_span));
Self {
id: ContextId(id),
verbose,
tree: Tree {
arena,
root,
current: root,
}
.into(),
}
}
pub(crate) fn id(&self) -> ContextId {
self.id
}
pub(crate) fn tree(&self) -> MutexGuard<'_, Tree> {
self.tree.lock()
}
pub(crate) fn verbose(&self) -> bool {
self.verbose
}
}
pub fn current_tree() -> Option<Tree> {
current_context().map(|c| c.tree().clone())
}