1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use crate::{InlineError, Result};
use std::io::ErrorKind;

/// Blocking way of resolving stylesheets from various sources.
pub trait StylesheetResolver: Send + Sync {
    /// Retrieve a stylesheet from a network or local filesystem location.
    ///
    /// # Errors
    ///
    /// Any network or filesystem related error, or an error during response parsing.
    fn retrieve(&self, location: &str) -> Result<String> {
        if location.starts_with("https") | location.starts_with("http") {
            #[cfg(feature = "http")]
            {
                self.retrieve_from_url(location)
            }

            #[cfg(not(feature = "http"))]
            {
                Err(std::io::Error::new(
                    ErrorKind::Unsupported,
                    "Loading external URLs requires the `http` feature",
                )
                .into())
            }
        } else {
            #[cfg(feature = "file")]
            {
                self.retrieve_from_path(location)
            }
            #[cfg(not(feature = "file"))]
            {
                Err(std::io::Error::new(
                    ErrorKind::Unsupported,
                    "Loading local files requires the `file` feature",
                )
                .into())
            }
        }
    }
    /// Retrieve a stylesheet from a network location.
    ///
    /// # Errors
    ///
    /// Any network-related error, or an error during response parsing.
    fn retrieve_from_url(&self, url: &str) -> Result<String> {
        Err(self.unsupported(&format!("Loading external URLs is not supported: {url}")))
    }
    /// Retrieve a stylesheet from the local filesystem.
    ///
    /// # Errors
    ///
    /// Any filesystem-related error.
    fn retrieve_from_path(&self, path: &str) -> Result<String> {
        let path = path.trim_start_matches("file://");
        std::fs::read_to_string(path).map_err(|error| match error.kind() {
            ErrorKind::NotFound => InlineError::MissingStyleSheet {
                path: path.to_string(),
            },
            #[cfg(target_family = "wasm")]
            ErrorKind::Unsupported => self.unsupported(&format!(
                "Loading local files is not supported on WASM: {path}"
            )),
            _ => InlineError::IO(error),
        })
    }
    /// Return the "Unsupported" kind of error.
    fn unsupported(&self, reason: &str) -> InlineError {
        std::io::Error::new(ErrorKind::Unsupported, reason).into()
    }
}

/// Default stylesheet resolver.
#[derive(Debug, Default)]
pub struct DefaultStylesheetResolver;

impl StylesheetResolver for DefaultStylesheetResolver {
    #[cfg(feature = "http")]
    fn retrieve_from_url(&self, url: &str) -> Result<String> {
        let into_error = |error| InlineError::Network {
            error,
            location: url.to_string(),
        };
        reqwest::blocking::get(url)
            .map_err(into_error)?
            .text()
            .map_err(into_error)
    }
}