diff --git a/Cargo.toml b/Cargo.toml index 9a9021b..9aa77e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,3 +68,8 @@ sqlite = "^0.30.3" [features] # This feature is enabled when building for a flatpak environment flatpak = [] + +#[profile.release] +#codegen-units = 1 +#opt-level = "z" # Optimize for size. +#lto = true \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7af565a..dead40a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![deny(clippy::unwrap_in_result)] #![deny(clippy::get_unwrap)] #![deny(clippy::unwrap_used)] +#![deny(clippy::indexing_slicing)] mod config; mod migration; diff --git a/src/platforms/egs/get_manifests.rs b/src/platforms/egs/get_manifests.rs index 3e1add6..9567e70 100644 --- a/src/platforms/egs/get_manifests.rs +++ b/src/platforms/egs/get_manifests.rs @@ -67,25 +67,10 @@ fn get_manifest_item(dir_entry: DirEntry, _path: Option) -> Option(reader) { if let Some(compat_folder) = _path { - //Strip off the c:\\ - item.manifest_location = compat_folder - .join("pfx") - .join("dosdevices") - .join(item.manifest_location[0..2].to_lowercase()) - .join(item.manifest_location[3..].replace('\\', "/")) - - .to_string_lossy() - .to_string(); - - item.install_location = compat_folder - .join("pfx") - .join("dosdevices") - .join(item.install_location[0..2].to_lowercase()) - .join(item.install_location[3..].replace('\\', "/")) - - .to_string_lossy() - .to_string(); - + item.manifest_location = + replace_with_dosdevices(&compat_folder, &item.manifest_location); + item.install_location = + replace_with_dosdevices(&compat_folder, &item.install_location); return Some(item); } } @@ -99,6 +84,21 @@ fn get_manifest_item(dir_entry: DirEntry, _path: Option) -> Option String { + let drive = location.get(0..2).map(|drive| drive.to_lowercase()); + let rest_path = location.get(3..).map(|rest| rest.replace('\\', "/")); + if let (Some(drive), Some(rest_path)) = (drive, rest_path) { + let path_buf = compat_folder + .join("pfx") + .join("dosdevices") + .join(drive) + .join(rest_path); + path_buf.to_string_lossy().to_string() + } else { + location.to_string() + } +} + //Commented out because it will change from machine to machine // #[cfg(test)] // pub mod test{ diff --git a/src/platforms/itch/butler_db_parser.rs b/src/platforms/itch/butler_db_parser.rs index 9c110fe..9f0ac24 100644 --- a/src/platforms/itch/butler_db_parser.rs +++ b/src/platforms/itch/butler_db_parser.rs @@ -65,6 +65,7 @@ mod tests { //Okay to unwrap in tests #![allow(clippy::unwrap_in_result)] #![allow(clippy::unwrap_used)] + #![allow(clippy::indexing_slicing)] use super::*; diff --git a/src/platforms/lutris/game_list_parser.rs b/src/platforms/lutris/game_list_parser.rs index 13fd3de..75b8dc0 100644 --- a/src/platforms/lutris/game_list_parser.rs +++ b/src/platforms/lutris/game_list_parser.rs @@ -10,6 +10,8 @@ pub fn parse_lutris_games(input: &str) -> Vec { #[cfg(test)] mod tests { + + #![allow(clippy::indexing_slicing)] use super::*; #[test] diff --git a/src/settings.rs b/src/settings.rs index 293e40a..e5ceb74 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -54,7 +54,7 @@ pub fn load_setting_sections() -> eyre::Result> { for line in lines { if line.starts_with('[') && line.ends_with(']') { add_sections(¤t_section_name, ¤t_section_lines, &mut result); - current_section_name = Some(line[1..line.len() - 1].to_string()); + current_section_name = line.get(1..line.len() - 1).map(|s| s.to_string()); current_section_lines.clear(); } else { current_section_lines.push(line.to_string()); diff --git a/src/steam/collections.rs b/src/steam/collections.rs index 215cedb..2b701f8 100644 --- a/src/steam/collections.rs +++ b/src/steam/collections.rs @@ -272,12 +272,9 @@ fn get_namespace_keys>(steamid: S, db: &mut DB) -> HashSet fn get_namespaces(db: &mut DB, key_bytes: &[u8]) -> Option> { match db.get(key_bytes) { - Some(got) => { - let collection_bytes = got.as_slice(); - let collectin_str = String::from_utf8_lossy(collection_bytes)[1..].to_string(); - let collection = serde_json::from_str(&collectin_str).unwrap_or_default(); - Some(collection) - } + Some(got) => String::from_utf8_lossy(got.as_slice()) + .get(1..) + .and_then(|s| serde_json::from_str(s).ok()), _ => None, } } @@ -329,7 +326,7 @@ fn serialize_collection_value>(name: S, game_ids: &[usize]) -> Str fn name_to_key>(name: S) -> String { let base64 = base64::encode(name.as_ref()); let base64_no_end = if base64.ends_with("==") { - &base64[..base64.len() - 2] + base64.get(..base64.len() - 2).unwrap_or_default() } else { &base64 }; @@ -368,15 +365,20 @@ pub fn write_vdf_collection_to_string>( let key = "\t\"user-collections\"\t\t"; if let Some(start_index) = input.find_substring(key) { let start_index_plus_key = start_index + key.len(); - if let Some(line_index) = input[start_index_plus_key..].find('\n') { + if let Some(line_index) = input.get(start_index_plus_key..).and_then(|i| i.find('\n')) { let end_index_in_full = line_index + start_index_plus_key; - let result = format!( - "{}{}{}", - &input[..start_index_plus_key], - encoded_json, - &input[end_index_in_full..] - ); - return Some(result); + if let (Some(before), Some(after)) = ( + input.get(..start_index_plus_key), + input.get(end_index_in_full..), + ) { + let result = format!( + "{}{}{}", + before, + encoded_json, + after + ); + return Some(result); + } } } None diff --git a/src/steam/installed_games.rs b/src/steam/installed_games.rs index 269a504..aeab05c 100644 --- a/src/steam/installed_games.rs +++ b/src/steam/installed_games.rs @@ -40,8 +40,9 @@ fn get_install_folders(settings: &SteamSettings) -> Vec { if let Ok(vdf_file) = std::fs::read_to_string(vdf_path) { for line in vdf_file.lines() { if line.contains("\"path\"") { - let path_string = &line[11..line.len() - 1]; - result.push(Path::new(&path_string).join("steamapps").to_path_buf()); + if let Some(path_string) = line.get(11..line.len() - 1) { + result.push(Path::new(&path_string).join("steamapps").to_path_buf()); + } } } } @@ -63,19 +64,18 @@ fn parse_manifest_file(path: &Path) -> Option { fn parse_manifest_string>(string: S) -> Option { let mut lines = string.as_ref().lines(); - let app_id_line = lines.find(|l| l.contains("\"appid\"")); - let name_line = lines.find(|l| l.contains("\"name\"")); - match (app_id_line, name_line) { - (Some(app_id_line), Some(name_line)) => { - let appid = app_id_line[11..app_id_line.len() - 1].to_string().parse(); - match appid { - Ok(appid) => Some(SteamGameInfo { - name: name_line[10..name_line.len() - 1].to_string(), - appid, - }), - Err(_) => None, - } - } + let appid: Option = lines + .find(|l| l.contains("\"appid\"")) + .and_then(|line| line.get(11..line.len() - 1)) + .and_then(|app_id_str| app_id_str.parse().ok()); + let name_line = lines + .find(|l| l.contains("\"name\"")) + .and_then(|line| line.get(10..line.len() - 1)); + match (appid, name_line) { + (Some(appid), Some(name)) => Some(SteamGameInfo { + name: name.to_string(), + appid, + }), _ => None, } } diff --git a/src/steam/proton_vdf_util.rs b/src/steam/proton_vdf_util.rs index 49a2613..01e175c 100644 --- a/src/steam/proton_vdf_util.rs +++ b/src/steam/proton_vdf_util.rs @@ -2,7 +2,7 @@ use std::path::Path; use nom::FindSubstring; -pub fn setup_proton_games>(games: &[B]) -> eyre::Result<()>{ +pub fn setup_proton_games>(games: &[B]) -> eyre::Result<()> { if let Ok(home) = std::env::var("HOME") { let config_file = Path::new(&home).join(".local/share/Steam/config/config.vdf"); if config_file.exists() { @@ -30,28 +30,32 @@ fn enable_proton_games, B: AsRef>(vdf_content: S, games: &[B] }; let proton_replace_string = include_str!("proton_string.txt"); - let section_str = &vdf_content[section_info.start..section_info.append_end]; - let games_strings_to_add = games - .iter() - .filter(|g| { - let game_section_start = format!("\"{}\"\n", g.as_ref()); - !section_str.contains(&game_section_start) - }) - .map(|game_id| { - let res = proton_replace_string.to_string(); - let res = res.replace("\"X\"", &format!("\"{}\"", game_id.as_ref())); - let res = res.replace('=', &base_indent_string); - res.replace('+', &field_indent_string) - }); - let mut new_section = section_str.to_string(); - for game_string in games_strings_to_add { - new_section.push_str(&game_string); - } - new_section.push_str(§ion_info.end_key); + let section_str = vdf_content.get(section_info.start..section_info.append_end); + if let Some(section_str) = section_str { + let games_strings_to_add = games + .iter() + .filter(|g| { + let game_section_start = format!("\"{}\"\n", g.as_ref()); + !section_str.contains(&game_section_start) + }) + .map(|game_id| { + let res = proton_replace_string.to_string(); + let res = res.replace("\"X\"", &format!("\"{}\"", game_id.as_ref())); + let res = res.replace('=', &base_indent_string); + res.replace('+', &field_indent_string) + }); + let mut new_section = section_str.to_string(); + for game_string in games_strings_to_add { + new_section.push_str(&game_string); + } + new_section.push_str(§ion_info.end_key); - let before_section = &vdf_content[..section_info.start]; - let after_section = &vdf_content[section_info.end..]; - return format!("{}{}{}", before_section, new_section, after_section); + if let Some(before_section) = vdf_content.get(..section_info.start) { + if let Some(after_section) = vdf_content.get(section_info.end..) { + return format!("{}{}{}", before_section, new_section, after_section); + } + } + } } else { //TODO make this an error instead? println!("Could not find proton section in steam, try to manually set proton on at least one game and then rerun"); @@ -72,14 +76,14 @@ fn find_indexes>(vdf_content: S) -> Option { let vdf_content = vdf_content.as_ref(); if let Some(compat_index) = vdf_content.find_substring(compat_key) { let compat_index = compat_index + compat_key.len(); - let after_key = vdf_content[compat_index..].to_string(); - if let Some(base_indentation) = after_key.find('{') { + let after_key = vdf_content.get(compat_index..); + if let Some(base_indentation) = after_key.and_then(|k| k.find('{')) { let mut end_key = "\n".to_string(); for _i in 0..base_indentation { end_key.push('\t'); } end_key.push('}'); - if let Some(end_index) = after_key.as_str().find_substring(&end_key) { + if let Some(end_index) = after_key.and_then(|a| a.find_substring(&end_key)) { return Some(SectionInfo { start: compat_index, end: compat_index + end_index + end_key.len(), @@ -100,6 +104,7 @@ mod tests { //Okay to unwrap in tests #![allow(clippy::unwrap_in_result)] #![allow(clippy::unwrap_used)] + #![allow(clippy::indexing_slicing)] use super::*; #[test] diff --git a/src/steamgriddb/cached_search.rs b/src/steamgriddb/cached_search.rs index 4988923..93d7519 100644 --- a/src/steamgriddb/cached_search.rs +++ b/src/steamgriddb/cached_search.rs @@ -19,8 +19,8 @@ impl<'a> CachedSearch<'a> { } pub fn save(&self) { - if let Err(err) = save_search_map(&self.search_map){ - eprintln!("Failed saving searchmap : {:?}",err); + if let Err(err) = save_search_map(&self.search_map) { + eprintln!("Failed saving searchmap : {:?}", err); } } @@ -46,14 +46,14 @@ impl<'a> CachedSearch<'a> { } println!("Searching for {}", query.as_ref()); let search = self.client.search(query.as_ref()).await?; - if search.is_empty() { - return Ok(None); + let first_id = search.get(0).map(|f| f.id); + match first_id { + Some(assumed_id) => { + self.search_map.insert(app_id, (query.into(), assumed_id)); + Ok(Some(assumed_id)) + } + None => Ok(None), } - let first_item = &search[0]; - let assumed_id = first_item.id; - self.search_map.insert(app_id, (query.into(), assumed_id)); - - Ok(Some(assumed_id)) } } @@ -62,9 +62,7 @@ fn get_search_map() -> SearchMap { if path.exists() { std::fs::read_to_string(path) .ok() - .and_then(|string| { - serde_json::from_str::(&string).ok() - }) + .and_then(|string| serde_json::from_str::(&string).ok()) .unwrap_or_default() } else { SearchMap::new() diff --git a/src/steamgriddb/downloader.rs b/src/steamgriddb/downloader.rs index c9d88f4..c58c0e8 100644 --- a/src/steamgriddb/downloader.rs +++ b/src/steamgriddb/downloader.rs @@ -242,10 +242,15 @@ async fn search_for_images_to_download( .await; match image_search_result { Ok(images) => { - let images = images - .iter() - .enumerate() - .map(|(index, image)| (image, shortcuts[index], image_ids[index])); + let images = images.iter().enumerate().filter_map(|(index, image)| { + if let (Some(shortcut), Some(image_id)) = + (shortcuts.get(index), image_ids.get(index)) + { + Some((image, shortcut, image_id)) + } else { + None + } + }); let download_for_this_type = stream::iter(images) .filter_map(|(image, shortcut, game_id)| { let extension = image @@ -257,7 +262,7 @@ async fn search_for_images_to_download( async move { let image_url = match image { Ok(img) => Some(img.url.clone()), - Err(_) => get_steam_image_url(game_id, &image_type).await, + Err(_) => get_steam_image_url(*game_id, &image_type).await, }; image_url.map(|url| ToDownload { path, diff --git a/src/ui/components/steam_user_select.rs b/src/ui/components/steam_user_select.rs index 0be174c..692321e 100644 --- a/src/ui/components/steam_user_select.rs +++ b/src/ui/components/steam_user_select.rs @@ -7,7 +7,7 @@ pub fn render_user_select<'a>( ) -> Option<&'a SteamUsersInfo> { if let Some(mut selected_user) = steam_user { let id_before = selected_user.user_id.clone(); - if steam_users.len() <= 1{ + if steam_users.len() <= 1 { return None; } if !steam_users.is_empty() { @@ -21,11 +21,11 @@ pub fn render_user_select<'a>( } let id_now = selected_user.user_id.clone(); if !id_before.eq(&id_now) { - return Some(selected_user); + Some(selected_user) + } else { + None } - } else if !steam_users.is_empty() { - return Some(&steam_users[0]); + } else { + return steam_users.get(0); } - - None }