|
|
|
@@ -1,5 +1,7 @@
|
|
|
|
use serde::Deserialize;
|
|
|
|
use serde::Deserialize;
|
|
|
|
|
|
|
|
use serde_json::Value;
|
|
|
|
use uuid::Uuid;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
use chrono::NaiveDate;
|
|
|
|
|
|
|
|
|
|
|
|
// Info from here:
|
|
|
|
// Info from here:
|
|
|
|
// https://scryfall.com/docs/api/cards
|
|
|
|
// https://scryfall.com/docs/api/cards
|
|
|
|
@@ -33,7 +35,7 @@ struct ScryfallCard {
|
|
|
|
// NOTE: Probably a bad idea to rename color -> colour just for the sake
|
|
|
|
// NOTE: Probably a bad idea to rename color -> colour just for the sake
|
|
|
|
pub cmc: Option<f64>, // TODO: Make this a proper Decimal - see "Little Girl" card for example of cmc of 0.5
|
|
|
|
pub cmc: Option<f64>, // TODO: Make this a proper Decimal - see "Little Girl" card for example of cmc of 0.5
|
|
|
|
#[serde(rename = "color_identity")]
|
|
|
|
#[serde(rename = "color_identity")]
|
|
|
|
pub colour_identity: Vec<Colour>,
|
|
|
|
pub colour_identity: Option<Vec<Colour>>,
|
|
|
|
#[serde(rename = "color_indicator")]
|
|
|
|
#[serde(rename = "color_indicator")]
|
|
|
|
pub colour_indicator: Option<Vec<Colour>>,
|
|
|
|
pub colour_indicator: Option<Vec<Colour>>,
|
|
|
|
#[serde(rename = "colors")]
|
|
|
|
#[serde(rename = "colors")]
|
|
|
|
@@ -59,11 +61,11 @@ struct ScryfallCard {
|
|
|
|
// https://scryfall.com/docs/api/cards#print-fields
|
|
|
|
// https://scryfall.com/docs/api/cards#print-fields
|
|
|
|
pub artist: Option<String>,
|
|
|
|
pub artist: Option<String>,
|
|
|
|
pub artist_ids: Option<Vec<String>>,
|
|
|
|
pub artist_ids: Option<Vec<String>>,
|
|
|
|
pub attraction_lights: Option<Vec<String>>, // TODO: I'm not actually sure what these look like - I should test
|
|
|
|
pub attraction_lights: Option<Vec<u8>>,
|
|
|
|
pub booster: bool,
|
|
|
|
pub booster: bool,
|
|
|
|
#[serde(rename = "border_color")]
|
|
|
|
#[serde(rename = "border_color")]
|
|
|
|
pub border_colour: BorderColour,
|
|
|
|
pub border_colour: BorderColour,
|
|
|
|
pub card_back_id: Uuid,
|
|
|
|
pub card_back_id: Option<Uuid>, // Scryfall docs says this should not be null, but ZHS Growing Rites of Itlimoc seems to not have one... maybe it's the back side?
|
|
|
|
pub collector_number: String,
|
|
|
|
pub collector_number: String,
|
|
|
|
pub content_warning: Option<bool>,
|
|
|
|
pub content_warning: Option<bool>,
|
|
|
|
pub digital: bool,
|
|
|
|
pub digital: bool,
|
|
|
|
@@ -79,17 +81,39 @@ struct ScryfallCard {
|
|
|
|
pub highres_image: bool,
|
|
|
|
pub highres_image: bool,
|
|
|
|
pub illustration_id: Option<Uuid>,
|
|
|
|
pub illustration_id: Option<Uuid>,
|
|
|
|
pub image_status: ImageStatus,
|
|
|
|
pub image_status: ImageStatus,
|
|
|
|
pub image_uris: ImageURIs,
|
|
|
|
pub image_uris: Option<ImageURIs>,
|
|
|
|
pub oversized: bool,
|
|
|
|
pub oversized: bool,
|
|
|
|
pub prices: Prices,
|
|
|
|
pub prices: Prices,
|
|
|
|
pub printed_name: Option<String>,
|
|
|
|
pub printed_name: Option<String>,
|
|
|
|
pub printed_text: Option<String>,
|
|
|
|
pub printed_text: Option<String>,
|
|
|
|
pub printed_type_line: Option<String>,
|
|
|
|
pub printed_type_line: Option<String>,
|
|
|
|
pub promo: bool,
|
|
|
|
pub promo: bool,
|
|
|
|
pub promo_types: Option<Vec<String>>, // TODO: Check what types exist - could be a enumeratable selection
|
|
|
|
pub promo_types: Option<Vec<String>>, // TODO enum?
|
|
|
|
//pub purchase_uris: Option<Vec<bool>>, // FIXME
|
|
|
|
pub purchase_uris: Option<PurchaseUris>,
|
|
|
|
pub rarity: Rarity,
|
|
|
|
pub rarity: Rarity,
|
|
|
|
// TODO - the rest (and the purachase URIs above)
|
|
|
|
pub related_uris: Value, // TODO: - list all the URIs? Maybe? Who cares?
|
|
|
|
|
|
|
|
pub released_at: NaiveDate,
|
|
|
|
|
|
|
|
pub reprint: bool,
|
|
|
|
|
|
|
|
pub scryfall_set_uri: String, // URI
|
|
|
|
|
|
|
|
pub set_name: String,
|
|
|
|
|
|
|
|
pub set_search_uri: String, // URI
|
|
|
|
|
|
|
|
pub set_type: String, // TODO: Enum?
|
|
|
|
|
|
|
|
pub set_uri: String, // URI
|
|
|
|
|
|
|
|
pub set: String,
|
|
|
|
|
|
|
|
pub set_id: Uuid,
|
|
|
|
|
|
|
|
pub story_spotlight: bool,
|
|
|
|
|
|
|
|
pub textless: bool,
|
|
|
|
|
|
|
|
pub variation: bool,
|
|
|
|
|
|
|
|
pub variation_of: Option<Uuid>,
|
|
|
|
|
|
|
|
pub security_stamp: Option<SecurityStamp>,
|
|
|
|
|
|
|
|
pub watermark: Option<String>,
|
|
|
|
|
|
|
|
#[serde(rename = "preview.previewed_at")]
|
|
|
|
|
|
|
|
pub preview_previewed_at: Option<NaiveDate>,
|
|
|
|
|
|
|
|
#[serde(rename = "preview.source_uri")]
|
|
|
|
|
|
|
|
pub preview_source_uri: Option<String>, // URI
|
|
|
|
|
|
|
|
#[serde(rename = "preview.source")]
|
|
|
|
|
|
|
|
pub preview_source: Option<String>
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// https://scryfall.com/docs/api/cards#card-face-objects
|
|
|
|
// https://scryfall.com/docs/api/cards#card-face-objects
|
|
|
|
@@ -100,13 +124,29 @@ struct ScryfallCardFaceObject {
|
|
|
|
pub artist_id: Option<Uuid>, // UUID
|
|
|
|
pub artist_id: Option<Uuid>, // UUID
|
|
|
|
pub cmc: Option<f64>, // TODO: Make this a proper Decimal - see "Little Girl" card for example of cmc of 0.5
|
|
|
|
pub cmc: Option<f64>, // TODO: Make this a proper Decimal - see "Little Girl" card for example of cmc of 0.5
|
|
|
|
#[serde(rename = "color_identity")]
|
|
|
|
#[serde(rename = "color_identity")]
|
|
|
|
pub colour_identity: Vec<Colour>,
|
|
|
|
pub colour_identity: Option<Vec<Colour>>,
|
|
|
|
#[serde(rename = "color_indicator")]
|
|
|
|
#[serde(rename = "color_indicator")]
|
|
|
|
pub colour_indicator: Option<Vec<Colour>>,
|
|
|
|
pub colour_indicator: Option<Vec<Colour>>,
|
|
|
|
#[serde(rename = "colors")]
|
|
|
|
#[serde(rename = "colors")]
|
|
|
|
pub colours: Option<Vec<Colour>>,
|
|
|
|
pub colours: Option<Vec<Colour>>,
|
|
|
|
pub defence: Option<String>,
|
|
|
|
pub defence: Option<String>,
|
|
|
|
// TODO: Complete
|
|
|
|
pub flavour_text: Option<String>,
|
|
|
|
|
|
|
|
pub illustration_id: Option<Uuid>,
|
|
|
|
|
|
|
|
pub image_uris: Option<ImageURIs>,
|
|
|
|
|
|
|
|
pub layout: Option<String>,
|
|
|
|
|
|
|
|
pub loyalty: Option<String>,
|
|
|
|
|
|
|
|
pub mana_cost: Option<String>,
|
|
|
|
|
|
|
|
pub name: String,
|
|
|
|
|
|
|
|
pub object: String,
|
|
|
|
|
|
|
|
pub oracle_id: Option<Uuid>,
|
|
|
|
|
|
|
|
pub oracle_text: Option<String>,
|
|
|
|
|
|
|
|
pub power: Option<String>,
|
|
|
|
|
|
|
|
pub printed_name: Option<String>,
|
|
|
|
|
|
|
|
pub printed_text: Option<String>,
|
|
|
|
|
|
|
|
pub printed_type_line: Option<String>,
|
|
|
|
|
|
|
|
pub toughness: Option<String>,
|
|
|
|
|
|
|
|
pub type_line: Option<String>,
|
|
|
|
|
|
|
|
pub watermark: Option<String>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// https://scryfall.com/docs/api/cards#related-card-objects
|
|
|
|
// https://scryfall.com/docs/api/cards#related-card-objects
|
|
|
|
@@ -115,7 +155,7 @@ struct ScryfallCardFaceObject {
|
|
|
|
struct ScryfallRelatedCardObject {
|
|
|
|
struct ScryfallRelatedCardObject {
|
|
|
|
pub id: Uuid,
|
|
|
|
pub id: Uuid,
|
|
|
|
pub object: String, // Always "related_card"
|
|
|
|
pub object: String, // Always "related_card"
|
|
|
|
pub component: String, // One of token, meld_part, meld_result, combo_piece
|
|
|
|
pub component: Component,
|
|
|
|
pub name: String,
|
|
|
|
pub name: String,
|
|
|
|
pub type_line: String,
|
|
|
|
pub type_line: String,
|
|
|
|
pub uri: String // URI
|
|
|
|
pub uri: String // URI
|
|
|
|
@@ -133,6 +173,10 @@ enum Colour {
|
|
|
|
Red,
|
|
|
|
Red,
|
|
|
|
#[serde(rename = "G")]
|
|
|
|
#[serde(rename = "G")]
|
|
|
|
Green,
|
|
|
|
Green,
|
|
|
|
|
|
|
|
#[serde(rename = "C")] // I don't think it's meant to work like this... but eh
|
|
|
|
|
|
|
|
Colourless,
|
|
|
|
|
|
|
|
#[serde(rename = "T")] // See "Sole Performer"
|
|
|
|
|
|
|
|
Tap
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
@@ -214,7 +258,7 @@ enum Frame {
|
|
|
|
#[serde(rename = "2003")]
|
|
|
|
#[serde(rename = "2003")]
|
|
|
|
OhThree,
|
|
|
|
OhThree,
|
|
|
|
#[serde(rename = "2015")]
|
|
|
|
#[serde(rename = "2015")]
|
|
|
|
OhFifteen,
|
|
|
|
Fifteen,
|
|
|
|
#[serde(rename = "future")]
|
|
|
|
#[serde(rename = "future")]
|
|
|
|
Future
|
|
|
|
Future
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -270,7 +314,9 @@ enum FrameEffect {
|
|
|
|
#[serde(rename = "upsidedowndfc")]
|
|
|
|
#[serde(rename = "upsidedowndfc")]
|
|
|
|
UpsideDownDFC,
|
|
|
|
UpsideDownDFC,
|
|
|
|
#[serde(rename = "spree")]
|
|
|
|
#[serde(rename = "spree")]
|
|
|
|
Spree
|
|
|
|
Spree,
|
|
|
|
|
|
|
|
#[serde(rename = "fullart")]
|
|
|
|
|
|
|
|
FullArt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
@@ -281,7 +327,11 @@ enum Game {
|
|
|
|
#[serde(rename = "mtgo")]
|
|
|
|
#[serde(rename = "mtgo")]
|
|
|
|
MTGO,
|
|
|
|
MTGO,
|
|
|
|
#[serde(rename = "arena")]
|
|
|
|
#[serde(rename = "arena")]
|
|
|
|
Arena
|
|
|
|
Arena,
|
|
|
|
|
|
|
|
#[serde(rename = "astral")]
|
|
|
|
|
|
|
|
Astral,
|
|
|
|
|
|
|
|
#[serde(rename = "sega")]
|
|
|
|
|
|
|
|
Sega
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
@@ -336,11 +386,50 @@ enum Rarity {
|
|
|
|
Bonus
|
|
|
|
Bonus
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
|
|
|
|
struct PurchaseUris {
|
|
|
|
|
|
|
|
tcgplayer: String, // Option?
|
|
|
|
|
|
|
|
cardmarket: String,
|
|
|
|
|
|
|
|
cardhoarder: String,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
|
|
|
|
enum SecurityStamp {
|
|
|
|
|
|
|
|
#[serde(rename = "oval")]
|
|
|
|
|
|
|
|
Oval,
|
|
|
|
|
|
|
|
#[serde(rename = "triangle")]
|
|
|
|
|
|
|
|
Triangle,
|
|
|
|
|
|
|
|
#[serde(rename = "acorn")]
|
|
|
|
|
|
|
|
Acorn,
|
|
|
|
|
|
|
|
#[serde(rename = "circle")]
|
|
|
|
|
|
|
|
Circle,
|
|
|
|
|
|
|
|
#[serde(rename = "arena")]
|
|
|
|
|
|
|
|
Arena,
|
|
|
|
|
|
|
|
#[serde(rename = "heart")]
|
|
|
|
|
|
|
|
Heart
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
|
|
|
|
enum Component {
|
|
|
|
|
|
|
|
#[serde(rename = "token")]
|
|
|
|
|
|
|
|
Token,
|
|
|
|
|
|
|
|
#[serde(rename = "meld_part")]
|
|
|
|
|
|
|
|
MeldPart,
|
|
|
|
|
|
|
|
#[serde(rename = "meld_result")]
|
|
|
|
|
|
|
|
MeldResult,
|
|
|
|
|
|
|
|
#[serde(rename = "combo_piece")]
|
|
|
|
|
|
|
|
ComboPiece,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use super::*;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::fs;
|
|
|
|
use std::fs;
|
|
|
|
|
|
|
|
use std::io::{BufRead, BufReader};
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
fn deserialise_nissa() {
|
|
|
|
fn deserialise_nissa() {
|
|
|
|
@@ -348,8 +437,7 @@ mod tests {
|
|
|
|
f.push("test_files/nissa.json");
|
|
|
|
f.push("test_files/nissa.json");
|
|
|
|
assert!(f.exists());
|
|
|
|
assert!(f.exists());
|
|
|
|
let fc = fs::read_to_string(f).unwrap();
|
|
|
|
let fc = fs::read_to_string(f).unwrap();
|
|
|
|
let nissa: ScryfallCard = serde_json::from_str(&fc).unwrap();
|
|
|
|
let _nissa: ScryfallCard = serde_json::from_str(&fc).unwrap();
|
|
|
|
println!("{:#?}", nissa);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
@@ -358,8 +446,7 @@ mod tests {
|
|
|
|
f.push("test_files/black_lotus.json");
|
|
|
|
f.push("test_files/black_lotus.json");
|
|
|
|
assert!(f.exists());
|
|
|
|
assert!(f.exists());
|
|
|
|
let fc = fs::read_to_string(f).unwrap();
|
|
|
|
let fc = fs::read_to_string(f).unwrap();
|
|
|
|
let bl: ScryfallCard = serde_json::from_str(&fc).unwrap();
|
|
|
|
let _bl: ScryfallCard = serde_json::from_str(&fc).unwrap();
|
|
|
|
println!("{:#?}", bl);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
@@ -368,22 +455,37 @@ mod tests {
|
|
|
|
f.push("test_files/little_girl.json");
|
|
|
|
f.push("test_files/little_girl.json");
|
|
|
|
assert!(f.exists());
|
|
|
|
assert!(f.exists());
|
|
|
|
let fc = fs::read_to_string(f).unwrap();
|
|
|
|
let fc = fs::read_to_string(f).unwrap();
|
|
|
|
let lg: ScryfallCard = serde_json::from_str(&fc).unwrap();
|
|
|
|
let _lg: ScryfallCard = serde_json::from_str(&fc).unwrap();
|
|
|
|
println!("{:#?}", lg);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
#[ignore]
|
|
|
|
fn try_deserialise_all_cards() {
|
|
|
|
fn try_deserialise_all_cards_line_by_line() {
|
|
|
|
// TODO Actually write this funtion.
|
|
|
|
// This function is uuuuuuugly and I'm sure a terrible way to go about things
|
|
|
|
// The idea will be to run over one of the Scryfall dumps of all the cards so I can be
|
|
|
|
let mut f = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
|
|
// confident I've got things working.
|
|
|
|
f.push("test_files/all-cards.json");
|
|
|
|
// I noticed that Scryfall very helpfully provides their dumps with 1 card per line.
|
|
|
|
assert!(f.exists(), "You need to download the all-cards-... file from Scryfall bulk data. Can be found here: https://scryfall.com/docs/api/bulk-data and rename to all-cards.json");
|
|
|
|
// So using something like the link below should hopefully mean I don't have to load in the
|
|
|
|
let ac = fs::File::open(f).unwrap();
|
|
|
|
// whole 2+GB file all at once
|
|
|
|
let reader = BufReader::new(ac);
|
|
|
|
// https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines
|
|
|
|
for line in reader.lines().skip(1) {
|
|
|
|
// I think that's what it should do at least. I bet it'll still take a while tho.
|
|
|
|
let mut line = line.unwrap();
|
|
|
|
// If that doesn't work - I should try somehow use the serde from_reader function
|
|
|
|
let c = line.pop().unwrap();
|
|
|
|
// I don't 100% know how I'll use that though.
|
|
|
|
// this is so dumb...
|
|
|
|
|
|
|
|
if c == '}' {
|
|
|
|
|
|
|
|
line.push('}');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if line.len() < 1 { continue };
|
|
|
|
|
|
|
|
let a_card: Result<ScryfallCard, serde_json::Error> = serde_json::from_str(&line.as_ref());
|
|
|
|
|
|
|
|
match a_card {
|
|
|
|
|
|
|
|
Err(error) => {
|
|
|
|
|
|
|
|
println!("{:#?}", line);
|
|
|
|
|
|
|
|
println!("{:#?}", error);
|
|
|
|
|
|
|
|
panic!();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Ok(_) => (),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|