Add synchronous close() to TempFile and TempDir#15
Conversation
`drop_async` deletes without blocking the runtime but returns `()`, and the implicit `Drop` swallows any deletion error. Add `close(self) -> io::Result<()>` as the synchronous, error-observable sibling so callers can detect a failed unlink/rmdir instead of having it silently dropped. Semantics mirror `drop_async` exactly, just synchronously: - gates on `Arc::into_inner`, so only the sole owner deletes; with live clones it returns `Ok(())` and leaves cleanup to their `Drop` - disarms the ownership flag only after a confirmed removal (or `NotFound`) - on error, leaves `Drop` armed as a backstop to retry; the returned `Err` is informational Rounds out the manual-closing API (keep / persist / drop_async / close) from issue #8. Tests cover owned-file deletion, the live-clone no-delete path, borrowed no-op, and recursive directory removal. README gains a one-line pointer next to drop_async.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #15 +/- ##
==========================================
- Coverage 67.53% 67.48% -0.05%
==========================================
Files 6 6
Lines 539 569 +30
==========================================
+ Hits 364 384 +20
- Misses 175 185 +10 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Incremental ReviewAll issues from the previous review (at
Files Reviewed (5 files)
Overall assessment: The Reviewed by claude-4.6-sonnet-20260217 · 311,776 tokens |
There was a problem hiding this comment.
Pull request overview
Adds a synchronous, error-reporting close(self) -> std::io::Result<()> API to TempFile and TempDir, complementing the existing async drop_async() by allowing callers to explicitly delete immediately and observe deletion failures.
Changes:
- Implement
TempFile::close()andTempDir::close()with semantics aligned todrop_async(sole-owner deletion viaArc::into_inner, disarm-after-success). - Add behavioral tests covering owned vs cloned vs borrowed close behavior, and recursive dir removal.
- Update README with a pointer to the new synchronous close option.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/tempfile.rs |
Adds TempFile::close() and documents synchronous, error-observable deletion. |
src/tempdir.rs |
Adds TempDir::close() and documents synchronous, error-observable recursive removal. |
tests/drop_safety.rs |
Adds tests for close() semantics across ownership/cloning/borrowed cases. |
README.md |
Mentions close() as a synchronous alternative to drop_async(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- close() now disarms on a deletion error instead of leaving Drop armed. A
synchronous retry of the same failing syscall is pointless, and the previous
"leave armed" path made the doc ("file/dir left in place") self-contradictory
(Drop would immediately retry remove_*). close() now leaves the file/dir
deterministically in place and returns the error for the caller to handle.
Docs reworded to match, and to note remove_dir_all is non-atomic (partial
removal possible on error). Contrast with drop_async (whose sync Drop backstop
is a genuinely different mechanism after a possibly-cancelled async removal)
is now explained.
- close_deletes_owned_dir_recursively: keep() the inner file so it stays on disk
with its handle closed, instead of holding an open handle inside the dir. The
open handle would block remove_dir_all on Windows. Now correct on all targets,
no cfg guard needed.
- README: clarify the commented close() call is an alternative to drop_async
(which consumes `file`), not an additional line.
- lib.rs: add close to the AtomicOwnership doc list of methods that disarm.
|
Also addressed the out-of-diff observation from the review summary: |
## What Adds the three 0.8.0 changes that were missing from `CHANGELOG.md` but shipped in the tagged + published `v0.8.0` (and are now on the [GitHub release](https://github.com/sunsided/async-tempfile-rs/releases/tag/v0.8.0)): - **Added** — `close()` on `TempFile`/`TempDir`, the synchronous error-observable sibling of `drop_async` (#15) - **Changed** — path-separator `prefix`/`suffix` rejected with the new `Error::InvalidAffix`, preventing directory escape (#14) - **Internal** — test coverage ~67% → ~94% (#16) ## Why The `CHANGELOG.md` 0.8.0 section only covered the #13 work (builder/keep/persist/drop_async/forbid-unsafe/unpredictable names). #14 and #15 landed afterward and were tagged into 0.8.0 without changelog entries, so the in-repo changelog understated what 0.8.0 actually contains. This brings it in line with the release notes and crates.io. ## Note Docs-only; no code change. The version line stays `0.8.0` — these edits describe the already-released version, they don't introduce a new one.
What
Adds
close(self) -> std::io::Result<()>to bothTempFileandTempDir: the synchronous, error-observable sibling ofdrop_async.Why
The existing manual-cleanup API has a gap:
drop_async(self)returns()— no way to observe a deletion failure.Dropalso swallows any error (it has nowhere to report it).close()lets a caller delete now and find out whether the unlink/rmdir actually succeeded. Usedrop_asyncin async code to avoid blocking the executor on the syscall; reach forclose()when you want the result synchronously.Semantics (identical to
drop_async, minus the runtime)Arc::into_inner: only the sole owner deletes. With live clones it returnsOk(())and leaves cleanup to theirDrop— no double-free.NotFound).Droparmed as a backstop (which will retry); the returnedErris informational, not the only line of defense.Tests (
tests/drop_safety.rs)close_deletes_owned_file_and_reports_okclose_via_clone_leaves_file_for_remaining_reference(clone keeps it alive; dropped later → gone)close_on_borrowed_file_is_a_noopclose_deletes_owned_dir_recursivelyReview order
src/tempfile.rs/src/tempdir.rs— the twoclose()impls (compare against thedrop_asyncdirectly above each).tests/drop_safety.rs— the four behavior cases.README.md— one-line pointer.Verification
cargo test,cargo test --features uuid,cargo clippy --all-features --all-targets,cargo fmt --checkall clean.Context
Rounds out the manual-closing API (
keep/persist/drop_async/close) discussed in #8. True language-levelAsyncDropremains tracked in #1 (blocked on the unstable Rust feature).