diff --git a/Cargo.lock b/Cargo.lock index 6535f7c..f47b4e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1102,6 +1102,7 @@ dependencies = [ "chrono", "reqwest", "rss", + "serde", "serde_json", "tokio", "warp", @@ -1163,6 +1164,20 @@ name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" diff --git a/Cargo.toml b/Cargo.toml index 835b4ad..d209c53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ lto = true [dependencies] rss = "1.9.0" warp = "0.2.5" -reqwest = "0.10.8" chrono = "0.4.19" serde_json = "1.0" +reqwest = "0.10.8" +serde = { version = "1.0.117", features = ["derive"] } tokio = { version = "0.2.22", features = ["rt-core", "macros"] } diff --git a/src/main.rs b/src/main.rs index 8bd641e..07403d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ extern crate rss; extern crate warp; +extern crate serde; extern crate tokio; extern crate chrono; extern crate reqwest; @@ -8,6 +9,7 @@ use std::env; use std::convert::Infallible; use reqwest::Client; use serde_json::json; +use serde::Deserialize; use warp::{Filter, http::response}; use chrono::{Utc, DateTime, NaiveDateTime}; use rss::{Item, ItemBuilder, ChannelBuilder, GuidBuilder}; @@ -31,6 +33,46 @@ const QUERY: &str = r#"query ($id: Int) { } }"#; +#[derive(Deserialize)] +struct Root { + pub data: Data +} + +#[derive(Deserialize)] +#[serde(rename_all="PascalCase")] +struct Data { + pub media: Media +} + +#[derive(Deserialize)] +#[serde(rename_all="camelCase")] +struct Media { + pub title: MediaTitle, + pub airing_schedule: AiringScheduleConnection, + pub episodes: Option, + pub site_url: String +} + +#[derive(Deserialize)] +struct MediaTitle { + pub romaji: String, + pub english: Option +} + +#[derive(Deserialize)] +struct AiringScheduleConnection { + pub nodes: Vec +} + +#[derive(Deserialize)] +#[serde(rename_all="camelCase")] +struct AiringSchedule { + pub id: isize, + pub airing_at: i64, + pub time_until_airing: i64, + pub episode: isize +} + async fn give_response(anime_id: usize) -> Result { let json = json!({"query": QUERY, "variables": {"id": anime_id}}); let client = Client::new(); @@ -41,7 +83,7 @@ async fn give_response(anime_id: usize) -> Result .send() .await { Ok(resp) => match resp.text().await { - Ok(resp) => match serde_json::from_str::(&resp) { + Ok(resp) => match serde_json::from_str::(&resp) { Ok(resp) => resp, Err(err) => { eprintln!("ERROR: {}", err); @@ -58,27 +100,27 @@ async fn give_response(anime_id: usize) -> Result return Ok(response::Builder::new().status(503).header("Content-Type", "type/plain").body(format!("503\n{}", err))); } }; - let resp = &resp["data"]["Media"]; - let title = resp["title"]["english"].as_str().unwrap_or(resp["title"]["romaji"].as_str().unwrap()); - let mut items: Vec = Vec::new(); let mut pub_date = Utc::now().to_rfc2822(); let mut last_build_date = pub_date.clone(); - for i in resp["airingSchedule"]["nodes"].as_array().unwrap() { - let i_date = DateTime::::from_utc(NaiveDateTime::from_timestamp(i["airingAt"].as_i64().unwrap(), 0), Utc).to_rfc2822(); + let resp = &resp.data.media; + let title = resp.title.english.as_ref().unwrap_or(&resp.title.romaji); + let mut items: Vec = Vec::with_capacity(resp.airing_schedule.nodes.len()); + for i in &resp.airing_schedule.nodes { + let i_date = DateTime::::from_utc(NaiveDateTime::from_timestamp(i.airing_at, 0), Utc).to_rfc2822(); pub_date = i_date.clone(); - if i["timeUntilAiring"].as_i64().unwrap() > 0 { + if i.time_until_airing > 0 { break; } last_build_date = i_date.clone(); - let mut title = format!("{} - {}", title, i["episode"].as_u64().unwrap()); - if i["episode"].as_u64().unwrap() == resp["episodes"].as_u64().unwrap_or(0) { + let mut title = format!("{} - {}", title, i.episode); + if Some(i.episode) == resp.episodes { title.push_str(" END"); } items.push(ItemBuilder::default() .title(title) - .link(resp["siteUrl"].as_str().unwrap().to_string()) + .link(resp.site_url.clone()) .pub_date(i_date) - .guid(GuidBuilder::default().permalink(false).value(format!("ran-{}-{}", anime_id, i["id"].as_u64().unwrap())).build().unwrap()) + .guid(GuidBuilder::default().permalink(false).value(format!("ran-{}-{}", anime_id, i.id)).build().unwrap()) .build() .unwrap() ); @@ -86,7 +128,7 @@ async fn give_response(anime_id: usize) -> Result items.reverse(); let channel = ChannelBuilder::default() .title(title) - .link(resp["siteUrl"].as_str().unwrap()) + .link(resp.site_url.clone()) .description(format!("Aired episodes of {}", title)) .pub_date(pub_date) .last_build_date(last_build_date)