rspack_style/css/
rule.rs

1use crate::css::fileinfo::{FileInfo, FileWeakRef};
2use crate::css::node::{NodeRef, NodeWeakRef, StyleNode};
3use crate::css::parse::Parse;
4use crate::css::select_node::SelectorNode;
5use crate::css::style_rule::StyleRuleNode;
6use crate::css::var::VarRuleNode;
7use crate::extend::string::StringExtend;
8use crate::extend::vec_str::VecCharExtend;
9use crate::sourcemap::loc::{Loc, LocMap};
10use crate::style_core::context::ParseContext;
11use crate::style_core::option::OptionExtend;
12use crate::util::str_handle::{merge_spaces, merge_wrap};
13use serde::ser::SerializeStruct;
14use serde::{Serialize, Serializer};
15use serde_json::{Map, Value};
16use std::cell::RefCell;
17use std::collections::HashSet;
18use std::fmt::Write;
19use std::fmt::{Debug, Formatter};
20use std::ops::Deref;
21use std::rc::Rc;
22
23#[derive(Clone)]
24pub struct RuleNode {
25  // 选择器 文字
26  pub selector: Option<SelectorNode>,
27  // 根据 原始内容 -> 转化的 字符数组
28  pub origin_charlist: Vec<char>,
29  // 节点坐标
30  pub loc: Option<Loc>,
31  // 当前所有 索引 对应的 坐标行列 -> 用于执行 sourcemap
32  pub locmap: Option<LocMap>,
33  // 节点 父节点
34  pub parent: NodeWeakRef,
35  // 自己的引用关系
36  pub weak_self: NodeWeakRef,
37  // 节点 子节点
38  pub block_node: Vec<StyleNode>,
39  // 文件弱引用
40  pub file_info: FileWeakRef,
41  // 全局上下文
42  pub context: ParseContext,
43}
44
45impl Serialize for RuleNode {
46  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
47  where
48    S: Serializer,
49  {
50    let mut state = serializer.serialize_struct("RuleNode", 4)?;
51    state.serialize_field("content", &self.origin_charlist.poly())?;
52    state.serialize_field("loc", &self.loc)?;
53    state.serialize_field("select", &self.selector.as_ref().unwrap())?;
54    state.serialize_field("block_node", &self.block_node)?;
55    state.end()
56  }
57}
58
59impl Debug for RuleNode {
60  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61    f.debug_struct("RuleNode")
62      .field("content", &self.origin_charlist.poly())
63      .field("loc", &self.loc)
64      .field("select", &self.selector.as_ref().unwrap().value())
65      .field("block_node", &self.block_node)
66      .finish()
67  }
68}
69
70impl RuleNode {
71  ///
72  /// 构造方法
73  ///
74  pub fn new(
75    charlist: Vec<char>,
76    selector_txt: Vec<char>,
77    loc: Option<Loc>,
78    file_info: FileWeakRef,
79    context: ParseContext,
80  ) -> Result<NodeRef, String> {
81    let mut change_loc: Option<Loc> = loc;
82    let obj = RuleNode {
83      selector: None,
84      origin_charlist: charlist,
85      loc,
86      locmap: None,
87      block_node: vec![],
88      parent: None,
89      weak_self: None,
90      file_info: file_info.clone(),
91      context,
92    };
93    let heapobj = Rc::new(RefCell::new(obj));
94    let wek_self = Rc::downgrade(&heapobj);
95    heapobj.borrow_mut().weak_self = Some(wek_self.clone());
96
97    let selector = match SelectorNode::new(selector_txt, &mut change_loc, Some(wek_self), file_info)
98    {
99      Ok(result) => result,
100      Err(msg) => {
101        return Err(msg);
102      }
103    };
104    heapobj.borrow_mut().selector = Some(selector);
105    if heapobj.deref().borrow().get_options().sourcemap {
106      heapobj.borrow_mut().loc = change_loc.as_ref().cloned();
107      let (calcmap, _) = LocMap::merge(
108        change_loc.as_ref().unwrap(),
109        &heapobj.borrow().origin_charlist,
110      );
111      heapobj.borrow_mut().locmap = Some(calcmap);
112    }
113    heapobj.borrow_mut().parse_heap()?;
114    Ok(heapobj)
115  }
116
117  ///
118  /// 反序列化
119  ///
120  pub fn deserializer(
121    map: &Map<String, Value>,
122    context: ParseContext,
123    parent: NodeWeakRef,
124    fileinfo: FileWeakRef,
125  ) -> Result<Rc<RefCell<Self>>, String> {
126    let mut rule_node = Self {
127      selector: None,
128      origin_charlist: vec![],
129      loc: None,
130      locmap: None,
131      parent: parent.as_ref().cloned(),
132      weak_self: None,
133      block_node: vec![],
134      file_info: fileinfo.as_ref().cloned(),
135      context: context.clone(),
136    };
137    if let Some(Value::String(content)) = map.get("content") {
138      rule_node.origin_charlist = content.to_char_vec();
139    } else {
140      return Err("deserializer RuleNode has error -> content is empty!".to_string());
141    }
142    if let Some(Value::Object(loc)) = map.get("loc") {
143      rule_node.loc = Some(Loc::deserializer(loc));
144      rule_node.locmap =
145        Some(LocMap::merge(rule_node.loc.as_ref().unwrap(), &rule_node.origin_charlist).0);
146    } else {
147      rule_node.locmap = Some(LocMap::new(&rule_node.origin_charlist));
148    }
149    let heapobj = Rc::new(RefCell::new(rule_node));
150    let weak_self = Rc::downgrade(&heapobj);
151    heapobj.borrow_mut().weak_self = Some(weak_self.clone());
152    let json_block_node = map.get("block_node");
153    let mut block_node_recovery_list = vec![];
154    if let Some(Value::Array(block_nodes)) = json_block_node {
155      for json_node in block_nodes {
156        if let Value::Object(json_stylenode) = json_node {
157          block_node_recovery_list.push(StyleNode::deserializer(
158            json_stylenode,
159            context.clone(),
160            Some(weak_self.clone()),
161            fileinfo.as_ref().cloned(),
162          )?);
163        }
164      }
165    }
166    if let Some(Value::Object(map)) = map.get("select") {
167      heapobj.borrow_mut().selector = Some(SelectorNode::deserializer(
168        map,
169        Some(weak_self),
170        fileinfo.as_ref().cloned(),
171      )?);
172    } else {
173      return Err("deserializer RuleNode has error -> select is empty!".to_string());
174    }
175    heapobj.borrow_mut().block_node = block_node_recovery_list;
176    Ok(heapobj)
177  }
178
179  ///
180  /// parse 当前文件下 所有的 select 字符串
181  /// 需要 第一遍 完成基本遍历
182  /// 由 fileinfo -> call 调用
183  ///
184  pub fn parse_select_all_node(&self) -> Result<(), String> {
185    for node in self.block_node.iter() {
186      if let StyleNode::Rule(heapnode) = node {
187        let mut mut_node = heapnode.borrow_mut();
188        if let Some(SelectorNode::Select(s_node)) = mut_node.selector.as_mut() {
189          s_node.parse()?;
190        }
191        drop(mut_node);
192        heapnode.borrow().parse_select_all_node()?;
193      }
194    }
195    Ok(())
196  }
197
198  pub fn visit_mut_file(&self, fileinfo: &mut FileInfo) {
199    self.block_node.iter().for_each(|x| {
200      if let StyleNode::Rule(rule) = x {
201        rule.borrow().visit_mut_file(fileinfo);
202      }
203    });
204  }
205
206  pub fn getrules(&self) -> Vec<NodeRef> {
207    let mut list = vec![];
208
209    self.block_node.iter().for_each(|x| {
210      if let StyleNode::Rule(rule) = x {
211        list.push(rule.clone());
212      }
213    });
214    list
215  }
216
217  pub fn get_style_rule(&self) -> Vec<StyleRuleNode> {
218    let mut list = vec![];
219    self.block_node.iter().for_each(|x| {
220      if let StyleNode::Var(VarRuleNode::StyleRule(style)) = x {
221        list.push(style.clone());
222      }
223    });
224    list
225  }
226
227  pub fn code_gen(&self, content: &mut String, map: &mut HashSet<String>) -> Result<(), String> {
228    let rules = self.get_style_rule();
229    let (select_txt, media_txt) = self.selector.as_ref().unwrap().code_gen(map).unwrap();
230    let mut tab: String = "".to_string();
231    let mut index = 0;
232    let option = self.get_options();
233
234    let mut br_char = "\n";
235    if option.minify {
236      br_char = " ";
237    } else {
238      while index < option.tabspaces {
239        tab += " ";
240        index += 1;
241      }
242    }
243
244    let handle_str = |content: &str| merge_spaces(merge_wrap(content).as_str());
245
246    // example -> @keyframes, @font-family
247    if select_txt.find('@') == Some(0) {
248      let single_key_rule_content = {
249        if option.minify {
250          handle_str(self.origin_charlist.poly().as_str())
251        } else {
252          self.origin_charlist.poly()
253        }
254      };
255
256      if media_txt.is_empty() {
257        *content += format!(
258          "{}{}{}{}{}{}{}",
259          br_char,
260          select_txt,
261          "{",
262          br_char,
263          tab.clone() + &tab.clone() + single_key_rule_content.as_str(),
264          br_char,
265          "}"
266        )
267        .as_str();
268      } else {
269        *content += format!(
270          "{}{}{}{}{}{}{}{}{}{}{}{}",
271          br_char,
272          media_txt,
273          br_char,
274          "{",
275          tab.clone() + &select_txt,
276          br_char,
277          "{",
278          tab.clone() + &tab.clone() + &tab.clone() + single_key_rule_content.as_str(),
279          br_char,
280          tab.clone() + "}",
281          br_char,
282          "}"
283        )
284        .as_str();
285      }
286
287      // 后续不递归了
288      return Ok(());
289    } else if !rules.is_empty() {
290      let create_rules = |tab: String| -> Result<String, String> {
291        let mut res: String = "".to_string();
292        for (index, rule_res) in rules.iter().enumerate() {
293          if index != rules.len() - 1 {
294            if !option.minify {
295              writeln!(res, "{}{}", tab.clone(), rule_res.code_gen()?)
296                .expect("write stream has error");
297            } else {
298              write!(res, " {}{}", tab.clone(), rule_res.code_gen()?)
299                .expect("write stream has error");
300            }
301          } else {
302            write!(res, "{}{}", tab.clone(), rule_res.code_gen()?).expect("write stream has error");
303          }
304        }
305        Ok(res)
306      };
307
308      if media_txt.is_empty() {
309        *content += format!(
310          "{}{}{}{}{}{}{}{}",
311          br_char,
312          select_txt,
313          " {",
314          br_char,
315          create_rules(tab)?,
316          br_char,
317          "}",
318          br_char,
319        )
320        .as_ref();
321      } else {
322        *content += format!(
323          "{}{}{}{}{}{}{}{}{}{}{}{}",
324          br_char,
325          media_txt,
326          " {",
327          br_char,
328          tab.clone() + &select_txt,
329          " {",
330          br_char,
331          create_rules(tab.clone() + &tab.clone())?,
332          br_char,
333          "  }",
334          br_char,
335          "}"
336        )
337        .as_ref();
338      }
339    }
340
341    for node_ref in self.getrules() {
342      node_ref.deref().borrow().code_gen(content, map)?;
343    }
344
345    Ok(())
346  }
347}