lightningcss/rules/
layer.rs

1//! The `@layer` rule.
2
3use super::{CssRuleList, Location, MinifyContext};
4use crate::error::{MinifyError, ParserError, PrinterError};
5use crate::parser::DefaultAtRule;
6use crate::printer::Printer;
7use crate::traits::{Parse, ToCss};
8use crate::values::string::CowArcStr;
9#[cfg(feature = "visitor")]
10use crate::visitor::Visit;
11use cssparser::*;
12use smallvec::SmallVec;
13
14/// A [`<layer-name>`](https://drafts.csswg.org/css-cascade-5/#typedef-layer-name) within
15/// a `@layer` or `@import` rule.
16///
17/// Nested layers are represented using a list of identifiers. In CSS syntax, these are dot-separated.
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
21#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
22pub struct LayerName<'i>(#[cfg_attr(feature = "serde", serde(borrow))] pub SmallVec<[CowArcStr<'i>; 1]>);
23
24macro_rules! expect_non_whitespace {
25  ($parser: ident, $($branches: tt)+) => {{
26    let start_location = $parser.current_source_location();
27    match *$parser.next_including_whitespace()? {
28      $($branches)+
29      ref token => {
30        return Err(start_location.new_basic_unexpected_token_error(token.clone()))
31      }
32    }
33  }}
34}
35
36impl<'i> Parse<'i> for LayerName<'i> {
37  fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
38    let mut parts = SmallVec::new();
39    let ident = input.expect_ident()?;
40    parts.push(ident.into());
41
42    loop {
43      let name = input.try_parse(|input| {
44        expect_non_whitespace! {input,
45          Token::Delim('.') => Ok(()),
46        }?;
47
48        expect_non_whitespace! {input,
49          Token::Ident(ref id) => Ok(id.into()),
50        }
51      });
52
53      match name {
54        Ok(name) => parts.push(name),
55        Err(_) => break,
56      }
57    }
58
59    Ok(LayerName(parts))
60  }
61}
62
63impl<'i> ToCss for LayerName<'i> {
64  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
65  where
66    W: std::fmt::Write,
67  {
68    let mut first = true;
69    for name in &self.0 {
70      if first {
71        first = false;
72      } else {
73        dest.write_char('.')?;
74      }
75
76      serialize_identifier(name, dest)?;
77    }
78
79    Ok(())
80  }
81}
82
83/// A [@layer statement](https://drafts.csswg.org/css-cascade-5/#layer-empty) rule.
84///
85/// See also [LayerBlockRule](LayerBlockRule).
86#[derive(Debug, Clone, PartialEq)]
87#[cfg_attr(feature = "visitor", derive(Visit))]
88#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
91pub struct LayerStatementRule<'i> {
92  /// The layer names to declare.
93  #[cfg_attr(feature = "serde", serde(borrow))]
94  #[cfg_attr(feature = "visitor", skip_visit)]
95  pub names: Vec<LayerName<'i>>,
96  /// The location of the rule in the source file.
97  #[cfg_attr(feature = "visitor", skip_visit)]
98  pub loc: Location,
99}
100
101impl<'i> ToCss for LayerStatementRule<'i> {
102  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
103  where
104    W: std::fmt::Write,
105  {
106    #[cfg(feature = "sourcemap")]
107    dest.add_mapping(self.loc);
108    dest.write_str("@layer ")?;
109    self.names.to_css(dest)?;
110    dest.write_char(';')
111  }
112}
113
114/// A [@layer block](https://drafts.csswg.org/css-cascade-5/#layer-block) rule.
115#[derive(Debug, Clone, PartialEq)]
116#[cfg_attr(feature = "visitor", derive(Visit))]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
119#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
120pub struct LayerBlockRule<'i, R = DefaultAtRule> {
121  /// The name of the layer to declare, or `None` to declare an anonymous layer.
122  #[cfg_attr(feature = "serde", serde(borrow))]
123  #[cfg_attr(feature = "visitor", skip_visit)]
124  pub name: Option<LayerName<'i>>,
125  /// The rules within the `@layer` rule.
126  pub rules: CssRuleList<'i, R>,
127  /// The location of the rule in the source file.
128  #[cfg_attr(feature = "visitor", skip_visit)]
129  pub loc: Location,
130}
131
132impl<'i, T: Clone> LayerBlockRule<'i, T> {
133  pub(crate) fn minify(
134    &mut self,
135    context: &mut MinifyContext<'_, 'i>,
136    parent_is_unused: bool,
137  ) -> Result<bool, MinifyError> {
138    self.rules.minify(context, parent_is_unused)?;
139
140    Ok(self.rules.0.is_empty())
141  }
142}
143
144impl<'a, 'i, T: ToCss> ToCss for LayerBlockRule<'i, T> {
145  fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
146  where
147    W: std::fmt::Write,
148  {
149    #[cfg(feature = "sourcemap")]
150    dest.add_mapping(self.loc);
151    dest.write_str("@layer")?;
152    if let Some(name) = &self.name {
153      dest.write_char(' ')?;
154      name.to_css(dest)?;
155    }
156
157    dest.whitespace()?;
158    dest.write_char('{')?;
159    dest.indent();
160    dest.newline()?;
161    self.rules.to_css(dest)?;
162    dest.dedent();
163    dest.newline()?;
164    dest.write_char('}')
165  }
166}