fediglam/src/api/services/drive.zig

130 lines
3.5 KiB
Zig

const std = @import("std");
const util = @import("util");
const sql = @import("sql");
const types = @import("./types.zig");
const Uuid = util.Uuid;
const DateTime = util.DateTime;
const Entry = types.drive.DriveEntry;
fn doGetQuery(db: anytype, comptime clause: []const u8, args: anytype, alloc: std.mem.Allocator) !Entry {
const q = std.fmt.comptimePrint(
\\SELECT id, path, owner_id, name, file_id, kind, parent_directory_id
\\FROM drive_entry_path
\\WHERE {s}
\\LIMIT 1
,
.{clause},
);
return db.queryRow(Entry, q, args, alloc) catch |err| switch (err) {
error.NoRows => return error.NotFound,
else => |e| return e,
};
}
pub fn stat(db: anytype, owner: Uuid, path: []const u8, alloc: std.mem.Allocator) !Entry {
return try doGetQuery(
db,
"owner_id = $1 AND path = ('/' || $2)",
.{
owner,
std.mem.trim(u8, path, "/"),
},
alloc,
);
}
pub fn get(db: anytype, id: Uuid, alloc: std.mem.Allocator) !Entry {
return try doGetQuery(db, "id = $1", .{id}, alloc);
}
/// Creates a file or directory
pub fn create(db: anytype, owner: Uuid, dir: []const u8, name: []const u8, file_id: ?Uuid, alloc: std.mem.Allocator) !Uuid {
if (name.len == 0) return error.EmptyName;
const id = Uuid.randV4(util.getThreadPrng());
const tx = try db.begin();
errdefer tx.rollback();
const parent = try stat(tx, owner, dir, alloc);
defer util.deepFree(alloc, parent);
tx.insert("drive_entry", .{
.id = id,
.owner_id = owner,
.name = name,
.parent_directory_id = parent.id,
.file_id = file_id,
}, alloc) catch |err| switch (err) {
error.UniqueViolation => return error.PathAlreadyExists,
else => |e| return e,
};
try tx.commit();
return id;
}
pub fn delete(db: anytype, id: Uuid, alloc: std.mem.Allocator) !void {
const tx = try db.beginOrSavepoint();
errdefer tx.rollback();
if ((try tx.queryRow(
std.meta.Tuple(&.{usize}),
\\SELECT COUNT(1)
\\FROM drive_entry
\\WHERE parent_directory_id = $1
,
.{id},
alloc,
))[0] != 0) {
return error.DirectoryNotEmpty;
}
try tx.exec("DELETE FROM drive_entry WHERE id = $1", .{id}, alloc);
try tx.commitOrRelease();
}
pub fn move(db: anytype, owner: Uuid, src: []const u8, dest: []const u8, alloc: std.mem.Allocator) !void {
const tx = try db.beginOrSavepoint();
errdefer tx.rollback();
const val = try stat(tx, owner, src, alloc);
defer util.deepFree(alloc, val);
if (val.parent_directory_id == null) return error.RootDirectory;
var split = std.mem.splitBackwards(u8, std.mem.trim(u8, dest, "/"), "/");
const name = split.first();
const dir = split.rest();
const parent = try stat(tx, owner, dir, alloc);
defer util.deepFree(alloc, parent);
try tx.exec(
\\UPDATE drive_entry
\\SET name = $1, parent_directory_id = $2
\\WHERE id = $3
,
.{ name, parent.id, val.id },
alloc,
);
try tx.commitOrRelease();
}
// TODO: paginate this
pub fn list(db: anytype, id: Uuid, alloc: std.mem.Allocator) ![]Entry {
return (db.queryRows(Entry,
\\SELECT id, path, owner_id, name, file_id, kind, parent_directory_id
\\FROM drive_entry_path
\\WHERE parent_directory_id = $1
, .{id}, null, alloc) catch |err| switch (err) {
error.NoRows => return error.NotFound,
else => |e| return e,
});
}