247 lines
8.0 KiB
Zig
247 lines
8.0 KiB
Zig
pub const LocalProviderId = "local";
|
|
|
|
// TODO: the container might be freed while this runs
|
|
// Tab should hold something at pass it here
|
|
fn loadChildren(container: *Container) void {
|
|
defer {
|
|
container.childrenLoading = false;
|
|
}
|
|
|
|
var dir = std.fs.cwd().openDir(container.item.nativePath.path, .{ .iterate = true }) catch {
|
|
// const errorContent = std.fmt.allocPrint(container.item.allocator, "Could not open directory '{s}'.", .{container.item.nativePath.path}) catch return;
|
|
// container.item.errors.append(.{ .content = errorContent }) catch return;
|
|
return;
|
|
};
|
|
defer dir.close();
|
|
|
|
var it = dir.iterate();
|
|
while (it.next() catch return) |entry| {
|
|
const child = container.item.fullName.getChild(entry.name, container.item.allocator) catch return;
|
|
|
|
container.children.mutex.lock();
|
|
defer container.children.mutex.unlock();
|
|
container.children.data.append(child) catch return;
|
|
}
|
|
}
|
|
|
|
const VTable: ProviderVTable = .{
|
|
.getItemByFullName = getItemByFullNameGeneric,
|
|
.canHandle = canHandleGeneric,
|
|
.deinit = deinitGeneric,
|
|
};
|
|
|
|
pub fn getItemByFullNameGeneric(
|
|
context: *anyopaque,
|
|
fullName: FullName,
|
|
initContext: *const InitContext,
|
|
allocator: std.mem.Allocator,
|
|
) GetItemsError!*Item {
|
|
const self: *LocalContentProvider = @ptrCast(@alignCast(context));
|
|
return self.getItemByFullName(fullName, initContext, allocator);
|
|
}
|
|
|
|
pub fn canHandleGeneric(
|
|
context: *anyopaque,
|
|
fullName: FullName,
|
|
) bool {
|
|
const self: *LocalContentProvider = @ptrCast(@alignCast(context));
|
|
return self.canHandle(fullName);
|
|
}
|
|
|
|
pub fn deinitGeneric(_: *anyopaque) void {}
|
|
|
|
pub fn getFullNameByNativePath(allocator: std.mem.Allocator, nativePath: NativePath) !FullName {
|
|
const correct_sep = try std.mem.replaceOwned(u8, allocator, nativePath.path, std.fs.path.sep_str, "/");
|
|
defer allocator.free(correct_sep);
|
|
|
|
const asd = if (std.mem.startsWith(u8, correct_sep, "/"))
|
|
correct_sep[1..]
|
|
else
|
|
correct_sep;
|
|
|
|
const full_name_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ LocalProviderId, asd });
|
|
|
|
return .{ .path = full_name_path };
|
|
}
|
|
|
|
pub fn getNativePathByFullName(allocator: std.mem.Allocator, fullName: FullName) ![]u8 {
|
|
std.debug.assert(!std.mem.endsWith(u8, fullName.path, "/"));
|
|
|
|
if (fullName.path.len < LocalProviderId.len or
|
|
!std.mem.eql(u8, fullName.path[0..LocalProviderId.len], LocalProviderId))
|
|
{
|
|
return ProviderError.WrongProvider;
|
|
}
|
|
|
|
//NOTE: their might be an other edge case where the fullname is local/ that is not handled by this
|
|
if (fullName.path.len == LocalProviderId.len) {
|
|
if (native_os == .linux)
|
|
return try std.fmt.allocPrint(allocator, "/", .{})
|
|
else
|
|
return ProviderError.WrongProvider;
|
|
}
|
|
|
|
const fullNameWithoutId = fullName.path[LocalProviderId.len + 1 ..];
|
|
var native_path = try std.mem.replaceOwned(u8, allocator, fullNameWithoutId, "/", std.fs.path.sep_str);
|
|
|
|
if (native_os == .linux) {
|
|
const linux_native_path = try std.fmt.allocPrint(allocator, "/{s}", .{native_path});
|
|
allocator.free(native_path);
|
|
native_path = linux_native_path;
|
|
}
|
|
|
|
return native_path;
|
|
}
|
|
|
|
pub const LocalContentProvider = struct {
|
|
threadPool: *std.Thread.Pool,
|
|
|
|
pub fn getItemByFullName(
|
|
self: *LocalContentProvider,
|
|
fullName: FullName,
|
|
initContext: *const InitContext,
|
|
allocator: std.mem.Allocator,
|
|
) GetItemsError!*Item {
|
|
const native_path = try getNativePathByFullName(allocator, fullName);
|
|
defer allocator.free(native_path);
|
|
|
|
const kind: union(enum) {
|
|
directory,
|
|
file: struct { size: u64 },
|
|
} = blk: {
|
|
// FIXME: properly handle different errors
|
|
var dir = std.fs.cwd().openDir(native_path, .{});
|
|
if (dir) |*d| {
|
|
d.close();
|
|
break :blk .directory;
|
|
} else |_| {}
|
|
|
|
var file = std.fs.cwd().openFile(native_path, .{});
|
|
if (file) |*f| {
|
|
const size = size: {
|
|
const stat = f.stat() catch break :size 0;
|
|
break :size stat.size;
|
|
};
|
|
f.close();
|
|
break :blk .{ .file = .{ .size = size } };
|
|
} else |_| {}
|
|
|
|
return GetItemsError.NotExists;
|
|
};
|
|
|
|
return switch (kind) {
|
|
.directory => blk: {
|
|
const container = try allocator.create(Container);
|
|
container.* = Container{
|
|
.children = locked(std.ArrayList(FullName)){
|
|
.data = std.ArrayList(FullName).init(allocator),
|
|
},
|
|
.childrenLoading = true,
|
|
.item = undefined,
|
|
};
|
|
|
|
const val: ItemEnum = .{
|
|
.container = container,
|
|
};
|
|
try initItem(
|
|
self,
|
|
&container.item,
|
|
val,
|
|
fullName,
|
|
allocator,
|
|
);
|
|
|
|
if (!initContext.skipChildInit) {
|
|
try self.threadPool.spawn(loadChildren, .{container});
|
|
} else {
|
|
container.childrenLoading = false;
|
|
}
|
|
|
|
break :blk &container.item;
|
|
},
|
|
.file => |file| blk: {
|
|
const element = try allocator.create(Element);
|
|
element.* = Element{
|
|
.item = undefined,
|
|
.content_size = file.size,
|
|
};
|
|
|
|
const val: ItemEnum = .{
|
|
.element = element,
|
|
};
|
|
|
|
try initItem(
|
|
self,
|
|
&element.item,
|
|
val,
|
|
fullName,
|
|
allocator,
|
|
);
|
|
|
|
break :blk &element.item;
|
|
},
|
|
};
|
|
}
|
|
|
|
fn initItem(
|
|
contentProvider: *LocalContentProvider,
|
|
item: *Item,
|
|
innerItem: ItemEnum,
|
|
fullName: FullName,
|
|
allocator: std.mem.Allocator,
|
|
) !void {
|
|
const basename = std.fs.path.basename(fullName.path);
|
|
|
|
const name = try allocator.dupe(u8, basename);
|
|
const displayName = try allocator.dupe(u8, basename);
|
|
const fullName2 = try allocator.dupe(u8, fullName.path);
|
|
const nativePath = try getNativePathByFullName(allocator, fullName);
|
|
|
|
const parentFullName = try fullName.getParent(allocator);
|
|
const parent = if (parentFullName) |p| models.AbsolutePath{ .path = p, .type = .container } else null;
|
|
|
|
item.* = Item{
|
|
.allocator = allocator,
|
|
.provider = contentProvider.provider(),
|
|
.name = name,
|
|
.displayName = displayName,
|
|
.fullName = .{ .path = fullName2 },
|
|
.nativePath = models.NativePath{ .path = nativePath },
|
|
.parent = parent,
|
|
.item = innerItem,
|
|
.errors = std.ArrayList(models.Error).init(allocator),
|
|
};
|
|
}
|
|
|
|
pub fn canHandle(
|
|
_: *LocalContentProvider,
|
|
fullName: FullName,
|
|
) bool {
|
|
return std.mem.startsWith(u8, fullName.path, "local/");
|
|
}
|
|
|
|
pub fn provider(self: *LocalContentProvider) Provider {
|
|
return Provider{
|
|
.object = self,
|
|
.vtable = &VTable,
|
|
};
|
|
}
|
|
};
|
|
|
|
const std = @import("std");
|
|
const models = @import("../models.zig");
|
|
const Provider = @import("provider.zig").Provider;
|
|
const ProviderVTable = @import("provider.zig").VTable;
|
|
const GetItemsError = @import("provider.zig").GetItemsError;
|
|
const ProviderError = @import("provider.zig").ProviderError;
|
|
const InitContext = @import("provider.zig").InitContext;
|
|
|
|
const FullName = models.FullName;
|
|
const NativePath = models.NativePath;
|
|
const Item = models.Item;
|
|
const ItemEnum = models.ItemEnum;
|
|
const Element = models.Element;
|
|
const Container = models.Container;
|
|
const native_os = @import("builtin").os.tag;
|
|
const locked = @import("../sync.zig").locked;
|