Initial commit
This commit is contained in:
commit
ea042c33ba
26 changed files with 4452 additions and 0 deletions
160
src/infrastructure/scryfall.rs
Normal file
160
src/infrastructure/scryfall.rs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
use anyhow::{Context, Result};
|
||||
use reqwest::{Client, StatusCode, Url};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::domain::{CardInfoRequest, OriginalCardName, OriginalCardNameFetcher};
|
||||
|
||||
pub struct ScryfallOriginalCardNameFetcher {
|
||||
client: Client,
|
||||
url: Url,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CardResponse {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl ScryfallOriginalCardNameFetcher {
|
||||
pub fn new(client: Client, url: Url) -> Self {
|
||||
Self { client, url }
|
||||
}
|
||||
}
|
||||
|
||||
impl OriginalCardNameFetcher for ScryfallOriginalCardNameFetcher {
|
||||
async fn fetch_original_card_name(
|
||||
&self,
|
||||
request: &CardInfoRequest,
|
||||
) -> Result<Option<OriginalCardName>> {
|
||||
let url = match request {
|
||||
CardInfoRequest::ByPrintedName { name } => {
|
||||
let mut url = self
|
||||
.url
|
||||
.join("/cards/named")
|
||||
.context("failed to form scryfall named url")?;
|
||||
url.query_pairs_mut().append_pair("fuzzy", name);
|
||||
url
|
||||
}
|
||||
CardInfoRequest::BySetAndNumber { set, number } => self
|
||||
.url
|
||||
.join(&format!("/cards/{set}/{number}"))
|
||||
.context("failed to form scryfall set+number url")?,
|
||||
};
|
||||
let res = self
|
||||
.client
|
||||
.get(url)
|
||||
.header("user-agent", "mtg-price-bot/1.0")
|
||||
.header("accept", "application/json")
|
||||
.send()
|
||||
.await
|
||||
.context("failed to get card info from scryfall")?;
|
||||
if res.status() == StatusCode::NOT_FOUND {
|
||||
return Ok(None);
|
||||
}
|
||||
let response = res
|
||||
.json::<CardResponse>()
|
||||
.await
|
||||
.context("failed to parse scryfall response")?;
|
||||
Ok(Some(OriginalCardName(response.name)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use serde_json::json;
|
||||
#[tokio::test]
|
||||
async fn test_search_card_by_printed_name() {
|
||||
let mut server = mockito::Server::new_async().await;
|
||||
|
||||
let mock = server
|
||||
.mock("GET", "/cards/named")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fuzzy".into(),
|
||||
"Тефери, герой доминарии".into(),
|
||||
))
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(
|
||||
json!({
|
||||
"name": "Teferi, Hero of Dominaria"
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let url = Url::parse(server.url().as_str()).unwrap();
|
||||
|
||||
let name = ScryfallOriginalCardNameFetcher::new(Client::new(), url)
|
||||
.fetch_original_card_name(&CardInfoRequest::ByPrintedName {
|
||||
name: "Тефери, герой доминарии".into(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(name.unwrap().0, "Teferi, Hero of Dominaria");
|
||||
|
||||
mock.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_card_by_set_and_number() {
|
||||
let mut server = mockito::Server::new_async().await;
|
||||
|
||||
let mock = server
|
||||
.mock("GET", "/cards/GRN/123")
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(
|
||||
json!({
|
||||
"name": "Teferi, Hero of Dominaria"
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let url = Url::parse(server.url().as_str()).unwrap();
|
||||
|
||||
let name = ScryfallOriginalCardNameFetcher::new(Client::new(), url)
|
||||
.fetch_original_card_name(&CardInfoRequest::BySetAndNumber {
|
||||
set: "GRN".into(),
|
||||
number: "123".into(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(name.unwrap().0, "Teferi, Hero of Dominaria");
|
||||
|
||||
mock.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_card_not_found() {
|
||||
let mut server = mockito::Server::new_async().await;
|
||||
|
||||
let mock = server
|
||||
.mock("GET", "/cards/named")
|
||||
.match_query(mockito::Matcher::UrlEncoded(
|
||||
"fuzzy".into(),
|
||||
"Тефери, герой доминарии".into(),
|
||||
))
|
||||
.with_status(404)
|
||||
.create_async()
|
||||
.await;
|
||||
|
||||
let url = Url::parse(server.url().as_str()).unwrap();
|
||||
|
||||
let result = ScryfallOriginalCardNameFetcher::new(Client::new(), url)
|
||||
.fetch_original_card_name(&CardInfoRequest::ByPrintedName {
|
||||
name: "Тефери, герой доминарии".into(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(result.is_none());
|
||||
|
||||
mock.assert_async().await;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue