pub const Tab = struct { allocator: std.mem.Allocator, currentLocation: ?*Container, currentItems: locked(?std.ArrayList(Arc(*Item))), currentLocationChanged: Observable(?*Container), currentItemsChanged: bool = false, threadPool: *std.Thread.Pool, rootProvider: *RootProvider, pub fn init( self: *Tab, threadPool: *std.Thread.Pool, rootProvider: *RootProvider, allocator: std.mem.Allocator, ) void { self.* = Tab{ .allocator = allocator, .currentItems = .{ .data = null }, .currentLocationChanged = Observable(?*Container).init(allocator), .currentLocation = null, .threadPool = threadPool, .rootProvider = rootProvider, }; } pub fn setCurrentLocation(self: *Tab, newLocationFullName: models.FullName) !void { if (self.currentLocation) |c| { c.item.deinit(); } errdefer { self.currentLocation = null; self.currentLocationChanged.notify(null); } const newLocation = try self.rootProvider.getItemByFullName(newLocationFullName, &.{}, self.allocator); errdefer { newLocation.deinit(); } const newLocationContainer = switch (newLocation.item) { .container => |c| c, .element => return error.NotContainer, }; self.currentLocation = newLocationContainer; self.currentLocationChanged.notify(newLocationContainer); //TODO: Proper error handling std.Thread.Pool.spawn(self.threadPool, loadItemsWrapper, .{ self, newLocationContainer }) catch unreachable; } fn loadItemsWrapper(self: *Tab, location: *Container) void { loadItems(self, location) catch return; } fn loadItems(self: *Tab, location: *Container) !void { { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); const data = self.currentItems.data; self.currentItems.data = null; if (data) |currentItems| { for (currentItems.items) |*item| { if (item.releaseUnwrap()) |i| i.deinit(); } currentItems.deinit(); } } { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); self.currentItems.data = std.ArrayList(Arc(*Item)).init(self.allocator); } errdefer { self.currentItems.data.?.deinit(); self.currentItems.data = null; } //TODO: add async while (location.childrenLoading) { std.Thread.sleep(1 * std.time.ns_per_ms); } for (location.children.items) |item| { const resolvedItem = location.item.provider.getItemByFullName(item, &.{ .skipChildInit = true }, self.allocator) catch |e| { //TODO: save error to container errors std.debug.print("error {} {s}\r\n", .{ e, item.path }); continue; }; self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); // const arc = try Arc(*Item).init(self.allocator, resolvedItem); // try self.currentItems.data.?.append(arc); try self.currentItems.data.?.append(try Arc(*Item).init(self.allocator, resolvedItem)); } { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); self.currentItemsChanged = true; } } pub fn deinit(self: *Tab) void { if (self.currentLocation) |c| { c.item.deinit(); } const data = self.currentItems.data; self.currentItems.data = null; if (data) |currentItems| { for (currentItems.items) |arc_item| { if (arc_item.releaseUnwrap()) |item| { item.deinit(); } } currentItems.deinit(); } self.allocator.destroy(self); } }; const std = @import("std"); const assert = std.debug.assert; const Mutex = std.Thread.Mutex; const models = @import("../models.zig"); const Container = models.Container; const Item = models.Item; const locked = @import("../sync.zig").locked; const Observable = @import("../observable.zig").Observable; const RootProvider = @import("../provider/root.zig").RootProvider; const Arc = @import("zigrc").Arc;