diff --git a/src/cache/store.zig b/src/cache/store.zig index 338b9f7..cfea1f9 100644 --- a/src/cache/store.zig +++ b/src/cache/store.zig @@ -693,7 +693,6 @@ pub fn serializePortfolio(allocator: std.mem.Allocator, lots: []const Lot) ![]co /// Deserialize a portfolio from SRF data. Caller owns the returned Portfolio. pub fn deserializePortfolio(allocator: std.mem.Allocator, data: []const u8) !Portfolio { - const LotType = @import("../models/portfolio.zig").LotType; var lots: std.ArrayList(Lot) = .empty; errdefer { for (lots.items) |lot| { @@ -706,134 +705,22 @@ pub fn deserializePortfolio(allocator: std.mem.Allocator, data: []const u8) !Por } var reader = std.Io.Reader.fixed(data); - const parsed = srf.parse(&reader, allocator, .{ .alloc_strings = false }) catch return error.InvalidData; - defer parsed.deinit(); + var it = srf.iterator(&reader, allocator, .{ .alloc_strings = false }) catch return error.InvalidData; + defer it.deinit(); - for (parsed.records) |record| { - var lot = Lot{ - .symbol = "", - .shares = 0, - .open_date = Date.epoch, - .open_price = 0, - }; - var sym_raw: ?[]const u8 = null; - var note_raw: ?[]const u8 = null; - var account_raw: ?[]const u8 = null; - var sec_type_raw: ?[]const u8 = null; - var ticker_raw: ?[]const u8 = null; + while (try it.next()) |fields| { + var lot = fields.to(Lot) catch continue; - for (record.fields) |field| { - if (std.mem.eql(u8, field.key, "symbol")) { - if (field.value) |v| sym_raw = switch (v) { - .string => |s| s, - else => null, - }; - } else if (std.mem.eql(u8, field.key, "shares")) { - if (field.value) |v| lot.shares = Store.numVal(v); - } else if (std.mem.eql(u8, field.key, "open_date")) { - if (field.value) |v| { - const str = switch (v) { - .string => |s| s, - else => continue, - }; - lot.open_date = Date.parse(str) catch continue; - } - } else if (std.mem.eql(u8, field.key, "open_price")) { - if (field.value) |v| lot.open_price = Store.numVal(v); - } else if (std.mem.eql(u8, field.key, "close_date")) { - if (field.value) |v| { - const str = switch (v) { - .string => |s| s, - else => continue, - }; - lot.close_date = Date.parse(str) catch null; - } - } else if (std.mem.eql(u8, field.key, "close_price")) { - if (field.value) |v| lot.close_price = Store.numVal(v); - } else if (std.mem.eql(u8, field.key, "note")) { - if (field.value) |v| note_raw = switch (v) { - .string => |s| s, - else => null, - }; - } else if (std.mem.eql(u8, field.key, "account")) { - if (field.value) |v| account_raw = switch (v) { - .string => |s| s, - else => null, - }; - } else if (std.mem.eql(u8, field.key, "security_type")) { - if (field.value) |v| sec_type_raw = switch (v) { - .string => |s| s, - else => null, - }; - } else if (std.mem.eql(u8, field.key, "maturity_date")) { - if (field.value) |v| { - const str = switch (v) { - .string => |s| s, - else => continue, - }; - lot.maturity_date = Date.parse(str) catch null; - } - } else if (std.mem.eql(u8, field.key, "rate")) { - if (field.value) |v| { - const r = Store.numVal(v); - if (r > 0) lot.rate = r; - } - } else if (std.mem.eql(u8, field.key, "drip")) { - if (field.value) |v| { - switch (v) { - .string => |s| lot.drip = std.mem.eql(u8, s, "true") or std.mem.eql(u8, s, "1"), - .number => |n| lot.drip = n > 0, - .boolean => |b| lot.drip = b, - else => {}, - } - } - } else if (std.mem.eql(u8, field.key, "ticker")) { - if (field.value) |v| ticker_raw = switch (v) { - .string => |s| s, - else => null, - }; - } else if (std.mem.eql(u8, field.key, "price")) { - if (field.value) |v| { - const p = Store.numVal(v); - if (p > 0) lot.price = p; - } - } else if (std.mem.eql(u8, field.key, "price_date")) { - if (field.value) |v| { - const str = switch (v) { - .string => |s| s, - else => continue, - }; - lot.price_date = Date.parse(str) catch null; - } - } - } + // Dupe owned strings before iterator.deinit() frees the backing buffer + lot.symbol = try allocator.dupe(u8, lot.symbol); + if (lot.note) |n| lot.note = try allocator.dupe(u8, n); + if (lot.account) |a| lot.account = try allocator.dupe(u8, a); + if (lot.ticker) |t| lot.ticker = try allocator.dupe(u8, t); - // Determine lot type - if (sec_type_raw) |st| { - lot.security_type = LotType.fromString(st); - } - - // Cash lots don't require a symbol -- generate a placeholder - if (lot.security_type == .cash) { - if (sym_raw == null) { - lot.symbol = try allocator.dupe(u8, "CASH"); - } else { - lot.symbol = try allocator.dupe(u8, sym_raw.?); - } - } else if (sym_raw) |s| { - lot.symbol = try allocator.dupe(u8, s); - } else continue; - - if (note_raw) |n| { - lot.note = try allocator.dupe(u8, n); - } - - if (account_raw) |a| { - lot.account = try allocator.dupe(u8, a); - } - - if (ticker_raw) |t| { - lot.ticker = try allocator.dupe(u8, t); + // Cash lots without a symbol get a placeholder + if (lot.security_type == .cash and lot.symbol.len == 0) { + allocator.free(lot.symbol); + lot.symbol = try allocator.dupe(u8, "CASH"); } try lots.append(allocator, lot);