lightningcss/properties/
css_modules.rs1use crate::dependencies::Location;
4use crate::error::{ParserError, PrinterError};
5use crate::printer::Printer;
6use crate::traits::{Parse, ToCss};
7use crate::values::ident::{CustomIdent, CustomIdentList};
8use crate::values::string::CowArcStr;
9#[cfg(feature = "visitor")]
10use crate::visitor::Visit;
11use cssparser::*;
12use smallvec::SmallVec;
13
14#[derive(Debug, Clone, PartialEq)]
16#[cfg_attr(feature = "visitor", derive(Visit))]
17#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
20pub struct Composes<'i> {
21 #[cfg_attr(feature = "serde", serde(borrow))]
23 pub names: CustomIdentList<'i>,
24 pub from: Option<Specifier<'i>>,
26 pub loc: Location,
28}
29
30#[derive(Debug, Clone, PartialEq)]
34#[cfg_attr(feature = "visitor", derive(Visit))]
35#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
36#[cfg_attr(
37 feature = "serde",
38 derive(serde::Serialize, serde::Deserialize),
39 serde(tag = "type", content = "value", rename_all = "kebab-case")
40)]
41#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
42pub enum Specifier<'i> {
43 Global,
45 #[cfg_attr(feature = "serde", serde(borrow))]
47 File(CowArcStr<'i>),
48 SourceIndex(u32),
50}
51
52impl<'i> Parse<'i> for Composes<'i> {
53 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
54 let loc = input.current_source_location();
55 let mut names = SmallVec::new();
56 while let Ok(name) = input.try_parse(parse_one_ident) {
57 names.push(name);
58 }
59
60 if names.is_empty() {
61 return Err(input.new_custom_error(ParserError::InvalidDeclaration));
62 }
63
64 let from = if input.try_parse(|input| input.expect_ident_matching("from")).is_ok() {
65 Some(Specifier::parse(input)?)
66 } else {
67 None
68 };
69
70 Ok(Composes {
71 names,
72 from,
73 loc: loc.into(),
74 })
75 }
76}
77
78fn parse_one_ident<'i, 't>(
79 input: &mut Parser<'i, 't>,
80) -> Result<CustomIdent<'i>, ParseError<'i, ParserError<'i>>> {
81 let name = CustomIdent::parse(input)?;
82 if name.0.eq_ignore_ascii_case("from") {
83 return Err(input.new_error_for_next_token());
84 }
85
86 Ok(name)
87}
88
89impl ToCss for Composes<'_> {
90 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
91 where
92 W: std::fmt::Write,
93 {
94 let mut first = true;
95 for name in &self.names {
96 if first {
97 first = false;
98 } else {
99 dest.write_char(' ')?;
100 }
101 name.to_css(dest)?;
102 }
103
104 if let Some(from) = &self.from {
105 dest.write_str(" from ")?;
106 from.to_css(dest)?;
107 }
108
109 Ok(())
110 }
111}
112
113impl<'i> Parse<'i> for Specifier<'i> {
114 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
115 if let Ok(file) = input.try_parse(|input| input.expect_string_cloned()) {
116 Ok(Specifier::File(file.into()))
117 } else {
118 input.expect_ident_matching("global")?;
119 Ok(Specifier::Global)
120 }
121 }
122}
123
124impl<'i> ToCss for Specifier<'i> {
125 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
126 where
127 W: std::fmt::Write,
128 {
129 match self {
130 Specifier::Global => dest.write_str("global")?,
131 Specifier::File(file) => serialize_string(&file, dest)?,
132 Specifier::SourceIndex(..) => {}
133 }
134 Ok(())
135 }
136}