Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,213 changes: 0 additions & 1,213 deletions package-lock.json

This file was deleted.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@reduxjs/toolkit": "^1.9.5",
"@tauri-apps/api": "^1.3.0",
"@types/react-redux": "^7.1.25",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.24",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.3.2"
"react-redux": "^8.1.1",
"tailwindcss": "^3.3.2",
"lodash": "^4.17.21"
},
"devDependencies": {
"@tauri-apps/cli": "^1.3.1",
Expand Down
10 changes: 5 additions & 5 deletions src-tauri/src/filesystem/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ impl FsEventHandler {
CreateKind::Folder => DIRECTORY,
_ => return, // Other options are weird lol
}
.to_string();
.to_string();

let file_path = path.to_string_lossy().to_string();
current_volume.entry(filename).or_insert(vec![CachedPath {
current_volume.entry(filename).or_insert_with(|| vec![CachedPath {
file_path,
file_type,
}]);
Expand Down Expand Up @@ -100,7 +100,7 @@ impl FsEventHandler {
let file_type = if new_path.is_dir() { DIRECTORY } else { FILE };

let path_string = new_path.to_string_lossy().to_string();
current_volume.entry(filename).or_insert(vec![CachedPath {
current_volume.entry(filename).or_insert_with(|| vec![CachedPath {
file_path: path_string,
file_type: String::from(file_type),
}]);
Expand Down Expand Up @@ -130,7 +130,7 @@ pub fn run_cache_interval(state_mux: &StateSafe) {

tokio::spawn(async move {
// We use tokio spawn because async closures with std spawn is unstable
let mut interval = time::interval(Duration::from_secs(30));
let mut interval = time::interval(Duration::from_secs(60));
interval.tick().await; // Wait 30 seconds before doing first re-cache

loop {
Expand Down Expand Up @@ -163,7 +163,7 @@ fn save_to_cache(state: &mut MutexGuard<AppState>) {
&zstd::encode_all(serialized_cache.as_bytes(), 0)
.expect("Failed to compress cache contents.")[..],
)
.unwrap();
.unwrap();
}

/// Reads and decodes the cache file and stores it in memory for quick access.
Expand Down
123 changes: 123 additions & 0 deletions src-tauri/src/filesystem/explorer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use std::fs;
use std::fs::read_dir;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use notify::event::CreateKind;
use tauri::State;
use crate::errors::Error;
use crate::filesystem::cache::FsEventHandler;
use crate::filesystem::fs_utils;
use crate::filesystem::fs_utils::get_mount_point;
use crate::filesystem::volume::DirectoryChild;
use crate::StateSafe;

/// Opens a file at the given path. Returns a string if there was an error.
// NOTE(conaticus): I tried handling the errors nicely here but Tauri was mega cringe and wouldn't let me nest results in async functions, so used string error messages instead.
#[tauri::command]
pub async fn open_file(path: String) -> Result<(), Error> {
let output_res = open::commands(path)[0].output();
let output = match output_res {
Ok(output) => output,
Err(err) => {
let err_msg = format!("Failed to get open command output: {}", err);
return Err(Error::Custom(err_msg));
}
};

if output.status.success() {
return Ok(());
}

let err_msg = String::from_utf8(output.stderr).unwrap_or(String::from("Failed to open file and deserialize stderr."));
Err(Error::Custom(err_msg))
}

/// Searches and returns the files in a given directory. This is not recursive.
#[tauri::command]
pub async fn open_directory(path: String) -> Result<Vec<DirectoryChild>, ()> {
let Ok(directory) = read_dir(path) else {
return Ok(Vec::new());
};

Ok(directory
.map(|entry| {
let entry = entry.unwrap();

let file_name = entry.file_name().to_string_lossy().to_string();
let entry_is_file = entry.file_type().unwrap().is_file();
let entry = entry.path().to_string_lossy().to_string();

if entry_is_file {
return DirectoryChild::File(file_name, entry);
}

DirectoryChild::Directory(file_name, entry)
})
.collect())
}


#[tauri::command]
pub async fn create_file(state_mux: State<'_, StateSafe>, path: String) -> Result<(), Error> {
let mount_point_str = get_mount_point(path.clone()).unwrap_or_default();

let fs_event_manager = FsEventHandler::new(state_mux.deref().clone(), mount_point_str.into());
fs_event_manager.handle_create(CreateKind::File, Path::new(&path));

let res = fs::File::create(path);
match res {
Ok(_) => {
Ok(())
},
Err(err) => Err(Error::Custom(err.to_string())),
}
}

#[tauri::command]
pub async fn create_directory(state_mux: State<'_, StateSafe>, path: String) -> Result<(), Error> {
let mount_point_str = get_mount_point(path.clone()).unwrap_or_default();

let fs_event_manager = FsEventHandler::new(state_mux.deref().clone(), mount_point_str.into());
fs_event_manager.handle_create(CreateKind::Folder, Path::new(&path));

let res = fs::create_dir(path);
match res {
Ok(_) => {
Ok(())
},
Err(err) => Err(Error::Custom(err.to_string())),
}
}

#[tauri::command]
pub async fn rename_file(state_mux: State<'_, StateSafe>, old_path: String, new_path: String) -> Result<(), Error> {
let mount_point_str = get_mount_point(old_path.clone()).unwrap_or_default();

let mut fs_event_manager = FsEventHandler::new(state_mux.deref().clone(), mount_point_str.into());
fs_event_manager.handle_rename_from(Path::new(&old_path));
fs_event_manager.handle_rename_to(Path::new(&new_path));

let res = fs::rename(old_path, new_path);
match res {
Ok(_) => {
Ok(())
},
Err(err) => Err(Error::Custom(err.to_string())),
}
}

#[tauri::command]
pub async fn delete_file(state_mux: State<'_, StateSafe>, path: String) -> Result<(), Error> {
let mount_point_str = get_mount_point(path.clone()).unwrap_or_default();

let fs_event_manager = FsEventHandler::new(state_mux.deref().clone(), mount_point_str.into());
fs_event_manager.handle_delete(Path::new(&path));

let res = fs::remove_file(path);
match res {
Ok(_) => {
Ok(())
},
Err(err) => Err(Error::Custom(err.to_string())),
}
}
12 changes: 12 additions & 0 deletions src-tauri/src/filesystem/fs_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use std::path::{Path, PathBuf};

pub fn get_mount_point(path: String) -> Option<String> {
let path = Path::new(&path);
let root = path.components().next()?;
let mount_point = root.as_os_str().to_string_lossy().into_owned();

let mut mount_point_path = PathBuf::new();
mount_point_path.push(&mount_point);
mount_point_path.push("\\");
Some(mount_point_path.to_string_lossy().into_owned())
}
53 changes: 3 additions & 50 deletions src-tauri/src/filesystem/mod.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,11 @@
pub mod explorer;
pub mod cache;
pub mod volume;

use crate::filesystem::volume::DirectoryChild;
use std::fs::read_dir;
use crate::errors::Error;
mod fs_utils;

pub const DIRECTORY: &str = "directory";
pub const FILE: &str = "file";

pub const fn bytes_to_gb(bytes: u64) -> u16 {
(bytes / (1e+9 as u64)) as u16
}

/// Opens a file at the given path. Returns a string if there was an error.
// NOTE(conaticus): I tried handling the errors nicely here but Tauri was mega cringe and wouldn't let me nest results in async functions, so used string error messages instead.
#[tauri::command]
pub async fn open_file(path: String) -> Result<(), Error> {
let output_res = open::commands(path)[0].output();
let output = match output_res {
Ok(output) => output,
Err(err) => {
let err_msg = format!("Failed to get open command output: {}", err);
return Err(Error::Custom(err_msg));
}
};

if output.status.success() {
return Ok(());
}

let err_msg = String::from_utf8(output.stderr).unwrap_or(String::from("Failed to open file and deserialize stderr."));
Err(Error::Custom(err_msg))
}

/// Searches and returns the files in a given directory. This is not recursive.
#[tauri::command]
pub async fn open_directory(path: String) -> Result<Vec<DirectoryChild>, ()> {
let Ok(directory) = read_dir(path) else {
return Ok(Vec::new());
};

Ok(directory
.map(|entry| {
let entry = entry.unwrap();

let file_name = entry.file_name().to_string_lossy().to_string();
let entry_is_file = entry.file_type().unwrap().is_file();
let entry = entry.path().to_string_lossy().to_string();

if entry_is_file {
return DirectoryChild::File(file_name, entry);
}

DirectoryChild::Directory(file_name, entry)
})
.collect())
}
}
6 changes: 3 additions & 3 deletions src-tauri/src/filesystem/volume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl Volume {
WalkDir::new(self.mountpoint.clone())
.into_iter()
.par_bridge()
.filter_map(|entry| entry.ok())
.filter_map(Result::ok)
.for_each(|entry| {
let file_name = entry.file_name().to_string_lossy().to_string();
let file_path = entry.path().to_string_lossy().to_string();
Expand Down Expand Up @@ -95,7 +95,7 @@ impl Volume {

let mut watcher = notify::recommended_watcher(move |res| match res {
Ok(event) => fs_event_manager.handle_event(event),
Err(e) => panic!("Failed to handle event: {:?}", e),
Err(e) => panic!("Failed to handle event: {}", e),
})
.unwrap();

Expand All @@ -106,7 +106,7 @@ impl Volume {

block_in_place(|| loop {
thread::park();
})
});
});
}
}
Expand Down
8 changes: 6 additions & 2 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod filesystem;
mod search;
mod errors;

use filesystem::{open_directory, open_file};
use filesystem::explorer::{open_file, open_directory, create_file, create_directory, rename_file, delete_file};
use filesystem::volume::get_volumes;
use search::search_directory;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -36,7 +36,11 @@ async fn main() {
get_volumes,
open_directory,
search_directory,
open_file
open_file,
create_file,
create_directory,
rename_file,
delete_file
])
.manage(Arc::new(Mutex::new(AppState::default())))
.run(tauri::generate_context!())
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"withGlobalTauri": false
},
"package": {
"productName": "file-explorer",
"productName": "File Explorer",
"version": "0.0.0"
},
"tauri": {
Expand Down
Loading