scryfall/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![deny(missing_docs)]
3//! [Scryfall](https://scryfall.com) provides a REST-like API for ingesting our card data
4//! programatically. The API exposes information available on the regular site
5//! in easy-to-consume formats.
6//!
7//! ## Cards
8//! The main way to fetch cards from this API is the [`Card`] struct.
9//!
10//! This allows you to get cards from `scryfall` using all of their available
11//! REST Apis
12//!
13//! ```rust
14//! use scryfall::card::Card;
15//! # tokio_test::block_on(async {
16//! match Card::named_fuzzy("Light Bolt").await {
17//!     Ok(card) => assert_eq!(card.name, "Lightning Bolt"),
18//!     Err(e) => panic!("{e:?}"),
19//! }
20//! # })
21//! ```
22//!
23//! Double faced cards have some of their properties inside the card_faces array instead of at
24//! the top level.
25//! ```
26//! use scryfall::card::{Card, Color};
27//! # tokio_test::block_on(async {
28//!
29//! match Card::named("Delver of Secrets").await {
30//!     Ok(card) => {
31//!         assert!(card.colors.is_none());
32//!         assert_eq!(card.card_faces.unwrap()[0].colors, Some(vec![Color::Blue]));
33//!     }
34//!     Err(e) => panic!("{e:?}"),
35//! }
36//!
37//! # })
38//! ```
39//!
40//! ## Sets
41//! You can also fetch information about a card set.
42//!
43//! The available routes for this can be seen on [`Set`]
44//!
45//! ```rust
46//! use scryfall::set::Set;
47//! # tokio_test::block_on(async {
48//! assert_eq!(Set::code("mmq").await.unwrap().name, "Mercadian Masques")
49//! # })
50//! ```
51//!
52//! ## Catalogs
53//! Finally `scryfall` also allows you to fetch *catalogs* which
54//! are collections of Magic the Gathering data points.
55//!
56//! For example, one could fetch all available card names.
57//! ```rust,no_run
58//! use scryfall::catalog::Catalog;
59//! # tokio_test::block_on(async {
60//! assert!(Catalog::card_names().await.unwrap().data.len() > 0)
61//! # })
62//! ```
63//!
64//! ## Advanced Search
65//!
66//! One of the main features of `scryfall` is its advanced search.
67//! For this the [`search`] module provides a type safe api
68//! to interact and query the search engine. For advanced features like
69//! sorting and collation, see [`search::advanced`].
70pub mod bulk;
71pub mod card;
72pub mod catalog;
73pub mod error;
74pub mod format;
75pub mod list;
76pub mod ruling;
77pub mod search;
78pub mod set;
79pub mod uri;
80mod util;
81
82/// The result type used to describe all fallible operations of the scryfall
83/// crate.
84pub type Result<T> = std::result::Result<T, error::Error>;
85
86pub use card::Card;
87pub use catalog::Catalog;
88pub use error::Error;
89pub use ruling::Ruling;
90pub use set::Set;
91
92#[cfg(test)]
93mod tests {
94    use std::convert::TryFrom;
95
96    use futures::stream::StreamExt;
97
98    use serde_json::{from_str, to_string};
99
100    use crate::search::prelude::*;
101    use crate::set::{Set, SetCode};
102
103    #[test]
104    fn set_code_serde_test() {
105        let instance = SetCode::try_from("war").unwrap();
106        let new_instance: SetCode = from_str(&to_string(&instance).unwrap()).unwrap();
107        assert_eq!(new_instance, instance);
108
109        let instance = SetCode::try_from("wwar").unwrap();
110        let new_instance: SetCode = from_str(&to_string(&instance).unwrap()).unwrap();
111        assert_eq!(new_instance, instance)
112    }
113
114    #[tokio::test]
115    #[ignore]
116    async fn all_sets() {
117        Set::all()
118            .await
119            .unwrap()
120            .into_stream()
121            .map(Result::unwrap)
122            .for_each(|set| async move {
123                assert!(set.code.get().len() >= 3);
124            })
125            .await;
126    }
127
128    #[tokio::test]
129    #[ignore]
130    async fn all_sets_buffered() {
131        Set::all()
132            .await
133            .unwrap()
134            .into_stream_buffered(10)
135            .map(Result::unwrap)
136            .for_each(|set| async move {
137                assert!(set.code.get().len() >= 3);
138            })
139            .await;
140    }
141
142    #[tokio::test]
143    #[ignore]
144    async fn latest_cards() {
145        Set::all()
146            .await
147            .unwrap()
148            .into_stream()
149            .map(Result::unwrap)
150            .take(30)
151            .for_each_concurrent(None, |s| async move {
152                let set_cards = set(s.code).search().await;
153                if let Err(e) = set_cards {
154                    println!("Could not search for cards in '{}' - {}", s.name, e);
155                }
156            })
157            .await;
158    }
159
160    #[tokio::test]
161    #[ignore]
162    async fn latest_cards_buffered() {
163        Set::all()
164            .await
165            .unwrap()
166            .into_stream_buffered(10)
167            .map(Result::unwrap)
168            .take(30)
169            .for_each_concurrent(None, |s| async move {
170                let set_cards = set(s.code).search().await;
171                if let Err(e) = set_cards {
172                    println!("Could not search for cards in '{}' - {}", s.name, e);
173                }
174            })
175            .await;
176    }
177}