feat(xmtp_macro): #[derive(Retryable)] proc macro for RetryableError#3753
feat(xmtp_macro): #[derive(Retryable)] proc macro for RetryableError#3753insipx wants to merge 2 commits into
Conversation
|
Claude finished @insipx's task —— View job Code Review - June 10, 2026 17:13 UTCTodo List:
Review SummaryThe Strengths:
Code quality notes:
No actionable issues found beyond what was already addressed in previous review rounds. |
8f10674 to
5642d07
Compare
ApprovabilityVerdict: Needs human review This PR introduces a substantial new proc macro (~375 lines) with multiple parsing paths and code generation logic. While well-tested and compile-time only, new feature components of this scope warrant human review to verify the macro's design and generated code correctness. You can customize Macroscope's approvability policy. Learn more. |
247dede to
193d75b
Compare
|
CI analysis of the
|
193d75b to
4701e13
Compare
Dismissing prior approval to re-evaluate 4701e13
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3753 +/- ##
==========================================
+ Coverage 84.44% 84.48% +0.03%
==========================================
Files 409 409
Lines 59606 60082 +476
==========================================
+ Hits 50335 50761 +426
- Misses 9271 9321 +50 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Derive that generates `impl xmtp_common::RetryableError`, mirroring the existing ErrorCode derive. Satisfies the long-standing TODO in retry.rs. Variant rules (first match wins): #[retry(when = EXPR)], #[retry(true|false)], bare #[retry], #[retry(inherit)], #[from] auto-forward, else the container #[retry(default = ...)] baseline (false by default). Supports structs. Tested with 11 behavior tests (in xmtp_common) covering every rule, plus 5 trybuild UI tests pinning the spanned compile_error paths (conflicting keys, default-on-variant, inherit arity, unknown key, union). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4701e13 to
8a5996e
Compare
…able)]
Replaces ~47 hand-written `impl RetryableError` blocks across 8 crates (+xnet)
with the derive. Net -456 lines. Behavior preserved per-variant:
- forwarding arms -> #[retry(inherit)]
- hardcoded trues -> #[retry(true)]; all-true / inverted enums -> #[retry(default = true)]
- hardcoded falses -> unannotated (baseline)
- multi-field forwards & collection aggregation -> #[retry(when = ...)]
- ClientError::Generic -> #[retry(when = this.contains("database is locked"))]
- GroupMessageProcessingError's nested match collapsed via a new derive on
ProcessMessageWithAppDataError (gains a RetryableError<Mls> where-bound)
Equivalence was verified by an exhaustive variant-by-variant audit against the
deleted impls (~350 variants total, zero mismatches). Per-enum golden tests were
intentionally NOT kept: the macro's own test suite (18 behavior tests + 11
trybuild fixtures in #3753) is the behavioral contract, and annotations are the
source of truth thereafter — same trust model as the hand-written impls had.
Still hand-written by design: the 22 specialized RetryableError<Mls> impls for
foreign openmls/diesel types, GroupAppDataError (instantiation-specific),
ReceiveErrors/SyncSummary one-line aggregators, and test mocks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…able)]
Replaces ~47 hand-written `impl RetryableError` blocks across 8 crates (+xnet)
with the derive. Net -456 lines. Behavior preserved per-variant:
- forwarding arms -> #[retry(inherit)]
- hardcoded trues -> #[retry(true)]; all-true / inverted enums -> #[retry(default = true)]
- hardcoded falses -> unannotated (baseline)
- multi-field forwards & collection aggregation -> #[retry(when = ...)]
- ClientError::Generic -> #[retry(when = this.contains("database is locked"))]
- GroupMessageProcessingError's nested match collapsed via a new derive on
ProcessMessageWithAppDataError (gains a RetryableError<Mls> where-bound)
Equivalence was verified by an exhaustive variant-by-variant audit against the
deleted impls (~350 variants total, zero mismatches). Per-enum golden tests were
intentionally NOT kept: the macro's own test suite (18 behavior tests + 11
trybuild fixtures in #3753) is the behavioral contract, and annotations are the
source of truth thereafter — same trust model as the hand-written impls had.
Still hand-written by design: the 22 specialized RetryableError<Mls> impls for
foreign openmls/diesel types, GroupAppDataError (instantiation-specific),
ReceiveErrors/SyncSummary one-line aggregators, and test mocks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Adds a
#[derive(Retryable)]proc macro that generatesimpl xmtp_common::RetryableError, replacing the hand-written per-enum boilerplate (satisfies the long-standing TODO inretry.rs). Mirrors the existing#[derive(ErrorCode)]macro: same crate (xmtp_macro), same re-export path (xmtp_common::Retryable), same spanned-error discipline.Variant rules (first match wins)
#[retry(when = EXPR)]EXPRwith referenced fields bound (this/ names /this0..)#[retry(false)]/#[retry(true)]/ bare#[retry]#[retry(inherit)]is_retryable()#[from]carries no retry semantics — forwarding is always explicit via#[retry(inherit)]. A workspace census found 98#[from]variants whose hand-written behavior was a hardcoded true/false (foreign inner types like prost/http/derive_builder) vs only 33 that forward; auto-forwarding (tried first) forced a noisy#[retry(false)]on the 3×-more-common case and made unannotated variants non-local to read. With explicitinherit, an unannotated variant is always the baseline.Other properties:
whenbinds only the fields the expression references (token scan) — nounused_variablesunder-D warnings.split_for_impl); empty enums emitmatch *self {}; structs supported (whenoverself).#[retry(true)]on an enum) is a spanned compile error, not a silent no-op.Testing
xmtp_common::retry::derive_testscovering every rule incl. generics, subset-when, empty enum, bare-#[from]-takes-baseline,default = trueover#[from].compile_errorpath (crates/xmtp_macro/tests/ui/retryable/).cargo clippy --locked --all-features --all-targets -Dwarningsclean on touched crates; fmt clean; hakari no-op.Migration of existing hand-written impls is stacked on top: #3754.
🤖 Generated with Claude Code
Note
Add
#[derive(Retryable)]proc macro forRetryableErrorinxmtp_macroRetryablederive macro in xmtp_macro/src/retryable.rs that generatesxmtp_common::RetryableErrorimplementations viais_retryable(&self).#[retry]attributes:#[retry(true|false)],#[retry(inherit)](forwards to a single field),#[retry(when = expr)], and#[retry(default = ...)]on enum containers.Retryablefromxmtp_commonso consumers can import it asxmtp_common::Retryable.trybuildcovering invalid attribute combinations (conflicting keys, wrong placement, unknown keys, arity errors, union types).tests/uiso trybuild.stderrsnapshots are available in hermetic Nix builds.Macroscope summarized 8a5996e.