1use crate::error::Result;
7use lru::LruCache;
8use std::collections::HashMap;
9use std::sync::Arc;
10use std::sync::atomic::{AtomicU64, Ordering};
11use std::time::{Duration, Instant};
12use parking_lot::RwLock;
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).unwrap_or(std::num::NonZeroUsize::new(100).unwrap()),
56 ))),
57 hit_rate: AtomicU64::new(0),
58 miss_rate: AtomicU64::new(0),
59 total_requests: AtomicU64::new(0),
60 }
61 }
62
63 pub fn get(&self, key: &str) -> Option<String> {
65 self.total_requests.fetch_add(1, Ordering::Relaxed);
66
67 let mut cache = self.cache.write();
68 if let Some(value) = cache.get(key) {
69 self.hit_rate.fetch_add(1, Ordering::Relaxed);
70 Some(value.clone())
71 } else {
72 self.miss_rate.fetch_add(1, Ordering::Relaxed);
73 None
74 }
75 }
76
77 pub fn put(&self, key: String, value: String) {
79 let mut cache = self.cache.write();
80 cache.put(key, value);
81 }
82
83 pub fn hit_rate(&self) -> f64 {
85 let hits = self.hit_rate.load(Ordering::Relaxed) as f64;
86 let total = self.total_requests.load(Ordering::Relaxed) as f64;
87
88 if total == 0.0 { 0.0 } else { hits / total }
89 }
90
91 pub fn miss_rate(&self) -> f64 {
93 let misses = self.miss_rate.load(Ordering::Relaxed) as f64;
94 let total = self.total_requests.load(Ordering::Relaxed) as f64;
95
96 if total == 0.0 { 0.0 } else { misses / total }
97 }
98
99 pub fn total_requests(&self) -> u64 {
101 self.total_requests.load(Ordering::Relaxed)
102 }
103
104 pub fn clear(&self) {
106 let mut cache = self.cache.write();
107 cache.clear();
108 }
109
110 pub fn len(&self) -> usize {
112 let cache = self.cache.read();
113 cache.len()
114 }
115
116 pub fn is_empty(&self) -> bool {
118 let cache = self.cache.read();
119 cache.is_empty()
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum OptimizationLevel {
126 None,
127 Low,
128 Medium,
129 High,
130 Maximum,
131}
132
133impl OptimizationLevel {
134 pub fn cache_capacity(&self) -> usize {
136 match self {
137 OptimizationLevel::None => 0,
138 OptimizationLevel::Low => 100,
139 OptimizationLevel::Medium => 500,
140 OptimizationLevel::High => 1000,
141 OptimizationLevel::Maximum => 5000,
142 }
143 }
144
145 pub fn optimization_factor(&self) -> f64 {
147 match self {
148 OptimizationLevel::None => 1.0,
149 OptimizationLevel::Low => 1.2,
150 OptimizationLevel::Medium => 1.5,
151 OptimizationLevel::High => 2.0,
152 OptimizationLevel::Maximum => 3.0,
153 }
154 }
155}
156
157#[derive(Debug)]
159pub struct PerformanceOptimizer {
160 class_cache: ClassCache,
161 css_cache: ClassCache,
162 optimization_level: OptimizationLevel,
163 performance_metrics: Arc<RwLock<Vec<PerformanceMetric>>>,
164 error_metrics: Arc<RwLock<Vec<ErrorMetric>>>,
165 usage_metrics: Arc<RwLock<HashMap<String, UsageMetric>>>,
166}
167
168impl PerformanceOptimizer {
169 pub fn new() -> Self {
171 Self::with_optimization_level(OptimizationLevel::Medium)
172 }
173
174 pub fn with_optimization_level(level: OptimizationLevel) -> Self {
176 let capacity = level.cache_capacity();
177 Self {
178 class_cache: ClassCache::new(capacity),
179 css_cache: ClassCache::new(capacity),
180 optimization_level: level,
181 performance_metrics: Arc::new(RwLock::new(Vec::new())),
182 error_metrics: Arc::new(RwLock::new(Vec::new())),
183 usage_metrics: Arc::new(RwLock::new(HashMap::new())),
184 }
185 }
186
187 pub fn optimize_class_generation(&mut self, classes: &[String]) -> Result<String> {
189 let start = Instant::now();
190 let cache_key = self.generate_cache_key(classes);
191
192 if let Some(cached_result) = self.class_cache.get(&cache_key) {
194 self.record_performance("class_generation_cached", start.elapsed(), true);
195 return Ok(cached_result);
196 }
197
198 let result = self.generate_classes(classes)?;
200
201 self.class_cache.put(cache_key, result.clone());
203
204 self.record_performance("class_generation", start.elapsed(), true);
205 Ok(result)
206 }
207
208 pub fn optimize_css_generation(&mut self, css: &str) -> Result<String> {
210 let start = Instant::now();
211 let cache_key = format!("css:{}", css);
212
213 if let Some(cached_result) = self.css_cache.get(&cache_key) {
215 self.record_performance("css_generation_cached", start.elapsed(), true);
216 return Ok(cached_result);
217 }
218
219 let result = self.optimize_css(css)?;
221
222 self.css_cache.put(cache_key, result.clone());
224
225 self.record_performance("css_generation", start.elapsed(), true);
226 Ok(result)
227 }
228
229 fn generate_cache_key(&self, classes: &[String]) -> String {
231 let mut key = String::new();
232 for class in classes {
233 key.push_str(class);
234 key.push('|');
235 }
236 key
237 }
238
239 fn generate_classes(&self, classes: &[String]) -> Result<String> {
241 let optimized_classes = match self.optimization_level {
243 OptimizationLevel::None => classes.to_vec(),
244 OptimizationLevel::Low => self.optimize_classes_low(classes),
245 OptimizationLevel::Medium => self.optimize_classes_medium(classes),
246 OptimizationLevel::High => self.optimize_classes_high(classes),
247 OptimizationLevel::Maximum => self.optimize_classes_maximum(classes),
248 };
249
250 Ok(optimized_classes.join(" "))
251 }
252
253 fn optimize_classes_low(&self, classes: &[String]) -> Vec<String> {
255 let mut unique_classes: Vec<String> = classes.to_vec();
257 unique_classes.sort();
258 unique_classes.dedup();
259 unique_classes
260 }
261
262 fn optimize_classes_medium(&self, classes: &[String]) -> Vec<String> {
264 let mut optimized = self.optimize_classes_low(classes);
265
266 optimized = self.remove_conflicting_classes(optimized);
268
269 optimized
270 }
271
272 fn optimize_classes_high(&self, classes: &[String]) -> Vec<String> {
274 let mut optimized = self.optimize_classes_medium(classes);
275
276 optimized = self.merge_similar_classes(optimized);
278
279 optimized
280 }
281
282 fn optimize_classes_maximum(&self, classes: &[String]) -> Vec<String> {
284 let mut optimized = self.optimize_classes_high(classes);
285
286 optimized = self.apply_advanced_optimizations(optimized);
288
289 optimized
290 }
291
292 fn remove_conflicting_classes(&self, classes: Vec<String>) -> Vec<String> {
294 let mut result = Vec::new();
295 let mut seen_groups: HashMap<String, String> = HashMap::new();
296
297 for class in classes {
298 let group = self.get_class_group(&class);
299 if let Some(existing) = seen_groups.get(&group) {
300 if self.is_more_specific(&class, existing) {
302 if let Some(pos) = result.iter().position(|c| c == existing) {
303 result.remove(pos);
304 }
305 result.push(class.clone());
306 seen_groups.insert(group, class);
307 }
308 } else {
309 result.push(class.clone());
310 seen_groups.insert(group, class);
311 }
312 }
313
314 result
315 }
316
317 fn merge_similar_classes(&self, classes: Vec<String>) -> Vec<String> {
319 classes
323 }
324
325 fn apply_advanced_optimizations(&self, classes: Vec<String>) -> Vec<String> {
327 classes
333 }
334
335 fn get_class_group(&self, class: &str) -> String {
337 if class.starts_with("bg-") {
338 "background".to_string()
339 } else if class.starts_with("text-") {
340 "text".to_string()
341 } else if class.starts_with("border-") {
342 "border".to_string()
343 } else if class.starts_with("p-") || class.starts_with("px-") || class.starts_with("py-") {
344 "padding".to_string()
345 } else if class.starts_with("m-") || class.starts_with("mx-") || class.starts_with("my-") {
346 "margin".to_string()
347 } else {
348 "other".to_string()
349 }
350 }
351
352 fn is_more_specific(&self, class1: &str, class2: &str) -> bool {
354 class1.len() > class2.len()
356 }
357
358 fn optimize_css(&self, css: &str) -> Result<String> {
360 let mut optimized = css.to_string();
361
362 optimized = optimized.replace(" ", " ");
364 optimized = optimized.replace("\n", "");
365 optimized = optimized.replace("\t", "");
366
367 optimized = optimized.replace(";}", "}");
369
370 Ok(optimized)
371 }
372
373 pub fn record_performance(&self, operation: &str, duration: Duration, success: bool) {
375 let metric = PerformanceMetric {
376 operation: operation.to_string(),
377 duration,
378 timestamp: Instant::now(),
379 success,
380 };
381
382 let mut metrics = self.performance_metrics.write();
383 metrics.push(metric);
384
385 let len = metrics.len();
387 if len > 1000 {
388 metrics.drain(0..len - 1000);
389 }
390 }
391
392 pub fn record_error(&self, error_type: &str, error: &dyn std::error::Error) {
394 let metric = ErrorMetric {
395 error_type: error_type.to_string(),
396 error_message: error.to_string(),
397 timestamp: Instant::now(),
398 count: 1,
399 };
400
401 let mut metrics = self.error_metrics.write();
402 metrics.push(metric);
403
404 let len = metrics.len();
406 if len > 1000 {
407 metrics.drain(0..len - 1000);
408 }
409 }
410
411 pub fn record_usage(&self, class_pattern: &str, generation_time: Duration) {
413 let mut metrics = self.usage_metrics.write();
414
415 if let Some(usage) = metrics.get_mut(class_pattern) {
416 usage.usage_count += 1;
417 usage.last_used = Instant::now();
418 usage.average_generation_time = Duration::from_nanos(
419 ((usage.average_generation_time.as_nanos() + generation_time.as_nanos()) / 2)
420 as u64,
421 );
422 } else {
423 metrics.insert(
424 class_pattern.to_string(),
425 UsageMetric {
426 class_pattern: class_pattern.to_string(),
427 usage_count: 1,
428 last_used: Instant::now(),
429 average_generation_time: generation_time,
430 },
431 );
432 }
433 }
434
435 pub fn get_performance_metrics(&self) -> Vec<PerformanceMetric> {
437 let metrics = self.performance_metrics.read();
438 metrics.clone()
439 }
440
441 pub fn get_error_metrics(&self) -> Vec<ErrorMetric> {
443 let metrics = self.error_metrics.read();
444 metrics.clone()
445 }
446
447 pub fn get_usage_metrics(&self) -> HashMap<String, UsageMetric> {
449 let metrics = self.usage_metrics.read();
450 metrics.clone()
451 }
452
453 pub fn get_cache_stats(&self) -> CacheStats {
455 CacheStats {
456 class_cache_hit_rate: self.class_cache.hit_rate(),
457 class_cache_miss_rate: self.class_cache.miss_rate(),
458 class_cache_total_requests: self.class_cache.total_requests(),
459 class_cache_size: self.class_cache.len(),
460 css_cache_hit_rate: self.css_cache.hit_rate(),
461 css_cache_miss_rate: self.css_cache.miss_rate(),
462 css_cache_total_requests: self.css_cache.total_requests(),
463 css_cache_size: self.css_cache.len(),
464 }
465 }
466
467 pub fn set_optimization_level(&mut self, level: OptimizationLevel) {
469 self.optimization_level = level;
470 }
471
472 pub fn optimization_level(&self) -> OptimizationLevel {
474 self.optimization_level
475 }
476}
477
478impl Default for PerformanceOptimizer {
479 fn default() -> Self {
480 Self::new()
481 }
482}
483
484#[derive(Debug, Clone)]
486pub struct CacheStats {
487 pub class_cache_hit_rate: f64,
488 pub class_cache_miss_rate: f64,
489 pub class_cache_total_requests: u64,
490 pub class_cache_size: usize,
491 pub css_cache_hit_rate: f64,
492 pub css_cache_miss_rate: f64,
493 pub css_cache_total_requests: u64,
494 pub css_cache_size: usize,
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500
501 #[test]
502 fn test_optimization_level() {
503 assert_eq!(OptimizationLevel::None.cache_capacity(), 0);
504 assert_eq!(OptimizationLevel::Low.cache_capacity(), 100);
505 assert_eq!(OptimizationLevel::Medium.cache_capacity(), 500);
506 assert_eq!(OptimizationLevel::High.cache_capacity(), 1000);
507 assert_eq!(OptimizationLevel::Maximum.cache_capacity(), 5000);
508
509 assert_eq!(OptimizationLevel::None.optimization_factor(), 1.0);
510 assert_eq!(OptimizationLevel::Low.optimization_factor(), 1.2);
511 assert_eq!(OptimizationLevel::Medium.optimization_factor(), 1.5);
512 assert_eq!(OptimizationLevel::High.optimization_factor(), 2.0);
513 assert_eq!(OptimizationLevel::Maximum.optimization_factor(), 3.0);
514 }
515
516 #[test]
517 fn test_performance_optimizer_creation() {
518 let optimizer = PerformanceOptimizer::new();
519 assert_eq!(optimizer.optimization_level(), OptimizationLevel::Medium);
520 }
521
522 #[test]
523 fn test_performance_optimizer_with_level() {
524 let optimizer = PerformanceOptimizer::with_optimization_level(OptimizationLevel::High);
525 assert_eq!(optimizer.optimization_level(), OptimizationLevel::High);
526 }
527}