aboutsummaryrefslogtreecommitdiff
path: root/lib/std/Io/Dir.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2025-09-10 22:34:19 -0700
committerAndrew Kelley <andrew@ziglang.org>2025-10-29 06:20:48 -0700
commit0e9280ef1a80e8fdeaec40a41b9cb6f2c2d4a490 (patch)
treec069e1a0b58fe0eb1729652406b6dcea440a764c /lib/std/Io/Dir.zig
parentfc1e3d5bc9f4279ae6cd19577bb443aff8d4ccb6 (diff)
downloadzig-0e9280ef1a80e8fdeaec40a41b9cb6f2c2d4a490.tar.gz
zig-0e9280ef1a80e8fdeaec40a41b9cb6f2c2d4a490.zip
std.Io: extract Dir to separate file
Diffstat (limited to 'lib/std/Io/Dir.zig')
-rw-r--r--lib/std/Io/Dir.zig113
1 files changed, 113 insertions, 0 deletions
diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig
new file mode 100644
index 0000000000..a2ae96210e
--- /dev/null
+++ b/lib/std/Io/Dir.zig
@@ -0,0 +1,113 @@
+const Dir = @This();
+
+const std = @import("../std.zig");
+const Io = std.Io;
+const File = Io.File;
+
+handle: Handle,
+
+pub fn cwd() Dir {
+ return .{ .handle = std.fs.cwd().fd };
+}
+
+pub const Handle = std.posix.fd_t;
+
+pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
+ return io.vtable.fileOpen(io.userdata, dir, sub_path, flags);
+}
+
+pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
+ return io.vtable.createFile(io.userdata, dir, sub_path, flags);
+}
+
+pub const WriteFileOptions = struct {
+ /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
+ /// On WASI, `sub_path` should be encoded as valid UTF-8.
+ /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
+ sub_path: []const u8,
+ data: []const u8,
+ flags: File.CreateFlags = .{},
+};
+
+pub const WriteFileError = File.WriteError || File.OpenError || Io.Cancelable;
+
+/// Writes content to the file system, using the file creation flags provided.
+pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void {
+ var file = try dir.createFile(io, options.sub_path, options.flags);
+ defer file.close(io);
+ try file.writeAll(io, options.data);
+}
+
+pub const PrevStatus = enum {
+ stale,
+ fresh,
+};
+
+pub const UpdateFileError = File.OpenError;
+
+/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If
+/// they are equal, does nothing. Otherwise, atomically copies `source_path` to
+/// `dest_path`. The destination file gains the mtime, atime, and mode of the
+/// source file so that the next call to `updateFile` will not need a copy.
+///
+/// Returns the previous status of the file before updating.
+///
+/// * On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
+/// * On WASI, both paths should be encoded as valid UTF-8.
+/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
+pub fn updateFile(
+ source_dir: Dir,
+ io: Io,
+ source_path: []const u8,
+ dest_dir: Dir,
+ /// If directories in this path do not exist, they are created.
+ dest_path: []const u8,
+ options: std.fs.Dir.CopyFileOptions,
+) !PrevStatus {
+ var src_file = try source_dir.openFile(io, source_path, .{});
+ defer src_file.close();
+
+ const src_stat = try src_file.stat(io);
+ const actual_mode = options.override_mode orelse src_stat.mode;
+ check_dest_stat: {
+ const dest_stat = blk: {
+ var dest_file = dest_dir.openFile(io, dest_path, .{}) catch |err| switch (err) {
+ error.FileNotFound => break :check_dest_stat,
+ else => |e| return e,
+ };
+ defer dest_file.close(io);
+
+ break :blk try dest_file.stat(io);
+ };
+
+ if (src_stat.size == dest_stat.size and
+ src_stat.mtime == dest_stat.mtime and
+ actual_mode == dest_stat.mode)
+ {
+ return .fresh;
+ }
+ }
+
+ if (std.fs.path.dirname(dest_path)) |dirname| {
+ try dest_dir.makePath(io, dirname);
+ }
+
+ var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available.
+ var atomic_file = try dest_dir.atomicFile(io, dest_path, .{
+ .mode = actual_mode,
+ .write_buffer = &buffer,
+ });
+ defer atomic_file.deinit();
+
+ var src_reader: File.Reader = .initSize(io, src_file, &.{}, src_stat.size);
+ const dest_writer = &atomic_file.file_writer.interface;
+
+ _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) {
+ error.ReadFailed => return src_reader.err.?,
+ error.WriteFailed => return atomic_file.file_writer.err.?,
+ };
+ try atomic_file.flush();
+ try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime);
+ try atomic_file.renameIntoPlace();
+ return .stale;
+}