Skip to main content

iris_cssom/
cssrulelist.rs

1//! CSSRuleList - CSSOM 规则列表
2//!
3//! 实现 Web 标准的 CSSRuleList 接口。
4
5use std::sync::{Arc, Mutex};
6use crate::cssrule::CSSRuleTrait;
7
8/// CSSRuleList 对象
9///
10/// 表示一个 CSSRule 对象的集合,通常通过 `CSSStyleSheet.cssRules` 访问。
11/// 这是一个实时更新的列表(live list),当样式表变化时自动更新。
12///
13/// # 示例
14///
15/// ```rust
16/// use iris_cssom::cssrulelist::CSSRuleList;
17/// use iris_cssom::cssrule::CSSStyleRule;
18/// use std::sync::{Arc, Mutex};
19///
20/// let mut list = CSSRuleList::new();
21/// let rule = Arc::new(Mutex::new(CSSStyleRule::new(".class")));
22/// list.append_rule(rule);
23///
24/// assert_eq!(list.length(), 1);
25/// ```
26#[derive(Clone)]
27pub struct CSSRuleList {
28    /// 规则列表
29    rules: Vec<Arc<Mutex<dyn CSSRuleTrait>>>,
30}
31
32impl std::fmt::Debug for CSSRuleList {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("CSSRuleList")
35            .field("rules_count", &self.rules.len())
36            .finish()
37    }
38}
39
40impl CSSRuleList {
41    /// 创建空的规则列表
42    pub fn new() -> Self {
43        Self {
44            rules: Vec::new(),
45        }
46    }
47
48    /// 从规则向量创建
49    pub fn from_rules(rules: Vec<Arc<Mutex<dyn CSSRuleTrait>>>) -> Self {
50        Self { rules }
51    }
52
53    /// 获取规则数量
54    ///
55    /// # 示例
56    ///
57    /// ```rust
58    /// use iris_cssom::cssrulelist::CSSRuleList;
59    ///
60    /// let list = CSSRuleList::new();
61    /// assert_eq!(list.length(), 0);
62    /// ```
63    pub fn length(&self) -> u32 {
64        self.rules.len() as u32
65    }
66
67    /// 根据索引获取规则
68    ///
69    /// # 参数
70    ///
71    /// * `index` - 规则索引(从 0 开始)
72    ///
73    /// # 返回值
74    ///
75    /// 返回指定索引的规则,如果索引超出范围则返回 `None`
76    ///
77    /// # 示例
78    ///
79    /// ```rust
80    /// use iris_cssom::cssrulelist::CSSRuleList;
81    /// use iris_cssom::cssrule::CSSStyleRule;
82    /// use std::sync::{Arc, Mutex};
83    ///
84    /// let mut list = CSSRuleList::new();
85    /// let rule = Arc::new(Mutex::new(CSSStyleRule::new(".class")));
86    /// list.append_rule(rule.clone());
87    ///
88    /// let retrieved = list.item(0);
89    /// assert!(retrieved.is_some());
90    /// ```
91    pub fn item(&self, index: u32) -> Option<Arc<Mutex<dyn CSSRuleTrait>>> {
92        if index < self.rules.len() as u32 {
93            Some(Arc::clone(&self.rules[index as usize]))
94        } else {
95            None
96        }
97    }
98
99    /// 添加规则到列表末尾
100    ///
101    /// # 参数
102    ///
103    /// * `rule` - 要添加的规则
104    pub fn append_rule(&mut self, rule: Arc<Mutex<dyn CSSRuleTrait>>) {
105        self.rules.push(rule);
106    }
107
108    /// 插入规则到指定位置
109    ///
110    /// # 参数
111    ///
112    /// * `rule` - 要插入的规则
113    /// * `index` - 插入位置
114    ///
115    /// # 返回值
116    ///
117    /// 插入成功返回 `true`,索引超出范围返回 `false`
118    pub fn insert_rule(&mut self, rule: Arc<Mutex<dyn CSSRuleTrait>>, index: usize) -> bool {
119        if index > self.rules.len() {
120            return false;
121        }
122        self.rules.insert(index, rule);
123        true
124    }
125
126    /// 删除指定索引的规则
127    ///
128    /// # 参数
129    ///
130    /// * `index` - 要删除的规则索引
131    ///
132    /// # 返回值
133    ///
134    /// 删除成功返回 `true`,索引超出范围返回 `false`
135    pub fn remove_rule(&mut self, index: usize) -> bool {
136        if index < self.rules.len() {
137            self.rules.remove(index);
138            true
139        } else {
140            false
141        }
142    }
143
144    /// 清空所有规则
145    pub fn clear(&mut self) {
146        self.rules.clear();
147    }
148
149    /// 获取所有规则的迭代器
150    pub fn iter(&self) -> impl Iterator<Item = &Arc<Mutex<dyn CSSRuleTrait>>> {
151        self.rules.iter()
152    }
153
154    /// 获取所有规则的 CSS 文本
155    ///
156    /// # 示例
157    ///
158    /// ```rust
159    /// use iris_cssom::cssrulelist::CSSRuleList;
160    /// use iris_cssom::cssrule::CSSStyleRule;
161    /// use std::sync::{Arc, Mutex};
162    ///
163    /// let mut list = CSSRuleList::new();
164    /// let rule1 = Arc::new(Mutex::new(CSSStyleRule::new(".class1")));
165    /// let rule2 = Arc::new(Mutex::new(CSSStyleRule::new(".class2")));
166    /// list.append_rule(rule1);
167    /// list.append_rule(rule2);
168    ///
169    /// let css_texts = list.get_all_css_texts();
170    /// assert_eq!(css_texts.len(), 2);
171    /// ```
172    pub fn get_all_css_texts(&self) -> Vec<String> {
173        self.rules
174            .iter()
175            .map(|r| r.lock().unwrap().get_css_text().to_string())
176            .collect()
177    }
178}
179
180impl Default for CSSRuleList {
181    fn default() -> Self {
182        Self::new()
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use crate::cssrule::CSSStyleRule;
190
191    #[test]
192    fn test_new_list() {
193        let list = CSSRuleList::new();
194        assert_eq!(list.length(), 0);
195    }
196
197    #[test]
198    fn test_append_rule() {
199        let mut list = CSSRuleList::new();
200        let rule = Arc::new(Mutex::new(CSSStyleRule::new(".class")));
201        list.append_rule(rule);
202        assert_eq!(list.length(), 1);
203    }
204
205    #[test]
206    fn test_item() {
207        let mut list = CSSRuleList::new();
208        let rule = Arc::new(Mutex::new(CSSStyleRule::new(".class")));
209        list.append_rule(rule.clone());
210        
211        let retrieved = list.item(0);
212        assert!(retrieved.is_some());
213        
214        let not_found = list.item(1);
215        assert!(not_found.is_none());
216    }
217
218    #[test]
219    fn test_insert_rule() {
220        let mut list = CSSRuleList::new();
221        let rule1 = Arc::new(Mutex::new(CSSStyleRule::new(".class1")));
222        let rule2 = Arc::new(Mutex::new(CSSStyleRule::new(".class2")));
223        
224        list.append_rule(rule1);
225        list.insert_rule(rule2, 0); // 插入到开头
226        
227        assert_eq!(list.length(), 2);
228        assert!(list.item(0).unwrap().lock().unwrap().get_css_text().contains(".class2"));
229    }
230
231    #[test]
232    fn test_remove_rule() {
233        let mut list = CSSRuleList::new();
234        let rule = Arc::new(Mutex::new(CSSStyleRule::new(".class")));
235        list.append_rule(rule);
236        
237        assert!(list.remove_rule(0));
238        assert_eq!(list.length(), 0);
239        
240        assert!(!list.remove_rule(0)); // 已经空了
241    }
242
243    #[test]
244    fn test_clear() {
245        let mut list = CSSRuleList::new();
246        list.append_rule(Arc::new(Mutex::new(CSSStyleRule::new(".class1"))));
247        list.append_rule(Arc::new(Mutex::new(CSSStyleRule::new(".class2"))));
248        
249        list.clear();
250        assert_eq!(list.length(), 0);
251    }
252
253    #[test]
254    fn test_get_all_css_texts() {
255        let mut list = CSSRuleList::new();
256        list.append_rule(Arc::new(Mutex::new(CSSStyleRule::new(".class1"))));
257        list.append_rule(Arc::new(Mutex::new(CSSStyleRule::new(".class2"))));
258        
259        let texts = list.get_all_css_texts();
260        assert_eq!(texts.len(), 2);
261        assert!(texts[0].contains(".class1"));
262        assert!(texts[1].contains(".class2"));
263    }
264}