Skip to content

Commit 57253bd

Browse files
committed
feat: Merging styles
1 parent 3a5b6c7 commit 57253bd

File tree

3 files changed

+84
-13
lines changed

3 files changed

+84
-13
lines changed

src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Errors that may happen during inlining.
2+
use cssparser::ParseError;
23
use std::io;
34

45
/// Inlining error
@@ -15,3 +16,9 @@ impl From<io::Error> for InlineError {
1516
InlineError::IO(error)
1617
}
1718
}
19+
20+
impl From<(ParseError<'_, ()>, &str)> for InlineError {
21+
fn from(_: (ParseError<'_, ()>, &str)) -> Self {
22+
InlineError::ParseError
23+
}
24+
}

src/lib.rs

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ pub mod error;
105105
mod parse;
106106

107107
pub use error::InlineError;
108+
use std::collections::HashMap;
108109

109110
#[derive(Debug)]
110111
struct Rule {
@@ -151,14 +152,16 @@ pub fn inline(html: &str) -> Result<String, InlineError> {
151152
.filter_map(|node| node.into_element_ref())
152153
.filter(|element| rule.selectors.matches(element));
153154
for matching_element in matching_elements {
154-
let style = rule
155-
.declarations
156-
.iter()
157-
.map(|&(ref key, ref value)| format!("{}:{};", key, value));
158-
matching_element
159-
.attributes
160-
.borrow_mut()
161-
.insert("style", style.collect());
155+
let mut attributes = matching_element.attributes.borrow_mut();
156+
let style = if let Some(existing_style) = attributes.get("style") {
157+
merge_styles(existing_style, &rule.declarations)?
158+
} else {
159+
rule.declarations
160+
.iter()
161+
.map(|&(ref key, ref value)| format!("{}:{};", key, value))
162+
.collect()
163+
};
164+
attributes.insert("style", style);
162165
}
163166
}
164167

@@ -173,6 +176,28 @@ pub fn inline(html: &str) -> Result<String, InlineError> {
173176
Ok(String::from_utf8_lossy(&out).to_string())
174177
}
175178

179+
fn merge_styles(existing_style: &str, new_styles: &[Declaration]) -> Result<String, InlineError> {
180+
// Parse existing declarations in "style" attribute
181+
let mut input = cssparser::ParserInput::new(existing_style);
182+
let mut parser = cssparser::Parser::new(&mut input);
183+
let declarations =
184+
cssparser::DeclarationListParser::new(&mut parser, parse::CSSDeclarationListParser);
185+
// Merge existing with the new ones
186+
let mut styles: HashMap<String, String> = HashMap::new();
187+
for declaration in declarations.into_iter() {
188+
let (property, value) = declaration?;
189+
styles.insert(property.to_string(), value.to_string());
190+
}
191+
for (property, value) in new_styles.iter() {
192+
styles.insert(property.to_string(), value.to_string());
193+
}
194+
// Create a new declarations list
195+
Ok(styles
196+
.iter()
197+
.map(|(key, value)| format!("{}:{};", key, value))
198+
.collect::<String>())
199+
}
200+
176201
#[cfg(test)]
177202
mod tests {
178203
use crate::*;
@@ -190,9 +215,9 @@ p.footer { font-size: 1px}
190215
</style>
191216
</head>
192217
<body>
193-
<h1>Hi!</h1>
218+
<h1>Big Text</h1>
194219
<p><strong>Yes!</strong></p>
195-
<p class="footer">Feetnuts</p>
220+
<p class="footer">Foot notes</p>
196221
</body>
197222
</html>"#;
198223

@@ -213,12 +238,51 @@ p.footer { font-size: 1px}
213238
</style>
214239
</head>
215240
<body>
216-
<h1 style="color:red;">Hi!</h1>
241+
<h1 style="color:red;">Big Text</h1>
217242
<p style="font-size:2px ;"><strong style="text-decoration:none
218243
;">Yes!</strong></p>
219-
<p class="footer" style="font-size: 1px;">Feetnuts</p>
244+
<p class="footer" style="font-size: 1px;">Foot notes</p>
220245
221246
</body></html>"#
222247
)
223248
}
249+
250+
#[test]
251+
fn test_merge_styles() {
252+
let html = r#"<html>
253+
<head>
254+
<title>Test</title>
255+
<style>
256+
h1 { color:red; }
257+
</style>
258+
</head>
259+
<body>
260+
<h1 style="font-size: 1px">Big Text</h1>
261+
</body>
262+
</html>"#;
263+
let inlined = inline(html).expect("Should be valid");
264+
let valid = (inlined
265+
== r#"<html><head>
266+
<title>Test</title>
267+
<style>
268+
h1 { color:red; }
269+
</style>
270+
</head>
271+
<body>
272+
<h1 style="color:red;font-size: 1px;">Big Text</h1>
273+
274+
</body></html>"#)
275+
|| (inlined
276+
== r#"<html><head>
277+
<title>Test</title>
278+
<style>
279+
h1 { color:red; }
280+
</style>
281+
</head>
282+
<body>
283+
<h1 style="font-size: 1px;color:red;">Big Text</h1>
284+
285+
</body></html>"#);
286+
assert!(valid, inlined)
287+
}
224288
}

src/parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pub struct CSSRuleListParser;
2-
struct CSSDeclarationListParser;
2+
pub(crate) struct CSSDeclarationListParser;
33

44
pub type Declaration = (String, String);
55
pub type QualifiedRule = (String, Vec<Declaration>);

0 commit comments

Comments
 (0)