Skip to content

refactor(sdk): add Fault constructors, reject zero interval - #335

Merged
mfw78 merged 2 commits into
mainfrom
fix/fault-constructors-and-zero-interval
Aug 21, 2026
Merged

refactor(sdk): add Fault constructors, reject zero interval#335
mfw78 merged 2 commits into
mainfrom
fix/fault-constructors-and-zero-interval

Conversation

@mfw78

@mfw78 mfw78 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

What

Adds inherent constructors on Fault in crates/nexum-sdk/src/host.rs, one per free-text variant: Fault::unsupported, unavailable, denied, invalid_input, internal, each taking impl Into<String>. RateLimited and Timeout are unit variants and get none. The three module-local lifters are deleted and their call sites moved onto the constructors: balance-tracker's invalid_input, http-probe's invalid_input and internal, and price-alert's invalid. http-probe's inline Fault::InvalidInput(detail) in the FetchError arm goes through the constructor too, for consistency; the arm itself is unchanged.

price-alert no longer clamps every_n_blocks = 0 up to 1. It now refuses with every_n_blocks must be >= 1, matching http-probe, and a new test pins the refusal.

Why

Closes #140. Every example module reaches the SDK config conversion through config::get_required(...)?, so the only thing the local helpers still did was lift a free-text detail, and three copies had drifted into two names and two signatures. Per the issue's settled decisions the lifter becomes inherent constructors on Fault, where the type lives, discoverable by autocomplete and idiomatic; all five free-text variants get one so the rule is uniform rather than arbitrary.

A zero interval is rejected in both throttling modules rather than clamped. It fails loudly at init while the operator is watching, instead of producing a module that looks configured and quietly does the maximum work. Removing the clamp in favour of nothing was never on the table: is_multiple_of(0) is true only for block 0, so an unclamped zero would silently mean "never fire". Blast radius is bounded to the two example component.toml files, both of which set 1.

Testing

  • cargo fmt --all, cargo clippy --workspace --all-targets --all-features --locked -- -D warnings, clean
  • just build, then cargo nextest run --workspace --all-features --locked, 858 passed, 0 skipped
  • ./scripts/workspace-deps-lint.sh and cargo machete, both clean
  • New: parse_config_rejects_zero_every_n_blocks in price-alert. http-probe's parse_config_rejects_missing_urls_and_zero_throttle is untouched.

AI Assistance

Claude Opus 5 used for the constructor rollout, the call site migration and the price-alert refusal test.

mfw78 added 2 commits August 21, 2026 10:58
The three example modules each carried a local one-line lifter into
Fault::InvalidInput, drifted into two names and two signatures. Replace
them with inherent constructors on Fault itself, one per free-text
variant, each taking impl Into<String>. RateLimited and Timeout are unit
variants and get none.

price-alert clamped every_n_blocks = 0 up to 1, silently firing on every
block. Reject it instead, matching http-probe, with a test pinning the
refusal. Dropping the clamp without the refusal is not an option:
is_multiple_of(0) is true only for block 0, so a zero would silently
mean "never fire".

AI Assistance: Claude Opus 5 used for the constructor rollout, the call
site migration and the price-alert refusal test.
The value was clamped silently until this branch made it a boot refusal,
so the config comment now has something an operator needs.

AI Assistance: Claude Opus 5 used for the change.
@mfw78
mfw78 merged commit 6cb4e21 into main Aug 21, 2026
6 checks passed
@mfw78
mfw78 deleted the fix/fault-constructors-and-zero-interval branch August 21, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

modules: reconcile the three example fault helpers, and settle every_n_blocks = 0

1 participant