use std::env;
use std::path::Path;
use url::Url;
use crate::constants::{ceda, env as env_constants};
use crate::errors::{AuthResult, DownloadResult};
pub mod auth;
pub mod config;
pub mod download;
pub mod http;
pub use config::ClientConfig;
use auth::AuthHandler;
use download::DownloadHandler;
use http::HttpHandler;
#[derive(Debug)]
pub struct CedaClient {
http_handler: HttpHandler,
base_url: Url,
}
impl CedaClient {
pub async fn new_simple() -> AuthResult<Self> {
Self::new_simple_with_config(ClientConfig::default()).await
}
pub async fn new_simple_with_config(config: ClientConfig) -> AuthResult<Self> {
let client = config.build_http_client()?;
let http_handler = HttpHandler::new(client, config.rate_limit_rps)?;
let base_url = Url::parse(ceda::BASE_URL).expect("Base URL should be valid");
tracing::info!("Created simple CEDA client without authentication");
Ok(Self {
http_handler,
base_url,
})
}
pub async fn new() -> AuthResult<Self> {
Self::new_with_config(ClientConfig::default()).await
}
pub async fn new_with_config(config: ClientConfig) -> AuthResult<Self> {
let username = env::var(env_constants::USERNAME)?;
let password = env::var(env_constants::PASSWORD)?;
let client = config.build_http_client()?;
let http_handler = HttpHandler::new(client, config.rate_limit_rps)?;
let base_url = Url::parse(ceda::BASE_URL).expect("Base URL should be valid");
AuthHandler::authenticate(http_handler.client(), &username, &password).await?;
tracing::info!("Successfully authenticated with CEDA");
Ok(Self {
http_handler,
base_url,
})
}
pub async fn get_response(&self, url: &Url) -> DownloadResult<reqwest::Response> {
self.http_handler.get_response(url).await
}
pub async fn get_page(&self, url: &Url) -> DownloadResult<String> {
self.http_handler.get_page(url).await
}
pub async fn download_file(
&self,
url: &Url,
destination: &Path,
force: bool,
) -> DownloadResult<()> {
let download_handler = DownloadHandler::new(&self.http_handler);
download_handler
.download_file(url, destination, force)
.await
}
pub async fn download_file_content(&self, url: &str) -> DownloadResult<Vec<u8>> {
let download_handler = DownloadHandler::new(&self.http_handler);
download_handler.download_file_content(url).await
}
pub fn base_url(&self) -> &Url {
&self.base_url
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::AuthError;
#[tokio::test]
async fn test_simple_client_creation() {
let result = CedaClient::new_simple().await;
assert!(result.is_ok());
let client = result.unwrap();
assert!(
client.base_url.as_str() == ceda::BASE_URL
|| client.base_url.as_str() == format!("{}/", ceda::BASE_URL)
);
}
#[test]
fn test_client_creation_without_env() {
let rt = tokio::runtime::Runtime::new().unwrap();
unsafe {
env::remove_var(env_constants::USERNAME);
env::remove_var(env_constants::PASSWORD);
}
let result = rt.block_on(CedaClient::new());
assert!(result.is_err());
match result.unwrap_err() {
AuthError::EnvVar(_) => {
}
other => {
panic!("Expected AuthError::EnvVar, got {:?}", other);
}
}
}
#[test]
fn test_base_url_access() {
let base_url = Url::parse(ceda::BASE_URL).unwrap();
assert_eq!(base_url.scheme(), "https");
assert_eq!(base_url.host_str(), Some("data.ceda.ac.uk"));
}
}