feat(browse):基本完成文件夹浏览器的功能。
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use mountpoints::mountinfos;
|
||||
use serde::Serialize;
|
||||
use walkdir::WalkDir;
|
||||
use tauri::Runtime;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::utils;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct FileItem {
|
||||
@@ -11,16 +17,54 @@ pub struct FileItem {
|
||||
pub width: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct DirItem {
|
||||
pub id: String,
|
||||
pub dirname: String,
|
||||
pub path: String,
|
||||
pub root: bool,
|
||||
}
|
||||
|
||||
fn is_hidden(entry: &DirEntry) -> bool {
|
||||
entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map(|s| s.starts_with(".") || s.starts_with("$"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn is_self<P: AsRef<Path>>(entry: &DirEntry, target: P) -> bool {
|
||||
entry.path().eq(target.as_ref())
|
||||
}
|
||||
|
||||
fn is_root(entry: &DirEntry) -> bool {
|
||||
entry
|
||||
.path()
|
||||
.to_str()
|
||||
.map(|s| s.eq_ignore_ascii_case("/"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn scan_directory(target: String) -> Result<Vec<FileItem>, String> {
|
||||
pub async fn scan_directory(target: String) -> Result<Vec<FileItem>, String> {
|
||||
let mut file_items = WalkDir::new(target)
|
||||
.max_depth(1)
|
||||
.into_iter()
|
||||
.filter_entry(|entry| !is_hidden(entry))
|
||||
.filter_map(|f| f.ok())
|
||||
.filter(|f| f.path().is_file())
|
||||
.map(|f| {
|
||||
let (width, height) = image::image_dimensions(f.path())?;
|
||||
let file_hash_id = f
|
||||
.path()
|
||||
.to_str()
|
||||
.map(crate::utils::md5_hash)
|
||||
.map(|hash| utils::uuid_from(hash.as_slice()))
|
||||
.transpose()
|
||||
.map_err(|e| anyhow!(e))?
|
||||
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
|
||||
Ok(FileItem {
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
id: file_hash_id,
|
||||
filename: f
|
||||
.path()
|
||||
.file_name()
|
||||
@@ -44,3 +88,101 @@ pub fn scan_directory(target: String) -> Result<Vec<FileItem>, String> {
|
||||
|
||||
Ok(file_items)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn show_drives<R: Runtime>(
|
||||
_app: tauri::AppHandle<R>,
|
||||
_window: tauri::Window<R>,
|
||||
) -> Result<Vec<DirItem>, String> {
|
||||
let mut drives = vec![];
|
||||
match mountinfos() {
|
||||
Ok(mounts) => {
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
let mounts = mounts
|
||||
.iter()
|
||||
.filter(|m| !m.path.starts_with("/System") && !m.path.starts_with("/dev"));
|
||||
for mount in mounts {
|
||||
let dirname = mount
|
||||
.path
|
||||
.as_path()
|
||||
.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_os_string()
|
||||
.into_string()
|
||||
.unwrap();
|
||||
let dirname = if dirname.len() == 0 {
|
||||
String::from("/")
|
||||
} else {
|
||||
dirname
|
||||
};
|
||||
let dir_hash_id = mount
|
||||
.path
|
||||
.to_str()
|
||||
.map(crate::utils::md5_hash)
|
||||
.map(|hash| utils::uuid_from(hash.as_slice()))
|
||||
.transpose()?
|
||||
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
|
||||
drives.push(DirItem {
|
||||
id: dir_hash_id,
|
||||
dirname,
|
||||
path: String::from(mount.path.to_str().unwrap_or_default()),
|
||||
root: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(format!("不能列出系统中已挂载的磁盘:{}", err)),
|
||||
}
|
||||
Ok(drives)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn scan_for_child_dirs<R: Runtime>(
|
||||
_app: tauri::AppHandle<R>,
|
||||
_window: tauri::Window<R>,
|
||||
target: String,
|
||||
) -> Result<Vec<DirItem>, String> {
|
||||
println!("请求扫描文件夹:{}", target);
|
||||
let target = if target.eq_ignore_ascii_case("/") {
|
||||
Path::new(r"/")
|
||||
} else {
|
||||
Path::new(&target)
|
||||
};
|
||||
let mut child_dirs = WalkDir::new(target)
|
||||
.max_depth(1)
|
||||
.into_iter()
|
||||
.filter_entry(|entry| !is_hidden(entry) && !is_root(entry))
|
||||
.filter_map(|d| d.ok())
|
||||
.filter(|d| d.path().is_dir() && !is_self(d, target))
|
||||
.map(|d| {
|
||||
println!("扫描到的文件夹:{}", d.path().display());
|
||||
let dir_hash_id = d
|
||||
.path()
|
||||
.to_str()
|
||||
.map(crate::utils::md5_hash)
|
||||
.map(|hash| utils::uuid_from(hash.as_slice()))
|
||||
.transpose()
|
||||
.map_err(|e| anyhow!(e))?
|
||||
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
|
||||
Ok(DirItem {
|
||||
id: dir_hash_id,
|
||||
dirname: d
|
||||
.path()
|
||||
.file_name()
|
||||
.ok_or(anyhow!("不能获取到文件夹的名称。"))?
|
||||
.to_owned()
|
||||
.into_string()
|
||||
.unwrap(),
|
||||
path: d
|
||||
.path()
|
||||
.clone()
|
||||
.to_str()
|
||||
.ok_or(anyhow!("不能获取到文件夹路径。"))?
|
||||
.to_string(),
|
||||
root: false,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<DirItem>, anyhow::Error>>()
|
||||
.map_err(|e| e.to_string())?;
|
||||
child_dirs.sort_by(|a, b| a.dirname.partial_cmp(&b.dirname).unwrap());
|
||||
Ok(child_dirs)
|
||||
}
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod commands;
|
||||
mod utils;
|
||||
|
||||
use commands::AppHold;
|
||||
use tauri::Manager;
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![commands::prelude::scan_directory])
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::prelude::scan_directory,
|
||||
commands::prelude::show_drives,
|
||||
commands::prelude::scan_for_child_dirs
|
||||
])
|
||||
.setup(|app| {
|
||||
let main_window = app.get_window("main").unwrap();
|
||||
#[cfg(debug_assertions)]
|
||||
|
||||
19
src-tauri/src/utils.rs
Normal file
19
src-tauri/src/utils.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use md5::{Digest, Md5};
|
||||
use uuid::Builder;
|
||||
|
||||
pub fn md5_hash(source: &str) -> Vec<u8> {
|
||||
let mut hasher = Md5::new();
|
||||
hasher.update(source);
|
||||
let hash = hasher.finalize();
|
||||
hash.to_vec()
|
||||
}
|
||||
|
||||
pub fn uuid_from(source: &[u8]) -> Result<String, String> {
|
||||
let builder = Builder::from_md5_bytes(
|
||||
source
|
||||
.try_into()
|
||||
.map_err(|_| String::from("源内容长度不足。"))?,
|
||||
);
|
||||
|
||||
Ok(builder.into_uuid().to_string())
|
||||
}
|
||||
Reference in New Issue
Block a user