scryfall 0.25.0

A wrapper around the scryfall magic the gathering api
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
//! This module provides a definition of a Magic: The Gathering card, as well
//! as, ways to fetch them from scryfall.
//!
//! All the card's fields are public and identical in name to the ones
//! documented in the official [scryfall page](https://scryfall.com/docs/api/cards).
mod border_color;
mod card_faces;
mod color;
mod finishes;
mod frame;
mod frame_effect;
mod game;
mod image_status;
mod languages;
mod layout;
mod legality;
mod preview;
mod price;
mod produced_mana;
mod promo_types;
mod purchase_uris;
mod rarity;
mod related_card;
mod related_uris;
mod security_stamp;

use std::ops::{Index, IndexMut};

use chrono::NaiveDate;
use purchase_uris::PurchaseUris;
use related_uris::RelatedUris;
use serde::{Deserialize, Serialize};
use url::Url;
use uuid::Uuid;

pub use self::border_color::BorderColor;
pub use self::card_faces::CardFace;
pub use self::color::{Color, Colors, Multicolored};
pub use self::finishes::Finishes;
pub use self::frame::Frame;
pub use self::frame_effect::FrameEffect;
pub use self::game::Game;
pub use self::image_status::ImageStatus;
pub use self::layout::Layout;
pub use self::legality::Legality;
pub use self::preview::Preview;
pub use self::price::Price;
pub use self::produced_mana::{ProducedMana, UnfinityMana};
pub use self::promo_types::PromoType;
pub use self::rarity::Rarity;
pub use self::related_card::Component;
pub use self::related_card::RelatedCard;
pub use self::security_stamp::SecurityStamp;
use crate::format::Format;
use crate::list::{List, ListIter};
use crate::ruling::Ruling;
use crate::search::Search;
use crate::set::{Set, SetCode, SetType};
use crate::uri::Uri;
use crate::util::CARDS_URL;

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[cfg_attr(not(feature = "unknown_variants"), derive(Copy))]
#[cfg_attr(
    all(
        not(feature = "unknown_variants"),
        not(feature = "unknown_variants_slim")
    ),
    non_exhaustive
)]
#[serde(deny_unknown_fields)]
#[allow(missing_docs)]
pub struct CardLegality {
    #[serde(default)]
    pub standard: Legality,
    #[serde(default)]
    pub modern: Legality,
    #[serde(default)]
    pub legacy: Legality,
    #[serde(default)]
    pub vintage: Legality,
    #[serde(default)]
    pub commander: Legality,
    #[serde(default)]
    pub future: Legality,
    #[serde(default)]
    pub pauper: Legality,
    #[serde(default)]
    pub pioneer: Legality,
    #[serde(default)]
    pub penny: Legality,
    #[serde(default)]
    pub duel: Legality,
    #[serde(default, rename = "oldschool")]
    pub old_school: Legality,
    #[serde(default)]
    pub historic: Legality,
    #[serde(default)]
    pub gladiator: Legality,
    #[serde(default)]
    pub brawl: Legality,
    #[serde(default)]
    pub premodern: Legality,
    #[serde(default, rename = "paupercommander")]
    pub pauper_commander: Legality,
    #[serde(default)]
    pub alchemy: Legality,
    #[serde(default)]
    pub explorer: Legality,
    #[serde(default)]
    pub predh: Legality,
    #[serde(default)]
    pub oathbreaker: Legality,
    #[serde(default)]
    pub timeless: Legality,
    #[serde(default, rename = "standardbrawl")]
    pub standard_brawl: Legality,
    #[serde(default, rename = "historicbrawl")]
    pub historic_brawl: Legality,
}

impl Index<Format> for CardLegality {
    type Output = Legality;

