1use std::sync::{Arc, Mutex};
6use crate::cssom::CSSStyleDeclaration;
7
8#[allow(non_camel_case_types)]
10#[derive(Debug, Clone, PartialEq)]
11pub enum CSSRuleType {
12 STYLE_RULE,
14 MEDIA_RULE,
16 KEYFRAMES_RULE,
18 IMPORT_RULE,
20 FONT_FACE_RULE,
22 SUPPORTS_RULE,
24}
25
26#[derive(Debug, Clone)]
30pub struct CSSRuleOM {
31 pub rule_type: CSSRuleType,
33 css_text: String,
35 parent_stylesheet: Option<String>,
37}
38
39impl CSSRuleOM {
40 pub fn new(rule_type: CSSRuleType, css_text: &str) -> Self {
42 Self {
43 rule_type,
44 css_text: css_text.to_string(),
45 parent_stylesheet: None,
46 }
47 }
48
49 pub fn get_css_text(&self) -> &str {
51 &self.css_text
52 }
53
54 pub fn set_css_text(&mut self, text: &str) {
56 self.css_text = text.to_string();
57 }
58
59 pub fn parent_stylesheet(&self) -> Option<&str> {
61 self.parent_stylesheet.as_deref()
62 }
63}
64
65#[derive(Debug, Clone)]
79pub struct CSSStyleRule {
80 base: CSSRuleOM,
82 selector_text: String,
84 style: Arc<Mutex<CSSStyleDeclaration>>,
86}
87
88impl CSSStyleRule {
89 pub fn new(selector: &str) -> Self {
91 let css_text = format!("{} {{ }}", selector);
92 Self {
93 base: CSSRuleOM::new(CSSRuleType::STYLE_RULE, &css_text),
94 selector_text: selector.to_string(),
95 style: Arc::new(Mutex::new(CSSStyleDeclaration::new())),
96 }
97 }
98
99 pub fn from_internal(rule: &crate::css::CSSRule) -> Self {
101 let selector_text = rule.selector.text.clone();
102 let style = Arc::new(Mutex::new(
103 CSSStyleDeclaration::from_declarations(&rule.declarations)
104 ));
105 let css_text = format!("{} {{ {} }}", selector_text, style.lock().unwrap().get_css_text());
106
107 Self {
108 base: CSSRuleOM::new(CSSRuleType::STYLE_RULE, &css_text),
109 selector_text,
110 style,
111 }
112 }
113
114 pub fn selector_text(&self) -> &str {
116 &self.selector_text
117 }
118
119 pub fn set_selector_text(&mut self, text: &str) {
121 self.selector_text = text.to_string();
122 self.update_css_text();
123 }
124
125 pub fn style(&self) -> Arc<Mutex<CSSStyleDeclaration>> {
127 Arc::clone(&self.style)
128 }
129
130 fn update_css_text(&mut self) {
132 let style_text = self.style.lock().unwrap().get_css_text();
133 self.base.set_css_text(&format!(
134 "{} {{ {} }}",
135 self.selector_text, style_text
136 ));
137 }
138
139 pub fn to_internal(&self) -> crate::css::CSSRule {
141 let selector = crate::css::Selector::new(&self.selector_text);
142 let declarations = self.style.lock().unwrap().to_declarations();
143 crate::css::CSSRule::new(selector, declarations)
144 }
145}
146
147#[derive(Clone)]
160pub struct CSSMediaRule {
161 base: CSSRuleOM,
163 condition_text: String,
165 css_rules: Vec<Arc<Mutex<dyn CSSRuleTrait>>>,
167}
168
169impl std::fmt::Debug for CSSMediaRule {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 f.debug_struct("CSSMediaRule")
172 .field("base", &self.base)
173 .field("condition_text", &self.condition_text)
174 .field("css_rules_count", &self.css_rules.len())
175 .finish()
176 }
177}
178
179impl CSSMediaRule {
180 pub fn new(condition: &str) -> Self {
182 let css_text = format!("@media {} {{ }}", condition);
183 Self {
184 base: CSSRuleOM::new(CSSRuleType::MEDIA_RULE, &css_text),
185 condition_text: condition.to_string(),
186 css_rules: Vec::new(),
187 }
188 }
189
190 pub fn condition_text(&self) -> &str {
192 &self.condition_text
193 }
194
195 pub fn set_condition_text(&mut self, text: &str) {
197 self.condition_text = text.to_string();
198 self.update_css_text();
199 }
200
201 pub fn insert_rule(&mut self, rule: Arc<Mutex<dyn CSSRuleTrait>>, index: usize) {
203 if index > self.css_rules.len() {
204 self.css_rules.push(rule);
205 } else {
206 self.css_rules.insert(index, rule);
207 }
208 self.update_css_text();
209 }
210
211 pub fn delete_rule(&mut self, index: usize) -> bool {
213 if index < self.css_rules.len() {
214 self.css_rules.remove(index);
215 self.update_css_text();
216 true
217 } else {
218 false
219 }
220 }
221
222 pub fn length(&self) -> usize {
224 self.css_rules.len()
225 }
226
227 pub fn css_rule(&self, index: usize) -> Option<Arc<Mutex<dyn CSSRuleTrait>>> {
229 if index < self.css_rules.len() {
230 Some(Arc::clone(&self.css_rules[index]))
231 } else {
232 None
233 }
234 }
235
236 fn update_css_text(&mut self) {
238 let rules_text = self.css_rules
239 .iter()
240 .map(|r| r.lock().unwrap().get_css_text().to_string())
241 .collect::<Vec<_>>()
242 .join("\n");
243
244 self.base.set_css_text(&format!(
245 "@media {} {{\n{}\n}}",
246 self.condition_text, rules_text
247 ));
248 }
249}
250
251pub trait CSSRuleTrait {
253 fn rule_type(&self) -> CSSRuleType;
255
256 fn get_css_text(&self) -> &str;
258
259 fn set_css_text(&mut self, text: &str);
261}
262
263impl CSSRuleTrait for CSSStyleRule {
264 fn rule_type(&self) -> CSSRuleType {
265 CSSRuleType::STYLE_RULE
266 }
267
268 fn get_css_text(&self) -> &str {
269 self.base.get_css_text()
270 }
271
272 fn set_css_text(&mut self, text: &str) {
273 self.base.set_css_text(text);
274 }
275}
276
277impl CSSRuleTrait for CSSMediaRule {
278 fn rule_type(&self) -> CSSRuleType {
279 CSSRuleType::MEDIA_RULE
280 }
281
282 fn get_css_text(&self) -> &str {
283 self.base.get_css_text()
284 }
285
286 fn set_css_text(&mut self, text: &str) {
287 self.base.set_css_text(text);
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn test_css_style_rule() {
297 let rule = CSSStyleRule::new(".container");
298 assert_eq!(rule.selector_text(), ".container");
299 assert_eq!(rule.base.rule_type, CSSRuleType::STYLE_RULE);
300 }
301
302 #[test]
303 fn test_css_style_rule_set_property() {
304 let rule = CSSStyleRule::new(".container");
305 rule.style().lock().unwrap().set_property("color", "red", "");
306 assert_eq!(rule.style().lock().unwrap().get_property_value("color"), "red");
307 }
308
309 #[test]
310 fn test_css_style_rule_to_internal() {
311 let mut rule = CSSStyleRule::new(".container");
312 rule.style().lock().unwrap().set_property("color", "red", "");
313
314 let internal = rule.to_internal();
315 assert_eq!(internal.selector.text, ".container");
316 assert_eq!(internal.declarations.len(), 1);
317 }
318
319 #[test]
320 fn test_css_media_rule() {
321 let media_rule = CSSMediaRule::new("screen and (max-width: 600px)");
322 assert_eq!(media_rule.condition_text(), "screen and (max-width: 600px)");
323 assert_eq!(media_rule.base.rule_type, CSSRuleType::MEDIA_RULE);
324 }
325
326 #[test]
327 fn test_css_media_rule_insert_delete() {
328 let mut media_rule = CSSMediaRule::new("screen");
329 let style_rule = Arc::new(Mutex::new(CSSStyleRule::new(".class")));
330
331 media_rule.insert_rule(style_rule.clone(), 0);
332 assert_eq!(media_rule.length(), 1);
333
334 let deleted = media_rule.delete_rule(0);
335 assert!(deleted);
336 assert_eq!(media_rule.length(), 0);
337 }
338}