From b1627080559f8281e6f8fc787a01f22402118efc Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Thu, 19 Mar 2026 13:04:20 -0700 Subject: [PATCH] consolidate drip summary --- src/commands/portfolio.zig | 24 ++++-------------------- src/format.zig | 17 +++++++++++++++++ src/tui/portfolio_tab.zig | 21 ++++++++------------- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/commands/portfolio.zig b/src/commands/portfolio.zig index 6711244..4283b08 100644 --- a/src/commands/portfolio.zig +++ b/src/commands/portfolio.zig @@ -364,31 +364,15 @@ pub fn display( const drip = fmt.aggregateDripLots(lots_for_sym.items); if (!drip.st.isEmpty()) { - var avg_buf: [24]u8 = undefined; - var d1_buf: [10]u8 = undefined; - var d2_buf: [10]u8 = undefined; + var drip_buf: [128]u8 = undefined; try cli.setFg(out, color, cli.CLR_MUTED); - try out.print(" ST: {d} DRIP lots, {d:.1} shares, avg {s} ({s} to {s})\n", .{ - drip.st.lot_count, - drip.st.shares, - fmt.fmtMoneyAbs(&avg_buf, drip.st.avgCost()), - if (drip.st.first_date) |d| d.format(&d1_buf)[0..7] else "?", - if (drip.st.last_date) |d| d.format(&d2_buf)[0..7] else "?", - }); + try out.print(" {s}\n", .{fmt.fmtDripSummary(&drip_buf, "ST", drip.st)}); try cli.reset(out, color); } if (!drip.lt.isEmpty()) { - var avg_buf2: [24]u8 = undefined; - var d1_buf2: [10]u8 = undefined; - var d2_buf2: [10]u8 = undefined; + var drip_buf2: [128]u8 = undefined; try cli.setFg(out, color, cli.CLR_MUTED); - try out.print(" LT: {d} DRIP lots, {d:.1} shares, avg {s} ({s} to {s})\n", .{ - drip.lt.lot_count, - drip.lt.shares, - fmt.fmtMoneyAbs(&avg_buf2, drip.lt.avgCost()), - if (drip.lt.first_date) |d| d.format(&d1_buf2)[0..7] else "?", - if (drip.lt.last_date) |d| d.format(&d2_buf2)[0..7] else "?", - }); + try out.print(" {s}\n", .{fmt.fmtDripSummary(&drip_buf2, "LT", drip.lt)}); try cli.reset(out, color); } } diff --git a/src/format.zig b/src/format.zig index fd92545..7c77c14 100644 --- a/src/format.zig +++ b/src/format.zig @@ -427,6 +427,23 @@ pub fn aggregateDripLots(lots: []const Lot) DripAggregation { return result; } +/// Format a DRIP summary line: "ST: 12 DRIP lots, 3.5 shares, avg $45.67 (2023-01 to 2024-06)" +pub fn fmtDripSummary(buf: []u8, label: []const u8, summary: DripSummary) []const u8 { + var avg_buf: [24]u8 = undefined; + var d1_buf: [10]u8 = undefined; + var d2_buf: [10]u8 = undefined; + const d1: []const u8 = if (summary.first_date) |d| d.format(&d1_buf)[0..7] else "?"; + const d2: []const u8 = if (summary.last_date) |d| d.format(&d2_buf)[0..7] else "?"; + return std.fmt.bufPrint(buf, "{s}: {d} DRIP lots, {d:.1} shares, avg {s} ({s} to {s})", .{ + label, + summary.lot_count, + summary.shares, + fmtMoneyAbs(&avg_buf, summary.avgCost()), + d1, + d2, + }) catch "?"; +} + // ── Shared rendering helpers (CLI + TUI) ───────────────────── /// Layout constants for analysis breakdown views. diff --git a/src/tui/portfolio_tab.zig b/src/tui/portfolio_tab.zig index be9d82a..dfb46d2 100644 --- a/src/tui/portfolio_tab.zig +++ b/src/tui/portfolio_tab.zig @@ -867,20 +867,15 @@ pub fn drawContent(self: *App, arena: std.mem.Allocator, buf: []vaxis.Cell, widt } }, .drip_summary => { - const label_str: []const u8 = if (row.drip_is_lt) "LT" else "ST"; - var drip_avg_buf: [24]u8 = undefined; - var drip_d1_buf: [10]u8 = undefined; - var drip_d2_buf: [10]u8 = undefined; - const drip_d1: []const u8 = if (row.drip_date_first) |d| d.format(&drip_d1_buf)[0..7] else "?"; - const drip_d2: []const u8 = if (row.drip_date_last) |d| d.format(&drip_d2_buf)[0..7] else "?"; - const text = try std.fmt.allocPrint(arena, " {s}: {d} DRIP lots, {d:.1} shares, avg {s} ({s} to {s})", .{ - label_str, - row.drip_lot_count, - row.drip_shares, - fmt.fmtMoneyAbs(&drip_avg_buf, row.drip_avg_cost), - drip_d1, - drip_d2, + var drip_buf: [128]u8 = undefined; + const drip_text = fmt.fmtDripSummary(&drip_buf, if (row.drip_is_lt) "LT" else "ST", .{ + .lot_count = row.drip_lot_count, + .shares = row.drip_shares, + .cost = row.drip_shares * row.drip_avg_cost, + .first_date = row.drip_date_first, + .last_date = row.drip_date_last, }); + const text = try std.fmt.allocPrint(arena, " {s}", .{drip_text}); const drip_style = if (is_cursor) th.selectStyle() else th.mutedStyle(); try lines.append(arena, .{ .text = text, .style = drip_style }); },