1use derive_more::{Deref, DerefMut, Display as DeriveDisplay, From, Into};
2use indexmap::map::IndexMap;
3use std::fmt::Display;
4use std::fmt::Formatter;
5
6#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
7pub struct Block {
8 pub selectors: Selectors,
9 pub parameters: Parameters,
10}
11
12#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
13pub struct Media {
14 pub screen: Name,
15 pub entities: CssEntities,
16}
17
18#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
19pub struct Page {
20 pub selectors: Option<Name>,
21 pub parameters: Parameters,
22}
23
24#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
25pub struct Supports {
26 pub conditions: Name,
27 pub entities: CssEntities,
28}
29
30#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
31pub struct Keyframes {
32 pub webkit_prefix: bool,
33 pub name: Name,
34 pub blocks: KeyframeBlocks,
35}
36
37#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
38pub struct KeyframeBlock {
39 pub name: Name,
40 pub parameters: Parameters,
41}
42
43#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
44pub struct FontFace {
45 pub parameters: Parameters,
46}
47
48#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
49pub struct Viewport {
50 pub parameters: Parameters,
51}
52
53#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
54pub struct MsViewport {
55 pub parameters: Parameters,
56}
57
58#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
59pub struct NamespaceAt {
60 prefix: Option<Value>,
61 url: Value,
62}
63
64#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
65pub struct ImportAt {
66 url: Value,
67 media_queries: Option<Value>,
68}
69
70#[derive(Clone, Eq, PartialEq, Debug, From, Into)]
71pub struct CharsetAt {
72 charset: Value,
73}
74
75#[derive(Clone, Eq, PartialEq, Debug, From)]
76pub enum At {
77 Namespace(NamespaceAt),
78 Import(ImportAt),
79 Charset(CharsetAt),
80}
81
82#[derive(Clone, Eq, PartialEq, Debug, From, DeriveDisplay)]
83pub enum CssEntity {
84 Block(Block),
85 Media(Media),
86 Page(Page),
87 Supports(Supports),
88 FontFace(FontFace),
89 Viewport(Viewport),
90 MsViewport(MsViewport),
91 Keyframes(Keyframes),
92 At(At),
93}
94
95#[derive(Clone, Eq, PartialEq, Default, Debug, Deref, DerefMut, From, Into)]
96pub struct Selectors(pub(crate) Vec<SelectorWithPseudoClasses>);
97
98#[derive(Clone, Eq, PartialEq, Default, Debug, Deref, DerefMut, From, Into)]
99pub struct Parameters(pub(crate) IndexMap<Name, Value>);
100
101#[derive(Clone, Eq, PartialEq, Default, Debug, Deref, DerefMut, From, Into)]
102pub struct Blocks(pub(crate) Vec<Block>);
103
104#[derive(Clone, Eq, PartialEq, Default, Debug, Deref, DerefMut, From, Into)]
105pub struct KeyframeBlocks(pub(crate) Vec<KeyframeBlock>);
106
107#[derive(Clone, Eq, PartialEq, Default, Debug, Deref, DerefMut, From, Into)]
108pub struct CssEntities(pub(crate) Vec<CssEntity>);
109
110pub type Name = String;
111pub type Value = String;
112
113pub type Id = String;
114pub type Class = String;
115pub type Tag = String;
116
117#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
118pub struct SelectorWithPseudoClasses(pub Option<Selector>, pub Vec<PseudoClass>);
119impl Display for SelectorWithPseudoClasses {
120 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
121 if let Some(s) = &self.0 {
122 write!(f, "{}", s)?
123 }
124 for pc in self.1.iter() {
125 write!(f, "{}", pc)?
126 }
127
128 Ok(())
129 }
130}
131
132#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
133pub struct PseudoClass {
134 pub name: String,
135 pub params: Option<String>,
136 pub next: Option<String>,
137 pub prefix: String,
138}
139
140impl Display for PseudoClass {
141 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
142 write!(f, "{}{}", self.prefix, self.name)?;
143 if let Some(params) = &self.params {
144 write!(f, "({})", params)?;
145 }
146 if let Some(next) = &self.next {
147 write!(f, " {}", next)?;
148 }
149
150 Ok(())
151 }
152}
153
154#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
155pub enum Selector {
156 Id(Id),
157 Class(Class),
158 Tag(Tag),
159}
160
161impl Display for Selector {
162 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
163 match self {
164 Selector::Id(id) => {
165 write!(f, "#{}", id)
166 }
167 Selector::Class(class) => {
168 write!(f, ".{}", class)
169 }
170 Selector::Tag(tag) => {
171 write!(f, "{}", tag)
172 }
173 }
174 }
175}
176
177impl Display for Selectors {
178 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
179 let mut selectors = self
180 .0
181 .iter()
182 .map(|selector| format!("{}", selector))
183 .collect::<Vec<String>>();
184 selectors.sort();
185 write!(f, "{}", selectors.join(","))
186 }
187}
188
189impl Display for Parameters {
190 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
191 let parameters = self
192 .0
193 .iter()
194 .map(|(name, value)| format!("{}:{}", name, value))
195 .collect::<Vec<String>>();
196 write!(f, "{}", parameters.join(";"))
197 }
198}
199
200impl Display for Block {
201 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
202 write!(f, "{}{{{}}}", self.selectors, self.parameters)
203 }
204}
205
206impl Display for KeyframeBlock {
207 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208 write!(f, "{}{{{}}}", self.name, self.parameters)
209 }
210}
211
212impl Display for Media {
213 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
214 write!(f, "@media {}{{{}}}", self.screen, self.entities)
215 }
216}
217
218impl Display for Page {
219 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
220 write!(f, "@page")?;
221 if let Some(selectors) = &self.selectors {
222 write!(f, " {}", selectors)?
223 }
224 write!(f, " {{{}}}", self.parameters)?;
225 Ok(())
226 }
227}
228
229impl Display for Supports {
230 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
231 write!(f, "@supports {}{{{}}}", self.conditions, self.entities)
232 }
233}
234
235impl Display for FontFace {
236 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
237 write!(f, "@font-face {{{}}}", self.parameters)
238 }
239}
240
241impl Display for Viewport {
242 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
243 write!(f, "@viewport {{{}}}", self.parameters)
244 }
245}
246
247impl Display for MsViewport {
248 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
249 write!(f, "@-ms-viewport {{{}}}", self.parameters)
250 }
251}
252
253impl Display for Keyframes {
254 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
255 if self.webkit_prefix {
256 write!(f, "@-webkit-keyframes {}{{{}}}", self.name, self.blocks)
257 } else {
258 write!(f, "@keyframes {}{{{}}}", self.name, self.blocks)
259 }
260 }
261}
262
263impl Display for Blocks {
264 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
265 write!(
266 f,
267 "{}",
268 self.0
269 .iter()
270 .map(|block| format!("{}", block))
271 .collect::<Vec<String>>()
272 .join("")
273 )
274 }
275}
276
277impl Display for KeyframeBlocks {
278 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
279 write!(
280 f,
281 "{}",
282 self.0
283 .iter()
284 .map(|block| format!("{}", block))
285 .collect::<Vec<String>>()
286 .join("")
287 )
288 }
289}
290
291impl Display for NamespaceAt {
292 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
293 write!(f, "@namespace ")?;
294 if let Some(prefix) = &self.prefix {
295 write!(f, "{} ", prefix)?;
296 }
297 write!(f, "{};", self.url)?;
298 Ok(())
299 }
300}
301
302impl Display for CharsetAt {
303 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
304 write!(f, "@charset {};", self.charset)
305 }
306}
307
308impl Display for ImportAt {
309 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
310 write!(f, "@import {};", self.url)?;
311 if let Some(media_queries) = &self.media_queries {
312 write!(f, " {}", media_queries)?
313 }
314 Ok(())
315 }
316}
317
318impl Display for At {
319 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
320 match self {
321 At::Namespace(n) => {
322 write!(f, "{}", n)
323 }
324 At::Import(i) => {
325 write!(f, "{}", i)
326 }
327 At::Charset(c) => write!(f, "{}", c),
328 }
329 }
330}
331
332impl Display for CssEntities {
333 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
334 write!(
335 f,
336 "{}",
337 self.0
338 .iter()
339 .map(|block| format!("{}", block))
340 .collect::<Vec<String>>()
341 .join("")
342 )
343 }
344}
345
346#[cfg(test)]
347mod test {
348 use crate::structure::{Block, Blocks, Selector, SelectorWithPseudoClasses};
349 use indexmap::map::IndexMap;
350
351 #[test]
352 fn write_block() {
353 let blocks: Blocks = vec![
354 Block {
355 selectors: vec![
356 SelectorWithPseudoClasses(Some(Selector::Id("some_id".into())), vec![]),
357 SelectorWithPseudoClasses(Some(Selector::Tag("input".into())), vec![]),
358 ]
359 .into(),
360 parameters: {
361 let mut tmp = IndexMap::new();
362 tmp.insert("padding".into(), "5px 3px".into());
363 tmp.insert("color".into(), "white".into());
364 tmp.into()
365 },
366 },
367 Block {
368 selectors: vec![
369 SelectorWithPseudoClasses(Some(Selector::Id("some_id_2".into())), vec![]),
370 SelectorWithPseudoClasses(Some(Selector::Class("class".into())), vec![]),
371 ]
372 .into(),
373 parameters: {
374 let mut tmp = IndexMap::new();
375 tmp.insert("padding".into(), "5px 4px".into());
376 tmp.insert("color".into(), "black".into());
377 tmp.into()
378 },
379 },
380 ]
381 .into();
382 assert_eq!(format!("{}", blocks), "#some_id,input{padding:5px 3px;color:white}#some_id_2,.class{padding:5px 4px;color:black}")
383 }
384}