move snapshot types into a new model for reuse
This commit is contained in:
parent
75570419c5
commit
05d2fce9a5
2 changed files with 104 additions and 74 deletions
|
|
@ -22,6 +22,17 @@ const atomic = @import("../atomic.zig");
|
||||||
const version = @import("../version.zig");
|
const version = @import("../version.zig");
|
||||||
const portfolio_mod = @import("../models/portfolio.zig");
|
const portfolio_mod = @import("../models/portfolio.zig");
|
||||||
const Date = @import("../models/date.zig").Date;
|
const Date = @import("../models/date.zig").Date;
|
||||||
|
const model = @import("../models/snapshot.zig");
|
||||||
|
|
||||||
|
// Re-export record types so callers that reach `commands/snapshot.zig`
|
||||||
|
// (tests, mostly) still see the familiar names. New code should prefer
|
||||||
|
// `@import("models/snapshot.zig")` directly.
|
||||||
|
pub const MetaRow = model.MetaRow;
|
||||||
|
pub const TotalRow = model.TotalRow;
|
||||||
|
pub const TaxTypeRow = model.TaxTypeRow;
|
||||||
|
pub const AccountRow = model.AccountRow;
|
||||||
|
pub const LotRow = model.LotRow;
|
||||||
|
pub const Snapshot = model.Snapshot;
|
||||||
|
|
||||||
pub const SnapshotError = error{
|
pub const SnapshotError = error{
|
||||||
PortfolioEmpty,
|
PortfolioEmpty,
|
||||||
|
|
@ -356,80 +367,10 @@ pub fn quoteDateRange(infos: []const QuoteInfo) ?struct { min: Date, max: Date }
|
||||||
|
|
||||||
// ── Snapshot records ─────────────────────────────────────────
|
// ── Snapshot records ─────────────────────────────────────────
|
||||||
//
|
//
|
||||||
// Each record kind below is a plain struct suitable for `srf.fmtFrom`.
|
// Record structs live in `src/models/snapshot.zig` — see the re-exports
|
||||||
// Field order in the struct declaration IS the on-disk order — srf's
|
// near the top of this file. The types are separated from this command
|
||||||
// `Record.from` iterates `inline for (info.fields)`. The leading `kind`
|
// module so analytics code (`src/analytics/timeline.zig`) can reference
|
||||||
// field is the discriminator readers demux on.
|
// them without depending on a `commands/` module.
|
||||||
//
|
|
||||||
// IMPORTANT: `kind` does NOT have a default value. SRF elides fields
|
|
||||||
// whose value matches the declared default (see setField in srf.zig),
|
|
||||||
// so a `kind: []const u8 = "meta"` would vanish from the output. Each
|
|
||||||
// construction site supplies the tag explicitly.
|
|
||||||
//
|
|
||||||
// Optional fields default to `null` so they're elided on null values —
|
|
||||||
// that's the behavior we want for `price`, `quote_date`, etc.
|
|
||||||
|
|
||||||
pub const MetaRow = struct {
|
|
||||||
kind: []const u8,
|
|
||||||
snapshot_version: u32,
|
|
||||||
as_of_date: Date,
|
|
||||||
captured_at: i64,
|
|
||||||
zfin_version: []const u8,
|
|
||||||
quote_date_min: ?Date = null,
|
|
||||||
quote_date_max: ?Date = null,
|
|
||||||
stale_count: usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const TotalRow = struct {
|
|
||||||
kind: []const u8,
|
|
||||||
scope: []const u8,
|
|
||||||
value: f64,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const TaxTypeRow = struct {
|
|
||||||
kind: []const u8,
|
|
||||||
label: []const u8,
|
|
||||||
value: f64,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const AccountRow = struct {
|
|
||||||
kind: []const u8,
|
|
||||||
name: []const u8,
|
|
||||||
value: f64,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const LotRow = struct {
|
|
||||||
kind: []const u8,
|
|
||||||
symbol: []const u8,
|
|
||||||
lot_symbol: []const u8,
|
|
||||||
account: []const u8,
|
|
||||||
security_type: []const u8,
|
|
||||||
shares: f64,
|
|
||||||
open_price: f64,
|
|
||||||
cost_basis: f64,
|
|
||||||
value: f64,
|
|
||||||
/// Null for non-stock lots (cash/CD/illiquid have no per-share price).
|
|
||||||
price: ?f64 = null,
|
|
||||||
/// Null for non-stock lots.
|
|
||||||
quote_date: ?Date = null,
|
|
||||||
/// Emitted only when true (default is false, which srf skips).
|
|
||||||
quote_stale: bool = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Snapshot = struct {
|
|
||||||
meta: MetaRow,
|
|
||||||
totals: []TotalRow,
|
|
||||||
tax_types: []TaxTypeRow,
|
|
||||||
accounts: []AccountRow,
|
|
||||||
lots: []LotRow,
|
|
||||||
|
|
||||||
pub fn deinit(self: *Snapshot, allocator: std.mem.Allocator) void {
|
|
||||||
allocator.free(self.totals);
|
|
||||||
allocator.free(self.tax_types);
|
|
||||||
allocator.free(self.accounts);
|
|
||||||
allocator.free(self.lots);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Build the full snapshot in memory. Does not touch disk.
|
/// Build the full snapshot in memory. Does not touch disk.
|
||||||
fn buildSnapshot(
|
fn buildSnapshot(
|
||||||
|
|
|
||||||
89
src/models/snapshot.zig
Normal file
89
src/models/snapshot.zig
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
//! Snapshot record types — the wire format for `history/<date>-portfolio.srf`.
|
||||||
|
//!
|
||||||
|
//! Each record kind below is a plain struct suitable for `srf.fmtFrom`
|
||||||
|
//! on the write side and `srf.parse` → `Record.to(T)` on the read side.
|
||||||
|
//! Field order in the struct declaration IS the on-disk order: srf's
|
||||||
|
//! `Record.from` iterates `inline for (info.fields)`. The leading `kind`
|
||||||
|
//! field is the discriminator readers use to demux heterogeneous records.
|
||||||
|
//!
|
||||||
|
//! Lives in `src/models/` because these types describe the data format
|
||||||
|
//! itself — they're consumed by the snapshot writer command
|
||||||
|
//! (`src/commands/snapshot.zig`), the history reader (`src/history.zig`),
|
||||||
|
//! and the timeline analytics (`src/analytics/timeline.zig`). Putting
|
||||||
|
//! them in `commands/` would force analytics to depend on a command
|
||||||
|
//! module, which would be backwards.
|
||||||
|
//!
|
||||||
|
//! IMPORTANT: `kind` does NOT have a default value. SRF elides fields
|
||||||
|
//! whose value matches the declared default (see `setField` in
|
||||||
|
//! `srf.zig`), so `kind: []const u8 = "meta"` would vanish from the
|
||||||
|
//! output. Each construction site supplies the tag explicitly.
|
||||||
|
//!
|
||||||
|
//! Optional fields default to `null` so they're elided on null values —
|
||||||
|
//! that's the behavior we want for `price`, `quote_date`, etc.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Date = @import("date.zig").Date;
|
||||||
|
|
||||||
|
pub const MetaRow = struct {
|
||||||
|
kind: []const u8,
|
||||||
|
snapshot_version: u32,
|
||||||
|
as_of_date: Date,
|
||||||
|
captured_at: i64,
|
||||||
|
zfin_version: []const u8,
|
||||||
|
quote_date_min: ?Date = null,
|
||||||
|
quote_date_max: ?Date = null,
|
||||||
|
stale_count: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TotalRow = struct {
|
||||||
|
kind: []const u8,
|
||||||
|
scope: []const u8,
|
||||||
|
value: f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TaxTypeRow = struct {
|
||||||
|
kind: []const u8,
|
||||||
|
label: []const u8,
|
||||||
|
value: f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const AccountRow = struct {
|
||||||
|
kind: []const u8,
|
||||||
|
name: []const u8,
|
||||||
|
value: f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const LotRow = struct {
|
||||||
|
kind: []const u8,
|
||||||
|
symbol: []const u8,
|
||||||
|
lot_symbol: []const u8,
|
||||||
|
account: []const u8,
|
||||||
|
security_type: []const u8,
|
||||||
|
shares: f64,
|
||||||
|
open_price: f64,
|
||||||
|
cost_basis: f64,
|
||||||
|
value: f64,
|
||||||
|
/// Null for non-stock lots (cash/CD/illiquid have no per-share price).
|
||||||
|
price: ?f64 = null,
|
||||||
|
/// Null for non-stock lots.
|
||||||
|
quote_date: ?Date = null,
|
||||||
|
/// Emitted only when true (default is false, which srf skips).
|
||||||
|
quote_stale: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// In-memory portfolio snapshot. `meta` is a single record; the four
|
||||||
|
/// slices are per-section record collections.
|
||||||
|
pub const Snapshot = struct {
|
||||||
|
meta: MetaRow,
|
||||||
|
totals: []TotalRow,
|
||||||
|
tax_types: []TaxTypeRow,
|
||||||
|
accounts: []AccountRow,
|
||||||
|
lots: []LotRow,
|
||||||
|
|
||||||
|
pub fn deinit(self: *Snapshot, allocator: std.mem.Allocator) void {
|
||||||
|
allocator.free(self.totals);
|
||||||
|
allocator.free(self.tax_types);
|
||||||
|
allocator.free(self.accounts);
|
||||||
|
allocator.free(self.lots);
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue