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