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