Skip to content

Commit

Permalink
Add accept and user agent headers to all requests
Browse files Browse the repository at this point in the history
  • Loading branch information
mendess committed Jul 29, 2024
1 parent ebb21bc commit 8fbafaf
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 21 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "scryfall"
version = "0.17.1"
version = "0.17.2"
authors = ["Mendess2526 <[email protected]>"]
edition = "2018"
edition = "2021"
description = "A wrapper around the scryfall magic the gathering api"
license = "MIT"
repository = "https://github.com/mendess/scryfall-rs"
Expand Down
6 changes: 5 additions & 1 deletion src/bulk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::ruling::Ruling;
use crate::uri::Uri;
use crate::util::array_stream_reader::ArrayStreamReader;
use crate::util::BULK_DATA_URL;
use crate::Error;

/// Scryfall provides daily exports of our card data in bulk files. Each of
/// these files is represented as a bulk_data object via the API. URLs for files
Expand Down Expand Up @@ -164,7 +165,10 @@ impl<T: DeserializeOwned> BulkDataFile<T> {
pub async fn download(&self, path: impl AsRef<Path>) -> crate::Result<()> {
let path = path.as_ref();
let response = self.download_uri.fetch_raw().await?;
let content = response.bytes().await?;
let content = response.bytes().await.map_err(|e| Error::ReqwestError {
error: e.into(),
url: self.download_uri.inner().clone(),
})?;
io::copy(&mut content.as_ref(), &mut File::create(path)?)?;
Ok(())
}
Expand Down
25 changes: 12 additions & 13 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ pub enum Error {
UrlParseError(#[from] UrlParseError),

/// Something went wrong when making the HTTP request.
#[error("Error making request: {0}")]
ReqwestError(Box<ReqwestError>, String),
#[error("Error making request to {url}: {error}")]
ReqwestError {
/// The error reqwest returned.
error: Box<ReqwestError>,
/// The url that was involved.
url: url::Url,
},

/// Scryfall error. Please refer to the [official docs](https://scryfall.com/docs/api/errors).
#[error("Scryfall error: {0}")]
ScryfallError(ScryfallError),
ScryfallError(Box<ScryfallError>),

/// HTTP error with status code.
#[error("HTTP error: {0}")]
Expand All @@ -53,16 +58,6 @@ impl From<SerdeError> for Box<Error> {
}
}

impl From<ReqwestError> for Error {
fn from(err: ReqwestError) -> Self {
let s = err
.url()
.map(|url| url.to_string())
.unwrap_or_else(|| format!("{}", err));
Error::ReqwestError(Box::new(err), s)
}
}

impl From<UrlParseError> for Box<Error> {
fn from(err: UrlParseError) -> Self {
Box::new(err.into())
Expand Down Expand Up @@ -97,6 +92,10 @@ pub struct ScryfallError {
/// as human-readable strings in this array.
#[serde(default)]
pub warnings: Vec<String>,

#[cfg(test)]
#[serde(rename = "object")]
_object: String,
}

impl fmt::Display for ScryfallError {
Expand Down
49 changes: 44 additions & 5 deletions src/uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
//! that data.
use std::convert::TryFrom;
use std::marker::PhantomData;
use std::sync::OnceLock;

use httpstatus::StatusCode;
use reqwest::header::{self, HeaderValue};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use url::Url;
Expand All @@ -21,14 +23,23 @@ use crate::list::{List, ListIter};
/// [`List`][crate::list::List]`<_>`, then additional methods `fetch_iter`
/// and `fetch_all` are available, giving access to objects from all pages
/// of the collection.
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Hash, Debug)]
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(test, serde(deny_unknown_fields))]
#[serde(transparent)]
pub struct Uri<T> {
url: Url,
_marker: PhantomData<fn() -> T>,
}

impl<T> std::fmt::Debug for Uri<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Uri")
.field("url", &self.url)
.field("type", &std::any::type_name::<T>())
.finish()
}
}

impl<T> TryFrom<&str> for Uri<T> {
type Error = crate::error::Error;

Expand Down Expand Up @@ -75,6 +86,23 @@ impl<T> AsRef<str> for Uri<T> {
}
}

fn client() -> &'static reqwest::Client {
static CLIENT: OnceLock<reqwest::Client> = OnceLock::new();
CLIENT.get_or_init(|| {
reqwest::Client::builder()
.default_headers(
[
(header::ACCEPT, HeaderValue::from_static("*/*")),
(header::USER_AGENT, HeaderValue::from_static("scryfall-rs")),
]
.into_iter()
.collect(),
)
.build()
.unwrap()
})
}

impl<T: DeserializeOwned> Uri<T> {
/// Fetches a resource from the Scryfall API and deserializes it into a type
/// `T`.
Expand All @@ -95,20 +123,31 @@ impl<T: DeserializeOwned> Uri<T> {
pub async fn fetch(&self) -> crate::Result<T> {
match self.fetch_raw().await {
Ok(response) => match response.status().as_u16() {
200..=299 => Ok(response.json().await?),
200..=299 => response.json().await.map_err(|e| Error::ReqwestError {
error: e.into(),
url: self.url.clone(),
}),
status => Err(Error::HttpError(StatusCode::from(status))),
},
Err(e) => Err(e),
}
}

pub(crate) async fn fetch_raw(&self) -> crate::Result<reqwest::Response> {
match reqwest::get(self.url.clone()).await {
match client().get(self.url.clone()).send().await {
Ok(response) => match response.status().as_u16() {
400..=599 => Err(Error::ScryfallError(response.json().await?)),
400..=599 => Err(Error::ScryfallError(response.json().await.map_err(
|e| Error::ReqwestError {
error: e.into(),
url: self.url.clone(),
},
)?)),
_ => Ok(response),
},
Err(e) => Err(Error::ReqwestError(e.into(), self.url.to_string())),
Err(e) => Err(Error::ReqwestError {
error: e.into(),
url: self.url.clone(),
}),
}
}
}
Expand Down

0 comments on commit 8fbafaf

Please sign in to comment.