diff --git a/scryfall_deser/src/lib.rs b/scryfall_deser/src/lib.rs index fdbbd53..ebedfe1 100644 --- a/scryfall_deser/src/lib.rs +++ b/scryfall_deser/src/lib.rs @@ -24,9 +24,71 @@ struct ScryfallCard { pub uri: String, //URI // Gameplay Fields + // https://scryfall.com/docs/api/cards#gameplay-fields pub all_parts: Option>, + pub card_faces: Option>, + // NOTE: Much of the next is a repeat of what's in the ScryfallCardFaceObject if you change something here, change something there + // NOTE: Probably a bad idea to rename color -> colour just for the sake + pub cmc: Option, // TODO: Make this a proper Decimal - see "Little Girl" card for example of cmc of 0.5 + #[serde(rename = "color_identity")] + pub colour_identity: Vec, + #[serde(rename = "color_indicator")] + pub colour_indicator: Option>, + #[serde(rename = "colors")] + pub colours: Option>, + pub edhrec_rank: Option, + pub defence: Option, + pub hand_modifier: Option, + pub keywords: Vec, // Words like "Flying" + pub legalities: FormatLegalities, + pub life_modifier: Option, + pub loyalty: Option, + pub mana_cost: Option, + pub name: String, + pub oracle_text: Option, + pub penny_rank: Option, + pub power: Option, + pub produced_mana: Option>, + pub reserved: bool, + pub toughness: Option, + pub type_line: String, + // Print Fields + // https://scryfall.com/docs/api/cards#print-fields + pub artist: Option, + pub artist_ids: Option>, + pub attraction_lights: Option>, // TODO: I'm not actually sure what these look like - I should test + pub booster: bool, + #[serde(rename = "border_color")] + pub border_colour: BorderColour, + pub card_back_id: String, // UUID + pub collector_number: String, + pub content_warning: Option, + pub digital: bool, + pub finishes: Vec, + #[serde(rename = "flavor_name")] + pub flavour_name: Option, + #[serde(rename = "flavor_text")] + pub flavour_text: Option, + pub frame_effects: Option>, + pub frame: Frame, + pub full_art: bool, + pub games: Vec, + pub highres_image: bool, + pub illustration_id: Option, // UUID + pub image_status: ImageStatus, + pub image_uris: ImageURIs, + pub oversized: bool, + pub prices: Prices, + pub printed_name: Option, + pub printed_text: Option, + pub printed_type_line: Option, + pub promo: bool, + pub promo_types: Vec, // TODO: Check what types exist - could be a enumeratable selection + pub purchase_uris: Option>, // FIXME + pub rarity: Rarity, + // TODO - the rest (and the purachase URIs above) } // https://scryfall.com/docs/api/cards#card-face-objects @@ -36,11 +98,14 @@ struct ScryfallCardFaceObject { pub artist: Option, pub artist_id: Option, // UUID pub cmc: Option, // TODO: Make this a proper Decimal - see "Little Girl" card for example of cmc of 0.5 + #[serde(rename = "color_identity")] pub colour_identity: Vec, + #[serde(rename = "color_indicator")] pub colour_indicator: Option>, + #[serde(rename = "colors")] pub colours: Option>, pub defence: Option, - pub edhrec_rank: Option, + // TODO: Complete } // https://scryfall.com/docs/api/cards#related-card-objects @@ -67,10 +132,207 @@ enum Colour { Red, #[serde(rename = "G")] Green, - //#[serde(rename = "C")] - // Colourless - // Just realised that colourless is just an empty vector... probably need a custom deserialiser, - // or just handle it at a higher level... probably just that - the caller/user can figure it out +} + +#[derive(Deserialize, Debug)] +enum Legality { + #[serde(rename = "legal")] + Legal, + #[serde(rename = "not_legal")] + NotLegal, + #[serde(rename = "banned")] + Banned, + #[serde(rename = "restricted")] + Restricted, +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +struct FormatLegalities { + standard: Legality, + future: Legality, + historic: Legality, + timeless: Legality, + gladiator: Legality, + pioneer: Legality, + explorer: Legality, + modern: Legality, + legacy: Legality, + pauper: Legality, + vintage: Legality, + penny: Legality, + commander: Legality, + oathbreaker: Legality, + standardbrawl: Legality, + brawl: Legality, + alchemy: Legality, + paupercommander: Legality, + duel: Legality, + oldschool: Legality, + premodern: Legality, + predh: Legality +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +enum BorderColour { + #[serde(rename = "black")] + Black, + #[serde(rename = "white")] + White, + #[serde(rename = "borderless")] + Borderless, + #[serde(rename = "yellow")] + Yellow, + #[serde(rename = "silver")] + Silver, + #[serde(rename = "gold")] + Gold +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +enum Finish { + #[serde(rename = "foil")] + Foil, + #[serde(rename = "nonfoil")] + NonFoil, + #[serde(rename = "etched")] + Etched +} + +// https://scryfall.com/docs/api/frames#frames +// This is probably dumb... +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +enum Frame { + #[serde(rename = "1993")] + NinetyThree, + #[serde(rename = "1997")] + NinetySeven, + #[serde(rename = "2003")] + OhThree, + #[serde(rename = "2015")] + OhFifteen, + #[serde(rename = "future")] + Future +} + +// https://scryfall.com/docs/api/frames#frame-effects +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +enum FrameEffect { + #[serde(rename = "legendary")] + Legendary, + #[serde(rename = "miracle")] + Miracle, + #[serde(rename = "enchantment")] + Enchantment, + #[serde(rename = "draft")] + Draft, + #[serde(rename = "devoid")] + Devoid, + #[serde(rename = "tombstone")] + Tombstone, + #[serde(rename = "colorshifted")] + Colourshifted, + #[serde(rename = "inverted")] + Inverted, + #[serde(rename = "sunmoondfc")] + SunMoonDFC, + #[serde(rename = "compasslanddfc")] + CompassLandDFC, + #[serde(rename = "originpwdfc")] + OriginPwDFC, + #[serde(rename = "mooneldrazidfc")] + MoonEldraziDFC, + #[serde(rename = "waxingandwaningmoondfc")] + WaxingAndWaningMoonDFC, + #[serde(rename = "showcase")] + Showcase, + #[serde(rename = "extendedart")] + ExtendedArt, + #[serde(rename = "companion")] + Companion, + #[serde(rename = "etched")] + Etched, + #[serde(rename = "snow")] + Snow, + #[serde(rename = "lesson")] + Lesson, + #[serde(rename = "shatteredglass")] + ShatteredGlass, + #[serde(rename = "convertdfc")] + ConvertDFC, + #[serde(rename = "fandfc")] + FanDFC, + #[serde(rename = "upsidedowndfc")] + UpsideDownDFC, + #[serde(rename = "spree")] + Spree +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +enum Game { + #[serde(rename = "paper")] + Paper, + #[serde(rename = "mtgo")] + MTGO, + #[serde(rename = "arena")] + Arena +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +enum ImageStatus { + #[serde(rename = "missing")] + Missing, + #[serde(rename = "placeholder")] + Placeholder, + #[serde(rename = "lowres")] + LowResolution, + #[serde(rename = "highres_scan")] + HighResolutionScan +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +struct ImageURIs { + png: Option, + border_crop: Option, + art_crop: Option, + large: Option, + normal: Option, + small: Option +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +struct Prices { + usd: Option, // TODO Convert to f64? + usd_foil: Option, + usd_etched: Option, + eur: Option, + eur_foil: Option, + tix: Option +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +enum Rarity { + #[serde(rename = "common")] + Common, + #[serde(rename = "uncommon")] + Uncommon, + #[serde(rename = "rare")] + Rare, + #[serde(rename = "special")] + Special, + #[serde(rename = "mythic")] + Mythic, + #[serde(rename = "bonus")] + Bonus } #[cfg(test)] @@ -88,4 +350,24 @@ mod tests { let nissa: ScryfallCard = serde_json::from_str(&fc).unwrap(); println!("{:#?}", nissa); } + + #[test] + fn deserialise_black_lotus() { + let mut f = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + f.push("test_files/black_lotus.json"); + assert!(f.exists()); + let fc = fs::read_to_string(f).unwrap(); + let bl: ScryfallCard = serde_json::from_str(&fc).unwrap(); + println!("{:#?}", bl); + } + + #[test] + fn deserialise_little_girl() { + let mut f = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + f.push("test_files/little_girl.json"); + assert!(f.exists()); + let fc = fs::read_to_string(f).unwrap(); + let lg: ScryfallCard = serde_json::from_str(&fc).unwrap(); + println!("{:#?}", lg); + } } diff --git a/scryfall_deser/test_files/black_lotus.json b/scryfall_deser/test_files/black_lotus.json new file mode 100644 index 0000000..8b7852a --- /dev/null +++ b/scryfall_deser/test_files/black_lotus.json @@ -0,0 +1,117 @@ +{ + "object": "card", + "id": "f5d24a5b-c950-4fd9-99e6-a4b979d915b3", + "oracle_id": "5089ec1a-f881-4d55-af14-5d996171203b", + "multiverse_ids": [], + "name": "Black Lotus", + "lang": "en", + "released_at": "2023-12-08", + "uri": "https://api.scryfall.com/cards/f5d24a5b-c950-4fd9-99e6-a4b979d915b3", + "scryfall_uri": "https://scryfall.com/card/ovnt/2023NA/black-lotus?utm_source=api", + "layout": "normal", + "highres_image": false, + "image_status": "lowres", + "image_uris": { + "small": "https://cards.scryfall.io/small/front/f/5/f5d24a5b-c950-4fd9-99e6-a4b979d915b3.jpg?1738105453", + "normal": "https://cards.scryfall.io/normal/front/f/5/f5d24a5b-c950-4fd9-99e6-a4b979d915b3.jpg?1738105453", + "large": "https://cards.scryfall.io/large/front/f/5/f5d24a5b-c950-4fd9-99e6-a4b979d915b3.jpg?1738105453", + "png": "https://cards.scryfall.io/png/front/f/5/f5d24a5b-c950-4fd9-99e6-a4b979d915b3.png?1738105453", + "art_crop": "https://cards.scryfall.io/art_crop/front/f/5/f5d24a5b-c950-4fd9-99e6-a4b979d915b3.jpg?1738105453", + "border_crop": "https://cards.scryfall.io/border_crop/front/f/5/f5d24a5b-c950-4fd9-99e6-a4b979d915b3.jpg?1738105453" + }, + "mana_cost": "{0}", + "cmc": 0, + "type_line": "Artifact", + "oracle_text": "{T}, Sacrifice Black Lotus: Add three mana of any one color.", + "colors": [], + "color_identity": [], + "keywords": [], + "produced_mana": [ + "B", + "G", + "R", + "U", + "W" + ], + "legalities": { + "standard": "not_legal", + "future": "not_legal", + "historic": "not_legal", + "timeless": "not_legal", + "gladiator": "not_legal", + "pioneer": "not_legal", + "explorer": "not_legal", + "modern": "not_legal", + "legacy": "banned", + "pauper": "not_legal", + "vintage": "restricted", + "penny": "not_legal", + "commander": "banned", + "oathbreaker": "banned", + "standardbrawl": "not_legal", + "brawl": "not_legal", + "alchemy": "not_legal", + "paupercommander": "not_legal", + "duel": "banned", + "oldschool": "not_legal", + "premodern": "not_legal", + "predh": "banned" + }, + "games": [ + "paper" + ], + "reserved": true, + "foil": false, + "nonfoil": true, + "finishes": [ + "nonfoil" + ], + "oversized": true, + "promo": false, + "reprint": true, + "variation": false, + "set_id": "c6a6b61b-143a-43f2-b74d-b140f3d93490", + "set": "ovnt", + "set_name": "Vintage Championship", + "set_type": "memorabilia", + "set_uri": "https://api.scryfall.com/sets/c6a6b61b-143a-43f2-b74d-b140f3d93490", + "set_search_uri": "https://api.scryfall.com/cards/search?order=set&q=e%3Aovnt&unique=prints", + "scryfall_set_uri": "https://scryfall.com/sets/ovnt?utm_source=api", + "rulings_uri": "https://api.scryfall.com/cards/f5d24a5b-c950-4fd9-99e6-a4b979d915b3/rulings", + "prints_search_uri": "https://api.scryfall.com/cards/search?order=released&q=oracleid%3A5089ec1a-f881-4d55-af14-5d996171203b&unique=prints", + "collector_number": "2023NA", + "digital": false, + "rarity": "special", + "flavor_text": "2023 North America\nVintage Championship", + "card_back_id": "0aeebaf5-8c7d-4636-9e82-8c27447861f7", + "artist": "Scott M. Fischer", + "artist_ids": [ + "23b0cf43-3e43-44c6-8329-96446eca5bce" + ], + "illustration_id": "4bc3f69f-66b6-4a6f-8b55-09df0ea4cb89", + "border_color": "black", + "frame": "2015", + "security_stamp": "oval", + "full_art": false, + "textless": false, + "booster": false, + "story_spotlight": false, + "prices": { + "usd": null, + "usd_foil": null, + "usd_etched": null, + "eur": null, + "eur_foil": null, + "tix": null + }, + "related_uris": { + "tcgplayer_infinite_articles": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&trafcat=infinite&u=https%3A%2F%2Finfinite.tcgplayer.com%2Fsearch%3FcontentMode%3Darticle%26game%3Dmagic%26q%3DBlack%2BLotus", + "tcgplayer_infinite_decks": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&trafcat=infinite&u=https%3A%2F%2Finfinite.tcgplayer.com%2Fsearch%3FcontentMode%3Ddeck%26game%3Dmagic%26q%3DBlack%2BLotus", + "edhrec": "https://edhrec.com/route/?cc=Black+Lotus" + }, + "purchase_uris": { + "tcgplayer": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&u=https%3A%2F%2Fwww.tcgplayer.com%2Fsearch%2Fmagic%2Fproduct%3FproductLineName%3Dmagic%26q%3DBlack%2BLotus%26view%3Dgrid", + "cardmarket": "https://www.cardmarket.com/en/Magic/Products/Search?referrer=scryfall&searchString=Black+Lotus&utm_campaign=card_prices&utm_medium=text&utm_source=scryfall", + "cardhoarder": "https://www.cardhoarder.com/cards?affiliate_id=scryfall&data%5Bsearch%5D=Black+Lotus&ref=card-profile&utm_campaign=affiliate&utm_medium=card&utm_source=scryfall" + } +} diff --git a/scryfall_deser/test_files/little_girl.json b/scryfall_deser/test_files/little_girl.json new file mode 100644 index 0000000..8b09a53 --- /dev/null +++ b/scryfall_deser/test_files/little_girl.json @@ -0,0 +1,122 @@ +{ + "object": "card", + "id": "90f17b85-a866-48e8-aae0-55330109550e", + "oracle_id": "b0d1c34c-30f1-4c07-9527-38b49231eb9f", + "multiverse_ids": [ + 74257 + ], + "tcgplayer_id": 37883, + "cardmarket_id": 14779, + "name": "Little Girl", + "lang": "en", + "released_at": "2004-11-19", + "uri": "https://api.scryfall.com/cards/90f17b85-a866-48e8-aae0-55330109550e", + "scryfall_uri": "https://scryfall.com/card/unh/16/little-girl?utm_source=api", + "layout": "normal", + "highres_image": true, + "image_status": "highres_scan", + "image_uris": { + "small": "https://cards.scryfall.io/small/front/9/0/90f17b85-a866-48e8-aae0-55330109550e.jpg?1562488879", + "normal": "https://cards.scryfall.io/normal/front/9/0/90f17b85-a866-48e8-aae0-55330109550e.jpg?1562488879", + "large": "https://cards.scryfall.io/large/front/9/0/90f17b85-a866-48e8-aae0-55330109550e.jpg?1562488879", + "png": "https://cards.scryfall.io/png/front/9/0/90f17b85-a866-48e8-aae0-55330109550e.png?1562488879", + "art_crop": "https://cards.scryfall.io/art_crop/front/9/0/90f17b85-a866-48e8-aae0-55330109550e.jpg?1562488879", + "border_crop": "https://cards.scryfall.io/border_crop/front/9/0/90f17b85-a866-48e8-aae0-55330109550e.jpg?1562488879" + }, + "mana_cost": "{HW}", + "cmc": 0.5, + "type_line": "Creature — Human Child", + "oracle_text": "", + "power": ".5", + "toughness": ".5", + "colors": [ + "W" + ], + "color_identity": [ + "W" + ], + "keywords": [], + "legalities": { + "standard": "not_legal", + "future": "not_legal", + "historic": "not_legal", + "timeless": "not_legal", + "gladiator": "not_legal", + "pioneer": "not_legal", + "explorer": "not_legal", + "modern": "not_legal", + "legacy": "not_legal", + "pauper": "not_legal", + "vintage": "not_legal", + "penny": "not_legal", + "commander": "not_legal", + "oathbreaker": "not_legal", + "standardbrawl": "not_legal", + "brawl": "not_legal", + "alchemy": "not_legal", + "paupercommander": "not_legal", + "duel": "not_legal", + "oldschool": "not_legal", + "premodern": "not_legal", + "predh": "not_legal" + }, + "games": [ + "paper" + ], + "reserved": false, + "foil": true, + "nonfoil": true, + "finishes": [ + "nonfoil", + "foil" + ], + "oversized": false, + "promo": false, + "reprint": false, + "variation": false, + "set_id": "4c8bc76a-05a5-43db-aaf0-34deb347b871", + "set": "unh", + "set_name": "Unhinged", + "set_type": "funny", + "set_uri": "https://api.scryfall.com/sets/4c8bc76a-05a5-43db-aaf0-34deb347b871", + "set_search_uri": "https://api.scryfall.com/cards/search?order=set&q=e%3Aunh&unique=prints", + "scryfall_set_uri": "https://scryfall.com/sets/unh?utm_source=api", + "rulings_uri": "https://api.scryfall.com/cards/90f17b85-a866-48e8-aae0-55330109550e/rulings", + "prints_search_uri": "https://api.scryfall.com/cards/search?order=released&q=oracleid%3Ab0d1c34c-30f1-4c07-9527-38b49231eb9f&unique=prints", + "collector_number": "16", + "digital": false, + "rarity": "common", + "flavor_text": "In the future, she may be a distinguished leader, a great scholar, or a decorated hero. These days all she does is pee the bed.", + "card_back_id": "0aeebaf5-8c7d-4636-9e82-8c27447861f7", + "artist": "Rebecca Guay", + "artist_ids": [ + "f8f662fa-d597-46a3-afb2-91d6e13243e2" + ], + "illustration_id": "fa376327-cd38-4f28-a8ab-7f61cf35a455", + "border_color": "silver", + "frame": "2003", + "full_art": false, + "textless": false, + "booster": true, + "story_spotlight": false, + "prices": { + "usd": "0.29", + "usd_foil": "16.25", + "usd_etched": null, + "eur": "0.29", + "eur_foil": "12.69", + "tix": null + }, + "related_uris": { + "gatherer": "https://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=74257&printed=false", + "tcgplayer_infinite_articles": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&trafcat=infinite&u=https%3A%2F%2Finfinite.tcgplayer.com%2Fsearch%3FcontentMode%3Darticle%26game%3Dmagic%26q%3DLittle%2BGirl", + "tcgplayer_infinite_decks": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&trafcat=infinite&u=https%3A%2F%2Finfinite.tcgplayer.com%2Fsearch%3FcontentMode%3Ddeck%26game%3Dmagic%26q%3DLittle%2BGirl", + "edhrec": "https://edhrec.com/route/?cc=Little+Girl" + }, + "purchase_uris": { + "tcgplayer": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&u=https%3A%2F%2Fwww.tcgplayer.com%2Fproduct%2F37883%3Fpage%3D1", + "cardmarket": "https://www.cardmarket.com/en/Magic/Products/Singles/Unhinged/Little-Girl?referrer=scryfall&utm_campaign=card_prices&utm_medium=text&utm_source=scryfall", + "cardhoarder": "https://www.cardhoarder.com/cards?affiliate_id=scryfall&data%5Bsearch%5D=Little+Girl&ref=card-profile&utm_campaign=affiliate&utm_medium=card&utm_source=scryfall" + } +} +