    fn index(&self, index: Format) -> &Self::Output {
        match index {
            Format::Standard => &self.standard,
            Format::Modern => &self.modern,
            Format::Legacy => &self.legacy,
            Format::Vintage => &self.vintage,
            Format::Commander => &self.commander,
            Format::Future => &self.future,
            Format::Pauper => &self.pauper,
            Format::Pioneer => &self.pioneer,
            Format::Penny => &self.penny,
            Format::Duel => &self.duel,
            Format::OldSchool => &self.old_school,
            Format::Historic => &self.historic,
            Format::Gladiator => &self.gladiator,
            Format::Brawl => &self.brawl,
            Format::Premodern => &self.premodern,
            Format::PauperCommander => &self.pauper_commander,
            Format::Alchemy => &self.alchemy,
            Format::Explorer => &self.explorer,
            Format::Predh => &self.predh,
            Format::Oathbreaker => &self.oathbreaker,
            Format::Timeless => &self.timeless,
            Format::StandardBrawl => &self.standard_brawl,
            Format::HistoricBrawl => &self.historic_brawl,
        }
    }
}

impl IndexMut<Format> for CardLegality {
    fn index_mut(&mut self, index: Format) -> &mut Self::Output {
        match index {
            Format::Standard => &mut self.standard,
            Format::Modern => &mut self.modern,
            Format::Legacy => &mut self.legacy,
            Format::Vintage => &mut self.vintage,
            Format::Commander => &mut self.commander,
            Format::Future => &mut self.future,
            Format::Pauper => &mut self.pauper,
            Format::Pioneer => &mut self.pioneer,
            Format::Penny => &mut self.penny,
            Format::Duel => &mut self.duel,
            Format::OldSchool => &mut self.old_school,
            Format::Historic => &mut self.historic,
            Format::Gladiator => &mut self.gladiator,
            Format::Brawl => &mut self.brawl,
            Format::Premodern => &mut self.premodern,
            Format::PauperCommander => &mut self.pauper_commander,
            Format::Alchemy => &mut self.alchemy,
            Format::Explorer => &mut self.explorer,
            Format::Predh => &mut self.predh,
            Format::Oathbreaker => &mut self.oathbreaker,
            Format::Timeless => &mut self.timeless,
            Format::StandardBrawl => &mut self.standard_brawl,
            Format::HistoricBrawl => &mut self.historic_brawl,
        }
    }
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[cfg_attr(test, serde(deny_unknown_fields))]
/// Scryfall produces multiple sizes of images and image crops for each Card object. Links to these
/// images are available in each Card objects’ image_uris properties.
///
/// Field         | Size       | Format | Example
///  ---          | ---        | ---    | ---
/// `png`         | 745 × 1040 | PNG    | [Example Image](https://cards.scryfall.io/png/front/6/d/6da045f8-6278-4c84-9d39-025adf0789c1.png?1562404626)
/// `border_crop` | 480 × 680  | JPG    | [Example Image](https://cards.scryfall.io/border_crop/front/6/d/6da045f8-6278-4c84-9d39-025adf0789c1.jpg?1562404626)
/// `art_crop`    | Varies     | JPG    | [Example Image](https://cards.scryfall.io/art_crop/front/6/d/6da045f8-6278-4c84-9d39-025adf0789c1.jpg?1562404626)
/// `large`       | 672 × 936  | JPG    | [Example Image](https://cards.scryfall.io/large/front/6/d/6da045f8-6278-4c84-9d39-025adf0789c1.jpg?1562404626)
/// `normal`      | 488 × 680  | JPG    | [Example Image](https://cards.scryfall.io/normal/front/6/d/6da045f8-6278-4c84-9d39-025adf0789c1.jpg?1562404626)
/// `small`       | 146 × 204  | JPG    | [Example Image](https://cards.scryfall.io/small/front/6/d/6da045f8-6278-4c84-9d39-025adf0789c1.jpg?1562404626)
pub struct ImageUris {
    /// A transparent, rounded full card PNG. This is the best image to use for videos or other
    /// high-quality content.
    #[serde(default)]
    pub png: Option<Url>,
    /// A full card image with the rounded corners and the majority of the border cropped off.
    /// Designed for dated contexts where rounded images can’t be used.
    #[serde(default)]
    pub border_crop: Option<Url>,
    /// A rectangular crop of the card’s art only. Not guaranteed to be perfect for cards with
    /// outlier designs or strange frame arrangements
    #[serde(default)]
    pub art_crop: Option<Url>,
    ///  A large full card image
    #[serde(default)]
    pub large: Option<Url>,
    /// A medium-sized full card image
    #[serde(default)]
    pub normal: Option<Url>,
    /// A small full card image. Designed for use as thumbnail or list icon.
    #[serde(default)]
    pub small: Option<Url>,
}

/// Card objects represent individual Magic: The Gathering cards that players
/// could obtain and add to their collection (with a few minor exceptions).
///
/// ## Card Names
/// Internally, Scryfall tracks the uniqueness of “Oracle names.” (i.e. names
/// you can pick when an effect asks you to “choose a card name”). Each unique
/// Oracle name is separately available in the card names catalog.
///
/// Note that while most Oracle card names are unique, Scryfall also indexes
/// other objects such as tokens and Unstable set variants which do not always
/// have a unique name.
///
/// ## Multiface Cards
/// Magic cards can have multiple faces. The faces could be shown divided on the
/// front of the card as in split cards and flip cards, or the card can be
/// double-sided as in transform cards and double-sided tokens.
///
/// Scryfall represents multiface cards as a single object with a card_faces
/// array describing the distinct faces.
///
/// ---
///
/// For more details, see the [official documentation](https://scryfall.com/docs/api/cards).
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[cfg_attr(test, serde(deny_unknown_fields))]
#[non_exhaustive]
pub struct Card {
    // region Core Card Fields
    // =======================
    /// This card’s Arena ID, if any. A large percentage of cards are not
    /// available on Arena and do not have this ID.
    pub arena_id: Option<usize>,

