parcel_css/rules/
layer.rs

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