tailwind_rs_scanner/
parallel_processor.rs1use 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#[derive(Debug)]
18pub struct ParallelProcessor {
19 enabled: bool,
21 max_workers: Option<usize>,
23}
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub struct ProcessingStats {
28 pub total_files: usize,
30 pub processed_files: usize,
32 pub total_classes: usize,
34 pub unique_classes: usize,
36 pub total_time: std::time::Duration,
38 pub average_file_time: std::time::Duration,
40 pub memory_usage: usize,
42}
43
44impl ParallelProcessor {
47 pub fn new(enabled: bool) -> Self {
49 Self {
50 enabled,
51 max_workers: None,
52 }
53 }
54
55 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 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 self.process_files_parallel(files, extractor, &mut results)
76 .await?;
77 } else {
78 self.process_files_sequential(files, extractor, &mut results)
80 .await?;
81 }
82
83 Ok(results)
84 }
85
86 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 async fn process_files_parallel(
103 &self,
104 files: &[FileInfo],
105 extractor: &ClassExtractor,
106 results: &mut HashMap<PathBuf, Vec<ExtractedClass>>,
107 ) -> Result<()> {
108 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}