Skip to content

Commit

Permalink
feat: proper rust error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Drevoed committed Nov 24, 2022
1 parent 6db219e commit 6792fb5
Show file tree
Hide file tree
Showing 14 changed files with 308 additions and 280 deletions.
13 changes: 13 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ hudsucker = "0.18.0"
tracing = "0.1.37"
tokio-rustls = "0.23.4"
tokio-tungstenite = "0.17.2"
tokio = { version = "1.21.2", features = ["signal"] }
tokio = { version = "1.21.2", features = ["signal", "macros", "rt-multi-thread"] }
rustls-pemfile = "1.0.1"
reqwest = { version = "0.11.13", features = ["stream"] }
futures-util = "0.3.25"
Expand All @@ -63,11 +63,12 @@ regex = "1"
file_diff = "1.0.0"
rust-ini = "0.18.0"
ctrlc = "3.2.3"
thiserror = "1.0.37"

[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = [ "custom-protocol" ]
default = ["custom-protocol"]
# this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = [ "tauri/custom-protocol" ]
custom-protocol = ["tauri/custom-protocol"]
4 changes: 4 additions & 0 deletions src-tauri/rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ newline_style = "Unix"
tab_spaces = 2
use_field_init_shorthand = true
use_try_shorthand = true
format_code_in_doc_comments = true
wrap_comments = true
format_strings = true
imports_granularity = "Crate"
12 changes: 4 additions & 8 deletions src-tauri/src/downloader.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use once_cell::sync::Lazy;

use std::cmp::min;
use std::fs::File;
use std::io::Write;
use std::sync::Mutex;
use std::{cmp::min, fs::File, io::Write, sync::Mutex};

use futures_util::StreamExt;

// This will create a downloads list that will be used to check if we should continue downloading the file
// This will create a downloads list that will be used to check if we should
// continue downloading the file
static DOWNLOADS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));

// Lots of help from: https://gist.github.com/giuliano-oliveira/4d11d6b3bb003dba3a1b53f43d81b30d
Expand Down Expand Up @@ -111,7 +109,5 @@ pub fn stop_download(path: String) {
}

// Delete the file from disk
if let Err(_e) = std::fs::remove_file(&path) {
// Do nothing
}
std::fs::remove_file(&path).unwrap_or(());
}
74 changes: 74 additions & 0 deletions src-tauri/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use serde::Serialize;
use thiserror::Error;

/*
This will not have mutex lock error
because we are going to get rid of locking data structures so
we can leave unwraps `as is` for now
*/

