use std::fs::File; use std::hash::Hasher; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use thiserror::Error; use twox_hash::XxHash64; use uuid::Uuid; // TODO: Change this seed const XXHASH64_SEED: u64 = 1337; /// GameSaveLocation represents a path that holds the files of a Game's saves. #[derive(Debug, Clone)] pub struct GameSaveLocation { pub friendly_name: Option, pub original_path: PathBuf, files: Vec, uuid: Uuid, } impl GameSaveLocation { /// Constructs a GameSaveLocation /// /// # Arguments /// * `path` - The path of the Game Save(s) /// * `files` - A Vector containing GameFiles, which are used to track the hashes of game files /// * `friendly_name` - A Friendly name for the Game Save /// /// # Examples /// ``` /// # use save_sync::game::{GameSaveLocation, GameFile}; /// let path = "/home/user/Documents/some_company/some_game/saves"; /// let files: Vec = Vec::new(); /// let friendly_name = "Some Game".to_string(); /// let game_save_location = GameSaveLocation::new(path, files, Some(friendly_name)); /// ``` pub fn new

(path: P, files: Vec, friendly_name: Option) -> Self where P: AsRef, { Self { friendly_name, original_path: path.as_ref().to_path_buf(), files, uuid: Uuid::new_v4(), } } } /// GameFile is the representation of a on-disk file inside of a GameSaveLocation /// /// This class keeps track of a Hash of the file, which allows Save Sync to identify when a /// tracked file has changed #[derive(Debug, Clone)] pub struct GameFile { pub original_path: PathBuf, pub hash: u64, } impl GameFile { /// Constructs a new GameFile /// /// Will fail if: /// * Save Sync is unable to open and read the contents of the file at `path` /// /// # Arguments /// * `path` - The path of the game file /// /// # Examples /// ``` /// # use save_sync::game::GameFile; /// let path = "/home/user/Documents/some_company/some_game/saves"; /// match GameFile::new(path) { /// Ok(_) => { /* Do something with the file */ } /// Err(err) => { eprintln!("Error while attempting to calculate the hash of {}", path)} /// }; /// ``` pub fn new>(path: P) -> std::io::Result { let path = path.as_ref(); let file = File::open(path)?; Ok(Self { original_path: path.to_path_buf(), hash: Self::calculate_hash(file)?, }) } fn calculate_hash(mut buf: impl Read) -> std::io::Result { let mut hash_writer = HashWriter(XxHash64::with_seed(XXHASH64_SEED)); std::io::copy(&mut buf, &mut hash_writer)?; Ok(hash_writer.0.finish()) } } /// The Error type for Interactions involving GameFiles #[derive(Error, Debug)] pub enum GameFileError { #[error(transparent)] IoError(#[from] std::io::Error), } #[derive(Debug, Default)] struct BackupPath { inner: Option, } impl BackupPath { fn new>(path: P) -> Self { Self { inner: Some(path.as_ref().to_path_buf()), } } } struct HashWriter(T); impl Write for HashWriter { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.0.write(buf); Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { self.write(buf).map(|_| ()) } }