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.
This commit is contained in:
Philip Kristoffersen
2022-03-17 21:49:47 +01:00
committed by GitHub
parent 0cecbfe1da
commit fb1ff859f1
4 changed files with 77 additions and 30 deletions
Generated
+10
View File
@@ -95,6 +95,7 @@ dependencies = [
"fltk", "fltk",
"fltk-theme", "fltk-theme",
"futures", "futures",
"is_executable",
"nom 7.0.0", "nom 7.0.0",
"nom_locate", "nom_locate",
"reqwest", "reqwest",
@@ -599,6 +600,15 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" 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]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.8" version = "0.4.8"
+1
View File
@@ -22,6 +22,7 @@ toml = { version = "^0.5.8", optional = true }
futures = { version = "^0.3.17" } futures = { version = "^0.3.17" }
dashmap = { version = "^4.0.2", features = ["serde"] } dashmap = { version = "^4.0.2", features = ["serde"] }
fltk-theme = {version ="0.4" , optional = true } fltk-theme = {version ="0.4" , optional = true }
is_executable = "1.0.1"
[build-dependencies] [build-dependencies]
fl2rust = { version = "0.4", optional = true } fl2rust = { version = "0.4", optional = true }
+37 -14
View File
@@ -4,10 +4,18 @@ use nom::{
IResult, IResult,
}; };
use serde::{Deserialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct DbPaths { pub(crate) struct DbPaths {
pub(crate) base_path: String, pub(crate) base_path: String,
pub(crate) path: String, pub(crate) paths: Vec<String>,
}
#[derive(Deserialize, Debug, Clone)]
struct Candidate{
pub path:String,
} }
pub(crate) fn parse_butler_db<'a>(content: &'a [u8]) -> nom::IResult<&[u8], Vec<DbPaths>> { pub(crate) fn parse_butler_db<'a>(content: &'a [u8]) -> nom::IResult<&[u8], Vec<DbPaths>> {
@@ -22,20 +30,35 @@ fn parse_path<'a>(i: &'a [u8]) -> nom::IResult<&[u8], DbPaths> {
let (i, base_path) = take_until(suffix)(i)?; let (i, base_path) = take_until(suffix)(i)?;
let base_path = String::from_utf8_lossy(base_path).to_string(); let base_path = String::from_utf8_lossy(base_path).to_string();
let prefix = ":[{\"path\":\""; let prefix = "\"candidates\":[";
let suffix = "\",\"depth"; let suffix = "]}";
let (i, _taken) = take_until(prefix)(i)?; let (i, _taken) = take_until(prefix)(i)?;
let (i, _taken) = tag(prefix)(i)?; let (i, _taken) = tag(prefix)(i)?;
let (i, path) = take_until(suffix)(i)?; let (i, candidates_json) = take_until(suffix)(i)?;
let path = String::from_utf8_lossy(path).to_string(); let candidates_json = format!("[{}]",String::from_utf8_lossy(candidates_json).to_string());
IResult::Ok(( let candidates = serde_json::from_str::<Vec<Candidate>>(&candidates_json);
match candidates{
Ok(candidates) => {
return IResult::Ok((
i, i,
DbPaths { DbPaths {
base_path, base_path,
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)] #[cfg(test)]
@@ -52,26 +75,26 @@ mod tests {
assert_eq!(paths.len(), 6); assert_eq!(paths.len(), 6);
assert_eq!(paths[0].base_path, "/home/philip/.config/itch/apps/islands"); 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!( assert_eq!(
paths[1].base_path, paths[1].base_path,
"/home/philip/.config/itch/apps/night-in-the-woods" "/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].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!( assert_eq!(
paths[3].base_path, paths[3].base_path,
"/home/philip/.config/itch/apps/overland" "/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!( assert_eq!(
paths[4].base_path, paths[4].base_path,
"/home/philip/.config/itch/apps/night-in-the-woods" "/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].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] #[test]
@@ -83,7 +106,7 @@ mod tests {
assert_eq!(paths.len(), 94); assert_eq!(paths.len(), 94);
assert_eq!(paths[0].base_path, "/home/deck/.config/itch/apps/risetoruins"); 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 //The parser finds douplicates
assert_eq!(paths[0], paths[1]); assert_eq!(paths[0], paths[1]);
assert_eq!(paths[1], paths[2]); assert_eq!(paths[1], paths[2]);
+16 -3
View File
@@ -4,6 +4,7 @@ use super::{ItchGame, ItchSettings};
use crate::platform::{Platform, SettingsValidity}; use crate::platform::{Platform, SettingsValidity};
use failure::*; use failure::*;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use is_executable::IsExecutable;
use std::collections::HashSet; use std::collections::HashSet;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::Path; use std::path::Path;
@@ -48,9 +49,12 @@ impl Platform<ItchGame, ItchErrors> for ItchPlatform {
}), }),
}?; }?;
//This is done dedupe //This is done to paths dedupe
let paths: HashSet<&DbPaths> = paths.iter().collect(); 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) Ok(res)
} }
@@ -78,6 +82,12 @@ fn dbpath_to_game(paths: &DbPaths) -> Option<ItchGame> {
return None; return None;
} }
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 gz_bytes = std::fs::read(&recipt).unwrap();
let mut d = GzDecoder::new(gz_bytes.as_slice()); let mut d = GzDecoder::new(gz_bytes.as_slice());
let mut s = String::new(); let mut s = String::new();
@@ -86,10 +96,13 @@ fn dbpath_to_game(paths: &DbPaths) -> Option<ItchGame> {
let receipt_op: Option<Receipt> = serde_json::from_str(&s).ok(); let receipt_op: Option<Receipt> = serde_json::from_str(&s).ok();
receipt_op.map(|re| ItchGame { receipt_op.map(|re| ItchGame {
install_path: paths.base_path.to_owned(), install_path: paths.base_path.to_owned(),
executable: paths.path.to_owned(), executable: executable.to_owned(),
title: re.game.title, title: re.game.title,
}) })
} }
None => None,
}
}
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
pub fn get_default_location() -> String { pub fn get_default_location() -> String {