use crate::utils::{self, ProjectDirError}; use log::{debug, info, trace, warn}; use save_sync::db::{establish_connection, query::GameSaveQuery, Database}; use save_sync::game::{GameFile, GameSaveLocation}; use std::path::{Path, PathBuf}; use thiserror::Error; use walkdir::WalkDir; #[derive(Debug, Clone)] pub struct Archive { tracked_games: Vec, data_root: PathBuf, config_root: PathBuf, } impl Archive { /// Returns a Result, potentially containing a new instance of Archive /// /// This method infers the proper config and data user directories, /// so having `$HOME` unset will cause the method to fail. /// /// # Examples /// ``` /// # use client::archive::Archive; /// let archive_res = Archive::try_default(); /// ``` pub fn try_default() -> Result { let user = utils::get_project_dirs()?; let data = user.data_dir().to_path_buf(); let config = user.config_dir().to_path_buf(); debug!("Created default Archive with: {:?} and {:?}", data, config); Ok(Self { tracked_games: Vec::new(), data_root: data, config_root: config, }) } /// Creates a new instance of Archive /// /// # Arguments /// * `data_root` - Path to the user application data folder /// * `config_root` - Path to the user configuration data folder. /// /// # Examples /// ``` /// # use client::archive::Archive; /// let archive = Archive::new("/home/user/.local/share/save-sync", "/home/user/.config/save-sync"); /// ``` pub fn new>(data_root: P, config_root: P) -> Self { let data_root = data_root.as_ref(); let config_root = config_root.as_ref(); debug!( "Created new Archive with: {:?} and {:?}", data_root, config_root ); Self { tracked_games: Vec::new(), data_root: data_root.to_path_buf(), config_root: config_root.to_path_buf(), } } } impl Archive { /// Adds a path and its contents to the list of tracked game files /// /// TODO: Add note here about how GameSaveLocation, GameFile and tracking individual files rather than directories work /// /// # Arguments /// * `path` - The path which will be tracked along with any children it may have /// /// # Examples /// ``` /// # use client::archive::Archive; /// let mut archive = Archive::try_default().expect("Failed to create an Archive"); /// let save_path = "/home/user/Documents/generic_company/generic_game/save_folder"; /// match archive.track_game(save_path) { /// Ok(_) => println!("Save Sync is now tracking {}", save_path), /// Err(err) => eprintln!("Failed to track {}: {:?}", save_path, err) /// }; /// ``` pub fn track_game>(&mut self, path: P) -> Result<(), GameTrackError> { let game_save_loc = self.get_game_save_files(path, None)?; Self::write_game_save(&game_save_loc); self.tracked_games.push(game_save_loc); Ok(()) } /// Adds a path and its contents to the list of tracked game files /// /// TODO: Add similar note to the one in [`track_game`] /// /// # Arguments /// * `path` - The path which will be tracked along with any children it may have /// /// # Examples /// ``` /// # use client::archive::Archive; /// let mut archive = Archive::try_default().expect("Failed to create an Archive"); /// let save_path = "/home/user/Documents/generic_company/generic_game/save_folder"; /// let friendly_name = "Generic Game"; /// match archive.track_game_with_friendly(save_path, friendly_name) { /// Ok(_) => println!("Save Sync is now tracking {}", friendly_name), /// Err(err) => eprintln!("Save Sync failed to start tracking {}", friendly_name) /// }; pub fn track_game_with_friendly

(&mut self, path: P, name: &str) -> Result<(), GameTrackError> where P: AsRef, { let game_save_loc = self.get_game_save_files(path, Some(name))?; Self::write_game_save(&game_save_loc); self.tracked_games.push(game_save_loc); Ok(()) } fn get_game_save_files

( &mut self, path: P, friendly_name: Option<&str>, ) -> Result where P: AsRef, { use GameTrackError::*; let path = path.as_ref(); let mut game_files: Vec = Vec::new(); if path.is_dir() { for maybe_entry in WalkDir::new(path) { match maybe_entry { Ok(entry) => { if entry.path().is_file() { let game_file = GameFile::new(entry.path())?; game_files.push(game_file); } // FIXME: WalkDir will also return the paths of files. DO we want to track these? // if so, how will be do that? } Err(err) => { let io_err: std::io::Error = err.into(); return Err(io_err.into()); } } } } else if path.is_file() { // We've been requested to track an individual file. todo!("Implement the ability to track a single file instead of a directory") } else { return Err(UnknownFileSystemObject(path.to_path_buf())); } // FIXME: There most likely is a function that does this (check clippy) let friendly_name = friendly_name.map(|s| s.to_owned()); Ok(GameSaveLocation::new(path, game_files, friendly_name)) } } impl Archive { pub fn drop_game>(path: P) -> Result, GameDropError> { let conn = establish_connection(); let query = GameSaveQuery::Path(path.as_ref()); Ok(Database::drop_game_save(&conn, query)?) } pub fn drop_game_with_friendly(name: &str) -> Result, GameDropError> { let conn = establish_connection(); let query = GameSaveQuery::FriendlyName(name); Ok(Database::drop_game_save(&conn, query)?) } } impl Archive { pub fn get_game

(path: P) -> Result, GameGetError> where P: AsRef, { let conn = establish_connection(); let query = GameSaveQuery::Path(path.as_ref()); Ok(Database::get_game_save(&conn, query)?) } pub fn get_game_with_friendly(name: &str) -> Result, GameGetError> { let conn = establish_connection(); let query = GameSaveQuery::FriendlyName(name); Ok(Database::get_game_save(&conn, query)?) } } impl Archive { pub fn get_all_games() -> Result>, GameGetError> { let conn = establish_connection(); Ok(Database::get_all_game_saves(&conn)?) } } impl Archive { fn write_game_save(game_save_loc: &GameSaveLocation) -> Result<(), GameTrackError> { let conn = establish_connection(); Ok(Database::write_game_save(&conn, &game_save_loc)?) } } #[derive(Error, Debug)] pub enum GameTrackError { #[error(transparent)] IoError(#[from] std::io::Error), #[error("{0:?} is not a supported inode type (File System Object)")] UnknownFileSystemObject(PathBuf), // FIXME: Is there a better name for this? #[error(transparent)] DatabaseError(#[from] save_sync::db::DatabaseError), } #[derive(Error, Debug)] pub enum GameDropError { #[error("Unable to find Game with the name {0}")] UnknownFriendlyName(String), #[error("Unable to find game with the path {0:?}")] UnknownPath(PathBuf), #[error(transparent)] DatabaseError(#[from] save_sync::db::DatabaseError), } #[derive(Error, Debug)] pub enum GameGetError { #[error(transparent)] DatabaseError(#[from] save_sync::db::DatabaseError), }