Skip to content

refactor(events): seal notification, sync event payloads (non_exhaustive + bon builder) - #1003

Merged
jlucaso1 merged 1 commit into
mainfrom
claude/whatsapp-rust-pr-review-wjppqi
Jul 7, 2026
Merged

refactor(events): seal notification, sync event payloads (non_exhaustive + bon builder)#1003
jlucaso1 merged 1 commit into
mainfrom
claude/whatsapp-rust-pr-review-wjppqi

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

What

Second tranche of the pre-1.0 event-payload API freeze, after ServerAck in #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)

  • App-state sync mutations (12): ContactUpdate, PinUpdate, MuteUpdate, ArchiveUpdate, StarUpdate, MarkChatAsReadUpdate, DeleteChatUpdate, ClearChatUpdate, UserStatusMuteUpdate, DeleteMessageForMeUpdate, LabelEditUpdate, LabelAssociationUpdate.
  • Notification / contact / presence / group (10): 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 Option attributes passed through maybe_* setters. This is behavior-preserving: same values, same order, no logic change. Spread across chat_actions.rs (10), notification/profile.rs (5), labels.rs (2), and one each in accessors.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 hard E0639 error, so no construction site can be silently left behind.
  • bon's typestate build() rejects a missing required field at compile time, so no required field can be dropped in the conversion.
  • Every Option field was checked against the struct definition and routed through its maybe_* setter, preserving the original value.

Verification

  • cargo build -p whatsapp-rust clean.
  • cargo clippy -p whatsapp-rust -p wacore --tests clean.
  • cargo fmt --all --check clean.
  • cargo test -p whatsapp-rust --lib → 954 passed, 0 failed.

Follow-ups (not in this PR)

  • The message/newsletter payloads (Receipt, MessageBatch, InboundMessage, UndecryptableMessage, newsletter events).
  • The pairing payloads and the three inline Event variants (PairingQrCode / PairingCode / PairingCodeRefresh) converted to newtypes.
  • The unit-struct markers (Connected, Disconnected, etc.) — decide per case whether an empty sealed struct is worth it.
  • A trybuild compile-fail test to lock the seal permanently.
  • Optionally migrate EventInterest off u64.

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.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Listen, 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.

Changes

Builder-based event construction

Layer / File(s) Summary
Event struct definitions gain builder support
wacore/src/types/events.rs
Twenty-plus event payload structs (SelfPushNameUpdated, ChatPresenceUpdate, PresenceUpdate, PictureUpdate, and others) get bon::Builder derives and #[non_exhaustive] markers.
Push name dispatch
src/client/accessors.rs, src/client/app_state.rs
SelfPushNameUpdated construction switches to builder chain for from_server/old_name/new_name.
Chat presence dispatch
src/client/messaging.rs
ChatPresenceUpdate for Event::ChatPresence built via builder, setting source, state, media.
Chat mutation dispatch
src/features/chat_actions.rs
Mute, pin, archive, star, contact, mark_chat_as_read, deleteChat, clearChat, userStatusMute, and deleteMessageForMe events all switch from struct literals to builder construction.
Label mutation dispatch
src/features/labels.rs
LabelEditUpdate and LabelAssociationUpdate construction switches to builder APIs.
Group notification dispatch
src/handlers/notification/groups.rs
GroupUpdate built via builder with maybe_participant/maybe_participant_pn for optional fields.
Profile/contact notification dispatch
src/handlers/notification/profile.rs
PictureUpdate, UserAboutUpdate, ContactUpdated, ContactNumberChanged, ContactSyncRequested switch to builder construction.
Presence dispatch
src/handlers/presence.rs
PresenceUpdate for Event::Presence built via builder with maybe_last_seen.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

  • oxidezap/whatsapp-rust#310: Both PRs modify dispatch_chat_mutation event payload construction for MuteUpdate/PinUpdate/ArchiveUpdate/StarUpdate in src/features/chat_actions.rs.
  • oxidezap/whatsapp-rust#327: Both PRs touch GroupUpdate handling in wacore/src/types/events.rs and its dispatch in src/handlers/notification/groups.rs.
  • oxidezap/whatsapp-rust#406: Both PRs touch DeleteChatUpdate/DeleteMessageForMeUpdate payload construction in dispatch_chat_mutation.

Suggested labels: api-design, breaking-change

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)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: sealing event payloads and switching them to bon builders.
Description check ✅ Passed The description matches the changeset and accurately explains the builder migration and non_exhaustive sealing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/whatsapp-rust-pr-review-wjppqi

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 45c5b64 and 6f5f270.

