chore: clean up warnings and public api

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-03-02 21:39:54 -06:00
parent bf2336386e
commit f098430a78
4 changed files with 99 additions and 102 deletions

View File

@ -1,5 +1,5 @@
use crate::utils::{self, ProjectDirError}; use crate::utils::{self, ProjectDirError};
use log::{debug, info, trace, warn}; use log::debug;
use save_sync::db::{establish_connection, query::GameSaveQuery, Database}; use save_sync::db::{establish_connection, query::GameSaveQuery, Database};
use save_sync::game::{GameFile, GameSaveLocation}; use save_sync::game::{GameFile, GameSaveLocation};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -158,13 +158,13 @@ impl Archive {
} }
impl Archive { impl Archive {
pub fn drop_game<P: AsRef<Path>>(path: P) -> Result<Option<()>, GameDropError> { pub fn drop_game<P: AsRef<Path>>(path: P) -> Result<usize, GameDropError> {
let conn = establish_connection(); let conn = establish_connection();
let query = GameSaveQuery::Path(path.as_ref()); let query = GameSaveQuery::Path(path.as_ref());
Ok(Database::drop_game_save(&conn, query)?) Ok(Database::drop_game_save(&conn, query)?)
} }
pub fn drop_game_with_friendly(name: &str) -> Result<Option<()>, GameDropError> { pub fn drop_game_with_friendly(name: &str) -> Result<usize, GameDropError> {
let conn = establish_connection(); let conn = establish_connection();
let query = GameSaveQuery::FriendlyName(name); let query = GameSaveQuery::FriendlyName(name);
Ok(Database::drop_game_save(&conn, query)?) Ok(Database::drop_game_save(&conn, query)?)

View File

@ -2,9 +2,7 @@ use clap::{crate_authors, crate_description, crate_version, ArgMatches};
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use client::archive::Archive; use client::archive::Archive;
use dotenv::dotenv; use dotenv::dotenv;
use log::{debug, info}; use log::info;
use save_sync::db::{establish_connection, Database};
use std::path::Path;
fn main() { fn main() {
dotenv().ok(); dotenv().ok();
@ -116,42 +114,48 @@ fn tracked_save_info(matches: &ArgMatches) {
Archive::get_game(path_str).expect("Failed to get game save info from Archive") Archive::get_game(path_str).expect("Failed to get game save info from Archive")
}; };
let game = maybe_game.expect("No tracked game save found"); match maybe_game {
Some(game) => {
if let Some(name) = game.friendly_name {
println!("Friendly Name: {}", name);
} else {
println!("Friendly Name: None");
}
if let Some(name) = game.friendly_name { println!("Original Path: {:?}", game.original_path);
println!("Friendly Name: {}", name); println!("UUID: {:?}", game.uuid);
} else { println!("---\nFiles:");
println!("Friendly Name: None");
}
println!("Original Path: {:?}", game.original_path); for file in game.files {
println!("UUID: {:?}", game.uuid); println!("Path: {:?}", file.original_path);
println!("---\nFiles:"); println!("Hash: {}", file.hash);
println!();
for file in game.files { }
println!("Path: {:?}", file.original_path); }
println!("Hash: {}", file.hash); None => println!("No tracked game save was found."),
println!();
} }
} }
fn list_tracked_saves(_matches: &ArgMatches) { fn list_tracked_saves(_matches: &ArgMatches) {
let games = Archive::get_all_games() let maybe_games = Archive::get_all_games().expect("Failed to get all Games from the Archive");
.expect("Failed to get all Games from the Archive")
.expect("There are no tracked Games");
for game in games { match maybe_games {
if let Some(name) = game.friendly_name { Some(games) => {
print!("[{}] ", name); for game in games {
if let Some(name) = game.friendly_name {
print!("[{}] ", name);
}
println!("{:?}", game.original_path);
println!("UUID: {:?}", game.uuid);
println!("---");
}
} }
println!("{:?}", game.original_path); None => println!("There are no tracked games"),
println!("UUID: {:?}", game.uuid);
println!("---");
} }
} }
fn drop_save(matches: &ArgMatches) { fn drop_save(matches: &ArgMatches) {
let maybe_compromised = if let Some(name) = matches.value_of("friendly") { let items_deleted = if let Some(name) = matches.value_of("friendly") {
Archive::drop_game_with_friendly(name).expect("Archive failed to delete from database") Archive::drop_game_with_friendly(name).expect("Archive failed to delete from database")
} else { } else {
let path_str = matches let path_str = matches
@ -161,8 +165,9 @@ fn drop_save(matches: &ArgMatches) {
Archive::drop_game(path_str).expect("Archive failed to delete from database") Archive::drop_game(path_str).expect("Archive failed to delete from database")
}; };
match maybe_compromised { match items_deleted {
Some(()) => println!("Game successfully dropped from the list"), 0 => println!("Save Sync can't drop what was never tracked to begin with"),
None => panic!("Database Invariant broken. Database is corrupted."), 1 => println!("Save Sync successfully dropped the game from the list of tracked games"),
_ => unreachable!(),
} }
} }

View File

@ -1,27 +1,6 @@
use directories::ProjectDirs; use directories::ProjectDirs;
use std::path::Path;
use thiserror::Error; use thiserror::Error;
pub fn calc_file_hash(path: &Path) -> Option<u64> {
unimplemented!()
}
pub fn archive_directory(src: &Path, dst: &Path) -> Result<(), Box<dyn std::error::Error>> {
unimplemented!()
}
pub fn archive_file(src: &Path, dst: &Path) -> Result<(), Box<dyn std::error::Error>> {
unimplemented!()
}
pub fn unarchive_directory(src: &Path, dst: &Path) -> Result<(), Box<dyn std::error::Error>> {
unimplemented!()
}
pub fn unarchive_file(src: &Path, dst: &Path) -> Result<(), Box<dyn std::error::Error>> {
unimplemented!()
}
pub fn get_project_dirs() -> Result<ProjectDirs, ProjectDirError> { pub fn get_project_dirs() -> Result<ProjectDirs, ProjectDirError> {
ProjectDirs::from("dev", "musuka", "save-sync").ok_or(ProjectDirError::HomeNotSet) ProjectDirs::from("dev", "musuka", "save-sync").ok_or(ProjectDirError::HomeNotSet)
} }

109
src/db.rs
View File

@ -74,13 +74,11 @@ impl Database {
pub fn write_game_file(conn: &SqliteConnection, file: &GameFile, needle: i32) -> Result<()> { pub fn write_game_file(conn: &SqliteConnection, file: &GameFile, needle: i32) -> Result<()> {
use super::models::NewGameFile; use super::models::NewGameFile;
use super::schema::game_file; use super::schema::game_file;
use DatabaseError::InvalidPathError; use DatabaseError::InvalidPath;
let hash_bytes: [u8; 8] = file.hash.to_be_bytes(); let hash_bytes: [u8; 8] = file.hash.to_be_bytes();
let path = &file.original_path; let path = &file.original_path;
let path_str = path let path_str = path.to_str().ok_or_else(|| InvalidPath(path.clone()))?;
.to_str()
.ok_or_else(|| InvalidPathError(path.clone()))?;
let new_game_file = NewGameFile { let new_game_file = NewGameFile {
original_path: path_str, original_path: path_str,
@ -104,7 +102,7 @@ impl Database {
) -> ResultantOption<GameFile> { ) -> ResultantOption<GameFile> {
use super::models::DbGameFile; use super::models::DbGameFile;
use super::schema::game_file::dsl::{file_hash, game_file, original_path}; use super::schema::game_file::dsl::{file_hash, game_file, original_path};
use DatabaseError::InvalidPathError; use DatabaseError::{InvalidPath, UnexpectedBehaviour};
let game_files = match query { let game_files = match query {
GameFileQuery::Hash(hash) => { GameFileQuery::Hash(hash) => {
@ -116,17 +114,17 @@ impl Database {
GameFileQuery::Path(path) => { GameFileQuery::Path(path) => {
let path_str = path let path_str = path
.to_str() .to_str()
.ok_or_else(|| InvalidPathError(path.to_path_buf()))?; .ok_or_else(|| InvalidPath(path.to_path_buf()))?;
game_file game_file
.filter(original_path.eq(path_str)) .filter(original_path.eq(path_str))
.load::<DbGameFile>(conn)? .load::<DbGameFile>(conn)?
} }
}; };
let maybe_game_files = if game_files.len() == 1 { let maybe_game_files = match game_files.len() {
Some((&game_files[0]).into()) 0 => None,
} else { 1 => Some((&game_files[0]).into()),
None num => return Err(UnexpectedBehaviour(format!("{:?}", query), num)),
}; };
Ok(maybe_game_files) Ok(maybe_game_files)
@ -168,9 +166,9 @@ impl Database {
// Drop GameFile Implementations // Drop GameFile Implementations
impl Database { impl Database {
fn drop_game_file(conn: &SqliteConnection, query: GameFileQuery) -> ResultantOption<()> { fn drop_game_file(conn: &SqliteConnection, query: GameFileQuery) -> Result<usize> {
use super::schema::game_file::dsl::{file_hash, game_file, original_path}; use super::schema::game_file::dsl::{file_hash, game_file, original_path};
use DatabaseError::InvalidPathError; use DatabaseError::{InvalidPath, UnexpectedBehaviour};
let num_deleted = match query { let num_deleted = match query {
GameFileQuery::Hash(hash) => { GameFileQuery::Hash(hash) => {
@ -181,13 +179,17 @@ impl Database {
GameFileQuery::Path(path) => { GameFileQuery::Path(path) => {
let path_str = path let path_str = path
.to_str() .to_str()
.ok_or_else(|| InvalidPathError(path.to_path_buf()))?; .ok_or_else(|| InvalidPath(path.to_path_buf()))?;
let expr = game_file.filter(original_path.eq(path_str)); let expr = game_file.filter(original_path.eq(path_str));
diesel::delete(expr).execute(conn)? diesel::delete(expr).execute(conn)?
} }
}; };
Ok(if num_deleted == 1 { Some(()) } else { None }) if num_deleted <= 1 {
Ok(num_deleted)
} else {
Err(UnexpectedBehaviour(format!("{:?}", query), num_deleted))
}
} }
} }
@ -219,13 +221,10 @@ impl Database {
// Write Game Save Location to Database // Write Game Save Location to Database
use super::models::NewGameSaveLocation; use super::models::NewGameSaveLocation;
use super::schema::game_save_location; use super::schema::game_save_location;
use DatabaseEntry::Location; use DatabaseError::InvalidPath;
use DatabaseError::{InvalidPathError, MissingDatabaseEntry};
let path = &save_loc.original_path; let path = &save_loc.original_path;
let original_path = path let original_path = path.to_str().ok_or_else(|| InvalidPath(path.clone()))?;
.to_str()
.ok_or_else(|| InvalidPathError(path.clone()))?;
let new_game_save = NewGameSaveLocation { let new_game_save = NewGameSaveLocation {
friendly_name: save_loc.friendly_name.as_deref(), friendly_name: save_loc.friendly_name.as_deref(),
@ -238,16 +237,12 @@ impl Database {
.execute(conn)?; .execute(conn)?;
// Get the ID of the Game Save in the database // Get the ID of the Game Save in the database
let maybe_id = Self::get_game_save_id(conn, GameSaveQuery::Uuid(save_loc.uuid))?; let id = Self::get_game_save_id(conn, GameSaveQuery::Uuid(save_loc.uuid))?
.expect("Failed to read entry in database after writing it.");
match maybe_id { // Write all the GameFiles into the database
Some(id) => { for game_file in &save_loc.files {
// Write all the GameFiles into the database Self::write_game_file(conn, game_file, id)?;
for game_file in &save_loc.files {
Self::write_game_file(conn, game_file, id)?;
}
}
None => return Err(MissingDatabaseEntry(Location(save_loc.clone()))),
} }
Ok(()) Ok(())
@ -264,7 +259,7 @@ impl Database {
use super::schema::game_save_location::dsl::{ use super::schema::game_save_location::dsl::{
friendly_name, game_save_location, id, original_path, uuid, friendly_name, game_save_location, id, original_path, uuid,
}; };
use DatabaseError::InvalidPathError; use DatabaseError::{InvalidPath, UnexpectedBehaviour};
let save_locs = match query { let save_locs = match query {
GameSaveQuery::Id(needle) => game_save_location GameSaveQuery::Id(needle) => game_save_location
@ -276,7 +271,7 @@ impl Database {
GameSaveQuery::Path(path) => { GameSaveQuery::Path(path) => {
let path_str = path let path_str = path
.to_str() .to_str()
.ok_or_else(|| InvalidPathError(path.to_path_buf()))?; .ok_or_else(|| InvalidPath(path.to_path_buf()))?;
game_save_location game_save_location
.filter(original_path.eq(path_str)) .filter(original_path.eq(path_str))
.load::<DbGameSaveLocation>(conn)? .load::<DbGameSaveLocation>(conn)?
@ -289,10 +284,10 @@ impl Database {
} }
}; };
let maybe_save_loc = if save_locs.len() == 1 { let maybe_save_loc = match save_locs.len() {
Some((&save_locs[0]).into()) 0 => None,
} else { 1 => Some((&save_locs[0]).into()),
None num => return Err(UnexpectedBehaviour(format!("{:?}", query), num)),
}; };
Ok(maybe_save_loc) Ok(maybe_save_loc)
@ -302,7 +297,7 @@ impl Database {
use super::schema::game_save_location::dsl::{ use super::schema::game_save_location::dsl::{
friendly_name, game_save_location, id, original_path, uuid, friendly_name, game_save_location, id, original_path, uuid,
}; };
use DatabaseError::InvalidPathError; use DatabaseError::{InvalidPath, UnexpectedBehaviour};
let ids = match query { let ids = match query {
GameSaveQuery::Id(id_value) => vec![id_value], // Why? GameSaveQuery::Id(id_value) => vec![id_value], // Why?
@ -320,7 +315,7 @@ impl Database {
GameSaveQuery::Path(path) => { GameSaveQuery::Path(path) => {
let path_str = path let path_str = path
.to_str() .to_str()
.ok_or_else(|| InvalidPathError(path.to_path_buf()))?; .ok_or_else(|| InvalidPath(path.to_path_buf()))?;
game_save_location game_save_location
.select(id) .select(id)
.filter(original_path.eq(path_str)) .filter(original_path.eq(path_str))
@ -328,31 +323,44 @@ impl Database {
} }
}; };
// FIXME: Is there are more ergonomic way of doing this? let maybe_id = match ids.len() {
Ok(if ids.len() == 1 { Some(ids[0]) } else { None }) 0 => None,
1 => Some(ids[0]),
num => return Err(UnexpectedBehaviour(format!("{:?}", query), num)),
};
Ok(maybe_id)
} }
pub fn get_all_game_saves(conn: &SqliteConnection) -> ResultantOption<Vec<GameSaveLocation>> { pub fn get_all_game_saves(conn: &SqliteConnection) -> ResultantOption<Vec<GameSaveLocation>> {
use super::models::DbGameSaveLocation; use super::models::DbGameSaveLocation;
use super::schema::game_save_location::dsl::game_save_location; use super::schema::game_save_location::dsl::game_save_location;
let db_save_locs = game_save_location let save_locs = game_save_location
.load::<DbGameSaveLocation>(conn)? .load::<DbGameSaveLocation>(conn)?
.iter() .iter()
.map(GameSaveLocation::from) .map(GameSaveLocation::from)
.collect(); .collect::<Vec<GameSaveLocation>>();
Ok(Some(db_save_locs)) let empty = save_locs.is_empty();
Ok(if empty { None } else { Some(save_locs) })
} }
} }
// Drop GameSaveLocation Implementations // Drop GameSaveLocation Implementations
impl Database { impl Database {
pub fn drop_game_save(conn: &SqliteConnection, query: GameSaveQuery) -> ResultantOption<()> { pub fn drop_game_save(conn: &SqliteConnection, query: GameSaveQuery) -> Result<usize> {
use super::schema::game_save_location::dsl::{ use super::schema::game_save_location::dsl::{
friendly_name, game_save_location, id, original_path, uuid, friendly_name, game_save_location, id, original_path, uuid,
}; };
use DatabaseError::InvalidPathError; use DatabaseError::{InvalidPath, UnexpectedBehaviour};
// Drop all files related to the to-be-deleted Game Save
if let Some(game_save) = Self::get_game_save(conn, query)? {
for file in game_save.files {
Self::drop_game_file(conn, GameFileQuery::Hash(file.hash))?;
}
}
let num_deleted = match query { let num_deleted = match query {
GameSaveQuery::Id(needle) => { GameSaveQuery::Id(needle) => {
@ -366,7 +374,7 @@ impl Database {
GameSaveQuery::Path(path) => { GameSaveQuery::Path(path) => {
let path_str = path let path_str = path
.to_str() .to_str()
.ok_or_else(|| InvalidPathError(path.to_path_buf()))?; .ok_or_else(|| InvalidPath(path.to_path_buf()))?;
let expr = game_save_location.filter(original_path.eq(path_str)); let expr = game_save_location.filter(original_path.eq(path_str));
diesel::delete(expr).execute(conn)? diesel::delete(expr).execute(conn)?
} }
@ -377,19 +385,24 @@ impl Database {
} }
}; };
// FIXME: Is there are more ergonomic way of doing this? if num_deleted <= 1 {
Ok(if num_deleted == 1 { Some(()) } else { None }) Ok(num_deleted)
} else {
Err(UnexpectedBehaviour(format!("{:?}", query), num_deleted))
}
} }
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum DatabaseError { pub enum DatabaseError {
#[error("The path {0:?} can not be converted to a UTF-8 String")] #[error("The path {0:?} can not be converted to a UTF-8 String")]
InvalidPathError(PathBuf), InvalidPath(PathBuf),
#[error(transparent)] #[error(transparent)]
OrmError(#[from] diesel::result::Error), Orm(#[from] diesel::result::Error),
#[error("Expected {0:?} to be present in the DB but it was not")] #[error("Expected {0:?} to be present in the DB but it was not")]
MissingDatabaseEntry(DatabaseEntry), MissingDatabaseEntry(DatabaseEntry),
#[error("The Query: \"{0}\" affected {1} database entries (it should only affect one)")]
UnexpectedBehaviour(String, usize),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]