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#[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}