    /// A unique ID for this card in Scryfall’s database.
    pub id: Uuid,

    // TODO(msmorgan): Language enum? https://scryfall.com/docs/api/languages
    /// A language code for this printing.
    pub lang: String,

    /// This card’s Magic Online ID (also known as the Catalog ID), if any. A
    /// large percentage of cards are not available on Magic Online and do not
    /// have this ID.
    pub mtgo_id: Option<usize>,

    /// This card’s foil Magic Online ID (also known as the Catalog ID), if any.
    /// A large percentage of cards are not available on Magic Online and do not
    /// have this ID.
    pub mtgo_foil_id: Option<usize>,

    /// This card’s multiverse IDs on Gatherer, if any, as an array of integers.
    /// Note that Scryfall includes many promo cards, tokens, and other esoteric
    /// objects that do not have these identifiers.
    pub multiverse_ids: Option<Vec<usize>>,

    /// This card’s ID on TCGplayer’s API, also known as the `productId`.
    pub tcgplayer_id: Option<usize>,

    /// This card’s ID on TCGplayer’s API, for its etched version if that version is a separate product.
    pub tcgplayer_etched_id: Option<usize>,

    /// This card’s ID on Cardmarket’s API, also known as the `idProduct`.
    pub cardmarket_id: Option<usize>,

    /// A unique ID for this card’s oracle identity. This value is consistent
    /// across reprinted card editions, and unique among different cards with
    /// the same name (tokens, Unstable variants, etc).
    pub oracle_id: Option<Uuid>,

    /// A link to where you can begin paginating all re/prints for this card on
    /// Scryfall’s API.
    pub prints_search_uri: Uri<List<Card>>,

    /// A link to this card’s rulings list on Scryfall’s API.
    pub rulings_uri: Uri<Vec<Ruling>>,

    /// A link to this card’s permapage on Scryfall’s website.
    pub scryfall_uri: Url,

    /// A link to this card object on Scryfall’s API.
    pub uri: Uri<Card>,
    // ==========================
    // endregion Core Card Fields
    //
    // region Gameplay Fields
    // ======================
    /// If this card is closely related to other cards, this property will be an
    /// array with Related Card Objects.
    pub all_parts: Option<Vec<RelatedCard>>,

    /// An array of Card Face objects, if this card is multifaced.
    pub card_faces: Option<Vec<CardFace>>,

    /// The card’s converted mana cost. Note that some funny cards have
    /// fractional mana costs.
    pub cmc: Option<f32>,

    /// This card’s color identity.
    pub color_identity: Vec<Color>,

    /// The colors in this card’s color indicator, if any. A null value for this
    /// field indicates the card does not have one.
    pub color_indicator: Option<Vec<Color>>,

    /// This card’s colors, if the overall card has colors defined by the rules.
    /// Otherwise the colors will be on the card_faces objects, see below.
    pub colors: Option<Vec<Color>>,

