mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Disallow indexing (#320)
* Remove indexing * Introduce replace_with_dosdevices * Remove all direct indexing
This commit is contained in:
@@ -68,3 +68,8 @@ sqlite = "^0.30.3"
|
|||||||
[features]
|
[features]
|
||||||
# This feature is enabled when building for a flatpak environment
|
# This feature is enabled when building for a flatpak environment
|
||||||
flatpak = []
|
flatpak = []
|
||||||
|
|
||||||
|
#[profile.release]
|
||||||
|
#codegen-units = 1
|
||||||
|
#opt-level = "z" # Optimize for size.
|
||||||
|
#lto = true
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#![deny(clippy::unwrap_in_result)]
|
#![deny(clippy::unwrap_in_result)]
|
||||||
#![deny(clippy::get_unwrap)]
|
#![deny(clippy::get_unwrap)]
|
||||||
#![deny(clippy::unwrap_used)]
|
#![deny(clippy::unwrap_used)]
|
||||||
|
#![deny(clippy::indexing_slicing)]
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod migration;
|
mod migration;
|
||||||
|
|||||||
@@ -67,25 +67,10 @@ fn get_manifest_item(dir_entry: DirEntry, _path: Option<PathBuf>) -> Option<Mani
|
|||||||
{
|
{
|
||||||
if let Ok(mut item) = serde_json::from_reader::<_, ManifestItem>(reader) {
|
if let Ok(mut item) = serde_json::from_reader::<_, ManifestItem>(reader) {
|
||||||
if let Some(compat_folder) = _path {
|
if let Some(compat_folder) = _path {
|
||||||
//Strip off the c:\\
|
item.manifest_location =
|
||||||
item.manifest_location = compat_folder
|
replace_with_dosdevices(&compat_folder, &item.manifest_location);
|
||||||
.join("pfx")
|
item.install_location =
|
||||||
.join("dosdevices")
|
replace_with_dosdevices(&compat_folder, &item.install_location);
|
||||||
.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();
|
|
||||||
|
|
||||||
return Some(item);
|
return Some(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,6 +84,21 @@ fn get_manifest_item(dir_entry: DirEntry, _path: Option<PathBuf>) -> Option<Mani
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn replace_with_dosdevices(compat_folder: &Path, location: &str) -> 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
|
//Commented out because it will change from machine to machine
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// pub mod test{
|
// pub mod test{
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ mod tests {
|
|||||||
//Okay to unwrap in tests
|
//Okay to unwrap in tests
|
||||||
#![allow(clippy::unwrap_in_result)]
|
#![allow(clippy::unwrap_in_result)]
|
||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ pub fn parse_lutris_games(input: &str) -> Vec<LutrisGame> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ pub fn load_setting_sections() -> eyre::Result<HashMap<String, String>> {
|
|||||||
for line in lines {
|
for line in lines {
|
||||||
if line.starts_with('[') && line.ends_with(']') {
|
if line.starts_with('[') && line.ends_with(']') {
|
||||||
add_sections(¤t_section_name, ¤t_section_lines, &mut result);
|
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();
|
current_section_lines.clear();
|
||||||
} else {
|
} else {
|
||||||
current_section_lines.push(line.to_string());
|
current_section_lines.push(line.to_string());
|
||||||
|
|||||||
+17
-15
@@ -272,12 +272,9 @@ fn get_namespace_keys<S: AsRef<str>>(steamid: S, db: &mut DB) -> HashSet<String>
|
|||||||
|
|
||||||
fn get_namespaces(db: &mut DB, key_bytes: &[u8]) -> Option<Vec<(i32, String)>> {
|
fn get_namespaces(db: &mut DB, key_bytes: &[u8]) -> Option<Vec<(i32, String)>> {
|
||||||
match db.get(key_bytes) {
|
match db.get(key_bytes) {
|
||||||
Some(got) => {
|
Some(got) => String::from_utf8_lossy(got.as_slice())
|
||||||
let collection_bytes = got.as_slice();
|
.get(1..)
|
||||||
let collectin_str = String::from_utf8_lossy(collection_bytes)[1..].to_string();
|
.and_then(|s| serde_json::from_str(s).ok()),
|
||||||
let collection = serde_json::from_str(&collectin_str).unwrap_or_default();
|
|
||||||
Some(collection)
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,7 +326,7 @@ fn serialize_collection_value<S: AsRef<str>>(name: S, game_ids: &[usize]) -> Str
|
|||||||
fn name_to_key<S: AsRef<str>>(name: S) -> String {
|
fn name_to_key<S: AsRef<str>>(name: S) -> String {
|
||||||
let base64 = base64::encode(name.as_ref());
|
let base64 = base64::encode(name.as_ref());
|
||||||
let base64_no_end = if base64.ends_with("==") {
|
let base64_no_end = if base64.ends_with("==") {
|
||||||
&base64[..base64.len() - 2]
|
base64.get(..base64.len() - 2).unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
&base64
|
&base64
|
||||||
};
|
};
|
||||||
@@ -368,15 +365,20 @@ pub fn write_vdf_collection_to_string<S: AsRef<str>>(
|
|||||||
let key = "\t\"user-collections\"\t\t";
|
let key = "\t\"user-collections\"\t\t";
|
||||||
if let Some(start_index) = input.find_substring(key) {
|
if let Some(start_index) = input.find_substring(key) {
|
||||||
let start_index_plus_key = start_index + key.len();
|
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 end_index_in_full = line_index + start_index_plus_key;
|
||||||
let result = format!(
|
if let (Some(before), Some(after)) = (
|
||||||
"{}{}{}",
|
input.get(..start_index_plus_key),
|
||||||
&input[..start_index_plus_key],
|
input.get(end_index_in_full..),
|
||||||
encoded_json,
|
) {
|
||||||
&input[end_index_in_full..]
|
let result = format!(
|
||||||
);
|
"{}{}{}",
|
||||||
return Some(result);
|
before,
|
||||||
|
encoded_json,
|
||||||
|
after
|
||||||
|
);
|
||||||
|
return Some(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ fn get_install_folders(settings: &SteamSettings) -> Vec<PathBuf> {
|
|||||||
if let Ok(vdf_file) = std::fs::read_to_string(vdf_path) {
|
if let Ok(vdf_file) = std::fs::read_to_string(vdf_path) {
|
||||||
for line in vdf_file.lines() {
|
for line in vdf_file.lines() {
|
||||||
if line.contains("\"path\"") {
|
if line.contains("\"path\"") {
|
||||||
let path_string = &line[11..line.len() - 1];
|
if let Some(path_string) = line.get(11..line.len() - 1) {
|
||||||
result.push(Path::new(&path_string).join("steamapps").to_path_buf());
|
result.push(Path::new(&path_string).join("steamapps").to_path_buf());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,19 +64,18 @@ fn parse_manifest_file(path: &Path) -> Option<SteamGameInfo> {
|
|||||||
|
|
||||||
fn parse_manifest_string<S: AsRef<str>>(string: S) -> Option<SteamGameInfo> {
|
fn parse_manifest_string<S: AsRef<str>>(string: S) -> Option<SteamGameInfo> {
|
||||||
let mut lines = string.as_ref().lines();
|
let mut lines = string.as_ref().lines();
|
||||||
let app_id_line = lines.find(|l| l.contains("\"appid\""));
|
let appid: Option<u32> = lines
|
||||||
let name_line = lines.find(|l| l.contains("\"name\""));
|
.find(|l| l.contains("\"appid\""))
|
||||||
match (app_id_line, name_line) {
|
.and_then(|line| line.get(11..line.len() - 1))
|
||||||
(Some(app_id_line), Some(name_line)) => {
|
.and_then(|app_id_str| app_id_str.parse().ok());
|
||||||
let appid = app_id_line[11..app_id_line.len() - 1].to_string().parse();
|
let name_line = lines
|
||||||
match appid {
|
.find(|l| l.contains("\"name\""))
|
||||||
Ok(appid) => Some(SteamGameInfo {
|
.and_then(|line| line.get(10..line.len() - 1));
|
||||||
name: name_line[10..name_line.len() - 1].to_string(),
|
match (appid, name_line) {
|
||||||
appid,
|
(Some(appid), Some(name)) => Some(SteamGameInfo {
|
||||||
}),
|
name: name.to_string(),
|
||||||
Err(_) => None,
|
appid,
|
||||||
}
|
}),
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::path::Path;
|
|||||||
|
|
||||||
use nom::FindSubstring;
|
use nom::FindSubstring;
|
||||||
|
|
||||||
pub fn setup_proton_games<B: AsRef<str>>(games: &[B]) -> eyre::Result<()>{
|
pub fn setup_proton_games<B: AsRef<str>>(games: &[B]) -> eyre::Result<()> {
|
||||||
if let Ok(home) = std::env::var("HOME") {
|
if let Ok(home) = std::env::var("HOME") {
|
||||||
let config_file = Path::new(&home).join(".local/share/Steam/config/config.vdf");
|
let config_file = Path::new(&home).join(".local/share/Steam/config/config.vdf");
|
||||||
if config_file.exists() {
|
if config_file.exists() {
|
||||||
@@ -30,28 +30,32 @@ fn enable_proton_games<S: AsRef<str>, B: AsRef<str>>(vdf_content: S, games: &[B]
|
|||||||
};
|
};
|
||||||
|
|
||||||
let proton_replace_string = include_str!("proton_string.txt");
|
let proton_replace_string = include_str!("proton_string.txt");
|
||||||
let section_str = &vdf_content[section_info.start..section_info.append_end];
|
let section_str = vdf_content.get(section_info.start..section_info.append_end);
|
||||||
let games_strings_to_add = games
|
if let Some(section_str) = section_str {
|
||||||
.iter()
|
let games_strings_to_add = games
|
||||||
.filter(|g| {
|
.iter()
|
||||||
let game_section_start = format!("\"{}\"\n", g.as_ref());
|
.filter(|g| {
|
||||||
!section_str.contains(&game_section_start)
|
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();
|
.map(|game_id| {
|
||||||
let res = res.replace("\"X\"", &format!("\"{}\"", game_id.as_ref()));
|
let res = proton_replace_string.to_string();
|
||||||
let res = res.replace('=', &base_indent_string);
|
let res = res.replace("\"X\"", &format!("\"{}\"", game_id.as_ref()));
|
||||||
res.replace('+', &field_indent_string)
|
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 {
|
let mut new_section = section_str.to_string();
|
||||||
new_section.push_str(&game_string);
|
for game_string in games_strings_to_add {
|
||||||
}
|
new_section.push_str(&game_string);
|
||||||
new_section.push_str(§ion_info.end_key);
|
}
|
||||||
|
new_section.push_str(§ion_info.end_key);
|
||||||
|
|
||||||
let before_section = &vdf_content[..section_info.start];
|
if let Some(before_section) = vdf_content.get(..section_info.start) {
|
||||||
let after_section = &vdf_content[section_info.end..];
|
if let Some(after_section) = vdf_content.get(section_info.end..) {
|
||||||
return format!("{}{}{}", before_section, new_section, after_section);
|
return format!("{}{}{}", before_section, new_section, after_section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//TODO make this an error instead?
|
//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");
|
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<S: AsRef<str>>(vdf_content: S) -> Option<SectionInfo> {
|
|||||||
let vdf_content = vdf_content.as_ref();
|
let vdf_content = vdf_content.as_ref();
|
||||||
if let Some(compat_index) = vdf_content.find_substring(compat_key) {
|
if let Some(compat_index) = vdf_content.find_substring(compat_key) {
|
||||||
let compat_index = compat_index + compat_key.len();
|
let compat_index = compat_index + compat_key.len();
|
||||||
let after_key = vdf_content[compat_index..].to_string();
|
let after_key = vdf_content.get(compat_index..);
|
||||||
if let Some(base_indentation) = after_key.find('{') {
|
if let Some(base_indentation) = after_key.and_then(|k| k.find('{')) {
|
||||||
let mut end_key = "\n".to_string();
|
let mut end_key = "\n".to_string();
|
||||||
for _i in 0..base_indentation {
|
for _i in 0..base_indentation {
|
||||||
end_key.push('\t');
|
end_key.push('\t');
|
||||||
}
|
}
|
||||||
end_key.push('}');
|
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 {
|
return Some(SectionInfo {
|
||||||
start: compat_index,
|
start: compat_index,
|
||||||
end: compat_index + end_index + end_key.len(),
|
end: compat_index + end_index + end_key.len(),
|
||||||
@@ -100,6 +104,7 @@ mod tests {
|
|||||||
//Okay to unwrap in tests
|
//Okay to unwrap in tests
|
||||||
#![allow(clippy::unwrap_in_result)]
|
#![allow(clippy::unwrap_in_result)]
|
||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::indexing_slicing)]
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ impl<'a> CachedSearch<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self) {
|
pub fn save(&self) {
|
||||||
if let Err(err) = save_search_map(&self.search_map){
|
if let Err(err) = save_search_map(&self.search_map) {
|
||||||
eprintln!("Failed saving searchmap : {:?}",err);
|
eprintln!("Failed saving searchmap : {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,14 +46,14 @@ impl<'a> CachedSearch<'a> {
|
|||||||
}
|
}
|
||||||
println!("Searching for {}", query.as_ref());
|
println!("Searching for {}", query.as_ref());
|
||||||
let search = self.client.search(query.as_ref()).await?;
|
let search = self.client.search(query.as_ref()).await?;
|
||||||
if search.is_empty() {
|
let first_id = search.get(0).map(|f| f.id);
|
||||||
return Ok(None);
|
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() {
|
if path.exists() {
|
||||||
std::fs::read_to_string(path)
|
std::fs::read_to_string(path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|string| {
|
.and_then(|string| serde_json::from_str::<SearchMap>(&string).ok())
|
||||||
serde_json::from_str::<SearchMap>(&string).ok()
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
SearchMap::new()
|
SearchMap::new()
|
||||||
|
|||||||
@@ -242,10 +242,15 @@ async fn search_for_images_to_download<T: SearchSettings>(
|
|||||||
.await;
|
.await;
|
||||||
match image_search_result {
|
match image_search_result {
|
||||||
Ok(images) => {
|
Ok(images) => {
|
||||||
let images = images
|
let images = images.iter().enumerate().filter_map(|(index, image)| {
|
||||||
.iter()
|
if let (Some(shortcut), Some(image_id)) =
|
||||||
.enumerate()
|
(shortcuts.get(index), image_ids.get(index))
|
||||||
.map(|(index, image)| (image, shortcuts[index], image_ids[index]));
|
{
|
||||||
|
Some((image, shortcut, image_id))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
let download_for_this_type = stream::iter(images)
|
let download_for_this_type = stream::iter(images)
|
||||||
.filter_map(|(image, shortcut, game_id)| {
|
.filter_map(|(image, shortcut, game_id)| {
|
||||||
let extension = image
|
let extension = image
|
||||||
@@ -257,7 +262,7 @@ async fn search_for_images_to_download<T: SearchSettings>(
|
|||||||
async move {
|
async move {
|
||||||
let image_url = match image {
|
let image_url = match image {
|
||||||
Ok(img) => Some(img.url.clone()),
|
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 {
|
image_url.map(|url| ToDownload {
|
||||||
path,
|
path,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pub fn render_user_select<'a>(
|
|||||||
) -> Option<&'a SteamUsersInfo> {
|
) -> Option<&'a SteamUsersInfo> {
|
||||||
if let Some(mut selected_user) = steam_user {
|
if let Some(mut selected_user) = steam_user {
|
||||||
let id_before = selected_user.user_id.clone();
|
let id_before = selected_user.user_id.clone();
|
||||||
if steam_users.len() <= 1{
|
if steam_users.len() <= 1 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if !steam_users.is_empty() {
|
if !steam_users.is_empty() {
|
||||||
@@ -21,11 +21,11 @@ pub fn render_user_select<'a>(
|
|||||||
}
|
}
|
||||||
let id_now = selected_user.user_id.clone();
|
let id_now = selected_user.user_id.clone();
|
||||||
if !id_before.eq(&id_now) {
|
if !id_before.eq(&id_now) {
|
||||||
return Some(selected_user);
|
Some(selected_user)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
} else if !steam_users.is_empty() {
|
} else {
|
||||||
return Some(&steam_users[0]);
|
return steam_users.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user