1use crate::error::Result;
8use crate::responsive::Breakpoint;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12#[derive(Debug, Clone)]
14pub struct EnhancedVariantParser {
15 variants: HashMap<String, VariantDefinition>,
17 breakpoints: HashMap<String, Breakpoint>,
19 custom_variants: HashMap<String, CustomVariant>,
21}
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct VariantDefinition {
26 pub name: String,
28 pub variant_type: VariantType,
30 pub selector_pattern: String,
32 pub media_query: Option<String>,
34 pub specificity: u32,
36 pub combinable: bool,
38 pub dependencies: Vec<String>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
44pub enum VariantType {
45 State,
47 Responsive,
49 DarkMode,
51 Group,
53 Peer,
55 PseudoElement,
57 Container,
59 Layer,
61 Custom,
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67pub struct CustomVariant {
68 pub name: String,
70 pub selector_pattern: String,
72 pub media_query: Option<String>,
74 pub specificity: u32,
76 pub combinable: bool,
78}
79
80#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub struct VariantCombination {
83 pub variants: Vec<ParsedVariant>,
85 pub base_class: String,
87 pub selector: String,
89 pub media_query: Option<String>,
91 pub specificity: u32,
93 pub is_valid: bool,
95 pub errors: Vec<String>,
97}
98
99#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub struct ParsedVariant {
102 pub name: String,
104 pub variant_type: VariantType,
106 pub selector_fragment: String,
108 pub specificity: u32,
110 pub position: usize,
112}
113
114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct VariantParseResult {
117 pub combination: VariantCombination,
119 pub metadata: VariantMetadata,
121}
122
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct VariantMetadata {
126 pub processing_time: std::time::Duration,
128 pub variant_count: usize,
130 pub combination_count: usize,
132 pub memory_usage: usize,
134 pub cache_hits: usize,
136 pub cache_misses: usize,
138}
139
140impl EnhancedVariantParser {
141 pub fn new() -> Self {
143 let mut parser = Self {
144 variants: HashMap::new(),
145 breakpoints: HashMap::new(),
146 custom_variants: HashMap::new(),
147 };
148
149 parser.initialize_default_variants();
150 parser.initialize_breakpoints();
151
152 parser
153 }
154
155 fn initialize_default_variants(&mut self) {
157 self.add_variant(VariantDefinition {
159 name: "hover".to_string(),
160 variant_type: VariantType::State,
161 selector_pattern: ":hover".to_string(),
162 media_query: None,
163 specificity: 10,
164 combinable: true,
165 dependencies: Vec::new(),
166 });
167
168 self.add_variant(VariantDefinition {
169 name: "focus".to_string(),
170 variant_type: VariantType::State,
171 selector_pattern: ":focus".to_string(),
172 media_query: None,
173 specificity: 10,
174 combinable: true,
175 dependencies: Vec::new(),
176 });
177
178 self.add_variant(VariantDefinition {
179 name: "active".to_string(),
180 variant_type: VariantType::State,
181 selector_pattern: ":active".to_string(),
182 media_query: None,
183 specificity: 10,
184 combinable: true,
185 dependencies: Vec::new(),
186 });
187
188 self.add_variant(VariantDefinition {
189 name: "visited".to_string(),
190 variant_type: VariantType::State,
191 selector_pattern: ":visited".to_string(),
192 media_query: None,
193 specificity: 10,
194 combinable: true,
195 dependencies: Vec::new(),
196 });
197
198 self.add_variant(VariantDefinition {
199 name: "disabled".to_string(),
200 variant_type: VariantType::State,
201 selector_pattern: ":disabled".to_string(),
202 media_query: None,
203 specificity: 10,
204 combinable: true,
205 dependencies: Vec::new(),
206 });
207
208 self.add_variant(VariantDefinition {
210 name: "dark".to_string(),
211 variant_type: VariantType::DarkMode,
212 selector_pattern: ".dark".to_string(),
213 media_query: None,
214 specificity: 20,
215 combinable: true,
216 dependencies: Vec::new(),
217 });
218
219 self.add_variant(VariantDefinition {
221 name: "group-hover".to_string(),
222 variant_type: VariantType::Group,
223 selector_pattern: ".group:hover".to_string(),
224 media_query: None,
225 specificity: 15,
226 combinable: true,
227 dependencies: Vec::new(),
228 });
229
230 self.add_variant(VariantDefinition {
231 name: "group-focus".to_string(),
232 variant_type: VariantType::Group,
233 selector_pattern: ".group:focus".to_string(),
234 media_query: None,
235 specificity: 15,
236 combinable: true,
237 dependencies: Vec::new(),
238 });
239
240 self.add_variant(VariantDefinition {
242 name: "peer-hover".to_string(),
243 variant_type: VariantType::Peer,
244 selector_pattern: ".peer:hover".to_string(),
245 media_query: None,
246 specificity: 15,
247 combinable: true,
248 dependencies: Vec::new(),
249 });
250
251 self.add_variant(VariantDefinition {
252 name: "peer-focus".to_string(),
253 variant_type: VariantType::Peer,
254 selector_pattern: ".peer:focus".to_string(),
255 media_query: None,
256 specificity: 15,
257 combinable: true,
258 dependencies: Vec::new(),
259 });
260
261 self.add_variant(VariantDefinition {
263 name: "before".to_string(),
264 variant_type: VariantType::PseudoElement,
265 selector_pattern: "::before".to_string(),
266 media_query: None,
267 specificity: 10,
268 combinable: true,
269 dependencies: Vec::new(),
270 });
271
272 self.add_variant(VariantDefinition {
273 name: "after".to_string(),
274 variant_type: VariantType::PseudoElement,
275 selector_pattern: "::after".to_string(),
276 media_query: None,
277 specificity: 10,
278 combinable: true,
279 dependencies: Vec::new(),
280 });
281
282 self.add_variant(VariantDefinition {
284 name: "container".to_string(),
285 variant_type: VariantType::Container,
286 selector_pattern: "@container".to_string(),
287 media_query: None,
288 specificity: 25,
289 combinable: true,
290 dependencies: Vec::new(),
291 });
292
293 self.add_variant(VariantDefinition {
295 name: "layer".to_string(),
296 variant_type: VariantType::Layer,
297 selector_pattern: "@layer".to_string(),
298 media_query: None,
299 specificity: 30,
300 combinable: true,
301 dependencies: Vec::new(),
302 });
303
304 self.add_variant(VariantDefinition {
306 name: "sm".to_string(),
307 variant_type: VariantType::Responsive,
308 selector_pattern: "".to_string(),
309 media_query: Some("(min-width: 640px)".to_string()),
310 specificity: 20,
311 combinable: false,
312 dependencies: Vec::new(),
313 });
314
315 self.add_variant(VariantDefinition {
316 name: "md".to_string(),
317 variant_type: VariantType::Responsive,
318 selector_pattern: "".to_string(),
319 media_query: Some("(min-width: 768px)".to_string()),
320 specificity: 20,
321 combinable: false,
322 dependencies: Vec::new(),
323 });
324
325 self.add_variant(VariantDefinition {
326 name: "lg".to_string(),
327 variant_type: VariantType::Responsive,
328 selector_pattern: "".to_string(),
329 media_query: Some("(min-width: 1024px)".to_string()),
330 specificity: 20,
331 combinable: false,
332 dependencies: Vec::new(),
333 });
334
335 self.add_variant(VariantDefinition {
336 name: "xl".to_string(),
337 variant_type: VariantType::Responsive,
338 selector_pattern: "".to_string(),
339 media_query: Some("(min-width: 1280px)".to_string()),
340 specificity: 20,
341 combinable: false,
342 dependencies: Vec::new(),
343 });
344
345 self.add_variant(VariantDefinition {
346 name: "2xl".to_string(),
347 variant_type: VariantType::Responsive,
348 selector_pattern: "".to_string(),
349 media_query: Some("(min-width: 1536px)".to_string()),
350 specificity: 20,
351 combinable: false,
352 dependencies: Vec::new(),
353 });
354 }
355
356 fn initialize_breakpoints(&mut self) {
358 self.breakpoints.insert("sm".to_string(), Breakpoint::Sm);
359 self.breakpoints.insert("md".to_string(), Breakpoint::Md);
360 self.breakpoints.insert("lg".to_string(), Breakpoint::Lg);
361 self.breakpoints.insert("xl".to_string(), Breakpoint::Xl);
362 self.breakpoints.insert("2xl".to_string(), Breakpoint::Xl);
363 }
364
365 pub fn add_variant(&mut self, variant: VariantDefinition) {
367 self.variants.insert(variant.name.clone(), variant);
368 }
369
370 pub fn add_custom_variant(&mut self, variant: CustomVariant) {
372 self.custom_variants.insert(variant.name.clone(), variant);
373 }
374
375 pub fn parse_class(&self, class: &str) -> Result<VariantParseResult> {
377 let start_time = std::time::Instant::now();
378
379 let (variants, base_class) = self.parse_variants_advanced(class);
381
382 let variant_count = variants.len();
384 let combination = self.generate_variant_combination(variants, base_class)?;
385
386 let processing_time = start_time.elapsed();
387
388 Ok(VariantParseResult {
389 combination,
390 metadata: VariantMetadata {
391 processing_time,
392 variant_count,
393 combination_count: 1,
394 memory_usage: 0, cache_hits: 0, cache_misses: 1, },
398 })
399 }
400
401 fn parse_variants_advanced(&self, class: &str) -> (Vec<String>, String) {
403 let mut variants = Vec::new();
404 let mut remaining = class.to_string();
405
406 let variant_patterns = [
408 ("dark:", "dark"),
410 ("group-hover:", "group-hover"),
412 ("group-focus:", "group-focus"),
413 ("group-active:", "group-active"),
414 ("group-disabled:", "group-disabled"),
415 ("peer-hover:", "peer-hover"),
417 ("peer-focus:", "peer-focus"),
418 ("peer-active:", "peer-active"),
419 ("peer-disabled:", "peer-disabled"),
420 ("hover:", "hover"),
422 ("focus:", "focus"),
423 ("active:", "active"),
424 ("visited:", "visited"),
425 ("disabled:", "disabled"),
426 ("before:", "before"),
428 ("after:", "after"),
429 ("container:", "container"),
431 ("layer:", "layer"),
433 ("sm:", "sm"),
435 ("md:", "md"),
436 ("lg:", "lg"),
437 ("xl:", "xl"),
438 ("2xl:", "2xl"),
439 ];
440
441 loop {
443 let mut found = false;
444 for (prefix, variant) in &variant_patterns {
445 if remaining.starts_with(prefix) {
446 variants.push(variant.to_string());
447 remaining = remaining
448 .strip_prefix(prefix)
449 .unwrap_or(&remaining)
450 .to_string();
451 found = true;
452 break;
453 }
454 }
455
456 if !found {
457 break;
458 }
459 }
460
461 (variants, remaining)
462 }
463
464 fn generate_variant_combination(
466 &self,
467 variants: Vec<String>,
468 base_class: String,
469 ) -> Result<VariantCombination> {
470 let mut parsed_variants = Vec::new();
471 let mut selector_parts = Vec::new();
472 let mut media_query = None;
473 let mut total_specificity = 10; let mut errors = Vec::new();
475
476 for (i, variant_name) in variants.iter().enumerate() {
478 if let Some(variant_def) = self.variants.get(variant_name) {
479 let parsed_variant = ParsedVariant {
480 name: variant_name.clone(),
481 variant_type: variant_def.variant_type.clone(),
482 selector_fragment: variant_def.selector_pattern.clone(),
483 specificity: variant_def.specificity,
484 position: i,
485 };
486
487 parsed_variants.push(parsed_variant.clone());
488 selector_parts.push(variant_def.selector_pattern.clone());
489 total_specificity += variant_def.specificity;
490
491 if let Some(mq) = &variant_def.media_query {
493 media_query = Some(mq.clone());
494 }
495 } else if let Some(custom_variant) = self.custom_variants.get(variant_name) {
496 let parsed_variant = ParsedVariant {
497 name: variant_name.clone(),
498 variant_type: VariantType::Custom,
499 selector_fragment: custom_variant.selector_pattern.clone(),
500 specificity: custom_variant.specificity,
501 position: i,
502 };
503
504 parsed_variants.push(parsed_variant.clone());
505 selector_parts.push(custom_variant.selector_pattern.clone());
506 total_specificity += custom_variant.specificity;
507
508 if let Some(mq) = &custom_variant.media_query {
509 media_query = Some(mq.clone());
510 }
511 } else {
512 errors.push(format!("Unknown variant: {}", variant_name));
513 }
514 }
515
516 let selector = self.generate_selector(selector_parts, &base_class);
518
519 let is_valid = self.validate_combination(&parsed_variants, &errors);
521
522 Ok(VariantCombination {
523 variants: parsed_variants,
524 base_class,
525 selector,
526 media_query,
527 specificity: total_specificity,
528 is_valid,
529 errors,
530 })
531 }
532
533 fn generate_selector(&self, parts: Vec<String>, base_class: &str) -> String {
535 let mut selector = String::new();
536
537 for part in parts {
539 if part.starts_with('.') {
540 selector.push_str(&part);
541 selector.push(' ');
542 } else if part.starts_with(':') {
543 selector.push_str(&part);
544 } else if part.starts_with('@') {
545 selector.push_str(&part);
547 selector.push(' ');
548 }
549 }
550
551 selector.push_str(&format!(".{}", base_class));
553
554 selector
555 }
556
557 fn validate_combination(&self, variants: &[ParsedVariant], errors: &[String]) -> bool {
559 if !errors.is_empty() {
560 return false;
561 }
562
563 let mut variant_types = std::collections::HashSet::new();
565 for variant in variants {
566 if !variant_types.insert(&variant.variant_type) {
567 match variant.variant_type {
569 VariantType::State => {
570 }
572 VariantType::Responsive => {
573 return false;
575 }
576 VariantType::DarkMode => {
577 return false;
579 }
580 _ => {
581 }
583 }
584 }
585 }
586
587 true
588 }
589
590 pub fn get_variant(&self, name: &str) -> Option<&VariantDefinition> {
592 self.variants.get(name)
593 }
594
595 pub fn get_all_variants(&self) -> &HashMap<String, VariantDefinition> {
597 &self.variants
598 }
599
600 pub fn get_custom_variants(&self) -> &HashMap<String, CustomVariant> {
602 &self.custom_variants
603 }
604
605 pub fn remove_variant(&mut self, name: &str) -> Option<VariantDefinition> {
607 self.variants.remove(name)
608 }
609
610 pub fn remove_custom_variant(&mut self, name: &str) -> Option<CustomVariant> {
612 self.custom_variants.remove(name)
613 }
614
615 pub fn clear_variants(&mut self) {
617 self.variants.clear();
618 self.custom_variants.clear();
619 }
620
621 pub fn reset_to_defaults(&mut self) {
623 self.clear_variants();
624 self.initialize_default_variants();
625 }
626}
627
628impl Default for EnhancedVariantParser {
629 fn default() -> Self {
630 Self::new()
631 }
632}
633
634#[cfg(test)]
635mod tests {
636 use super::*;
637
638 #[test]
639 fn test_enhanced_variant_parser_creation() {
640 let parser = EnhancedVariantParser::new();
641 assert!(!parser.variants.is_empty());
642 assert!(!parser.breakpoints.is_empty());
643 }
644
645 #[test]
646 fn test_variant_definition_creation() {
647 let variant = VariantDefinition {
648 name: "hover".to_string(),
649 variant_type: VariantType::State,
650 selector_pattern: ":hover".to_string(),
651 media_query: None,
652 specificity: 10,
653 combinable: true,
654 dependencies: Vec::new(),
655 };
656
657 assert_eq!(variant.name, "hover");
658 assert_eq!(variant.variant_type, VariantType::State);
659 assert_eq!(variant.specificity, 10);
660 }
661
662 #[test]
663 fn test_parse_simple_class() {
664 let parser = EnhancedVariantParser::new();
665 let result = parser.parse_class("p-4");
666
667 assert!(result.is_ok());
668 let result = result.unwrap();
669 assert_eq!(result.combination.base_class, "p-4");
670 assert!(result.combination.variants.is_empty());
671 assert!(result.combination.is_valid);
672 }
673
674 #[test]
675 fn test_parse_single_variant() {
676 let parser = EnhancedVariantParser::new();
677 let result = parser.parse_class("hover:bg-blue-500");
678
679 assert!(result.is_ok());
680 let result = result.unwrap();
681 assert_eq!(result.combination.base_class, "bg-blue-500");
682 assert_eq!(result.combination.variants.len(), 1);
683 assert_eq!(result.combination.variants[0].name, "hover");
684 assert!(result.combination.is_valid);
685 }
686
687 #[test]
688 fn test_parse_multiple_variants() {
689 let parser = EnhancedVariantParser::new();
690 let result = parser.parse_class("dark:hover:bg-blue-500");
691
692 assert!(result.is_ok());
693 let result = result.unwrap();
694 assert_eq!(result.combination.base_class, "bg-blue-500");
695 assert_eq!(result.combination.variants.len(), 2);
696 assert!(result.combination.is_valid);
697 }
698
699 #[test]
700 fn test_parse_responsive_variant() {
701 let parser = EnhancedVariantParser::new();
702 let result = parser.parse_class("md:p-4");
703
704 assert!(result.is_ok());
705 let result = result.unwrap();
706 assert_eq!(result.combination.base_class, "p-4");
707 assert_eq!(result.combination.variants.len(), 1);
708 assert_eq!(result.combination.variants[0].name, "md");
709 assert!(result.combination.is_valid);
710 }
711
712 #[test]
713 fn test_parse_complex_combination() {
714 let parser = EnhancedVariantParser::new();
715 let result = parser.parse_class("dark:group-hover:focus:bg-blue-500");
716
717 assert!(result.is_ok());
718 let result = result.unwrap();
719 assert_eq!(result.combination.base_class, "bg-blue-500");
720 assert_eq!(result.combination.variants.len(), 3);
721 assert!(result.combination.is_valid);
722 }
723
724 #[test]
725 fn test_custom_variant_addition() {
726 let mut parser = EnhancedVariantParser::new();
727 let custom_variant = CustomVariant {
728 name: "custom".to_string(),
729 selector_pattern: ".custom".to_string(),
730 media_query: None,
731 specificity: 15,
732 combinable: true,
733 };
734
735 parser.add_custom_variant(custom_variant);
736 assert!(parser.custom_variants.contains_key("custom"));
737 }
738
739 #[test]
740 fn test_variant_removal() {
741 let mut parser = EnhancedVariantParser::new();
742 let removed = parser.remove_variant("hover");
743 assert!(removed.is_some());
744 assert!(!parser.variants.contains_key("hover"));
745 }
746
747 #[test]
748 fn test_parser_reset() {
749 let mut parser = EnhancedVariantParser::new();
750 parser.add_custom_variant(CustomVariant {
751 name: "test".to_string(),
752 selector_pattern: ".test".to_string(),
753 media_query: None,
754 specificity: 10,
755 combinable: true,
756 });
757
758 assert!(parser.custom_variants.contains_key("test"));
759
760 parser.reset_to_defaults();
761 assert!(!parser.custom_variants.contains_key("test"));
762 assert!(parser.variants.contains_key("hover"));
763 }
764}