Skip to content

Commit cec4315

Browse files
committed
chore: Report an error in CLI if css-inline is built without the cli feature
1 parent 68d5be2 commit cec4315

File tree

5 files changed

+237
-152
lines changed

5 files changed

+237
-152
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ jobs:
219219
- name: Install cargo-llvm-cov
220220
uses: taiki-e/install-action@cargo-llvm-cov
221221

222+
- uses: taiki-e/install-action@cargo-hack
223+
222224
- uses: actions/setup-python@v4
223225
with:
224226
python-version: 3.7
@@ -229,8 +231,12 @@ jobs:
229231
# Starts the server in background
230232
python ./css-inline/tests/server.py &
231233
232-
- name: Generate code coverage
233-
run: cargo llvm-cov --all-features --lcov --output-path lcov.info
234+
- name: Run tests
235+
run: cargo hack llvm-cov --no-report
236+
working-directory: ./css-inline
237+
238+
- name: Generate coverage reports
239+
run: cargo llvm-cov report --lcov --output-path lcov.info
234240
working-directory: ./css-inline
235241

236242
- name: Upload coverage to Codecov

css-inline/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ impl<'a> CSSInliner<'a> {
293293
}
294294
}
295295

296-
fn load_external(mut location: &str) -> Result<String> {
296+
fn load_external(location: &str) -> Result<String> {
297297
if location.starts_with("https") | location.starts_with("http") {
298298
#[cfg(feature = "http")]
299299
{
@@ -312,7 +312,7 @@ fn load_external(mut location: &str) -> Result<String> {
312312
} else {
313313
#[cfg(feature = "file")]
314314
{
315-
location = location.trim_start_matches("file://");
315+
let location = location.trim_start_matches("file://");
316316
std::fs::read_to_string(location).map_err(|error| match error.kind() {
317317
ErrorKind::NotFound => InlineError::MissingStyleSheet {
318318
path: location.to_string(),

css-inline/src/main.rs

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1-
use css_inline::{CSSInliner, InlineOptions};
2-
use rayon::prelude::*;
3-
use std::{
4-
borrow::Cow,
5-
error::Error,
6-
ffi::OsString,
7-
fmt::{Display, Write as FmtWrite},
8-
fs::{read_to_string, File},
9-
io::{self, Read, Write},
10-
path::Path,
11-
sync::atomic::{AtomicI32, Ordering},
12-
};
1+
#[cfg(not(feature = "cli"))]
2+
fn main() {
3+
eprintln!("`css-inline` CLI is only available with the `cli` feature");
4+
std::process::exit(1);
5+
}
6+
7+
#[cfg(feature = "cli")]
8+
fn main() -> Result<(), Box<dyn std::error::Error>> {
9+
use css_inline::{CSSInliner, InlineOptions};
10+
use rayon::prelude::*;
11+
use std::{
12+
borrow::Cow,
13+
ffi::OsString,
14+
fmt::{Display, Write as FmtWrite},
15+
fs::{read_to_string, File},
16+
io::{self, Read, Write},
17+
path::Path,
18+
sync::atomic::{AtomicI32, Ordering},
19+
};
1320

14-
const VERSION_MESSAGE: &[u8] = concat!("css-inline ", env!("CARGO_PKG_VERSION"), "\n").as_bytes();
15-
const HELP_MESSAGE: &[u8] = concat!(
16-
"css-inline ",
17-
env!("CARGO_PKG_VERSION"),
18-
r#"
21+
const VERSION_MESSAGE: &[u8] =
22+
concat!("css-inline ", env!("CARGO_PKG_VERSION"), "\n").as_bytes();
23+
const HELP_MESSAGE: &[u8] = concat!(
24+
"css-inline ",
25+
env!("CARGO_PKG_VERSION"),
26+
r#"
1927
Dmitry Dygalo <[email protected]>
2028
2129
css-inline inlines CSS into HTML documents.
@@ -50,37 +58,36 @@ OPTIONS:
5058
--output-filename-prefix
5159
Custom prefix for output files. Defaults to `inlined.`.
5260
"#
53-
)
54-
.as_bytes();
61+
)
62+
.as_bytes();
5563

56-
struct Args {
57-
keep_style_tags: bool,
58-
base_url: Option<String>,
59-
extra_css: Option<String>,
60-
output_filename_prefix: Option<OsString>,
61-
load_remote_stylesheets: bool,
62-
files: Vec<String>,
63-
}
64+
struct Args {
65+
keep_style_tags: bool,
66+
base_url: Option<String>,
67+
extra_css: Option<String>,
68+
output_filename_prefix: Option<OsString>,
69+
load_remote_stylesheets: bool,
70+
files: Vec<String>,
71+
}
6472

65-
fn parse_url(url: Option<String>) -> Result<Option<url::Url>, url::ParseError> {
66-
Ok(if let Some(url) = url {
67-
Some(url::Url::parse(url.as_str())?)
68-
} else {
69-
None
70-
})
71-
}
73+
fn parse_url(url: Option<String>) -> Result<Option<url::Url>, url::ParseError> {
74+
Ok(if let Some(url) = url {
75+
Some(url::Url::parse(url.as_str())?)
76+
} else {
77+
None
78+
})
79+
}
7280

73-
fn format_error(filename: Option<&str>, error: impl Display) {
74-
let mut buffer = String::with_capacity(128);
75-
if let Some(filename) = filename {
76-
writeln!(buffer, "Filename: {}", filename).expect("Failed to write to buffer");
81+
fn format_error(filename: Option<&str>, error: impl Display) {
82+
let mut buffer = String::with_capacity(128);
83+
if let Some(filename) = filename {
84+
writeln!(buffer, "Filename: {}", filename).expect("Failed to write to buffer");
85+
}
86+
buffer.push_str("Status: ERROR\n");
87+
writeln!(buffer, "Details: {}", error).expect("Failed to write to buffer");
88+
eprintln!("{}", buffer.trim());
7789
}
78-
buffer.push_str("Status: ERROR\n");
79-
writeln!(buffer, "Details: {}", error).expect("Failed to write to buffer");
80-
eprintln!("{}", buffer.trim());
81-
}
8290

83-
fn main() -> Result<(), Box<dyn Error>> {
8491
let mut args = pico_args::Arguments::from_env();
8592
let exit_code = AtomicI32::new(0);
8693
if args.contains(["-h", "--help"]) {

css-inline/tests/test_cli.rs

Lines changed: 95 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
use assert_cmd::Command;
2-
use std::fs;
3-
use test_case::test_case;
42

53
fn css_inline() -> Command {
64
Command::cargo_bin("css-inline").unwrap()
75
}
86

9-
#[test]
10-
fn success() {
11-
css_inline()
12-
.arg("tests/example.html")
13-
.arg("--output-filename-prefix=keep-style-tags.")
14-
.assert()
15-
.success()
16-
.stdout("tests/example.html: SUCCESS\n");
17-
let content = fs::read_to_string("tests/keep-style-tags.example.html").unwrap();
18-
assert_eq!(
19-
content,
20-
"<html><head>\n \
7+
#[cfg(feature = "cli")]
8+
pub mod tests {
9+
use super::css_inline;
10+
use std::fs;
11+
use test_case::test_case;
12+
13+
#[test]
14+
fn success() {
15+
css_inline()
16+
.arg("tests/example.html")
17+
.arg("--output-filename-prefix=keep-style-tags.")
18+
.assert()
19+
.success()
20+
.stdout("tests/example.html: SUCCESS\n");
21+
let content = fs::read_to_string("tests/keep-style-tags.example.html").unwrap();
22+
assert_eq!(
23+
content,
24+
"<html><head>\n \
2125
\n \
2226
\n \
2327
\n\
@@ -26,80 +30,96 @@ fn success() {
2630
<a class=\"test-class\" href=\"https://example.com\" style=\"color: #ffffff;\">Test</a>\n\
2731
<h1 style=\"text-decoration: none;\">Test</h1>\n\n\n\
2832
</body></html>"
29-
)
30-
}
33+
)
34+
}
3135

32-
#[test]
33-
fn keep_style_tags() {
34-
css_inline()
35-
.arg("tests/example.html")
36-
.arg("--keep-style-tags")
37-
.assert()
38-
.success()
39-
.stdout("tests/example.html: SUCCESS\n");
40-
let content = fs::read_to_string("tests/inlined.example.html").unwrap();
41-
assert_eq!(
42-
content,
43-
"<html><head>\n \n \
36+
#[test]
37+
fn keep_style_tags() {
38+
css_inline()
39+
.arg("tests/example.html")
40+
.arg("--keep-style-tags")
41+
.assert()
42+
.success()
43+
.stdout("tests/example.html: SUCCESS\n");
44+
let content = fs::read_to_string("tests/inlined.example.html").unwrap();
45+
assert_eq!(
46+
content,
47+
"<html><head>\n \n \
4448
<style>\n h1 {\n text-decoration: none;\n }\n </style>\n \
4549
<style>\n .test-class {\n color: #ffffff;\n }\n\n a {\n color: #17bebb;\n }\n </style>\n\
4650
</head>\n\
4751
<body>\n\
4852
<a class=\"test-class\" href=\"https://example.com\" style=\"color: #ffffff;\">Test</a>\n\
4953
<h1 style=\"text-decoration: none;\">Test</h1>\n\n\n\
5054
</body></html>"
51-
)
52-
}
55+
)
56+
}
5357

54-
#[test]
55-
fn wrong_base_url() {
56-
css_inline()
57-
.arg("--base-url=https://:::::")
58-
.write_stdin(r#"<html><head><title>Test</title><link href="external.css" rel="stylesheet" type="text/css"></head><body><h1>Hello world!</h1></body></html>"#)
59-
.assert()
60-
.failure()
61-
.stderr("Status: ERROR\nDetails: empty host\n");
62-
}
58+
#[test]
59+
fn wrong_base_url() {
60+
css_inline()
61+
.arg("--base-url=https://:::::")
62+
.write_stdin(r#"<html><head><title>Test</title><link href="external.css" rel="stylesheet" type="text/css"></head><body><h1>Hello world!</h1></body></html>"#)
63+
.assert()
64+
.failure()
65+
.stderr("Status: ERROR\nDetails: empty host\n");
66+
}
6367

64-
#[test]
65-
fn not_found() {
66-
css_inline().arg("unknown.html").assert().failure().stderr(
67-
"Filename: unknown.html\nStatus: ERROR\nDetails: No such file or directory (os error 2)\n",
68-
);
69-
}
68+
#[test]
69+
fn not_found() {
70+
css_inline().arg("unknown.html").assert().failure().stderr(
71+
"Filename: unknown.html\nStatus: ERROR\nDetails: No such file or directory (os error 2)\n",
72+
);
73+
}
7074

71-
#[test]
72-
fn invalid_css() {
73-
css_inline()
74-
.write_stdin(r#"<html><head><title>Test</title><style>h1 {background-color: blue;}</style></head><body><h1 style="@wrong { color: ---}">Hello world!</h1></body></html>"#)
75-
.assert()
76-
.failure()
77-
.stderr("Status: ERROR\nDetails: Invalid @ rule: wrong\n");
78-
}
75+
#[test]
76+
fn invalid_css() {
77+
css_inline()
78+
.write_stdin(r#"<html><head><title>Test</title><style>h1 {background-color: blue;}</style></head><body><h1 style="@wrong { color: ---}">Hello world!</h1></body></html>"#)
79+
.assert()
80+
.failure()
81+
.stderr("Status: ERROR\nDetails: Invalid @ rule: wrong\n");
82+
}
7983

80-
#[test]
81-
fn invalid_css_in_file() {
82-
css_inline()
83-
.arg("tests/invalid-example.html")
84-
.assert()
85-
.failure()
86-
.stderr(
87-
"Filename: tests/invalid-example.html\nStatus: ERROR\nDetails: Invalid @ rule: wrong\n",
88-
);
89-
}
84+
#[test]
85+
fn invalid_css_in_file() {
86+
css_inline()
87+
.arg("tests/invalid-example.html")
88+
.assert()
89+
.failure()
90+
.stderr(
91+
"Filename: tests/invalid-example.html\nStatus: ERROR\nDetails: Invalid @ rule: wrong\n",
92+
);
93+
}
9094

91-
#[test]
92-
fn stdin() {
93-
css_inline()
94-
.write_stdin(r#"<html><head><title>Test</title><style>h1 {background-color: blue;}</style></head><body><h1>Hello world!</h1></body></html>"#)
95-
.assert()
96-
.success()
97-
.stdout("<html><head><title>Test</title></head><body><h1 style=\"background-color: blue;\">Hello world!</h1></body></html>");
95+
#[test]
96+
fn stdin() {
97+
css_inline()
98+
.write_stdin(r#"<html><head><title>Test</title><style>h1 {background-color: blue;}</style></head><body><h1>Hello world!</h1></body></html>"#)
99+
.assert()
100+
.success()
101+
.stdout("<html><head><title>Test</title></head><body><h1 style=\"background-color: blue;\">Hello world!</h1></body></html>");
102+
}
103+
104+
#[test_case("--help", "css-inline inlines CSS into HTML documents.")]
105+
#[test_case("--version", "css-inline 0.9.0")]
106+
fn args(arg: &str, expected: &str) {
107+
let stdout = css_inline().arg(arg).assert().success().to_string();
108+
assert!(stdout.contains(expected), "{}", stdout);
109+
}
98110
}
99111

100-
#[test_case("--help", "css-inline inlines CSS into HTML documents.")]
101-
#[test_case("--version", "css-inline 0.9.0")]
102-
fn args(arg: &str, expected: &str) {
103-
let stdout = css_inline().arg(arg).assert().success().to_string();
104-
assert!(stdout.contains(expected), "{}", stdout);
112+
#[cfg(not(feature = "cli"))]
113+
pub mod tests {
114+
use super::css_inline;
115+
116+
#[test]
117+
fn test_no_cli_feature() {
118+
let cmd = css_inline().assert().failure();
119+
let stdout = &cmd.get_output().stderr;
120+
assert_eq!(
121+
stdout,
122+
b"`css-inline` CLI is only available with the `cli` feature\n"
123+
);
124+
}
105125
}

0 commit comments

Comments
 (0)