Skip to main content

ralph_workflow/pipeline/
mod.rs

1//! Pipeline Execution Module
2//!
3//! This module contains the core pipeline execution infrastructure:
4//! - Types for tracking pipeline statistics and RAII cleanup
5//! - Model flag resolution utilities
6//! - Command execution helpers with fault-tolerant fallback chains
7//! - Timer utilities for tracking execution duration
8//! - Session management for agent continuation
9//! - Log file path management
10//!
11//! # Module Structure
12//!
13//! - [`model_flag`] - Model flag resolution and provider detection
14//! - [`runner`] - Pipeline runtime and command execution with fallback
15//! - [`types`] - Pipeline statistics tracking and RAII guards
16//! - [`session`] - Session extraction and continuation for XSD retries
17//! - [`logfile`] - Unified log file path creation, parsing, and discovery
18
19#![deny(unsafe_code)]
20
21mod clipboard;
22mod fallback;
23pub mod idle_timeout;
24pub mod logfile;
25mod model_flag;
26mod prompt;
27mod runner;
28pub mod session;
29mod types;
30
31#[cfg(any(test, feature = "test-utils"))]
32pub mod test_trait;
33
34pub use fallback::OutputValidator;
35pub use prompt::{run_with_prompt, PipelineRuntime, PromptCommand};
36pub use runner::{
37    run_with_fallback_and_validator, run_xsd_retry_with_session, FallbackConfig, XsdRetryConfig,
38};
39pub use types::{AgentPhaseGuard, Stats};
40
41// ===== Timer Utilities =====
42
43use std::time::{Duration, Instant};
44
45/// Timer for tracking execution duration
46#[derive(Clone)]
47pub struct Timer {
48    start_time: Instant,
49    phase_start: Instant,
50}
51
52impl Timer {
53    /// Create a new timer, starting now
54    pub fn new() -> Self {
55        let now = Instant::now();
56        Self {
57            start_time: now,
58            phase_start: now,
59        }
60    }
61
62    /// Start a new phase timer
63    pub fn start_phase(&mut self) {
64        self.phase_start = Instant::now();
65    }
66
67    /// Get elapsed time since timer start
68    pub fn elapsed(&self) -> Duration {
69        self.start_time.elapsed()
70    }
71
72    /// Get elapsed time since phase start
73    pub fn phase_elapsed(&self) -> Duration {
74        self.phase_start.elapsed()
75    }
76
77    /// Format a duration as "Xm YYs"
78    pub fn format_duration(duration: Duration) -> String {
79        let total_secs = duration.as_secs();
80        let mins = total_secs / 60;
81        let secs = total_secs % 60;
82        format!("{mins}m {secs:02}s")
83    }
84
85    /// Get formatted elapsed time since start
86    pub fn elapsed_formatted(&self) -> String {
87        Self::format_duration(self.elapsed())
88    }
89
90    /// Get formatted elapsed time since phase start
91    pub fn phase_elapsed_formatted(&self) -> String {
92        Self::format_duration(self.phase_elapsed())
93    }
94}
95
96impl Default for Timer {
97    fn default() -> Self {
98        Self::new()
99    }
100}
101
102#[cfg(test)]
103mod timer_tests {
104    use super::*;
105    use std::thread;
106
107    #[test]
108    fn test_format_duration_zero() {
109        let d = Duration::from_secs(0);
110        assert_eq!(Timer::format_duration(d), "0m 00s");
111    }
112
113    #[test]
114    fn test_format_duration_seconds() {
115        let d = Duration::from_secs(30);
116        assert_eq!(Timer::format_duration(d), "0m 30s");
117    }
118
119    #[test]
120    fn test_format_duration_minutes() {
121        let d = Duration::from_secs(65);
122        assert_eq!(Timer::format_duration(d), "1m 05s");
123    }
124
125    #[test]
126    fn test_format_duration_large() {
127        let d = Duration::from_secs(3661);
128        assert_eq!(Timer::format_duration(d), "61m 01s");
129    }
130
131    #[test]
132    fn test_timer_elapsed() {
133        let timer = Timer::new();
134        thread::sleep(Duration::from_millis(10));
135        assert!(timer.elapsed() >= Duration::from_millis(10));
136    }
137
138    #[test]
139    fn test_timer_phase() {
140        let mut timer = Timer::new();
141        thread::sleep(Duration::from_millis(10));
142        timer.start_phase();
143        thread::sleep(Duration::from_millis(10));
144        // Phase elapsed should be less than total elapsed
145        assert!(timer.phase_elapsed() < timer.elapsed());
146    }
147}
148
149#[cfg(test)]
150mod tests;