#[derive(Debug, Error)]
/// Error type to signal about different errors that could occur
/// while Cultivation is running.
pub enum CultivationError {
// We hide sensitive information in production
#[cfg_attr(debug_assertions, error("IO error has occured: {:?}", .0))]
#[cfg_attr(not(debug_assertions), error("IO error has occured."))]
IO(#[from] tokio::io::Error),
// #[cfg_attr(debug_assertions, error("IO error has occured: {:?}", .0))]
// #[cfg_attr(not(debug_assertions), error("IO error has occured."))]
// IOStd(#[from] std::io::Error),
#[cfg_attr(debug_assertions, error("Tauri failed with an error: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Tauri failed with an error"))]
Tauri(#[from] tauri::Error),
#[cfg_attr(debug_assertions, error("Failed to create zip archive: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Zip related function has failed"))]
Zip(#[from] zip::result::ZipError),
#[cfg_attr(debug_assertions, error("Failed to unzip archive: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Failed to unzip archive"))]
Unzip(#[from] zip_extract::ZipExtractError),
#[cfg_attr(debug_assertions, error("Failed to unrar: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Failed to unrar"))]
Unrar(#[from] UnrarError),
#[cfg_attr(debug_assertions, error("HTTP request failed: {}", .0))]
#[cfg_attr(not(debug_assertions), error("HTTP request failed"))]
Reqwest(#[from] reqwest::Error),
#[cfg_attr(debug_assertions, error("Malformed language config: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Malformed language config"))]
MalformedLangObject(#[from] serde_json::Error),
#[cfg_attr(debug_assertions, error("Ctrlc handler failed: {}", .0))]
#[cfg_attr(
not(debug_assertions),
error("Could not set graceful shutdown handler")
)]
CtrlCError(#[from] ctrlc::Error),
#[cfg_attr(debug_assertions, error("Certificate generation failed: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Certificate generation has failed."))]
Certificate(#[from] rcgen::RcgenError),
#[cfg_attr(debug_assertions, error("Custom error: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Something went wrong"))]
#[allow(unused)]
Custom(String),
}

#[derive(Debug, Error)]
#[cfg_attr(debug_assertions, error("Failed to unrar: {}", .0))]
#[cfg_attr(not(debug_assertions), error("Failed to unrar"))]
pub struct UnrarError(String);

impl<T> From<unrar::error::UnrarError<T>> for UnrarError {
fn from(value: unrar::error::UnrarError<T>) -> Self {
Self(value.to_string())
}
}

impl Serialize for CultivationError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}

pub type CultivationResult<T> = Result<T, CultivationError>;
136 changes: 56 additions & 80 deletions src-tauri/src/file_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use crate::error::CultivationResult;
use file_diff::diff;
use std::fs;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::{
fs,
io::{Read, Write},
path::PathBuf,
};

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn rename(path: String, new_name: String) {
#[deprecated = "file helper is redundant"]
pub fn rename(path: String, new_name: String) -> CultivationResult<()> {
let mut new_path = path.clone();

// Check if file/folder to replace exists
if fs::metadata(&path).is_err() {
return;
return Ok(());
}

// Check if path uses forward or back slashes
Expand All @@ -19,147 +24,118 @@ pub fn rename(path: String, new_name: String) {

let path_replaced = &path.replace(new_path.split('/').last().unwrap(), &new_name);

match fs::rename(&path, path_replaced) {
Ok(_) => {
println!("Renamed {} to {}", &path, path_replaced);
}
Err(e) => {
println!("Error: {}", e);
}
};
fs::rename(&path, path_replaced).map_err(Into::into)
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn dir_create(path: String) {
fs::create_dir_all(path).unwrap();
#[deprecated = "file helper is redundant"]
pub fn dir_create(path: String) -> CultivationResult<()> {
fs::create_dir_all(path).map_err(Into::into)
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
#[deprecated = "file helper is redundant"]
pub fn dir_exists(path: &str) -> bool {
let path_buf = PathBuf::from(path);
fs::metadata(path_buf).is_ok()
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn dir_is_empty(path: &str) -> bool {
#[deprecated = "file helper is redundant"]
pub fn dir_is_empty(path: &str) -> CultivationResult<bool> {
let path_buf = PathBuf::from(path);
fs::read_dir(path_buf).unwrap().count() == 0
Ok(fs::read_dir(path_buf)?.count() == 0)
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn dir_delete(path: &str) {
#[deprecated = "file helper is redundant"]
pub fn dir_delete(path: &str) -> CultivationResult<()> {
let path_buf = PathBuf::from(path);
fs::remove_dir_all(path_buf).unwrap();
fs::remove_dir_all(path_buf).map_err(Into::into)
}

#[tauri::command]
pub fn are_files_identical(path1: &str, path2: &str) -> bool {
diff(path1, path2)
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn copy_file(path: String, new_path: String) -> bool {
#[deprecated = "file helper is redundant"]
pub fn copy_file(path: String, new_path: String) -> CultivationResult<()> {
let filename = &path.split('/').last().unwrap();
let path_buf = PathBuf::from(&path);

// If the new path doesn't exist, create it.
if !dir_exists(PathBuf::from(&new_path).pop().to_string().as_str()) {
std::fs::create_dir_all(&new_path).unwrap();
std::fs::create_dir_all(&new_path)?;
}

// Copy old to new
match std::fs::copy(path_buf, format!("{}/{}", new_path, filename)) {
Ok(_) => true,
Err(e) => {
println!("Failed to copy file: {}", e);
println!("Path: {}", path);
println!("New Path: {}", new_path);
false
}
}
std::fs::copy(path_buf, format!("{}/{}", new_path, filename))?;
Ok(())
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn copy_file_with_new_name(path: String, new_path: String, new_name: String) -> bool {
#[deprecated = "file helper is redundant"]
pub fn copy_file_with_new_name(
path: String,
new_path: String,
new_name: String,
) -> CultivationResult<()> {
let mut new_path_buf = PathBuf::from(&new_path);
let path_buf = PathBuf::from(&path);

// If the new path doesn't exist, create it.
if !dir_exists(PathBuf::from(&new_path).pop().to_string().as_str()) {
match std::fs::create_dir_all(&new_path) {
Ok(_) => {}
Err(e) => {
println!("Failed to create directory: {}", e);
return false;
}
};
std::fs::create_dir_all(&new_path)?;
}

new_path_buf.push(new_name);

// Copy old to new
match std::fs::copy(path_buf, &new_path_buf) {
Ok(_) => true,
Err(e) => {
println!("Failed to copy file: {}", e);
println!("Path: {}", path);
println!("New Path: {}", new_path);
false
}
}
std::fs::copy(path_buf, &new_path_buf)?;

Ok(())
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn delete_file(path: String) -> bool {
#[deprecated = "file helper is redundant"]
pub fn delete_file(path: String) -> CultivationResult<()> {
let path_buf = PathBuf::from(&path);

match std::fs::remove_file(path_buf) {
Ok(_) => true,
Err(e) => {
println!("Failed to delete file: {}", e);
false
}
};

false
std::fs::remove_file(path_buf).map_err(Into::into)
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn read_file(path: String) -> String {
#[deprecated = "file helper is redundant"]
pub fn read_file(path: String) -> CultivationResult<String> {
let path_buf = PathBuf::from(&path);

let mut file = match fs::File::open(path_buf) {
Ok(file) => file,
Err(e) => {
println!("Failed to open file: {}", e);
return String::new();
}
};
let mut file = fs::File::open(path_buf)?;

let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();

contents
Ok(contents)
}

// TODO: remove this file helper, already exists an api for this in tauri.
#[tauri::command]
pub fn write_file(path: String, contents: String) {
#[deprecated = "file helper is redundant"]
pub fn write_file(path: String, contents: String) -> CultivationResult<()> {
let path_buf = PathBuf::from(&path);

// Create file if it exists, otherwise just open and rewrite
let mut file = match fs::File::create(path_buf) {
Ok(file) => file,
Err(e) => {
println!("Failed to open file: {}", e);
return;
}
};
let mut file = fs::File::create(path_buf)?;

// Write contents to file
match file.write_all(contents.as_bytes()) {
Ok(_) => (),
Err(e) => {
println!("Failed to write to file: {}", e);
}
}
file.write_all(contents.as_bytes()).map_err(Into::into)
}
Loading

0 comments on commit 6792fb5

Please sign in to comment.