rustyle_css/
composition.rs

1//! Style composition system
2//!
3//! Provides style merging, inheritance, parameterized mixins, and @apply directive support.
4
5use std::collections::HashMap;
6
7/// Style composition utilities with advanced merging
8pub struct StyleComposition;
9
10impl StyleComposition {
11    /// Merge multiple CSS strings
12    pub fn merge(css_strings: &[&str]) -> String {
13        css_strings.join(" ")
14    }
15
16    /// Apply a base style with overrides (overrides take precedence)
17    pub fn apply(base: &str, overrides: &str) -> String {
18        // Simple merge - in a full implementation, would handle property conflicts
19        format!("{} {}", base, overrides)
20    }
21
22    /// Inherit styles from a parent
23    pub fn inherit(parent: &str, child: &str) -> String {
24        format!("{} {}", parent, child)
25    }
26
27    /// Merge styles with conflict resolution (last wins)
28    pub fn merge_with_resolution(css_strings: &[&str]) -> String {
29        // Extract properties and resolve conflicts
30        let mut properties: HashMap<String, String> = HashMap::new();
31
32        for css in css_strings {
33            // Simple property extraction (basic implementation)
34            for part in css.split(';') {
35                let part = part.trim();
36                if let Some((prop, val)) = part.split_once(':') {
37                    properties.insert(prop.trim().to_string(), val.trim().to_string());
38                }
39            }
40        }
41
42        // Rebuild CSS from properties
43        properties
44            .iter()
45            .map(|(prop, val)| format!("{}: {}", prop, val))
46            .collect::<Vec<_>>()
47            .join("; ")
48    }
49}
50
51/// Style mixin (reusable style block) with parameter support
52#[derive(Clone, Debug)]
53pub struct StyleMixin {
54    pub name: String,
55    pub css: String,
56    pub parameters: Vec<String>,
57}
58
59impl StyleMixin {
60    /// Create a new style mixin
61    pub fn new(name: &str, css: &str) -> Self {
62        Self {
63            name: name.to_string(),
64            css: css.to_string(),
65            parameters: Vec::new(),
66        }
67    }
68
69    /// Create a parameterized mixin
70    pub fn parameterized(name: &str, css_template: &str, parameters: Vec<&str>) -> Self {
71        Self {
72            name: name.to_string(),
73            css: css_template.to_string(),
74            parameters: parameters.iter().map(|s| s.to_string()).collect(),
75        }
76    }
77
78    /// Apply the mixin with parameters
79    pub fn apply_with_params(&self, params: &HashMap<&str, &str>) -> String {
80        let mut result = self.css.clone();
81
82        // Replace parameter placeholders with actual values
83        for (key, value) in params {
84            let placeholder = format!("${}", key);
85            result = result.replace(&placeholder, value);
86        }
87
88        result
89    }
90
91    /// Apply the mixin without parameters
92    pub fn apply(&self) -> &str {
93        &self.css
94    }
95
96    /// Get mixin parameters
97    pub fn parameters(&self) -> &[String] {
98        &self.parameters
99    }
100}
101
102/// Style registry for mixins with composition support
103pub struct MixinRegistry {
104    mixins: HashMap<String, StyleMixin>,
105}
106
107impl MixinRegistry {
108    /// Create a new mixin registry
109    pub fn new() -> Self {
110        Self {
111            mixins: HashMap::new(),
112        }
113    }
114
115    /// Register a mixin
116    pub fn register(&mut self, mixin: StyleMixin) {
117        self.mixins.insert(mixin.name.clone(), mixin);
118    }
119
120    /// Get a mixin by name
121    pub fn get(&self, name: &str) -> Option<&StyleMixin> {
122        self.mixins.get(name)
123    }
124
125    /// Apply a mixin by name with parameters
126    pub fn apply(&self, name: &str, params: &HashMap<&str, &str>) -> Option<String> {
127        self.mixins.get(name).map(|mixin| {
128            if mixin.parameters.is_empty() {
129                mixin.css.clone()
130            } else {
131                mixin.apply_with_params(params)
132            }
133        })
134    }
135
136    /// Compose multiple mixins
137    pub fn compose(&self, mixin_names: &[&str], params: &HashMap<&str, &str>) -> String {
138        let mut result = Vec::new();
139
140        for name in mixin_names {
141            if let Some(css) = self.apply(name, params) {
142                result.push(css);
143            }
144        }
145
146        result.join(" ")
147    }
148
149    /// Check if a mixin exists
150    pub fn has(&self, name: &str) -> bool {
151        self.mixins.contains_key(name)
152    }
153
154    /// Get all registered mixin names
155    pub fn names(&self) -> Vec<String> {
156        self.mixins.keys().cloned().collect()
157    }
158}
159
160impl Default for MixinRegistry {
161    fn default() -> Self {
162        Self::new()
163    }
164}