Renderer/src/template/registry.zig

287 lines
9.7 KiB
Zig
Raw Normal View History

2026-07-09 23:16:38 +01:00
const std = @import("std");
const App = @import("app");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn Registry(
comptime K: type,
comptime V: type,
) type {
return struct {
const Self = @This();
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Metadata = struct {
source: ?[]const u8 = null,
reload: ?*const fn (*V, App.scheduler.Context) anyerror!void = null,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Entry = struct {
key: K,
value: V,
};
//
// FIELDS
//
meta: std.ArrayList(Metadata),
lookup: std.AutoHashMap(K, usize),
entries: std.ArrayList(Entry),
alloc: std.mem.Allocator,
//
// LIFECYCLE
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(alloc: std.mem.Allocator) Self {
return .{
.meta = .empty,
.lookup = .init(alloc),
.entries = .empty,
.alloc = alloc,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
self.meta.deinit(self.alloc);
self.lookup.deinit();
self.entries.deinit(self.alloc);
}
//
// INSERTION
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn put(
self: *Self,
key: K,
value: V,
) !void {
if (self.lookup.contains(key)) return error.Duplicate;
try self.putNoDup(key, value, .{});
}
/// ----------------------------------------------------
/// Insert or replace
/// ----------------------------------------------------
pub fn upsert(
self: *Self,
key: K,
value: V,
) !?V {
if (self.lookup.contains(key)) {
const idx = self.lookup.get(key).?;
const old = self.entries.items[idx].value;
self.entries.items[idx].value = value;
return old;
}
try self.putNoDup(key, value, .{});
return null;
}
/// ----------------------------------------------------
/// Insert with metadata
/// ----------------------------------------------------
pub fn putWith(
self: *Self,
key: K,
value: V,
metadata: Metadata,
) !void {
if (self.lookup.contains(key)) return error.Duplicate;
try self.putNoDup(key, value, metadata);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn putNoDup(
self: *Self,
key: K,
value: V,
metadata: Metadata,
) !void {
try self.entries.append(self.alloc, .{ .key = key, .value = value });
errdefer _ = self.entries.swapRemove(self.entries.items.len - 1);
try self.meta.append(self.alloc, metadata);
errdefer _ = self.meta.swapRemove(self.meta.items.len - 1);
try self.lookup.put(key, self.entries.items.len - 1);
}
//
// REMOVAL
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn remove(self: *Self, key: K) bool {
const idx = self.lookup.get(key) orelse return false;
self.removeIndex(idx);
return true;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn removeLast(self: *Self) bool {
if (self.entries.items.len == 0) return false;
const last = self.entries.items.len - 1;
_ = self.lookup.remove(self.entries.items[last].key);
self.entries.swapRemove(last);
self.meta.swapRemove(last);
return true;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn removeIndex(self: *Self, idx: usize) void {
const last = self.entries.items.len - 1;
// --- UPDATE LOOKUP ---
if (idx != last) {
self.lookup.put(self.entries.items[last].key, idx) catch {};
}
_ = self.meta.swapRemove(idx);
_ = self.lookup.remove(self.entries.items[idx].key);
_ = self.entries.swapRemove(idx);
}
//
// LOOKUP
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn contains(self: Self, key: K) bool {
return self.lookup.contains(key);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn get(self: Self, key: K) ?*const V {
const idx = self.lookup.get(key) orelse return null;
return &self.entries.items[idx].value;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getPtr(self: *Self, key: K) ?*V {
const idx = self.lookup.get(key) orelse return null;
return &self.entries.items[idx].value;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getEntry(self: Self, key: K) ?*const Entry {
const idx = self.lookup.get(key) orelse return null;
return &self.entries.items[idx];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getMetadata(self: Self, key: K) ?*const Metadata {
const idx = self.lookup.get(key) orelse return null;
return &self.meta.items[idx];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getPtrMetadata(self: *Self, key: K) ?*Metadata {
const idx = self.lookup.get(key) orelse return null;
return &self.meta.items[idx];
}
//
// ITERATION
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn count(self: Self) usize {
return self.entries.items.len;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn values(self: Self) []const Entry {
return self.entries.items;
}
/// ----------------------------------------------------
/// Value, Metadata
/// ----------------------------------------------------
pub fn iterator(self: *Self) Iterator {
return .{ .registry = self, .index = 0 };
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Iterator = struct {
registry: *Self,
index: usize,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn next(it: *Iterator) ?struct { value: *V, meta: *Metadata } {
if (it.index >= it.registry.entries.items.len) return null;
defer it.index += 1;
return .{
.value = &it.registry.entries.items[it.index].value,
.meta = &it.registry.meta.items[it.index],
};
}
};
//
// IDK
//
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn reloadAll(
self: *Self,
ctx: App.scheduler.Context,
) anyerror!void {
var it = self.iterator();
while (it.next()) |entry| {
if (entry.meta.reload) |reload| try reload(entry.value, ctx);
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinitReverse(self: *Self) void {
while (self.entries.items.len > 0) {
_ = self.removeLast();
}
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn clear(self: *Self) void {
self.meta.clearRetainingCapacity();
self.lookup.clearRetainingCapacity();
self.entries.clearRetainingCapacity();
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn clearAndFree(self: *Self) void {
self.meta.clearAndFree();
self.lookup.clearAndFree();
self.entries.clearAndFree();
}
};
}