1const Atomic = @This();
2
3const std = @import("../../std.zig");
4const Io = std.Io;
5const File = std.Io.File;
6const Dir = std.Io.Dir;
7const assert = std.debug.assert;
8
9file: File,
10file_basename_hex: u64,
11file_open: bool,
12file_exists: bool,
13
14dir: Dir,
15close_dir_on_deinit: bool,
16
17dest_sub_path: []const u8,
18
19pub const InitError = File.OpenError;
20
21/// To release all resources, always call `deinit`, even after a successful
22/// `finish`.
23pub fn deinit(af: *Atomic, io: Io) void {
24 if (af.file_open) {
25 af.file.close(io);
26 af.file_open = false;
27 }
28 if (af.file_exists) {
29 const tmp_sub_path = std.fmt.hex(af.file_basename_hex);
30 af.dir.deleteFile(io, &tmp_sub_path) catch {};
31 af.file_exists = false;
32 }
33 if (af.close_dir_on_deinit) {
34 af.dir.close(io);
35 af.close_dir_on_deinit = false;
36 }
37 af.* = undefined;
38}
39
40pub const LinkError = File.HardLinkError || Dir.RenamePreserveError;
41
42/// Atomically materializes the file into place, failing with
43/// `error.PathAlreadyExists` if something already exists there.
44///
45/// If this operation could not be done with an unnamed temporary file, the
46/// named temporary file will be deleted in a following operation, which may
47/// independently fail. The result of that operation is stored in `delete_err`.
48pub fn link(af: *Atomic, io: Io) LinkError!void {
49 if (af.file_exists) {
50 if (af.file_open) {
51 af.file.close(io);
52 af.file_open = false;
53 }
54 const tmp_sub_path = std.fmt.hex(af.file_basename_hex);
55 try af.dir.renamePreserve(&tmp_sub_path, af.dir, af.dest_sub_path, io);
56 af.file_exists = false;
57 } else {
58 assert(af.file_open);
59 try af.file.hardLink(io, af.dir, af.dest_sub_path, .{});
60 af.file.close(io);
61 af.file_open = false;
62 }
63}
64
65pub const ReplaceError = Dir.RenameError;
66
67/// Atomically materializes the file into place, replacing any file that
68/// already exists there.
69///
70/// Calling this function requires setting `CreateFileAtomicOptions.replace` to
71/// `true`.
72///
73/// On Windows, this function introduces a period of time where some file
74/// system operations on the destination file will result in
75/// `error.AccessDenied`, including rename operations (such as the one used in
76/// this function).
77pub fn replace(af: *Atomic, io: Io) ReplaceError!void {
78 assert(af.file_exists); // Wrong value for `CreateFileAtomicOptions.replace`.
79 if (af.file_open) {
80 af.file.close(io);
81 af.file_open = false;
82 }
83 const tmp_sub_path = std.fmt.hex(af.file_basename_hex);
84 try af.dir.rename(&tmp_sub_path, af.dir, af.dest_sub_path, io);
85 af.file_exists = false;
86}