user-errors management...any non-user error panics like before
This commit is contained in:
parent
987f472226
commit
f50928f1c5
24 changed files with 105 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,3 +9,4 @@ coverage/
|
||||||
# `ZFIN_HOME=examples/<name> zfin projections` immediately.
|
# `ZFIN_HOME=examples/<name> zfin projections` immediately.
|
||||||
!examples/**/*.srf
|
!examples/**/*.srf
|
||||||
scripts/
|
scripts/
|
||||||
|
.tmp/
|
||||||
|
|
|
||||||
24
AGENTS.md
24
AGENTS.md
|
|
@ -308,6 +308,30 @@ Ask the user instead.**
|
||||||
stop. You do not commit. You do not prepare a commit. You hand off the
|
stop. You do not commit. You do not prepare a commit. You hand off the
|
||||||
working tree and wait.
|
working tree and wait.
|
||||||
|
|
||||||
|
### NEVER write to `/tmp`. Use `$(cwd)/.tmp` instead.
|
||||||
|
|
||||||
|
- **`/tmp` is off-limits for any temporary file the assistant creates.**
|
||||||
|
Not for scratch scripts, not for redirected stdout, not for `cp`
|
||||||
|
backups, not for "just for a second" experiments. The user's workflow
|
||||||
|
treats `/tmp` as their own space; assistant-created files there
|
||||||
|
pollute it across sessions and hide from `git status`.
|
||||||
|
- Use `$(cwd)/.tmp/` (a `.tmp` directory at the repository root) for
|
||||||
|
every transient file. `mkdir -p .tmp` if it doesn't exist;
|
||||||
|
`.gitignore` it (or rely on a project-level `.gitignore` rule that
|
||||||
|
catches it). Files in `.tmp` show up in `ls` next to the work,
|
||||||
|
surface in `git status` if the ignore rule is wrong, and get
|
||||||
|
cleaned up at the end of the task with a single `rm -rf .tmp/*`.
|
||||||
|
- This applies to:
|
||||||
|
- Bash redirections: `cmd > .tmp/out` not `cmd > /tmp/out`.
|
||||||
|
- Test fixture scratch files.
|
||||||
|
- `cp file /tmp/file.bak` for "I'll restore in a sec" backups —
|
||||||
|
use `cp file .tmp/file.bak` instead.
|
||||||
|
- Heredoc-created throwaway scripts to test a Zig snippet
|
||||||
|
(`zig run /tmp/foo.zig` → `zig run .tmp/foo.zig`).
|
||||||
|
- The ONE exception: temp files created by the system / by other
|
||||||
|
tools (mise installations, zig's own cache symlinks, etc.) that
|
||||||
|
legitimately use `/tmp`. Don't try to redirect those.
|
||||||
|
|
||||||
### Documentation-file conventions
|
### Documentation-file conventions
|
||||||
|
|
||||||
- **`TODO.md` holds only what's still open.** Git already tracks what was
|
- **`TODO.md` holds only what's still open.** Git already tracks what was
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{UnexpectedArg},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -2278,6 +2278,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ UnexpectedArg, EmptyFile, NoAccountsFound, UnexpectedHeader },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(_: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ MissingSubcommand, UnexpectedArg, UnknownSubcommand },
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Data types to show in the stats table (skip candles_meta and meta — internal bookkeeping).
|
/// Data types to show in the stats table (skip candles_meta and meta — internal bookkeeping).
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ InvalidDate, MissingDateArg, PortfolioLoadFailed, SameDate, SnapshotNotFound, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ DuplicateEndpoint, InvalidArg, MissingOpenDate, PrepareFailed, ResolveFailed, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ zfin divs T # historical AT&T dividends
|
\\ zfin divs T # historical AT&T dividends
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingSymbol, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ zfin earnings AAPL | head -8 # last two years of quarters
|
\\ zfin earnings AAPL | head -8 # last two years of quarters
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingSymbol, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ MissingArg, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ zfin etf TQQQ # leveraged (warning surfaced)
|
\\ zfin etf TQQQ # leveraged (warning surfaced)
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingSymbol, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
//! .synopsis = "Show 1y/3y/5y/10y trailing returns",
|
//! .synopsis = "Show 1y/3y/5y/10y trailing returns",
|
||||||
//! .help = "...", // multi-line; rendered by `--help`
|
//! .help = "...", // multi-line; rendered by `--help`
|
||||||
//! .uppercase_first_arg = true,
|
//! .uppercase_first_arg = true,
|
||||||
|
//! .user_errors = error{ MissingSymbol, UnexpectedArg },
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs;
|
//! pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs;
|
||||||
|
|
@ -125,8 +126,47 @@ pub const Meta = struct {
|
||||||
/// optional opt-in; the `false` answer is just as load-bearing
|
/// optional opt-in; the `false` answer is just as load-bearing
|
||||||
/// as `true`.
|
/// as `true`.
|
||||||
uppercase_first_arg: bool,
|
uppercase_first_arg: bool,
|
||||||
|
|
||||||
|
/// Error set listing the command's user-level errors — errors
|
||||||
|
/// where the command has already printed a useful message to
|
||||||
|
/// stderr and the dispatcher should just return exit 1 silently.
|
||||||
|
/// Anything NOT in this set propagates to Zig's panic handler
|
||||||
|
/// with a stack trace, signaling a genuine bug worth investigating.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
/// - `error.MissingSymbol` (parseArgs printed "requires a symbol
|
||||||
|
/// argument") → user-level.
|
||||||
|
/// - `error.SnapshotNotFound` (run printed "No snapshot at or
|
||||||
|
/// before X") → user-level.
|
||||||
|
/// - `error.OutOfMemory`, `error.Unexpected*` → not user-level;
|
||||||
|
/// these should crash visibly so they don't get swallowed.
|
||||||
|
///
|
||||||
|
/// No default — every command author must enumerate the errors
|
||||||
|
/// their `parseArgs` and `run` deliberately return as user
|
||||||
|
/// signals. Commands with no user-level errors (`version`)
|
||||||
|
/// declare `error{}`. Adding a new `return error.X` to a
|
||||||
|
/// command means you also add `X` here if it's user-level —
|
||||||
|
/// the explicit list IS the contract.
|
||||||
|
user_errors: type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Returns true if `err` is a member of the comptime-known
|
||||||
|
/// `UserErrors` error set. Used by the framework dispatcher to
|
||||||
|
/// decide whether a `Module.run` error is a user-level signal
|
||||||
|
/// (return exit 1 silently) or an internal bug (propagate to
|
||||||
|
/// Zig's panic handler).
|
||||||
|
///
|
||||||
|
/// Implemented as a comptime-unrolled equality chain over the
|
||||||
|
/// error set's variants. Cost is one comparison per variant; the
|
||||||
|
/// largest user-error set today is on the order of 10 variants,
|
||||||
|
/// so this is cheap.
|
||||||
|
pub fn isUserError(comptime UserErrors: type, err: anyerror) bool {
|
||||||
|
inline for (@typeInfo(UserErrors).error_set orelse return false) |variant| {
|
||||||
|
if (err == @field(anyerror, variant.name)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Refresh policy ────────────────────────────────────────────
|
// ── Refresh policy ────────────────────────────────────────────
|
||||||
|
|
||||||
/// Cache-freshness policy for the invocation. Set via the global
|
/// Cache-freshness policy for the invocation. Set via the global
|
||||||
|
|
@ -432,6 +472,7 @@ const ProbeModule = struct {
|
||||||
.synopsis = "test probe",
|
.synopsis = "test probe",
|
||||||
.help = "Usage: zfin probe\n",
|
.help = "Usage: zfin probe\n",
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
|
|
@ -481,6 +522,7 @@ test "printCommandHelp: appends newline when meta.help lacks one" {
|
||||||
.synopsis = "x",
|
.synopsis = "x",
|
||||||
.help = "no trailing newline",
|
.help = "no trailing newline",
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{},
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
@ -536,6 +578,7 @@ test "printGroupedUsage: groups in canonical order with headers + synopses" {
|
||||||
.synopsis = "alpha synopsis",
|
.synopsis = "alpha synopsis",
|
||||||
.help = "alpha help\n",
|
.help = "alpha help\n",
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{},
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
@ -550,6 +593,7 @@ test "printGroupedUsage: groups in canonical order with headers + synopses" {
|
||||||
.synopsis = "beta synopsis",
|
.synopsis = "beta synopsis",
|
||||||
.help = "beta help\n",
|
.help = "beta help\n",
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{},
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
@ -592,6 +636,7 @@ test "printGroupedUsage: omits empty groups" {
|
||||||
.synopsis = "only synopsis",
|
.synopsis = "only synopsis",
|
||||||
.help = "h\n",
|
.help = "h\n",
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{},
|
||||||
};
|
};
|
||||||
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(_: *RunCtx, _: []const []const u8) !ParsedArgs {
|
||||||
return .{};
|
return .{};
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\DATE accepts YYYY-MM-DD or relative shortcuts (1W/1M/1Q/1Y).
|
\\DATE accepts YYYY-MM-DD or relative shortcuts (1W/1M/1Q/1Y).
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ UnexpectedArg, MissingFlagValue, InvalidFlagValue, UnknownMetric, UnknownResolution },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Error = error{
|
pub const Error = error{
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ zfin lookup 037833100 # → AAPL
|
\\ zfin lookup 037833100 # → AAPL
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingCusip, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ InvalidStep, MissingStep, NoData, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RunError = error{
|
pub const RunError = error{
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ zfin options AAPL --ntm 12
|
\\ zfin options AAPL --ntm 12
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingSymbol, UnexpectedArg, MissingFlagValue, InvalidFlagValue },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ zfin perf NVDA # individual stock
|
\\ zfin perf NVDA # individual stock
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingSymbol, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{UnexpectedArg},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ UnexpectedArg, MissingFlagValue, InvalidFlagValue, MutuallyExclusive, NoSnapshot, PortfolioLoadFailed },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ zfin quote spy # symbols are case-insensitive
|
\\ zfin quote spy # symbols are case-insensitive
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingSymbol, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Quote data extracted from the real-time API (or synthesized from candles).
|
/// Quote data extracted from the real-time API (or synthesized from candles).
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{ UnexpectedArg, BadMetadata, NoCommitBeforeDate, NoMetadata, PathMissingInRev, WriteFailed },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\ 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), ...
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
|
.user_errors = error{ MissingSymbol, UnexpectedArg },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
pub fn parseArgs(ctx: *framework.RunCtx, cmd_args: []const []const u8) !ParsedArgs {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub const meta: framework.Meta = .{
|
||||||
\\
|
\\
|
||||||
,
|
,
|
||||||
.uppercase_first_arg = false,
|
.uppercase_first_arg = false,
|
||||||
|
.user_errors = error{UnexpectedArg},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parse `--verbose`/`-v`. Unknown args produce an error on stderr.
|
/// Parse `--verbose`/`-v`. Unknown args produce an error on stderr.
|
||||||
|
|
|
||||||
17
src/main.zig
17
src/main.zig
|
|
@ -376,8 +376,21 @@ fn runCli(init: std.process.Init) !u8 {
|
||||||
try cmd_framework.normalizeFirstArg(allocator, cmd_args)
|
try cmd_framework.normalizeFirstArg(allocator, cmd_args)
|
||||||
else
|
else
|
||||||
cmd_args;
|
cmd_args;
|
||||||
const parsed = Module.parseArgs(&ctx, dispatched_args) catch return 1;
|
const parsed = Module.parseArgs(&ctx, dispatched_args) catch |err| {
|
||||||
try Module.run(&ctx, parsed);
|
// parseArgs errors: if the command declared this
|
||||||
|
// error as user-level, exit 1 silently (the
|
||||||
|
// command already printed a stderr message).
|
||||||
|
// Otherwise propagate so genuine bugs (OOM, etc.)
|
||||||
|
// surface with a stack trace.
|
||||||
|
if (cmd_framework.isUserError(Module.meta.user_errors, err)) return 1;
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
Module.run(&ctx, parsed) catch |err| {
|
||||||
|
// Same treatment for run errors: user-level errors
|
||||||
|
// become exit 1; everything else propagates.
|
||||||
|
if (cmd_framework.isUserError(Module.meta.user_errors, err)) return 1;
|
||||||
|
return err;
|
||||||
|
};
|
||||||
try out.flush();
|
try out.flush();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue