fediglam/src/api/methods/drive.zig

207 lines
5.8 KiB
Zig

const std = @import("std");
const util = @import("util");
const pkg = @import("../lib.zig");
const services = @import("../services.zig");
const Uuid = util.Uuid;
const DateTime = util.DateTime;
const ApiContext = pkg.ApiContext;
const DriveEntry = pkg.drive.DriveEntry;
pub fn upload(
alloc: std.mem.Allocator,
ctx: ApiContext,
svcs: anytype,
args: pkg.drive.UploadArgs,
body: []const u8,
) !Uuid {
const owner = ctx.userId() orelse return error.NoToken;
const file_id = try svcs.createFile(alloc, owner, .{
.filename = args.filename,
.description = args.description,
.content_type = args.content_type,
.sensitive = args.sensitive,
}, body);
const entry_id = entry: {
errdefer svcs.deleteFile(alloc, file_id) catch |err| {
std.log.err("Unable to delete file {}: {}", .{ file_id, err });
};
break :entry svcs.createDriveEntry(
alloc,
owner,
args.dir,
args.filename,
file_id,
) catch |err| switch (err) {
error.PathAlreadyExists => {
var buf: [256]u8 = undefined;
var split = std.mem.splitBackwards(u8, args.filename, ".");
const ext = split.first();
const name = split.rest();
const new_name = try std.fmt.bufPrint(&buf, "{s}.{s}.{s}", .{ name, file_id, ext });
break :entry try svcs.createDriveEntry(
alloc,
owner,
args.dir,
new_name,
file_id,
);
},
else => |e| return e,
};
};
return entry_id;
}
pub fn mkdir(
alloc: std.mem.Allocator,
ctx: ApiContext,
svcs: anytype,
parent_path: []const u8,
name: []const u8,
) !Uuid {
const user_id = ctx.userId() orelse return error.NoToken;
return try svcs.createDriveEntry(alloc, user_id, parent_path, name, null);
}
pub fn delete(
alloc: std.mem.Allocator,
ctx: ApiContext,
svcs: anytype,
path: []const u8,
) !void {
const user_id = ctx.userId() orelse return error.NoToken;
const entry = try svcs.statDriveEntry(alloc, user_id, path);
defer util.deepFree(alloc, entry);
try svcs.deleteDriveEntry(alloc, entry.id);
if (entry.file_id) |file_id| try svcs.deleteFile(alloc, file_id);
}
pub fn move(
alloc: std.mem.Allocator,
ctx: ApiContext,
svcs: anytype,
src: []const u8,
dest: []const u8,
) !void {
const user_id = ctx.userId() orelse return error.NoToken;
try svcs.moveDriveEntry(alloc, user_id, src, dest);
}
pub fn get(
alloc: std.mem.Allocator,
ctx: ApiContext,
svcs: anytype,
path: []const u8,
) !pkg.drive.DriveEntry {
const user_id = ctx.userId() orelse return error.NoToken;
const entry = try svcs.statDriveEntry(alloc, user_id, path);
defer util.deepFree(alloc, entry);
return try convert(alloc, svcs, entry, true);
}
pub fn getById(
alloc: std.mem.Allocator,
ctx: ApiContext,
svcs: anytype,
id: Uuid,
) !pkg.drive.DriveEntry {
const user_id = ctx.userId() orelse return error.NoToken;
const entry = try svcs.getDriveEntry(alloc, id);
defer util.deepFree(alloc, entry);
if (!Uuid.eql(entry.owner_id, user_id)) return error.NotFound;
return try convert(alloc, svcs, entry, true);
}
// TODO: These next two functions are more about files than drive entries, consider refactor?
pub fn update(
alloc: std.mem.Allocator,
ctx: ApiContext,
svcs: anytype,
path: []const u8,
meta: pkg.files.UpdateArgs,
) !void {
const user_id = ctx.userId() orelse return error.NoToken;
const entry = try svcs.statDriveEntry(alloc, user_id, path);
defer util.deepFree(alloc, entry);
try svcs.updateFileMetadata(alloc, entry.file_id orelse return error.NotAFile, meta);
}
pub fn dereference(
alloc: std.mem.Allocator,
_: ApiContext,
svcs: anytype,
file_id: Uuid,
) !pkg.files.DerefResult {
const meta = try svcs.statFile(alloc, file_id);
errdefer util.deepFree(alloc, meta);
return .{
.meta = meta,
.data = try svcs.derefFile(alloc, file_id),
};
}
fn convert(
alloc: std.mem.Allocator,
svcs: anytype,
entry: services.drive.DriveEntry,
recurse: bool,
) !DriveEntry {
if (entry.file_id) |file_id| return .{
.file = .{
.id = entry.id,
.owner_id = entry.owner_id,
.name = entry.name,
.path = entry.path,
.parent_directory_id = entry.parent_directory_id,
.meta = try svcs.statFile(alloc, file_id),
},
} else return .{
.dir = .{
.id = entry.id,
.owner_id = entry.owner_id,
.name = entry.name,
.path = entry.path,
.parent_directory_id = entry.parent_directory_id,
.children = blk: {
if (!recurse) break :blk null;
const children = try svcs.listDriveEntry(alloc, entry.id);
const result = alloc.alloc(DriveEntry, children.len) catch |err| {
util.deepFree(alloc, children);
return err;
};
var count: usize = 0;
errdefer for (children) |child, i| {
if (i < count)
util.deepFree(alloc, result[i])
else
util.deepFree(alloc, child);
};
defer alloc.free(children);
errdefer alloc.free(result);
for (children) |child, i| {
result[i] = try convert(alloc, svcs, child, false);
count += 1;
}
break :blk result;
},
},
};
}