windows: Implement `fs::trash_file` and `fs::trash_dir` (#17711)

Junkui Zhang created

https://github.com/user-attachments/assets/43370cee-26a5-4d27-b86f-656127e03b4a



Release Notes:

- N/A

Change summary

Cargo.toml          |  1 +
crates/fs/src/fs.rs | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)

Detailed changes

Cargo.toml 🔗

@@ -483,6 +483,7 @@ version = "0.58"
 features = [
     "implement",
     "Foundation_Numerics",
+    "Storage",
     "System",
     "System_Threading",
     "UI_ViewManagement",

crates/fs/src/fs.rs 🔗

@@ -342,6 +342,24 @@ impl Fs for RealFs {
         }
     }
 
+    #[cfg(target_os = "windows")]
+    async fn trash_file(&self, path: &Path, _options: RemoveOptions) -> Result<()> {
+        use windows::{
+            core::HSTRING,
+            Storage::{StorageDeleteOption, StorageFile},
+        };
+        // todo(windows)
+        // When new version of `windows-rs` release, make this operation `async`
+        let path = path.canonicalize()?.to_string_lossy().to_string();
+        let path_str = path.trim_start_matches("\\\\?\\");
+        if path_str.is_empty() {
+            anyhow::bail!("File path is empty!");
+        }
+        let file = StorageFile::GetFileFromPathAsync(&HSTRING::from(path_str))?.get()?;
+        file.DeleteAsync(StorageDeleteOption::Default)?.get()?;
+        Ok(())
+    }
+
     #[cfg(target_os = "macos")]
     async fn trash_dir(&self, path: &Path, options: RemoveOptions) -> Result<()> {
         self.trash_file(path, options).await
@@ -352,6 +370,25 @@ impl Fs for RealFs {
         self.trash_file(path, options).await
     }
 
+    #[cfg(target_os = "windows")]
+    async fn trash_dir(&self, path: &Path, _options: RemoveOptions) -> Result<()> {
+        use windows::{
+            core::HSTRING,
+            Storage::{StorageDeleteOption, StorageFolder},
+        };
+
+        let path = path.canonicalize()?.to_string_lossy().to_string();
+        let path_str = path.trim_start_matches("\\\\?\\");
+        if path_str.is_empty() {
+            anyhow::bail!("Folder path is empty!");
+        }
+        // todo(windows)
+        // When new version of `windows-rs` release, make this operation `async`
+        let folder = StorageFolder::GetFolderFromPathAsync(&HSTRING::from(path_str))?.get()?;
+        folder.DeleteAsync(StorageDeleteOption::Default)?.get()?;
+        Ok(())
+    }
+
     async fn open_sync(&self, path: &Path) -> Result<Box<dyn io::Read>> {
         Ok(Box::new(std::fs::File::open(path)?))
     }