const std = @import("std"); const memora = @import("memora"); const Storage = memora.Storage; pub const Image = @import("image.zig"); const Self = @This(); pub const empty: Self = .{}; const Timestamp = struct { index: usize, node: std.DoublyLinkedList.Node, pub fn next(self: *Timestamp) ?*Timestamp { return @fieldParentPtr("node", self.node.next orelse return null); } pub fn prev(self: *Timestamp) ?*Timestamp { return @fieldParentPtr("node", self.node.prev orelse return null); } }; items: std.ArrayList(Image) = .empty, rw_lock: std.Thread.RwLock = .{}, timestamp_order: std.DoublyLinkedList = .{}, pub fn init( self: *Self, storage: *Storage, ) !void { var dir = storage.dir.openDir("image", .{ .iterate = true }) catch blk: { try storage.dir.makeDir("image"); break :blk try storage.dir.openDir("image", .{ .iterate = true }); }; defer dir.close(); var iterator = dir.iterate(); errdefer self.items.deinit(storage.allocator); while (try iterator.next()) |entry| { if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".jpg")) { try self.add( storage.allocator, try .init(storage, entry.name[0..entry.name.len - 4]) ); } } } pub fn add(self: *Self, allocator: std.mem.Allocator, image: Image) !void { self.rw_lock.lock(); defer self.rw_lock.unlock(); try self.items.append( allocator, image, ); var current = self.timestamp_order.first; if (current == null) { const timestamp = try allocator.create(Timestamp); timestamp.index = self.items.items.len - 1; self.timestamp_order.append(×tamp.node); return; } while (current) |c| { const index = @as(*Timestamp, @fieldParentPtr("node", c)).index; if (image.timestamp orelse 0 > self.items.items[index].timestamp orelse 0) { const timestamp = try allocator.create(Timestamp); timestamp.index = self.items.items.len - 1; self.timestamp_order.insertBefore(c, ×tamp.node); break; } current = c.next; } } pub fn save( self: *Self, storage: *Storage, reader: *std.Io.Reader, size: usize, ) !void { return self.add(storage.allocator, try Image.new(storage, reader, size)); } pub fn list(self: *Self) memora.locked.Shared([]Image) { self.rw_lock.lockShared(); return .{ .value = self.items.items, .rw_lock = &self.rw_lock, }; } pub fn first_by_timestamp(self: *Self) memora.locked.Shared(?*Timestamp) { self.rw_lock.lockShared(); return .{ .value = if (self.timestamp_order.first) |first| @fieldParentPtr("node", first) else null, .rw_lock = &self.rw_lock, }; }