1use std::{borrow::Cow, collections::BTreeMap};
25
26pub type ScopeId = Cow<'static, str>;
29pub type Style = Cow<'static, str>;
32pub type Order = u32;
34
35#[derive(Debug, Default, Clone)]
36pub struct LayeredCss {
37 pub styles: BTreeMap<ScopeId, StyleTree>,
40}
41impl LayeredCss {
42 pub fn new() -> Self {
43 Self {
44 styles: BTreeMap::new(),
45 }
46 }
47 pub fn root_scope_exist(&self, root_scope_id: impl Into<ScopeId>) -> bool {
48 self.styles.contains_key(&root_scope_id.into())
49 }
50 pub fn layer_exist_in_root_scope(
51 &self,
52 root_scope_id: impl Into<ScopeId>,
53 layer_scope_id: impl Into<ScopeId>,
54 ) -> bool {
55 let root_scope_id = root_scope_id.into();
56 let layer_scope_id = layer_scope_id.into();
57 self.styles
58 .get(&root_scope_id)
59 .map(|style_tree| style_tree.uniq_layers.contains_key(&layer_scope_id))
60 .unwrap_or(false)
61 }
62
63 pub fn add_style_from_parts(
66 &mut self,
67 root_scope_id: impl Into<ScopeId>,
68 order_in_chain: Order,
69 layer_scope_id: impl Into<ScopeId>,
70 style: impl Into<Style>,
71 ) -> bool {
72 let root_scope_id = root_scope_id.into();
73 let layer_scope_id = layer_scope_id.into();
74 let style = style.into();
75 let layers_of_chain = &mut self
76 .styles
77 .entry(root_scope_id)
78 .or_insert_with(Default::default)
79 .uniq_layers;
80 if let Some((order, _)) = layers_of_chain.get(&layer_scope_id) {
81 debug_assert_eq!(*order, order_in_chain);
82 return false;
83 }
84 if style.is_empty() {
85 return false;
86 }
87 let res = layers_of_chain
88 .insert(layer_scope_id, (order_in_chain, style))
89 .is_none();
90 debug_assert!(res);
91 true
92 }
93 #[cfg(feature = "rcss_enable")]
97 pub fn add_style_with_order<T>(&mut self, root_scope_id: ScopeId, order_in_chain: Order) -> bool
98 where
99 T: rcss::ScopeCommon,
100 {
101 self.add_style_from_parts(root_scope_id, order_in_chain, T::SCOPE_ID, T::STYLE)
102 }
103
104 #[cfg(feature = "rcss_enable")]
112 pub fn add_style_chain<C>(&mut self, ts_chain: &C) -> bool
113 where
114 C: rcss::extend::in_chain_ops::ScopeChainOps,
115 {
116 let root_scope_id = ts_chain.root_scope_id();
117 let mut chain = vec![];
120
121 ts_chain.for_each(|scope_id, style| {
122 chain.push((scope_id, style));
123 });
124
125 debug_assert_eq!(
126 chain.last().expect("Chain is empty").0,
127 ts_chain.root_scope_id()
128 );
129 let mut any_update = false;
130
131 for (order, (scope_id, style)) in chain.into_iter().rev().enumerate() {
132 any_update |= self.add_style_from_parts(root_scope_id, order as Order, scope_id, style);
133 }
134 any_update
135 }
136}
137#[derive(Default, Clone, Debug)]
139pub struct StyleTree {
140 pub uniq_layers: BTreeMap<ScopeId, (Order, Style)>,
144}
145
146impl StyleTree {
147 pub fn render(
151 &self,
152 always_output_layer: bool,
153 root_scope_id: impl Into<ScopeId>,
154 ) -> Option<String> {
155 let root_scope_id = root_scope_id.into();
156 let root_scope = self.uniq_layers.get(&root_scope_id)?.clone();
157
158 debug_assert_eq!(root_scope.0, 0, "Root layer must have order 0");
159
160 let mut rest = self
161 .uniq_layers
162 .iter()
163 .filter(|(layer, _)| **layer != root_scope_id)
164 .peekable();
165
166 if !always_output_layer && rest.peek().is_none() {
167 return Some(root_scope.1.to_string());
169 }
170 let mut ordered_layers: Vec<_> = rest
171 .map(|(layer, (order, style))| (order, layer, style))
172 .collect();
173 ordered_layers.sort_by_key(|(order, _, _)| *order);
174 ordered_layers.insert(0, (&root_scope.0, &root_scope_id, &root_scope.1));
175
176 let mut first: bool = true;
177 let mut header = String::from("@layer ");
178
179 for (_, scope_id, _) in &ordered_layers {
180 if !first {
181 header.push(',');
182 }
183 header.push_str(scope_id);
184 first = false;
185 }
186 header.push(';');
187
188 let mut style = header;
189
190 for (_, scope_id, layer_impl) in ordered_layers {
192 style.push_str("@layer ");
193 style.push_str(scope_id);
194 style.push_str("{");
195 style.push_str(layer_impl);
196 style.push_str("}");
197 }
198 Some(style)
199 }
200}
201
202#[cfg(test)]
203#[cfg(feature = "rcss_enable")]
204mod test {
205 use rcss::ScopeCommon;
206
207 use super::*;
208 #[test]
209 fn check_raw_api() {
210 let mut chain = LayeredCss::new();
211 assert!(chain.add_style_from_parts("root", 0, "root", "style1{color:red}"));
212 assert!(chain.add_style_from_parts("root", 1, "layer2", "style2{color:blue}"));
213 assert!(chain.add_style_from_parts("root", 2, "layer3", "style3{color:green}"));
214 assert!(!chain.add_style_from_parts("root", 2, "layer3", "style3{color:green}"));
216
217 let representation = chain
218 .styles
219 .get("root")
220 .unwrap()
221 .render(false, "root")
222 .unwrap();
223 let mut expectation = String::from("@layer root,layer2,layer3;");
224 expectation.push_str("@layer root{style1{color:red}}");
225 expectation.push_str("@layer layer2{style2{color:blue}}");
226 expectation.push_str("@layer layer3{style3{color:green}}");
227 assert_eq!(representation, expectation);
228 }
229
230 #[test]
231 fn test_chain() {
232 rcss::css! {
233 @rcss(pub struct Style1);
234 .foo{color:red}
235 }
236 rcss::css! {
237 @rcss(pub struct Style2);
238 @rcss(extend Style1);
239 .foo{color:orange}
240 }
241 rcss::css! {
242 @rcss(pub struct Style3);
243 @rcss(extend Style2);
244 .foo{color:green}
245 }
246 let mut chain = LayeredCss::new();
247 assert!(chain.add_style_chain(&Style3::new()));
248 let root_id = Style1::SCOPE_ID;
249 let layer2_id = Style2::SCOPE_ID;
250 let layer3_id = Style3::SCOPE_ID;
251 let root_foo = Style1::new().foo;
252 let layer2_foo = Style2::new().foo.split_whitespace().last().unwrap();
253 let layer3_foo = Style3::new().foo.split_whitespace().last().unwrap();
254
255 let mut expectation = format!("@layer {root_id},{layer2_id},{layer3_id};");
256
257 expectation.push_str(&format!("@layer {root_id}{{.{root_foo}{{color:red}}}}"));
258 expectation.push_str(&format!(
259 "@layer {layer2_id}{{.{layer2_foo}{{color:orange}}}}"
260 ));
261 expectation.push_str(&format!(
262 "@layer {layer3_id}{{.{layer3_foo}{{color:green}}}}"
263 ));
264
265 assert_eq!(
266 chain
267 .styles
268 .get(root_id)
269 .unwrap()
270 .render(false, root_id)
271 .unwrap(),
272 expectation
273 );
274 }
275}