Commit e00e9c0fbf

Andrew Kelley <andrew@ziglang.org>
2023-11-22 21:54:16
std.fs: extract AtomicFile to separate file
1 parent c95e2e6
Changed files (3)
lib/std/fs/AtomicFile.zig
@@ -0,0 +1,84 @@
+file: File,
+// TODO either replace this with rand_buf or use []u16 on Windows
+tmp_path_buf: [TMP_PATH_LEN:0]u8,
+dest_basename: []const u8,
+file_open: bool,
+file_exists: bool,
+close_dir_on_deinit: bool,
+dir: Dir,
+
+pub const InitError = File.OpenError;
+
+const RANDOM_BYTES = 12;
+const TMP_PATH_LEN = fs.base64_encoder.calcSize(RANDOM_BYTES);
+
+/// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
+pub fn init(
+    dest_basename: []const u8,
+    mode: File.Mode,
+    dir: Dir,
+    close_dir_on_deinit: bool,
+) InitError!AtomicFile {
+    var rand_buf: [RANDOM_BYTES]u8 = undefined;
+    var tmp_path_buf: [TMP_PATH_LEN:0]u8 = undefined;
+
+    while (true) {
+        std.crypto.random.bytes(rand_buf[0..]);
+        const tmp_path = fs.base64_encoder.encode(&tmp_path_buf, &rand_buf);
+        tmp_path_buf[tmp_path.len] = 0;
+
+        const file = dir.createFile(
+            tmp_path,
+            .{ .mode = mode, .exclusive = true },
+        ) catch |err| switch (err) {
+            error.PathAlreadyExists => continue,
+            else => |e| return e,
+        };
+
+        return AtomicFile{
+            .file = file,
+            .tmp_path_buf = tmp_path_buf,
+            .dest_basename = dest_basename,
+            .file_open = true,
+            .file_exists = true,
+            .close_dir_on_deinit = close_dir_on_deinit,
+            .dir = dir,
+        };
+    }
+}
+
+/// Always call deinit, even after a successful finish().
+pub fn deinit(self: *AtomicFile) void {
+    if (self.file_open) {
+        self.file.close();
+        self.file_open = false;
+    }
+    if (self.file_exists) {
+        self.dir.deleteFile(&self.tmp_path_buf) catch {};
+        self.file_exists = false;
+    }
+    if (self.close_dir_on_deinit) {
+        self.dir.close();
+    }
+    self.* = undefined;
+}
+
+pub const FinishError = posix.RenameError;
+
+pub fn finish(self: *AtomicFile) FinishError!void {
+    assert(self.file_exists);
+    if (self.file_open) {
+        self.file.close();
+        self.file_open = false;
+    }
+    try posix.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename);
+    self.file_exists = false;
+}
+
+const AtomicFile = @This();
+const std = @import("../std.zig");
+const File = std.fs.File;
+const Dir = std.fs.Dir;
+const fs = std.fs;
+const assert = std.debug.assert;
+const posix = std.os;
lib/std/fs.zig
@@ -7,11 +7,11 @@ const base64 = std.base64;
 const crypto = std.crypto;
 const Allocator = std.mem.Allocator;
 const assert = std.debug.assert;
-const math = std.math;
 
 const is_darwin = builtin.os.tag.isDarwin();
 
 pub const Dir = @import("fs/Dir.zig");
+pub const AtomicFile = @import("fs/AtomicFile.zig");
 
 pub const has_executable_bit = switch (builtin.os.tag) {
     .windows, .wasi => false,
@@ -150,85 +150,6 @@ pub fn copyFileAbsolute(
     return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args);
 }
 
-pub const AtomicFile = struct {
-    file: File,
-    // TODO either replace this with rand_buf or use []u16 on Windows
-    tmp_path_buf: [TMP_PATH_LEN:0]u8,
-    dest_basename: []const u8,
-    file_open: bool,
-    file_exists: bool,
-    close_dir_on_deinit: bool,
-    dir: Dir,
-
-    pub const InitError = File.OpenError;
-
-    const RANDOM_BYTES = 12;
-    const TMP_PATH_LEN = base64_encoder.calcSize(RANDOM_BYTES);
-
-    /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
-    pub fn init(
-        dest_basename: []const u8,
-        mode: File.Mode,
-        dir: Dir,
-        close_dir_on_deinit: bool,
-    ) InitError!AtomicFile {
-        var rand_buf: [RANDOM_BYTES]u8 = undefined;
-        var tmp_path_buf: [TMP_PATH_LEN:0]u8 = undefined;
-
-        while (true) {
-            crypto.random.bytes(rand_buf[0..]);
-            const tmp_path = base64_encoder.encode(&tmp_path_buf, &rand_buf);
-            tmp_path_buf[tmp_path.len] = 0;
-
-            const file = dir.createFile(
-                tmp_path,
-                .{ .mode = mode, .exclusive = true },
-            ) catch |err| switch (err) {
-                error.PathAlreadyExists => continue,
-                else => |e| return e,
-            };
-
-            return AtomicFile{
-                .file = file,
-                .tmp_path_buf = tmp_path_buf,
-                .dest_basename = dest_basename,
-                .file_open = true,
-                .file_exists = true,
-                .close_dir_on_deinit = close_dir_on_deinit,
-                .dir = dir,
-            };
-        }
-    }
-
-    /// Always call deinit, even after a successful finish().
-    pub fn deinit(self: *AtomicFile) void {
-        if (self.file_open) {
-            self.file.close();
-            self.file_open = false;
-        }
-        if (self.file_exists) {
-            self.dir.deleteFile(&self.tmp_path_buf) catch {};
-            self.file_exists = false;
-        }
-        if (self.close_dir_on_deinit) {
-            self.dir.close();
-        }
-        self.* = undefined;
-    }
-
-    pub const FinishError = std.os.RenameError;
-
-    pub fn finish(self: *AtomicFile) FinishError!void {
-        assert(self.file_exists);
-        if (self.file_open) {
-            self.file.close();
-            self.file_open = false;
-        }
-        try os.renameat(self.dir.fd, self.tmp_path_buf[0..], self.dir.fd, self.dest_basename);
-        self.file_exists = false;
-    }
-};
-
 /// Create a new directory, based on an absolute path.
 /// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates
 /// on both absolute and relative paths.
CMakeLists.txt
@@ -247,6 +247,7 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/lib/std/fmt/errol/lookup.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/fmt/parse_float.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/fs.zig"
+    "${CMAKE_SOURCE_DIR}/lib/std/fs/AtomicFile.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/fs/Dir.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/fs/file.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/fs/get_app_data_dir.zig"