lightningcss/
traits.rs

1//! Traits for parsing and serializing CSS.
2
3use crate::context::PropertyHandlerContext;
4use crate::declaration::{DeclarationBlock, DeclarationList};
5use crate::error::{ParserError, PrinterError};
6use crate::printer::Printer;
7use crate::properties::{Property, PropertyId};
8use crate::stylesheet::{ParserOptions, PrinterOptions};
9use crate::targets::{Browsers, Targets};
10use crate::vendor_prefix::VendorPrefix;
11use cssparser::*;
12
13#[cfg(feature = "into_owned")]
14pub use static_self::IntoOwned;
15
16/// Trait for things that can be parsed from CSS syntax.
17pub trait Parse<'i>: Sized {
18  /// Parse a value of this type using an existing parser.
19  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>>;
20
21  /// Parse a value from a string.
22  ///
23  /// (This is a convenience wrapper for `parse` and probably should not be overridden.)
24  fn parse_string(input: &'i str) -> Result<Self, ParseError<'i, ParserError<'i>>> {
25    let mut input = ParserInput::new(input);
26    let mut parser = Parser::new(&mut input);
27    let result = Self::parse(&mut parser)?;
28    parser.expect_exhausted()?;
29    Ok(result)
30  }
31}
32
33pub(crate) use lightningcss_derive::Parse;
34
35impl<'i, T: Parse<'i>> Parse<'i> for Option<T> {
36  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
37    Ok(input.try_parse(T::parse).ok())
38  }
39}
40
41impl<'i, T: Parse<'i>> Parse<'i> for Box<T> {
42  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
43    Ok(Box::new(T::parse(input)?))
44  }
45}
46
47/// Trait for things that can be parsed from CSS syntax and require ParserOptions.
48pub trait ParseWithOptions<'i>: Sized {
49  /// Parse a value of this type with the given options.
50  fn parse_with_options<'t>(
51    input: &mut Parser<'i, 't>,
52    options: &ParserOptions<'_, 'i>,
53  ) -> Result<Self, ParseError<'i, ParserError<'i>>>;
54
55  /// Parse a value from a string with the given options.
56  fn parse_string_with_options(
57    input: &'i str,
58    options: ParserOptions<'_, 'i>,
59  ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
60    let mut input = ParserInput::new(input);
61    let mut parser = Parser::new(&mut input);
62    Self::parse_with_options(&mut parser, &options)
63  }
64}
65
66impl<'i, T: Parse<'i>> ParseWithOptions<'i> for T {
67  #[inline]
68  fn parse_with_options<'t>(
69    input: &mut Parser<'i, 't>,
70    _options: &ParserOptions,
71  ) -> Result<Self, ParseError<'i, ParserError<'i>>> {
72    T::parse(input)
73  }
74}
75
76/// Trait for things the can serialize themselves in CSS syntax.
77pub trait ToCss {
78  /// Serialize `self` in CSS syntax, writing to `dest`.
79  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
80  where
81    W: std::fmt::Write;
82
83  /// Serialize `self` in CSS syntax and return a string.
84  ///
85  /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
86  #[inline]
87  fn to_css_string(&self, options: PrinterOptions) -> Result<String, PrinterError> {
88    let mut s = String::new();
89    let mut printer = Printer::new(&mut s, options);
90    self.to_css(&mut printer)?;
91    Ok(s)
92  }
93}
94
95pub(crate) use lightningcss_derive::ToCss;
96
97impl<'a, T> ToCss for &'a T
98where
99  T: ToCss + ?Sized,
100{
101  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
102  where
103    W: std::fmt::Write,
104  {
105    (*self).to_css(dest)
106  }
107}
108
109impl<T: ToCss> ToCss for Box<T> {
110  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
111  where
112    W: std::fmt::Write,
113  {
114    (**self).to_css(dest)
115  }
116}
117
118impl<T: ToCss> ToCss for Option<T> {
119  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
120  where
121    W: std::fmt::Write,
122  {
123    if let Some(v) = self {
124      v.to_css(dest)?;
125    }
126    Ok(())
127  }
128}
129
130pub(crate) trait PropertyHandler<'i>: Sized {
131  fn handle_property(
132    &mut self,
133    property: &Property<'i>,
134    dest: &mut DeclarationList<'i>,
135    context: &mut PropertyHandlerContext<'i, '_>,
136  ) -> bool;
137  fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>);
138}
139
140pub(crate) mod private {
141  pub trait TryAdd<T> {
142    fn try_add(&self, other: &T) -> Option<T>;
143  }
144
145  pub trait AddInternal {
146    fn add(self, other: Self) -> Self;
147  }
148}
149
150pub(crate) trait FromStandard<T>: Sized {
151  fn from_standard(val: &T) -> Option<Self>;
152}
153
154pub(crate) trait FallbackValues: Sized {
155  fn get_fallbacks(&mut self, targets: Targets) -> Vec<Self>;
156}
157
158/// Trait for shorthand properties.
159pub(crate) trait Shorthand<'i>: Sized {
160  /// Returns a shorthand from the longhand properties defined in the given declaration block.
161  fn from_longhands(decls: &DeclarationBlock<'i>, vendor_prefix: VendorPrefix) -> Option<(Self, bool)>;
162
163  /// Returns a list of longhand property ids for this shorthand.
164  fn longhands(vendor_prefix: VendorPrefix) -> Vec<PropertyId<'static>>;
165
166  /// Returns a longhand property for this shorthand.
167  fn longhand(&self, property_id: &PropertyId) -> Option<Property<'i>>;
168
169  /// Updates this shorthand from a longhand property.
170  fn set_longhand(&mut self, property: &Property<'i>) -> Result<(), ()>;
171}
172
173/// A trait for values that support binary operations.
174pub trait Op {
175  /// Returns the result of the operation in the same type.
176  fn op<F: FnOnce(f32, f32) -> f32>(&self, rhs: &Self, op: F) -> Self;
177  /// Returns the result of the operation in a different type.
178  fn op_to<T, F: FnOnce(f32, f32) -> T>(&self, rhs: &Self, op: F) -> T;
179}
180
181macro_rules! impl_op {
182  ($t: ty, $trait: ident $(:: $x: ident)*, $op: ident) => {
183    impl $trait$(::$x)* for $t {
184      type Output = $t;
185
186      fn $op(self, rhs: Self) -> Self::Output {
187        self.op(&rhs, $trait$(::$x)*::$op)
188      }
189    }
190  };
191}
192
193pub(crate) use impl_op;
194use smallvec::SmallVec;
195
196/// A trait for values that potentially support a binary operation (e.g. if they have the same unit).
197pub trait TryOp: Sized {
198  /// Returns the result of the operation in the same type, if possible.
199  fn try_op<F: FnOnce(f32, f32) -> f32>(&self, rhs: &Self, op: F) -> Option<Self>;
200  /// Returns the result of the operation in a different type, if possible.
201  fn try_op_to<T, F: FnOnce(f32, f32) -> T>(&self, rhs: &Self, op: F) -> Option<T>;
202}
203
204impl<T: Op> TryOp for T {
205  fn try_op<F: FnOnce(f32, f32) -> f32>(&self, rhs: &Self, op: F) -> Option<Self> {
206    Some(self.op(rhs, op))
207  }
208
209  fn try_op_to<U, F: FnOnce(f32, f32) -> U>(&self, rhs: &Self, op: F) -> Option<U> {
210    Some(self.op_to(rhs, op))
211  }
212}
213
214/// A trait for values that can be mapped by applying a function.
215pub trait Map {
216  /// Returns the result of the operation.
217  fn map<F: FnOnce(f32) -> f32>(&self, op: F) -> Self;
218}
219
220/// A trait for values that can potentially be mapped.
221pub trait TryMap: Sized {
222  /// Returns the result of the operation, if possible.
223  fn try_map<F: FnOnce(f32) -> f32>(&self, op: F) -> Option<Self>;
224}
225
226impl<T: Map> TryMap for T {
227  fn try_map<F: FnOnce(f32) -> f32>(&self, op: F) -> Option<Self> {
228    Some(self.map(op))
229  }
230}
231
232/// A trait for values that can return a sign.
233pub trait Sign {
234  /// Returns the sign of the value.
235  fn sign(&self) -> f32;
236
237  /// Returns whether the value is positive.
238  fn is_sign_positive(&self) -> bool {
239    f32::is_sign_positive(self.sign())
240  }
241
242  /// Returns whether the value is negative.
243  fn is_sign_negative(&self) -> bool {
244    f32::is_sign_negative(self.sign())
245  }
246}
247
248/// A trait for values that can potentially return a sign.
249pub trait TrySign {
250  /// Returns the sign of the value, if possible.
251  fn try_sign(&self) -> Option<f32>;
252
253  /// Returns whether the value is positive. If not possible, returns false.
254  fn is_sign_positive(&self) -> bool {
255    self.try_sign().map_or(false, |s| f32::is_sign_positive(s))
256  }
257
258  /// Returns whether the value is negative. If not possible, returns false.
259  fn is_sign_negative(&self) -> bool {
260    self.try_sign().map_or(false, |s| f32::is_sign_negative(s))
261  }
262}
263
264impl<T: Sign> TrySign for T {
265  fn try_sign(&self) -> Option<f32> {
266    Some(self.sign())
267  }
268}
269
270/// A trait for values that can be zero.
271pub trait Zero {
272  /// Returns the zero value.
273  fn zero() -> Self;
274
275  /// Returns whether the value is zero.
276  fn is_zero(&self) -> bool;
277}
278
279/// A trait for values that can check if they are compatible with browser targets.
280pub trait IsCompatible {
281  /// Returns whether the value is compatible with all of the given browser targets.
282  fn is_compatible(&self, browsers: Browsers) -> bool;
283}
284
285impl<T: IsCompatible> IsCompatible for SmallVec<[T; 1]> {
286  fn is_compatible(&self, browsers: Browsers) -> bool {
287    self.iter().all(|v| v.is_compatible(browsers))
288  }
289}
290
291impl<T: IsCompatible> IsCompatible for Vec<T> {
292  fn is_compatible(&self, browsers: Browsers) -> bool {
293    self.iter().all(|v| v.is_compatible(browsers))
294  }
295}
296
297/// A trait to provide parsing of custom at-rules.
298///
299/// For example, there could be different implementations for top-level at-rules
300/// (`@media`, `@font-face`, …)
301/// and for page-margin rules inside `@page`.
302///
303/// Default implementations that reject all at-rules are provided,
304/// so that `impl AtRuleParser<(), ()> for ... {}` can be used
305/// for using `DeclarationListParser` to parse a declarations list with only qualified rules.
306///
307/// Note: this trait is copied from cssparser and modified to provide parser options.
308pub trait AtRuleParser<'i>: Sized {
309  /// The intermediate representation of prelude of an at-rule.
310  type Prelude;
311
312  /// The finished representation of an at-rule.
313  type AtRule;
314
315  /// The error type that is included in the ParseError value that can be returned.
316  type Error: 'i;
317
318  /// Parse the prelude of an at-rule with the given `name`.
319  ///
320  /// Return the representation of the prelude and the type of at-rule,
321  /// or `Err(())` to ignore the entire at-rule as invalid.
322  ///
323  /// The prelude is the part after the at-keyword
324  /// and before the `;` semicolon or `{ /* ... */ }` block.
325  ///
326  /// At-rule name matching should be case-insensitive in the ASCII range.
327  /// This can be done with `std::ascii::Ascii::eq_ignore_ascii_case`,
328  /// or with the `match_ignore_ascii_case!` macro.
329  ///
330  /// The given `input` is a "delimited" parser
331  /// that ends wherever the prelude should end.
332  /// (Before the next semicolon, the next `{`, or the end of the current block.)
333  fn parse_prelude<'t>(
334    &mut self,
335    name: CowRcStr<'i>,
336    input: &mut Parser<'i, 't>,
337    options: &ParserOptions<'_, 'i>,
338  ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
339    let _ = name;
340    let _ = input;
341    let _ = options;
342    Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
343  }
344
345  /// End an at-rule which doesn't have block. Return the finished
346  /// representation of the at-rule.
347  ///
348  /// The location passed in is source location of the start of the prelude.
349  /// `is_nested` indicates whether the rule is nested inside a style rule.
350  ///
351  /// This is only called when either the `;` semicolon indeed follows the prelude,
352  /// or parser is at the end of the input.
353  fn rule_without_block(
354    &mut self,
355    prelude: Self::Prelude,
356    start: &ParserState,
357    options: &ParserOptions<'_, 'i>,
358    is_nested: bool,
359  ) -> Result<Self::AtRule, ()> {
360    let _ = prelude;
361    let _ = start;
362    let _ = options;
363    let _ = is_nested;
364    Err(())
365  }
366
367  /// Parse the content of a `{ /* ... */ }` block for the body of the at-rule.
368  ///
369  /// The location passed in is source location of the start of the prelude.
370  /// `is_nested` indicates whether the rule is nested inside a style rule.
371  ///
372  /// Return the finished representation of the at-rule
373  /// as returned by `RuleListParser::next` or `DeclarationListParser::next`,
374  /// or `Err(())` to ignore the entire at-rule as invalid.
375  ///
376  /// This is only called when a block was found following the prelude.
377  fn parse_block<'t>(
378    &mut self,
379    prelude: Self::Prelude,
380    start: &ParserState,
381    input: &mut Parser<'i, 't>,
382    options: &ParserOptions<'_, 'i>,
383    is_nested: bool,
384  ) -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
385    let _ = prelude;
386    let _ = start;
387    let _ = input;
388    let _ = options;
389    let _ = is_nested;
390    Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid))
391  }
392}