From 713f41fb9680c8701e0c9157a5bea902de22dcd9 Mon Sep 17 00:00:00 2001 From: gpskwlkr Date: Sat, 30 Mar 2024 11:27:55 +0400 Subject: [PATCH] better error handling & new arg --- .gitignore | 3 +++ Cargo.toml | 2 +- Makefile | 37 +++++++++++++++++++++++++++++++++++++ README.md | 7 +++++-- src/args.rs | 21 +++++++++++++++++---- src/main.rs | 38 ++++++++++++++++++++++++++++++-------- src/utils.rs | 26 +++++++++++++++++--------- 7 files changed, 110 insertions(+), 24 deletions(-) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index 5db20f2..625871e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ Cargo.lock *.pdb shell.nix + +*.zip +*.tar.gz diff --git a/Cargo.toml b/Cargo.toml index db3c361..e4dc01b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "protondb-check" -version = "0.2.2" +version = "0.3.2" edition = "2021" license = "MIT" description = "CLI tool for checking ProtonDB rating of your Steam games." diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..af316cb --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +# Define the targets and their respective filenames +TARGET_LINUX = x86_64-unknown-linux-gnu +TARGET_WINDOWS = x86_64-pc-windows-gnu +OUT_DIR_LINUX = target/$(TARGET_LINUX)/release +OUT_DIR_WINDOWS = target/$(TARGET_WINDOWS)/release +PROJECT_NAME = protondb-check +TAR_FILE = $(PROJECT_NAME)_$(TARGET_LINUX).tar.gz +ZIP_FILE = $(PROJECT_NAME)_$(TARGET_WINDOWS).zip + +# Default target +all: build pack + +linux: + cargo build --release --target=$(TARGET_LINUX) + tar -czvf $(TAR_FILE) -C $(OUT_DIR_LINUX) $(PROJECT_NAME) + +windows: + cargo build --release --target=$(TARGET_WINDOWS) + zip -r $(ZIP_FILE) $(OUT_DIR_WINDOWS)/$(PROJECT_NAME).exe + +# Build the project for Linux and Windows +build: + cargo build --release --target=$(TARGET_LINUX) + cargo build --release --target=$(TARGET_WINDOWS) + +# Pack the built binaries +pack: + tar -czvf $(TAR_FILE) -C $(OUT_DIR_LINUX) $(PROJECT_NAME) + zip -r $(ZIP_FILE) $(OUT_DIR_WINDOWS)/$(PROJECT_NAME).exe + +# Clean up the build artifacts +clean: + cargo clean + rm -f $(TAR_FILE) $(ZIP_FILE) + +.PHONY: all build pack clean + diff --git a/README.md b/README.md index 1c0bd88..45cd4c5 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,12 @@ ProtonDB Checker. # Available commands +> While `-p` or `-a` are both listed as not required, at least one should be provided. + | Command | Args | Required | Example | | -------------- | ------------------- | -------- | ------------------------------------- | -| protondb-check | `-p` `--profile-id` | Yes | `protondb-check -p 76561198354374976` | +| protondb-check | `-p` `--profile-id` | No | `protondb-check -p 76561198354374976` | +| protondb-check | `-a` `--app-id` | No | `protondb-check -a 271590` | # Install @@ -52,7 +55,7 @@ You can install `protondb-check` via `cargo install protondb-check` -or using prebuilt binaries on the [Releases]() page. +or using prebuilt binaries on the [Releases](https://github.com/gpskwlkr/protondb-check/releases) page. ## MacOS diff --git a/src/args.rs b/src/args.rs index 3249e2b..973d4fe 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,4 +1,5 @@ use clap::{Parser, Subcommand}; +use anyhow::{anyhow, Result, Context}; #[derive(Subcommand)] enum Command { @@ -8,12 +9,24 @@ enum Command { #[command(author, version, about, long_about = None)] pub struct Args { /// Steam profile ID - #[arg(short = 'p', long = "profile-id", required = true)] - pub profile_id: String, + #[arg(short = 'p', long = "profile-id", required = false)] + pub profile_id: Option, + + /// + #[arg(short = 'a', long = "app-id", required = false)] + pub app_id: Option, } impl Args { - pub fn new() -> Self { - Args::parse() + pub fn new() -> Result { + Ok(Args::parse()) + } + + pub fn validate_args(&self) -> Result<(), anyhow::Error> { + if self.profile_id.is_none() && self.app_id.is_none() { + return Err(anyhow!("Either --profile-id or --app-id must be provided")).with_context(|| "validate_args"); + } + + Ok(()) } } diff --git a/src/main.rs b/src/main.rs index 24621fb..2648fb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,33 @@ +use args::Args; use itertools::Itertools; -use anyhow::anyhow; +use anyhow::{anyhow, Result, Context}; mod structs; mod utils; mod args; -fn main() -> Result<(), anyhow::Error> { - use terminal_menu::{menu, label, button, run, mut_menu}; +fn main() -> Result<()> { + let args = Args::new()?; + + args.validate_args()?; + + if args.app_id.is_some() { + handle_app_id(args)?; + } else { + handle_profile_id(args)?; + } + + Ok(()) +} + +fn handle_profile_id(args: Args) -> Result<()> { - let args = args::Args::new(); + use terminal_menu::{menu, label, button, run, mut_menu}; - let steam_profile_id:u64 = match args.profile_id.parse() { + let steam_profile_id:u64 = match args.profile_id.unwrap().parse() { Ok(value) => value, Err(_error) => { - return Err(anyhow!("Please provide valid Steam profile ID.")) + return Err(anyhow!("Please provide valid Steam profile ID.")).with_context(|| "handle_profile_id") } }; @@ -37,10 +51,18 @@ fn main() -> Result<(), anyhow::Error> { { let mm = mut_menu(&menu); let game = games_list.get(mm.selected_item_name()).unwrap(); - let proton_response = utils::check_proton_db(&game.app_id); + let proton_response = utils::check_proton_db(&game.app_id)?; - utils::output(&proton_response, &game.app_id, &game.name); + utils::output(&proton_response, &game.app_id, Some(&game.name)); } Ok(()) } + +fn handle_app_id(args: Args) -> Result<()> { + let app_id = args.app_id.unwrap(); + let proton_response = utils::check_proton_db(&app_id)?; + utils::output(&proton_response, &app_id, None); + + Ok(()) +} diff --git a/src/utils.rs b/src/utils.rs index 3fe2f91..0dd7523 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use reqwest::blocking::get; -use anyhow::anyhow; +use anyhow::{anyhow, Result, Context}; use crate::structs::{GamesList, Game, ProtonAPIResponse}; @@ -12,13 +12,13 @@ const KNOWN_NOT_GAMES: &[u32] = &[ const PROTON_API_URL: &str = "https://www.protondb.com/api/v1/reports/summaries/"; -pub fn get_games_list(steam_id: u64) -> Result, anyhow::Error> { +pub fn get_games_list(steam_id: u64) -> Result> { let url = format!("https://steamcommunity.com/profiles/{}/games?tab=all&xml=1", steam_id); let response = get(url).unwrap(); let xml_string = response.text().unwrap(); let games_list: GamesList = match serde_xml_rs::from_str(&xml_string) { Ok(value) => value, - Err(_error) => return Err(anyhow!("Unable to retrieve Steam data. Is Steam profile ID valid?")) + Err(_error) => return Err(anyhow!("Unable to retrieve Steam data. Is Steam profile ID valid?")).with_context(|| "get_games_list") }; let game_map: HashMap = games_list.games.game.into_iter() @@ -29,18 +29,26 @@ pub fn get_games_list(steam_id: u64) -> Result, anyhow::Er Ok(game_map) } -pub fn check_proton_db(app_id: &u32) -> ProtonAPIResponse { +pub fn check_proton_db(app_id: &u32) -> Result { let url = format!("{}{}.json", PROTON_API_URL, app_id); let response_text = get(url).unwrap().text().unwrap(); - let api_response: ProtonAPIResponse = serde_json::from_str(&response_text).expect("Failed to deserialize JSON"); + let api_response = match serde_json::from_str(&response_text) { + Ok(value) => value, + Err(_error) => return Err(anyhow!("Unable to retrieve data. Possibly not found in ProtonDB")).with_context(|| "check_proton_db") + }; - api_response + Ok(api_response) } -pub fn output(response: &ProtonAPIResponse, app_id: &u32, game: &str) { +pub fn output(response: &ProtonAPIResponse, app_id: &u32, game: Option<&str>) { println!("----------------------"); println!("App ID: {}", app_id); - println!("Game: {}", game); + if let Some(game_name) = game + { + println!("Game: {}", game_name); + } else { + println!("Note: game name couldn't be fetched in this mode."); + } println!("General Tier: {}", response.tier); println!("Trending Tier: {}", response.trending_tier); println!("Success chance: {}%", calculate_percent(response.score)); @@ -51,6 +59,6 @@ fn calculate_percent(score: f32) -> f32 { if score >= 1.00 { score } else { - ((score * 100.0) as i32) as f32 / 100.0 * 100.0 + ((score * 100.0) as i32) as f32 } }