goods_reqwest/
lib.rs

1use {
2    goods::AssetNotFound,
3    reqwest::{Client, IntoUrl, StatusCode},
4    std::future::Future,
5};
6
7#[cfg(target_arch = "wasm32")]
8use futures_core::future::LocalBoxFuture;
9
10#[cfg(not(target_arch = "wasm32"))]
11use futures_core::future::BoxFuture;
12
13/// Asset source that treats asset key as URL and fetches data from it.
14/// Based on `reqwest` crate.
15#[derive(Debug, Default)]
16pub struct ReqwestSource {
17    client: Client,
18}
19
20impl ReqwestSource {
21    pub fn new() -> Self {
22        ReqwestSource::default()
23    }
24
25    pub fn with_client(client: Client) -> Self {
26        ReqwestSource { client }
27    }
28}
29
30#[cfg(not(target_arch = "wasm32"))]
31impl goods::AutoLocalSource for ReqwestSource {}
32
33impl ReqwestSource {
34    async fn read_impl(
35        &self,
36        request: impl Future<Output = Result<reqwest::Response, reqwest::Error>>,
37    ) -> eyre::Result<Box<[u8]>> {
38        let response = request.await.map_err(|_err| {
39            #[cfg(feature = "trace")]
40            tracing::debug!("Error fetching asset: {}", _err);
41            AssetNotFound
42        })?;
43        let status = response.status();
44        match status {
45            StatusCode::OK => {
46                let bytes = response.bytes().await?;
47                Ok(bytes.as_ref().into())
48            }
49            StatusCode::NO_CONTENT | StatusCode::MOVED_PERMANENTLY | StatusCode::NOT_FOUND => {
50                Err(AssetNotFound.into())
51            }
52            _ => {
53                #[cfg(feature = "trace")]
54                tracing::warn!("Unexpected status: {}", status);
55                Err(AssetNotFound.into())
56            }
57        }
58    }
59}
60
61#[cfg(target_arch = "wasm32")]
62impl<U> goods::LocalSource<U> for ReqwestSource
63where
64    U: IntoUrl + Clone + 'static,
65{
66    fn read_local(&self, url: &U) -> LocalBoxFuture<'_, eyre::Result<Box<[u8]>>> {
67        let request = self.client.get(url.clone()).send();
68        Box::pin(self.read_impl(request))
69    }
70}
71
72#[cfg(not(target_arch = "wasm32"))]
73impl<U> goods::Source<U> for ReqwestSource
74where
75    U: IntoUrl + Clone + 'static,
76{
77    fn read(&self, url: &U) -> BoxFuture<'_, eyre::Result<Box<[u8]>>> {
78        let request = self.client.get(url.clone()).send();
79        Box::pin(self.read_impl(request))
80    }
81}