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}