Skip to main content

trace_tally/
lib.rs

1#![cfg_attr(feature = "tracing", doc = include_str!("../README.md"))]
2
3pub(crate) mod runner;
4pub(crate) mod task;
5#[cfg(feature = "tracing")]
6pub(crate) mod tracing;
7pub(crate) mod view;
8pub(crate) mod writer;
9
10pub mod widgets;
11
12#[cfg(test)]
13mod test;
14
15/// Re-exports of all public types and traits.
16pub mod prelude {
17    pub use crate::runner::{ActionSource, RenderLoop};
18    pub use crate::task::TaskId;
19    #[cfg(feature = "tracing")]
20    pub use crate::tracing::*;
21    pub use crate::view::{EventView, FrameWriter, TaskView};
22    pub use crate::widgets::*;
23    pub use crate::writer::TaskRenderer;
24    pub use crate::{Action, Renderer};
25}
26
27pub use crate::prelude::*;
28
29/// Defines how tasks and events are rendered to the terminal.
30///
31/// Implement this trait to control the visual output of the task tree.
32/// Each frame, the writer walks the task hierarchy and calls the render
33/// methods in order: [`render_task_line`], [`render_event_line`] for each
34/// buffered event, then recurses into child tasks. Override [`render_task`]
35/// to change this traversal.
36///
37/// [`render_task`]: Renderer::render_task
38/// [`render_task_line`]: Renderer::render_task_line
39/// [`render_event_line`]: Renderer::render_event_line
40///
41/// # Example
42///
43/// ```rust
44/// use trace_tally::*;
45/// use std::io::Write;
46///
47/// struct MyRenderer;
48///
49/// impl Renderer for MyRenderer {
50///     type EventData = String;
51///     type TaskData = String;
52///
53///     fn render_task_line(
54///         &mut self, f: &mut FrameWriter<'_>, task: &TaskView<'_, Self>,
55///     ) -> std::io::Result<()> {
56///         writeln!(f, "{}{}", " ".repeat(task.depth()), task.data())
57///     }
58///
59///     fn render_event_line(
60///         &mut self, f: &mut FrameWriter<'_>, event: &EventView<'_, Self>,
61///     ) -> std::io::Result<()> {
62///         writeln!(f, "{}  {}", " ".repeat(event.depth()), event.data())
63///     }
64/// }
65/// ```
66pub trait Renderer: Sized {
67    /// Data stored per event (e.g. a log message or span field snapshot).
68    type EventData: Send + 'static;
69    /// Data stored per task (e.g. a task name or metadata).
70    type TaskData: Send + 'static;
71
72    /// Called once at the start of each render frame, before any tasks are visited.
73    fn on_render_start(&mut self) {}
74
75    /// Called once at the end of each render frame, after all tasks have been visited.
76    fn on_render_end(&mut self) {}
77
78    /// Renders a complete task and its descendants.
79    ///
80    /// The default implementation renders the task line, then the last 3
81    /// buffered events (skipped for completed tasks), then recurses into
82    /// subtasks. Override this to change traversal order or the event cap.
83    #[allow(unused_variables)]
84    fn render_task(
85        &mut self,
86        f: &mut FrameWriter<'_>,
87        task: &TaskView<'_, Self>,
88    ) -> Result<(), std::io::Error> {
89        self.render_task_line(f, task)?;
90        if task.active() {
91            for event in task.events().rev().take(3).rev() {
92                self.render_event_line(f, &event)?;
93            }
94        }
95        for subtask in task.subtasks() {
96            self.render_task(f, &subtask)?;
97        }
98        Ok(())
99    }
100
101    /// Renders the task header on task start.
102    #[allow(unused_variables)]
103    fn render_task_line(
104        &mut self,
105        f: &mut FrameWriter<'_>,
106        task: &TaskView<'_, Self>,
107    ) -> Result<(), std::io::Error> {
108        Ok(())
109    }
110
111    /// Renders a single buffered event within a task.
112    #[allow(unused_variables)]
113    fn render_event_line(
114        &mut self,
115        f: &mut FrameWriter<'_>,
116        event: &EventView<'_, Self>,
117    ) -> Result<(), std::io::Error> {
118        Ok(())
119    }
120}
121
122/// A state change in the task tree.
123///
124/// # Example
125///
126/// ```rust
127/// use trace_tally::*;
128///
129/// struct MyRenderer;
130/// impl Renderer for MyRenderer {
131///     type EventData = String;
132///     type TaskData = String;
133/// }
134///
135/// let mut renderer = TaskRenderer::new(MyRenderer);
136/// renderer.update(Action::TaskStart {
137///     id: TaskId::from(2),
138///     parent: None,
139///     data: "my task".into(),
140/// });
141/// renderer.render(&mut std::io::stderr()).unwrap();
142/// ```
143#[derive(Debug, Clone)]
144pub enum Action<R: Renderer> {
145    /// A new event on an existing task (or the root if `parent` is `None`).
146    Event {
147        parent: Option<TaskId>,
148        data: R::EventData,
149    },
150    /// A new task has started.
151    ///
152    /// If `parent` is `None` or refers to an unknown ID, the task is
153    /// attached to the virtual root.
154    TaskStart {
155        id: TaskId,
156        parent: Option<TaskId>,
157        data: R::TaskData,
158    },
159    /// A task has completed.
160    TaskEnd { id: TaskId },
161    /// Mark all pending tasks as cancelled.
162    ///
163    /// Walks every task reachable from root and sets them as cancelled.
164    /// Cancelled tasks still render (via [`TaskView::cancelled`]) but are
165    /// flushed from the active frame on the next render, like completed tasks.
166    CancelAll,
167}