    /// This card’s overall rank/popularity on EDHREC. Not all cards are ranked.
    pub edhrec_rank: Option<usize>,

    /// True if this card is on the Commander Game Changer list.
    pub game_changer: Option<bool>,

    /// True if this printing exists in a foil version.
    pub foil: bool,

    /// This card’s hand modifier, if it is Vanguard card. This value will
    /// contain a delta, such as -1.
    pub hand_modifier: Option<String>,

    /// An array of keywords that this card uses, such as 'Flying' and
    /// 'Cumulative upkeep'.
    pub keywords: Vec<String>,

    /// A code for this card’s layout.
    pub layout: Layout,

    /// An object describing the legality of this card across play formats.
    /// Possible legalities are legal, not_legal, restricted, and banned.
    pub legalities: CardLegality,

    /// This card’s life modifier, if it is Vanguard card. This value will
    /// contain a delta, such as +2.
    pub life_modifier: Option<String>,

    /// This loyalty if any. Note that some cards have loyalties that are not
    /// numeric, such as X.
    pub loyalty: Option<String>,

    /// The mana cost for this card. This value will be any empty string "" if
    /// the cost is absent. Remember that per the game rules, a missing mana
    /// cost and a mana cost of {0} are different values. Multi-faced cards will
    /// report this value in card faces.
    pub mana_cost: Option<String>,

    /// The name of this card. If this card has multiple faces, this field will
    /// contain both names separated by ` // `.
    pub name: String,

    /// True if this printing exists in a nonfoil version.
    pub nonfoil: bool,

    /// The Oracle text for this card, if any.
    pub oracle_text: Option<String>,

    /// True if this card is oversized.
    pub oversized: bool,

    /// This card’s power, if any. Note that some cards have powers that are not
    /// numeric, such as *.
    pub power: Option<String>,

    /// Colors of mana that this card could produce.
    pub produced_mana: Option<Vec<ProducedMana>>,

    /// True if this card is on the Reserved List.
    pub reserved: bool,

    /// This card’s toughness, if any. Note that some cards have toughnesses
    /// that are not numeric, such as *.
    pub toughness: Option<String>,

    /// The type line of this card.
    pub type_line: Option<String>,

    /// This card’s rank/popularity on Penny Dreadful. Not all cards are ranked.
    pub penny_rank: Option<u64>,

    /// This face’s defense, if any.
    pub defense: Option<String>,
    // =========================
    // endregion Gameplay Fields
    //
    // region Print Fields
    // ===================
    /// The name of the illustrator of this card. Newly spoiled cards may not
    /// have this field yet.
    pub artist: Option<String>,

    /// The IDs of the artists that illustrated this card. Newly spoiled cards may not have this field yet.
    pub artist_ids: Option<Vec<Uuid>>,

    /// Whether this card is found in boosters.
    pub booster: bool,

    /// This card’s border color: black, borderless, gold, silver, or white.
    pub border_color: BorderColor,

    /// The Scryfall ID for the card back design present on this card.
    pub card_back_id: Option<Uuid>,

    /// This card’s collector number. Note that collector numbers can contain
    /// non-numeric characters, such as letters or `★`.
    pub collector_number: String,

    /// True if you should consider avoiding use of this print downstream.
    #[serde(default)]
    pub content_warning: bool,

    /// True if this card was only released in a video game.
    pub digital: bool,

    /// The just-for-fun name printed on the card (such as for Godzilla series
    /// cards).
    pub flavor_name: Option<String>,

    /// The flavor text, if any.
    pub flavor_text: Option<String>,

    /// This card’s frame effects, if any.
    #[serde(default)]
    pub frame_effects: Vec<FrameEffect>,

    /// This card’s frame layout.
    pub frame: Frame,

    /// True if this card’s artwork is larger than normal.
    pub full_art: bool,

    /// A list of games that this card print is available in, paper, arena,
    /// and/or mtgo.
    pub games: Vec<Game>,

    /// True if this card’s imagery is high resolution.
    pub highres_image: bool,

    /// A unique identifier for the card artwork that remains consistent across
    /// reprints. Newly spoiled cards may not have this field yet.
    pub illustration_id: Option<Uuid>,

