1use crate::error::Result;
7use lru::LruCache;
8use parking_lot::RwLock;
9use std::collections::HashMap;
10use std::sync::atomic::{AtomicU64, Ordering};
11use std::sync::Arc;
12use std::time::{Duration, Instant};
13
14#[derive(Debug, Clone)]
16pub struct PerformanceMetric {
17 pub operation: String,
18 pub duration: Duration,
19 pub timestamp: Instant,
20 pub success: bool,
21}
22
23#[derive(Debug, Clone)]
25pub struct ErrorMetric {
26 pub error_type: String,
27 pub error_message: String,
28 pub timestamp: Instant,
29 pub count: u64,
30}
31
32#[derive(Debug, Clone)]
34pub struct UsageMetric {
35 pub class_pattern: String,
36 pub usage_count: u64,
37 pub last_used: Instant,
38 pub average_generation_time: Duration,
39}
40
41#[derive(Debug)]
43pub struct ClassCache {
44 cache: Arc<RwLock<LruCache<String, String>>>,
45 hit_rate: AtomicU64,
46 miss_rate: AtomicU64,
47 total_requests: AtomicU64,
48}
49
50impl ClassCache {
51 pub fn new(capacity: usize) -> Self {
53 Self {
54 cache: Arc::new(RwLock::new(LruCache::new(
55 std::num::NonZeroUsize::new(capacity)
56 .unwrap_or(std::num::NonZeroUsize::new(100).unwrap()),
57 ))),
58 hit_rate: AtomicU64::new(0),
59 miss_rate: AtomicU64::new(0),
60 total_requests: AtomicU64::new(0),
61 }
62 }
63
64 pub fn get(&self, key: &str) -> Option<String> {
66 self.total_requests.fetch_add(1, Ordering::Relaxed);
67
68 let mut cache = self.cache.write();
69 if let Some(value) = cache.get(key) {
70 self.hit_rate.fetch_add(1, Ordering::Relaxed);
71 Some(value.clone())
72 } else {
73 self.miss_rate.fetch_add(1, Ordering::Relaxed);
74 None
75 }
76 }
77
78 pub fn put(&self, key: String, value: String) {
80 let mut cache = self.cache.write();
81 cache.put(key, value);
82 }
83
84 pub fn hit_rate(&self) -> f64 {
86 let hits = self.hit_rate.load(Ordering::Relaxed) as f64;
87 let total = self.total_requests.load(Ordering::Relaxed) as f64;
88
89 if total == 0.0 {
90 0.0
91 } else {
92 hits / total
93 }
94 }
95
96 pub fn miss_rate(&self) -> f64 {
98 let misses = self.miss_rate.load(Ordering::Relaxed) as f64;
99 let total = self.total_requests.load(Ordering::Relaxed) as f64;
100
101 if total == 0.0 {
102 0.0
103 } else {
104 misses / total
105 }
106 }
107
108 pub fn total_requests(&self) -> u64 {
110 self.total_requests.load(Ordering::Relaxed)
111 }
112
113 pub fn clear(&self) {
115 let mut cache = self.cache.write();
116 cache.clear();
117 }
118
119 pub fn len(&self) -> usize {
121 let cache = self.cache.read();
122 cache.len()
123 }
124
125 pub fn is_empty(&self) -> bool {
127 let cache = self.cache.read();
128 cache.is_empty()
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum OptimizationLevel {
135 None,
136 Low,
137 Medium,
138 High,
139 Maximum,
140}
141
142impl OptimizationLevel {
143 pub fn cache_capacity(&self) -> usize {
145 match self {
146 OptimizationLevel::None => 0,
147 OptimizationLevel::Low => 100,
148 OptimizationLevel::Medium => 500,
149 OptimizationLevel::High => 1000,
150 OptimizationLevel::Maximum => 5000,
151 }
152 }
153
154 pub fn optimization_factor(&self) -> f64 {
156 match self {
157 OptimizationLevel::None => 1.0,
158 OptimizationLevel::Low => 1.2,
159 OptimizationLevel::Medium => 1.5,
160 OptimizationLevel::High => 2.0,
161 OptimizationLevel::Maximum => 3.0,
162 }
163 }
164}
165
166#[derive(Debug)]
168pub struct PerformanceOptimizer {
169 class_cache: ClassCache,
170 css_cache: ClassCache,
171 optimization_level: OptimizationLevel,
172 performance_metrics: Arc<RwLock<Vec<PerformanceMetric>>>,
173 error_metrics: Arc<RwLock<Vec<ErrorMetric>>>,
174 usage_metrics: Arc<RwLock<HashMap<String, UsageMetric>>>,
175}
176
177impl PerformanceOptimizer {
178 pub fn new() -> Self {
180 Self::with_optimization_level(OptimizationLevel::Medium)
181 }
182
183 pub fn with_optimization_level(level: OptimizationLevel) -> Self {
185 let capacity = level.cache_capacity();
186 Self {
187 class_cache: ClassCache::new(capacity),
188 css_cache: ClassCache::new(capacity),
189 optimization_level: level,
190 performance_metrics: Arc::new(RwLock::new(Vec::new())),
191 error_metrics: Arc::new(RwLock::new(Vec::new())),
192 usage_metrics: Arc::new(RwLock::new(HashMap::new())),
193 }
194 }
195
196 pub fn optimize_class_generation(&mut self, classes: &[String]) -> Result<String> {
198 let start = Instant::now();
199 let cache_key = self.generate_cache_key(classes);
200
201 if let Some(cached_result) = self.class_cache.get(&cache_key) {
203 self.record_performance("class_generation_cached", start.elapsed(), true);
204 return Ok(cached_result);
205 }
206
207 let result = self.generate_classes(classes)?;
209
210 self.class_cache.put(cache_key, result.clone());
212
213 self.record_performance("class_generation", start.elapsed(), true);
214 Ok(result)
215 }
216
217 pub fn optimize_css_generation(&mut self, css: &str) -> Result<String> {
219 let start = Instant::now();
220 let cache_key = format!("css:{}", css);
221
222 if let Some(cached_result) = self.css_cache.get(&cache_key) {
224 self.record_performance("css_generation_cached", start.elapsed(), true);
225 return Ok(cached_result);
226 }
227
228 let result = self.optimize_css(css)?;
230
231 self.css_cache.put(cache_key, result.clone());
233
234 self.record_performance("css_generation", start.elapsed(), true);
235 Ok(result)
236 }
237
238 fn generate_cache_key(&self, classes: &[String]) -> String {
240 let mut key = String::new();
241 for class in classes {
242 key.push_str(class);
243 key.push('|');
244 }
245 key
246 }
247
248 fn generate_classes(&self, classes: &[String]) -> Result<String> {
250 let optimized_classes = match self.optimization_level {
252 OptimizationLevel::None => classes.to_vec(),
253 OptimizationLevel::Low => self.optimize_classes_low(classes),
254 OptimizationLevel::Medium => self.optimize_classes_medium(classes),
255 OptimizationLevel::High => self.optimize_classes_high(classes),
256 OptimizationLevel::Maximum => self.optimize_classes_maximum(classes),
257 };
258
259 Ok(optimized_classes.join(" "))
260 }
261
262 fn optimize_classes_low(&self, classes: &[String]) -> Vec<String> {
264 let mut unique_classes: Vec<String> = classes.to_vec();
266 unique_classes.sort();
267 unique_classes.dedup();
268 unique_classes
269 }
270
271 fn optimize_classes_medium(&self, classes: &[String]) -> Vec<String> {
273 let mut optimized = self.optimize_classes_low(classes);
274
275 optimized = self.remove_conflicting_classes(optimized);
277
278 optimized
279 }
280
281 fn optimize_classes_high(&self, classes: &[String]) -> Vec<String> {
283 let mut optimized = self.optimize_classes_medium(classes);
284
285 optimized = self.merge_similar_classes(optimized);
287
288 optimized
289 }
290
291 fn optimize_classes_maximum(&self, classes: &[String]) -> Vec<String> {
293 let mut optimized = self.optimize_classes_high(classes);
294
295 optimized = self.apply_advanced_optimizations(optimized);
297
298 optimized
299 }
300
301 fn remove_conflicting_classes(&self, classes: Vec<String>) -> Vec<String> {
303 let mut result = Vec::new();
304 let mut seen_groups: HashMap<String, String> = HashMap::new();
305
306 for class in classes {
307 let group = self.get_class_group(&class);
308 if let Some(existing) = seen_groups.get(&group) {
309 if self.is_more_specific(&class, existing) {
311 if let Some(pos) = result.iter().position(|c| c == existing) {
312 result.remove(pos);
313 }
314 result.push(class.clone());
315 seen_groups.insert(group, class);
316 }
317 } else {
318 result.push(class.clone());
319 seen_groups.insert(group, class);
320 }
321 }
322
323 result
324 }
325
326 fn merge_similar_classes(&self, classes: Vec<String>) -> Vec<String> {
328 classes
332 }
333
334 fn apply_advanced_optimizations(&self, classes: Vec<String>) -> Vec<String> {
336 classes
342 }
343
344 fn get_class_group(&self, class: &str) -> String {
346 if class.starts_with("bg-") {
347 "background".to_string()
348 } else if class.starts_with("text-") {
349 "text".to_string()
350 } else if class.starts_with("border-") {
351 "border".to_string()
352 } else if class.starts_with("p-") || class.starts_with("px-") || class.starts_with("py-") {
353 "padding".to_string()
354 } else if class.starts_with("m-") || class.starts_with("mx-") || class.starts_with("my-") {
355 "margin".to_string()
356 } else {
357 "other".to_string()
358 }
359 }
360
361 fn is_more_specific(&self, class1: &str, class2: &str) -> bool {
363 class1.len() > class2.len()
365 }
366
367 fn optimize_css(&self, css: &str) -> Result<String> {
369 let mut optimized = css.to_string();
370
371 optimized = optimized.replace(" ", " ");
373 optimized = optimized.replace("\n", "");
374 optimized = optimized.replace("\t", "");
375
376 optimized = optimized.replace(";}", "}");
378
379 Ok(optimized)
380 }
381
382 pub fn record_performance(&self, operation: &str, duration: Duration, success: bool) {
384 let metric = PerformanceMetric {
385 operation: operation.to_string(),
386 duration,
387 timestamp: Instant::now(),
388 success,
389 };
390
391 let mut metrics = self.performance_metrics.write();
392 metrics.push(metric);
393
394 let len = metrics.len();
396 if len > 1000 {
397 metrics.drain(0..len - 1000);
398 }
399 }
400
401 pub fn record_error(&self, error_type: &str, error: &dyn std::error::Error) {
403 let metric = ErrorMetric {
404 error_type: error_type.to_string(),
405 error_message: error.to_string(),
406 timestamp: Instant::now(),
407 count: 1,
408 };
409
410 let mut metrics = self.error_metrics.write();
411 metrics.push(metric);
412
413 let len = metrics.len();
415 if len > 1000 {
416 metrics.drain(0..len - 1000);
417 }
418 }
419
420 pub fn record_usage(&self, class_pattern: &str, generation_time: Duration) {
422 let mut metrics = self.usage_metrics.write();
423
424 if let Some(usage) = metrics.get_mut(class_pattern) {
425 usage.usage_count += 1;
426 usage.last_used = Instant::now();
427 usage.average_generation_time = Duration::from_nanos(
428 ((usage.average_generation_time.as_nanos() + generation_time.as_nanos()) / 2)
429 as u64,
430 );
431 } else {
432 metrics.insert(
433 class_pattern.to_string(),
434 UsageMetric {
435 class_pattern: class_pattern.to_string(),
436 usage_count: 1,
437 last_used: Instant::now(),
438 average_generation_time: generation_time,
439 },
440 );
441 }
442 }
443
444 pub fn get_performance_metrics(&self) -> Vec<PerformanceMetric> {
446 let metrics = self.performance_metrics.read();
447 metrics.clone()
448 }
449
450 pub fn get_error_metrics(&self) -> Vec<ErrorMetric> {
452 let metrics = self.error_metrics.read();
453 metrics.clone()
454 }
455
456 pub fn get_usage_metrics(&self) -> HashMap<String, UsageMetric> {
458 let metrics = self.usage_metrics.read();
459 metrics.clone()
460 }
461
462 pub fn get_cache_stats(&self) -> CacheStats {
464 CacheStats {
465 class_cache_hit_rate: self.class_cache.hit_rate(),
466 class_cache_miss_rate: self.class_cache.miss_rate(),
467 class_cache_total_requests: self.class_cache.total_requests(),
468 class_cache_size: self.class_cache.len(),
469 css_cache_hit_rate: self.css_cache.hit_rate(),
470 css_cache_miss_rate: self.css_cache.miss_rate(),
471 css_cache_total_requests: self.css_cache.total_requests(),
472 css_cache_size: self.css_cache.len(),
473 }
474 }
475
476 pub fn set_optimization_level(&mut self, level: OptimizationLevel) {
478 self.optimization_level = level;
479 }
480
481 pub fn optimization_level(&self) -> OptimizationLevel {
483 self.optimization_level
484 }
485}
486
487impl Default for PerformanceOptimizer {
488 fn default() -> Self {
489 Self::new()
490 }
491}
492
493#[derive(Debug, Clone)]
495pub struct CacheStats {
496 pub class_cache_hit_rate: f64,
497 pub class_cache_miss_rate: f64,
498 pub class_cache_total_requests: u64,
499 pub class_cache_size: usize,
500 pub css_cache_hit_rate: f64,
501 pub css_cache_miss_rate: f64,
502 pub css_cache_total_requests: u64,
503 pub css_cache_size: usize,
504}
505
506#[cfg(test)]
507mod tests {
508 use super::*;
509
510 #[test]
511 fn test_optimization_level() {
512 assert_eq!(OptimizationLevel::None.cache_capacity(), 0);
513 assert_eq!(OptimizationLevel::Low.cache_capacity(), 100);
514 assert_eq!(OptimizationLevel::Medium.cache_capacity(), 500);
515 assert_eq!(OptimizationLevel::High.cache_capacity(), 1000);
516 assert_eq!(OptimizationLevel::Maximum.cache_capacity(), 5000);
517
518 assert_eq!(OptimizationLevel::None.optimization_factor(), 1.0);
519 assert_eq!(OptimizationLevel::Low.optimization_factor(), 1.2);
520 assert_eq!(OptimizationLevel::Medium.optimization_factor(), 1.5);
521 assert_eq!(OptimizationLevel::High.optimization_factor(), 2.0);
522 assert_eq!(OptimizationLevel::Maximum.optimization_factor(), 3.0);
523 }
524
525 #[test]
526 fn test_performance_optimizer_creation() {
527 let optimizer = PerformanceOptimizer::new();
528 assert_eq!(optimizer.optimization_level(), OptimizationLevel::Medium);
529 }
530
531 #[test]
532 fn test_performance_optimizer_with_level() {
533 let optimizer = PerformanceOptimizer::with_optimization_level(OptimizationLevel::High);
534 assert_eq!(optimizer.optimization_level(), OptimizationLevel::High);
535 }
536}