pokemon_tcg_sdk/
card.rs

1pub mod ability;
2pub mod ancient_trait;
3pub mod attack;
4pub mod cardmarket;
5pub mod image;
6pub mod legality;
7pub mod resistance;
8pub mod tcgplayer;
9pub mod weakness;
10
11use std::str::FromStr;
12
13use crate::{
14    client::{ApiResult, Client},
15    errors::ClientError,
16    set::Set,
17};
18use serde::{Deserialize, Serialize};
19
20use self::{
21    ability::Ability, ancient_trait::AncientTrait, attack::Attack, cardmarket::CardMarket,
22    image::Image, legality::Legality, resistance::Resistance, tcgplayer::TcgPlayer,
23    weakness::Weakness,
24};
25
26/// The Card Object
27/// https://docs.pokemontcg.io/api-reference/cards/card-object
28#[derive(Debug, Clone, Serialize, Deserialize)]
29#[cfg_attr(test, derive(Default))]
30pub struct Card {
31    /// Unique identifier for the object.
32    pub id: String,
33    /// The name of the card.
34    pub name: String,
35    /// The supertype of the card, such as Pokémon, Energy, or Trainer.
36    pub supertype: String,
37    /// A list of subtypes, such as Basic, EX, Mega, Rapid Strike, etc.
38    pub subtypes: Option<Vec<String>>,
39    /// The level of the card. This only pertains to cards from older sets and those of supertype Pokémon.
40    pub level: Option<String>,
41    /// The hit points of the card.
42    pub hp: Option<String>,
43    /// The energy types for a card, such as Fire, Water, Grass, etc.
44    pub types: Option<Vec<String>>,
45    /// Which Pokémon this card evolves from.
46    #[serde(alias = "evolvesFrom")]
47    pub evolves_from: Option<String>,
48    /// Which Pokémon this card evolves to. Can be multiple, for example, Eevee.
49    #[serde(alias = "evolvesTo")]
50    pub evolves_to: Option<Vec<String>>,
51    /// Any rules associated with the card. For example, VMAX rules, Mega rules, or various trainer rules.
52    pub rules: Option<Vec<String>>,
53    /// The ancient trait for a given card.
54    #[serde(alias = "ancientTrait")]
55    pub ancient_trait: Option<AncientTrait>,
56    /// One or more abilities for a given card.
57    pub abilities: Option<Vec<Ability>>,
58    /// One or more attacks for a given card
59    pub attacks: Option<Vec<Attack>>,
60    /// One or more weaknesses for a given card
61    pub weaknesses: Option<Vec<Weakness>>,
62    /// One or more resistances for a given card
63    pub resistances: Option<Vec<Resistance>>,
64    /// A list of costs it takes to retreat and return the card to your bench. Each cost is an energy type, such as Water or Fire.
65    #[serde(alias = "retreatCost")]
66    pub retreat_cost: Option<Vec<String>>,
67    /// The converted retreat cost for a card is the count of energy types found within the retreatCost field. For example, ["Fire", "Water"] has a converted retreat cost of 2.
68    #[serde(alias = "convertedRetreatCost")]
69    pub converted_retreat_cost: Option<usize>,
70    /// The set details embedded into the card. See the set object for more details.
71    pub set: Set,
72    /// The number of the card.
73    pub number: Option<String>,
74    /// The artist of the card.
75    pub artist: Option<String>,
76    /// The rarity of the card, such as "Common" or "Rare Rainbow".
77    pub rarity: Option<String>,
78    /// The flavor text of the card. This is the text that can be found on some Pokémon cards that is usually italicized near the bottom of the card.
79    #[serde(alias = "flavorText")]
80    pub flavor_text: Option<String>,
81    /// The national pokedex numbers associated with any Pokémon featured on a given card.
82    #[serde(alias = "nationalPokedexNumbers")]
83    pub national_pokedex_numbers: Option<Vec<usize>>,
84    /// The legalities for a given card. A legality will not be present in the hash if it is not legal. If it is legal or banned, it will be present.
85    pub legalities: Option<Legality>,
86    /// A letter symbol found on each card that identifies whether it is legal to use in tournament play. Regulation marks were introduced on cards in the Sword & Shield Series.
87    #[serde(alias = "regulationMark")]
88    pub regulation_mark: Option<String>,
89    /// The images for a card.
90    pub images: Option<Image>,
91    /// The TCGPlayer information for a given card. ALL PRICES ARE IN US DOLLARS.
92    pub tcgplayer: Option<TcgPlayer>,
93    /// The cardmarket information for a given card. ALL PRICES ARE IN EUROS.
94    pub cardmarket: Option<CardMarket>,
95}
96
97pub struct GetCardRequest {
98    pub id: String,
99}
100
101impl GetCardRequest {
102    pub fn new(id: &str) -> Self {
103        GetCardRequest { id: id.into() }
104    }
105}
106
107pub struct SearchCardsRequest {
108    ///The search query.
109    pub query: Option<String>,
110    /// The page of data to access.
111    pub page: Option<u16>,
112    /// The maximum amount of cards to return. Max of 250.
113    pub page_size: Option<u8>,
114    /// The field(s) to order the results by.
115    pub order_by: Option<String>,
116}
117
118impl SearchCardsRequest {
119    pub fn new(query: &str) -> Self {
120        SearchCardsRequest {
121            query: Some(String::from_str(query).unwrap()),
122            page: None,
123            page_size: None,
124            order_by: None,
125        }
126    }
127}
128
129impl Client {
130    /// Fetch the details of a single card.
131    ///
132    /// https://docs.pokemontcg.io/api-reference/cards/get-card
133    pub async fn get_card(&self, request: GetCardRequest) -> Result<Card, ClientError> {
134        let card = self
135            .http_client
136            .get(format!("{}/cards/{}", self.base_url, request.id))
137            .send()
138            .await?
139            .json::<ApiResult<Card>>()
140            .await?;
141
142        Client::parse_response(card)
143    }
144
145    /// Search for one or many cards given a search query.
146    ///
147    /// https://docs.pokemontcg.io/api-reference/cards/search-cards
148    pub async fn search_cards(
149        &self,
150        request: SearchCardsRequest,
151    ) -> Result<Vec<Card>, ClientError> {
152        let cards = self
153            .http_client
154            .get(format!("{}/cards", self.base_url))
155            .query(&[
156                ("q", request.query),
157                ("page", request.page.map(|p| p.to_string())),
158                ("pageSize", request.page_size.map(|p| p.to_string())),
159                ("orderBy", request.order_by),
160            ])
161            .send()
162            .await?
163            .json::<ApiResult<Vec<Card>>>()
164            .await?;
165
166        Client::parse_response(cards)
167    }
168
169    /// Get all cards (will take awhile, automatically pages through data)
170    pub async fn get_all_cards(&self) -> Result<Vec<Card>, ClientError> {
171        let mut page = 1;
172        let page_size = 250;
173        let mut total_pages: usize = 0;
174        let mut cards: Vec<Card> = vec![];
175
176        loop {
177            let resp = self
178                .http_client
179                .get(format!("{}/cards", self.base_url))
180                .query(&[("page", page)])
181                .send()
182                .await?
183                .json::<ApiResult<Vec<Card>>>()
184                .await?;
185
186            let total_count = if let ApiResult::Ok(ev) = &resp {
187                ev.total_count
188            } else {
189                None
190            };
191
192            match Client::parse_response(resp) {
193                Ok(mut cv) => {
194                    cards.append(&mut cv);
195                    if let Some(tc) = total_count {
196                        total_pages = ((tc / page_size) as f64).ceil() as usize;
197                    }
198
199                    if page > total_pages {
200                        break;
201                    }
202
203                    page += 1;
204                }
205                Err(e) => {
206                    return Err(e);
207                }
208            };
209        }
210
211        Ok(cards)
212    }
213}