move snapshot types into a new model for reuse
This commit is contained in:
parent
ea0dd624b3
commit
a7e8dc6030
2 changed files with 104 additions and 74 deletions
|
|
@ -22,6 +22,17 @@ const atomic = @import("../atomic.zig");
|
|||
const version = @import("../version.zig");
|
||||
const portfolio_mod = @import("../models/portfolio.zig");
|
||||
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{
|
||||
PortfolioEmpty,
|
||||
|
|
@ -356,80 +367,10 @@ pub fn quoteDateRange(infos: []const QuoteInfo) ?struct { min: Date, max: Date }
|
|||
|
||||
// ── Snapshot records ─────────────────────────────────────────
|
||||
//
|
||||
// Each record kind below is a plain struct suitable for `srf.fmtFrom`.
|
||||
// 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 demux on.
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
// Record structs live in `src/models/snapshot.zig` — see the re-exports
|
||||
// near the top of this file. The types are separated from this command
|
||||
// module so analytics code (`src/analytics/timeline.zig`) can reference
|
||||
// them without depending on a `commands/` module.
|
||||
|
||||
/// Build the full snapshot in memory. Does not touch disk.
|
||||
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