    /// A computer-readable indicator for the state of this card’s image,
    /// one of `missing`, `placeholder`, `lowres`, or `highres_scan`.
    pub image_status: ImageStatus,

    /// An object listing available imagery for this card. See the [Card Imagery](https://scryfall.com/docs/api/images) article for more information.
    #[serde(default)]
    pub image_uris: Option<ImageUris>,

    /// An object containing daily price information for this card, including
    /// `usd`, `usd_foil`, `eur`, and `tix` prices, as strings.
    #[serde(default)]
    pub prices: Price,

    /// The localized name printed on this card, if any.
    pub printed_name: Option<String>,

    /// The localized text printed on this card, if any.
    pub printed_text: Option<String>,

    /// The localized type line printed on this card, if any.
    pub printed_type_line: Option<String>,

    /// True if this card is a promotional print.
    pub promo: bool,

    /// An object providing URIs to this card’s listing on major marketplaces.
    pub purchase_uris: Option<PurchaseUris>,

    /// This card’s rarity. One of `common`, `uncommon`, `rare`, or `mythic`.
    pub rarity: Rarity,

    /// An object providing URIs to this card’s listing on other Magic: The
    /// Gathering online resources.
    pub related_uris: Option<RelatedUris>,

    /// The date this card was first released.
    pub released_at: NaiveDate,

    /// True if this card is a reprint.
    pub reprint: bool,

    /// A link to this card’s set on Scryfall’s website.
    pub scryfall_set_uri: String,

    /// This card’s full set name.
    pub set_name: String,

    /// A link to where you can begin paginating this card’s set on the Scryfall
    /// API.
    pub set_search_uri: Uri<List<Card>>,

    /// The type of set this printing is in.
    pub set_type: SetType,

    /// A link to this card’s set object on Scryfall’s API.
    pub set_uri: Uri<Set>,

    /// This card’s set code.
    pub set: SetCode,

    /// The scryfall id of the set it belongs to.
    pub set_id: Uuid,

    /// True if this card is a Story Spotlight.
    pub story_spotlight: bool,

    /// True if the card is printed without text.
    pub textless: bool,

    /// Whether this card is a variation of another printing.
    pub variation: bool,

    /// The printing ID of the printing this card is a variation of.
    pub variation_of: Option<Uuid>,

    /// This card’s watermark, if any.
    pub watermark: Option<String>,

    /// Information about when and where the card was originally previewed.
    #[serde(default)]
    pub preview: Preview,

    /// The finishes the card can come in.
    pub finishes: Vec<Finishes>,

    /// The security stamp on this card, if any.
    #[serde(default)]
    pub security_stamp: Option<SecurityStamp>,

    /// An array of strings describing what categories of promo cards this card falls into.
    #[serde(default)]
    pub promo_types: Vec<PromoType>,

    /// The lit Unfinity attractions lights on this card, if any.
    pub attraction_lights: Option<Vec<u8>>,
    /* ======================
     * endregion Print Fields */
    #[cfg(test)]
    #[serde(rename = "object")]
    _object: String,
}

impl Card {
    /// Fetches a random card.
    ///
    /// # Examples
    /// ```rust
    /// # use scryfall::card::Card;
    /// # fn main() -> scryfall::Result<()> {
    /// # tokio_test::block_on(async {
    /// let card = Card::random().await?;
    /// println!("{}", &card.name);
    /// # Ok(())
    /// # })
    /// # }
    /// ```
    pub async fn random() -> crate::Result<Card> {
        Uri::from(CARDS_URL.join("random/")?).fetch().await
    }

