css_inline/
resolver.rs

1use crate::{InlineError, Result};
2use std::io::ErrorKind;
3
4/// Blocking way of resolving stylesheets from various sources.
5pub trait StylesheetResolver: Send + Sync {
6    /// Retrieve a stylesheet from a network or local filesystem location.
7    ///
8    /// # Errors
9    ///
10    /// Any network or filesystem related error, or an error during response parsing.
11    fn retrieve(&self, location: &str) -> Result<String> {
12        if location.starts_with("https") | location.starts_with("http") {
13            #[cfg(feature = "http")]
14            {
15                self.retrieve_from_url(location)
16            }
17
18            #[cfg(not(feature = "http"))]
19            {
20                Err(std::io::Error::new(
21                    ErrorKind::Unsupported,
22                    "Loading external URLs requires the `http` feature",
23                )
24                .into())
25            }
26        } else {
27            #[cfg(feature = "file")]
28            {
29                self.retrieve_from_path(location)
30            }
31            #[cfg(not(feature = "file"))]
32            {
33                Err(std::io::Error::new(
34                    ErrorKind::Unsupported,
35                    "Loading local files requires the `file` feature",
36                )
37                .into())
38            }
39        }
40    }
41    /// Retrieve a stylesheet from a network location.
42    ///
43    /// # Errors
44    ///
45    /// Any network-related error, or an error during response parsing.
46    fn retrieve_from_url(&self, url: &str) -> Result<String> {
47        Err(self.unsupported(&format!("Loading external URLs is not supported: {url}")))
48    }
49    /// Retrieve a stylesheet from the local filesystem.
50    ///
51    /// # Errors
52    ///
53    /// Any filesystem-related error.
54    fn retrieve_from_path(&self, path: &str) -> Result<String> {
55        let path = path.trim_start_matches("file://");
56        std::fs::read_to_string(path).map_err(|error| match error.kind() {
57            ErrorKind::NotFound => InlineError::MissingStyleSheet {
58                path: path.to_string(),
59            },
60            #[cfg(target_family = "wasm")]
61            ErrorKind::Unsupported => self.unsupported(&format!(
62                "Loading local files is not supported on WASM: {path}"
63            )),
64            _ => InlineError::IO(error),
65        })
66    }
67    /// Return the "Unsupported" kind of error.
68    fn unsupported(&self, reason: &str) -> InlineError {
69        std::io::Error::new(ErrorKind::Unsupported, reason).into()
70    }
71}
72
73/// Default stylesheet resolver.
74#[derive(Debug, Default)]
75pub struct DefaultStylesheetResolver;
76
77impl StylesheetResolver for DefaultStylesheetResolver {
78    #[cfg(feature = "http")]
79    fn retrieve_from_url(&self, url: &str) -> Result<String> {
80        let into_error = |error| InlineError::Network {
81            error,
82            location: url.to_string(),
83        };
84        reqwest::blocking::get(url)
85            .map_err(into_error)?
86            .text()
87            .map_err(into_error)
88    }
89}