📒 Files selected for processing (9)
  • src/client/accessors.rs
  • src/client/app_state.rs
  • src/client/messaging.rs
  • src/features/chat_actions.rs
  • src/features/labels.rs
  • src/handlers/notification/groups.rs
  • src/handlers/notification/profile.rs
  • src/handlers/presence.rs
  • wacore/src/types/events.rs

Comment thread wacore/src/types/events.rs
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR applies the same "event-payload API freeze" pattern established in #1002 to 22 more structs — adding #[non_exhaustive] and #[derive(bon::Builder)] to notification, presence, contact, group, and app-state-sync payloads in wacore/src/types/events.rs, then migrating all construction sites across 8 caller files to use the generated builders.

  • Struct seal (wacore): 22 structs gain #[non_exhaustive] + bon::Builder. Option<T> fields are wired through maybe_* setters; required fields are wired through plain setters. The migration is exhaustively compiler-enforced (E0639 on any missed struct literal outside the defining crate, typestate build() on any missed required field).
  • Construction sites (whatsapp-rust): All 22 cross-crate dispatch sites are migrated to the builder pattern, preserving field values and order exactly. No logic changes.

Confidence Score: 5/5

Safe to merge — purely mechanical API-seal migration with no logic changes.

Every changed line is a direct, field-by-field translation from struct-literal to named builder. All Option fields are correctly routed through maybe_* setters (verified against each struct definition), all required fields remain required in the typestate builder, and no construction site was missed (both within this PR and in the broader codebase as confirmed by grep). The project's own test suite (954 tests) and the compiler's E0639 enforcement together provide strong correctness guarantees for this kind of refactor.

No files require special attention. All 9 changed files contain straightforward, uniform builder migrations.

Important Files Changed

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
Loading
%%{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
Loading

Reviews (1): Last reviewed commit: "refactor(events): seal notification & sy..." | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

📦 Binary size report

Metric main PR Δ
bin size (stripped) 10.81 MiB 10.81 MiB +1.56 KiB (+0.01%) 🔺
bin .text 8.81 MiB 8.81 MiB +1.50 KiB (+0.02%) 🔺
bin allocated (text+data+bss) 10.81 MiB 10.81 MiB +3.98 KiB (+0.04%) 🔺
llvm-lines wacore 504,323 504,323 0
llvm-lines wacore copies 17,278 17,278 0
llvm-lines whatsapp-rust lib 758,006 761,162 +3,156 (+0.42%) 🔺
llvm-lines whatsapp-rust lib copies 24,596 24,680 +84 (+0.34%) 🔺
deps crates (Cargo.lock) 468 468 0
.text per crate
Crate main PR Δ
.text whatsapp_rust 1.63 MiB 1.63 MiB +1.47 KiB (+0.09%) 🔺
.text wacore 531.47 KiB 531.47 KiB 0
.text wacore_binary 157.70 KiB 157.70 KiB 0
.text wacore_libsignal 178.73 KiB 178.73 KiB 0
.text wacore_appstate 156.45 KiB 156.45 KiB 0
.text wacore_noise 26.05 KiB 26.05 KiB 0
.text waproto 1.60 MiB 1.60 MiB 0
.text whatsapp_rust_sqlite_storage 512.98 KiB 512.98 KiB 0
.text whatsapp_rust_tokio_transport 43.61 KiB 43.61 KiB 0
.text whatsapp_rust_ureq_http_client 10.47 KiB 10.47 KiB 0
.text std 1.00 MiB 1.00 MiB +9 B (+0.00%) 🔺
.text other deps 2.95 MiB 2.95 MiB 0
Top movers (cargo-bloat attribution)
Crate main PR Δ
whatsapp_rust 1.63 MiB 1.63 MiB +1.47 KiB (+0.09%)

Baseline: 45c5b642c (latest main run) · Head: a5cd6f3d9 · Graphs

@jlucaso1 jlucaso1 changed the title refactor(events): seal notification &amp; sync event payloads (non_exhaustive + bon builder) refactor(events): seal notification, sync event payloads (non_exhaustive + bon builder) Jul 7, 2026
@jlucaso1
jlucaso1 merged commit cd317bc into main Jul 7, 2026
24 checks passed
@jlucaso1
jlucaso1 deleted the claude/whatsapp-rust-pr-review-wjppqi branch July 7, 2026 18:42
jlucaso1 pushed a commit to oxidezap/whatsapp-rust-docs that referenced this pull request Jul 7, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants