Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/integration/action_log.rs
1use crate::Event;
2use instant::Instant;
3use std::{io::Write, path::Path, time::Duration};
4 
5/// Well-known key used to store the `ActionLog` inside `StepDataMap`.
6pub const ACTION_LOG_KEY: &str = "action_log";
7 
8/// A single event recorded in the action log.
9pub struct ActionEntry {
10 /// Wall-clock instant this entry was recorded.
11 recorded_at: Instant,
12 /// Human-readable description of the event.
13 description: String,
14}
15 
16/// Returns a concise, human-readable description of an event for the action log.
17pub fn event_description(event: &Event) -> String {
18 match event {
19 Event::KeyDown { chars, .. } => format!("KeyDown '{chars}'"),
20 Event::TypedCharacters { chars } => format!("TypedCharacters '{chars}'"),
21 Event::LeftMouseDown { .. } => "LeftMouseDown".to_string(),
22 Event::LeftMouseUp { .. } => "LeftMouseUp".to_string(),
23 Event::LeftMouseDragged { .. } => "LeftMouseDragged".to_string(),
24 Event::RightMouseDown { .. } => "RightMouseDown".to_string(),
25 Event::MiddleMouseDown { .. } => "MiddleMouseDown".to_string(),
26 Event::MouseMoved { .. } => "MouseMoved".to_string(),
27 Event::ScrollWheel { .. } => "ScrollWheel".to_string(),
28 Event::ModifierStateChanged { .. } => "ModifierStateChanged".to_string(),
29 Event::ModifierKeyChanged { .. } => "ModifierKeyChanged".to_string(),
30 Event::DragAndDropFiles { .. } => "DragAndDropFiles".to_string(),
31 Event::DragFiles { .. } => "DragFiles".to_string(),
32 Event::DragFileExit => "DragFileExit".to_string(),
33 Event::SetMarkedText { .. } => "SetMarkedText".to_string(),
34 Event::ClearMarkedText => "ClearMarkedText".to_string(),
35 Event::ForwardMouseDown { .. } => "ForwardMouseDown".to_string(),
36 Event::BackMouseDown { .. } => "BackMouseDown".to_string(),
37 }
38}
39 
40/// Accumulates timestamped test events during an integration test run.
41///
42/// When recording is active, `write_to_file` renders each entry with its
43/// offset into the recording (e.g. `[+00:03.142]`). If recording was never
44/// started the offset is computed relative to the first entry instead so
45/// the log is still useful.
46#[derive(Default)]
47pub struct ActionLog {
48 entries: Vec<ActionEntry>,
49 recording_start: Option<Instant>,
50}
51 
52impl ActionLog {
53 pub fn new() -> Self {
54 Self::default()
55 }
56 
57 /// Marks the instant at which recording started. All log entries
58 /// will be displayed with offsets relative to this instant.
59 pub fn set_recording_start(&mut self, start: Instant) {
60 self.recording_start = Some(start);
61 }
62 
63 /// Appends an entry with the current wall-clock time.
64 pub fn record(&mut self, description: impl Into<String>) {
65 self.entries.push(ActionEntry {
66 recorded_at: Instant::now(),
67 description: description.into(),
68 });
69 }
70 
71 /// Writes the action log to a plain-text file.
72 ///
73 /// Each line has the form:
74 /// ```text
75 /// [+MM:SS.mmm] description
76 /// ```
77 /// The offset is relative to `recording_start` (or to the first entry's
78 /// timestamp if recording was never explicitly started).
79 pub fn write_to_file(&self, path: &Path) -> anyhow::Result<()> {
80 if self.entries.is_empty() {
81 return Ok(());
82 }
83 
84 if let Some(parent) = path.parent() {
85 std::fs::create_dir_all(parent)?;
86 }
87 
88 let base = self.recording_start.unwrap_or(self.entries[0].recorded_at);
89 
90 let file = std::fs::File::create(path)?;
91 let mut writer = std::io::BufWriter::new(file);
92 
93 for entry in &self.entries {
94 let offset = entry
95 .recorded_at
96 .checked_duration_since(base)
97 .unwrap_or(Duration::ZERO);
98 let total_secs = offset.as_secs();
99 let minutes = total_secs / 60;
100 let seconds = total_secs % 60;
101 let millis = offset.subsec_millis();
102 writeln!(
103 writer,
104 "[+{minutes:02}:{seconds:02}.{millis:03}] {}",
105 entry.description
106 )?;
107 }
108 
109 log::info!(
110 "ActionLog: wrote {} entries to {}",
111 self.entries.len(),
112 path.display()
113 );
114 Ok(())
115 }
116}
117 
118/// Helper to retrieve a mutable reference to the log from a `StepDataMap`.
119pub fn get_action_log_mut(step_data_map: &mut super::step::StepDataMap) -> Option<&mut ActionLog> {
120 step_data_map.get_mut::<_, ActionLog>(ACTION_LOG_KEY)
121}
122 
123/// Helper to retrieve a shared reference to the log from a `StepDataMap`.
124pub fn get_action_log(step_data_map: &super::step::StepDataMap) -> Option<&ActionLog> {
125 step_data_map.get::<_, ActionLog>(ACTION_LOG_KEY)
126}
127