rework framework to something much more sane
This commit is contained in:
parent
3b02c97153
commit
825419c7c3
22 changed files with 597 additions and 601 deletions
|
|
@ -7,11 +7,11 @@ const Money = @import("../Money.zig");
|
||||||
|
|
||||||
pub const ParsedArgs = struct {};
|
pub const ParsedArgs = struct {};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "analysis";
|
.name = "analysis",
|
||||||
pub const group: framework.Group = .portfolio;
|
.group = .portfolio,
|
||||||
pub const synopsis: []const u8 = "Show portfolio breakdowns by asset class, sector, geo, account, tax type";
|
.synopsis = "Show portfolio breakdowns by asset class, sector, geo, account, tax type",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin analysis
|
\\Usage: zfin analysis
|
||||||
\\
|
\\
|
||||||
\\Show portfolio analysis: equities/fixed-income split, plus
|
\\Show portfolio analysis: equities/fixed-income split, plus
|
||||||
|
|
@ -23,7 +23,8 @@ pub const meta = struct {
|
||||||
\\Run `zfin enrich <portfolio.srf> > metadata.srf` to bootstrap
|
\\Run `zfin enrich <portfolio.srf> > metadata.srf` to bootstrap
|
||||||
\\classifications, then edit by hand.
|
\\classifications, then edit by hand.
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -2250,11 +2250,11 @@ pub const ParsedArgs = struct {
|
||||||
stale_days: u32 = default_stale_days,
|
stale_days: u32 = default_stale_days,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "audit";
|
.name = "audit",
|
||||||
pub const group: framework.Group = .hygiene;
|
.group = .hygiene,
|
||||||
pub const synopsis: []const u8 = "Reconcile portfolio against brokerage exports + portfolio hygiene check";
|
.synopsis = "Reconcile portfolio against brokerage exports + portfolio hygiene check",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin audit [opts]
|
\\Usage: zfin audit [opts]
|
||||||
\\
|
\\
|
||||||
\\Two modes in one command:
|
\\Two modes in one command:
|
||||||
|
|
@ -2275,7 +2275,8 @@ pub const meta = struct {
|
||||||
\\ --schwab-summary Schwab account summary; copy from accounts
|
\\ --schwab-summary Schwab account summary; copy from accounts
|
||||||
\\ summary page, paste to stdin, then ^D
|
\\ summary page, paste to stdin, then ^D
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ pub const ParsedArgs = struct {
|
||||||
sub: Subcommand,
|
sub: Subcommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "cache";
|
.name = "cache",
|
||||||
pub const group: framework.Group = .infra;
|
.group = .infra,
|
||||||
pub const synopsis: []const u8 = "Inspect or clear the local provider-data cache";
|
.synopsis = "Inspect or clear the local provider-data cache",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin cache <stats|clear>
|
\\Usage: zfin cache <stats|clear>
|
||||||
\\
|
\\
|
||||||
\\Subcommands:
|
\\Subcommands:
|
||||||
|
|
@ -31,7 +31,8 @@ pub const meta = struct {
|
||||||
\\
|
\\
|
||||||
\\Cache directory is `$ZFIN_CACHE_DIR` if set, else `~/.cache/zfin`.
|
\\Cache directory is `$ZFIN_CACHE_DIR` if set, else `~/.cache/zfin`.
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -87,11 +87,11 @@ pub const ParsedArgs = struct {
|
||||||
commit_after: ?git.CommitSpec = null,
|
commit_after: ?git.CommitSpec = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "compare";
|
.name = "compare",
|
||||||
pub const group: framework.Group = .timeseries;
|
.group = .timeseries,
|
||||||
pub const synopsis: []const u8 = "Compare portfolio at two points in time (or vs. today)";
|
.synopsis = "Compare portfolio at two points in time (or vs. today)",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage:
|
\\Usage:
|
||||||
\\ zfin compare <DATE> compare DATE vs. live
|
\\ zfin compare <DATE> compare DATE vs. live
|
||||||
\\ zfin compare <DATE1> <DATE2> compare two historical dates
|
\\ zfin compare <DATE1> <DATE2> compare two historical dates
|
||||||
|
|
@ -124,7 +124,8 @@ pub const meta = struct {
|
||||||
\\ --commit-after <SPEC> Pin the after-side commit; accepts
|
\\ --commit-after <SPEC> Pin the after-side commit; accepts
|
||||||
\\ `working` for the working copy.
|
\\ `working` for the working copy.
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -187,11 +187,11 @@ pub const ParsedArgs = struct {
|
||||||
after: ?git.CommitSpec = null,
|
after: ?git.CommitSpec = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "contributions";
|
.name = "contributions",
|
||||||
pub const group: framework.Group = .timeseries;
|
.group = .timeseries,
|
||||||
pub const synopsis: []const u8 = "Show money added since the last recorded state in git";
|
.synopsis = "Show money added since the last recorded state in git",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin contributions [opts]
|
\\Usage: zfin contributions [opts]
|
||||||
\\
|
\\
|
||||||
\\Show contributions, withdrawals, and lot-level changes between
|
\\Show contributions, withdrawals, and lot-level changes between
|
||||||
|
|
@ -224,7 +224,8 @@ pub const meta = struct {
|
||||||
\\--since and --commit-before describe the same axis; pass at most
|
\\--since and --commit-before describe the same axis; pass at most
|
||||||
\\one. Same for --until and --commit-after.
|
\\one. Same for --until and --commit-after.
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ pub const ParsedArgs = struct {
|
||||||
symbol: []const u8,
|
symbol: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "divs";
|
.name = "divs",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Show dividend history (with TTM yield) for a symbol";
|
.synopsis = "Show dividend history (with TTM yield) for a symbol",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin divs <SYMBOL>
|
\\Usage: zfin divs <SYMBOL>
|
||||||
\\
|
\\
|
||||||
\\Show the dividend history for a symbol from Polygon.io. Cached
|
\\Show the dividend history for a symbol from Polygon.io. Cached
|
||||||
|
|
@ -24,7 +24,7 @@ pub const meta = struct {
|
||||||
\\ zfin divs VTI # quarterly distributions + yield
|
\\ zfin divs VTI # quarterly distributions + yield
|
||||||
\\ zfin divs T # historical AT&T dividends
|
\\ zfin divs T # historical AT&T dividends
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ pub const ParsedArgs = struct {
|
||||||
symbol: []const u8,
|
symbol: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "earnings";
|
.name = "earnings",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Show earnings history (with EPS surprise) and upcoming events";
|
.synopsis = "Show earnings history (with EPS surprise) and upcoming events",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin earnings <SYMBOL>
|
\\Usage: zfin earnings <SYMBOL>
|
||||||
\\
|
\\
|
||||||
\\Show the earnings history (estimate vs. actual + surprise %)
|
\\Show the earnings history (estimate vs. actual + surprise %)
|
||||||
|
|
@ -30,7 +30,7 @@ pub const meta = struct {
|
||||||
\\ zfin earnings NVDA
|
\\ zfin earnings NVDA
|
||||||
\\ zfin earnings AAPL | head -8 # last two years of quarters
|
\\ zfin earnings AAPL | head -8 # last two years of quarters
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ pub const ParsedArgs = struct {
|
||||||
arg: []const u8,
|
arg: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "enrich";
|
.name = "enrich",
|
||||||
pub const group: framework.Group = .hygiene;
|
.group = .hygiene,
|
||||||
pub const synopsis: []const u8 = "Bootstrap metadata.srf from Alpha Vantage (25 req/day limit)";
|
.synopsis = "Bootstrap metadata.srf from Alpha Vantage (25 req/day limit)",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin enrich <FILE|SYMBOL>
|
\\Usage: zfin enrich <FILE|SYMBOL>
|
||||||
\\
|
\\
|
||||||
\\Bootstrap a `metadata.srf` classification file from Alpha
|
\\Bootstrap a `metadata.srf` classification file from Alpha
|
||||||
|
|
@ -39,7 +39,8 @@ pub const meta = struct {
|
||||||
\\ zfin enrich portfolio.srf > metadata.srf # whole portfolio
|
\\ zfin enrich portfolio.srf > metadata.srf # whole portfolio
|
||||||
\\ zfin enrich AAPL >> metadata.srf # single symbol append
|
\\ zfin enrich AAPL >> metadata.srf # single symbol append
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ pub const ParsedArgs = struct {
|
||||||
symbol: []const u8,
|
symbol: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "etf";
|
.name = "etf",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Show ETF profile (holdings, sectors, expense ratio)";
|
.synopsis = "Show ETF profile (holdings, sectors, expense ratio)",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin etf <SYMBOL>
|
\\Usage: zfin etf <SYMBOL>
|
||||||
\\
|
\\
|
||||||
\\Show the ETF profile (expense ratio, AUM, dividend yield,
|
\\Show the ETF profile (expense ratio, AUM, dividend yield,
|
||||||
|
|
@ -25,7 +25,7 @@ pub const meta = struct {
|
||||||
\\ zfin etf VTI # broad market index
|
\\ zfin etf VTI # broad market index
|
||||||
\\ zfin etf TQQQ # leveraged (warning surfaced)
|
\\ zfin etf TQQQ # leveraged (warning surfaced)
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,12 @@
|
||||||
//! ```zig
|
//! ```zig
|
||||||
//! pub const ParsedArgs = ...; // struct or tagged union
|
//! pub const ParsedArgs = ...; // struct or tagged union
|
||||||
//!
|
//!
|
||||||
//! pub const meta = struct {
|
//! pub const meta: framework.Meta = .{
|
||||||
//! pub const name: []const u8 = "perf";
|
//! .name = "perf",
|
||||||
//! pub const group: framework.Group = .symbol_lookup;
|
//! .group = .symbol_lookup,
|
||||||
//! pub const synopsis: []const u8 = "Show 1y/3y/5y/10y trailing returns";
|
//! .synopsis = "Show 1y/3y/5y/10y trailing returns",
|
||||||
//! pub const help: []const u8 = ...; // multi-line; rendered by `--help`
|
//! .help = "...", // multi-line; rendered by `--help`
|
||||||
|
//! .uppercase_first_arg = true,
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs;
|
//! pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs;
|
||||||
|
|
@ -72,6 +73,58 @@ pub const Group = enum {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Meta ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Per-command metadata. Every command module declares
|
||||||
|
/// `pub const meta: framework.Meta = .{ ... };` with all fields
|
||||||
|
/// populated. The compiler enforces the contract via the field
|
||||||
|
/// types; `validateCommandModule` reduces to a single
|
||||||
|
/// `expectDeclWithType` check on the declaration's type.
|
||||||
|
///
|
||||||
|
/// Fields are deliberately required (no defaults) so command
|
||||||
|
/// authors think about each one rather than relying on framework
|
||||||
|
/// defaults. In particular, `uppercase_first_arg` deserves an
|
||||||
|
/// explicit decision per command — see field doc-comment.
|
||||||
|
pub const Meta = struct {
|
||||||
|
/// User-facing subcommand name. Must match the field name used
|
||||||
|
/// for this command in the `command_modules` registry literal
|
||||||
|
/// (the dispatcher matches by `f.name`).
|
||||||
|
name: []const u8,
|
||||||
|
|
||||||
|
/// Workflow-grouping tag for the `zfin help` sectioned output.
|
||||||
|
/// See `Group` for the canonical taxonomy.
|
||||||
|
group: Group,
|
||||||
|
|
||||||
|
/// One-line description rendered next to the command name in
|
||||||
|
/// `zfin help`.
|
||||||
|
synopsis: []const u8,
|
||||||
|
|
||||||
|
/// Multi-line help text rendered by `zfin <cmd> --help`. The
|
||||||
|
/// dispatcher emits this verbatim; include the usage line,
|
||||||
|
/// flag descriptions, and any caveats. Trailing newline is
|
||||||
|
/// added automatically if missing.
|
||||||
|
help: []const u8,
|
||||||
|
|
||||||
|
/// Whether to uppercase `cmd_args[0]` before calling
|
||||||
|
/// `parseArgs`. Symbol-taking commands (perf, quote, divs,
|
||||||
|
/// splits, options, earnings, etf, history-symbol-mode,
|
||||||
|
/// lookup) set this to `true` so users can type
|
||||||
|
/// `zfin perf aapl` and have it normalized to `AAPL`.
|
||||||
|
/// Non-symbol commands (cache, version, portfolio, ...) set
|
||||||
|
/// it to `false`.
|
||||||
|
///
|
||||||
|
/// The dispatcher's `normalizeFirstArg` honors a "skip when
|
||||||
|
/// arg starts with `-`" rule, so flag-leading invocations
|
||||||
|
/// like `zfin history --since 2026-01-01` pass through
|
||||||
|
/// unchanged regardless of this setting.
|
||||||
|
///
|
||||||
|
/// No default — every command author must make an explicit
|
||||||
|
/// choice. This is metadata about command shape, not an
|
||||||
|
/// optional opt-in; the `false` answer is just as load-bearing
|
||||||
|
/// as `true`.
|
||||||
|
uppercase_first_arg: bool,
|
||||||
|
};
|
||||||
|
|
||||||
// ── Refresh policy ────────────────────────────────────────────
|
// ── Refresh policy ────────────────────────────────────────────
|
||||||
|
|
||||||
/// Cache-freshness policy for the invocation. Defined here ahead of
|
/// Cache-freshness policy for the invocation. Defined here ahead of
|
||||||
|
|
@ -218,62 +271,22 @@ pub fn validateCommandModule(comptime Module: type) void {
|
||||||
@compileError("Command module `" ++ mod_name ++ "` is missing `pub const ParsedArgs = ...;` " ++
|
@compileError("Command module `" ++ mod_name ++ "` is missing `pub const ParsedArgs = ...;` " ++
|
||||||
"(use `pub const ParsedArgs = struct {};` if the command takes no arguments)");
|
"(use `pub const ParsedArgs = struct {};` if the command takes no arguments)");
|
||||||
}
|
}
|
||||||
if (!@hasDecl(Module, "meta")) {
|
|
||||||
@compileError("Command module `" ++ mod_name ++ "` is missing `pub const meta = struct { ... };` " ++
|
|
||||||
"(see src/commands/framework.zig for the full contract)");
|
|
||||||
}
|
|
||||||
|
|
||||||
const meta_decl = Module.meta;
|
|
||||||
const ParsedArgs = Module.ParsedArgs;
|
const ParsedArgs = Module.ParsedArgs;
|
||||||
|
|
||||||
// ── meta fields ────────────────────────────────────────
|
// ── meta ───────────────────────────────────────────────
|
||||||
|
// One check covers all five fields. The compiler enforces
|
||||||
|
// field types via `Meta`'s declaration; missing fields
|
||||||
|
// surface as Zig's native "missing field" comptime errors
|
||||||
|
// pointing at the call site.
|
||||||
validator.expectDeclWithType(
|
validator.expectDeclWithType(
|
||||||
"Command module",
|
"Command module",
|
||||||
mod_name,
|
mod_name,
|
||||||
meta_decl,
|
Module,
|
||||||
"name",
|
"meta",
|
||||||
[]const u8,
|
Meta,
|
||||||
"pub const name: []const u8 = \"...\";",
|
"pub const meta: framework.Meta = .{ .name = \"...\", .group = ..., .synopsis = \"...\", .help = \"...\", .uppercase_first_arg = ... };",
|
||||||
);
|
);
|
||||||
validator.expectDeclWithType(
|
|
||||||
"Command module",
|
|
||||||
mod_name,
|
|
||||||
meta_decl,
|
|
||||||
"group",
|
|
||||||
Group,
|
|
||||||
"pub const group: framework.Group = .symbol_lookup;",
|
|
||||||
);
|
|
||||||
validator.expectDeclWithType(
|
|
||||||
"Command module",
|
|
||||||
mod_name,
|
|
||||||
meta_decl,
|
|
||||||
"synopsis",
|
|
||||||
[]const u8,
|
|
||||||
"pub const synopsis: []const u8 = \"one-line description for `zfin help`\";",
|
|
||||||
);
|
|
||||||
validator.expectDeclWithType(
|
|
||||||
"Command module",
|
|
||||||
mod_name,
|
|
||||||
meta_decl,
|
|
||||||
"help",
|
|
||||||
[]const u8,
|
|
||||||
"pub const help: []const u8 = \"multi-line text for `zfin <cmd> --help`\";",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Optional: `uppercase_first_arg`. Symbol-taking commands
|
|
||||||
// (perf, quote, divs, ...) set this to `true` so the
|
|
||||||
// dispatcher uppercases `cmd_args[0]` before calling
|
|
||||||
// `parseArgs`. When omitted, defaults to false.
|
|
||||||
if (@hasDecl(meta_decl, "uppercase_first_arg")) {
|
|
||||||
validator.expectDeclWithType(
|
|
||||||
"Command module",
|
|
||||||
mod_name,
|
|
||||||
meta_decl,
|
|
||||||
"uppercase_first_arg",
|
|
||||||
bool,
|
|
||||||
"pub const uppercase_first_arg: bool = true; // (optional, defaults to false)",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── parseArgs ──────────────────────────────────────────
|
// ── parseArgs ──────────────────────────────────────────
|
||||||
validator.expectFnInferredError(
|
validator.expectFnInferredError(
|
||||||
|
|
@ -301,16 +314,6 @@ pub fn validateCommandModule(comptime Module: type) void {
|
||||||
|
|
||||||
// ── Help rendering ────────────────────────────────────────────
|
// ── Help rendering ────────────────────────────────────────────
|
||||||
|
|
||||||
/// Whether a command module opts into having its first positional
|
|
||||||
/// argument uppercased before `parseArgs` runs. Symbol-taking
|
|
||||||
/// commands set `meta.uppercase_first_arg = true` so users can type
|
|
||||||
/// `zfin perf aapl` and have it normalize to `AAPL`. Honors the
|
|
||||||
/// "skip when the arg is a flag" rule (i.e. starts with `-`).
|
|
||||||
pub fn uppercasesFirstArg(comptime Module: type) bool {
|
|
||||||
if (!@hasDecl(Module.meta, "uppercase_first_arg")) return false;
|
|
||||||
return Module.meta.uppercase_first_arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `cmd_args[0]` exists, is non-empty, and isn't a flag, return
|
/// If `cmd_args[0]` exists, is non-empty, and isn't a flag, return
|
||||||
/// a copy with the first byte-string uppercased. Otherwise return
|
/// a copy with the first byte-string uppercased. Otherwise return
|
||||||
/// `cmd_args` unchanged. The new slice is owned by `allocator`
|
/// `cmd_args` unchanged. The new slice is owned by `allocator`
|
||||||
|
|
@ -405,11 +408,12 @@ const testing = std.testing;
|
||||||
const ProbeModule = struct {
|
const ProbeModule = struct {
|
||||||
pub const ParsedArgs = struct {};
|
pub const ParsedArgs = struct {};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: Meta = .{
|
||||||
pub const name: []const u8 = "probe";
|
.name = "probe",
|
||||||
pub const group: Group = .infra;
|
.group = .infra,
|
||||||
pub const synopsis: []const u8 = "test probe";
|
.synopsis = "test probe",
|
||||||
pub const help: []const u8 = "Usage: zfin probe\n";
|
.help = "Usage: zfin probe\n",
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
|
|
@ -453,11 +457,12 @@ test "printCommandHelp: writes meta.help and ensures trailing newline" {
|
||||||
test "printCommandHelp: appends newline when meta.help lacks one" {
|
test "printCommandHelp: appends newline when meta.help lacks one" {
|
||||||
const NoNewline = struct {
|
const NoNewline = struct {
|
||||||
pub const ParsedArgs = struct {};
|
pub const ParsedArgs = struct {};
|
||||||
pub const meta = struct {
|
pub const meta: Meta = .{
|
||||||
pub const name: []const u8 = "x";
|
.name = "x",
|
||||||
pub const group: Group = .infra;
|
.group = .infra,
|
||||||
pub const synopsis: []const u8 = "x";
|
.synopsis = "x",
|
||||||
pub const help: []const u8 = "no trailing newline";
|
.help = "no trailing newline",
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
@ -470,29 +475,6 @@ test "printCommandHelp: appends newline when meta.help lacks one" {
|
||||||
try testing.expectEqualStrings("no trailing newline\n", w.buffered());
|
try testing.expectEqualStrings("no trailing newline\n", w.buffered());
|
||||||
}
|
}
|
||||||
|
|
||||||
test "uppercasesFirstArg: defaults to false when meta omits the field" {
|
|
||||||
try testing.expect(!uppercasesFirstArg(ProbeModule));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "uppercasesFirstArg: honors meta.uppercase_first_arg when present" {
|
|
||||||
const M = struct {
|
|
||||||
pub const ParsedArgs = struct {};
|
|
||||||
pub const meta = struct {
|
|
||||||
pub const name: []const u8 = "m";
|
|
||||||
pub const group: Group = .symbol_lookup;
|
|
||||||
pub const synopsis: []const u8 = "m";
|
|
||||||
pub const help: []const u8 = "m";
|
|
||||||
pub const uppercase_first_arg: bool = true;
|
|
||||||
};
|
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
|
||||||
return .{};
|
|
||||||
}
|
|
||||||
pub fn run(_: *RunCtx, _: ParsedArgs) !void {}
|
|
||||||
};
|
|
||||||
comptime validateCommandModule(M);
|
|
||||||
try testing.expect(uppercasesFirstArg(M));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "normalizeFirstArg: empty args returns slice unchanged" {
|
test "normalizeFirstArg: empty args returns slice unchanged" {
|
||||||
const empty: []const []const u8 = &.{};
|
const empty: []const []const u8 = &.{};
|
||||||
const out = try normalizeFirstArg(testing.allocator, empty);
|
const out = try normalizeFirstArg(testing.allocator, empty);
|
||||||
|
|
@ -530,11 +512,12 @@ test "printGroupedUsage: groups in canonical order with headers + synopses" {
|
||||||
const FakeRegistry = struct {
|
const FakeRegistry = struct {
|
||||||
const ProbeA = struct {
|
const ProbeA = struct {
|
||||||
pub const ParsedArgs = struct {};
|
pub const ParsedArgs = struct {};
|
||||||
pub const meta = struct {
|
pub const meta: Meta = .{
|
||||||
pub const name: []const u8 = "alpha";
|
.name = "alpha",
|
||||||
pub const group: Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "alpha synopsis";
|
.synopsis = "alpha synopsis",
|
||||||
pub const help: []const u8 = "alpha help\n";
|
.help = "alpha help\n",
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
@ -543,11 +526,12 @@ test "printGroupedUsage: groups in canonical order with headers + synopses" {
|
||||||
};
|
};
|
||||||
const ProbeB = struct {
|
const ProbeB = struct {
|
||||||
pub const ParsedArgs = struct {};
|
pub const ParsedArgs = struct {};
|
||||||
pub const meta = struct {
|
pub const meta: Meta = .{
|
||||||
pub const name: []const u8 = "beta";
|
.name = "beta",
|
||||||
pub const group: Group = .infra;
|
.group = .infra,
|
||||||
pub const synopsis: []const u8 = "beta synopsis";
|
.synopsis = "beta synopsis",
|
||||||
pub const help: []const u8 = "beta help\n";
|
.help = "beta help\n",
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
@ -584,11 +568,12 @@ test "printGroupedUsage: omits empty groups" {
|
||||||
const FakeRegistry = struct {
|
const FakeRegistry = struct {
|
||||||
const Probe = struct {
|
const Probe = struct {
|
||||||
pub const ParsedArgs = struct {};
|
pub const ParsedArgs = struct {};
|
||||||
pub const meta = struct {
|
pub const meta: Meta = .{
|
||||||
pub const name: []const u8 = "only";
|
.name = "only",
|
||||||
pub const group: Group = .infra;
|
.group = .infra,
|
||||||
pub const synopsis: []const u8 = "only synopsis";
|
.synopsis = "only synopsis",
|
||||||
pub const help: []const u8 = "h\n";
|
.help = "h\n",
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
|
||||||
|
|
@ -55,12 +55,12 @@ pub const ParsedArgs = union(enum) {
|
||||||
portfolio: PortfolioOpts,
|
portfolio: PortfolioOpts,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "history";
|
.name = "history",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Price history (symbol) or portfolio value timeline";
|
.synopsis = "Price history (symbol) or portfolio value timeline",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage:
|
\\Usage:
|
||||||
\\ zfin history <SYMBOL> # last 30 days of candles
|
\\ zfin history <SYMBOL> # last 30 days of candles
|
||||||
\\ zfin history [flags] # portfolio-value timeline
|
\\ zfin history [flags] # portfolio-value timeline
|
||||||
|
|
@ -82,7 +82,7 @@ pub const meta = struct {
|
||||||
\\
|
\\
|
||||||
\\DATE accepts YYYY-MM-DD or relative shortcuts (1W/1M/1Q/1Y).
|
\\DATE accepts YYYY-MM-DD or relative shortcuts (1W/1M/1Q/1Y).
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ pub const ParsedArgs = struct {
|
||||||
cusip: []const u8,
|
cusip: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "lookup";
|
.name = "lookup",
|
||||||
pub const group: framework.Group = .hygiene;
|
.group = .hygiene,
|
||||||
pub const synopsis: []const u8 = "Look up a CUSIP to ticker via OpenFIGI";
|
.synopsis = "Look up a CUSIP to ticker via OpenFIGI",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin lookup <CUSIP>
|
\\Usage: zfin lookup <CUSIP>
|
||||||
\\
|
\\
|
||||||
\\Look up a CUSIP (9-char alphanumeric security identifier) to
|
\\Look up a CUSIP (9-char alphanumeric security identifier) to
|
||||||
|
|
@ -28,7 +28,7 @@ pub const meta = struct {
|
||||||
\\Examples:
|
\\Examples:
|
||||||
\\ zfin lookup 037833100 # → AAPL
|
\\ zfin lookup 037833100 # → AAPL
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,11 @@ pub const ParsedArgs = struct {
|
||||||
real: bool = false,
|
real: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "milestones";
|
.name = "milestones",
|
||||||
pub const group: framework.Group = .portfolio;
|
.group = .portfolio,
|
||||||
pub const synopsis: []const u8 = "Show portfolio threshold crossings (each $1M, doublings, etc.)";
|
.synopsis = "Show portfolio threshold crossings (each $1M, doublings, etc.)",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin milestones --step <expr> [--real]
|
\\Usage: zfin milestones --step <expr> [--real]
|
||||||
\\
|
\\
|
||||||
\\Find the dates the portfolio first reached each of a
|
\\Find the dates the portfolio first reached each of a
|
||||||
|
|
@ -60,7 +60,8 @@ pub const meta = struct {
|
||||||
\\Note: crossing dates are "first observed at," bounded by the
|
\\Note: crossing dates are "first observed at," bounded by the
|
||||||
\\source series cadence (typically weekly).
|
\\source series cadence (typically weekly).
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ pub const ParsedArgs = struct {
|
||||||
ntm: usize = 8,
|
ntm: usize = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "options";
|
.name = "options",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Show options chain (all expirations) for a symbol";
|
.synopsis = "Show options chain (all expirations) for a symbol",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin options <SYMBOL> [--ntm <N>]
|
\\Usage: zfin options <SYMBOL> [--ntm <N>]
|
||||||
\\
|
\\
|
||||||
\\Show the options chain (all expirations) for a symbol from
|
\\Show the options chain (all expirations) for a symbol from
|
||||||
|
|
@ -32,7 +32,7 @@ pub const meta = struct {
|
||||||
\\ zfin options SPY
|
\\ zfin options SPY
|
||||||
\\ zfin options AAPL --ntm 12
|
\\ zfin options AAPL --ntm 12
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ pub const ParsedArgs = struct {
|
||||||
symbol: []const u8,
|
symbol: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "perf";
|
.name = "perf",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Show 1y/3y/5y/10y trailing returns (Morningstar-style)";
|
.synopsis = "Show 1y/3y/5y/10y trailing returns (Morningstar-style)",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin perf <SYMBOL>
|
\\Usage: zfin perf <SYMBOL>
|
||||||
\\
|
\\
|
||||||
\\Show Morningstar-style trailing returns for a symbol — 1Y,
|
\\Show Morningstar-style trailing returns for a symbol — 1Y,
|
||||||
|
|
@ -32,7 +32,7 @@ pub const meta = struct {
|
||||||
\\ zfin perf VTI # total-market index, 30+ years of data
|
\\ zfin perf VTI # total-market index, 30+ years of data
|
||||||
\\ zfin perf NVDA # individual stock
|
\\ zfin perf NVDA # individual stock
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ pub const ParsedArgs = struct {
|
||||||
force_refresh: bool = false,
|
force_refresh: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "portfolio";
|
.name = "portfolio",
|
||||||
pub const group: framework.Group = .portfolio;
|
.group = .portfolio,
|
||||||
pub const synopsis: []const u8 = "Load and analyze the portfolio (positions + valuations + watchlist)";
|
.synopsis = "Load and analyze the portfolio (positions + valuations + watchlist)",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin portfolio [--refresh]
|
\\Usage: zfin portfolio [--refresh]
|
||||||
\\
|
\\
|
||||||
\\Load `portfolio.srf` (cwd → ZFIN_HOME), refresh per-symbol
|
\\Load `portfolio.srf` (cwd → ZFIN_HOME), refresh per-symbol
|
||||||
|
|
@ -33,7 +33,8 @@ pub const meta = struct {
|
||||||
\\ Useful when you suspect cached data is wrong
|
\\ Useful when you suspect cached data is wrong
|
||||||
\\ or after rotating providers.
|
\\ or after rotating providers.
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,11 @@ pub const CompareArgs = struct {
|
||||||
as_of: ?Date = null,
|
as_of: ?Date = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "projections";
|
.name = "projections",
|
||||||
pub const group: framework.Group = .portfolio;
|
.group = .portfolio,
|
||||||
pub const synopsis: []const u8 = "Retirement projections, benchmark comparison, percentile bands";
|
.synopsis = "Retirement projections, benchmark comparison, percentile bands",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin projections [opts]
|
\\Usage: zfin projections [opts]
|
||||||
\\
|
\\
|
||||||
\\Default mode: percentile-bands view of the portfolio's
|
\\Default mode: percentile-bands view of the portfolio's
|
||||||
|
|
@ -106,7 +106,8 @@ pub const meta = struct {
|
||||||
\\
|
\\
|
||||||
\\Date forms: YYYY-MM-DD or relative (1W/1M/1Q/1Y).
|
\\Date forms: YYYY-MM-DD or relative (1W/1M/1Q/1Y).
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ pub const ParsedArgs = struct {
|
||||||
symbol: []const u8,
|
symbol: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "quote";
|
.name = "quote",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Show latest quote with chart and 20-day history";
|
.synopsis = "Show latest quote with chart and 20-day history",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin quote <SYMBOL>
|
\\Usage: zfin quote <SYMBOL>
|
||||||
\\
|
\\
|
||||||
\\Show the latest real-time quote for a symbol (Yahoo / TwelveData)
|
\\Show the latest real-time quote for a symbol (Yahoo / TwelveData)
|
||||||
|
|
@ -29,7 +29,7 @@ pub const meta = struct {
|
||||||
\\ zfin quote AAPL
|
\\ zfin quote AAPL
|
||||||
\\ zfin quote spy # symbols are case-insensitive
|
\\ zfin quote spy # symbols are case-insensitive
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,11 @@ pub const ParsedArgs = struct {
|
||||||
as_of_override: ?Date = null,
|
as_of_override: ?Date = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "snapshot";
|
.name = "snapshot",
|
||||||
pub const group: framework.Group = .timeseries;
|
.group = .timeseries,
|
||||||
pub const synopsis: []const u8 = "Write a daily portfolio snapshot to history/";
|
.synopsis = "Write a daily portfolio snapshot to history/",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin snapshot [opts]
|
\\Usage: zfin snapshot [opts]
|
||||||
\\
|
\\
|
||||||
\\Compute a portfolio snapshot for today (or a historical date with
|
\\Compute a portfolio snapshot for today (or a historical date with
|
||||||
|
|
@ -94,7 +94,8 @@ pub const meta = struct {
|
||||||
\\If `history/<as_of_date>-portfolio.srf` already exists and `--force`
|
\\If `history/<as_of_date>-portfolio.srf` already exists and `--force`
|
||||||
\\wasn't passed, the run skips with a stderr message.
|
\\wasn't passed, the run skips with a stderr message.
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ pub const ParsedArgs = struct {
|
||||||
symbol: []const u8,
|
symbol: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "splits";
|
.name = "splits",
|
||||||
pub const group: framework.Group = .symbol_lookup;
|
.group = .symbol_lookup,
|
||||||
pub const synopsis: []const u8 = "Show split history for a symbol";
|
.synopsis = "Show split history for a symbol",
|
||||||
pub const uppercase_first_arg: bool = true;
|
.uppercase_first_arg = true,
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin splits <SYMBOL>
|
\\Usage: zfin splits <SYMBOL>
|
||||||
\\
|
\\
|
||||||
\\Show the split history for a symbol from Polygon.io. Cached
|
\\Show the split history for a symbol from Polygon.io. Cached
|
||||||
|
|
@ -24,7 +24,7 @@ pub const meta = struct {
|
||||||
\\ zfin splits AAPL # 4:1 (2020-08-31), 7:1 (2014-06-09)
|
\\ zfin splits AAPL # 4:1 (2020-08-31), 7:1 (2014-06-09)
|
||||||
\\ zfin splits NVDA # 10:1 (2024-06-10), 4:1 (2021-07-20), ...
|
\\ zfin splits NVDA # 10:1 (2024-06-10), 4:1 (2021-07-20), ...
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,11 @@ pub const ParsedArgs = struct {
|
||||||
verbose: bool = false,
|
verbose: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const meta = struct {
|
pub const meta: framework.Meta = .{
|
||||||
pub const name: []const u8 = "version";
|
.name = "version",
|
||||||
pub const group: framework.Group = .infra;
|
.group = .infra,
|
||||||
pub const synopsis: []const u8 = "Show zfin version and build info";
|
.synopsis = "Show zfin version and build info",
|
||||||
pub const help: []const u8 =
|
.help =
|
||||||
\\Usage: zfin version [-v|--verbose]
|
\\Usage: zfin version [-v|--verbose]
|
||||||
\\
|
\\
|
||||||
\\Print zfin's version + build date. With `--verbose`/`-v`, also
|
\\Print zfin's version + build date. With `--verbose`/`-v`, also
|
||||||
|
|
@ -31,7 +31,8 @@ pub const meta = struct {
|
||||||
\\resolved ZFIN_HOME, and cache directory — useful for bug
|
\\resolved ZFIN_HOME, and cache directory — useful for bug
|
||||||
\\reports.
|
\\reports.
|
||||||
\\
|
\\
|
||||||
;
|
,
|
||||||
|
.uppercase_first_arg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,7 @@ fn runCli(init: std.process.Init) !u8 {
|
||||||
.color = color,
|
.color = color,
|
||||||
.out = out,
|
.out = out,
|
||||||
};
|
};
|
||||||
const dispatched_args = if (comptime cmd_framework.uppercasesFirstArg(Module))
|
const dispatched_args = if (comptime Module.meta.uppercase_first_arg)
|
||||||
try cmd_framework.normalizeFirstArg(allocator, cmd_args)
|
try cmd_framework.normalizeFirstArg(allocator, cmd_args)
|
||||||
else
|
else
|
||||||
cmd_args;
|
cmd_args;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue