morph_cli/core/execution/
mod.rs1pub 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}