use reqwest::Client;
use std::time::Duration;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("HTTP error: {0}")]
Http(String),
#[error("Other error: {0}")]
Other(String),
}
impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Self {
Error::Http(e.to_string())
}
}
pub async fn fetch_http(url: &str) -> Result<Vec<u8>> {
let client = Client::builder()
.timeout(Duration::from_secs(300)) .build()
.map_err(|e| Error::Other(format!("Failed to create HTTP client: {e}")))?;
let response = client
.get(url)
.send()
.await
.map_err(|e| Error::Http(format!("Failed to fetch URL {url}: {e}")))?;
if !response.status().is_success() {
return Err(Error::Http(format!(
"HTTP request failed with status: {}",
response.status()
)));
}
let bytes = response
.bytes()
.await
.map_err(|e| Error::Http(format!("Failed to read response body: {e}")))?;
Ok(bytes.to_vec())
}
pub fn fetch_http_sync(url: &str) -> Result<Vec<u8>> {
tokio::runtime::Runtime::new()
.map_err(|e| Error::Other(format!("Failed to create runtime: {e}")))?
.block_on(fetch_http(url))
}
pub fn is_http_url(s: &str) -> bool {
s.starts_with("http://") || s.starts_with("https://")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_http_url() {
assert!(is_http_url("http://example.com/data.csv"));
assert!(is_http_url("https://example.com/data.csv"));
assert!(!is_http_url("file:///data.csv"));
assert!(!is_http_url("/path/to/file.csv"));
assert!(!is_http_url("data.csv"));
}
}