diff --git a/.changes/fs-read-dir-broken-symlink.md b/.changes/fs-read-dir-broken-symlink.md new file mode 100644 index 000000000..d1aa3d015 --- /dev/null +++ b/.changes/fs-read-dir-broken-symlink.md @@ -0,0 +1,7 @@ +--- +"fs": "patch" +"fs-js": "patch" +--- + +Fix `readDir` function failing to read directories that contain broken symlinks. + diff --git a/examples/api/package.json b/examples/api/package.json index 693588b30..e0cd66ca8 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -1,12 +1,13 @@ { - "name": "svelte-app", + "name": "api", "private": true, "version": "2.0.2", "type": "module", "scripts": { "dev": "vite --clearScreen false", "build": "vite build", - "serve": "vite preview" + "serve": "vite preview", + "tauri": "tauri" }, "dependencies": { "@tauri-apps/api": "2.0.3", diff --git a/package.json b/package.json index 02d2bd0a9..14b1d776c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "build": "pnpm run -r --parallel --filter !plugins-workspace --filter !\"./plugins/*/examples/**\" --filter !\"./examples/*\" build", "lint": "eslint .", "format": "prettier --write .", - "format:check": "prettier --check ." + "format:check": "prettier --check .", + "example:api:dev": "pnpm run --filter \"api\" tauri dev" }, "devDependencies": { "@eslint/js": "9.14.0", diff --git a/plugins/fs/src/commands.rs b/plugins/fs/src/commands.rs index 99eb5aa09..3b5cc44e5 100644 --- a/plugins/fs/src/commands.rs +++ b/plugins/fs/src/commands.rs @@ -16,7 +16,7 @@ use std::{ borrow::Cow, fs::File, io::{BufReader, Lines, Read, Write}, - path::{Path, PathBuf}, + path::PathBuf, str::FromStr, sync::Mutex, time::{SystemTime, UNIX_EPOCH}, @@ -245,32 +245,12 @@ pub fn mkdir( #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DirEntry { - pub name: Option, + pub name: String, pub is_directory: bool, pub is_file: bool, pub is_symlink: bool, } -fn read_dir_inner>(path: P) -> crate::Result> { - let mut files_and_dirs: Vec = vec![]; - for entry in std::fs::read_dir(path)? { - let path = entry?.path(); - let file_type = path.metadata()?.file_type(); - files_and_dirs.push(DirEntry { - is_directory: file_type.is_dir(), - is_file: file_type.is_file(), - is_symlink: std::fs::symlink_metadata(&path) - .map(|md| md.file_type().is_symlink()) - .unwrap_or(false), - name: path - .file_name() - .map(|name| name.to_string_lossy()) - .map(|name| name.to_string()), - }); - } - Result::Ok(files_and_dirs) -} - #[tauri::command] pub async fn read_dir( webview: Webview, @@ -287,14 +267,37 @@ pub async fn read_dir( options.as_ref().and_then(|o| o.base_dir), )?; - read_dir_inner(&resolved_path) - .map_err(|e| { - format!( - "failed to read directory at path: {} with error: {e}", - resolved_path.display() - ) + let entries = std::fs::read_dir(&resolved_path).map_err(|e| { + format!( + "failed to read directory at path: {} with error: {e}", + resolved_path.display() + ) + })?; + + let entries = entries + .filter_map(|entry| { + let entry = entry.ok()?; + let name = entry.file_name().into_string().ok()?; + let metadata = entry.file_type(); + macro_rules! method_or_false { + ($method:ident) => { + if let Ok(metadata) = &metadata { + metadata.$method() + } else { + false + } + }; + } + Some(DirEntry { + name, + is_file: method_or_false!(is_file), + is_directory: method_or_false!(is_dir), + is_symlink: method_or_false!(is_symlink), + }) }) - .map_err(Into::into) + .collect(); + + Ok(entries) } #[tauri::command]