use crate::{CargoClient, ExtendedCrate, GetCrateError, GetCrateRequestView};
use async_trait::async_trait;
use reqwest::{Client, StatusCode};
use url::Url;
pub struct HttpCargoClient {
client: Client,
server: Url,
}
impl HttpCargoClient {
pub fn new() -> Self {
Self {
client: Client::builder()
.user_agent("cargo_client")
.build()
.expect("building the client always succeeds"),
server: Url::parse("https://crates.io/").expect("the crates.io URL is well-formed"),
}
}
pub fn api_url<I>(&self, segments: I) -> Url
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let mut res = self.server.clone();
{
let mut p = res.path_segments_mut().expect("Cargo registry URL has path segments");
p.extend(["api", "v1"]);
p.extend(segments);
}
res
}
}
impl Default for HttpCargoClient {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl CargoClient for HttpCargoClient {
async fn get_crate(&self, req: GetCrateRequestView<'_>) -> Result<ExtendedCrate, GetCrateError> {
let url = self.api_url(["crates", req.name]);
let res = self
.client
.get(url)
.send()
.await
.map_err(|e| GetCrateError::Send(format!("{e:?}")))?;
match res.status() {
StatusCode::OK => {
let body: ExtendedCrate = res.json().await.map_err(|e| GetCrateError::Receive(format!("{e:?}")))?;
Ok(body)
}
StatusCode::NOT_FOUND => Err(GetCrateError::NotFound),
code => Err(GetCrateError::Receive(format!("unexpected status code {code}"))),
}
}
}