Skip to main content

tailwind_rs_scanner/
parallel_processor.rs

1//! Parallel processing implementation
2//!
3//! This module provides parallel file processing capabilities for
4//! efficient content scanning.
5
6use crate::class_extractor::ClassExtractor;
7use crate::class_extractor::ExtractedClass;
8use crate::content_config::ScanConfig;
9use crate::error::{Result, ScannerError};
10use crate::file_scanner::FileInfo;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::path::PathBuf;
14use std::time::Instant;
15
16/// Parallel processor for file processing
17#[derive(Debug)]
18pub struct ParallelProcessor {
19    /// Whether parallel processing is enabled
20    enabled: bool,
21    /// Maximum number of workers
22    max_workers: Option<usize>,
23}
24
25/// Processing statistics
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub struct ProcessingStats {
28    /// Total number of files
29    pub total_files: usize,
30    /// Number of processed files
31    pub processed_files: usize,
32    /// Total number of classes found
33    pub total_classes: usize,
34    /// Number of unique classes
35    pub unique_classes: usize,
36    /// Total processing time
37    pub total_time: std::time::Duration,
38    /// Average time per file
39    pub average_file_time: std::time::Duration,
40    /// Memory usage in bytes
41    pub memory_usage: usize,
42}
43
44// Default implementation is in file_scanner.rs
45
46impl ParallelProcessor {
47    /// Create a new parallel processor
48    pub fn new(enabled: bool) -> Self {
49        Self {
50            enabled,
51            max_workers: None,
52        }
53    }
54
55    /// Create with maximum workers
56    pub fn with_max_workers(enabled: bool, max_workers: usize) -> Self {
57        Self {
58            enabled,
59            max_workers: Some(max_workers),
60        }
61    }
62
63    /// Process files in parallel
64    pub async fn process_files(
65        &self,
66        files: &[FileInfo],
67        extractor: &ClassExtractor,
68        config: &ScanConfig,
69    ) -> Result<HashMap<PathBuf, Vec<ExtractedClass>>> {
70        let start_time = Instant::now();
71        let mut results = HashMap::new();
72
73        if self.enabled && files.len() > 1 {
74            // Parallel processing
75            self.process_files_parallel(files, extractor, &mut results)
76                .await?;
77        } else {
78            // Sequential processing
79            self.process_files_sequential(files, extractor, &mut results)
80                .await?;
81        }
82
83        Ok(results)
84    }
85
86    /// Process files sequentially
87    async fn process_files_sequential(
88        &self,
89        files: &[FileInfo],
90        extractor: &ClassExtractor,
91        results: &mut HashMap<PathBuf, Vec<ExtractedClass>>,
92    ) -> Result<()> {
93        for file in files {
94            if let Ok(classes) = extractor.extract_classes(file).await {
95                results.insert(file.path.clone(), classes);
96            }
97        }
98        Ok(())
99    }
100
101    /// Process files in parallel
102    async fn process_files_parallel(
103        &self,
104        files: &[FileInfo],
105        extractor: &ClassExtractor,
106        results: &mut HashMap<PathBuf, Vec<ExtractedClass>>,
107    ) -> Result<()> {
108        // For now, use sequential processing
109        // In a real implementation, this would use rayon or tokio for parallel processing
110        self.process_files_sequential(files, extractor, results)
111            .await
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_parallel_processor_creation() {
121        let processor = ParallelProcessor::new(true);
122        assert!(processor.enabled);
123    }
124
125    #[test]
126    fn test_parallel_processor_with_max_workers() {
127        let processor = ParallelProcessor::with_max_workers(true, 4);
128        assert!(processor.enabled);
129        assert_eq!(processor.max_workers, Some(4));
130    }
131
132    #[test]
133    fn test_processing_stats_default() {
134        let stats = ProcessingStats::default();
135        assert_eq!(stats.total_files, 0);
136        assert_eq!(stats.processed_files, 0);
137        assert_eq!(stats.total_classes, 0);
138    }
139}