async_inspect/timeline/
mod.rs

1//! Execution timeline tracking
2//!
3//! This module provides event tracking and timeline management for async operations.
4
5use crate::task::{TaskId, TaskState};
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::time::{Duration, Instant};
9
10/// Unique identifier for an event
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct EventId(u64);
13
14impl EventId {
15    /// Create a new event ID
16    #[must_use]
17    pub fn new(id: u64) -> Self {
18        Self(id)
19    }
20
21    /// Get the raw u64 value
22    #[must_use]
23    pub fn as_u64(&self) -> u64 {
24        self.0
25    }
26}
27
28/// Type of event that occurred
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub enum EventKind {
31    /// A new task was spawned
32    TaskSpawned {
33        /// Task name
34        name: String,
35        /// Parent task, if any
36        parent: Option<TaskId>,
37        /// Source location
38        location: Option<String>,
39    },
40
41    /// Task started being polled
42    PollStarted,
43
44    /// Task finished being polled
45    PollEnded {
46        /// Time spent in this poll
47        duration: Duration,
48    },
49
50    /// Task started waiting at an await point
51    AwaitStarted {
52        /// Name/description of what we're waiting for
53        await_point: String,
54        /// Source location
55        location: Option<String>,
56    },
57
58    /// Task finished waiting at an await point
59    AwaitEnded {
60        /// Name of the await point
61        await_point: String,
62        /// How long we waited
63        duration: Duration,
64    },
65
66    /// Task completed successfully
67    TaskCompleted {
68        /// Total task duration
69        duration: Duration,
70    },
71
72    /// Task failed or was cancelled
73    TaskFailed {
74        /// Error message, if any
75        error: Option<String>,
76    },
77
78    /// Custom inspection point
79    InspectionPoint {
80        /// Label for this point
81        label: String,
82        /// Optional message
83        message: Option<String>,
84    },
85
86    /// State change event
87    StateChanged {
88        /// Previous state
89        old_state: TaskState,
90        /// New state
91        new_state: TaskState,
92    },
93}
94
95impl fmt::Display for EventKind {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            Self::TaskSpawned { name, .. } => write!(f, "Spawned: {name}"),
99            Self::PollStarted => write!(f, "Poll started"),
100            Self::PollEnded { duration } => {
101                write!(f, "Poll ended ({:.2}ms)", duration.as_secs_f64() * 1000.0)
102            }
103            Self::AwaitStarted { await_point, .. } => write!(f, "Await started: {await_point}"),
104            Self::AwaitEnded {
105                await_point,
106                duration,
107            } => {
108                write!(
109                    f,
110                    "Await ended: {} ({:.2}ms)",
111                    await_point,
112                    duration.as_secs_f64() * 1000.0
113                )
114            }
115            Self::TaskCompleted { duration } => {
116                write!(f, "Completed ({:.2}s)", duration.as_secs_f64())
117            }
118            Self::TaskFailed { error } => {
119                if let Some(err) = error {
120                    write!(f, "Failed: {err}")
121                } else {
122                    write!(f, "Failed")
123                }
124            }
125            Self::InspectionPoint { label, message } => {
126                if let Some(msg) = message {
127                    write!(f, "Inspection[{label}]: {msg}")
128                } else {
129                    write!(f, "Inspection[{label}]")
130                }
131            }
132            Self::StateChanged {
133                old_state,
134                new_state,
135            } => {
136                write!(f, "State: {old_state} → {new_state}")
137            }
138        }
139    }
140}
141
142/// An event that occurred during async execution
143#[derive(Debug, Clone)]
144pub struct Event {
145    /// Unique event identifier
146    pub id: EventId,
147
148    /// Task this event belongs to
149    pub task_id: TaskId,
150
151    /// When the event occurred
152    pub timestamp: Instant,
153
154    /// Type and details of the event
155    pub kind: EventKind,
156}
157
158impl Event {
159    /// Create a new event
160    #[must_use]
161    pub fn new(id: u64, task_id: TaskId, kind: EventKind) -> Self {
162        Self {
163            id: EventId::new(id),
164            task_id,
165            timestamp: Instant::now(),
166            kind,
167        }
168    }
169
170    /// Get the age of this event
171    #[must_use]
172    pub fn age(&self) -> Duration {
173        self.timestamp.elapsed()
174    }
175}
176
177impl fmt::Display for Event {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        write!(
180            f,
181            "[{:.3}s] Task {}: {}",
182            self.age().as_secs_f64(),
183            self.task_id,
184            self.kind
185        )
186    }
187}
188
189/// Timeline of events
190#[derive(Debug, Default)]
191pub struct Timeline {
192    /// All events in chronological order
193    events: Vec<Event>,
194
195    /// Start time of the timeline
196    start_time: Option<Instant>,
197}
198
199impl Timeline {
200    /// Create a new timeline
201    #[must_use]
202    pub fn new() -> Self {
203        Self {
204            events: Vec::new(),
205            start_time: None,
206        }
207    }
208
209    /// Add an event to the timeline
210    pub fn add_event(&mut self, event: Event) {
211        if self.start_time.is_none() {
212            self.start_time = Some(event.timestamp);
213        }
214        self.events.push(event);
215    }
216
217    /// Get all events
218    #[must_use]
219    pub fn events(&self) -> &[Event] {
220        &self.events
221    }
222
223    /// Get events for a specific task
224    #[must_use]
225    pub fn events_for_task(&self, task_id: TaskId) -> Vec<&Event> {
226        self.events
227            .iter()
228            .filter(|e| e.task_id == task_id)
229            .collect()
230    }
231
232    /// Get the total duration of the timeline
233    #[must_use]
234    pub fn duration(&self) -> Duration {
235        self.start_time
236            .map_or(Duration::ZERO, |start| start.elapsed())
237    }
238
239    /// Get number of events
240    #[must_use]
241    pub fn len(&self) -> usize {
242        self.events.len()
243    }
244
245    /// Check if timeline is empty
246    #[must_use]
247    pub fn is_empty(&self) -> bool {
248        self.events.is_empty()
249    }
250
251    /// Clear all events
252    pub fn clear(&mut self) {
253        self.events.clear();
254        self.start_time = None;
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crate::task::TaskId;
262
263    #[test]
264    fn test_timeline_creation() {
265        let timeline = Timeline::new();
266        assert!(timeline.is_empty());
267        assert_eq!(timeline.len(), 0);
268    }
269
270    #[test]
271    fn test_add_event() {
272        let mut timeline = Timeline::new();
273        let task_id = TaskId::new();
274        let event = Event::new(
275            1,
276            task_id,
277            EventKind::TaskSpawned {
278                name: "test".to_string(),
279                parent: None,
280                location: None,
281            },
282        );
283
284        timeline.add_event(event);
285        assert_eq!(timeline.len(), 1);
286    }
287
288    #[test]
289    fn test_events_for_task() {
290        let mut timeline = Timeline::new();
291        let task1 = TaskId::new();
292        let task2 = TaskId::new();
293
294        timeline.add_event(Event::new(1, task1, EventKind::PollStarted));
295        timeline.add_event(Event::new(2, task2, EventKind::PollStarted));
296        timeline.add_event(Event::new(
297            3,
298            task1,
299            EventKind::PollEnded {
300                duration: Duration::from_millis(10),
301            },
302        ));
303
304        let task1_events = timeline.events_for_task(task1);
305        assert_eq!(task1_events.len(), 2);
306    }
307}