//! Client to download data from malie.io use anyhow::{Context, Result, anyhow}; use camino::Utf8PathBuf; use tokio::fs::File; use tokio_stream::StreamExt; use tokio_util::io::StreamReader; use tracing::debug; use super::models::Index; use crate::directories::data_cache_directory; /// Client to download data from mallie.io pub struct Client { client: reqwest::Client, data_cache_directory: Utf8PathBuf, } const TCGL_BASE_URL: &str = "https://cdn.malie.io/file/malie-io/tcgl/export"; impl Client { /// Create a new `Client` pub async fn new() -> Result { Ok(Self { client: reqwest::Client::new(), data_cache_directory: data_cache_directory().await?, }) } pub async fn download_all_data(&self) -> Result<()> { self.download_tcgl_index_json().await?; let index = self.load_tcgl_index().await?; println!("{index:?}"); Ok(()) } pub async fn download_tcgl_index_json(&self) -> Result<()> { let file_path = self.data_cache_directory.join("tcgl_index.json"); let url = format!("{TCGL_BASE_URL}/index.json"); self.download_if_not_exists(file_path, &url).await?; Ok(()) } async fn load_tcgl_index(&self) -> Result { let file_path = self.data_cache_directory.join("tcgl_index.json"); let index = tokio::fs::read_to_string(&file_path) .await .with_context(|| format!("Failed to read {file_path}"))?; let index: Index = serde_json::from_str(&index).with_context(|| format!("Couldn't parse {file_path}"))?; Ok(index) } async fn download_if_not_exists(&self, file_path: Utf8PathBuf, url: &str) -> Result<()> { if let Ok(true) = tokio::fs::try_exists(&file_path).await { debug!("Found {}, skipping download", &file_path); return Ok(()); } let response = self.client.get(url).send().await?; if !response.status().is_success() { return Err(anyhow!( "Error {} when downloading: {}", response.status(), url )); } let mut file = File::create_new(&file_path) .await .with_context(|| format!("Couldn't create file {file_path}"))?; tokio::io::copy_buf( &mut StreamReader::new( response .bytes_stream() .map(|result| result.map_err(std::io::Error::other)), ), &mut file, ) .await .with_context(|| format!("While writing to file {file_path}"))?; file.sync_all().await?; Ok(()) } }