use crate::io::{http, uri_to_path, ApiResult};
use crate::prelude::{read, PathBuf};
use crate::{Location, Scheme};
use color_eyre::eyre::eyre;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Source {
Local(PathBuf),
Remote(String),
Unsupported(String),
}
impl Source {
pub async fn read(source: &str, offline: bool) -> ApiResult<String> {
Self::read_bytes(source, offline)
.await
.and_then(|bytes| String::from_utf8(bytes).map_err(|why| eyre!("Failed to decode source as UTF-8 — {why}")))
}
pub async fn read_bytes(source: &str, offline: bool) -> ApiResult<Vec<u8>> {
read_parsed_bytes(Self::parse(source), offline).await
}
pub fn parse(source: &str) -> Self {
let location: Location = source.parse().expect("Location::from_str is infallible");
match location {
| Location::Detailed { scheme: Scheme::File, .. } => {
let local_source = source
.strip_prefix("file://localhost/")
.map(|value| format!("file:///{value}"))
.unwrap_or_else(|| source.to_string());
Self::Local(uri_to_path(PathBuf::from(local_source)))
}
| Location::Detailed {
scheme: Scheme::HTTPS | Scheme::HTTP,
..
} => Self::Remote(source.to_string()),
| Location::Detailed {
scheme: Scheme::Unsupported, ..
} => Self::Unsupported(source.to_string()),
| Location::Simple(_) => Self::Local(PathBuf::from(source)),
}
}
}
impl From<Location> for Source {
fn from(location: Location) -> Self {
let scheme = location.scheme();
let uri = location.uri().unwrap_or_default();
match scheme {
| Scheme::File => Self::Local(uri_to_path(PathBuf::from(uri))),
| Scheme::HTTPS | Scheme::HTTP => Self::Remote(uri),
| Scheme::Unsupported => Self::Unsupported(uri),
}
}
}
async fn read_parsed_bytes(source: Source, offline: bool) -> ApiResult<Vec<u8>> {
match source {
| Source::Local(path) => read(path).map_err(|why| eyre!("Failed to read source — {why}")),
| Source::Remote(url) => read_remote_source_bytes(&url, offline).await,
| Source::Unsupported(scheme) => Err(eyre!("Unsupported source URI scheme '{scheme}'")),
}
}
async fn read_remote_source_bytes(url: &str, offline: bool) -> ApiResult<Vec<u8>> {
match offline {
| true => Err(eyre!("Cannot read remote source while offline")),
| false => http::response_body_bytes(http::get(url).send().await, "Failed to download source").await,
}
}