Skip to main content

morph_cli/core/execution/
mod.rs

1pub mod parallel;
2pub mod scheduler;
3
4pub use parallel::{Job, ParallelExecutor, WorkerPool, create_executor};
5pub use scheduler::{ExecutionMetrics, Task, TaskResult, TaskScheduler};
6
7use std::time::Duration;
8
9#[derive(Debug, Clone)]
10pub struct ExecutionConfig {
11    pub max_workers: usize,
12    pub sequential: bool,
13    pub chunk_size: usize,
14    pub timeout: Option<Duration>,
15}
16
17impl Default for ExecutionConfig {
18    fn default() -> Self {
19        Self {
20            max_workers: num_cpus(),
21            sequential: false,
22            chunk_size: 100,
23            timeout: None,
24        }
25    }
26}
27
28impl ExecutionConfig {
29    pub fn sequential() -> Self {
30        Self {
31            max_workers: 1,
32            sequential: true,
33            chunk_size: 1,
34            timeout: None,
35        }
36    }
37
38    pub fn with_workers(workers: usize) -> Self {
39        Self {
40            max_workers: workers.max(1),
41            sequential: workers == 1,
42            ..Default::default()
43        }
44    }
45}
46
47pub fn num_cpus() -> usize {
48    std::thread::available_parallelism()
49        .map(|n| n.get())
50        .unwrap_or(1)
51}
52
53pub struct ExecutionStats {
54    pub files_processed: usize,
55    pub total_time: Duration,
56    pub parse_time: Duration,
57    pub transform_time: Duration,
58}
59
60impl ExecutionStats {
61    pub fn throughput(&self) -> f64 {
62        let secs = self.total_time.as_secs_f64();
63        if secs > 0.0 {
64            self.files_processed as f64 / secs
65        } else {
66            0.0
67        }
68    }
69
70    pub fn summary(&self) -> String {
71        format!(
72            "Processed {} files in {:.2}s ({:.2} files/s)",
73            self.files_processed,
74            self.total_time.as_secs_f64(),
75            self.throughput()
76        )
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_execution_config_default() {
86        let config = ExecutionConfig::default();
87        assert!(config.max_workers >= 1);
88        assert!(!config.sequential);
89    }
90
91    #[test]
92    fn test_execution_config_sequential() {
93        let config = ExecutionConfig::sequential();
94        assert!(config.sequential);
95        assert_eq!(config.max_workers, 1);
96    }
97
98    #[test]
99    fn test_execution_config_with_workers() {
100        let config = ExecutionConfig::with_workers(4);
101        assert_eq!(config.max_workers, 4);
102    }
103
104    #[test]
105    fn test_execution_stats() {
106        let stats = ExecutionStats {
107            files_processed: 100,
108            total_time: Duration::from_secs(10),
109            parse_time: Duration::from_secs(3),
110            transform_time: Duration::from_secs(5),
111        };
112        assert_eq!(stats.throughput(), 10.0);
113    }
114
115    #[test]
116    fn test_num_cpus() {
117        let cpus = num_cpus();
118        assert!(cpus >= 1);
119    }
120}