From d5c3a7685f3037418b425e30e64a7811db027466 Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Sun, 27 Aug 2023 04:32:55 -0400 Subject: [PATCH] Create automatic query response unwrapping trait --- cli/src/main.rs | 20 +++++++++------- cli/src/queries.rs | 38 ++++++++++++++++++++---------- cli/src/queries/search_games.rs | 41 +++++++++++++++++++++++++++++---- 3 files changed, 74 insertions(+), 25 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index b127bbf..aab0b92 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,6 +1,8 @@ +#![feature(iterator_try_collect)] + +use futures::executor::block_on; use std::io::{self, Write}; use std::path::Path; -use futures::executor::block_on; mod queries; use queries::*; @@ -24,7 +26,6 @@ fn get_auth_key(config_dir: &Path) -> Option { } } - fn main() { let mut config_dir = dirs::config_dir().unwrap(); config_dir.push("ggelo"); @@ -34,13 +35,16 @@ fn main() { let mut search = String::new(); print!("Search for game: "); let _ = io::stdout().flush(); - io::stdin().read_line(&mut search).expect("Error reading from stdin"); + io::stdin() + .read_line(&mut search) + .expect("Error reading from stdin"); - if let Some(response) = block_on( - run_query::(VideogameSearchVars { name: search }, &auth_key)).data { - for maybe_game in response.videogames.unwrap().nodes.unwrap().into_iter() { - let game = maybe_game.unwrap(); - println!("{:?} - {}", game.id.unwrap(), game.name.unwrap()); + if let Some(response) = block_on(run_query::( + VideogameSearchVars { name: search }, + &auth_key, + )) { + for game in response.into_iter() { + println!("{} - {}", game.id, game.name); } } else { println!("No response"); diff --git a/cli/src/queries.rs b/cli/src/queries.rs index 4aa838f..aedb693 100644 --- a/cli/src/queries.rs +++ b/cli/src/queries.rs @@ -1,22 +1,36 @@ -use cynic::QueryBuilder; -use serde::{Serialize, Deserialize}; +use std::fmt::{Display, Formatter}; + +use cynic::{GraphQlResponse, QueryBuilder}; +use serde::{Deserialize, Serialize}; pub mod search_games; use schema::schema; -/// HACK: Unfortunately, start.gg seems to use integers for its ID type, whereas -/// cynic always assumes that IDs are strings. To get around that, we define a -/// new scalar type that serializes to u64. -#[derive(cynic::Scalar, Debug)] -pub struct ID(u64); +// HACK: Unfortunately, start.gg seems to use integers for its ID type, whereas +// cynic always assumes that IDs are strings. To get around that, we define a +// new scalar type that serializes to u64. +#[derive(cynic::Scalar, Debug, Copy, Clone)] +pub struct ID(pub u64); +impl Display for ID { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + ::fmt(&self.0, f) + } +} -pub async fn run_query(vars: Vars, auth: &str) -> cynic::GraphQlResponse - where Builder: QueryBuilder, - Vars: Serialize, - for<'de> Builder: Deserialize<'de> +pub trait QueryUnwrap: QueryBuilder { + type Unwrapped; + fn unwrap_response(response: GraphQlResponse) -> Option; +} + +// Generic function for running start.gg queries +pub async fn run_query(vars: Vars, auth: &str) -> Option +where + Builder: QueryUnwrap, + Vars: Serialize, + for<'de> Builder: Deserialize<'de>, { use cynic::http::SurfExt; @@ -27,5 +41,5 @@ pub async fn run_query(vars: Vars, auth: &str) -> cynic: .run_graphql(query) .await; - response.unwrap() + >::unwrap_response(response.unwrap()) } diff --git a/cli/src/queries/search_games.rs b/cli/src/queries/search_games.rs index 7b62a00..bca68e3 100644 --- a/cli/src/queries/search_games.rs +++ b/cli/src/queries/search_games.rs @@ -1,15 +1,15 @@ - +use super::{QueryUnwrap, ID}; +use cynic::GraphQlResponse; use schema::schema; -use super::ID; -// VARIABLES +// Query #[derive(cynic::QueryVariables)] pub struct VideogameSearchVars { - pub name: String + pub name: String, } -// QUERY +// Query #[derive(cynic::QueryFragment, Debug)] #[cynic(graphql_type = "Query", variables = "VideogameSearchVars")] @@ -28,3 +28,34 @@ pub struct Videogame { pub id: Option, pub name: Option, } + +// Unwrapping + +pub struct VideogameResponse { + pub id: ID, + pub name: String, +} + +impl QueryUnwrap for VideogameSearch { + type Unwrapped = Vec; + + fn unwrap_response( + response: GraphQlResponse, + ) -> Option> { + Some( + response + .data? + .videogames? + .nodes? + .into_iter() + .map(|game| { + let game_ = game?; + Some(VideogameResponse { + id: game_.id?, + name: game_.name?, + }) + }) + .try_collect()?, + ) + } +}