Skip to main content

whatsapp_rust/
lib.rs

1// Compile-checks the README examples as doctests, so the advertised quick
2// start can never silently rot.
3#![doc = include_str!("../README.md")]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5// Instrumenting large async fns (e.g. process_sync_task) wraps them in deep
6// `Instrumented` future types; the default depth limit overflows when the
7// `tracing` + `tracing-pii` paths combine. Raise it (compile-time only).
8#![recursion_limit = "512"]
9
10// Process-wide allocation counter shared by empirical unit-test guards. It sees
11// every thread, so measurements go through `min_allocs`, which retries until a
12// window lands quiet rather than trusting any single one.
13#[cfg(test)]
14#[allow(clippy::disallowed_types)]
15pub(crate) mod test_alloc {
16    use std::alloc::{GlobalAlloc, Layout, System};
17    use std::sync::atomic::{AtomicU64, Ordering};
18
19    pub(crate) static ALLOCS: AtomicU64 = AtomicU64::new(0);
20
21    struct CountingAlloc;
22
23    unsafe impl GlobalAlloc for CountingAlloc {
24        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
25            ALLOCS.fetch_add(1, Ordering::Relaxed);
26            unsafe { System.alloc(layout) }
27        }
28
29        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
30            unsafe { System.dealloc(ptr, layout) }
31        }
32    }
33
34    #[global_allocator]
35    static GLOBAL: CountingAlloc = CountingAlloc;
36
37    /// Smallest allocation delta observed while running `op`, retrying until it
38    /// reaches `expected`.
39    ///
40    /// `ALLOCS` counts every allocation in the process, so a sibling test thread
41    /// allocating inside the window inflates that window's delta. A fixed
42    /// iteration count only hopes one of its windows lands quiet, which is a
43    /// flake under a loaded CI runner; retrying until the delta reaches
44    /// `expected` makes ambient traffic cost iterations instead of a false
45    /// failure. A real regression never reaches `expected`, so the caller's
46    /// assertion still fires — with the count actually observed.
47    pub(crate) fn min_allocs<T>(expected: u64, mut op: impl FnMut() -> T) -> u64 {
48        // Bounded so a genuine regression fails instead of spinning forever.
49        // The happy path exits on its first quiet window, so a budget this
50        // large is free unless something is actually wrong.
51        const BUDGET: u32 = 100_000;
52
53        let mut min = u64::MAX;
54        for _ in 0..BUDGET {
55            let before = ALLOCS.load(Ordering::Relaxed);
56            let value = std::hint::black_box(op());
57            let after = ALLOCS.load(Ordering::Relaxed);
58            drop(value);
59            min = min.min(after - before);
60            if min <= expected {
61                break;
62            }
63        }
64        min
65    }
66}
67
68pub use wacore::appstate::schemas;
69pub use wacore::client_profile::ClientProfile;
70/// Optional metrics emission (the `metrics` feature). No-op when the feature is off.
71pub use wacore::telemetry;
72pub use wacore::{
73    iq::privacy as privacy_settings, proto_helpers, sticker_pack, store::traits, webp,
74};
75pub use wacore_binary::CompactString;
76pub use wacore_binary::OwnedNodeRef;
77pub use wacore_binary::builder::NodeBuilder;
78pub use wacore_binary::{Jid, Server};
79
80// Whole-crate re-exports so a git consumer needs a single dependency:
81// every `wacore::…`/`wacore_binary::…`/`waproto::…` path is reachable as
82// `whatsapp_rust::wacore::…` (etc.) without declaring the sibling crates.
83pub use wacore;
84pub use wacore_binary;
85pub use waproto;
86
87// Third-party re-exports: these crates' types appear in the public API, so
88// consumers must name them; a direct dependency would have to version-match
89// this crate exactly.
90pub use anyhow;
91pub use async_channel;
92pub use async_trait::async_trait;
93pub use bytes;
94pub use futures;
95pub use serde;
96pub use serde_json;
97pub use wacore::chrono;
98pub use waproto::buffa;
99
100pub mod cache;
101pub use cache::Freshness;
102pub mod portable_cache;
103pub(crate) mod resend_rate_limiter;
104
105pub mod cache_config;
106pub use cache_config::{
107    CacheConfig, CacheEntryConfig, CacheStores, MsgSecretPolicy, MsgSecretRetention,
108    OriginalMessageResolver,
109};
110pub mod cache_store;
111pub(crate) mod pending_device_sync;
112pub(crate) mod sender_key_device_cache;
113pub use cache_store::CacheStore;
114pub mod http;
115pub mod types;
116
117pub mod client;
118pub(crate) mod flush_scope;
119/// Shared base error for transport/connection concerns; the per-domain error
120/// types embed it.
121pub use client::ClientError;
122pub use client::NodeFilter;
123pub use client::{
124    AllocSnapshot, CollectionStats, HttpResourceReport, MemoryReport, ResourceReport,
125    StatsSnapshot, StorageResourceReport, TransportResourceReport,
126};
127pub use client::{CallError, Voip};
128pub use client::{Client, ClientBuild, ClientBuilder, ClientBuilderError, RawNodeLease};
129#[cfg(feature = "client-lifecycle")]
130#[cfg_attr(docsrs, doc(cfg(feature = "client-lifecycle")))]
131pub use client::{ClientLifecycle, ConnectionScope, ConnectionScopeState};
132pub use client::{ConnectError, ConnectStage, SignalMaintenanceError};
133pub use types::durability_hook::InboundDurabilityHook;
134pub use types::retry_admission::RetryAdmission;
135pub mod download;
136pub mod error;
137pub use error::{ErrorChainExt, ServerRejection, Sources};
138pub mod handlers;
139pub use handlers::chatstate::ChatStateEvent;
140pub mod handshake;
141pub mod jid_utils;
142pub mod keepalive;
143pub mod mediaconn;
144pub mod message;
145pub(crate) mod msg_secret_buffer;
146pub mod pair;
147pub mod pair_code;
148pub mod passkey;
149#[cfg(feature = "plugins")]
150#[cfg_attr(docsrs, doc(cfg(feature = "plugins")))]
151pub mod plugins;
152#[cfg(feature = "plugins")]
153#[cfg_attr(docsrs, doc(cfg(feature = "plugins")))]
154pub use plugins::{
155    ClientPlugin, PluginCapabilities, PluginCapability, PluginConnectionScope,
156    PluginConnectionTasks, PluginContext, PluginCoreEventSubscription, PluginCoreEvents,
157    PluginEventEndpointConfig, PluginEventEndpointStats, PluginEventEnvelope, PluginEventOverflow,
158    PluginEventPayloadEncoding, PluginEventPublishError, PluginEventPublishReport,
159    PluginEventPublisherStats, PluginEventReceiveError, PluginEventRouteError, PluginEventRouter,
160    PluginEventRouterStats, PluginEventSelector, PluginEventSubscribeError,
161    PluginEventSubscription, PluginEventTopic, PluginEventTryReceiveError, PluginEvents,
162    PluginFuture, PluginHealth, PluginHostConfig, PluginHostStats, PluginIq, PluginIqError,
163    PluginManifest, PluginMessaging, PluginMessagingError, PluginPlanError, PluginResourceError,
164    PluginState, PluginStats, PluginTasks, UntypedClientPlugin,
165};
166pub mod request;
167pub(crate) mod signal_flush;
168pub use request::IqError;
169#[cfg(feature = "tokio-runtime")]
170pub mod runtime_impl;
171#[cfg(feature = "tokio-runtime")]
172pub use runtime_impl::TokioRuntime;
173pub use wacore::runtime::Runtime;
174pub mod send;
175pub use send::{EditOptions, PinDuration, RevokeType, SendError, SendOptions, SendResult};
176pub use wacore::send::StanzaType;
177pub mod media;
178pub mod session;
179pub mod socket;
180pub mod store;
181pub mod transport;
182pub mod upload;
183#[cfg(feature = "voip-runtime")]
184pub mod voip;
185pub use upload::UploadOptions;
186
187pub mod pdo;
188pub mod prekeys;
189pub mod receipt;
190pub mod retry;
191pub mod unified_session;
192
193pub mod appstate_sync;
194pub mod history_sync;
195pub mod usync;
196
197pub mod features;
198pub use features::{
199    AppStateError, BatchGroupResult, Blocking, BlockingError, BlocklistEntry, ChatActions,
200    ChatStateError, ChatStateType, Chatstate, Comments, Community, CommunityError,
201    CommunitySubgroup, ContactError, Contacts, CreateCommunityOptions, CreateCommunityResult,
202    CreateGroupResult, EncType, EncryptedEdit, EventCreationParams, EventResponseType, Events,
203    GroupAppealStatus, GroupCreateOptions, GroupDescription, GroupEphemeralSettings, GroupError,
204    GroupJoinError, GroupMetadata, GroupParticipant, GroupParticipantDetails,
205    GroupParticipantOptions, GroupProfilePicture, GroupSubject, GroupType, Groups, GrowthLockInfo,
206    InviteInfoError, IsOnWhatsAppResult, JoinGroupResult, Labels, LinkSubgroupsResult,
207    MediaRetryResult, MediaReupload, MediaReuploadError, MediaReuploadRequest, MemberAddMode,
208    MemberLinkMode, MemberShareHistoryMode, MembershipApprovalMode, MembershipRequest,
209    MessageEditError, MessageRetransmission, Mex, MexError, MexErrorExtensions, MexGraphQLError,
210    MexRequest, MexResponse, NackReason, Newsletter, NewsletterError, NewsletterMessage,
211    NewsletterMessageType, NewsletterMetadata, NewsletterReactionCount, NewsletterRole,
212    NewsletterState, NewsletterVerification, ParticipantChangeResponse, ParticipantType,
213    PictureType, PollError, PollOptionResult, PollVoteCiphertext, Polls, Presence, PresenceError,
214    PresenceStatus, PreviousDescription, Profile, ProfileError, ProfilePicture, ReachoutTimelock,
215    RetryReason, RetryRequestError, RetryRequestOptions, RetryRequestOutcome, SecretEncKind,
216    SecretEncrypted, SetProfilePictureResponse, Signal, SignalError, SignalSessionInfo,
217    SignalSessionMigration, StanzaRejection, StanzaResponseError, Status, StatusPrivacySetting,
218    StatusSendOptions, SyncActionMessageRange, TcToken, TcTokenError, UnlinkSubgroupsResult,
219    UserInfo, UsyncSubprotocolError, VerifiedName, group_type, message_key, message_range,
220};
221
222pub mod bot;
223pub mod lid_pn_cache;
224#[cfg(feature = "signal")]
225pub mod shutdown;
226#[cfg(feature = "signal")]
227pub use shutdown::shutdown_signal;
228pub mod spam_report;
229pub mod sync_task;
230pub mod version;
231
232/// One-import surface for the common bot path:
233/// `use whatsapp_rust::prelude::*;`.
234pub mod prelude {
235    pub use crate::bot::{Bot, BotBuilder, BotHandle, EventDelivery, MessageContext};
236    pub use crate::client::{Client, ClientBuilder, ClientBuilderError, ClientError, RawNodeLease};
237    #[cfg(feature = "client-lifecycle")]
238    #[cfg_attr(docsrs, doc(cfg(feature = "client-lifecycle")))]
239    pub use crate::client::{ClientLifecycle, ConnectionScope, ConnectionScopeState};
240    pub use crate::client::{ConnectError, ConnectStage};
241    #[cfg(feature = "plugins")]
242    #[cfg_attr(docsrs, doc(cfg(feature = "plugins")))]
243    pub use crate::plugins::{
244        ClientPlugin, PluginCapability, PluginConnectionScope, PluginContext,
245        PluginCoreEventSubscription, PluginEventEndpointConfig, PluginEventOverflow,
246        PluginEventPayloadEncoding, PluginEventRouter, PluginEventSelector,
247        PluginEventSubscription, PluginEventTopic, PluginEvents, PluginFuture, PluginHostConfig,
248        PluginManifest, UntypedClientPlugin,
249    };
250    pub use crate::request::IqError;
251    #[cfg(feature = "tokio-runtime")]
252    pub use crate::runtime_impl::TokioRuntime;
253    pub use crate::send::{EditOptions, SendError, SendOptions, SendResult};
254    #[cfg(feature = "signal")]
255    pub use crate::shutdown::shutdown_signal;
256    #[cfg(feature = "sqlite-storage")]
257    pub use crate::store::SqliteStore;
258    pub use crate::types::events::{
259        BatchOrigin, ChannelEventHandler, Event, EventHandler, EventInterest, EventKind,
260        InboundMessage, MessageBatch, Subscription,
261    };
262    pub use crate::types::message::MessageInfo;
263    pub use crate::{Jid, Server};
264    pub use wacore::proto_helpers::{MessageBuilderExt, MessageExt};
265    /// Optional sub-message wrapper in `wa::Message` literals.
266    pub use waproto::buffa::MessageField;
267    /// The protobuf namespace (`wa::Message`, `wa::message::*`).
268    pub use waproto::whatsapp as wa;
269}
270
271pub use spam_report::{SpamFlow, SpamReportRequest, SpamReportResult};
272
273#[cfg(test)]
274pub mod test_utils;
275
276#[cfg(test)]
277mod reexports_test;