    /// Returns a [`ListIter`] of the cards that match the search terms.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// use futures::stream::{self, StreamExt};
    /// use futures::future;
    /// assert!(
    /// # tokio_test::block_on(async {
    ///     Card::search("lightning").await
    ///         .unwrap()
    ///         .into_stream()
    ///         .map(Result::unwrap)
    ///         .all(|x| {
    ///             future::ready(
    ///                 x.name.to_lowercase().contains("lightning") ||
    ///                 x.flavor_name.is_some_and(|n| n.to_lowercase().contains("lightning"))
    ///             )
    ///         })
    ///         .await
    /// # })
    /// )
    /// ```
    ///
    /// ```rust
    /// use scryfall::card::Card;
    /// use futures::stream::{self, StreamExt};
    /// use futures::future;
    /// assert!(
    /// # tokio_test::block_on(async {
    ///     Card::search("lightning").await
    ///         .unwrap()
    ///         .into_stream_buffered(10)
    ///         .map(Result::unwrap)
    ///         .all(|x| {
    ///             future::ready(
    ///                 x.name.to_lowercase().contains("lightning") ||
    ///                 x.flavor_name.is_some_and(|n| n.to_lowercase().contains("lightning"))
    ///             )
    ///         })
    ///         .await
    /// # })
    /// )
    /// ```
    ///
    /// ```rust
    /// # use scryfall::search::prelude::*;
    /// # use futures::stream::{self, StreamExt};
    /// # use futures::future;
    /// # fn main() -> scryfall::Result<()> {
    /// use scryfall::Card;
    /// # tokio_test::block_on(async {
    /// let mut demolish = Card::search(set("war").and(collector_number(123))).await?.into_stream().map(Result::unwrap);
    /// assert!(demolish.all(|card| future::ready(&card.name == "Demolish")).await);
    /// # Ok(())
    /// # })
    /// # }
    /// ```
    ///
    /// ```rust
    /// # use scryfall::search::prelude::*;
    /// # use futures::stream::{self, StreamExt};
    /// # use futures::future;
    /// # fn main() -> scryfall::Result<()> {
    /// use scryfall::Card;
    /// # tokio_test::block_on(async {
    /// let mut demolish = Card::search(set("war").and(collector_number(123))).await?.into_stream_buffered(10).map(Result::unwrap);
    /// assert!(demolish.all(|card| future::ready(&card.name == "Demolish")).await);
    /// # Ok(())
    /// # })
    /// # }
    /// ```
    ///
    /// ```
    /// # use scryfall::search::prelude::*;
    /// use scryfall::{Card, Error};
    /// # tokio_test::block_on(async {
    /// let error = Card::search(power(gte(NumProperty::Power))).await.unwrap_err();
    /// if let Error::ScryfallError(e) = error {
    ///     assert!(e.details.contains("All of your terms were ignored"));
    ///     assert!(e.warnings.len() > 0);
    /// }
    /// # else {
    /// #     panic!("Wrong error type: {0} {0:?}", error)
    /// # }
    /// })
    /// ```
    pub async fn search(query: impl Search) -> crate::Result<ListIter<Card>> {
        let mut url = CARDS_URL.join("search/")?;
        query.write_query(&mut url)?;
        Uri::from(url).fetch_iter().await
    }

    /// Returns all cards that match a query, as a `Vec`. If there is more than
    /// one page of cards, this will involve multiple requests to Scryfall
    /// to get all the cards.
    /// ```rust
    /// # use scryfall::search::prelude::*;
    /// # fn main() -> scryfall::Result<()> {
    /// use scryfall::search::prelude::*;
    /// use scryfall::Card;
    /// # tokio_test::block_on(async {
    /// let all_six_sixes = Card::search_all(power(6).and(toughness(6))).await?;
    /// assert!(all_six_sixes.iter().any(|c| &c.name == "Colossal Dreadmaw"));
    /// # Ok(())
    /// # })
    /// # }
    /// ```
    pub async fn search_all(query: impl Search) -> crate::Result<Vec<Card>> {
        let mut url = CARDS_URL.join("search/")?;
        query.write_query(&mut url)?;
        Uri::from(url).fetch_all().await
    }

    /// Fetches a random card matching a search query.
    ///
    /// # Examples
    /// ```rust
    /// # use scryfall::Card;
    /// # fn main() -> scryfall::Result<()> {
    /// # tokio_test::block_on(async {
    /// let card = Card::search_random("t:Merfolk").await?;
    /// assert!(card.type_line.unwrap().contains("Merfolk"));
    /// # Ok(())
    /// # })
    /// # }
    /// ```
    pub async fn search_random(query: impl Search) -> crate::Result<Card> {
        let mut url = CARDS_URL.join("random/")?;
        query.write_query(&mut url)?;
        Uri::from(url).fetch().await
    }

