lightningcss/
visitor.rs

1//! Visitors for traversing the values in a StyleSheet.
2//!
3//! The [Visitor](Visitor) trait includes methods for visiting and transforming rules, properties, and values within a StyleSheet.
4//! Each value implements the [Visit](Visit) trait, which knows how to visit the value itself, as well as its children.
5//! A Visitor is configured to only visit specific types of values using [VisitTypes](VisitTypes) flags. This enables
6//! entire branches to be skipped when a type does not contain any relevant values.
7//!
8//! # Example
9//!
10//! This example transforms a stylesheet, adding a prefix to all URLs, and converting pixels to rems.
11//!
12//! ```
13//! use std::convert::Infallible;
14//! use lightningcss::{
15//!   stylesheet::{StyleSheet, ParserOptions, PrinterOptions},
16//!   visitor::{Visitor, Visit, VisitTypes},
17//!   visit_types,
18//!   values::length::LengthValue,
19//!   values::url::Url
20//! };
21//!
22//! let mut stylesheet = StyleSheet::parse(
23//!   r#"
24//!     .foo {
25//!       background: url(bg.png);
26//!       width: 32px;
27//!     }
28//!   "#,
29//!   ParserOptions::default()
30//! ).unwrap();
31//!
32//! struct MyVisitor;
33//! impl<'i> Visitor<'i> for MyVisitor {
34//!   type Error = Infallible;
35//!
36//!   fn visit_types(&self) -> VisitTypes {
37//!     visit_types!(URLS | LENGTHS)
38//!   }
39//!
40//!   fn visit_url(&mut self, url: &mut Url<'i>) -> Result<(), Self::Error> {
41//!     url.url = format!("https://mywebsite.com/{}", url.url).into();
42//!     Ok(())
43//!   }
44//!
45//!   fn visit_length(&mut self, length: &mut LengthValue) -> Result<(), Self::Error> {
46//!     match length {
47//!       LengthValue::Px(px) => *length = LengthValue::Rem(*px / 16.0),
48//!       _ => {}
49//!     }
50//!
51//!     Ok(())
52//!   }
53//! }
54//!
55//! stylesheet.visit(&mut MyVisitor).unwrap();
56//!
57//! let res = stylesheet.to_css(PrinterOptions { minify: true, ..Default::default() }).unwrap();
58//! assert_eq!(res.code, ".foo{background:url(https://mywebsite.com/bg.png);width:2rem}");
59//! ```
60
61use crate::{
62  declaration::DeclarationBlock,
63  media_query::{MediaFeature, MediaFeatureValue, MediaList, MediaQuery},
64  parser::DefaultAtRule,
65  properties::{
66    custom::{EnvironmentVariable, Function, TokenList, TokenOrValue, Variable},
67    Property,
68  },
69  rules::{supports::SupportsCondition, CssRule, CssRuleList},
70  selector::{Selector, SelectorList},
71  stylesheet::StyleSheet,
72  values::{
73    angle::Angle,
74    color::CssColor,
75    ident::{CustomIdent, DashedIdent},
76    image::Image,
77    length::LengthValue,
78    ratio::Ratio,
79    resolution::Resolution,
80    time::Time,
81    url::Url,
82  },
83};
84use bitflags::bitflags;
85use indexmap::IndexMap;
86use smallvec::SmallVec;
87
88pub(crate) use lightningcss_derive::Visit;
89
90bitflags! {
91  /// Describes what a [Visitor](Visitor) will visit when traversing a StyleSheet.
92  ///
93  /// Flags may be combined to visit multiple types. The [visit_types](visit_types) macro allows
94  /// combining flags in a `const` expression.
95  #[derive(PartialEq, Eq, Clone, Copy)]
96  pub struct VisitTypes: u32 {
97    /// Visit rules.
98    const RULES = 1 << 0;
99    /// Visit properties;
100    const PROPERTIES = 1 << 1;
101    /// Visit urls.
102    const URLS = 1 << 2;
103    /// Visit colors.
104    const COLORS = 1 << 3;
105    /// Visit images.
106    const IMAGES = 1 << 4;
107    /// Visit lengths.
108    const LENGTHS = 1 << 5;
109    /// Visit angles.
110    const ANGLES = 1 << 6;
111    /// Visit ratios.
112    const RATIOS = 1 << 7;
113    /// Visit resolutions.
114    const RESOLUTIONS = 1 << 8;
115    /// Visit times.
116    const TIMES = 1 << 9;
117    /// Visit custom identifiers.
118    const CUSTOM_IDENTS = 1 << 10;
119    /// Visit dashed identifiers.
120    const DASHED_IDENTS = 1 << 11;
121    /// Visit variables.
122    const VARIABLES = 1 << 12;
123    /// Visit environment variables.
124    const ENVIRONMENT_VARIABLES = 1 << 13;
125    /// Visit media queries.
126    const MEDIA_QUERIES = 1 << 14;
127    /// Visit supports conditions.
128    const SUPPORTS_CONDITIONS = 1 << 15;
129    /// Visit selectors.
130    const SELECTORS = 1 << 16;
131    /// Visit custom functions.
132    const FUNCTIONS = 1 << 17;
133    /// Visit a token.
134    const TOKENS = 1 << 18;
135  }
136}
137
138/// Constructs a constant [VisitTypes](VisitTypes) from flags.
139#[macro_export]
140macro_rules! visit_types {
141  ($( $flag: ident )|+) => {
142    $crate::visitor::VisitTypes::from_bits_truncate(0 $(| $crate::visitor::VisitTypes::$flag.bits())+)
143  }
144}
145
146/// A trait for visiting or transforming rules, properties, and values in a StyleSheet.
147pub trait Visitor<'i, T: Visit<'i, T, Self> = DefaultAtRule> {
148  /// The `Err` value for `Result`s returned by `visit_*` methods.
149  type Error;
150
151  /// Returns the types of values that this visitor should visit. By default, it returns
152  /// `Self::TYPES`, but this can be overridden to change the value at runtime.
153  fn visit_types(&self) -> VisitTypes;
154
155  /// Visits a stylesheet.
156  #[inline]
157  fn visit_stylesheet<'o>(&mut self, stylesheet: &mut StyleSheet<'i, 'o, T>) -> Result<(), Self::Error> {
158    stylesheet.visit_children(self)
159  }
160
161  /// Visits a rule list.
162  #[inline]
163  fn visit_rule_list(&mut self, rules: &mut CssRuleList<'i, T>) -> Result<(), Self::Error> {
164    rules.visit_children(self)
165  }
166
167  /// Visits a rule.
168  #[inline]
169  fn visit_rule(&mut self, rule: &mut CssRule<'i, T>) -> Result<(), Self::Error> {
170    rule.visit_children(self)
171  }
172
173  /// Visits a declaration block.
174  #[inline]
175  fn visit_declaration_block(&mut self, decls: &mut DeclarationBlock<'i>) -> Result<(), Self::Error> {
176    decls.visit_children(self)
177  }
178
179  /// Visits a property.
180  #[inline]
181  fn visit_property(&mut self, property: &mut Property<'i>) -> Result<(), Self::Error> {
182    property.visit_children(self)
183  }
184
185  /// Visits a url.
186  fn visit_url(&mut self, _url: &mut Url<'i>) -> Result<(), Self::Error> {
187    Ok(())
188  }
189
190  /// Visits a color.
191  #[allow(unused_variables)]
192  fn visit_color(&mut self, color: &mut CssColor) -> Result<(), Self::Error> {
193    Ok(())
194  }
195
196  /// Visits an image.
197  #[inline]
198  fn visit_image(&mut self, image: &mut Image<'i>) -> Result<(), Self::Error> {
199    image.visit_children(self)
200  }
201
202  /// Visits a length.
203  #[allow(unused_variables)]
204  fn visit_length(&mut self, length: &mut LengthValue) -> Result<(), Self::Error> {
205    Ok(())
206  }
207
208  /// Visits an angle.
209  #[allow(unused_variables)]
210  fn visit_angle(&mut self, angle: &mut Angle) -> Result<(), Self::Error> {
211    Ok(())
212  }
213
214  /// Visits a ratio.
215  #[allow(unused_variables)]
216  fn visit_ratio(&mut self, ratio: &mut Ratio) -> Result<(), Self::Error> {
217    Ok(())
218  }
219
220  /// Visits a resolution.
221  #[allow(unused_variables)]
222  fn visit_resolution(&mut self, resolution: &mut Resolution) -> Result<(), Self::Error> {
223    Ok(())
224  }
225
226  /// Visits a time.
227  #[allow(unused_variables)]
228  fn visit_time(&mut self, time: &mut Time) -> Result<(), Self::Error> {
229    Ok(())
230  }
231
232  /// Visits a custom ident.
233  #[allow(unused_variables)]
234  fn visit_custom_ident(&mut self, ident: &mut CustomIdent) -> Result<(), Self::Error> {
235    Ok(())
236  }
237
238  /// Visits a dashed ident.
239  #[allow(unused_variables)]
240  fn visit_dashed_ident(&mut self, ident: &mut DashedIdent) -> Result<(), Self::Error> {
241    Ok(())
242  }
243
244  /// Visits a variable reference.
245  #[inline]
246  fn visit_variable(&mut self, var: &mut Variable<'i>) -> Result<(), Self::Error> {
247    var.visit_children(self)
248  }
249
250  /// Visits an environment variable reference.
251  #[inline]
252  fn visit_environment_variable(&mut self, env: &mut EnvironmentVariable<'i>) -> Result<(), Self::Error> {
253    env.visit_children(self)
254  }
255
256  /// Visits a media query list.
257  #[inline]
258  fn visit_media_list(&mut self, media: &mut MediaList<'i>) -> Result<(), Self::Error> {
259    media.visit_children(self)
260  }
261
262  /// Visits a media query.
263  #[inline]
264  fn visit_media_query(&mut self, query: &mut MediaQuery<'i>) -> Result<(), Self::Error> {
265    query.visit_children(self)
266  }
267
268  /// Visits a media feature.
269  #[inline]
270  fn visit_media_feature(&mut self, feature: &mut MediaFeature<'i>) -> Result<(), Self::Error> {
271    feature.visit_children(self)
272  }
273
274  /// Visits a media feature value.
275  #[inline]
276  fn visit_media_feature_value(&mut self, value: &mut MediaFeatureValue<'i>) -> Result<(), Self::Error> {
277    value.visit_children(self)
278  }
279
280  /// Visits a supports condition.
281  #[inline]
282  fn visit_supports_condition(&mut self, condition: &mut SupportsCondition<'i>) -> Result<(), Self::Error> {
283    condition.visit_children(self)
284  }
285
286  /// Visits a selector list.
287  #[inline]
288  fn visit_selector_list(&mut self, selectors: &mut SelectorList<'i>) -> Result<(), Self::Error> {
289    selectors.visit_children(self)
290  }
291
292  /// Visits a selector.
293  #[allow(unused_variables)]
294  fn visit_selector(&mut self, selector: &mut Selector<'i>) -> Result<(), Self::Error> {
295    Ok(())
296  }
297
298  /// Visits a custom function.
299  #[inline]
300  fn visit_function(&mut self, function: &mut Function<'i>) -> Result<(), Self::Error> {
301    function.visit_children(self)
302  }
303
304  /// Visits a token list.
305  #[inline]
306  fn visit_token_list(&mut self, tokens: &mut TokenList<'i>) -> Result<(), Self::Error> {
307    tokens.visit_children(self)
308  }
309
310  /// Visits a token or value in an unparsed property.
311  #[inline]
312  fn visit_token(&mut self, token: &mut TokenOrValue<'i>) -> Result<(), Self::Error> {
313    token.visit_children(self)
314  }
315}
316
317/// A trait for values that can be visited by a [Visitor](Visitor).
318pub trait Visit<'i, T: Visit<'i, T, V>, V: ?Sized + Visitor<'i, T>> {
319  /// The types of values contained within this value and its children.
320  /// This is used to skip branches that don't have any values requested
321  /// by the Visitor.
322  const CHILD_TYPES: VisitTypes;
323
324  /// Visits the value by calling an appropriate method on the Visitor.
325  /// If no corresponding visitor method exists, then the children are visited.
326  #[inline]
327  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
328    self.visit_children(visitor)
329  }
330
331  /// Visit the children of this value.
332  fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error>;
333}
334
335impl<'i, T: Visit<'i, T, V>, V: ?Sized + Visitor<'i, T>, U: Visit<'i, T, V>> Visit<'i, T, V> for Option<U> {
336  const CHILD_TYPES: VisitTypes = U::CHILD_TYPES;
337
338  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
339    if let Some(v) = self {
340      v.visit(visitor)
341    } else {
342      Ok(())
343    }
344  }
345
346  fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error> {
347    if let Some(v) = self {
348      v.visit_children(visitor)
349    } else {
350      Ok(())
351    }
352  }
353}
354
355impl<'i, T: Visit<'i, T, V>, V: ?Sized + Visitor<'i, T>, U: Visit<'i, T, V>> Visit<'i, T, V> for Box<U> {
356  const CHILD_TYPES: VisitTypes = U::CHILD_TYPES;
357
358  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
359    self.as_mut().visit(visitor)
360  }
361
362  fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error> {
363    self.as_mut().visit_children(visitor)
364  }
365}
366
367impl<'i, T: Visit<'i, T, V>, V: ?Sized + Visitor<'i, T>, U: Visit<'i, T, V>> Visit<'i, T, V> for Vec<U> {
368  const CHILD_TYPES: VisitTypes = U::CHILD_TYPES;
369
370  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
371    self.iter_mut().try_for_each(|v| v.visit(visitor))
372  }
373
374  fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error> {
375    self.iter_mut().try_for_each(|v| v.visit_children(visitor))
376  }
377}
378
379impl<'i, A: smallvec::Array<Item = U>, U: Visit<'i, T, V>, T: Visit<'i, T, V>, V: ?Sized + Visitor<'i, T>>
380  Visit<'i, T, V> for SmallVec<A>
381{
382  const CHILD_TYPES: VisitTypes = U::CHILD_TYPES;
383
384  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
385    self.iter_mut().try_for_each(|v| v.visit(visitor))
386  }
387
388  fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error> {
389    self.iter_mut().try_for_each(|v| v.visit_children(visitor))
390  }
391}
392
393impl<'i, T, V, U, W> Visit<'i, T, V> for IndexMap<U, W>
394where
395  T: Visit<'i, T, V>,
396  V: ?Sized + Visitor<'i, T>,
397  W: Visit<'i, T, V>,
398{
399  const CHILD_TYPES: VisitTypes = W::CHILD_TYPES;
400
401  fn visit(&mut self, visitor: &mut V) -> Result<(), V::Error> {
402    self.iter_mut().try_for_each(|(_k, v)| v.visit(visitor))
403  }
404
405  fn visit_children(&mut self, visitor: &mut V) -> Result<(), V::Error> {
406    self.iter_mut().try_for_each(|(_k, v)| v.visit_children(visitor))
407  }
408}
409
410macro_rules! impl_visit {
411  ($t: ty) => {
412    impl<'i, V: ?Sized + Visitor<'i, T>, T: Visit<'i, T, V>> Visit<'i, T, V> for $t {
413      const CHILD_TYPES: VisitTypes = VisitTypes::empty();
414
415      fn visit_children(&mut self, _: &mut V) -> Result<(), V::Error> {
416        Ok(())
417      }
418    }
419  };
420}
421
422impl_visit!(u8);
423impl_visit!(u16);
424impl_visit!(u32);
425impl_visit!(i32);
426impl_visit!(f32);
427impl_visit!(bool);
428impl_visit!(char);
429impl_visit!(str);
430impl_visit!(String);
431impl_visit!((f32, f32));