Skip to content

refactor(#33)!: replace terrors::OneOf with domain error enums#74

Merged
joeldsouzax merged 16 commits into
mainfrom
feat/33-remove-oneof
Jul 4, 2026
Merged

refactor(#33)!: replace terrors::OneOf with domain error enums#74
joeldsouzax merged 16 commits into
mainfrom
feat/33-remove-oneof

Conversation

@joeldsouzax

@joeldsouzax joeldsouzax commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #33 (P2.3 · Error ergonomics). Removes the terrors::OneOf<(...)> error-union layer across the crate in favour of purpose-built thiserror enums, and drops the terrors dependency. The unions were only ever 1–2 variants, so OneOf cost consumers a runtime downcast (.take:: / .narrow::) for no matchability benefit.

Breaking (MINOR under 0.x) — return types change on public APIs; see CHANGELOG.

What changed

  • New MatterBuildError { Parsing(#[from] ParsingError), Validation(#[from] ValidationError) } — matter builder (from_qualified_base64, from_qualified_base2, build) now returns this instead of OneOf<(ParsingError, ValidationError)>.
  • New VerificationError { Signature(#[from] SignatureError), CodeMismatch(#[from] CodeMismatchError) }crypto::verify return type.
  • Indexer builder — single-element OneOf<(E,)> collapsed to the bare IndexerParseError / IndexerValidationError.
  • serder bug fixmap_qb64_error previously string-jammed a ParsingError into ValidationError::UnknownMatterCode(err.to_string()) (a parse failure mislabeled as validation, structured error erased). Now routes each domain correctly via a new SerderError::UnparseablePrimitive { field, source: ParsingError } variant. TDD regression test pins both routing arms.
  • stream — migrated stream::parse off terrors; removed an unreachable!() panic (banned in production) in favour of a total match.
  • Dependencyterrors removed from Cargo.toml, Cargo.lock, and fuzz/Cargo.lock.
  • Docs — CHANGELOG (Changed/Fixed) and CLAUDE.md error-handling convention rewritten to "thiserror enums for unions; don't reintroduce terrors".

#[from] keeps ? propagation working, so call sites were untouched. No #[non_exhaustive] — pre-1.0, a new variant is an intentional MINOR bump, and exhaustive matching gives tag-pinning consumers a compile error exactly when they upgrade into a new failure mode.

Consumer migration

Replace err.take::<T>() / err.narrow::<T, _>() with a normal match on the new enums (or the bare indexer error types).

Test plan

  • nix flake check green (clippy god-level, fmt, taplo, audit, deny, nextest across feature combos, doctest, wasm, no_std, typos, fuzz-replay) — run independently, EXIT=0
  • New enums: variant-reachability + Display-transparency tests (both variants each)
  • Migrated call sites: matter/indexer/crypto tests moved to enum matching
  • serder bug fix: regression test fails-red on the old behavior, both routing arms pinned
  • git grep terrors|OneOf clean across code/config

🤖 Generated with Claude Code


Added after review (fuzz-found panic fix)

While fixing the deep-fuzz CI workflow (#75), the now-working fuzzer immediately caught a pre-existing panic in this exact file: MatterBuilder::from_qualified_base64 panicked (range end index N out of range for slice of length 0) on malformed qb64 whose decoded buffer is shorter than the code's declared lead size (input 5BAA). Fixed here (same file/error path) by bounds-checking both lead-byte slices via .get(), returning MatterBuildError::Validation(ValidationError::StructuralIntegrityError). Includes a TDD regression test + a committed fuzz corpus seed. This is a panic-on-untrusted-input / DoS fix (CLAUDE.md mandatory rule).

@codspeed-hq

codspeed-hq Bot commented Jul 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 21 untouched benchmarks


Comparing feat/33-remove-oneof (c44c8db) with main (98d5139)

Open in CodSpeed

joeldsouzax and others added 16 commits July 4, 2026 20:16
Replace OneOf<(..)> with per-call-site thiserror enums (MatterBuildError,
VerificationError), collapse single-element indexer OneOfs to bare types,
fix serder ParsingError->ValidationError string-jamming bug, drop terrors dep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… a stringified ValidationError

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…orts

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… unions, no terrors)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…flake check gate

The new SerderError::UnparseablePrimitive variant uses the valid English
spelling 'unparseable', which the typos default dictionary corrects to
'unparsable'. Allowlist the deliberate spelling in _typos.toml and fix an
unrelated 'mis-routed' -> 'misrouted' comment typo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
deep-fuzz's matter_from_qb64 target found that from_qualified_base64 panics with
'range end index N out of range for slice of length 0' on malformed qb64 whose
decoded buffer is shorter than the code's declared lead size (crash input: 5BAA).
Both lead-byte slices (ps!=0 and ps==0 branches) indexed buf without a bounds check.

Replace the panicking index with .get(), returning
MatterBuildError::Validation(ValidationError::StructuralIntegrityError) when the
buffer is too short. Parsing untrusted bytes must never panic (CLAUDE.md).

Adds a regression unit test and commits the crash input as a fuzz corpus seed
(guarded by the cesr-fuzz-replay gate). Note: the existing qb64 never-panics
proptest did not generate this structural case — fuzzing complements proptests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joeldsouzax joeldsouzax force-pushed the feat/33-remove-oneof branch from 78f097d to c44c8db Compare July 4, 2026 18:16
@joeldsouzax joeldsouzax enabled auto-merge (squash) July 4, 2026 18:21
@joeldsouzax joeldsouzax merged commit f0e77b5 into main Jul 4, 2026
19 checks passed
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.

P2.3 · Error ergonomics review

1 participant