220 lines
7.5 KiB
Rust
220 lines
7.5 KiB
Rust
use crate::utils::{self, ProjectDirError};
|
|
use log::{debug, info, trace, warn};
|
|
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<GameSaveLocation>,
|
|
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<Self, ProjectDirError> {
|
|
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<P: AsRef<Path>>(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(),
|
|
}
|
|
}
|
|
|
|
/// 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().unwrap();
|
|
/// 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<P: AsRef<Path>>(&mut self, path: P) -> Result<(), GameTrackError> {
|
|
let game_save_loc = self.get_game_save_files(path, None)?;
|
|
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().unwrap();
|
|
/// 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<P>(&mut self, path: P, name: &str) -> Result<(), GameTrackError>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let game_save_loc = self.get_game_save_files(path, Some(name))?;
|
|
self.tracked_games.push(game_save_loc);
|
|
Ok(())
|
|
}
|
|
|
|
fn get_game_save_files<P>(
|
|
&mut self,
|
|
path: P,
|
|
friendly_name: Option<&str>,
|
|
) -> Result<GameSaveLocation, GameTrackError>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
use GameTrackError::*;
|
|
|
|
let path = path.as_ref();
|
|
let mut game_files: Vec<GameFile> = 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))
|
|
}
|
|
|
|
/// Removes a game from the list of tracked games
|
|
///
|
|
/// Will fail if:
|
|
/// * The path provided isn't associated with any game
|
|
///
|
|
/// # Arguments
|
|
/// * `path` - The path of the tracked game save location which will be dropped
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// # use client::archive::Archive;
|
|
/// let mut archive = Archive::try_default().unwrap();
|
|
/// let drop_res = archive.drop_game("/home/user/Documents/generic_company/generic_game/save_folder");
|
|
/// ```
|
|
pub fn drop_game<P: AsRef<Path>>(&mut self, path: P) -> Result<(), GameDropError> {
|
|
self.tracked_games
|
|
.retain(|game| game.original_path != path.as_ref());
|
|
|
|
// TODO: Remove backup copy of game save location on disk
|
|
Ok(())
|
|
}
|
|
|
|
/// Removes a game from the list of tracked games using the game's friendly name
|
|
///
|
|
/// Otherwise, is identical to [`Archive::drop_game`]
|
|
///
|
|
/// # Arguments
|
|
/// * `name` - The friendly name of the tracked game save location which will be dropped
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// # use client::archive::Archive;
|
|
/// let mut archive = Archive::try_default().unwrap();
|
|
/// let drop_res = archive.drop_game("raging_loop");
|
|
/// ```
|
|
pub fn drop_game_with_friendly(&mut self, name: &str) -> Result<(), GameDropError> {
|
|
self.tracked_games.retain(|game| match &game.friendly_name {
|
|
Some(f_name) => f_name != name,
|
|
None => false,
|
|
});
|
|
|
|
// TODO: Remove backup copy of game save location on disk
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[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?
|
|
}
|
|
|
|
#[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),
|
|
}
|