Skip to main content

trace_tally/
view.rs

1use std::io::Write;
2use std::time::Duration;
3
4use crate::task::{EventIndex, Task, TaskStore};
5use crate::{Renderer, TaskId};
6
7/// Write target with ANSI cursor control for frame clearing.
8///
9/// Wraps an [`std::io::Write`] target. Use `write!` / `writeln!` to produce
10/// output within renderer callbacks.
11///
12/// ```rust,ignore
13/// fn render_task_line(
14///     &mut self, f: &mut FrameWriter<'_>, task: &TaskView<'_, Self>,
15/// ) -> std::io::Result<()> {
16///     writeln!(f, "Task: {}", task.data())
17/// }
18/// ```
19pub struct FrameWriter<'a> {
20    target: &'a mut dyn Write,
21    frame_lines: usize,
22}
23
24impl<'a> FrameWriter<'a> {
25    pub(crate) fn new(target: &'a mut dyn Write, frame_lines: usize) -> Self {
26        Self {
27            target,
28            frame_lines,
29        }
30    }
31
32    pub(crate) fn clear_frame(&mut self) -> Result<(), std::io::Error> {
33        let lines_drawn = self.frame_lines;
34        if lines_drawn > 0 {
35            write!(self, "\r\x1b[{}A\x1b[2K\x1b[J", lines_drawn).unwrap();
36            self.target.flush()?;
37        }
38        self.frame_lines = 0;
39        Ok(())
40    }
41
42    pub(crate) fn frame_lines(&self) -> usize {
43        self.frame_lines
44    }
45}
46
47impl<'a> Write for FrameWriter<'a> {
48    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
49        let newlines = buf.iter().filter(|&&b| b == b'\n').count();
50        self.frame_lines += newlines;
51        self.target.write(buf)
52    }
53
54    fn flush(&mut self) -> std::io::Result<()> {
55        self.target.flush()
56    }
57}
58
59/// Read-only view of a task, passed to [`Renderer`] callbacks.
60///
61/// ```rust,ignore
62/// fn render_task_line(
63///     &mut self, f: &mut FrameWriter<'_>, task: &TaskView<'_, Self>,
64/// ) -> std::io::Result<()> {
65///     let prefix = if task.active() { ".." } else { "done" };
66///     writeln!(f, "[{}] {} (depth={})", prefix, task.data(), task.depth())
67/// }
68/// ```
69#[derive(Clone, Copy)]
70pub struct TaskView<'a, R: Renderer> {
71    id: TaskId,
72    tasks: &'a TaskStore<R>,
73}
74
75impl<'a, R: Renderer> TaskView<'a, R> {
76    /// Creates a view over the task with the given `id`.
77    pub fn new(tasks: &'a TaskStore<R>, id: TaskId) -> Self {
78        Self { id, tasks }
79    }
80
81    /// Returns TaskId of this task.
82    pub fn id(&self) -> TaskId {
83        self.id
84    }
85
86    /// Returns the user-defined data stored on this task.
87    pub fn data(&self) -> &R::TaskData {
88        self.tasks.task(&self.id).data.as_ref().unwrap()
89    }
90
91    /// Returns the nesting depth of this task (root children are depth 1).
92    pub fn depth(&self) -> usize {
93        self.tasks.task(&self.id).depth
94    }
95
96    /// How long since this task started.
97    pub fn elapsed(&self) -> Duration {
98        self.tasks.task(&self.id).started_at.elapsed()
99    }
100
101    /// Returns `true` if the task is active (not completed or cancelled).
102    pub fn active(&self) -> bool {
103        !self.completed() && !self.cancelled()
104    }
105
106    /// Returns `true` if the task's span has closed.
107    pub fn completed(&self) -> bool {
108        self.tasks.task(&self.id).completed
109    }
110
111    /// Returns `true` if the task was marked cancelled by [`crate::Action::CancelAll`].
112    pub fn cancelled(&self) -> bool {
113        self.tasks.task(&self.id).cancelled
114    }
115
116    /// Returns the parent task, or `None` for root-level tasks.
117    pub fn parent<'b>(&'b self) -> Option<TaskView<'b, R>> {
118        self.tasks
119            .task(&self.id)
120            .parent
121            .map(|id| TaskView::new(self.tasks, id))
122    }
123
124    /// Returns an iterator over the task's buffered events.
125    pub fn events<'b>(
126        &'b self,
127    ) -> impl DoubleEndedIterator<Item = EventView<'b, R>> + ExactSizeIterator {
128        (0..self.tasks.task(&self.id).events.len())
129            .map(move |id| EventView::new(self.tasks, self.id, id))
130    }
131
132    /// Returns an iterator over the task's direct children.
133    pub fn subtasks<'b>(
134        &'b self,
135    ) -> impl DoubleEndedIterator<Item = TaskView<'b, R>> + ExactSizeIterator {
136        self.tasks
137            .task(&self.id)
138            .subtasks
139            .iter()
140            .map(move |id| TaskView::new(self.tasks, *id))
141    }
142
143    /// Returns the position of this task among its parent's children.
144    pub fn index(&self) -> usize {
145        let parent = self.tasks.task(&self.id).parent.unwrap();
146        let parent = self.tasks.task(&parent);
147        parent.subtasks.get_index_of(&self.id).unwrap()
148    }
149
150    /// Create a view of another task in the same tree.
151    pub fn view(&self, id: TaskId) -> TaskView<'a, R> {
152        TaskView::new(self.tasks, id)
153    }
154}
155
156/// Read-only view of a buffered event, passed to [`Renderer::render_event_line`].
157///
158/// ```rust,ignore
159/// fn render_event_line(
160///     &mut self, f: &mut FrameWriter<'_>, event: &EventView<'_, Self>,
161/// ) -> std::io::Result<()> {
162///     writeln!(f, "{}> {}", " ".repeat(event.depth()), event.data())
163/// }
164/// ```
165pub struct EventView<'a, R: Renderer> {
166    tasks: &'a TaskStore<R>,
167    task: TaskId,
168    id: EventIndex,
169}
170
171impl<'a, R: Renderer> EventView<'a, R> {
172    pub(crate) fn new(tasks: &'a TaskStore<R>, task: TaskId, id: usize) -> Self {
173        Self {
174            tasks,
175            task,
176            id: EventIndex(id),
177        }
178    }
179
180    /// Returns `true` if this event belongs to the virtual root task.
181    pub fn is_root(&self) -> bool {
182        self.task == TaskId::ROOT
183    }
184
185    /// Returns the user-defined data stored on this event.
186    pub fn data(&self) -> &R::EventData {
187        let task = self.get_task();
188        task.events().get(self.id.0).unwrap()
189    }
190
191    /// Returns the nesting depth of the owning task.
192    pub fn depth(&self) -> usize {
193        self.get_task().depth
194    }
195
196    /// Returns the TaskView of the owning task.
197    pub fn task<'b>(&'b self) -> TaskView<'b, R> {
198        TaskView::new(self.tasks, self.task)
199    }
200
201    fn get_task(&self) -> &Task<R> {
202        self.tasks.task(&self.task)
203    }
204}