1use alloc::borrow::Cow;
2use core::fmt;
3
4use super::*;
5use crate::property::Property;
6
7#[derive(Clone, Debug, PartialEq)]
9pub enum PropertyMeta {
10 Normal {
12 property: Property,
14 },
15 Important {
17 property: Property,
19 },
20 DebugGroup {
28 original_name_value: Box<(String, String)>,
30 properties: Box<[Property]>,
32 important: bool,
34 disabled: bool,
36 },
37}
38
39impl PropertyMeta {
40 pub fn new_debug_properties(source: &str) -> Vec<Self> {
46 parser::parse_inline_style(source, parser::StyleParsingDebugMode::Debug).0
47 }
48
49 pub fn to_debug_state(&self, disabled: bool) -> Self {
55 match self {
56 Self::Normal { property } => Self::DebugGroup {
57 original_name_value: Box::new((
58 self.get_property_name().into(),
59 self.get_property_value_string(),
60 )),
61 properties: Box::new([property.clone()]),
62 important: false,
63 disabled,
64 },
65 Self::Important { property } => Self::DebugGroup {
66 original_name_value: Box::new((
67 self.get_property_name().into(),
68 self.get_property_value_string(),
69 )),
70 properties: Box::new([property.clone()]),
71 important: false,
72 disabled,
73 },
74 Self::DebugGroup {
75 original_name_value,
76 properties,
77 important,
78 ..
79 } => Self::DebugGroup {
80 original_name_value: original_name_value.clone(),
81 properties: properties.clone(),
82 important: *important,
83 disabled,
84 },
85 }
86 }
87
88 pub fn is_important(&self) -> bool {
90 match self {
91 Self::Normal { .. } => false,
92 Self::Important { .. } => true,
93 Self::DebugGroup { important, .. } => *important,
94 }
95 }
96
97 pub fn get_property_name(&self) -> Cow<'static, str> {
99 match self {
100 Self::Normal { property } => property.get_property_name().into(),
101 Self::Important { property } => property.get_property_name().into(),
102 Self::DebugGroup {
103 original_name_value,
104 ..
105 } => original_name_value.0.clone().into(),
106 }
107 }
108
109 pub fn get_property_value_string(&self) -> String {
113 match self {
114 Self::Normal { property } => property.get_property_value_string(),
115 Self::Important { property } => {
116 let mut v = property.get_property_value_string();
117 v.push_str(" !important");
118 v
119 }
120 Self::DebugGroup {
121 original_name_value,
122 ..
123 } => original_name_value.1.clone(),
124 }
125 }
126
127 pub fn is_disabled(&self) -> bool {
129 match self {
130 Self::Normal { .. } => false,
131 Self::Important { .. } => false,
132 Self::DebugGroup { disabled, .. } => *disabled,
133 }
134 }
135
136 pub fn is_invalid(&self) -> bool {
138 match self {
139 Self::Normal { .. } => false,
140 Self::Important { .. } => false,
141 Self::DebugGroup { properties, .. } => properties.is_empty(),
142 }
143 }
144
145 pub fn is_deprecated(&self) -> bool {
147 match self {
148 Self::Normal { property, .. } | Self::Important { property, .. } => {
149 property.is_deprecated()
150 }
151 Self::DebugGroup { .. } => false,
152 }
153 }
154
155 pub fn merge_to_node_properties(
157 &self,
158 node_properties: &mut NodeProperties,
159 parent_node_properties: Option<&NodeProperties>,
160 current_font_size: f32,
161 ) {
162 match self {
163 PropertyMeta::Normal { property: p } => {
164 node_properties.merge_property(p, parent_node_properties, current_font_size)
165 }
166 PropertyMeta::Important { property: p } => {
167 node_properties.merge_property(p, parent_node_properties, current_font_size)
168 }
169 PropertyMeta::DebugGroup {
170 properties,
171 disabled,
172 ..
173 } => {
174 if !disabled {
175 for p in &**properties {
176 node_properties.merge_property(p, parent_node_properties, current_font_size)
177 }
178 }
179 }
180 }
181 }
182
183 pub fn iter(&self) -> PropertyMetaIter {
185 PropertyMetaIter { pm: self, cur: 0 }
186 }
187
188 #[cfg(test)]
189 #[doc(hidden)]
190 pub fn property(&self) -> Option<Property> {
191 match self {
192 Self::Normal { property } | Self::Important { property } => Some(property.clone()),
193 Self::DebugGroup { .. } => None,
194 }
195 }
196}
197
198impl fmt::Display for PropertyMeta {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
200 if self.is_disabled() {
201 write!(
202 f,
203 "/* {}: {}; */",
204 self.get_property_name(),
205 self.get_property_value_string(),
206 )
207 } else {
208 write!(
209 f,
210 "{}: {};",
211 self.get_property_name(),
212 self.get_property_value_string(),
213 )
214 }
215 }
216}
217
218pub struct PropertyMetaIter<'a> {
220 pm: &'a PropertyMeta,
221 cur: usize,
222}
223
224impl<'a> Iterator for PropertyMetaIter<'a> {
225 type Item = &'a Property;
226
227 fn next(&mut self) -> Option<Self::Item> {
228 match self.pm {
229 PropertyMeta::Normal { property } | PropertyMeta::Important { property } => {
230 if self.cur == 0 {
231 self.cur = 1;
232 Some(property)
233 } else {
234 None
235 }
236 }
237 PropertyMeta::DebugGroup { properties, .. } => {
238 if self.cur < properties.len() {
239 let ret = &properties[self.cur];
240 self.cur += 1;
241 Some(ret)
242 } else {
243 None
244 }
245 }
246 }
247 }
248}
249
250#[derive(Clone, Debug)]
252pub struct Rule {
253 pub(crate) selector: Selector,
254 pub(crate) properties: Vec<PropertyMeta>,
255 pub(crate) media: Option<Rc<Media>>,
256 pub(super) index: u32,
257 pub(crate) has_font_size: bool,
258}
259
260impl fmt::Display for Rule {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 let media_queries = self.get_media_query_string_list();
263 for media in media_queries.iter() {
264 write!(f, "@media {} {{ ", media)?;
265 }
266 let selector = self.get_selector_string();
267 write!(f, "{} {{ ", selector)?;
268 for prop in self.properties() {
269 write!(
270 f,
271 "{}: {}; ",
272 prop.get_property_name(),
273 prop.get_property_value_string()
274 )?;
275 }
276 write!(f, "}}")?;
277 for _ in media_queries.iter() {
278 write!(f, " }}")?;
279 }
280 Ok(())
281 }
282}
283
284impl Rule {
285 pub fn new_empty() -> Box<Self> {
287 Box::new(Self {
288 selector: Selector::star_selector(),
289 properties: Vec::with_capacity(0),
290 media: None,
291 index: 0,
292 has_font_size: false,
293 })
294 }
295
296 pub(crate) fn new(
297 selector: Selector,
298 properties: Vec<PropertyMeta>,
299 media: Option<Rc<Media>>,
300 ) -> Box<Self> {
301 Self::new_with_index(selector, properties, media, 0)
302 }
303
304 pub(crate) fn new_with_index(
305 selector: Selector,
306 properties: Vec<PropertyMeta>,
307 media: Option<Rc<Media>>,
308 index: u32,
309 ) -> Box<Self> {
310 let mut has_font_size = false;
311 for p in properties.iter() {
312 match p {
313 PropertyMeta::Normal { property } | PropertyMeta::Important { property } => {
314 match property {
315 Property::FontSize(..) => {
316 has_font_size = true;
317 }
318 _ => {}
319 }
320 }
321 PropertyMeta::DebugGroup { properties, .. } => {
322 for property in properties.iter() {
323 match property {
324 Property::FontSize(..) => {
325 has_font_size = true;
326 }
327 _ => {}
328 }
329 }
330 }
331 }
332 }
333 Box::new(Self {
334 selector,
335 properties,
336 media,
337 index,
338 has_font_size,
339 })
340 }
341
342 pub fn from_parts_str<'a>(
346 media_query_str_list: impl IntoIterator<Item = &'a str>,
347 selector_str: &str,
348 ) -> Result<Box<Self>, Warning> {
349 let mut media = None;
350 for (index, media_str) in media_query_str_list.into_iter().enumerate() {
351 let cur_media = parser::parse_media_expression_only(media_str).map_err(|mut w| {
352 w.message = format!("{} (in media index {})", w.message.as_str(), index).into();
353 w
354 })?;
355 media = Some(Rc::new(cur_media));
356 }
357 let selector = parser::parse_selector_only(selector_str)?;
358 Ok(Self::new(selector, vec![], media))
359 }
360
361 pub fn modify_selector(&self, selector_str: &str) -> Result<Box<Self>, Warning> {
363 let media = self.media.clone();
364 let selector = parser::parse_selector_only(selector_str)?;
365 let properties = self.properties.clone();
366 Ok(Self::new(selector, properties, media))
367 }
368
369 pub fn add_properties(&self, p: impl IntoIterator<Item = PropertyMeta>) -> Box<Self> {
371 let media = self.media.clone();
372 let selector = self.selector.clone();
373 let mut properties = self.properties.clone();
374 for p in p {
375 properties.push(p);
376 }
377 Self::new(selector, properties, media)
378 }
379
380 pub fn set_property_disabled(&self, index: usize, disabled: bool) -> Option<Box<Self>> {
382 let media = self.media.clone();
383 let selector = self.selector.clone();
384 let mut properties = self.properties.clone();
385 if index < properties.len() {
386 properties[index] = properties[index].to_debug_state(disabled);
387 Some(Self::new(selector, properties, media))
388 } else {
389 None
390 }
391 }
392
393 pub fn remove_property(&self, index: usize) -> Option<Box<Self>> {
395 let media = self.media.clone();
396 let selector = self.selector.clone();
397 let mut properties = self.properties.clone();
398 if index < properties.len() {
399 properties.remove(index);
400 Some(Self::new(selector, properties, media))
401 } else {
402 None
403 }
404 }
405
406 pub fn replace_properties(
408 &self,
409 range: impl core::ops::RangeBounds<usize>,
410 p: impl IntoIterator<Item = PropertyMeta>,
411 ) -> Option<Box<Self>> {
412 use core::ops::Bound;
413 let media = self.media.clone();
414 let selector = self.selector.clone();
415 let mut properties = self.properties.clone();
416 let no_overflow = match range.end_bound() {
417 Bound::Unbounded => true,
418 Bound::Included(stp) => *stp < properties.len(),
419 Bound::Excluded(stp) => *stp <= properties.len(),
420 };
421 let no_reversed = match range.start_bound() {
422 Bound::Unbounded => true,
423 Bound::Included(st) => match range.end_bound() {
424 Bound::Unbounded => true,
425 Bound::Included(stp) => *st <= *stp,
426 Bound::Excluded(stp) => *st < *stp,
427 },
428 Bound::Excluded(st) => match range.end_bound() {
429 Bound::Unbounded => true,
430 Bound::Included(stp) => *st < *stp,
431 Bound::Excluded(stp) => st + 1 < *stp,
432 },
433 };
434 if no_overflow && no_reversed {
435 properties.splice(range, p);
436 Some(Self::new(selector, properties, media))
437 } else {
438 None
439 }
440 }
441
442 pub fn get_media_query_string_list(&self) -> Vec<String> {
444 let mut list = vec![];
445 if let Some(x) = &self.media {
446 x.to_media_query_string_list(&mut list);
447 }
448 list
449 }
450
451 pub fn get_selector_string(&self) -> String {
453 format!("{}", self.selector)
454 }
455
456 pub fn properties(&self) -> impl Iterator<Item = &PropertyMeta> {
458 self.properties.iter()
459 }
460
461 pub(crate) fn match_query<L: LengthNum, T: StyleNode>(
462 &self,
463 query: &[T],
464 media_query_status: &MediaQueryStatus<L>,
465 sheet_style_scope: Option<NonZeroUsize>,
466 ) -> Option<u16> {
467 match &self.media {
468 Some(media) => {
469 if !media.is_valid(media_query_status) {
470 return None;
471 }
472 }
473 None => {}
474 }
475 self.selector.match_query(query, sheet_style_scope)
476 }
477}