fs: add createFileAtomic and AtomicFile to the native API - #592
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds native atomic file creation using temporary files and atomic rename operations. The API supports linking, replacement, and cleanup, is exported through ChangesAtomic file creation
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/fs.zig`:
- Around line 203-210: Update createFileAtomic to use std.fs.path.dirname and
std.fs.path.basename for splitting dest_path, replacing the unsupported
std.Io.Dir.path references while preserving the existing parent-directory
handling and atomicFileInit flow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 96a60708-9361-4b65-ae0f-614d8489f0b8
📒 Files selected for processing (3)
docs/changelog.mdsrc/fs.zigsrc/zio.zig
| pub fn createFileAtomic(self: Dir, dest_path: []const u8, options: CreateFileAtomicOptions) CreateFileAtomicError!AtomicFile { | ||
| if (std.Io.Dir.path.dirname(dest_path)) |dirname| { | ||
| const parent = try self.openDir(dirname, .{}); | ||
| errdefer parent.close(); | ||
| return atomicFileInit(std.Io.Dir.path.basename(dest_path), parent, true, options); | ||
| } | ||
| return atomicFileInit(dest_path, self, false, options); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the declared/pinned Zig version and check for other usages of this path API.
cat build.zig.zon 2>/dev/null | head -30
rg -n 'std\.Io\.Dir\.path|std\.fs\.path\.(dirname|basename)' --type=zigRepository: lalinsky/zio
Length of output: 403
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- src/fs.zig around the target lines ---\n'
sed -n '1,260p' src/fs.zig | nl -ba | sed -n '170,230p'
printf '\n--- search for Io/path usage in repo ---\n'
rg -n 'std\.Io\.Dir\.path|std\.fs\.path\.(dirname|basename)|std\.Io' src build.zig build.zig.zon examples --type zig || trueRepository: lalinsky/zio
Length of output: 241
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- src/fs.zig around lines 170-230 ---'
sed -n '170,230p' src/fs.zig | nl -ba
echo
echo '--- repo search for Io/path usage ---'
rg -n 'std\.Io\.Dir\.path|std\.fs\.path\.(dirname|basename)|std\.Io' src build.zig build.zig.zon examples --type zig || trueRepository: lalinsky/zio
Length of output: 230
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- src/fs.zig around lines 190-215 ---'
awk 'NR>=190 && NR<=215 { printf "%d:%s\n", NR, $0 }' src/fs.zig
echo
echo '--- repo search for path API usage ---'
rg -n 'std\.Io\.Dir\.path|std\.fs\.path\.(dirname|basename)|std\.Io' src build.zig build.zig.zon examples --type zig || trueRepository: lalinsky/zio
Length of output: 11157
🌐 Web query:
Zig 0.16.0 std.Io.Dir.path dirname basename std.fs.path documentation
💡 Result:
In Zig 0.16.0, filesystem and path manipulation APIs are organized within the standard library, though undergoing a transition toward std.Io [1]. 1. std.fs.path: This module remains the primary location for path manipulation functions, including dirname and basename [2][3][4]. These functions operate on path strings (slices of bytes) and are cross-platform aware [3]. - std.fs.path.dirname(path: []const u8)?[]const u8: Returns the parent directory of the given path, or null if no parent exists [3]. - std.fs.path.basename(path: []const u8) []const u8: Returns the final component of the path (e.g., the filename) [3]. 2. std.fs.Dir vs std.Io.Dir: The Zig standard library is actively migrating filesystem APIs from std.fs to std.Io [1]. While std.fs.Dir still exists in 0.16.0, many of its methods are deprecated in favor of their counterparts in std.Io.Dir [5][6]. For example, methods like openFile, openDir, and makeDir on std.fs.Dir serve as wrappers that direct calls to std.Io.Dir [5]. 3. Documentation: The authoritative reference for these APIs in version 0.16.0 is the standard library documentation included with the Zig installation [7]. Because the standard library is evolving, checking the source code of lib/std/fs/path.zig and lib/std/Io/Dir.zig within your specific Zig 0.16.0 distribution is often the most reliable way to see the current signatures and deprecation status [5][2].
Citations:
- 1: Move Filesystem APIs to std.Io ziglang/zig#25738
- 2: https://github.com/ziglang/zig/blob/master/lib/std/fs/path.zig
- 3: https://ziglang.com.br/stdlib/std.fs.path-em-zig-refer%C3%AAncia-e-exemplos/
- 4: https://github.com/ziglang/zig/blob/738d2be9/lib/std/fs.zig
- 5: https://github.com/ziglang/zig/blob/master/lib/std/fs/Dir.zig
- 6: https://fossies.org/linux/misc/zig-0.16.0.tar.xz/zig-0.16.0/lib/std/Io/Dir.zig?M=633
- 7: https://ziglang.org/documentation/0.16.0/
🌐 Web query:
Zig 0.16.0 "std.Io.Dir.path" dirname basename site:ziglang.org/documentation OR site:github.com/ziglang/zig
💡 Result:
In Zig 0.16.0, std.Io.Dir is a newer interface intended to supersede the traditional std.fs.Dir [1][2]. However, path manipulation functions like dirname and basename are not methods of std.Io.Dir [2]. Instead, path manipulation remains part of the std.fs.path namespace [3][4]. To perform operations such as extracting a directory name or a base name from a string path, you should use the functions provided in std.fs.path: 1. std.fs.path.dirname(path: []const u8)?[]const u8: Returns the directory component of the path, or null if there is no directory component. 2. std.fs.path.basename(path: []const u8) []const u8: Returns the final component of the path. While the Zig standard library has begun migrating filesystem operations to std.Io [1][2], the core logic for parsing and manipulating path strings resides in std.fs.path [3][4]. When working with std.Io.Dir, you use it to interact with the filesystem (e.g., opening, reading, or writing files/directories), and you use std.fs.path to process the path strings themselves [3][4].
Citations:
- 1: std: Introduce
IoInterface ziglang/zig#25592 - 2: Move Filesystem APIs to std.Io ziglang/zig#25738
- 3: https://github.com/ziglang/zig/blob/master/lib/std/fs/path.zig
- 4: https://github.com/ziglang/zig/blob/master/lib/std/fs.zig
Switch to std.fs.path for path splitting
std.Io.Dir doesn’t provide path.dirname/basename in Zig 0.16, so src/fs.zig:204-207 should use std.fs.path.dirname(dest_path) and std.fs.path.basename(dest_path) instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/fs.zig` around lines 203 - 210, Update createFileAtomic to use
std.fs.path.dirname and std.fs.path.basename for splitting dest_path, replacing
the unsupported std.Io.Dir.path references while preserving the existing
parent-directory handling and atomicFileInit flow.
Mirror of std.Io's Dir.createFileAtomic/File.Atomic for zio's native fs API. The data is written to a randomly named exclusive temporary file in the destination's directory, then moved into place with an atomic rename: AtomicFile.replace() overwrites an existing destination, AtomicFile.link() fails with error.PathAlreadyExists instead. The temporary file cleanup in AtomicFile.deinit() runs uncancelable, like close(), so a canceled task does not leak temp files.
Adds a native-API mirror of
std.Io.Dir.createFileAtomic/std.Io.File.Atomic, following the same design as zio's existing vtable implementation inio.zig.API
Dir.createFileAtomic(dest_path, options)creates a randomly named exclusive temporary file in the destination's directory (opening the parent dir whendest_pathhas directory components), so the final move is always a same-directory rename, never a copy. Options:mode(default 0o664) andread.AtomicFile.replace()renames over an existing destination;AtomicFile.link()usesrenamePreserveand fails witherror.PathAlreadyExists. Unlike std there is noreplaceflag in the create options: the temp file is always named, so both finish methods are always valid.AtomicFile.deinit()must always be called; it deletes the temp file if it was not moved into place. The delete runs throughwaitForIoUncancelable, likeclose(), so a canceled task does not leak temporary files (the std-styledeleteFile catch {}would silently skip cleanup under cancellation).zio.AtomicFile, plus a cwd-levelfs.createFileAtomicconvenience matching the other module-level helpers.Testing
Five new unit tests: link to a free destination, link to an occupied destination (verifies
PathAlreadyExists, temp cleanup, and that the destination is untouched), replace over an existing file, destination path with directory components, and deinit-without-finish removing the temp file.Full native suite passes (559/559), and test binaries cross-compile for x86_64-windows-gnu, aarch64-macos and x86_64-freebsd.