refactor(events): seal notification, sync event payloads (non_exhaustive + bon builder) - #1003
Conversation
Second tranche of the pre-1.0 event-payload API freeze (after ServerAck in #1002). Applies the same seal to the notification and app-state-sync payload family so their fields can grow without breaking consumers: #[non_exhaustive] + #[derive(bon::Builder)] 22 structs: the app-state sync mutations (ContactUpdate, PinUpdate, MuteUpdate, ArchiveUpdate, StarUpdate, MarkChatAsReadUpdate, DeleteChatUpdate, ClearChatUpdate, UserStatusMuteUpdate, DeleteMessageForMeUpdate, LabelEditUpdate, LabelAssociationUpdate) plus the contact/profile/presence/group notification payloads (ChatPresence- Update, PresenceUpdate, PictureUpdate, UserAboutUpdate, ContactUpdated, ContactNumberChanged, ContactSyncRequested, GroupUpdate, PushNameUpdate, SelfPushNameUpdated). Their 22 cross-crate construction sites move from struct literals to the generated builder, passing Option attributes through maybe_* setters. Behavior-preserving: same values, same order, no logic change. The seal is enforced by the compiler (E0639 on any missed literal), and bon's typestate build() rejects a missing required field at compile time. Remaining: unit-struct markers, the inline Event variants, the message/ newsletter payloads, and a trybuild compile-fail guard.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📝 WalkthroughWalkthroughListen, this diff is the equivalent of upgrading our infrastructure so it scales cleanly — that's what we do at Meta. It converts numerous event payload structs (SelfPushNameUpdated, ChatPresenceUpdate, GroupUpdate, and many more) in wacore/src/types/events.rs to derive bon::Builder and marks them #[non_exhaustive], then updates all corresponding call sites across client, feature, and handler modules to construct these events via the fluent builder API instead of struct literals. No behavioral change to dispatched values. ChangesBuilder-based event construction
Estimated code review effort: 2 (Simple) | ~15 minutes Possibly related PRs
Suggested labels: We need this to work right — it's a clean, consistent refactor across the board. Ship it. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@wacore/src/types/events.rs`:
- Around line 442-443: The event payload structs in this module are being marked
non_exhaustive against the repo’s stated pre-1.0 policy, so remove the
#[non_exhaustive] attribute from the affected event types in the events
definitions and keep them directly constructible/readable. If the intent is to
change the policy instead, update the relevant coding guideline and add an
explicit rationale in the same change; otherwise, leave the structs such as the
ones around the Event payload definitions constructible without
#[non_exhaustive].
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 4c2aa440-17be-4070-9125-7431b1e52741
📒 Files selected for processing (9)
src/client/accessors.rssrc/client/app_state.rssrc/client/messaging.rssrc/features/chat_actions.rssrc/features/labels.rssrc/handlers/notification/groups.rssrc/handlers/notification/profile.rssrc/handlers/presence.rswacore/src/types/events.rs
|
| Filename | Overview |
|---|---|
| wacore/src/types/events.rs | Adds #[non_exhaustive] + bon::Builder to 22 event payload structs; no field types or field names changed, Option fields are correctly annotated. |
| src/features/chat_actions.rs | Migrates 10 construction sites to builders; all Option fields (e.g. participant_jid) correctly use maybe_* setters. |
| src/handlers/notification/profile.rs | Migrates 5 construction sites; ContactSyncRequested.after (derived as Option<DateTime<Utc>>) correctly routed through maybe_after. |
| src/handlers/notification/groups.rs | Migrates GroupUpdate dispatch; participant and participant_pn (both Option<Jid>) correctly use maybe_* setters. |
| src/handlers/presence.rs | Migrates PresenceUpdate dispatch; last_seen: Option<DateTime<Utc>> correctly uses maybe_last_seen. |
| src/features/labels.rs | Migrates LabelEditUpdate and LabelAssociationUpdate dispatch sites cleanly; no optional fields involved. |
| src/client/accessors.rs | Migrates SelfPushNameUpdated dispatch; keeps the existing fully-qualified path style. |
| src/client/app_state.rs | Migrates second SelfPushNameUpdated dispatch site in the app-state push-name handler. |
| src/client/messaging.rs | Migrates ChatPresenceUpdate dispatch; all three required fields set explicitly, no optional fields. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
subgraph wacore["wacore crate"]
E["events.rs — 22 structs sealed\n#[non_exhaustive] + bon::Builder"]
end
subgraph whatsapp_rust["whatsapp-rust crate"]
CA["chat_actions.rs — 10 sites"]
LB["labels.rs — 2 sites"]
AX["accessors.rs — 1 site"]
AS["app_state.rs — 1 site"]
MS["messaging.rs — 1 site"]
GR["notification/groups.rs — 1 site"]
PR["notification/profile.rs — 5 sites"]
PS["presence.rs — 1 site"]
end
E --> CA
E --> LB
E --> AX
E --> AS
E --> MS
E --> GR
E --> PR
E --> PS
CA --> BUS[CoreEventBus]
LB --> BUS
AX --> BUS
AS --> BUS
MS --> BUS
GR --> BUS
PR --> BUS
PS --> BUS
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
subgraph wacore["wacore crate"]
E["events.rs — 22 structs sealed\n#[non_exhaustive] + bon::Builder"]
end
subgraph whatsapp_rust["whatsapp-rust crate"]
CA["chat_actions.rs — 10 sites"]
LB["labels.rs — 2 sites"]
AX["accessors.rs — 1 site"]
AS["app_state.rs — 1 site"]
MS["messaging.rs — 1 site"]
GR["notification/groups.rs — 1 site"]
PR["notification/profile.rs — 5 sites"]
PS["presence.rs — 1 site"]
end
E --> CA
E --> LB
E --> AX
E --> AS
E --> MS
E --> GR
E --> PR
E --> PS
CA --> BUS[CoreEventBus]
LB --> BUS
AX --> BUS
AS --> BUS
MS --> BUS
GR --> BUS
PR --> BUS
PS --> BUS
Reviews (1): Last reviewed commit: "refactor(events): seal notification & sy..." | Re-trigger Greptile
There was a problem hiding this comment.
No issues found across 9 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Auto-approved: Mechanical migration from struct literals to bon::Builder for 22 event payload structs with #[non_exhaustive]. No logic changes, all tests pass.
Re-trigger cubic
📦 Binary size report
.text per crate
Top movers (cargo-bloat attribution)
Baseline: |
…pp-rust#1003) Updates concepts/events.mdx for oxidezap/whatsapp-rust#1003, which extends the pre-1.0 event-payload seal (#[non_exhaustive] + bon::Builder, started on ServerAck in #1002) to the 22 notification, presence, contact, group, and app-state-sync mutation payload structs. - Reworded the general payload-stability note to list every struct sealed so far instead of only calling out ServerAck. - Added #[non_exhaustive] and the bon::Builder derive to all 22 affected struct code blocks (SelfPushNameUpdated, ChatPresenceUpdate, PresenceUpdate, PictureUpdate, UserAboutUpdate, ContactUpdated, ContactNumberChanged, ContactSyncRequested, GroupUpdate, PushNameUpdate, ContactUpdate, PinUpdate, MuteUpdate, ArchiveUpdate, StarUpdate, MarkChatAsReadUpdate, DeleteChatUpdate, ClearChatUpdate, UserStatusMuteUpdate, DeleteMessageForMeUpdate, LabelEditUpdate, LabelAssociationUpdate). Construction sites move to `Type::builder()...build()`, but this is internal to the client (it dispatches these events) — no consumer-facing example in this doc constructs a payload, so only the struct definitions and the stability notes needed updating.
What
Second tranche of the pre-1.0 event-payload API freeze, after
ServerAckin #1002. It applies the same seal to the notification and app-state-sync payload family, so these payloads can gain fields later without breaking consumers:#[non_exhaustive]+#[derive(bon::Builder)]Structs sealed (22)
ContactUpdate,PinUpdate,MuteUpdate,ArchiveUpdate,StarUpdate,MarkChatAsReadUpdate,DeleteChatUpdate,ClearChatUpdate,UserStatusMuteUpdate,DeleteMessageForMeUpdate,LabelEditUpdate,LabelAssociationUpdate.ChatPresenceUpdate,PresenceUpdate,PictureUpdate,UserAboutUpdate,ContactUpdated,ContactNumberChanged,ContactSyncRequested,GroupUpdate,PushNameUpdate,SelfPushNameUpdated.Construction sites
The 22 cross-crate construction sites move from struct literals to the generated builder, with
Optionattributes passed throughmaybe_*setters. This is behavior-preserving: same values, same order, no logic change. Spread acrosschat_actions.rs(10),notification/profile.rs(5),labels.rs(2), and one each inaccessors.rs,app_state.rs,messaging.rs,notification/groups.rs,presence.rs.Safety
The migration is compiler-enforced end to end:
#[non_exhaustive]makes every missed struct literal a hardE0639error, so no construction site can be silently left behind.bon's typestatebuild()rejects a missing required field at compile time, so no required field can be dropped in the conversion.Optionfield was checked against the struct definition and routed through itsmaybe_*setter, preserving the original value.Verification
cargo build -p whatsapp-rustclean.cargo clippy -p whatsapp-rust -p wacore --testsclean.cargo fmt --all --checkclean.cargo test -p whatsapp-rust --lib→ 954 passed, 0 failed.Follow-ups (not in this PR)
Receipt,MessageBatch,InboundMessage,UndecryptableMessage, newsletter events).Eventvariants (PairingQrCode/PairingCode/PairingCodeRefresh) converted to newtypes.Connected,Disconnected, etc.) — decide per case whether an empty sealed struct is worth it.trybuildcompile-fail test to lock the seal permanently.EventInterestoffu64.