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