Skip to main content

iris_cssom/
cssrule.rs

1//! CSSRule 对象封装
2//!
3//! 实现 Web 标准的 CSSRule 接口及其子类型。
4
5use std::sync::{Arc, Mutex};
6use crate::cssom::CSSStyleDeclaration;
7
8/// CSSRule 类型枚举
9#[allow(non_camel_case_types)]
10#[derive(Debug, Clone, PartialEq)]
11pub enum CSSRuleType {
12    /// 样式规则: selector { ... }
13    STYLE_RULE,
14    /// 媒体规则: @media { ... }
15    MEDIA_RULE,
16    /// 关键帧规则: @keyframes { ... }
17    KEYFRAMES_RULE,
18    /// 导入规则: @import
19    IMPORT_RULE,
20    /// 字体规则: @font-face
21    FONT_FACE_RULE,
22    /// 支持规则: @supports
23    SUPPORTS_RULE,
24}
25
26/// CSSRule 对象(基类)
27///
28/// 表示一个 CSS 规则,是所有 CSS 规则类型的基类。
29#[derive(Debug, Clone)]
30pub struct CSSRuleOM {
31    /// 规则类型
32    pub rule_type: CSSRuleType,
33    /// CSS 文本
34    css_text: String,
35    /// 父样式表(可选)
36    parent_stylesheet: Option<String>,
37}
38
39impl CSSRuleOM {
40    /// 创建新的 CSSRule
41    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    /// 获取 CSS 文本
50    pub fn get_css_text(&self) -> &str {
51        &self.css_text
52    }
53
54    /// 设置 CSS 文本
55    pub fn set_css_text(&mut self, text: &str) {
56        self.css_text = text.to_string();
57    }
58
59    /// 获取父样式表名称
60    pub fn parent_stylesheet(&self) -> Option<&str> {
61        self.parent_stylesheet.as_deref()
62    }
63}
64
65/// CSSStyleRule - 样式规则
66///
67/// 表示一个普通的样式规则:`selector { declarations }`
68///
69/// # 示例
70///
71/// ```rust
72/// use iris_cssom::cssrule::{CSSStyleRule, CSSRuleOM};
73/// use iris_cssom::cssom::CSSStyleDeclaration;
74///
75/// let mut rule = CSSStyleRule::new(".class");
76/// rule.style().lock().unwrap().set_property("color", "red", "");
77/// ```
78#[derive(Debug, Clone)]
79pub struct CSSStyleRule {
80    /// 基础规则
81    base: CSSRuleOM,
82    /// 选择器文本
83    selector_text: String,
84    /// 样式声明
85    style: Arc<Mutex<CSSStyleDeclaration>>,
86}
87
88impl CSSStyleRule {
89    /// 创建新的样式规则
90    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    /// 从内部 CSSRule 创建
100    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    /// 获取选择器文本
115    pub fn selector_text(&self) -> &str {
116        &self.selector_text
117    }
118
119    /// 设置选择器文本
120    pub fn set_selector_text(&mut self, text: &str) {
121        self.selector_text = text.to_string();
122        self.update_css_text();
123    }
124
125    /// 获取样式声明对象
126    pub fn style(&self) -> Arc<Mutex<CSSStyleDeclaration>> {
127        Arc::clone(&self.style)
128    }
129
130    /// 更新 CSS 文本
131    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    /// 转换为内部 CSSRule
140    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/// CSSMediaRule - 媒体查询规则
148///
149/// 表示一个 @media 规则:`@media query { ...rules... }`
150///
151/// # 示例
152///
153/// ```rust
154/// use iris_cssom::cssrule::CSSMediaRule;
155///
156/// let mut media_rule = CSSMediaRule::new("screen and (max-width: 600px)");
157/// // 可以添加子规则
158/// ```
159#[derive(Clone)]
160pub struct CSSMediaRule {
161    /// 基础规则
162    base: CSSRuleOM,
163    /// 媒体查询条件
164    condition_text: String,
165    /// 子规则列表
166    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    /// 创建新的媒体规则
181    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    /// 获取媒体查询条件
191    pub fn condition_text(&self) -> &str {
192        &self.condition_text
193    }
194
195    /// 设置媒体查询条件
196    pub fn set_condition_text(&mut self, text: &str) {
197        self.condition_text = text.to_string();
198        self.update_css_text();
199    }
200
201    /// 插入子规则
202    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    /// 删除子规则
212    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    /// 获取子规则数量
223    pub fn length(&self) -> usize {
224        self.css_rules.len()
225    }
226
227    /// 获取子规则
228    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    /// 更新 CSS 文本
237    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
251/// CSSRule trait - 所有 CSSRule 类型的通用接口
252pub trait CSSRuleTrait {
253    /// 获取规则类型
254    fn rule_type(&self) -> CSSRuleType;
255    
256    /// 获取 CSS 文本
257    fn get_css_text(&self) -> &str;
258    
259    /// 设置 CSS 文本
260    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}