= Scryfall SDK for Rust
:icons: font
:toc:
:sectanchors:
:sectnums:
:source-highlighter: highlight.js
:highlightjs-theme: monokai-sublime
image:https://img.shields.io/github/workflow/status/dgian/scryfall-sdk-rust/CI?label=CI&style=for-the-badge[Build Status (GitHub Actions),link=https://github.com/dgian/scryfall-sdk-rust/actions]
image:https://img.shields.io/docsrs/scryfall_sdk_rust?label=docs.rs&style=for-the-badge[link="https://docs.rs/scryfall_sdk_rust"]
image:https://img.shields.io/crates/v/scryfall_sdk_rust?style=for-the-badge[link="https://crates.io/crates/scryfall_sdk_rust"]
This is a basic SDK written in https://www.rust-lang.org[Rust] for the amazing https://scryfall.com[Scryfall search-engine], a powerful search tool for Magic: The Gathering cards, sets etc.
This SDK is implemented mainly for learning and practicing Rust skills.
IMPORTANT: This is currently a WIP.
== Requirements
* *Minimum Rust version*: `1.61`
== Usage
First add the dependency to `Cargo.toml`:
[source, toml]
----
[dependencies]
scryfall_sdk_rust = "0.1"
----
The SDK exposes two HTTP clients using https://crates.io/crates/reqwest[reqwest crate]:
[loweralpha]
. async client (default) via `Scryfall` struct
. blocking client via `ScryfallBlocking` struct
NOTE: In order to use the blocking client you have to enable the `blocking` optional feature in Cargo.toml.
[source, toml]
----
[dependencies]
scryfall_sdk_rust = {version = "0.1", features = ["blocking"] }
----
=== Examples
In order to use the SDK, you have to take an instance of either client
and make a request with it using one of the <<_resources, provided resources>>.
For example:
.Get a single card by Scryfall id (async)
[source, rust]
----
use std::error::Error;
use scryfall_sdk_rust::{CardResource, Scryfall};
#[tokio::main] // <1>
async fn main() -> Result<(), Box<dyn Error>> {
let scryfall = Scryfall::default(); // <2>
let card = scryfall.request(
&CardResource::ById("f295b713-1d6a-43fd-910d-fb35414bf58a")
).await?; // <3>
Ok(println!("{:?}", card))
}
----
<1> Using https://crates.io/crates/tokio[tokio runtime] for the examples, but you can use anyone you like
<2> https://api.scryfall.com[Default scryfall api].You can also instantiate the client using `::from_url(&str)` to pass a custom url.
<3> Make an async request to retrieve the card's data
==== Card search
Scryfall provides a https://scryfall.com/docs/syntax[very powerful search syntax] which you
can leverage in order to search for cards. *Note* currently the SDK provides only usage of row `q` string in the query parmaeter.
Future versions will provide a Rust fluent api supporting the search keywords separately.
.Find red creatures with 3 power (async)
[source,rust]
----
use std::error::Error;
use scryfall_sdk_rust::{CardPageResource, Scryfall};
use scryfall_sdk_rust::resources::cards::SearchQueryParams;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let scryfall = Scryfall::default();
let search = CardPageResource::Search(
SearchQueryParams::with_q("c%3Ared+pow%3D3") // <1>
);
let cards = scryfall.request(&search).await?;
Ok(println!("{:?}", cards))
}
----
<1> Construct the search query based on fulltext search syntax (c:red + pow=3)
==== Error response handling
In the previous very basic examples, any kind of error,
either an error response from the API, or a client error
is propagated to the `Result` return of `main` function.
This is done through the special `?` rust operator which propagates
errors to the caller of a function.
In reality, you will probably want to handle those errors at some point,
at least the error responses (e.g. 404) from the API.
The `request` function of both clients returns a `Result<M, ErrorBody>`
which should contain either the Model for the expected object (e.g. Card) in case of success, or an `ErrorBody` in case of an error. For more info on the error response payloads (ErrorBody) see https://scryfall.com/docs/api/errors[Scryfall documentation].
An example of a possible error handling is the following
[source,rust]
----
use std::error::Error;
use scryfall_sdk_rust::{CardResource, Scryfall};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let scryfall = Scryfall::default();
let card = scryfall.request(
&CardResource::ById("f295b713-1d6a-43fd-910d-fb35414bf58a")
).await; // <1>
println!("{:?}", card
.map_err(|e| format!("Error {}: {}", e.status, e.details)
)); // <2>
let error = scryfall.request(
&CardResource::ById("invalid")
).await; // <3>
Ok(println!("{:?}", error
.map_err(|e| format!("Error {}: {}", e.status, e.details))
)) // <4>
}
----
<1> Get the details for an existing card. Notice the absence of `?` after `await` as opposed to the previous example.
<2> Printing here, will print the card details and not error as `map_err` does not affect the `Response::Ok` type of `Result`
<3> Get the details of a non-existing card should return an error (404)
<4> Printing here will print the details of the error response (status and detailed message) as the `Result` is now a `Response::Err` containing the respective `ErrorBody`
For client errors, e.g. when the Scryfall API server cannot be resolved,
or when the json response cannot be decoded for some reason,
a special `ErrorBody` will be returned. This will have `code = CLIENT_ERR`
and `status = 599` with `details` containing the original error cause.
[#_resources]
== List of implemented resources
The following are currently implemented:
- `CardResource` -> https://scryfall.com/docs/api/cards (single)
- `CardPageResource` -> https://scryfall.com/docs/api/cards (page/search)
- `CardCatalogResource` -> https://scryfall.com/docs/api/cards/autocomplete
- `CardCollectionResource` -> https://scryfall.com/docs/api/cards/collection
- `BulkDataListResource` -> https://scryfall.com/docs/api/bulk-data (list)
- `BulkDataResource` -> https://scryfall.com/docs/api/bulk-data (single)
- `CatalogResource` -> https://scryfall.com/docs/api/catalogs
- `CardSymbolsResource` -> https://scryfall.com/docs/api/card-symbols/all
- `ManaCostResource` -> https://scryfall.com/docs/api/card-symbols/parse-mana
- `CardSetListResource` -> https://scryfall.com/docs/api/sets (list)
- `CardSetResource` -> https://scryfall.com/docs/api/sets (single)
- `RulingListResource` -> https://scryfall.com/docs/api/rulings