Compare commits

...

3 Commits

Author SHA1 Message Date
a14d2dcb75 Don't look at that awful test...
I have tested it on the Oracle cards - so should like... mostly work for cards

Except for, I think, mistakes? in the Scryfall database.
2025-02-05 00:13:48 +00:00
66b22c1c30 Added a very poorly written test thingy
Started fixing a couple of mistakes
2025-02-04 23:45:53 +00:00
993ac46954 Got most of the fields I think 2025-02-04 23:21:23 +00:00
3 changed files with 134 additions and 30 deletions

View File

@@ -2,6 +2,7 @@
# will have compiled files and executables
debug/
target/
test_files/all-cards.json
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html

View File

@@ -6,6 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.39", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.138"
uuid = { version = "1.12.1", features = ["v4", "serde"] }

View File

@@ -1,5 +1,7 @@
use serde::Deserialize;
use serde_json::Value;
use uuid::Uuid;
use chrono::NaiveDate;
// Info from here:
// 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
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")]
pub colour_identity: Vec<Colour>,
pub colour_identity: Option<Vec<Colour>>,
#[serde(rename = "color_indicator")]
pub colour_indicator: Option<Vec<Colour>>,
#[serde(rename = "colors")]
@@ -59,11 +61,11 @@ struct ScryfallCard {
// https://scryfall.com/docs/api/cards#print-fields
pub artist: Option<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,
#[serde(rename = "border_color")]
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 content_warning: Option<bool>,
pub digital: bool,
@@ -79,17 +81,39 @@ struct ScryfallCard {
pub highres_image: bool,
pub illustration_id: Option<Uuid>,
pub image_status: ImageStatus,
pub image_uris: ImageURIs,
pub image_uris: Option<ImageURIs>,
pub oversized: bool,
pub prices: Prices,
pub printed_name: Option<String>,
pub printed_text: Option<String>,
pub printed_type_line: Option<String>,
pub promo: bool,
pub promo_types: Option<Vec<String>>, // TODO: Check what types exist - could be a enumeratable selection
//pub purchase_uris: Option<Vec<bool>>, // FIXME
pub promo_types: Option<Vec<String>>, // TODO enum?
pub purchase_uris: Option<PurchaseUris>,
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
@@ -100,13 +124,29 @@ struct ScryfallCardFaceObject {
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
#[serde(rename = "color_identity")]
pub colour_identity: Vec<Colour>,
pub colour_identity: Option<Vec<Colour>>,
#[serde(rename = "color_indicator")]
pub colour_indicator: Option<Vec<Colour>>,
#[serde(rename = "colors")]
pub colours: Option<Vec<Colour>>,
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
@@ -115,7 +155,7 @@ struct ScryfallCardFaceObject {
struct ScryfallRelatedCardObject {
pub id: Uuid,
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 type_line: String,
pub uri: String // URI
@@ -133,6 +173,10 @@ enum Colour {
Red,
#[serde(rename = "G")]
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)]
@@ -214,7 +258,7 @@ enum Frame {
#[serde(rename = "2003")]
OhThree,
#[serde(rename = "2015")]
OhFifteen,
Fifteen,
#[serde(rename = "future")]
Future
}
@@ -270,7 +314,9 @@ enum FrameEffect {
#[serde(rename = "upsidedowndfc")]
UpsideDownDFC,
#[serde(rename = "spree")]
Spree
Spree,
#[serde(rename = "fullart")]
FullArt
}
#[allow(dead_code)]
@@ -281,7 +327,11 @@ enum Game {
#[serde(rename = "mtgo")]
MTGO,
#[serde(rename = "arena")]
Arena
Arena,
#[serde(rename = "astral")]
Astral,
#[serde(rename = "sega")]
Sega
}
#[allow(dead_code)]
@@ -336,11 +386,50 @@ enum Rarity {
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)]
mod tests {
use super::*;
use std::path::PathBuf;
use std::fs;
use std::io::{BufRead, BufReader};
#[test]
fn deserialise_nissa() {
@@ -348,8 +437,7 @@ mod tests {
f.push("test_files/nissa.json");
assert!(f.exists());
let fc = fs::read_to_string(f).unwrap();
let nissa: ScryfallCard = serde_json::from_str(&fc).unwrap();
println!("{:#?}", nissa);
let _nissa: ScryfallCard = serde_json::from_str(&fc).unwrap();
}
#[test]
@@ -358,8 +446,7 @@ mod tests {
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);
let _bl: ScryfallCard = serde_json::from_str(&fc).unwrap();
}
#[test]
@@ -368,22 +455,37 @@ mod tests {
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);
let _lg: ScryfallCard = serde_json::from_str(&fc).unwrap();
}
#[test]
#[ignore]
fn try_deserialise_all_cards() {
// TODO Actually write this funtion.
// The idea will be to run over one of the Scryfall dumps of all the cards so I can be
// confident I've got things working.
// I noticed that Scryfall very helpfully provides their dumps with 1 card per line.
// So using something like the link below should hopefully mean I don't have to load in the
// whole 2+GB file all at once
// https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines
// I think that's what it should do at least. I bet it'll still take a while tho.
// If that doesn't work - I should try somehow use the serde from_reader function
// I don't 100% know how I'll use that though.
fn try_deserialise_all_cards_line_by_line() {
// This function is uuuuuuugly and I'm sure a terrible way to go about things
let mut f = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
f.push("test_files/all-cards.json");
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");
let ac = fs::File::open(f).unwrap();
let reader = BufReader::new(ac);
for line in reader.lines().skip(1) {
let mut line = line.unwrap();
let c = line.pop().unwrap();
// 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(_) => (),
}
}
}
}