From 2a7ac829ab86eb0c97e9dc92f5112e8167a3eada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lucas?= Date: Tue, 9 Jun 2026 10:30:32 -0300 Subject: [PATCH] api: mark lib-constructed response/result structs #[non_exhaustive] for 1.0 These structs are returned to consumers and only IsOnWhatsAppResult carried #[non_exhaustive] (the convention exists but was applied to exactly one of ~23). Adding a field to any of the others is a breaking change for downstream (Veloz, ESP32 firmware, whatsapp-ui), so this lands the attribute before 1.0. Scope is limited to structs constructed exclusively within their defining crate, so #[non_exhaustive] (which only restricts other crates) is purely additive: it keeps field reads working and blocks only external struct-literal construction and exhaustive matching of types nobody outside the lib builds. - wacore IQ responses: UserInfo, LidQueryResponse, BusinessProfile, BusinessHours, BusinessHoursConfig, BusinessCategory, GroupInfoResponse, GroupParticipantResponse, ParticipantChangeResponse, GroupParticipatingResponse - whatsapp-rust returns: UploadResponse, SendResult, CreateGroupResult, CreateCommunityResult, CommunitySubgroup, LinkSubgroupsResult, UnlinkSubgroupsResult Deliberately excluded: - Event payload structs (Receipt, ConnectFailure, ...) and MessageSource / DeviceListResponse: defined in wacore but constructed in the whatsapp-rust crate (cross-crate), so #[non_exhaustive] would break the build; they need wacore constructors first (separate follow-up). - SendOptions: users construct it via struct literal, so it needs a builder alongside #[non_exhaustive] (an API-design change, separate follow-up). Verified: whole-workspace --all-targets build and the CI clippy command both pass. --- src/features/community.rs | 4 ++++ src/features/groups.rs | 1 + src/send.rs | 1 + src/upload.rs | 1 + wacore/src/iq/business.rs | 4 ++++ wacore/src/iq/groups.rs | 4 ++++ wacore/src/iq/usync.rs | 2 ++ 7 files changed, 17 insertions(+) diff --git a/src/features/community.rs b/src/features/community.rs index 6055b28f1..3d7f15153 100644 --- a/src/features/community.rs +++ b/src/features/community.rs @@ -60,12 +60,14 @@ impl CreateCommunityOptions { /// Result of creating a community. #[derive(Debug, Clone)] +#[non_exhaustive] pub struct CreateCommunityResult { pub metadata: GroupMetadata, } /// A subgroup within a community. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub struct CommunitySubgroup { pub id: Jid, pub subject: String, @@ -76,6 +78,7 @@ pub struct CommunitySubgroup { /// Result of linking subgroups to a community. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub struct LinkSubgroupsResult { pub linked_jids: Vec, pub failed_groups: Vec<(Jid, u32)>, @@ -83,6 +86,7 @@ pub struct LinkSubgroupsResult { /// Result of unlinking subgroups from a community. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub struct UnlinkSubgroupsResult { pub unlinked_jids: Vec, pub failed_groups: Vec<(Jid, u32)>, diff --git a/src/features/groups.rs b/src/features/groups.rs index dedd59a24..05dda8a38 100644 --- a/src/features/groups.rs +++ b/src/features/groups.rs @@ -205,6 +205,7 @@ impl From for GroupMetadata { } #[derive(Debug, Clone)] +#[non_exhaustive] pub struct CreateGroupResult { pub metadata: GroupMetadata, } diff --git a/src/send.rs b/src/send.rs index 843941f23..d4b92312b 100644 --- a/src/send.rs +++ b/src/send.rs @@ -53,6 +53,7 @@ pub struct SendOptions { /// Result of a successfully sent message. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub struct SendResult { pub message_id: String, pub to: Jid, diff --git a/src/upload.rs b/src/upload.rs index 196c21bab..a2ddf4618 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -263,6 +263,7 @@ where } #[derive(Debug, Clone)] +#[non_exhaustive] pub struct UploadResponse { pub url: String, pub direct_path: String, diff --git a/wacore/src/iq/business.rs b/wacore/src/iq/business.rs index 016ed9aff..840594ff3 100644 --- a/wacore/src/iq/business.rs +++ b/wacore/src/iq/business.rs @@ -49,6 +49,7 @@ fn node_text(node: &NodeRef<'_>) -> Option { } #[derive(Debug, Clone, Default, serde::Serialize)] +#[non_exhaustive] pub struct BusinessProfile { #[serde(skip_serializing_if = "Option::is_none")] pub wid: Option, @@ -64,6 +65,7 @@ pub struct BusinessProfile { } #[derive(Debug, Clone, Default, serde::Serialize)] +#[non_exhaustive] pub struct BusinessHours { #[serde(skip_serializing_if = "Option::is_none")] pub timezone: Option, @@ -72,6 +74,7 @@ pub struct BusinessHours { } #[derive(Debug, Clone, serde::Serialize)] +#[non_exhaustive] pub struct BusinessHoursConfig { pub day_of_week: DayOfWeek, pub mode: BusinessHourMode, @@ -80,6 +83,7 @@ pub struct BusinessHoursConfig { } #[derive(Debug, Clone, serde::Serialize)] +#[non_exhaustive] pub struct BusinessCategory { pub id: String, pub name: String, diff --git a/wacore/src/iq/groups.rs b/wacore/src/iq/groups.rs index 9b475ab72..57f3afa9a 100644 --- a/wacore/src/iq/groups.rs +++ b/wacore/src/iq/groups.rs @@ -496,6 +496,7 @@ pub struct GroupQueryRequest { /// A participant in a group response. #[derive(Debug, Clone)] +#[non_exhaustive] pub struct GroupParticipantResponse { pub jid: Jid, pub phone_number: Option, @@ -542,6 +543,7 @@ impl ProtocolNode for GroupParticipantResponse { /// Response from a group info query. #[derive(Debug, Clone)] +#[non_exhaustive] pub struct GroupInfoResponse { pub id: Jid, pub subject: GroupSubject, @@ -986,6 +988,7 @@ impl ProtocolNode for GroupParticipatingRequest { /// Response containing all groups the user is participating in. #[derive(Debug, Clone, Default)] +#[non_exhaustive] pub struct GroupParticipatingResponse { pub groups: Vec, } @@ -1166,6 +1169,7 @@ pub struct AddRequestInfo { /// `type` is often omitted by the server. On `error == "403"` the /// `` child (`add_request` field) carries the V4 invite token. #[derive(Debug, Clone)] +#[non_exhaustive] pub struct ParticipantChangeResponse { pub jid: Jid, pub status: Option, diff --git a/wacore/src/iq/usync.rs b/wacore/src/iq/usync.rs index 96196a567..147e3843f 100644 --- a/wacore/src/iq/usync.rs +++ b/wacore/src/iq/usync.rs @@ -226,6 +226,7 @@ pub struct IsOnWhatsAppResult { /// User information from usync. #[derive(Debug, Clone)] +#[non_exhaustive] pub struct UserInfo { pub jid: Jid, pub lid: Option, @@ -739,6 +740,7 @@ impl LidQuerySpec { /// Response: just the LID mappings learned. #[derive(Debug, Clone)] +#[non_exhaustive] pub struct LidQueryResponse { pub lid_mappings: Vec, }