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 }