Merge branch 'dev'
This commit is contained in:
commit
864258cc01
|
@ -6,5 +6,9 @@ steps:
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
image: rust:latest
|
image: rust:latest
|
||||||
commands:
|
commands:
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
- export DATABASE_URL=/tmp/save-sync.db
|
||||||
|
>>>>>>> dev
|
||||||
- cargo build --all
|
- cargo build --all
|
||||||
- cargo test --all
|
- cargo test --all
|
|
@ -1 +1,2 @@
|
||||||
RUST_LOG=
|
RUST_LOG=
|
||||||
|
DATABASE_URL=
|
|
@ -13,5 +13,7 @@ members = ["server", "client"]
|
||||||
uuid = { version = "^0.8", features = ["v4"] }
|
uuid = { version = "^0.8", features = ["v4"] }
|
||||||
twox-hash = "^1.6"
|
twox-hash = "^1.6"
|
||||||
thiserror = "^1.0"
|
thiserror = "^1.0"
|
||||||
|
diesel = { version = "^1.4", features = ["sqlite"] }
|
||||||
|
dotenv = "^0.15"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl Archive {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use crate::client::archive::Archive;
|
/// # use client::archive::Archive;
|
||||||
/// let archive_res = Archive::try_default();
|
/// let archive_res = Archive::try_default();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn try_default() -> Result<Self, ProjectDirError> {
|
pub fn try_default() -> Result<Self, ProjectDirError> {
|
||||||
|
@ -45,7 +45,7 @@ impl Archive {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use crate::client::archive::Archive;
|
/// # use client::archive::Archive;
|
||||||
/// let archive = Archive::new("/home/user/.local/share/save-sync", "/home/user/.config/save-sync");
|
/// 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 {
|
pub fn new<P: AsRef<Path>>(data_root: P, config_root: P) -> Self {
|
||||||
|
@ -64,7 +64,7 @@ impl Archive {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a path and it's contents to the list of tracked game files
|
/// 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
|
/// TODO: Add note here about how GameSaveLocation, GameFile and tracking individual files rather than directories work
|
||||||
///
|
///
|
||||||
|
@ -73,12 +73,21 @@ impl Archive {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
|
<<<<<<< HEAD
|
||||||
/// # use crate::client::archive::Archive;
|
/// # use crate::client::archive::Archive;
|
||||||
/// let mut archive = Archive::try_default().unwrap();
|
/// let mut archive = Archive::try_default().unwrap();
|
||||||
/// let game_save_path = "/home/user/Documents/generic_company/generic_game/save_folder";
|
/// let game_save_path = "/home/user/Documents/generic_company/generic_game/save_folder";
|
||||||
/// match archive.track_game(game_save_path) {
|
/// match archive.track_game(game_save_path) {
|
||||||
/// Ok(_) => println!("Save Sync is now tracking {}", game_save_path),
|
/// Ok(_) => println!("Save Sync is now tracking {}", game_save_path),
|
||||||
/// Err(err) => eprintln!("Failed to track {}: {:?}", game_save_path, err)
|
/// Err(err) => eprintln!("Failed to track {}: {:?}", game_save_path, err)
|
||||||
|
=======
|
||||||
|
/// # 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)
|
||||||
|
>>>>>>> dev
|
||||||
/// };
|
/// };
|
||||||
/// ```
|
/// ```
|
||||||
pub fn track_game<P: AsRef<Path>>(&mut self, path: P) -> Result<(), GameTrackError> {
|
pub fn track_game<P: AsRef<Path>>(&mut self, path: P) -> Result<(), GameTrackError> {
|
||||||
|
@ -87,6 +96,23 @@ impl Archive {
|
||||||
Ok(())
|
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>
|
pub fn track_game_with_friendly<P>(&mut self, path: P, name: &str) -> Result<(), GameTrackError>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
|
@ -149,7 +175,7 @@ impl Archive {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use crate::client::archive::Archive;
|
/// # use client::archive::Archive;
|
||||||
/// let mut archive = Archive::try_default().unwrap();
|
/// let mut archive = Archive::try_default().unwrap();
|
||||||
/// let drop_res = archive.drop_game("/home/user/Documents/generic_company/generic_game/save_folder");
|
/// let drop_res = archive.drop_game("/home/user/Documents/generic_company/generic_game/save_folder");
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -170,7 +196,7 @@ impl Archive {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use crate::client::archive::Archive;
|
/// # use client::archive::Archive;
|
||||||
/// let mut archive = Archive::try_default().unwrap();
|
/// let mut archive = Archive::try_default().unwrap();
|
||||||
/// let drop_res = archive.drop_game("raging_loop");
|
/// let drop_res = archive.drop_game("raging_loop");
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# For documentation on how to configure this file,
|
||||||
|
# see diesel.rs/guides/configuring-diesel-cli
|
||||||
|
|
||||||
|
[print_schema]
|
||||||
|
file = "src/schema.rs"
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP TABLE game_file
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
CREATE TABLE game_file (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
original_path BLOB NOT NULL, -- TEXT assumes a Unicode Encoding
|
||||||
|
file_hash INTEGER NOT NULL, -- u64, but that's not
|
||||||
|
game_save_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (game_save_id) REFERENCES game_save_location (id)
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP TABLE game_save_location
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
CREATE TABLE game_save_location (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
friendly_name TEXT -- This can be null
|
||||||
|
original_PATH BLOB NOT NULL,
|
||||||
|
uuid BLOB NOT NULL
|
||||||
|
)
|
|
@ -0,0 +1,25 @@
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel::sqlite::SqliteConnection;
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
/// Establishes a DB Connection with a Sqlite Database
|
||||||
|
///
|
||||||
|
/// Will **panic** if:
|
||||||
|
/// * `$DATABASE_URL` is not set
|
||||||
|
/// * Save Sync fails to connect to the database at `$DATABASE_URL`
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use save_sync::db::establish_connection;
|
||||||
|
/// let connection = establish_connection();
|
||||||
|
/// ```
|
||||||
|
pub fn establish_connection() -> SqliteConnection {
|
||||||
|
dotenv().ok();
|
||||||
|
|
||||||
|
// TODO: Consider whether it is best practice to panic here
|
||||||
|
// or have establish_connection return a Result with a thiserror enum
|
||||||
|
|
||||||
|
let db_url = env::var("DATABASE_URL").expect("$DATABASE_URL was not set");
|
||||||
|
SqliteConnection::establish(&db_url).expect(&format!("Error connecting to {}", db_url))
|
||||||
|
}
|
44
src/game.rs
44
src/game.rs
|
@ -9,6 +9,7 @@ use uuid::Uuid;
|
||||||
// TODO: Change this seed
|
// TODO: Change this seed
|
||||||
const XXHASH64_SEED: u64 = 1337;
|
const XXHASH64_SEED: u64 = 1337;
|
||||||
|
|
||||||
|
/// GameSaveLocation represents a path that holds the files of a Game's saves.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GameSaveLocation {
|
pub struct GameSaveLocation {
|
||||||
pub friendly_name: Option<String>,
|
pub friendly_name: Option<String>,
|
||||||
|
@ -18,6 +19,21 @@ pub struct GameSaveLocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameSaveLocation {
|
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<GameFile> = Vec::new();
|
||||||
|
/// let friendly_name = "Some Game".to_string();
|
||||||
|
/// let game_save_location = GameSaveLocation::new(path, files, Some(friendly_name));
|
||||||
|
/// ```
|
||||||
pub fn new<P>(path: P, files: Vec<GameFile>, friendly_name: Option<String>) -> Self
|
pub fn new<P>(path: P, files: Vec<GameFile>, friendly_name: Option<String>) -> Self
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
|
@ -31,6 +47,10 @@ impl GameSaveLocation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GameFile {
|
pub struct GameFile {
|
||||||
pub original_path: PathBuf,
|
pub original_path: PathBuf,
|
||||||
|
@ -38,6 +58,23 @@ pub struct GameFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameFile {
|
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<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
|
pub fn new<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
|
@ -55,6 +92,7 @@ impl GameFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The Error type for Interactions involving GameFiles
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum GameFileError {
|
pub enum GameFileError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
@ -62,20 +100,18 @@ pub enum GameFileError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct BackupPath {
|
struct BackupPath {
|
||||||
inner: Option<PathBuf>,
|
inner: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackupPath {
|
impl BackupPath {
|
||||||
pub fn new<P: AsRef<Path>>(path: P) -> Self {
|
fn new<P: AsRef<Path>>(path: P) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Some(path.as_ref().to_path_buf()),
|
inner: Some(path.as_ref().to_path_buf()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackupPath {}
|
|
||||||
|
|
||||||
struct HashWriter<T: Hasher>(T);
|
struct HashWriter<T: Hasher>(T);
|
||||||
|
|
||||||
impl<T: Hasher> Write for HashWriter<T> {
|
impl<T: Hasher> Write for HashWriter<T> {
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate diesel;
|
||||||
|
|
||||||
|
pub mod db;
|
||||||
pub mod game;
|
pub mod game;
|
||||||
|
pub mod models;
|
||||||
|
pub mod schema;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Queryable)]
|
||||||
|
pub struct GameFile {
|
||||||
|
pub id: i32,
|
||||||
|
pub original_path: PathBuf,
|
||||||
|
pub file_hash: u64,
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
table! {
|
||||||
|
game_file (id) {
|
||||||
|
id -> Nullable<Integer>,
|
||||||
|
original_path -> Binary,
|
||||||
|
file_hash -> Integer,
|
||||||
|
game_save_id -> Integer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
game_save_location (id) {
|
||||||
|
id -> Nullable<Integer>,
|
||||||
|
friendly_name -> Text,
|
||||||
|
uuid -> Binary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
joinable!(game_file -> game_save_location (game_save_id));
|
||||||
|
|
||||||
|
allow_tables_to_appear_in_same_query!(
|
||||||
|
game_file,
|
||||||
|
game_save_location,
|
||||||
|
);
|
Loading…
Reference in New Issue