rustyle_css/css/
at_rules.rs

1//! Advanced CSS @rules
2//!
3//! Provides type-safe builders for @layer, @supports, @custom-media, and other @rules.
4
5/// @layer rule builder for CSS cascade layers
6#[derive(Clone, Debug)]
7pub struct LayerRule {
8    name: String,
9    rules: Vec<String>,
10}
11
12impl LayerRule {
13    /// Create a new @layer rule
14    pub fn new(name: &str) -> Self {
15        Self {
16            name: name.to_string(),
17            rules: Vec::new(),
18        }
19    }
20
21    /// Add a CSS rule to the layer
22    pub fn add_rule(mut self, rule: &str) -> Self {
23        self.rules.push(rule.to_string());
24        self
25    }
26
27    /// Add multiple rules
28    pub fn add_rules(mut self, rules: &[&str]) -> Self {
29        for rule in rules {
30            self.rules.push(rule.to_string());
31        }
32        self
33    }
34
35    /// Convert to CSS string
36    pub fn to_css(&self) -> String {
37        let rules_str = self.rules.join("\n  ");
38        format!("@layer {} {{\n  {}\n}}", self.name, rules_str)
39    }
40}
41
42/// @supports query builder for feature detection
43#[derive(Clone, Debug)]
44pub struct SupportsRule {
45    condition: String,
46    rules: Vec<String>,
47}
48
49impl SupportsRule {
50    /// Create a new @supports rule
51    pub fn new(condition: &str) -> Self {
52        Self {
53            condition: condition.to_string(),
54            rules: Vec::new(),
55        }
56    }
57
58    /// Create a @supports rule for a property
59    pub fn property(property: &str, value: &str) -> Self {
60        Self::new(&format!("({}: {})", property, value))
61    }
62
63    /// Create a @supports rule for a selector
64    pub fn selector(selector: &str) -> Self {
65        Self::new(&format!("selector({})", selector))
66    }
67
68    /// Combine multiple conditions with AND
69    pub fn and(mut self, condition: &str) -> Self {
70        self.condition = format!("{} and ({})", self.condition, condition);
71        self
72    }
73
74    /// Combine multiple conditions with OR
75    pub fn or(mut self, condition: &str) -> Self {
76        self.condition = format!("{} or ({})", self.condition, condition);
77        self
78    }
79
80    /// Add a CSS rule
81    pub fn add_rule(mut self, rule: &str) -> Self {
82        self.rules.push(rule.to_string());
83        self
84    }
85
86    /// Add multiple rules
87    pub fn add_rules(mut self, rules: &[&str]) -> Self {
88        for rule in rules {
89            self.rules.push(rule.to_string());
90        }
91        self
92    }
93
94    /// Convert to CSS string
95    pub fn to_css(&self) -> String {
96        let rules_str = self.rules.join("\n  ");
97        format!("@supports {} {{\n  {}\n}}", self.condition, rules_str)
98    }
99}
100
101/// @custom-media query builder
102#[derive(Clone, Debug)]
103pub struct CustomMediaRule {
104    name: String,
105    media_query: String,
106}
107
108impl CustomMediaRule {
109    /// Create a new @custom-media rule
110    pub fn new(name: &str, media_query: &str) -> Self {
111        Self {
112            name: name.to_string(),
113            media_query: media_query.to_string(),
114        }
115    }
116
117    /// Convert to CSS string
118    pub fn to_css(&self) -> String {
119        format!("@custom-media --{} {};", self.name, self.media_query)
120    }
121}
122
123/// @font-face rule builder
124#[derive(Clone, Debug)]
125pub struct FontFaceRule {
126    font_family: String,
127    src: Vec<String>,
128    font_weight: Option<String>,
129    font_style: Option<String>,
130    font_display: Option<String>,
131}
132
133impl FontFaceRule {
134    /// Create a new @font-face rule
135    pub fn new(font_family: &str) -> Self {
136        Self {
137            font_family: font_family.to_string(),
138            src: Vec::new(),
139            font_weight: None,
140            font_style: None,
141            font_display: None,
142        }
143    }
144
145    /// Add a font source
146    pub fn src(mut self, url: &str, format: Option<&str>) -> Self {
147        let src_value = if let Some(fmt) = format {
148            format!("url({}) format({})", url, fmt)
149        } else {
150            format!("url({})", url)
151        };
152        self.src.push(src_value);
153        self
154    }
155
156    /// Set font weight
157    pub fn font_weight(mut self, weight: &str) -> Self {
158        self.font_weight = Some(weight.to_string());
159        self
160    }
161
162    /// Set font style
163    pub fn font_style(mut self, style: &str) -> Self {
164        self.font_style = Some(style.to_string());
165        self
166    }
167
168    /// Set font display
169    pub fn font_display(mut self, display: &str) -> Self {
170        self.font_display = Some(display.to_string());
171        self
172    }
173
174    /// Convert to CSS string
175    pub fn to_css(&self) -> String {
176        let mut css = format!("@font-face {{\n  font-family: {};\n", self.font_family);
177
178        if !self.src.is_empty() {
179            css.push_str(&format!("  src: {};\n", self.src.join(", ")));
180        }
181
182        if let Some(ref weight) = self.font_weight {
183            css.push_str(&format!("  font-weight: {};\n", weight));
184        }
185
186        if let Some(ref style) = self.font_style {
187            css.push_str(&format!("  font-style: {};\n", style));
188        }
189
190        if let Some(ref display) = self.font_display {
191            css.push_str(&format!("  font-display: {};\n", display));
192        }
193
194        css.push_str("}");
195        css
196    }
197}