    /// Return a card with the exact name.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// # tokio_test::block_on(async {
    /// match Card::named("Lightning Bolt").await {
    ///     Ok(card) => assert_eq!(card.name, "Lightning Bolt"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    ///
    /// ```rust
    /// # use scryfall::card::Card;
    /// use scryfall::error::Error;
    /// # tokio_test::block_on(async {
    /// assert!(Card::named("Name that doesn't exist").await.is_err())
    /// # })
    /// ```
    pub async fn named(name: &str) -> crate::Result<Card> {
        let mut url = CARDS_URL.join("named")?;
        url.query_pairs_mut().append_pair("exact", name);
        Uri::from(url).fetch().await
    }

    /// Return a card using the scryfall fuzzy finder.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// # tokio_test::block_on(async {
    /// match Card::named_fuzzy("Light Bolt").await {
    ///     Ok(card) => assert_eq!(card.name, "Lightning Bolt"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    pub async fn named_fuzzy(query: &str) -> crate::Result<Card> {
        let mut url = CARDS_URL.join("named")?;
        url.query_pairs_mut().append_pair("fuzzy", query);
        Uri::from(url).fetch().await
    }

    /// Fetch a card by its set and number.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// # tokio_test::block_on(async {
    /// match Card::set_and_number("vma", 4).await {
    ///     Ok(card) => assert_eq!(card.name, "Black Lotus"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    pub async fn set_and_number(set_code: &str, number: usize) -> crate::Result<Card> {
        Uri::from(CARDS_URL.join(&format!("{set_code}/{number}"))?)
            .fetch()
            .await
    }

    /// Fetch a card by its multiverse id.
    ///
    /// # Examples
    /// ```rust
    /// # tokio_test::block_on(async {
    /// use scryfall::card::Card;
    /// match Card::multiverse(409574).await {
    ///     Ok(card) => assert_eq!(card.name, "Strip Mine"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    pub async fn multiverse(multiverse_id: usize) -> crate::Result<Card> {
        Uri::from(
            CARDS_URL
                .join("multiverse/")?
                .join(&multiverse_id.to_string())?,
        )
        .fetch()
        .await
    }

    /// Fetch a card by its mtgo id.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// # tokio_test::block_on(async {
    /// match Card::mtgo(54957).await {
    ///     Ok(card) => assert_eq!(card.name, "Ghost Quarter"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    pub async fn mtgo(mtgo_id: usize) -> crate::Result<Card> {
        Uri::from(CARDS_URL.join("mtgo/")?.join(&mtgo_id.to_string())?)
            .fetch()
            .await
    }

    /// Fetch a card by its arena id.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// # tokio_test::block_on(async {
    /// match Card::arena(67330).await {
    ///     Ok(card) => assert_eq!(card.name, "Yargle, Glutton of Urborg"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    pub async fn arena(arena_id: usize) -> crate::Result<Card> {
        Uri::from(CARDS_URL.join("arena/")?.join(&arena_id.to_string())?)
            .fetch()
            .await
    }

    /// Fetch a card by its tcgplayer id.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// # tokio_test::block_on(async {
    /// match Card::tcgplayer(67330).await {
    ///     Ok(card) => assert_eq!(card.name, "Fathom Mage"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    pub async fn tcgplayer(tcgplayer_id: usize) -> crate::Result<Card> {
        Uri::from(
            CARDS_URL
                .join("tcgplayer/")?
                .join(&tcgplayer_id.to_string())?,
        )
        .fetch()
        .await
    }

    /// Fetch a card by its Uuid.
    ///
    /// # Examples
    /// ```rust
    /// use scryfall::card::Card;
    /// # tokio_test::block_on(async {
    /// match Card::scryfall_id("0b81b329-4ef5-4b55-9fe7-9ed69477e96b".parse().unwrap()).await {
    ///     Ok(card) => assert_eq!(card.name, "Cowed by Wisdom"),
    ///     Err(e) => panic!("{:?}", e),
    /// }
    /// # })
    /// ```
    pub async fn scryfall_id(scryfall_id: Uuid) -> crate::Result<Card> {
        Uri::from(CARDS_URL.join(&scryfall_id.to_string())?)
            .fetch()
            .await
    }
}