1use super::{
2 selectors::Opt, AtRule, Comment, CssString, Import, SelectorSet, Value,
3};
4use crate::output::CssBuf;
5use std::io;
6
7#[derive(Clone, Debug)]
12pub struct Rule {
13 pub(crate) selectors: SelectorSet,
14 pub(crate) body: Vec<BodyItem>,
15}
16
17impl Rule {
18 pub fn new(selectors: SelectorSet) -> Self {
20 Self {
21 selectors,
22 body: Vec::new(),
23 }
24 }
25 pub fn push(&mut self, item: BodyItem) {
27 self.body.push(item);
28 }
29
30 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#[derive(Clone, Debug)]
57pub enum BodyItem {
58 Import(Import),
60 Property(Property),
62 CustomProperty(CustomProperty),
64 Comment(Comment),
66 ARule(AtRule),
68}
69
70impl BodyItem {
71 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#[derive(Clone, Debug)]
119pub struct Property {
120 name: String,
121 value: Value,
122}
123
124impl Property {
125 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#[derive(Clone, Debug)]
140pub struct CustomProperty {
141 name: String,
142 value: CssString,
143}
144
145impl CustomProperty {
146 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}