1use std::collections::HashMap;
7use super::types::*;
8
9pub struct PostCSSDevTools {
11 debugger: CSSDebugger,
12 inspector: CSSInspector,
13 analyzer: CSSAnalyzer,
14 formatter: CSSFormatter,
15}
16
17impl PostCSSDevTools {
18 pub fn new() -> Self {
20 Self {
21 debugger: CSSDebugger::new(),
22 inspector: CSSInspector::new(),
23 analyzer: CSSAnalyzer::new(),
24 formatter: CSSFormatter::new(),
25 }
26 }
27
28 pub fn debug_processing(&self, css: &str, steps: &[ProcessingStep]) -> Result<DebugResult, AdvancedFeatureError> {
30 let mut debug_info = DebugInfo::new();
31 let mut current_css = css.to_string();
32
33 for (i, step) in steps.iter().enumerate() {
34 let step_debug = StepDebugInfo {
35 step_name: step.name.clone(),
36 step_index: i,
37 input_css: current_css.clone(),
38 input_size: current_css.len(),
39 output_css: String::new(),
40 output_size: 0,
41 transformations: Vec::new(),
42 performance: OperationMetrics {
43 operation: step.name.clone(),
44 duration: std::time::Duration::from_secs(0),
45 memory_delta: 0,
46 cpu_usage: 0.0,
47 input_size: current_css.len(),
48 output_size: 0,
49 },
50 };
51
52 let result = (step.execute)(¤t_css)?;
54
55 let mut step_debug = step_debug;
56 step_debug.output_css = result.clone();
57 step_debug.output_size = result.len();
58
59 debug_info.steps.push(step_debug);
60 current_css = result;
61 }
62
63 Ok(DebugResult {
64 original_css: css.to_string(),
65 final_css: current_css,
66 debug_info: debug_info.clone(),
67 total_steps: steps.len(),
68 total_time: debug_info.total_time,
69 })
70 }
71
72 pub fn inspect_css(&self, css: &str) -> Result<InspectionResult, AdvancedFeatureError> {
74 let ast = self.parse_css(css)?;
75
76 let mut inspection = InspectionResult {
77 total_rules: ast.rules.len(),
78 total_properties: 0,
79 total_selectors: 0,
80 complexity_score: 0.0,
81 maintainability_score: 0.0,
82 issues: Vec::new(),
83 recommendations: Vec::new(),
84 };
85
86 for rule in &ast.rules {
88 inspection.total_properties += rule.properties.len();
89 inspection.total_selectors += 1;
90
91 let rule_complexity = self.calculate_rule_complexity(rule);
93 inspection.complexity_score += rule_complexity;
94
95 if rule.properties.is_empty() {
97 inspection.issues.push("Empty rule found".to_string());
98 }
99
100 if rule.selector.len() > 50 {
101 inspection.issues.push("Long selector found".to_string());
102 }
103 }
104
105 inspection.maintainability_score = self.calculate_maintainability_score(&inspection);
107
108 inspection.recommendations = self.generate_recommendations(&inspection);
110
111 Ok(inspection)
112 }
113
114 pub fn format_css(&self, css: &str, options: &FormatOptions) -> Result<String, AdvancedFeatureError> {
116 let ast = self.parse_css(css)?;
117 let mut formatted = String::new();
118
119 for rule in &options.rules {
121 match rule {
122 FormatRule::Indentation(indent) => {
123 formatted = self.apply_indentation(&ast, *indent)?;
124 }
125 FormatRule::LineBreaks(breaks) => {
126 formatted = self.apply_line_breaks(&ast, breaks)?;
127 }
128 FormatRule::Spacing(spacing) => {
129 formatted = self.apply_spacing(&ast, spacing)?;
130 }
131 FormatRule::Sorting(sort_options) => {
132 formatted = self.apply_sorting(&ast, sort_options)?;
133 }
134 }
135 }
136
137 Ok(formatted)
138 }
139
140 pub fn analyze_css_structure(&self, css: &str) -> Result<AnalysisResult, AdvancedFeatureError> {
142 let ast = self.parse_css(css)?;
143
144 let mut analysis = CSSAnalysis {
145 selectors: SelectorAnalysis {
146 total_selectors: ast.rules.len(),
147 complex_selectors: 0,
148 duplicate_selectors: 0,
149 specificity_scores: Vec::new(),
150 },
151 properties: PropertyAnalysis {
152 total_properties: 0,
153 duplicate_properties: 0,
154 unused_properties: 0,
155 property_usage: HashMap::new(),
156 },
157 specificity: SpecificityAnalysis {
158 max_specificity: 0,
159 avg_specificity: 0.0,
160 high_specificity_selectors: Vec::new(),
161 },
162 performance: PerformanceAnalysis {
163 estimated_size: css.len(),
164 complexity_score: 0.0,
165 optimization_opportunities: Vec::new(),
166 },
167 maintainability: MaintainabilityAnalysis {
168 maintainability_score: 0.0,
169 issues: Vec::new(),
170 recommendations: Vec::new(),
171 },
172 };
173
174 for rule in &ast.rules {
176 analysis.selectors.total_selectors += 1;
177
178 if rule.selector.contains(' ') || rule.selector.contains('>') {
180 analysis.selectors.complex_selectors += 1;
181 }
182
183 let specificity = self.calculate_specificity(&rule.selector);
185 analysis.selectors.specificity_scores.push(specificity);
186 analysis.specificity.max_specificity = analysis.specificity.max_specificity.max(specificity);
187
188 if specificity > 3 {
189 analysis.specificity.high_specificity_selectors.push(rule.selector.clone());
190 }
191 }
192
193 if !analysis.selectors.specificity_scores.is_empty() {
195 analysis.specificity.avg_specificity = analysis.selectors.specificity_scores.iter().sum::<usize>() as f64 / analysis.selectors.specificity_scores.len() as f64;
196 }
197
198 for rule in &ast.rules {
200 analysis.properties.total_properties += rule.properties.len();
201
202 for property in &rule.properties {
203 *analysis.properties.property_usage.entry(property.name.clone()).or_insert(0) += 1;
204 }
205 }
206
207 analysis.performance.complexity_score = self.calculate_css_complexity(&ast);
209
210 let maintainability_score = self.calculate_maintainability_score_analysis(&analysis);
212 analysis.maintainability.maintainability_score = maintainability_score;
213
214 let recommendations = self.generate_analysis_recommendations(&analysis);
215 Ok(AnalysisResult {
216 css: css.to_string(),
217 analysis,
218 recommendations,
219 })
220 }
221
222 fn parse_css(&self, css: &str) -> Result<CSSAST, AdvancedFeatureError> {
224 Ok(CSSAST {
226 rules: self.parse_rules(css)?,
227 comments: self.parse_comments(css)?,
228 })
229 }
230
231 fn parse_rules(&self, css: &str) -> Result<Vec<CSSRule>, AdvancedFeatureError> {
233 let mut rules = Vec::new();
234 let rule_pattern = regex::Regex::new(r"([^{]+)\s*\{([^}]+)\}").unwrap();
235
236 for cap in rule_pattern.captures_iter(css) {
237 let selector = cap[1].trim().to_string();
238 let properties = cap[2].trim().to_string();
239
240 rules.push(CSSRule {
241 selector,
242 properties: self.parse_properties(&properties)?,
243 line: 1, column: 1,
245 });
246 }
247
248 Ok(rules)
249 }
250
251 fn parse_properties(&self, properties_str: &str) -> Result<Vec<CSSProperty>, AdvancedFeatureError> {
253 let mut properties = Vec::new();
254 let property_pattern = regex::Regex::new(r"([^:]+):\s*([^;]+);").unwrap();
255
256 for cap in property_pattern.captures_iter(properties_str) {
257 properties.push(CSSProperty {
258 name: cap[1].trim().to_string(),
259 value: cap[2].trim().to_string(),
260 important: cap[2].contains("!important"),
261 });
262 }
263
264 Ok(properties)
265 }
266
267 fn parse_comments(&self, css: &str) -> Result<Vec<CSSComment>, AdvancedFeatureError> {
269 let mut comments = Vec::new();
270 let comment_pattern = regex::Regex::new(r"/\*([^*]|\*[^/])*\*/").unwrap();
271
272 for cap in comment_pattern.captures_iter(css) {
273 comments.push(CSSComment {
274 content: cap[0].to_string(),
275 line: 1, column: 1,
277 });
278 }
279
280 Ok(comments)
281 }
282
283 fn calculate_rule_complexity(&self, rule: &CSSRule) -> f64 {
285 let selector_complexity = rule.selector.len() as f64 * 0.1;
286 let property_complexity = rule.properties.len() as f64 * 0.05;
287 selector_complexity + property_complexity
288 }
289
290 fn calculate_maintainability_score(&self, inspection: &InspectionResult) -> f64 {
292 let mut score = 100.0;
293
294 score -= inspection.issues.len() as f64 * 5.0;
296
297 score -= inspection.complexity_score * 0.1;
299
300 (score as f64).max(0.0)
301 }
302
303 fn calculate_css_complexity(&self, ast: &CSSAST) -> f64 {
305 let mut complexity = 0.0;
306
307 for rule in &ast.rules {
308 complexity += self.calculate_rule_complexity(rule);
309 }
310
311 complexity
312 }
313
314 fn calculate_maintainability_score_analysis(&self, analysis: &CSSAnalysis) -> f64 {
316 let mut score = 100.0;
317
318 if analysis.specificity.max_specificity > 3 {
320 score -= 10.0;
321 }
322
323 if analysis.selectors.complex_selectors > analysis.selectors.total_selectors / 2 {
325 score -= 15.0;
326 }
327
328 if analysis.performance.complexity_score > 10.0 {
330 score -= 20.0;
331 }
332
333 (score as f64).max(0.0)
334 }
335
336 fn generate_recommendations(&self, inspection: &InspectionResult) -> Vec<String> {
338 let mut recommendations = Vec::new();
339
340 if inspection.issues.contains(&"Empty rule found".to_string()) {
341 recommendations.push("Remove empty CSS rules".to_string());
342 }
343
344 if inspection.issues.contains(&"Long selector found".to_string()) {
345 recommendations.push("Simplify long CSS selectors".to_string());
346 }
347
348 if inspection.complexity_score > 10.0 {
349 recommendations.push("Consider splitting complex CSS rules".to_string());
350 }
351
352 recommendations
353 }
354
355 fn generate_analysis_recommendations(&self, analysis: &CSSAnalysis) -> Vec<String> {
357 let mut recommendations = Vec::new();
358
359 if analysis.specificity.max_specificity > 3 {
360 recommendations.push("Reduce CSS specificity for better maintainability".to_string());
361 }
362
363 if analysis.selectors.complex_selectors > analysis.selectors.total_selectors / 2 {
364 recommendations.push("Simplify complex CSS selectors".to_string());
365 }
366
367 if analysis.performance.complexity_score > 10.0 {
368 recommendations.push("Optimize CSS for better performance".to_string());
369 }
370
371 recommendations
372 }
373
374 fn apply_indentation(&self, ast: &CSSAST, indent: usize) -> Result<String, AdvancedFeatureError> {
376 let mut formatted = String::new();
377 let indent_str = " ".repeat(indent);
378
379 for rule in &ast.rules {
380 formatted.push_str(&format!("{}{} {{\n", indent_str, rule.selector));
381
382 for property in &rule.properties {
383 formatted.push_str(&format!("{}{}: {};\n", indent_str.repeat(2), property.name, property.value));
384 }
385
386 formatted.push_str(&format!("{}}}\n", indent_str));
387 }
388
389 Ok(formatted)
390 }
391
392 fn apply_line_breaks(&self, ast: &CSSAST, breaks: &bool) -> Result<String, AdvancedFeatureError> {
394 if *breaks {
395 let mut formatted = String::new();
396
397 for rule in &ast.rules {
398 formatted.push_str(&format!("{}\n{{\n", rule.selector));
399
400 for property in &rule.properties {
401 formatted.push_str(&format!(" {}: {};\n", property.name, property.value));
402 }
403
404 formatted.push_str("}\n\n");
405 }
406
407 Ok(formatted)
408 } else {
409 let mut formatted = String::new();
411
412 for rule in &ast.rules {
413 formatted.push_str(&format!("{} {{ ", rule.selector));
414
415 for property in &rule.properties {
416 formatted.push_str(&format!("{}: {}; ", property.name, property.value));
417 }
418
419 formatted.push_str("} ");
420 }
421
422 Ok(formatted)
423 }
424 }
425
426 fn apply_spacing(&self, ast: &CSSAST, _spacing: &SpacingRule) -> Result<String, AdvancedFeatureError> {
428 self.apply_line_breaks(ast, &true)
430 }
431
432 fn apply_sorting(&self, ast: &CSSAST, _sort_options: &SortOptions) -> Result<String, AdvancedFeatureError> {
434 self.apply_line_breaks(ast, &true)
436 }
437
438 fn calculate_specificity(&self, selector: &str) -> usize {
440 let mut specificity = 0;
441
442 specificity += selector.matches('#').count() * 100;
444
445 specificity += selector.matches('.').count() * 10;
447 specificity += selector.matches('[').count() * 10;
448
449 specificity += selector.split_whitespace().count();
451
452 specificity
453 }
454}
455
456pub struct CSSDebugger;
458
459impl CSSDebugger {
460 pub fn new() -> Self {
461 Self
462 }
463}
464
465pub struct CSSInspector;
467
468impl CSSInspector {
469 pub fn new() -> Self {
470 Self
471 }
472}
473
474pub struct CSSAnalyzer;
476
477impl CSSAnalyzer {
478 pub fn new() -> Self {
479 Self
480 }
481}
482
483pub struct CSSFormatter;
485
486impl CSSFormatter {
487 pub fn new() -> Self {
488 Self
489 }
490}
491
492#[derive(Debug, Clone)]
494pub struct DebugResult {
495 pub original_css: String,
496 pub final_css: String,
497 pub debug_info: DebugInfo,
498 pub total_steps: usize,
499 pub total_time: std::time::Duration,
500}
501
502#[derive(Debug, Clone)]
504pub struct InspectionResult {
505 pub total_rules: usize,
506 pub total_properties: usize,
507 pub total_selectors: usize,
508 pub complexity_score: f64,
509 pub maintainability_score: f64,
510 pub issues: Vec<String>,
511 pub recommendations: Vec<String>,
512}
513
514#[derive(Debug, Clone)]
516pub struct AnalysisResult {
517 pub css: String,
518 pub analysis: CSSAnalysis,
519 pub recommendations: Vec<String>,
520}
521
522#[derive(Debug, Clone)]
524pub struct CSSAST {
525 pub rules: Vec<CSSRule>,
526 pub comments: Vec<CSSComment>,
527}
528
529#[derive(Debug, Clone)]
530pub struct CSSRule {
531 pub selector: String,
532 pub properties: Vec<CSSProperty>,
533 pub line: usize,
534 pub column: usize,
535}
536
537#[derive(Debug, Clone)]
538pub struct CSSProperty {
539 pub name: String,
540 pub value: String,
541 pub important: bool,
542}
543
544#[derive(Debug, Clone)]
545pub struct CSSComment {
546 pub content: String,
547 pub line: usize,
548 pub column: usize,
549}
550
551pub struct ProcessingStep {
553 pub name: String,
554 pub execute: fn(&str) -> Result<String, AdvancedFeatureError>,
555}
556
557impl ProcessingStep {
558 pub fn new(name: String, execute: fn(&str) -> Result<String, AdvancedFeatureError>) -> Self {
559 Self { name, execute }
560 }
561}
562
563#[cfg(test)]
564mod tests {
565 use super::*;
566
567 #[test]
568 fn test_dev_tools_creation() {
569 let _tools = PostCSSDevTools::new();
570 assert!(true);
572 }
573
574 #[test]
575 fn test_css_inspection() {
576 let tools = PostCSSDevTools::new();
577 let css = ".test { color: red; }";
578 let result = tools.inspect_css(css);
579 assert!(result.is_ok());
580
581 let inspection = result.unwrap();
582 assert_eq!(inspection.total_rules, 1);
583 assert_eq!(inspection.total_properties, 1);
584 }
585
586 #[test]
587 fn test_css_formatting() {
588 let tools = PostCSSDevTools::new();
589 let css = ".test{color:red;}";
590 let options = FormatOptions::default();
591 let result = tools.format_css(css, &options);
592 assert!(result.is_ok());
593
594 let formatted = result.unwrap();
595 assert!(formatted.contains('\n'));
596 }
597
598 #[test]
599 fn test_css_analysis() {
600 let tools = PostCSSDevTools::new();
601 let css = ".test { color: red; }";
602 let result = tools.analyze_css_structure(css);
603 assert!(result.is_ok());
604
605 let analysis = result.unwrap();
606 assert_eq!(analysis.analysis.selectors.total_selectors, 1);
607 assert_eq!(analysis.analysis.properties.total_properties, 1);
608 }
609
610 #[test]
611 fn test_processing_step() {
612 let step = ProcessingStep::new(
613 "test_step".to_string(),
614 |input| Ok(input.to_string())
615 );
616
617 let result = (step.execute)("test");
618 assert!(result.is_ok());
619 assert_eq!(result.unwrap(), "test");
620 }
621}