refactor: expose structs and commands in drive and sync modules
This commit is contained in:
parent
85a5e1d0de
commit
9c0afe87db
4 changed files with 123 additions and 148 deletions
|
@ -1,119 +1,101 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tauri::{Manager, Window};
|
||||
use tauri::{Emitter, Manager, Window};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct FileItem {
|
||||
pub struct FileItem {
|
||||
name: String,
|
||||
path: String,
|
||||
is_dir: bool,
|
||||
}
|
||||
|
||||
impl Drive {
|
||||
#[tauri::command]
|
||||
fn list_files(path: &str) -> Result<Vec<FileItem>, String> {
|
||||
let base_path = Path::new(path);
|
||||
let mut files = Vec::new();
|
||||
#[tauri::command]
|
||||
pub fn list_files(path: &str) -> Result<Vec<FileItem>, String> {
|
||||
let base_path = Path::new(path);
|
||||
let mut files = Vec::new();
|
||||
|
||||
if !base_path.exists() {
|
||||
return Err("Path does not exist".into());
|
||||
}
|
||||
if !base_path.exists() {
|
||||
return Err("Path does not exist".into());
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(base_path).map_err(|e| e.to_string())? {
|
||||
let entry = entry.map_err(|e| e.to_string())?;
|
||||
let path = entry.path();
|
||||
let name = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
for entry in fs::read_dir(base_path).map_err(|e| e.to_string())? {
|
||||
let entry = entry.map_err(|e| e.to_string())?;
|
||||
let path = entry.path();
|
||||
let name = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
|
||||
files.push(FileItem {
|
||||
name,
|
||||
path: path.to_str().unwrap_or("").to_string(),
|
||||
is_dir: path.is_dir(),
|
||||
});
|
||||
}
|
||||
|
||||
// Sort directories first, then files
|
||||
files.sort_by(|a, b| {
|
||||
if a.is_dir && !b.is_dir {
|
||||
std::cmp::Ordering::Less
|
||||
} else if !a.is_dir && b.is_dir {
|
||||
std::cmp::Ordering::Greater
|
||||
} else {
|
||||
a.name.cmp(&b.name)
|
||||
}
|
||||
files.push(FileItem {
|
||||
name,
|
||||
path: path.to_str().unwrap_or("").to_string(),
|
||||
is_dir: path.is_dir(),
|
||||
});
|
||||
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn upload_file(
|
||||
window: Window,
|
||||
src_path: String,
|
||||
dest_path: String,
|
||||
) -> Result<(), String> {
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use tauri::api::path::home_dir;
|
||||
|
||||
let src = PathBuf::from(&src_path);
|
||||
let dest_dir = PathBuf::from(&dest_path);
|
||||
let dest = dest_dir.join(src.file_name().ok_or("Invalid source file")?);
|
||||
|
||||
// Create destination directory if it doesn't exist
|
||||
if !dest_dir.exists() {
|
||||
fs::create_dir_all(&dest_dir).map_err(|e| e.to_string())?;
|
||||
// Sort directories first, then files
|
||||
files.sort_by(|a, b| {
|
||||
if a.is_dir && !b.is_dir {
|
||||
std::cmp::Ordering::Less
|
||||
} else if !a.is_dir && b.is_dir {
|
||||
std::cmp::Ordering::Greater
|
||||
} else {
|
||||
a.name.cmp(&b.name)
|
||||
}
|
||||
});
|
||||
|
||||
let mut source_file = File::open(&src).map_err(|e| e.to_string())?;
|
||||
let mut dest_file = File::create(&dest).map_err(|e| e.to_string())?;
|
||||
|
||||
let file_size = source_file.metadata().map_err(|e| e.to_string())?.len();
|
||||
let mut buffer = [0; 8192];
|
||||
let mut total_read = 0;
|
||||
|
||||
loop {
|
||||
let bytes_read = source_file.read(&mut buffer).map_err(|e| e.to_string())?;
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
dest_file
|
||||
.write_all(&buffer[..bytes_read])
|
||||
.map_err(|e| e.to_string())?;
|
||||
total_read += bytes_read as u64;
|
||||
|
||||
let progress = (total_read as f64 / file_size as f64) * 100.0;
|
||||
window
|
||||
.emit("upload_progress", progress)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn create_folder(path: String, name: String) -> Result<(), String> {
|
||||
let full_path = Path::new(&path).join(&name);
|
||||
if full_path.exists() {
|
||||
return Err("Folder already exists".into());
|
||||
}
|
||||
fs::create_dir(full_path).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
list_files,
|
||||
upload_file,
|
||||
create_folder
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn upload_file(window: Window, src_path: String, dest_path: String) -> Result<(), String> {
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
let src = PathBuf::from(&src_path);
|
||||
let dest_dir = PathBuf::from(&dest_path);
|
||||
let dest = dest_dir.join(src.file_name().ok_or("Invalid source file")?);
|
||||
|
||||
// Create destination directory if it doesn't exist
|
||||
if !dest_dir.exists() {
|
||||
fs::create_dir_all(&dest_dir).map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
let mut source_file = File::open(&src).map_err(|e| e.to_string())?;
|
||||
let mut dest_file = File::create(&dest).map_err(|e| e.to_string())?;
|
||||
|
||||
let file_size = source_file.metadata().map_err(|e| e.to_string())?.len();
|
||||
let mut buffer = [0; 8192];
|
||||
let mut total_read = 0;
|
||||
|
||||
loop {
|
||||
let bytes_read = source_file.read(&mut buffer).map_err(|e| e.to_string())?;
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
dest_file
|
||||
.write_all(&buffer[..bytes_read])
|
||||
.map_err(|e| e.to_string())?;
|
||||
total_read += bytes_read as u64;
|
||||
|
||||
let progress = (total_read as f64 / file_size as f64) * 100.0;
|
||||
window
|
||||
.emit("upload_progress", progress)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn create_folder(path: String, name: String) -> Result<(), String> {
|
||||
let full_path = Path::new(&path).join(&name);
|
||||
if full_path.exists() {
|
||||
return Err("Folder already exists".into());
|
||||
}
|
||||
fs::create_dir(full_path).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,39 +1,4 @@
|
|||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
use std::fs::{create_dir_all, File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::Mutex;
|
||||
use tauri::{Manager, Window};
|
||||
|
||||
pub mod drive;
|
||||
use drive::Drive::;
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.manage(AppState {
|
||||
sync_processes: Mutex::new(Vec::new()),
|
||||
sync_active: Mutex::new(false),
|
||||
})
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
save_config,
|
||||
list_files,
|
||||
start_sync,
|
||||
stop_sync,
|
||||
get_status
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
pub mod sync;
|
|
@ -2,7 +2,35 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
pub mod drive;
|
||||
pub mod sync;
|
||||
|
||||
fn main() {
|
||||
my_tauri_app_lib::run()
|
||||
use sync::AppState;
|
||||
use std::sync::Mutex;
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(AppState {
|
||||
sync_processes: Mutex::new(Vec::new()),
|
||||
sync_active: Mutex::new(false),
|
||||
})
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
sync::save_config,
|
||||
drive::list_files,
|
||||
drive::upload_file,
|
||||
drive::create_folder,
|
||||
sync::start_sync,
|
||||
sync::stop_sync,
|
||||
sync::get_status
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::env;
|
|||
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct RcloneConfig {
|
||||
pub struct RcloneConfig {
|
||||
name: String,
|
||||
remote_path: String,
|
||||
local_path: String,
|
||||
|
@ -18,7 +18,7 @@ struct RcloneConfig {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct SyncStatus {
|
||||
pub struct SyncStatus {
|
||||
name: String,
|
||||
status: String,
|
||||
transferred: String,
|
||||
|
@ -27,13 +27,13 @@ struct SyncStatus {
|
|||
last_updated: String,
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
sync_processes: Mutex<Vec<std::process::Child>>,
|
||||
sync_active: Mutex<bool>,
|
||||
pub(crate) struct AppState {
|
||||
pub sync_processes: Mutex<Vec<std::process::Child>>,
|
||||
pub sync_active: Mutex<bool>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn save_config(config: RcloneConfig) -> Result<(), String> {
|
||||
pub fn save_config(config: RcloneConfig) -> Result<(), String> {
|
||||
let home_dir = env::var("HOME").map_err(|_| "HOME environment variable not set".to_string())?;
|
||||
let config_path = Path::new(&home_dir).join(".config/rclone/rclone.conf");
|
||||
|
||||
|
@ -54,7 +54,7 @@ fn save_config(config: RcloneConfig) -> Result<(), String> {
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn start_sync(config: RcloneConfig, state: tauri::State<AppState>) -> Result<(), String> {
|
||||
pub fn start_sync(config: RcloneConfig, state: tauri::State<AppState>) -> Result<(), String> {
|
||||
let local_path = Path::new(&config.local_path);
|
||||
if !local_path.exists() {
|
||||
create_dir_all(local_path).map_err(|e| format!("Failed to create local path: {}", e))?;
|
||||
|
@ -78,7 +78,7 @@ fn start_sync(config: RcloneConfig, state: tauri::State<AppState>) -> Result<(),
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn stop_sync(state: tauri::State<AppState>) -> Result<(), String> {
|
||||
pub fn stop_sync(state: tauri::State<AppState>) -> Result<(), String> {
|
||||
let mut processes = state.sync_processes.lock().unwrap();
|
||||
for child in processes.iter_mut() {
|
||||
child.kill().map_err(|e| format!("Failed to kill process: {}", e))?;
|
||||
|
@ -89,7 +89,7 @@ fn stop_sync(state: tauri::State<AppState>) -> Result<(), String> {
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_status(remote_name: String) -> Result<SyncStatus, String> {
|
||||
pub fn get_status(remote_name: String) -> Result<SyncStatus, String> {
|
||||
let output = Command::new("rclone")
|
||||
.arg("rc")
|
||||
.arg("core/stats")
|
||||
|
@ -129,7 +129,7 @@ fn get_status(remote_name: String) -> Result<SyncStatus, String> {
|
|||
})
|
||||
}
|
||||
|
||||
fn format_bytes(bytes: u64) -> String {
|
||||
pub fn format_bytes(bytes: u64) -> String {
|
||||
const KB: u64 = 1024;
|
||||
const MB: u64 = KB * 1024;
|
||||
const GB: u64 = MB * 1024;
|
||||
|
|
Loading…
Add table
Reference in a new issue