1use std::fmt;
4
5use url::Url;
6
7#[derive(Debug)]
8pub enum FetchError {
9 UnsupportedScheme(String),
10 Io(std::io::Error),
11 InvalidData(String),
12}
13
14impl fmt::Display for FetchError {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match self {
17 Self::UnsupportedScheme(scheme) => {
18 write!(f, "unsupported URL scheme for script: {scheme}")
19 }
20 Self::Io(error) => write!(f, "IO error fetching script: {error}"),
21 Self::InvalidData(msg) => write!(f, "invalid script data: {msg}"),
22 }
23 }
24}
25
26impl std::error::Error for FetchError {}
27
28pub trait ScriptFetcher: 'static {
33 fn fetch(&self, url: &Url) -> Result<String, FetchError>;
34}
35
36pub struct DefaultScriptFetcher;
38
39impl ScriptFetcher for DefaultScriptFetcher {
40 fn fetch(&self, url: &Url) -> Result<String, FetchError> {
41 match url.scheme() {
42 "file" => {
43 let path = url
44 .to_file_path()
45 .map_err(|_| FetchError::InvalidData(format!("invalid file URL: {url}")))?;
46 std::fs::read_to_string(path).map_err(FetchError::Io)
47 }
48 "data" => {
49 let data_url = data_url::DataUrl::process(url.as_str())
50 .map_err(|err| FetchError::InvalidData(format!("{err:?}")))?;
51 let (bytes, _) = data_url
52 .decode_to_vec()
53 .map_err(|err| FetchError::InvalidData(format!("{err:?}")))?;
54 String::from_utf8(bytes).map_err(|err| FetchError::InvalidData(format!("{err:?}")))
55 }
56 scheme => Err(FetchError::UnsupportedScheme(scheme.to_string())),
57 }
58 }
59}