lib/sql/src/repository/remote.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const Allocator = std.mem.Allocator;
4 const fs_io = std.Options.debug_io;
5
6 pub fn Owner(comptime Sql: type, comptime Policy: type) type {
7 const history = Sql.history;
8 const sync = Sql.sync;
9
10 return struct {
11 pub const Opened = struct {
12 dir: std.Io.Dir,
13 lock: std.Io.File,
14 history: history.History,
15
16 pub fn deinit(self: *Opened) void {
17 self.history.deinit();
18 self.lock.close(fs_io);
19 self.dir.close(fs_io);
20 self.* = undefined;
21 }
22 };
23
24 pub fn write(
25 allocator: Allocator,
26 store_dir: []const u8,
27 remote_store_dir: []const u8,
28 ) !void {
29 var probe = openDir(remote_store_dir) catch
30 return error.SyncRemoteNotFound;
31 probe.close(fs_io);
32 const history_path = try std.fs.path.join(
33 allocator,
34 &.{ remote_store_dir, Policy.history_name },
35 );
36 defer allocator.free(history_path);
37 const lock_path = try std.fs.path.join(
38 allocator,
39 &.{ remote_store_dir, Policy.lock_name },
40 );
41 defer allocator.free(lock_path);
42
43 var entry = try sync.RemoteConfigEntry.initWithDefaultBranch(
44 allocator,
45 Policy.remote_name,
46 history_path,
47 lock_path,
48 Policy.branch_name,
49 );
50 defer entry.deinit(allocator);
51 var config = try sync.RemoteConfig.init(allocator, &.{entry});
52 defer config.deinit();
53
54 var dir = try std.Io.Dir.openDirAbsolute(fs_io, store_dir, .{});
55 defer dir.close(fs_io);
56 try sync.writeRemoteConfig(
57 allocator,
58 dir,
59 .{ .path = Policy.config_name },
60 &config,
61 );
62 }
63
64 pub fn read(
65 allocator: Allocator,
66 store_dir: []const u8,
67 ) !?[]u8 {
68 var dir = try std.Io.Dir.openDirAbsolute(fs_io, store_dir, .{});
69 defer dir.close(fs_io);
70 var config = sync.readRemoteConfig(
71 allocator,
72 dir,
73 .{ .path = Policy.config_name },
74 ) catch |err| switch (err) {
75 error.FileNotFound => if (Policy.missing_config_returns_null)
76 return null
77 else
78 return err,
79 else => return err,
80 };
81 defer config.deinit();
82 const entry = config.find(Policy.remote_name) orelse return null;
83 const remote_dir = std.fs.path.dirname(entry.history_path) orelse
84 return null;
85 return try allocator.dupe(u8, remote_dir);
86 }
87
88 pub fn open(
89 allocator: Allocator,
90 remote_store_dir: []const u8,
91 ) !Opened {
92 var dir = try openDir(remote_store_dir);
93 errdefer dir.close(fs_io);
94 _ = dir.statFile(fs_io, Policy.history_name, .{}) catch
95 return error.SyncRemoteNotFound;
96 var lock_file = try lock(dir);
97 errdefer lock_file.close(fs_io);
98 const opened = try history.History.open(
99 allocator,
100 dir,
101 .{ .path = Policy.history_name, .recovery = .reject },
102 );
103 return .{ .dir = dir, .lock = lock_file, .history = opened };
104 }
105
106 pub fn openDir(remote_store_dir: []const u8) !std.Io.Dir {
107 return std.Io.Dir.openDirAbsolute(
108 fs_io,
109 remote_store_dir,
110 .{},
111 ) catch |err| switch (err) {
112 error.FileNotFound, error.NotDir => return error.SyncRemoteNotFound,
113 else => return err,
114 };
115 }
116
117 pub fn lock(dir: std.Io.Dir) !std.Io.File {
118 return dir.createFile(fs_io, Policy.lock_name, .{
119 .read = true,
120 .truncate = false,
121 .permissions = @fromBackingInt(@intCast(0o600)),
122 .lock = .exclusive,
123 .lock_nonblocking = true,
124 }) catch |err| switch (err) {
125 error.WouldBlock => return error.SyncRemoteBusy,
126 else => return err,
127 };
128 }
129
130 pub fn historyExists(remote_dir: std.Io.Dir) bool {
131 _ = remote_dir.statFile(
132 fs_io,
133 Policy.history_name,
134 .{},
135 ) catch return false;
136 return true;
137 }
138
139 pub fn needsBootstrap(
140 allocator: Allocator,
141 remote_dir: std.Io.Dir,
142 ) !bool {
143 if (!historyExists(remote_dir)) return true;
144 var opened = try history.History.open(
145 allocator,
146 remote_dir,
147 .{ .path = Policy.history_name, .recovery = .reject },
148 );
149 defer opened.deinit();
150 return (try opened.ref(Policy.branch_name)) == null;
151 }
152 };
153 }