Remove unwraps (#316)

Remove all unwraps and clippy warnings
This commit is contained in:
Philip Kristoffersen
2023-01-07 13:48:26 +01:00
committed by GitHub
parent 59b67a11b7
commit f6188b130d
30 changed files with 706 additions and 553 deletions
+4 -4
View File
@@ -64,10 +64,10 @@ mod tests {
// Parameters for set environment 'XDG_CONFIG_HOME'
std::env::set_var(
"XDG_CONFIG_HOME",
std::env::var("HOME").unwrap() + "/.config/boilr",
std::env::var("HOME").unwrap_or_default() + "/.config/boilr",
);
let xdg_config_home = std::env::var("XDG_CONFIG_HOME").unwrap();
let xdg_config_home = std::env::var("XDG_CONFIG_HOME").unwrap_or_default();
let config_path = get_config_folder();
let test_path = PathBuf::from(xdg_config_home);
@@ -79,11 +79,11 @@ mod tests {
fn check_return_config_path() {
std::env::set_var(
"XDG_CONFIG_HOME",
std::env::var("HOME").unwrap() + "/.config/boilr",
std::env::var("HOME").unwrap_or_default() + "/.config/boilr",
);
let config_path = get_config_folder();
let current_path = std::env::var("HOME").unwrap() + "/.config/boilr";
let current_path = std::env::var("HOME").unwrap_or_default() + "/.config/boilr";
assert_eq!(config_path, PathBuf::from(current_path));
}
+6 -2
View File
@@ -1,3 +1,7 @@
#![deny(clippy::unwrap_in_result)]
#![deny(clippy::get_unwrap)]
#![deny(clippy::unwrap_used)]
mod config;
mod migration;
mod platforms;
@@ -16,9 +20,9 @@ fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
if args.contains(&"--no-ui".to_string()) {
ui::run_sync();
ui::run_sync()?;
} else {
ui::run_ui(args);
ui::run_ui(args)?;
}
Ok(())
}
+3 -1
View File
@@ -38,7 +38,9 @@ pub fn migrate_config() {
if let Ok(mut settings) = crate::settings::Settings::new() {
settings.config_version = Some(1);
let platforms = get_platforms();
save_settings(&settings, &platforms);
if let Err(err) = save_settings(&settings, &platforms){
eprintln!("Failed to load settings {:?}", err);
}
}
}
}
+3
View File
@@ -185,6 +185,9 @@ impl ManifestItem {
#[cfg(test)]
mod tests {
//Okay to unwrap in tests
#![allow(clippy::unwrap_in_result)]
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
+1 -1
View File
@@ -42,7 +42,7 @@ impl From<GogShortcut> for ShortcutOwned {
let icon_file = format!("goggame-{}.ico", gogs.game_id);
let icon_path = Path::new(&gogs.game_folder).join(icon_file);
let icon = if icon_path.exists() {
icon_path.to_str().unwrap().to_string()
icon_path.to_string_lossy().to_string()
} else {
"".to_string()
};
+3
View File
@@ -62,6 +62,9 @@ fn parse_path(i: &[u8]) -> nom::IResult<&[u8], DbPaths> {
#[cfg(test)]
mod tests {
//Okay to unwrap in tests
#![allow(clippy::unwrap_in_result)]
#![allow(clippy::unwrap_used)]
use super::*;
+1 -1
View File
@@ -18,7 +18,7 @@ pub struct ItchGame {
impl From<ItchGame> for ShortcutOwned {
fn from(game: ItchGame) -> Self {
let exe = Path::new(&game.install_path).join(&game.executable);
let exe = exe.to_str().unwrap().to_string();
let exe = exe.to_string_lossy().to_string();
let shortcut = Shortcut::new(
"0",
game.title.as_str(),
+14 -12
View File
@@ -29,7 +29,7 @@ impl ItchPlatform {
));
}
let shortcut_bytes = std::fs::read(&itch_db_location).unwrap();
let shortcut_bytes = std::fs::read(&itch_db_location)?;
let paths = match parse_butler_db(&shortcut_bytes) {
Ok((_, shortcuts)) => Ok(shortcuts),
@@ -59,17 +59,19 @@ fn dbpath_to_game(paths: &DbPaths) -> Option<ItchGame> {
.iter()
.filter(|p| Path::new(&paths.base_path).join(p).is_executable())
.find_map(|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<Receipt> = 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,
})
if let Ok(gz_bytes) = std::fs::read(&recipt) {
let mut d = GzDecoder::new(gz_bytes.as_slice());
let mut s = String::new();
if d.read_to_string(&mut s).is_ok() {
let receipt_op: Option<Receipt> = serde_json::from_str(&s).ok();
return receipt_op.map(|re| ItchGame {
install_path: paths.base_path.to_owned(),
executable: executable.to_owned(),
title: re.game.title,
});
}
}
None
})
}
+1 -5
View File
@@ -32,11 +32,7 @@ impl NeedsPorton<OriginPlatform> for OriginGame {
impl OriginPlatform {
fn get_shortcuts(&self) -> eyre::Result<Vec<OriginGame>> {
let origin_folders = get_default_locations();
if origin_folders.is_none() {
return Err(eyre::format_err!("Default path not found"));
}
let origin_folders = origin_folders.unwrap();
let origin_folders = get_default_locations().ok_or(eyre::format_err!("Default path not found"))?;
let origin_folder = origin_folders.local_content_path;
let origin_exe = origin_folders.exe_path;
let game_folders = origin_folder.join("LocalContent").read_dir()?;
+4 -3
View File
@@ -69,8 +69,8 @@ pub fn load_setting_sections() -> eyre::Result<HashMap<String, String>> {
Ok(result)
}
pub fn save_settings(settings: &Settings, platforms: &Platforms) {
let mut toml = toml::to_string(&settings).unwrap();
pub fn save_settings(settings: &Settings, platforms: &Platforms) -> eyre::Result<()>{
let mut toml = toml::to_string(&settings)?;
for platform in platforms {
let section_name = format!("[{}]", platform.code_name());
@@ -82,7 +82,8 @@ pub fn save_settings(settings: &Settings, platforms: &Platforms) {
}
let config_path = crate::config::get_config_file();
std::fs::write(config_path, toml).unwrap();
std::fs::write(config_path, toml)?;
Ok(())
}
fn add_sections(
+17 -16
View File
@@ -158,7 +158,7 @@ pub fn write_collections<S: AsRef<str>>(
&vdf_collections,
);
if let Some(new_string) = new_string {
std::fs::write(path, new_string).unwrap();
std::fs::write(path, new_string)?;
}
}
}
@@ -242,22 +242,19 @@ fn get_categories<S: AsRef<str>>(
Ok(res)
}
fn open_db() -> Result<DB, Box<dyn Error>> {
let location = get_level_db_location();
fn open_db() -> eyre::Result<DB> {
use eyre::eyre;
let location = get_level_db_location().ok_or(eyre!("Collections db not found"))?;
let options = Options::default();
let open_res = DB::open(location.unwrap(), options);
if let Err(e) = &open_res {
match &e.code {
rusty_leveldb::StatusCode::LockError => {
println!("Could not lock the steam level database, make sure steam is turned off when running synchronizations");
}
rusty_leveldb::StatusCode::NotFound => {
println!("Could not find the steam level database, try to open and close steam once and synchronize again");
}
_ => {}
};
}
Ok(open_res?)
let open_res = DB::open(location, options);
open_res.map_err(|e|{
use rusty_leveldb::StatusCode::*;
match e.code{
LockError => eyre!("Could not lock the steam level database, make sure steam is turned off when running synchronizations"),
NotFound => eyre!("Could not find the steam level database, try to open and close steam once and synchronize again"),
_ => eyre!("Failed opening collections file: {}",e.err),
}
})
}
fn get_namespace_keys<S: AsRef<str>>(steamid: S, db: &mut DB) -> HashSet<String> {
@@ -394,6 +391,10 @@ pub struct VdfCollection {
#[cfg(test)]
mod tests {
//Allow unwraps in test
#![allow(clippy::unwrap_in_result)]
#![allow(clippy::get_unwrap)]
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
+13 -7
View File
@@ -66,19 +66,25 @@ fn parse_manifest_string<S: AsRef<str>>(string: S) -> Option<SteamGameInfo> {
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)) => Some(SteamGameInfo {
name: name_line[10..name_line.len() - 1].to_string(),
appid: app_id_line[11..app_id_line.len() - 1]
.to_string()
.parse()
.unwrap(),
}),
(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,
}
}
_ => None,
}
}
#[cfg(test)]
mod tests {
//Okay to unwrap in tests
#![allow(clippy::unwrap_in_result)]
#![allow(clippy::unwrap_used)]
use super::*;
+6 -2
View File
@@ -2,16 +2,17 @@ use std::path::Path;
use nom::FindSubstring;
pub fn setup_proton_games<B: AsRef<str>>(games: &[B]) {
pub fn setup_proton_games<B: AsRef<str>>(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() {
if let Ok(config_content) = std::fs::read_to_string(&config_file) {
let new_string = enable_proton_games(config_content, games);
std::fs::write(config_file, new_string).unwrap();
std::fs::write(config_file, new_string)?;
}
}
}
Ok(())
}
fn enable_proton_games<S: AsRef<str>, B: AsRef<str>>(vdf_content: S, games: &[B]) -> String {
@@ -96,6 +97,9 @@ fn find_indexes<S: AsRef<str>>(vdf_content: S) -> Option<SectionInfo> {
#[cfg(target_family = "unix")]
mod tests {
//Okay to unwrap in tests
#![allow(clippy::unwrap_in_result)]
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
+20 -20
View File
@@ -8,14 +8,14 @@ use steam_shortcuts_util::{parse_shortcuts, shortcut::ShortcutOwned};
use super::SteamSettings;
pub fn get_shortcuts_for_user(user: &SteamUsersInfo) -> ShortcutInfo {
pub fn get_shortcuts_for_user(user: &SteamUsersInfo) -> eyre::Result<ShortcutInfo> {
let mut shortcuts = vec![];
let new_path = match &user.shortcut_path {
Some(shortcut_path) => {
let content = std::fs::read(shortcut_path).unwrap();
let content = std::fs::read(shortcut_path)?;
shortcuts = parse_shortcuts(content.as_slice())
.unwrap()
.map_err(|e| eyre::format_err!("Could not parse shortcuts: {:?}", e))?
.iter()
.map(|s| s.to_owned())
.collect();
@@ -27,15 +27,15 @@ pub fn get_shortcuts_for_user(user: &SteamUsersInfo) -> ShortcutInfo {
user.steam_user_data_folder
);
let path = Path::new(&user.steam_user_data_folder).join("config");
std::fs::create_dir_all(path.clone()).unwrap();
std::fs::create_dir_all(path.clone())?;
path.join("shortcuts.vdf")
}
};
ShortcutInfo {
Ok(ShortcutInfo {
shortcuts,
path: new_path,
}
})
}
pub struct ShortcutInfo {
@@ -51,22 +51,22 @@ pub struct SteamUsersInfo {
}
/// Get the paths to the steam users shortcuts (one for each user)
pub fn get_shortcuts_paths(
settings: &SteamSettings,
) -> Result<Vec<SteamUsersInfo>, Box<dyn Error + Sync + Send>> {
pub fn get_shortcuts_paths(settings: &SteamSettings) -> eyre::Result<Vec<SteamUsersInfo>> {
let steam_path_str = get_steam_path(settings)?;
let steam_path = Path::new(&steam_path_str);
if !steam_path.exists() {
return Result::Err(Box::new(SteamFolderNotFound {
location_tried: format!("{:?}", steam_path),
}));
return Err(eyre::format_err!(
"Steam folder not found at: {:?}",
steam_path
));
}
let user_data_path = steam_path.join("userdata");
if !user_data_path.exists() {
return Result::Err(Box::new(SteamFolderNotFound {
location_tried: format!("{:?}", user_data_path),
}));
return Err(eyre::format_err!(
"Steam user data folder not found at: {:?}",
user_data_path
));
}
if !user_data_path.exists() {}
@@ -89,7 +89,7 @@ pub fn get_shortcuts_paths(
if shortcuts_path.exists() {
return SteamUsersInfo {
steam_user_data_folder: folder_string,
shortcut_path: Some(shortcuts_path.to_str().unwrap().to_string()),
shortcut_path: Some(shortcuts_path.to_string_lossy().to_string()),
user_id,
};
} else {
@@ -104,7 +104,7 @@ pub fn get_shortcuts_paths(
Ok(users_info)
}
pub fn get_steam_path(settings: &SteamSettings) -> Result<String, Box<dyn Error + Sync + Send>> {
pub fn get_steam_path(settings: &SteamSettings) -> eyre::Result<String> {
let user_location = settings.location.clone();
let steam_path_str = match user_location {
Some(location) => location,
@@ -113,7 +113,7 @@ pub fn get_steam_path(settings: &SteamSettings) -> Result<String, Box<dyn Error
Ok(steam_path_str)
}
pub fn get_default_location() -> Result<String, Box<dyn Error + Sync + Send>> {
pub fn get_default_location() -> eyre::Result<String> {
#[cfg(target_os = "windows")]
let path_string = {
let key = "PROGRAMFILES(X86)";
@@ -198,8 +198,8 @@ impl Error for SteamUsersDataEmpty {
self.location_tried.as_str()
}
}
pub fn get_users_images(user: &SteamUsersInfo) -> Result<Vec<String>, Box<dyn Error>> {
let grid_folder = Path::new(user.steam_user_data_folder.as_str()).join("config/grid");
pub fn get_users_images(data_folder: &str) -> Result<Vec<String>, Box<dyn Error>> {
let grid_folder = Path::new(data_folder).join("config/grid");
if !grid_folder.exists() {
std::fs::create_dir_all(&grid_folder)?;
}
+14 -7
View File
@@ -19,7 +19,9 @@ impl<'a> CachedSearch<'a> {
}
pub fn save(&self) {
save_search_map(&self.search_map);
if let Err(err) = save_search_map(&self.search_map){
eprintln!("Failed saving searchmap : {:?}",err);
}
}
pub fn set_cache<S>(&mut self, app_id: u32, name: S, new_grid_id: usize)
@@ -58,16 +60,21 @@ impl<'a> CachedSearch<'a> {
fn get_search_map() -> SearchMap {
let path = get_cache_file();
if path.exists() {
let string = std::fs::read_to_string(path).unwrap();
serde_json::from_str::<SearchMap>(&string).expect("Failed to parse cache.json")
std::fs::read_to_string(path)
.ok()
.and_then(|string| {
serde_json::from_str::<SearchMap>(&string).ok()
})
.unwrap_or_default()
} else {
SearchMap::new()
}
}
fn save_search_map(search_map: &SearchMap) {
let string = serde_json::to_string(search_map).unwrap();
fn save_search_map(search_map: &SearchMap) -> eyre::Result<()> {
let string = serde_json::to_string(search_map)?;
let path = get_cache_file();
let mut file = File::create(path).unwrap();
file.write_all(string.as_bytes()).unwrap();
let mut file = File::create(path)?;
file.write_all(string.as_bytes())?;
Ok(())
}
+73 -38
View File
@@ -23,10 +23,31 @@ use crate::sync::SyncProgress;
const CONCURRENT_REQUESTS: usize = 10;
impl SearchSettings for Settings {
fn download_animated(&self) -> bool {
self.steamgrid_db.prefer_animated
}
fn download_big_picture(&self) -> bool {
self.steam.optimize_for_big_picture
}
fn allow_nsfw(&self) -> bool {
self.steamgrid_db.allow_nsfw
}
fn only_download_boilr_images(&self) -> bool {
self.steamgrid_db.only_download_boilr_images
}
fn is_image_banned(&self, image_type: &ImageType, app_id: u32) -> bool {
self.steamgrid_db.is_image_banned(image_type, app_id)
}
}
pub async fn download_images_for_users<'b>(
settings: &Settings,
users: &[SteamUsersInfo],
download_animated: bool,
sender: &mut Option<Sender<SyncProgress>>,
) {
let auth_key = &settings.steamgrid_db.auth_key;
@@ -40,24 +61,29 @@ pub async fn download_images_for_users<'b>(
if let Some(sender) = sender {
let _ = sender.send(SyncProgress::FindingImages);
}
let to_downloads = stream::iter(users)
.map(|user| {
let shortcut_info = get_shortcuts_for_user(user);
async move {
let known_images = get_users_images(user).unwrap_or_default();
let res = search_for_images_to_download(
known_images,
user.steam_user_data_folder.as_str(),
&shortcut_info.shortcuts,
search,
client,
download_animated,
settings.steam.optimize_for_big_picture,
settings,
)
.await;
res.unwrap_or_default()
}
let users_info = users.iter().filter_map(|user| {
let shortcut_info = get_shortcuts_for_user(user);
shortcut_info
.map(|shortcut_info| {
let data_folder = &user.steam_user_data_folder;
(shortcut_info, data_folder)
})
.ok()
});
let to_downloads = stream::iter(users_info)
.map(|(shortcut_info, data_folder)| async move {
let known_images = get_users_images(data_folder).unwrap_or_default();
let res = search_for_images_to_download(
known_images,
data_folder.as_str(),
&shortcut_info.shortcuts,
search,
client,
settings,
)
.await;
res.unwrap_or_default()
})
.buffer_unordered(CONCURRENT_REQUESTS)
.collect::<Vec<Vec<ToDownload>>>()
@@ -128,15 +154,21 @@ pub struct PublicGameResponse {
data: Option<PublicGameResponseData>,
}
async fn search_for_images_to_download(
pub trait SearchSettings {
fn download_animated(&self) -> bool;
fn download_big_picture(&self) -> bool;
fn allow_nsfw(&self) -> bool;
fn only_download_boilr_images(&self) -> bool;
fn is_image_banned(&self, image_type: &ImageType, app_id: u32) -> bool;
}
async fn search_for_images_to_download<T: SearchSettings>(
known_images: Vec<String>,
user_data_folder: &str,
shortcuts: &[ShortcutOwned],
search: &CachedSearch<'_>,
client: &Client,
download_animated: bool,
download_big_picture: bool,
settings: &Settings,
search_settins: &T,
) -> Result<Vec<ToDownload>, Box<dyn Error>> {
let types = {
let mut types = vec![
@@ -146,7 +178,7 @@ async fn search_for_images_to_download(
ImageType::WideGrid,
ImageType::Icon,
];
if download_big_picture {
if search_settins.download_big_picture() {
types.push(ImageType::BigPicture);
}
types
@@ -154,7 +186,7 @@ async fn search_for_images_to_download(
let shortcuts_to_search_for = shortcuts
.iter()
.filter(|s| !settings.steamgrid_db.only_download_boilr_images || s.is_boilr_shortcut())
.filter(|s| !search_settins.only_download_boilr_images() || s.is_boilr_shortcut())
.filter(|s| {
// if we are missing any of the images we need to search for them
types
@@ -171,13 +203,10 @@ async fn search_for_images_to_download(
let search_results_a = stream::iter(shortcuts_to_search_for)
.map(|s| async move {
let search_result = search.search(s.app_id, &s.app_name).await;
if search_result.is_err() {
return None;
match search_result {
Ok(Some(search_result)) => Some((s.app_id, search_result)),
_ => None,
}
let search_result = search_result.unwrap();
search_result?;
let search_result = search_result.unwrap();
Some((s.app_id, search_result))
})
.buffer_unordered(CONCURRENT_REQUESTS)
.collect::<Vec<Option<(u32, usize)>>>()
@@ -192,7 +221,7 @@ async fn search_for_images_to_download(
let images_needed = shortcuts
.iter()
.filter(|s| search_results.contains_key(&s.app_id))
.filter(|s| !settings.steamgrid_db.is_image_banned(&image_type, s.app_id))
.filter(|s| !search_settins.is_image_banned(&image_type, s.app_id))
.filter(|s| !known_images.contains(&image_type.file_name_no_extension(s.app_id)));
let image_ids: Vec<usize> = images_needed
.clone()
@@ -203,8 +232,14 @@ async fn search_for_images_to_download(
let shortcuts: Vec<&ShortcutOwned> = images_needed.collect();
for image_ids in image_ids.chunks(99) {
let image_search_result =
get_images_for_ids(client, image_ids, &image_type, download_animated, settings.steamgrid_db.allow_nsfw).await;
let image_search_result = get_images_for_ids(
client,
image_ids,
&image_type,
search_settins.download_animated(),
search_settins.allow_nsfw(),
)
.await;
match image_search_result {
Ok(images) => {
let images = images
@@ -237,7 +272,7 @@ async fn search_for_images_to_download(
to_download.extend(download_for_this_type);
}
Err(err) => println!("Error getting images: {}", err),
Err(err) => eprintln!("Error getting images: {}", err),
}
}
}
@@ -388,17 +423,17 @@ fn icon_url(steam_app_id: &str, icon_id: &str) -> String {
)
}
pub async fn download_to_download(to_download: &ToDownload) -> Result<(), Box<dyn Error>> {
pub async fn download_to_download(to_download: &ToDownload) -> eyre::Result<()> {
println!(
"Downloading {:?} for {} to {:?}",
to_download.image_type, to_download.app_name, to_download.path
);
let path = &to_download.path;
let url = &to_download.url;
let mut file = File::create(path).unwrap();
let mut file = File::create(path)?;
let response = reqwest::get(url).await?;
let content = response.bytes().await?;
file.write_all(&content).unwrap();
file.write_all(&content)?;
Ok(())
}
+37 -28
View File
@@ -34,15 +34,16 @@ pub fn disconnect_shortcut(settings: &Settings, app_id: u32) -> Result<(), Strin
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
for user in userinfo_shortcuts.iter_mut() {
let mut shortcut_info = get_shortcuts_for_user(user);
for shortcut in shortcut_info.shortcuts.iter_mut() {
if shortcut.app_id == app_id {
shortcut.dev_kit_game_id = "".to_string();
shortcut.tags.retain(|s| s != BOILR_TAG);
let shortcut_info = get_shortcuts_for_user(user);
if let Ok(mut shortcut_info) = shortcut_info {
for shortcut in shortcut_info.shortcuts.iter_mut() {
if shortcut.app_id == app_id {
shortcut.dev_kit_game_id = "".to_string();
shortcut.tags.retain(|s| s != BOILR_TAG);
}
}
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
}
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
}
Ok(())
@@ -53,9 +54,8 @@ pub fn sync_shortcuts(
platform_shortcuts: &[(String, Vec<ShortcutOwned>)],
sender: &mut Option<Sender<SyncProgress>>,
renames: &HashMap<u32, String>,
) -> Result<Vec<SteamUsersInfo>, String> {
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
) -> eyre::Result<Vec<SteamUsersInfo>> {
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)?;
let mut all_shortcuts: Vec<ShortcutOwned> = platform_shortcuts
.iter()
.flat_map(|s| s.1.clone())
@@ -86,10 +86,14 @@ pub fn sync_shortcuts(
println!("Appid: {} name: {}", shortcut.app_id, shortcut.app_name);
}
println!("Found {} user(s)", userinfo_shortcuts.len());
for user in userinfo_shortcuts.iter_mut() {
let ok_shorcuts = userinfo_shortcuts.iter_mut().filter_map(|user|{
let shortcut_info = get_shortcuts_for_user(user).ok();
shortcut_info.map(|shortcut_info| {
(user,shortcut_info)
})
});
for (user,mut shortcut_info) in ok_shorcuts {
let start_time = std::time::Instant::now();
let mut shortcut_info = get_shortcuts_for_user(user);
println!(
"Found {} shortcuts for user: {}",
shortcut_info.shortcuts.len(),
@@ -99,7 +103,7 @@ pub fn sync_shortcuts(
remove_old_shortcuts(&mut shortcut_info);
remove_shortcuts_with_same_appid(&mut shortcut_info, &all_shortcuts);
shortcut_info.shortcuts.extend(all_shortcuts.clone());
shortcut_info.shortcuts.extend(all_shortcuts.clone());
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
@@ -122,11 +126,12 @@ pub async fn download_images(
sender: &mut Option<Sender<SyncProgress>>,
) {
if settings.steamgrid_db.enabled {
if settings.steamgrid_db.prefer_animated {
println!("downloading animated images");
download_images_for_users(settings, userinfo_shortcuts, true, sender).await;
download_images_for_users(settings, userinfo_shortcuts, sender).await;
if settings.steamgrid_db.prefer_animated{
let mut set = settings.clone();
set.steamgrid_db.prefer_animated = false;
download_images_for_users(&set, userinfo_shortcuts, sender).await;
}
download_images_for_users(settings, userinfo_shortcuts, false, sender).await;
}
}
@@ -157,21 +162,25 @@ fn remove_old_shortcuts(shortcut_info: &mut ShortcutInfo) {
.retain(|shortcut| !shortcut.is_boilr_shortcut());
}
pub fn fix_all_shortcut_icons (
settings: &Settings,
) -> eyre::Result<()>{
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam).map_err(|e|eyre::format_err!("Could not find steam shortcuts; {e}"))?;
pub fn fix_all_shortcut_icons(settings: &Settings) -> eyre::Result<()> {
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
.map_err(|e| eyre::format_err!("Could not find steam shortcuts; {e}"))?;
for user in userinfo_shortcuts.iter_mut() {
let mut shortcut_info = get_shortcuts_for_user(user);
let changes = fix_shortcut_icons(user,&mut shortcut_info.shortcuts,settings.steam.optimize_for_big_picture);
if changes{
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
let shortcut_info = get_shortcuts_for_user(user);
if let Ok(mut shortcut_info) = shortcut_info {
let changes = fix_shortcut_icons(
user,
&mut shortcut_info.shortcuts,
settings.steam.optimize_for_big_picture,
);
if changes {
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
}
}
}
Ok(())
}
fn fix_shortcut_icons(
user: &SteamUsersInfo,
shortcuts: &mut Vec<ShortcutOwned>,
@@ -194,7 +203,7 @@ fn fix_shortcut_icons(
let path = image_folder.join(image_type.file_name(app_id, ext));
if !icon_exsists && path.exists() {
shortcut.icon = path.to_string_lossy().to_string();
has_changes= true;
has_changes = true;
break;
}
}
+172 -133
View File
@@ -1,147 +1,186 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use egui::{Button, ImageButton};
use futures::executor::block_on;
use tokio::runtime::Runtime;
use crate::steamgriddb::{ImageType, ToDownload};
use crate::steamgriddb::{ToDownload, ImageType};
use crate::ui::images::{clamp_to_width, ImageHandles, TextureDownloadState};
use crate::ui::ui_images::load_image_from_path;
pub fn render_image_from_path(
ui: &mut egui::Ui,
image_handles: &ImageHandles,
path: &Path,
pub struct GameButton {
path: PathBuf,
max_width: f32,
text: &str,
) -> bool {
render_possible_image(ui, image_handles, path, max_width, text, &ImageType::Grid, None, None)
text: String,
image_type: ImageType,
}
pub fn render_image_from_path_image_type(
ui: &mut egui::Ui,
image_handles: &ImageHandles,
path: &Path,
max_width: f32,
text: &str,
image_type: &ImageType,
) -> bool {
render_possible_image(ui, image_handles, path, max_width, text, image_type, None, None)
}
pub fn render_image_from_path_or_url(
ui: &mut egui::Ui,
image_handles: &ImageHandles,
path: &Path,
max_width: f32,
text: &str,
image_type: &ImageType,
rt: &Runtime,
url: &str,
) -> bool {
render_possible_image(
ui,
image_handles,
path,
max_width,
text,
image_type,
Some(rt),
Some(url),
)
}
fn render_possible_image(
ui: &mut egui::Ui,
image_handles: &ImageHandles,
path: &Path,
max_width: f32,
text: &str,
image_type: &ImageType,
rt: Option<&Runtime>,
url: Option<&str>,
) -> bool {
let image_key = path.to_string_lossy().to_string();
match image_handles.get_mut(&image_key) {
Some(mut state) => {
match state.value() {
TextureDownloadState::Downloading => {
ui.ctx().request_repaint();
//nothing to do,just wait
ui.spinner();
}
TextureDownloadState::Downloaded => {
//Need to load
let image_data = load_image_from_path(path);
match image_data {
Ok(image_data) => {
let handle = ui.ctx().load_texture(
&image_key,
image_data,
egui::TextureOptions::LINEAR,
);
*state.value_mut() = TextureDownloadState::Loaded(handle);
ui.spinner();
}
Err(_) => *state.value_mut() = TextureDownloadState::Failed,
}
ui.ctx().request_repaint();
}
TextureDownloadState::Loaded(texture_handle) => {
//need to show
let mut size = texture_handle.size_vec2();
clamp_to_width(&mut size, max_width);
let image_button = ImageButton::new(texture_handle, size);
if ui
.add_sized(size, image_button)
.on_hover_text(text)
.clicked()
{
return true;
}
}
TextureDownloadState::Failed => {
let button =
ui.add_sized([max_width, max_width * image_type.ratio()], Button::new(text).wrap(true));
if button.clicked() {
return true;
}
}
}
}
None => {
if !path.exists() && url.is_none() {
image_handles.insert(image_key, TextureDownloadState::Failed);
} else {
//We need to start a download
//Redownload if file is too small
if !path.exists()
|| std::fs::metadata(path).map(|m| m.len()).unwrap_or_default() < 2
{
image_handles.insert(image_key.clone(), TextureDownloadState::Downloading);
let to_download = ToDownload {
path: path.to_path_buf(),
url: url.unwrap().to_string(),
app_name: "Thumbnail".to_string(),
image_type: *image_type,
};
let image_handles = image_handles.clone();
let image_key = image_key.clone();
if let Some(rt) = rt {
rt.spawn_blocking(move || {
block_on(crate::steamgriddb::download_to_download(&to_download))
.unwrap();
image_handles.insert(image_key, TextureDownloadState::Downloaded);
});
}
} else {
image_handles.insert(image_key.clone(), TextureDownloadState::Downloaded);
}
}
impl GameButton {
pub fn new(path: &Path) -> Self {
Self {
max_width: 200.0,
path: path.to_path_buf(),
text: Default::default(),
image_type: ImageType::Grid,
}
}
pub fn width(&mut self, max_width: f32) -> &mut Self {
self.max_width = max_width;
self
}
pub fn text(&mut self, text: &str) -> &mut Self {
self.text = text.to_string();
self
}
pub fn image_type(&mut self, image_type:&ImageType) -> &mut Self{
self.image_type = *image_type;
self
}
pub fn show_download(
&self,
ui: &mut egui::Ui,
image_handles: &ImageHandles,
rt: &Runtime,
url: &str,
) -> bool {
self.render_possible_image(ui, image_handles, Some(rt), Some(url))
}
pub fn show(&self, ui: &mut egui::Ui, image_handles: &ImageHandles) -> bool {
self.render_possible_image(ui, image_handles, None, None)
}
fn render_possible_image(
&self,
ui: &mut egui::Ui,
image_handles: &ImageHandles,
rt: Option<&Runtime>,
url: Option<&str>,
) -> bool {
{
let path = &self.path;
let image_key = path.to_string_lossy().to_string();
match image_handles.get_mut(&image_key) {
Some(mut state) => {
match state.value() {
TextureDownloadState::Downloading => {
ui.ctx().request_repaint();
//nothing to do,just wait
ui.spinner();
}
TextureDownloadState::Downloaded => {
//Need to load
let image_data = load_image_from_path(path);
match image_data {
Ok(image_data) => {
let handle = ui.ctx().load_texture(
&image_key,
image_data,
egui::TextureOptions::LINEAR,
);
*state.value_mut() = TextureDownloadState::Loaded(handle);
ui.spinner();
}
Err(_) => *state.value_mut() = TextureDownloadState::Failed,
}
ui.ctx().request_repaint();
}
TextureDownloadState::Loaded(texture_handle) => {
//need to show
let mut size = texture_handle.size_vec2();
clamp_to_width(&mut size, self.max_width);
let image_button = ImageButton::new(texture_handle, size);
if ui
.add_sized(size, image_button)
.on_hover_text(&self.text)
.clicked()
{
return true;
}
}
TextureDownloadState::Failed => {
let button = ui.add_sized(
[self.max_width, self.max_width * self.image_type.ratio()],
Button::new(&self.text).wrap(true),
);
if button.clicked() {
return true;
}
}
}
}
None => {
match url {
Some(url) => {
download_image(
path,
image_handles,
&image_key,
url,
&self.image_type,
rt,
);
}
None => {
//Not possible to download
if !path.exists() {
image_handles.insert(image_key, TextureDownloadState::Failed);
}
}
}
}
}
false
}
}
false
}
impl Default for GameButton {
fn default() -> Self {
Self::new(Path::new(""))
}
}
fn download_image(
path: &Path,
image_handles: &ImageHandles,
image_key: &str,
url: &str,
image_type: &ImageType,
rt: Option<&Runtime>,
) {
//We need to start a download
//Redownload if file is too small
if !path.exists() || std::fs::metadata(path).map(|m| m.len()).unwrap_or_default() < 2 {
image_handles.insert(image_key.to_string(), TextureDownloadState::Downloading);
let to_download = ToDownload {
path: path.to_path_buf(),
url: url.to_string(),
app_name: "Thumbnail".to_string(),
image_type: *image_type,
};
let image_handles = image_handles.clone();
let image_key = image_key.to_string();
if let Some(rt) = rt {
rt.spawn_blocking(move || {
match block_on(crate::steamgriddb::download_to_download(&to_download)) {
Ok(_) => {
image_handles.insert(image_key, TextureDownloadState::Downloaded);
}
Err(err) => {
println!(
"Failed downloading image {} error: {:?}",
to_download.url, err
);
image_handles.insert(image_key, TextureDownloadState::Failed);
}
}
});
}
} else {
image_handles.insert(image_key.to_string(), TextureDownloadState::Downloaded);
}
}
+1 -3
View File
@@ -2,6 +2,4 @@ mod steam_user_select;
mod game_image_button;
pub use steam_user_select::render_user_select;
pub use game_image_button::render_image_from_path;
pub use game_image_button::render_image_from_path_or_url;
pub use game_image_button::render_image_from_path_image_type;
pub use game_image_button::GameButton;
+4 -4
View File
@@ -23,19 +23,19 @@ pub mod ui_images {
pub const LOGO_ICON: &[u8] = include_bytes!("../../resources/logo_small.png");
pub fn get_import_image() -> ImageData {
ImageData::Color(load_image_from_memory(IMPORT_GAMES_IMAGE).unwrap())
ImageData::Color(load_image_from_memory(IMPORT_GAMES_IMAGE).unwrap_or_default())
}
pub fn get_save_image() -> ImageData {
ImageData::Color(load_image_from_memory(SAVE_IMAGE).unwrap())
ImageData::Color(load_image_from_memory(SAVE_IMAGE).unwrap_or_default())
}
pub fn get_logo() -> ImageData {
ImageData::Color(load_image_from_memory(LOGO_32).unwrap())
ImageData::Color(load_image_from_memory(LOGO_32).unwrap_or_default())
}
pub fn get_logo_icon() -> IconData {
let image = image::load_from_memory(LOGO_ICON).unwrap();
let image = image::load_from_memory(LOGO_ICON).unwrap_or_default();
let image_buffer = image.to_rgba8();
let pixels = image_buffer.as_flat_samples();
IconData {
+1 -1
View File
@@ -6,7 +6,7 @@ use crate::{steam::SteamGameInfo};
#[derive(Debug, Clone)]
pub enum GameType {
Shortcut(ShortcutOwned),
Shortcut(Box<ShortcutOwned>),
SteamGame(SteamGameInfo),
}
+15 -21
View File
@@ -2,19 +2,15 @@ use std::path::{Path, PathBuf};
use steam_shortcuts_util::shortcut::ShortcutOwned;
use crate::{steamgriddb::ImageType, steam::SteamGameInfo};
use super::{gametype::GameType, constants::POSSIBLE_EXTENSIONS};
use crate::{steam::SteamGameInfo, steamgriddb::ImageType};
use super::{constants::POSSIBLE_EXTENSIONS, gametype::GameType};
pub trait HasImageKey {
///Gives a unique key to an image given its type and user path
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String);
}
impl HasImageKey for GameType {
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
match self {
@@ -24,31 +20,29 @@ impl HasImageKey for GameType {
}
}
impl HasImageKey for SteamGameInfo {
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
let mut keys = POSSIBLE_EXTENSIONS
.iter()
.map(|ext| key_from_extension(self.appid, image_type, user_path, ext));
let first = keys.next().unwrap();
let other = keys.find(|(exsists, _, _)| *exsists);
let (_, path, key) = other.unwrap_or(first);
(path, key)
let app_id = self.appid;
key(app_id, image_type, user_path)
}
}
impl HasImageKey for ShortcutOwned {
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
let mut keys = POSSIBLE_EXTENSIONS
.iter()
.map(|ext| key_from_extension(self.app_id, image_type, user_path, ext));
let first = keys.next().unwrap();
let other = keys.find(|(exsists, _, _)| *exsists);
let (_, path, key) = other.unwrap_or(first);
(path, key)
let app_id = self.app_id;
key(app_id, image_type, user_path)
}
}
fn key(app_id: u32, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
let ext = |ext| key_from_extension(app_id, image_type, user_path, ext);
let keys = POSSIBLE_EXTENSIONS.map(ext);
let other = keys.iter().find(|(exsists, _, _)| *exsists);
let first = ext(POSSIBLE_EXTENSIONS[0]);
let (_, path, key) = other.unwrap_or(&first);
(path.to_path_buf(), key.to_string())
}
fn key_from_extension(
app_id: u32,
image_type: &ImageType,
+77 -83
View File
@@ -1,20 +1,18 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use egui::{Grid, };
use egui::Grid;
use futures::executor::block_on;
use tokio::{ sync::watch};
use tokio::sync::watch;
use crate::{
steamgriddb::{get_image_extension, ImageType, ToDownload},
ui::{
components::GameButton,
images::{
constants::MAX_WIDTH,
hasimagekey::HasImageKey,
image_select_state::{ ImageSelectState},
possible_image::PossibleImage,
useraction::UserAction,
constants::MAX_WIDTH, hasimagekey::HasImageKey, image_select_state::ImageSelectState,
possible_image::PossibleImage, useraction::UserAction,
},
FetcStatus, MyEguiApp, components::render_image_from_path_or_url,
FetcStatus, MyEguiApp,
},
};
@@ -61,16 +59,10 @@ pub fn render_page_pick_image(
.show(ui, |ui| {
for image in images {
let path = image.thumbnail_path.as_path();
if render_image_from_path_or_url(
ui,
&state.image_handles,
path,
column_width,
&image.full_url,
image_type,
&app.rt,
&image.thumbnail_url,
) {
let mut button = GameButton::new(path);
button.width(column_width);
button.text("Pick image");
if button.show_download(ui, &state.image_handles, &app.rt,&image.thumbnail_url) {
return Some(image.clone());
}
column += 1;
@@ -82,9 +74,9 @@ pub fn render_page_pick_image(
None
})
.inner;
if let Some(x) = x {
if let Some(x) = x {
return Some(UserAction::ImageSelected(x));
}
}
}
_ => {
ui.horizontal(|ui| {
@@ -99,76 +91,78 @@ pub fn render_page_pick_image(
pub fn handle_image_selected(app: &mut MyEguiApp, image: PossibleImage) {
//We must have a user here
let user = app.image_selected_state.steam_user.as_ref().unwrap();
let selected_image_type = app
.image_selected_state
.image_type_selected
.as_ref()
.unwrap();
let selected_shortcut = app.image_selected_state.selected_shortcut.as_ref().unwrap();
let state = &app.image_selected_state;
if let (Some(user), Some(selected_image_type), Some(selected_shortcut)) = (
state.steam_user.as_ref(),
state.image_type_selected.as_ref(),
state.selected_shortcut.as_ref(),
) {
let get_image_extension = &get_image_extension(&image.mime);
let ext = get_image_extension;
let to_download_to_path = Path::new(&user.steam_user_data_folder)
.join("config")
.join("grid")
.join(selected_image_type.file_name(selected_shortcut.app_id(), ext));
let ext = get_image_extension(&image.mime);
let to_download_to_path = Path::new(&user.steam_user_data_folder)
.join("config")
.join("grid")
.join(selected_image_type.file_name(selected_shortcut.app_id(), ext));
delete_images_of_type(user, selected_shortcut, selected_image_type);
//Delete old possible images
let data_folder = Path::new(&user.steam_user_data_folder);
//Keep deleting images of this type untill we don't find any more
let mut path = get_shortcut_image_path(app, data_folder);
while Path::new(&path).exists() {
let _ = std::fs::remove_file(&path);
path = get_shortcut_image_path(app, data_folder);
}
//Put the loaded thumbnail into the image handler map, we can use that for preview
let full_image_key = to_download_to_path.to_string_lossy().to_string();
let _ = app
.image_selected_state
.image_handles
.remove(&full_image_key);
let thumbnail_key = image.thumbnail_path.to_string_lossy().to_string();
let thumbnail = app
.image_selected_state
.image_handles
.remove(&thumbnail_key);
if let Some((_key, thumbnail)) = thumbnail {
app.image_selected_state
//Put the loaded thumbnail into the image handler map, we can use that for preview
let full_image_key = to_download_to_path.to_string_lossy().to_string();
let _ = app
.image_selected_state
.image_handles
.insert(full_image_key, thumbnail);
}
.remove(&full_image_key);
let thumbnail_key = image.thumbnail_path.to_string_lossy().to_string();
let thumbnail = app
.image_selected_state
.image_handles
.remove(&thumbnail_key);
if let Some((_key, thumbnail)) = thumbnail {
app.image_selected_state
.image_handles
.insert(full_image_key, thumbnail);
}
let app_name = selected_shortcut.name();
let to_download = ToDownload {
path: to_download_to_path,
url: image.full_url.clone(),
app_name: app_name.to_string(),
image_type: *selected_image_type,
};
app.rt.spawn_blocking(move || {
let _ = block_on(crate::steamgriddb::download_to_download(&to_download));
});
let app_name = selected_shortcut.name();
let to_download = ToDownload {
path: to_download_to_path,
url: image.full_url.clone(),
app_name: app_name.to_string(),
image_type: *selected_image_type,
};
app.rt.spawn_blocking(move || {
let _ = block_on(crate::steamgriddb::download_to_download(&to_download));
});
clear_loaded_images(app);
{
app.image_selected_state.image_type_selected = None;
app.image_selected_state.image_options = watch::channel(FetcStatus::NeedsFetched).1;
clear_loaded_images(app);
{
app.image_selected_state.image_type_selected = None;
app.image_selected_state.image_options = watch::channel(FetcStatus::NeedsFetched).1;
}
}
}
fn get_shortcut_image_path(app: &MyEguiApp, data_folder: &Path) -> String {
app.image_selected_state
.selected_shortcut
.as_ref()
.unwrap()
.key(
&app.image_selected_state.image_type_selected.unwrap(),
data_folder,
)
.1
fn delete_images_of_type(
user: &crate::steam::SteamUsersInfo,
selected_shortcut: &crate::ui::images::gametype::GameType,
selected_image_type: &ImageType,
) {
//Delete old possible images
let data_folder = Path::new(&user.steam_user_data_folder);
//Keep deleting images of this type untill we don't find any more
let mut path = image_path(selected_shortcut, selected_image_type, data_folder);
while path.exists() {
let _ = std::fs::remove_file(path);
path = image_path(selected_shortcut, selected_image_type, data_folder);
}
}
fn image_path(
selected_shortcut: &crate::ui::images::gametype::GameType,
selected_image_type: &ImageType,
data_folder: &Path,
) -> PathBuf {
selected_shortcut.key(selected_image_type, data_folder).0
}
fn clear_loaded_images(app: &mut MyEguiApp) {
+57 -52
View File
@@ -1,10 +1,11 @@
use std::path::Path;
use crate::ui::components::GameButton;
use crate::ui::images::{
gametype::GameType, hasimagekey::HasImageKey, image_select_state::ImageSelectState,
useraction::UserAction, ImageHandles,
};
use crate::{steamgriddb::ImageType, ui::components::render_image_from_path_image_type};
use crate::{steamgriddb::ImageType};
const MAX_WIDTH: f32 = 300.;
@@ -12,59 +13,65 @@ pub fn render_page_shortcut_select_image_type(
ui: &mut egui::Ui,
state: &ImageSelectState,
) -> Option<UserAction> {
let shortcut = state.selected_shortcut.as_ref().unwrap();
let user_path = &state.steam_user.as_ref().unwrap().steam_user_data_folder;
let thumbnail = |ui: &mut egui::Ui, image_type: &ImageType| {
if render_thumbnail(ui, &state.image_handles, shortcut, image_type, user_path) {
Some(UserAction::ImageTypeSelected(*image_type))
} else {
None
}
};
let x = if ui.available_width() > MAX_WIDTH * 3. {
ui.horizontal(|ui| {
let x = ui.vertical(|ui| thumbnail(ui, &ImageType::Grid)).inner;
if x.is_some() {
return x;
let shortcut = &state.selected_shortcut.as_ref();
let user_path = state
.steam_user
.as_ref()
.map(|user| &user.steam_user_data_folder);
if let (Some(shortcut), Some(user_path)) = (shortcut, user_path) {
let thumbnail = |ui: &mut egui::Ui, image_type: &ImageType| {
if render_thumbnail(ui, &state.image_handles, shortcut, image_type, user_path) {
Some(UserAction::ImageTypeSelected(*image_type))
} else {
None
}
let x = ui
.vertical(|ui| {
let types = &[ImageType::Hero, ImageType::WideGrid, ImageType::Logo];
};
let x = if ui.available_width() > MAX_WIDTH * 3. {
ui.horizontal(|ui| {
let x = ui.vertical(|ui| thumbnail(ui, &ImageType::Grid)).inner;
if x.is_some() {
return x;
}
let x = ui
.vertical(|ui| {
let types = &[ImageType::Hero, ImageType::WideGrid, ImageType::Logo];
types
.iter()
.flat_map(|image_type| thumbnail(ui, image_type))
.next()
})
.inner;
if x.is_some() {
return x;
}
ui.vertical(|ui| {
let types = &[ImageType::Icon, ImageType::BigPicture];
types
.iter()
.flat_map(|image_type| thumbnail(ui, image_type))
.next()
})
.inner;
if x.is_some() {
return x;
}
ui.vertical(|ui| {
let types = &[ImageType::Icon, ImageType::BigPicture];
types
.iter()
.flat_map(|image_type| thumbnail(ui, image_type))
.next()
.inner
})
.inner
})
.inner
} else {
let types = ImageType::all();
types
.iter()
.flat_map(|image_type| thumbnail(ui, image_type))
.next()
};
} else {
let types = ImageType::all();
types
.iter()
.flat_map(|image_type| thumbnail(ui, image_type))
.next()
};
if ui
.button("Click here if the images are for a wrong game")
.clicked()
{
return Some(UserAction::CorrectGridId);
if ui
.button("Click here if the images are for a wrong game")
.clicked()
{
return Some(UserAction::CorrectGridId);
}
x
} else {
None
}
x
}
fn render_thumbnail(
@@ -76,12 +83,10 @@ fn render_thumbnail(
) -> bool {
let (path, _key) = shortcut.key(image_type, Path::new(&user_path));
let text = format!("Pick {} image", image_type.name());
render_image_from_path_image_type(
ui,
image_handles,
path.as_path(),
MAX_WIDTH,
&text,
image_type,
)
let mut image = GameButton::new(&path);
image
.width(MAX_WIDTH)
.text(&text)
.image_type(image_type)
.show(ui, image_handles)
}
+23 -16
View File
@@ -4,27 +4,30 @@ use steam_shortcuts_util::shortcut::ShortcutOwned;
use crate::{
steam::SteamUsersInfo,
steamgriddb::{ImageType, CachedSearch},
steamgriddb::{CachedSearch, ImageType},
ui::{
images::{
gametype::GameType, hasimagekey::HasImageKey,
texturestate::TextureDownloadState, useraction::UserAction,
gametype::GameType, hasimagekey::HasImageKey, texturestate::TextureDownloadState,
useraction::UserAction,
},
MyEguiApp, ui_images::load_image_from_path, components::render_image_from_path,
ui_images::load_image_from_path,
MyEguiApp, components::GameButton,
},
};
pub fn render_page_shortcut_images_overview(app: &MyEguiApp, ui: &mut egui::Ui) -> Option<UserAction> {
pub fn render_page_shortcut_images_overview(
app: &MyEguiApp,
ui: &mut egui::Ui,
) -> Option<UserAction> {
let user_info = &app.image_selected_state.steam_user;
let shortcuts = &app.image_selected_state.user_shortcuts;
let width = ui.available_size().x;
let column_width = 100.;
let column_padding = 23.;
let columns = (width / (column_width + column_padding)).floor() as u32;
let mut cur_column = 0;
match shortcuts {
Some(shortcuts) => {
let user_info = &app.image_selected_state.steam_user.as_ref().unwrap();
match (user_info, shortcuts) {
(Some(user_info), Some(shortcuts)) => {
if let Some(action) = egui::Grid::new("ui_images")
.show(ui, |ui| {
for shortcut in shortcuts {
@@ -46,7 +49,7 @@ pub fn render_page_shortcut_images_overview(app: &MyEguiApp, ui: &mut egui::Ui)
return action;
}
}
None => {
_ => {
ui.label("Could not find any shortcuts");
}
}
@@ -56,7 +59,7 @@ pub fn render_page_shortcut_images_overview(app: &MyEguiApp, ui: &mut egui::Ui)
fn render_image(
app: &MyEguiApp,
shortcut: &ShortcutOwned,
user_info: &&SteamUsersInfo,
user_info: &SteamUsersInfo,
column_width: f32,
ui: &mut egui::Ui,
) -> Option<Option<UserAction>> {
@@ -65,18 +68,21 @@ fn render_image(
Path::new(&user_info.steam_user_data_folder),
);
let clicked= render_image_from_path(ui, &app.image_selected_state.image_handles, Path::new(&key), column_width, &shortcut.app_name);
let mut button = GameButton::new(Path::new(&key));
button.text(&shortcut.app_name);
button.width(column_width);
let clicked = button.show(ui, &app.image_selected_state.image_handles);
if clicked {
return Some(Some(UserAction::ShortcutSelected(GameType::Shortcut(
shortcut.clone(),
Box::new(shortcut.clone()),
))));
}
None
}
pub fn handle_shortcut_selected(app: &mut MyEguiApp, shortcut: GameType, ui: &mut egui::Ui) {
let state = &mut app.image_selected_state;
//We must have a user to make see this action;
let user = state.steam_user.as_ref().unwrap();
let state = &mut app.image_selected_state;
//We must have a user to make see this action;
if let Some(user) = state.steam_user.as_ref() {
if let Some(auth_key) = &app.settings.steamgrid_db.auth_key {
let client = steamgriddb_api::Client::new(auth_key);
let search = CachedSearch::new(&client);
@@ -102,3 +108,4 @@ pub fn handle_shortcut_selected(app: &mut MyEguiApp, shortcut: GameType, ui: &mu
}
state.selected_shortcut = Some(shortcut);
}
}
+59 -54
View File
@@ -4,9 +4,9 @@ use super::{
hasimagekey::HasImageKey,
image_select_state::ImageSelectState,
pages::{
handle_correct_grid_request, handle_grid_change, handle_shortcut_selected,
render_page_pick_image, render_page_shortcut_images_overview,
render_page_shortcut_select_image_type, render_page_steam_images_overview, handle_image_selected,
handle_correct_grid_request, handle_grid_change, handle_image_selected,
handle_shortcut_selected, render_page_pick_image, render_page_shortcut_images_overview,
render_page_shortcut_select_image_type, render_page_steam_images_overview,
},
possible_image::PossibleImage,
texturestate::TextureDownloadState,
@@ -157,7 +157,7 @@ impl MyEguiApp {
self.handle_image_type_selected(image_type);
}
UserAction::ImageSelected(image) => {
handle_image_selected(self,image);
handle_image_selected(self, image);
}
UserAction::BackButton => {
self.handle_back_button_action();
@@ -197,11 +197,13 @@ impl MyEguiApp {
.image_selected_state
.selected_shortcut
.as_ref()
.unwrap()
.app_id();
self.settings
.steamgrid_db
.set_image_banned(&image_type, app_id, should_ban);
.map(|m| m.app_id());
if let Some(app_id) = app_id {
self.settings
.steamgrid_db
.set_image_banned(&image_type, app_id, should_ban);
}
self.handle_image_type_clear(image_type);
}
@@ -222,38 +224,39 @@ impl MyEguiApp {
self.rt.spawn_blocking(move || {
let task = download_images(&settings, &users, &mut sender_op);
block_on(task);
let _ = sender_op.unwrap().send(SyncProgress::Done);
if let Some(sender_op) = sender_op {
let _ = sender_op.send(SyncProgress::Done);
}
});
}
}
fn handle_image_type_clear(&mut self, image_type: ImageType) {
let app_id = self
.image_selected_state
.selected_shortcut
.as_ref()
.map(|s| s.app_id());
let data_folder = &self
.image_selected_state
.steam_user
.as_ref()
.unwrap()
.steam_user_data_folder;
for ext in POSSIBLE_EXTENSIONS {
let file_name = image_type.file_name(
self.image_selected_state
.selected_shortcut
.as_ref()
.unwrap()
.app_id(),
ext,
);
let path = Path::new(data_folder)
.join("config")
.join("grid")
.join(file_name);
if path.exists() {
let _ = std::fs::remove_file(&path);
.map(|s| &s.steam_user_data_folder);
if let (Some(app_id), Some(data_folder)) = (app_id, data_folder) {
for ext in POSSIBLE_EXTENSIONS {
let file_name = image_type.file_name(app_id, ext);
let path = Path::new(data_folder)
.join("config")
.join("grid")
.join(file_name);
if path.exists() {
let _ = std::fs::remove_file(&path);
}
let key = path.to_string_lossy().to_string();
self.image_selected_state.image_handles.remove(&key);
}
let key = path.to_string_lossy().to_string();
self.image_selected_state.image_handles.remove(&key);
self.image_selected_state.image_type_selected = None;
}
self.image_selected_state.image_type_selected = None;
}
fn handle_set_game_mode(&mut self, game_mode: GameMode) {
@@ -304,11 +307,6 @@ impl MyEguiApp {
};
}
fn handle_back_button_action(&mut self) {
let state = &mut self.image_selected_state;
if state.possible_names.is_some() {
@@ -331,28 +329,35 @@ fn load_image_grids(
ui: &mut egui::Ui,
) -> Vec<ShortcutOwned> {
let user_info = crate::steam::get_shortcuts_for_user(user);
let mut user_folder = user_info.path.clone();
user_folder.pop();
user_folder.pop();
let mut shortcuts = user_info.shortcuts;
shortcuts.sort_by_key(|s| s.app_name.clone());
let image_type = &ImageType::Grid;
for shortcut in &shortcuts {
let (path, key) = shortcut.key(image_type, &user_folder);
let loaded = state.image_handles.contains_key(&key);
if !loaded && path.exists() {
let image = load_image_from_path(&path);
if let Ok(image) = image {
let texture = ui
.ctx()
.load_texture(&key, image, egui::TextureOptions::LINEAR);
state
.image_handles
.insert(key, TextureDownloadState::Loaded(texture));
match user_info {
Ok(user_info) => {
let mut user_folder = user_info.path.clone();
user_folder.pop();
user_folder.pop();
let mut shortcuts = user_info.shortcuts;
shortcuts.sort_by_key(|s| s.app_name.clone());
let image_type = &ImageType::Grid;
for shortcut in &shortcuts {
let (path, key) = shortcut.key(image_type, &user_folder);
let loaded = state.image_handles.contains_key(&key);
if !loaded && path.exists() {
let image = load_image_from_path(&path);
if let Ok(image) = image {
let texture =
ui.ctx()
.load_texture(&key, image, egui::TextureOptions::LINEAR);
state
.image_handles
.insert(key, TextureDownloadState::Loaded(texture));
}
}
}
shortcuts
}
Err(_err) => {
vec![]
}
}
shortcuts
}
fn render_shortcut_mode_select(state: &ImageSelectState, ui: &mut egui::Ui) -> Option<UserAction> {
+30 -11
View File
@@ -62,14 +62,23 @@ impl MyEguiApp {
}
pub fn restore_backup(steam_settings: &SteamSettings, shortcut_path: &Path) -> bool {
let file_name = shortcut_path.file_name().unwrap();
let file_name = shortcut_path.file_name();
let paths = get_shortcuts_paths(steam_settings);
if let Ok(paths) = paths {
if let (Ok(paths), Some(file_name)) = (paths, file_name) {
for user in paths {
if let Some(user_shortcut_path) = user.shortcut_path {
if file_name.to_string_lossy().starts_with(&user.user_id) {
std::fs::copy(shortcut_path, Path::new(&user_shortcut_path)).unwrap();
println!("Restored shortcut to path : {}", user_shortcut_path);
match std::fs::copy(shortcut_path, Path::new(&user_shortcut_path)) {
Ok(_) => {
println!("Restored shortcut to path : {}", user_shortcut_path);
}
Err(err) => {
eprintln!(
"Failed to restored shortcut to path : {} gave error: {:?}",
user_shortcut_path, err
);
}
}
return true;
}
}
@@ -100,7 +109,7 @@ pub fn load_backups() -> Vec<PathBuf> {
result
}
const DATE_FORMAT :&str = "[year]-[month]-[day]-[hour]-[minute]-[second]";
const DATE_FORMAT: &str = "[year]-[month]-[day]-[hour]-[minute]-[second]";
pub fn backup_shortcuts(steam_settings: &SteamSettings) {
use time::OffsetDateTime;
@@ -108,18 +117,28 @@ pub fn backup_shortcuts(steam_settings: &SteamSettings) {
let backup_folder = get_backups_flder();
let paths = get_shortcuts_paths(steam_settings);
let date = OffsetDateTime::now_utc();
let format = format_description::parse(DATE_FORMAT).unwrap();
let date_string = date.format(&format).unwrap();
if let Ok(user_infos) = paths {
let format = format_description::parse(DATE_FORMAT);
if let Ok(format) = format{
let date_string = date.format(&format);
if let (Ok(date_string),Ok(user_infos)) = (date_string,paths) {
for user_info in user_infos {
if let Some(shortcut_path) = user_info.shortcut_path {
let new_path = backup_folder.join(format!(
"{}-{}-shortcuts.vdf",
user_info.user_id, date_string
));
println!("Backed up shortcut at: {:?}", new_path);
std::fs::copy(shortcut_path, &new_path).unwrap();
match std::fs::copy(shortcut_path, &new_path) {
Ok(_) => {
println!("Backed up shortcut at: {:?}", new_path);
}
Err(err) => {
eprintln!(
"Failed to backup shortcut at: {:?}, error: {:?}",
new_path, err
);
}
}
}
}
}}
}
}
+3 -1
View File
@@ -26,7 +26,9 @@ impl MyEguiApp {
let mut user_info = vec![];
for user in users {
let shortcut_info = get_shortcuts_for_user(&user);
user_info.push(shortcut_info);
if let Ok(shortcut_info) = shortcut_info {
user_info.push(shortcut_info);
}
}
user_info
})
+18 -7
View File
@@ -4,6 +4,7 @@ use futures::executor::block_on;
use steam_shortcuts_util::shortcut::ShortcutOwned;
use tokio::sync::watch;
use tokio::task::JoinHandle;
use crate::config::get_renames_file;
use crate::platforms::ShortcutToImport;
@@ -123,7 +124,14 @@ impl MyEguiApp {
});
}
pub fn run_sync(&mut self, wait: bool) {
pub fn run_sync_blocking(&mut self) -> eyre::Result<()> {
self.run_sync(true)
}
pub fn run_sync_async(&mut self) {
let _ = self.run_sync(false);
}
fn run_sync(&mut self, wait: bool) -> eyre::Result<()> {
let (sender, reciever) = watch::channel(SyncProgress::NotStarted);
let settings = self.settings.clone();
if settings.steam.stop_steam {
@@ -136,7 +144,7 @@ impl MyEguiApp {
let _ = sender.send(SyncProgress::Starting);
if all_ready {
let shortcuts_to_import = get_all_games(&self.games_to_sync);
let handle = self.rt.spawn_blocking(move || {
let handle: JoinHandle<eyre::Result<()>> = self.rt.spawn_blocking(move || {
#[cfg(target_family = "unix")]
setup_proton(shortcuts_to_import.iter());
@@ -145,12 +153,11 @@ impl MyEguiApp {
let mut some_sender = Some(sender);
backup_shortcuts(&settings.steam);
let usersinfo =
sync::sync_shortcuts(&settings, &import_games, &mut some_sender, &renames)
.unwrap();
sync::sync_shortcuts(&settings, &import_games, &mut some_sender, &renames)?;
let task = download_images(&settings, &usersinfo, &mut some_sender);
block_on(task);
//Run a second time to fix up shortcuts after images are downloaded
if let Err(e) = sync::fix_all_shortcut_icons(&settings){
if let Err(e) = sync::fix_all_shortcut_icons(&settings) {
eprintln!("Could not fix shortcuts with error {e}");
}
@@ -160,11 +167,13 @@ impl MyEguiApp {
if settings.steam.start_steam {
crate::steam::ensure_steam_started(&settings.steam);
}
Ok(())
});
if wait {
self.rt.block_on(handle).unwrap();
self.rt.block_on(handle)??;
}
}
Ok(())
}
}
@@ -202,6 +211,8 @@ where
crate::sync::symlinks::create_sym_links(&shortcut_info.shortcut);
}
}
setup_proton_games(&shortcuts_to_proton);
if let Err(err) = setup_proton_games(&shortcuts_to_proton){
eprintln!("failed to save proton settings: {:?}",err);
}
}
}
+26 -20
View File
@@ -15,13 +15,14 @@ use crate::{
};
use super::{
images::ImageSelectState,
ui_colors::{
BACKGROUND_COLOR, BG_STROKE_COLOR, EXTRA_BACKGROUND_COLOR, LIGHT_ORANGE, ORANGE, PURLPLE,
TEXT_COLOR,
},
ui_images::{get_import_image, get_logo, get_logo_icon, get_save_image},
ui_import_games::FetcStatus,
BackupState, DiconnectState, images::ImageSelectState,
BackupState, DiconnectState,
};
const SECTION_SPACING: f32 = 25.0;
@@ -70,12 +71,12 @@ pub struct MyEguiApp {
}
impl MyEguiApp {
pub fn new() -> Self {
let mut runtime = Runtime::new().unwrap();
let settings = Settings::new().expect("We must be able to load our settings");
pub fn new() -> eyre::Result<Self> {
let mut runtime = Runtime::new()?;
let settings = Settings::new()?;
let platforms = get_platforms();
let games_to_sync = create_games_to_sync(&mut runtime, &platforms);
Self {
Ok(Self {
selected_menu: Menues::Import,
settings,
rt: runtime,
@@ -88,7 +89,7 @@ impl MyEguiApp {
rename_map: get_rename_map(),
current_edit: Option::None,
platforms,
}
})
}
fn render_import_button(&mut self, ui: &mut egui::Ui) {
@@ -121,17 +122,19 @@ impl MyEguiApp {
let texture = self.get_import_image(ui);
let size = texture.size_vec2();
let image_button = ImageButton::new(texture, size * 0.40);
if all_ready && !syncing{
if all_ready && !syncing {
if ui
.add(image_button)
.on_hover_text("Import your games into steam")
.clicked(){
save_settings(&self.settings, &self.platforms);
self.run_sync(false);
.clicked()
{
if let Err(err) = save_settings(&self.settings, &self.platforms){
eprintln!("Failed to save settings {:?}",err);
}
}else{
ui
.add(image_button)
self.run_sync_async();
}
} else {
ui.add(image_button)
.on_hover_text("Waiting for sync to finish");
}
}
@@ -265,7 +268,9 @@ impl App for MyEguiApp {
let save_button = ImageButton::new(texture, size * 0.5);
if ui.add(save_button).on_hover_text("Save settings").clicked() {
save_settings(&self.settings, &self.platforms);
if let Err(err) = save_settings(&self.settings, &self.platforms){
eprintln!("Failed to save settings: {:?}",err);
}
}
});
}
@@ -341,17 +346,17 @@ fn setup(ctx: &egui::Context) {
create_style(&mut style);
ctx.set_style(style);
}
pub fn run_sync() {
let mut app = MyEguiApp::new();
pub fn run_sync() -> eyre::Result<()>{
let mut app = MyEguiApp::new()?;
while !all_ready(&app.games_to_sync) {
println!("Finding games, trying again in 500ms");
std::thread::sleep(Duration::from_secs_f32(0.5));
}
app.run_sync(true);
app.run_sync_blocking()
}
pub fn run_ui(args: Vec<String>) {
let app = MyEguiApp::new();
pub fn run_ui(args: Vec<String>) -> eyre::Result<()>{
let app = MyEguiApp::new()?;
let no_v_sync = args.contains(&"--no-vsync".to_string());
let fullscreen = is_fullscreen(&args);
let native_options = eframe::NativeOptions {
@@ -370,6 +375,7 @@ pub fn run_ui(args: Vec<String>) {
Box::new(app)
}),
);
Ok(())
}
fn is_fullscreen(args: &[String]) -> bool {
@@ -377,5 +383,5 @@ fn is_fullscreen(args: &[String]) -> bool {
Ok(value) => !value.is_empty(),
Err(_) => false,
};
is_steam_mode || args.contains(&"--fullscreen".to_string())
is_steam_mode || args.contains(&"--fullscreen".to_string())
}