1use crate::{InlineError, Result};
2use std::{borrow::Cow, io::ErrorKind, path::Path};
3
4fn to_file_path(location: &str) -> Cow<'_, Path> {
9 #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
10 if let Ok(url) = url::Url::parse(location) {
11 if url.scheme() == "file" && url.host().is_none() {
12 if let Ok(path) = url.to_file_path() {
13 return Cow::Owned(path);
14 }
15 }
16 }
17 Cow::Borrowed(Path::new(location.trim_start_matches("file://")))
18}
19
20pub trait StylesheetResolver: Send + Sync {
22 fn retrieve(&self, location: &str) -> Result<String> {
28 if location.starts_with("https") || location.starts_with("http") {
29 #[cfg(feature = "http")]
30 {
31 self.retrieve_from_url(location)
32 }
33
34 #[cfg(not(feature = "http"))]
35 {
36 Err(std::io::Error::new(
37 ErrorKind::Unsupported,
38 "Loading external URLs requires the `http` feature",
39 )
40 .into())
41 }
42 } else {
43 #[cfg(feature = "file")]
44 {
45 self.retrieve_from_path(location)
46 }
47 #[cfg(not(feature = "file"))]
48 {
49 Err(std::io::Error::new(
50 ErrorKind::Unsupported,
51 "Loading local files requires the `file` feature",
52 )
53 .into())
54 }
55 }
56 }
57 fn retrieve_from_url(&self, url: &str) -> Result<String> {
63 Err(self.unsupported(&format!("Loading external URLs is not supported: {url}")))
64 }
65 fn retrieve_from_path(&self, path: &str) -> Result<String> {
71 let path = to_file_path(path);
72 std::fs::read_to_string(&path).map_err(|error| match error.kind() {
73 ErrorKind::NotFound => InlineError::MissingStyleSheet {
74 path: path.display().to_string(),
75 },
76 #[cfg(target_family = "wasm")]
77 ErrorKind::Unsupported => self.unsupported(&format!(
78 "Loading local files is not supported on WASM: {}",
79 path.display()
80 )),
81 _ => InlineError::IO(error),
82 })
83 }
84 fn unsupported(&self, reason: &str) -> InlineError {
86 std::io::Error::new(ErrorKind::Unsupported, reason).into()
87 }
88}
89
90#[derive(Debug, Default)]
92pub struct DefaultStylesheetResolver;
93
94impl StylesheetResolver for DefaultStylesheetResolver {
95 #[cfg(feature = "http")]
96 fn retrieve_from_url(&self, url: &str) -> Result<String> {
97 let into_error = |error| InlineError::Network {
98 error,
99 location: url.to_string(),
100 };
101 reqwest::blocking::get(url)
102 .map_err(into_error)?
103 .text()
104 .map_err(into_error)
105 }
106}