use serde::{Deserialize, Serialize}; use reqwest; use prometheus::{GaugeVec, Opts}; use std::{env, fs::File, fs::OpenOptions, io::BufReader, collections::HashMap}; use crate::util; lazy_static::lazy_static! { pub static ref CARD_VALUES: GaugeVec = GaugeVec::new( Opts::new("card_value", "The value of cards"), &["name"] ).unwrap(); } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Card { name: String, prices: HashMap>, } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CardFromFile { name: String, count: usize, usd_value: Option, } #[derive(Serialize, Deserialize, Debug)] pub struct CardFile { pub cards: Vec, } pub async fn process_card(card_from_file: &mut CardFromFile) -> Result<(), Box> { let request_url = format!( "https://api.scryfall.com/cards/named?exact={}", card_from_file.name ); let response = reqwest::get(&request_url).await?; let card: Card = response.json().await?; if let Some(price) = card.prices.get("usd") { if let Some(price_str) = price { card_from_file.usd_value = Some(price_str.clone()); let value = price_str.parse::().unwrap(); CARD_VALUES.with_label_values(&[&card_from_file.name]).set(value); } else { card_from_file.usd_value = Some("0.0".to_string()); CARD_VALUES.with_label_values(&[&card_from_file.name]).set(0.0); } } Ok(()) } pub fn get_data_update_interval() -> Result> { let update_interval = dotenv::var("UPDATE_INTERVAL") .map_err(|_| "UPDATE_INTERVAL is not defined in the .env file")? .parse::() .map_err(|_| "UPDATE_INTERVAL is not a valid number")?; Ok(update_interval * 3600) } pub fn process_export_data(cards: &mut Vec) { cards.sort_by(|a, b| b.usd_value.partial_cmp(&a.usd_value).unwrap()); let top_10_cards = &cards[..10]; for card in top_10_cards { if let Some(price_str) = &card.usd_value { let value = price_str.parse::().unwrap(); CARD_VALUES.with_label_values(&[&card.name]).set(value); } } } pub async fn process_cards(interval: &mut tokio::time::Interval) -> Result<(), Box> { let args: Vec = env::args().collect(); if args.len() < 2 { eprintln!("Please provide the path to the JSON file as an argument."); return Ok(()); } let file_path = &args[1]; let file = File::open(file_path)?; let reader = BufReader::new(file); let mut cards_data: CardFile = serde_json::from_reader(reader)?; let pb = util::setup_progress_bar(cards_data.cards.len() as u64); for card_from_file in &mut cards_data.cards { process_card(card_from_file).await?; pb.inc(1); interval.tick().await; } process_export_data(&mut cards_data.cards); pb.finish_with_message("Completed!"); let file = OpenOptions::new() .write(true) .truncate(true) .open(file_path)?; serde_json::to_writer_pretty(file, &cards_data)?; Ok(()) }