rsass/css/
rule.rs

1use super::{
2    selectors::Opt, AtRule, Comment, CssString, Import, SelectorSet, Value,
3};
4use crate::output::CssBuf;
5use std::io;
6
7/// A css rule.
8///
9/// A rule binds a [`SelectorSet`] to a body of [`BodyItem`]s (mainly
10/// properties with [`Value`]s).
11#[derive(Clone, Debug)]
12pub struct Rule {
13    pub(crate) selectors: SelectorSet,
14    pub(crate) body: Vec<BodyItem>,
15}
16
17impl Rule {
18    /// Create a new Rule.
19    pub fn new(selectors: SelectorSet) -> Self {
20        Self {
21            selectors,
22            body: Vec::new(),
23        }
24    }
25    /// Add an item to the body of this rule.
26    pub fn push(&mut self, item: BodyItem) {
27        self.body.push(item);
28    }
29
30    /// Write this rule to a css output buffer.
31    pub(crate) fn write(&self, buf: &mut CssBuf) -> io::Result<()> {
32        if !self.body.is_empty() {
33            let s = self.selectors.no_placeholder();
34            if matches!(s, Opt::None) {
35                return Ok(());
36            }
37            buf.do_indent_no_nl();
38            let p = buf.len();
39            if let Opt::Some(s) = s {
40                s.write_to(buf);
41            }
42            if buf.len() == p {
43                buf.add_str("*");
44            }
45            buf.start_block();
46            for item in &self.body {
47                item.write(buf)?;
48            }
49            buf.end_block();
50        }
51        Ok(())
52    }
53}
54
55/// Something that may exist inside a rule.
56#[derive(Clone, Debug)]
57pub enum BodyItem {
58    /// An `@import` statement with a name and args.
59    Import(Import),
60    /// A property declaration with a name and a value.
61    Property(Property),
62    /// A custom property declaration with a name and a value.
63    CustomProperty(CustomProperty),
64    /// A comment
65    Comment(Comment),
66    /// Empty at-rules are allowed in a rule body.
67    ARule(AtRule),
68}
69
70impl BodyItem {
71    /// Write this item to a css output buffer.
72    pub(crate) fn write(&self, buf: &mut CssBuf) -> io::Result<()> {
73        match self {
74            Self::Comment(c) => c.write(buf),
75            Self::Import(import) => import.write(buf)?,
76            Self::Property(property) => property.write(buf),
77            Self::CustomProperty(property) => property.write(buf),
78            Self::ARule(rule) => rule.write(buf)?,
79        }
80        Ok(())
81    }
82}
83
84impl From<Comment> for BodyItem {
85    fn from(comment: Comment) -> Self {
86        Self::Comment(comment)
87    }
88}
89impl From<Import> for BodyItem {
90    fn from(import: Import) -> Self {
91        Self::Import(import)
92    }
93}
94impl From<Property> for BodyItem {
95    fn from(property: Property) -> Self {
96        Self::Property(property)
97    }
98}
99impl From<CustomProperty> for BodyItem {
100    fn from(property: CustomProperty) -> Self {
101        Self::CustomProperty(property)
102    }
103}
104
105impl TryFrom<AtRule> for BodyItem {
106    type Error = AtRule;
107
108    fn try_from(value: AtRule) -> Result<Self, Self::Error> {
109        if value.no_body() {
110            Ok(Self::ARule(value))
111        } else {
112            Err(value)
113        }
114    }
115}
116
117/// A css property; a name and [Value].
118#[derive(Clone, Debug)]
119pub struct Property {
120    name: String,
121    value: Value,
122}
123
124impl Property {
125    /// Create a new Property.
126    pub fn new(name: String, value: Value) -> Self {
127        Self { name, value }
128    }
129    pub(crate) fn write(&self, buf: &mut CssBuf) {
130        buf.do_indent_no_nl();
131        buf.add_str(&self.name);
132        buf.add_one(": ", ":");
133        buf.add_str(&self.value.to_string(buf.format()).replace('\n', " "));
134        buf.add_one(";\n", ";");
135    }
136}
137
138/// A css custom property (css variable); a name and a literal value.
139#[derive(Clone, Debug)]
140pub struct CustomProperty {
141    name: String,
142    value: CssString,
143}
144
145impl CustomProperty {
146    /// Construct a new custom property.
147    pub fn new(name: String, value: CssString) -> Self {
148        Self { name, value }
149    }
150    pub(crate) fn write(&self, buf: &mut CssBuf) {
151        buf.do_indent_no_nl();
152        buf.add_str(&self.name);
153        buf.add_str(":");
154        if !(self.value.quotes().is_none() || buf.format().is_compressed()) {
155            buf.add_str(" ");
156        }
157        buf.add_str(&self.value.to_string());
158        buf.add_one(";\n", ";");
159    }
160}