Files
readest/apps/readest-app/src-tauri/src/dir_scanner.rs
T
xijibomi-coffee f875ba88ac perf: use native walkdir for recursive imports from directory (#2993)
* fix(perf): replace JS recursion with native Rust walkdir for imports

* fix: implement security scope check in rust recursive scanner

* refactor and format code

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
2026-01-20 06:22:04 +01:00

97 lines
3.0 KiB
Rust

use std::path::Path;
use tauri::AppHandle;
use tauri_plugin_fs::FsExt;
use walkdir::WalkDir;
#[derive(serde::Serialize)]
pub struct ScannedFile {
pub path: String,
pub size: u64,
}
#[tauri::command]
pub fn read_dir(
app: AppHandle,
path: String,
recursive: bool,
extensions: Vec<String>,
) -> Result<Vec<ScannedFile>, String> {
let scope = app.fs_scope();
let path_buf = std::path::PathBuf::from(&path);
if !scope.is_allowed(&path_buf) {
return Err("Permission denied: Path not in filesystem scope".to_string());
}
let mut files = Vec::new();
let normalized_extensions: Vec<String> =
extensions.iter().map(|ext| ext.to_lowercase()).collect();
if recursive {
for entry_result in WalkDir::new(&path).into_iter() {
match entry_result {
Ok(entry) => {
if entry.file_type().is_file() {
if let Some(scanned_file) =
process_file_entry(entry.path(), &normalized_extensions)
{
files.push(scanned_file);
}
}
}
Err(e) => {
log::warn!("RUST: Skipping file due to error: {}", e);
}
}
}
} else {
match std::fs::read_dir(&path_buf) {
Ok(entries) => {
for entry_result in entries {
match entry_result {
Ok(entry) => {
let path = entry.path();
if path.is_file() {
if let Some(scanned_file) =
process_file_entry(&path, &normalized_extensions)
{
files.push(scanned_file);
}
}
}
Err(e) => {
log::warn!("RUST: Skipping entry due to error: {}", e);
}
}
}
}
Err(e) => {
return Err(format!("Failed to read directory: {}", e));
}
}
}
Ok(files)
}
fn process_file_entry(path: &Path, extensions: &[String]) -> Option<ScannedFile> {
if extensions.is_empty() || extensions.contains(&"*".to_string()) {
let size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
return Some(ScannedFile {
path: path.to_string_lossy().to_string(),
size,
});
} else if let Some(ext) = path.extension() {
let ext_str = ext.to_string_lossy().to_lowercase();
if extensions.contains(&ext_str) {
let size = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
return Some(ScannedFile {
path: path.to_string_lossy().to_string(),
size,
});
}
}
None
}