ygoprodb 0.1.0

Extracts card/cardset info from YGOProDB, then storing into a JSON file.
extern crate clap;
extern crate serde;
extern crate tokio;
extern crate bytes;
extern crate reqwest;
extern crate serde_json;

use clap::App;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let matches = App::new("ygoprodb-extractor")
        .version("1.0")
        .author("whoastonic <kingstonicthe1@gmail.com>")
        .about("Extract card/cardset data from YGOPro, then insert into a JSON file.")
        .arg(clap::Arg::with_name("output")
            .short("o")
            .long("output")
            .value_name("OUTPUT")
            .help("Sets output file.")
            .takes_value(true))
        .aliases(&["ygopro"])
        .get_matches();

    let output = matches.value_of("output").unwrap_or("ygoprodb.json");

    let ygoprodb_data = request::rcards().await?;

    files::write_to_json(ygoprodb_data.as_bytes(), &output)
        .expect("cannot write to JSON file.");
        
    Ok(())
}

pub mod request {
    const CARD_ENDPOINT: &'static str = "https://db.ygoprodeck.com/api/v7/cardinfo.php?misc=yes";
    const SETS_ENDPOINT: &'static str = "https://db.ygoprodeck.com/api/v7/cardsets.php";

    pub mod schemes {
        #[derive(serde::Deserialize, serde::Serialize)]
        pub struct ToJSON {
            pub sets: Vec<SetsData>,
            pub cards: Vec<CardData>
        }

        #[derive(Debug, serde::Deserialize)]
        pub struct CardEndpointResponse {
            pub data: Vec<CardData>
        }

        #[derive(serde::Deserialize, serde::Serialize)]
        pub struct SetsData {        
            pub set_name: String,
            pub set_code: String,
            pub num_of_cards: usize,
            pub tcg_date: Option<String>,
        }

        #[derive(Debug, serde::Deserialize, serde::Serialize)]
        pub struct CardData {
            pub id: usize,
            pub name: String,
            pub r#type: String,
            pub desc: String,
            pub race: String,
            pub archetype: Option<String>,
            pub card_sets: Option<Vec<CardsSet>>,
            pub card_images: Vec<CardsImage>,
            pub card_prices: Option<Vec<CardsPrice>>,
            pub misc_info: Option<Vec<MiscInfo>>,
        }

        #[derive(Debug, serde::Deserialize, serde::Serialize)]
        pub struct MiscInfo {
            pub beta_name: Option<String>,
            pub views: isize,
            pub viewsweek: isize,
            pub upvotes: isize,
            pub downvotes: isize,
            pub formats: Vec<String>,
            pub tcg_date: Option<String>,
            pub ocg_date: Option<String>,
            pub konami_id: Option<isize>,
            pub has_effect: Option<isize>,
        }

        #[derive(Debug, serde::Deserialize, serde::Serialize)]
        pub struct CardsImage {
            pub id: usize,
            pub image_url: String,
            pub image_url_small: String,
        }

        #[derive(Debug, serde::Deserialize, serde::Serialize)]
        pub struct CardsPrice {
            tcgplayer_price: String,
            ebay_price: String,
            amazon_price: String,
            coolstuffinc_price: String,
            cardmarket_price: String,
        }

        #[derive(Debug, serde::Deserialize, serde::Serialize)]
        pub struct CardsSet {
            pub set_name: String,
            pub set_code: String,
            pub set_rarity: String,
            pub set_rarity_code: String,
            pub set_price: String,
        }
    }

    pub async fn rcards () -> reqwest::Result<String> {
        let cards_bytes: schemes::CardEndpointResponse = reqwest::get(CARD_ENDPOINT)
            .await?
            .json()
            .await?;

        let sets_bytes: Vec<schemes::SetsData> = reqwest::get(SETS_ENDPOINT)
            .await?
            .json()
            .await?;
    
        let to_json = serde_json::to_string(&schemes::ToJSON {
            sets: sets_bytes,
            cards: cards_bytes.data,
        }).expect("cannot parse to JSON");

        Ok(to_json)
    }

}

pub mod files {
    use std::fs::{OpenOptions};
    use std::io::{self, Write};

    pub fn write_to_json (data: &[u8], file: &str) -> io::Result<()> {
        match OpenOptions::new()
            .write(true)
            .create(true)
            .open(file)
        {
            Ok(mut file) => Ok(file.write_all(data).expect("cannot write to file.")),
            Err(error) => Err(error)
        }
    }
}