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