morph-cli 0.1.0

AST-based codebase migration and codemod tool for JavaScript and TypeScript projects.
Documentation
pub mod parallel;
pub mod scheduler;

pub use parallel::{Job, ParallelExecutor, WorkerPool, create_executor};
pub use scheduler::{ExecutionMetrics, Task, TaskResult, TaskScheduler};

use std::time::Duration;

#[derive(Debug, Clone)]
pub struct ExecutionConfig {
    pub max_workers: usize,
    pub sequential: bool,
    pub chunk_size: usize,
    pub timeout: Option<Duration>,
}

impl Default for ExecutionConfig {
    fn default() -> Self {
        Self {
            max_workers: num_cpus(),
            sequential: false,
            chunk_size: 100,
            timeout: None,
        }
    }
}

impl ExecutionConfig {
    pub fn sequential() -> Self {
        Self {
            max_workers: 1,
            sequential: true,
            chunk_size: 1,
            timeout: None,
        }
    }

    pub fn with_workers(workers: usize) -> Self {
        Self {
            max_workers: workers.max(1),
            sequential: workers == 1,
            ..Default::default()
        }
    }
}

pub fn num_cpus() -> usize {
    std::thread::available_parallelism()
        .map(|n| n.get())
        .unwrap_or(1)
}

pub struct ExecutionStats {
    pub files_processed: usize,
    pub total_time: Duration,
    pub parse_time: Duration,
    pub transform_time: Duration,
}

impl ExecutionStats {
    pub fn throughput(&self) -> f64 {
        let secs = self.total_time.as_secs_f64();
        if secs > 0.0 {
            self.files_processed as f64 / secs
        } else {
            0.0
        }
    }

    pub fn summary(&self) -> String {
        format!(
            "Processed {} files in {:.2}s ({:.2} files/s)",
            self.files_processed,
            self.total_time.as_secs_f64(),
            self.throughput()
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_execution_config_default() {
        let config = ExecutionConfig::default();
        assert!(config.max_workers >= 1);
        assert!(!config.sequential);
    }

    #[test]
    fn test_execution_config_sequential() {
        let config = ExecutionConfig::sequential();
        assert!(config.sequential);
        assert_eq!(config.max_workers, 1);
    }

    #[test]
    fn test_execution_config_with_workers() {
        let config = ExecutionConfig::with_workers(4);
        assert_eq!(config.max_workers, 4);
    }

    #[test]
    fn test_execution_stats() {
        let stats = ExecutionStats {
            files_processed: 100,
            total_time: Duration::from_secs(10),
            parse_time: Duration::from_secs(3),
            transform_time: Duration::from_secs(5),
        };
        assert_eq!(stats.throughput(), 10.0);
    }

    #[test]
    fn test_num_cpus() {
        let cpus = num_cpus();
        assert!(cpus >= 1);
    }
}