From fb1ff859f1564ad08db7aed21d7c7b5d03fe2f8f Mon Sep 17 00:00:00 2001 From: Philip Kristoffersen Date: Thu, 17 Mar 2022 21:49:47 +0100 Subject: [PATCH] Only add executables from itch (#48) The Itch db can refer to multiple files, before the first one added was selected, now the first one that is executable is selected. --- Cargo.lock | 10 ++++++ Cargo.toml | 1 + src/itch/butler_db_parser.rs | 59 +++++++++++++++++++++++++----------- src/itch/itch_platform.rs | 37 ++++++++++++++-------- 4 files changed, 77 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c1b18c..ea76651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,7 @@ dependencies = [ "fltk", "fltk-theme", "futures", + "is_executable", "nom 7.0.0", "nom_locate", "reqwest", @@ -599,6 +600,15 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +[[package]] +name = "is_executable" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +dependencies = [ + "winapi", +] + [[package]] name = "itoa" version = "0.4.8" diff --git a/Cargo.toml b/Cargo.toml index d14042d..919ca2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ toml = { version = "^0.5.8", optional = true } futures = { version = "^0.3.17" } dashmap = { version = "^4.0.2", features = ["serde"] } fltk-theme = {version ="0.4" , optional = true } +is_executable = "1.0.1" [build-dependencies] fl2rust = { version = "0.4", optional = true } diff --git a/src/itch/butler_db_parser.rs b/src/itch/butler_db_parser.rs index 981dcde..53d0617 100644 --- a/src/itch/butler_db_parser.rs +++ b/src/itch/butler_db_parser.rs @@ -4,10 +4,18 @@ use nom::{ IResult, }; +use serde::{Deserialize}; + + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct DbPaths { pub(crate) base_path: String, - pub(crate) path: String, + pub(crate) paths: Vec, +} + +#[derive(Deserialize, Debug, Clone)] +struct Candidate{ + pub path:String, } pub(crate) fn parse_butler_db<'a>(content: &'a [u8]) -> nom::IResult<&[u8], Vec> { @@ -22,20 +30,35 @@ fn parse_path<'a>(i: &'a [u8]) -> nom::IResult<&[u8], DbPaths> { let (i, base_path) = take_until(suffix)(i)?; let base_path = String::from_utf8_lossy(base_path).to_string(); - let prefix = ":[{\"path\":\""; - let suffix = "\",\"depth"; + let prefix = "\"candidates\":["; + let suffix = "]}"; let (i, _taken) = take_until(prefix)(i)?; let (i, _taken) = tag(prefix)(i)?; - let (i, path) = take_until(suffix)(i)?; - let path = String::from_utf8_lossy(path).to_string(); + let (i, candidates_json) = take_until(suffix)(i)?; + let candidates_json = format!("[{}]",String::from_utf8_lossy(candidates_json).to_string()); - IResult::Ok(( - i, - DbPaths { - base_path, - path, + let candidates = serde_json::from_str::>(&candidates_json); + match candidates{ + Ok(candidates) => { + return IResult::Ok(( + i, + DbPaths { + base_path, + paths: candidates.iter().map(|c| c.path.clone()).collect(), + }, + )) }, - )) + Err(_err) => { + //we found a basepath, but no executables + return IResult::Ok(( + i, + DbPaths { + base_path, + paths: vec![], + }, + )) + }, + } } #[cfg(test)] @@ -52,26 +75,26 @@ mod tests { assert_eq!(paths.len(), 6); assert_eq!(paths[0].base_path, "/home/philip/.config/itch/apps/islands"); - assert_eq!(paths[0].path, "Islands_Linux.x86_64"); + assert_eq!(paths[0].paths[0], "Islands_Linux.x86_64"); assert_eq!( paths[1].base_path, "/home/philip/.config/itch/apps/night-in-the-woods" ); - assert_eq!(paths[1].path, "Night in the Woods.x86_64"); + assert_eq!(paths[1].paths[0], "Night in the Woods.x86_64"); assert_eq!(paths[2].base_path, "/home/philip/.config/itch/apps/islands"); - assert_eq!(paths[2].path, "Islands_Linux.x86_64"); + assert_eq!(paths[2].paths[0], "Islands_Linux.x86_64"); assert_eq!( paths[3].base_path, "/home/philip/.config/itch/apps/overland" ); - assert_eq!(paths[3].path, "Overland.x86_64"); + assert_eq!(paths[3].paths[0], "Overland.x86_64"); assert_eq!( paths[4].base_path, "/home/philip/.config/itch/apps/night-in-the-woods" ); - assert_eq!(paths[4].path, "Night in the Woods.x86_64"); + assert_eq!(paths[4].paths[0], "Night in the Woods.x86_64"); assert_eq!(paths[5].base_path, "/home/philip/.config/itch/apps/islands"); - assert_eq!(paths[5].path, "Islands_Linux.x86_64"); + assert_eq!(paths[5].paths[0], "Islands_Linux.x86_64"); } #[test] @@ -83,7 +106,7 @@ mod tests { assert_eq!(paths.len(), 94); assert_eq!(paths[0].base_path, "/home/deck/.config/itch/apps/risetoruins"); - assert_eq!(paths[0].path, "Core.jar"); + assert_eq!(paths[0].paths[0], "Core.jar"); //The parser finds douplicates assert_eq!(paths[0], paths[1]); assert_eq!(paths[1], paths[2]); diff --git a/src/itch/itch_platform.rs b/src/itch/itch_platform.rs index 24d58fa..af14363 100644 --- a/src/itch/itch_platform.rs +++ b/src/itch/itch_platform.rs @@ -4,6 +4,7 @@ use super::{ItchGame, ItchSettings}; use crate::platform::{Platform, SettingsValidity}; use failure::*; use flate2::read::GzDecoder; +use is_executable::IsExecutable; use std::collections::HashSet; use std::io::prelude::*; use std::path::Path; @@ -48,9 +49,12 @@ impl Platform for ItchPlatform { }), }?; - //This is done dedupe + //This is done to paths dedupe let paths: HashSet<&DbPaths> = paths.iter().collect(); - let res = paths.iter().filter_map(|e| dbpath_to_game(*e)).collect(); + let res = paths + .iter() + .filter_map(|e| dbpath_to_game(*e)) + .collect(); Ok(res) } @@ -78,17 +82,26 @@ fn dbpath_to_game(paths: &DbPaths) -> Option { return None; } - let gz_bytes = std::fs::read(&recipt).unwrap(); - let mut d = GzDecoder::new(gz_bytes.as_slice()); - let mut s = String::new(); - d.read_to_string(&mut s).unwrap(); + let executable = paths + .paths + .iter() + .find(|p| Path::new(&paths.base_path).join(&p).is_executable()); + match executable { + Some(executable) => { + let gz_bytes = std::fs::read(&recipt).unwrap(); + let mut d = GzDecoder::new(gz_bytes.as_slice()); + let mut s = String::new(); + d.read_to_string(&mut s).unwrap(); - let receipt_op: Option = serde_json::from_str(&s).ok(); - receipt_op.map(|re| ItchGame { - install_path: paths.base_path.to_owned(), - executable: paths.path.to_owned(), - title: re.game.title, - }) + let receipt_op: Option = serde_json::from_str(&s).ok(); + receipt_op.map(|re| ItchGame { + install_path: paths.base_path.to_owned(), + executable: executable.to_owned(), + title: re.game.title, + }) + } + None => None, + } } #[cfg(target_family = "unix")]