const std = @import("std"); const json_utils = @import("../json_utils.zig"); const Store = @import("../store.zig"); const assert = std.debug.assert; const SimpleItem = @This(); id: Store.Id, quantity: u64, pub fn init(id: Store.Id, quantity: u64) SimpleItem { return SimpleItem{ .id = id, .quantity = quantity }; } pub fn parse(store: *Store, slot_obj: std.json.ObjectMap) !?SimpleItem { const code = try json_utils.getStringRequired(slot_obj, "code"); if (code.len == 0) return null; const quantity = try json_utils.getIntegerRequired(slot_obj, "quantity"); if (quantity < 0) return error.InvalidQuantity; return SimpleItem{ .id = try store.items.getOrReserveId(code), .quantity = @intCast(quantity), }; } pub fn BoundedArray(comptime slot_count: u32) type { const Items = std.BoundedArray(SimpleItem, slot_count); return struct { items: Items, pub fn init() @This() { return @This(){ .items = Items.init(0) catch unreachable }; } pub fn parse(store: *Store, slots_array: std.json.Array) !@This() { var slots = Items.init(0) catch unreachable; for (slots_array.items) |slot_value| { const slot_obj = json_utils.asObject(slot_value) orelse return error.InvalidType; if (try SimpleItem.parse(store, slot_obj)) |slot| { try slots.append(slot); } } return @This(){ .items = slots }; } fn findSlotIndex(self: *const @This(), id: Store.Id) ?usize { for (0.., self.items.slice()) |i, *slot| { if (slot.id == id) { return i; } } return null; } fn findSlot(self: *@This(), id: Store.Id) ?*SimpleItem { if (self.findSlotIndex(id)) |index| { return &self.items.buffer[index]; } return null; } pub fn remove(self: *@This(), id: Store.Id, quantity: u64) void { const slot_index = self.findSlotIndex(id) orelse unreachable; const slot = self.items.get(slot_index); assert(slot.quantity >= quantity); slot.quantity -= quantity; if (slot.quantity == 0) { self.items.swapRemove(slot_index); } } pub fn add(self: *@This(), id: Store.Id, quantity: u64) !void { if (quantity == 0) return; if (self.findSlot(id)) |slot| { slot.quantity += quantity; } else { try self.items.append(SimpleItem.init(id, quantity)); } } pub fn addSlice(self: *@This(), items: []const SimpleItem) void { for (items) |item| { self.add(item.id, item.quantity); } } pub fn removeSlice(self: *@This(), items: []const SimpleItem) void { for (items) |item| { self.remove(item.id, item.quantity); } } pub fn getQuantity(self: *const @This(), id: Store.Id) u64 { if (self.findSlotIndex(id)) |index| { return self.items.get(index).quantity; } return 0; } pub fn totalQuantity(self: *const @This()) u64 { var count: u64 = 0; for (self.items.constSlice()) |slot| { count += slot.quantity; } return count; } pub fn slice(self: *@This()) []SimpleItem { return self.items.slice(); } pub fn format( self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { _ = fmt; _ = options; try writer.print("{s}{{ ", .{@typeName(@This())}); for (self.items.slice()) |item| { try writer.print("{}, ", .{item}); } try writer.writeAll("}"); } }; }