Skip to content

Commit 61aaf74

Browse files
committed
refactor(hm-util): use tokio::fs::rename instead of std::fs::rename
atomic_rename_over now delegates to tokio::fs::rename on Unix. write_atomic_restricted splits its spawn_blocking so the rename step goes through the async atomic_rename_over. The sync Unix atomic_rename_over_impl is removed (only Windows variant remains).
1 parent 3d8af12 commit 61aaf74

1 file changed

Lines changed: 27 additions & 21 deletions

File tree

  • crates/hm-util/src/os

crates/hm-util/src/os/fs.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ pub async fn write_atomic_restricted(
2929
file_mode: u32,
3030
dir_mode: u32,
3131
) -> io::Result<()> {
32-
let path = path.as_ref().to_owned();
32+
let dest = path.as_ref().to_owned();
3333
let contents = contents.as_ref().to_vec();
34-
tokio::task::spawn_blocking(move || {
34+
let path = dest.clone();
35+
36+
let tmp_path = tokio::task::spawn_blocking(move || {
3537
let parent = path.parent().ok_or_else(|| {
3638
io::Error::new(
3739
io::ErrorKind::InvalidInput,
@@ -56,21 +58,23 @@ pub async fn write_atomic_restricted(
5658

5759
write_file_with_mode(&tmp_path, &contents, file_mode)?;
5860

59-
let persist_result = atomic_rename_over_impl(&tmp_path, &path);
60-
if persist_result.is_err() {
61-
let _ = std::fs::remove_file(&tmp_path);
62-
}
63-
persist_result
61+
io::Result::Ok(tmp_path)
6462
})
6563
.await
66-
.map_err(io::Error::other)?
64+
.map_err(io::Error::other)??;
65+
66+
let rename_result = atomic_rename_over(&tmp_path, &dest).await;
67+
if rename_result.is_err() {
68+
let _ = tokio::fs::remove_file(&tmp_path).await;
69+
}
70+
rename_result
6771
}
6872

6973
/// Atomically replace `to` with `from`.
7074
///
71-
/// On Unix this is a single `rename(2)` call — atomic by POSIX
72-
/// guarantee. On Windows this uses `ReplaceFileW` (preserves ACLs
73-
/// and alternate data streams) when the target exists, falling back
75+
/// On Unix this delegates to [`tokio::fs::rename`] (`rename(2)` — atomic
76+
/// by POSIX guarantee). On Windows this uses `ReplaceFileW` (preserves
77+
/// ACLs and alternate data streams) when the target exists, falling back
7478
/// to `MoveFileExW` with `MOVEFILE_REPLACE_EXISTING` for first-write.
7579
///
7680
/// # Errors
@@ -81,11 +85,18 @@ pub async fn atomic_rename_over(
8185
from: impl AsRef<Path>,
8286
to: impl AsRef<Path>,
8387
) -> io::Result<()> {
84-
let from = from.as_ref().to_owned();
85-
let to = to.as_ref().to_owned();
86-
tokio::task::spawn_blocking(move || atomic_rename_over_impl(&from, &to))
87-
.await
88-
.map_err(io::Error::other)?
88+
#[cfg(unix)]
89+
{
90+
tokio::fs::rename(from.as_ref(), to.as_ref()).await
91+
}
92+
#[cfg(windows)]
93+
{
94+
let from = from.as_ref().to_owned();
95+
let to = to.as_ref().to_owned();
96+
tokio::task::spawn_blocking(move || atomic_rename_over_impl(&from, &to))
97+
.await
98+
.map_err(io::Error::other)?
99+
}
89100
}
90101

91102
/// Remove a file if it exists; silently return `Ok(())` if it does not.
@@ -148,11 +159,6 @@ fn write_file_with_mode(path: &Path, contents: &[u8], _mode: u32) -> io::Result<
148159
std::fs::write(path, contents)
149160
}
150161

151-
#[cfg(unix)]
152-
fn atomic_rename_over_impl(from: &Path, to: &Path) -> io::Result<()> {
153-
std::fs::rename(from, to)
154-
}
155-
156162
#[cfg(windows)]
157163
fn atomic_rename_over_impl(from: &Path, to: &Path) -> io::Result<()> {
158164
use windows::core::HSTRING;

0 commit comments

Comments
 (0)