2022-12-06 09:48:36 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const util = @import("util");
|
|
|
|
const sql = @import("sql");
|
2023-01-04 19:03:23 +00:00
|
|
|
const types = @import("./types.zig");
|
2022-12-06 09:48:36 +00:00
|
|
|
|
|
|
|
const Uuid = util.Uuid;
|
|
|
|
const DateTime = util.DateTime;
|
2023-01-04 19:03:23 +00:00
|
|
|
const Entry = types.drive.DriveEntry;
|
2022-12-06 09:48:36 +00:00
|
|
|
|
2023-01-04 19:25:08 +00:00
|
|
|
fn doGetQuery(db: anytype, comptime clause: []const u8, args: anytype, alloc: std.mem.Allocator) !Entry {
|
|
|
|
const q = std.fmt.comptimePrint(
|
2022-12-06 09:48:36 +00:00
|
|
|
\\SELECT id, path, owner_id, name, file_id, kind, parent_directory_id
|
|
|
|
\\FROM drive_entry_path
|
2023-01-04 19:25:08 +00:00
|
|
|
\\WHERE {s}
|
2022-12-06 09:48:36 +00:00
|
|
|
\\LIMIT 1
|
2023-01-04 19:25:08 +00:00
|
|
|
,
|
|
|
|
.{clause},
|
|
|
|
);
|
|
|
|
|
|
|
|
return db.queryRow(Entry, q, args, alloc) catch |err| switch (err) {
|
2022-12-06 09:48:36 +00:00
|
|
|
error.NoRows => return error.NotFound,
|
|
|
|
else => |e| return e,
|
2023-01-04 19:25:08 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2022-12-06 09:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a file or directory
|
2023-01-04 19:03:23 +00:00
|
|
|
pub fn create(db: anytype, owner: Uuid, dir: []const u8, name: []const u8, file_id: ?Uuid, alloc: std.mem.Allocator) !Uuid {
|
2022-12-06 09:48:36 +00:00
|
|
|
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();
|
2022-12-07 05:41:01 +00:00
|
|
|
|
2023-01-04 19:03:23 +00:00
|
|
|
return id;
|
2022-12-06 09:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2022-12-07 05:41:01 +00:00
|
|
|
pub fn list(db: anytype, id: Uuid, alloc: std.mem.Allocator) ![]Entry {
|
|
|
|
return (db.queryRows(Entry,
|
2022-12-06 09:48:36 +00:00
|
|
|
\\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,
|
|
|
|
});
|
|
|
|
}
|