diff --git a/whatsrust/src/lib.rs b/whatsrust/src/lib.rs index 11d6662..802e8e1 100644 --- a/whatsrust/src/lib.rs +++ b/whatsrust/src/lib.rs @@ -1,16 +1,14 @@ use std::{ ffi::{CStr, CString, c_char, c_void}, path::Path, - sync::{ - Arc, - atomic::{AtomicUsize, Ordering}, - }, + sync::Arc, }; #[macro_use] mod callbacks; mod registrations; mod lifecycle; +mod presence; mod abi; mod caches; mod events; @@ -25,6 +23,7 @@ pub use registrations::{ set_optimistic_text_sent_handler, set_presence_handler, }; pub use lifecycle::{connect, disconnect, logout, new_client, pair_phone}; +pub use presence::{SubscribePresenceResult, drain_raw_presence_diagnostics, subscribe_presence}; pub(crate) use models::file_kind_discriminant; pub use models::{ ChatSettings, CommunitiesError, CommunityInfo, Contact, DownloadFailed, Event, FileContent, @@ -35,7 +34,6 @@ pub use models::{ }; use strum::FromRepr; -static PRESENCE_CALLBACK_INGRESS: AtomicUsize = AtomicUsize::new(0); #[cfg(test)] mod file_kind_tests { use super::{FileKind, file_kind_discriminant}; @@ -648,84 +646,6 @@ impl CallbackTranslator for i64 { } } -unsafe fn take_owned_c_string( - value: *mut c_char, - free: unsafe extern "C" fn(*mut c_char), -) -> Option { - if value.is_null() { - return None; - } - let result = unsafe { CStr::from_ptr(value) } - .to_string_lossy() - .into_owned(); - unsafe { free(value) }; - Some(result) -} - -pub fn drain_raw_presence_diagnostics() -> Option { - unsafe { - let mut report = take_owned_c_string( - C_DrainRawPresenceDiagnostics(), - C_FreeRawPresenceDiagnostics, - ); - if std::env::var("WPTUI_PRESENCE_DEBUG").as_deref() == Ok("1") { - let ingress = PRESENCE_CALLBACK_INGRESS.swap(0, Ordering::Relaxed); - report.get_or_insert_default().push_str(&format!( - "Rust callback ingress Presence events: {ingress}\n" - )); - } - report - } -} - -#[cfg(test)] -mod raw_presence_diagnostic_tests { - use std::{ - ffi::{CString, c_char}, - sync::atomic::{AtomicBool, Ordering}, - }; - - use super::take_owned_c_string; - - static FREED: AtomicBool = AtomicBool::new(false); - - unsafe extern "C" fn free_test_string(value: *mut c_char) { - drop(unsafe { CString::from_raw(value) }); - FREED.store(true, Ordering::SeqCst); - } - - #[test] - fn owned_diagnostic_string_is_copied_and_freed_once() { - FREED.store(false, Ordering::SeqCst); - let value = CString::new("raw presence events received: 0\n") - .unwrap() - .into_raw(); - - let report = unsafe { take_owned_c_string(value, free_test_string) }; - - assert_eq!(report.as_deref(), Some("raw presence events received: 0\n")); - assert!(FREED.load(Ordering::SeqCst)); - assert!(unsafe { take_owned_c_string(std::ptr::null_mut(), free_test_string) }.is_none()); - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[repr(u8)] -pub enum SubscribePresenceResult { - Accepted = 0, - NoPrivacyToken = 1, - Rejected = 2, -} - -pub fn subscribe_presence(jid: &JID) -> SubscribePresenceResult { - let jid = CString::new(jid.0.as_ref()).unwrap(); - match unsafe { C_SubscribePresence(jid.as_ptr()) } { - 0 => SubscribePresenceResult::Accepted, - 1 => SubscribePresenceResult::NoPrivacyToken, - _ => SubscribePresenceResult::Rejected, - } -} - /// Keeps CStrings and C structs alive for the duration of an FFI call. /// The inner C structs are boxed so their heap addresses remain stable. #[allow(dead_code)] @@ -1253,7 +1173,7 @@ mod group_info_tests { /// JID is unusable. pub fn resolve_dm_chat(jid: &JID) -> Option { unsafe { - take_owned_c_string(C_ResolveDmChatId(CJID::from(jid)), C_FreeResolveDmChatId) + presence::take_owned_c_string(C_ResolveDmChatId(CJID::from(jid)), C_FreeResolveDmChatId) .map(JID::from) } } diff --git a/whatsrust/src/presence.rs b/whatsrust/src/presence.rs new file mode 100644 index 0000000..730b9b2 --- /dev/null +++ b/whatsrust/src/presence.rs @@ -0,0 +1,85 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + +use super::*; + +static PRESENCE_CALLBACK_INGRESS: AtomicUsize = AtomicUsize::new(0); + +pub(crate) fn record_callback_ingress() { + PRESENCE_CALLBACK_INGRESS.fetch_add(1, Ordering::Relaxed); +} + +pub(crate) unsafe fn take_owned_c_string( + value: *mut c_char, + free: unsafe extern "C" fn(*mut c_char), +) -> Option { + if value.is_null() { + return None; + } + let result = unsafe { CStr::from_ptr(value) } + .to_string_lossy() + .into_owned(); + unsafe { free(value) }; + Some(result) +} + +pub fn drain_raw_presence_diagnostics() -> Option { + unsafe { + let mut report = take_owned_c_string( + C_DrainRawPresenceDiagnostics(), + C_FreeRawPresenceDiagnostics, + ); + if std::env::var("WPTUI_PRESENCE_DEBUG").as_deref() == Ok("1") { + let ingress = PRESENCE_CALLBACK_INGRESS.swap(0, Ordering::Relaxed); + report.get_or_insert_default().push_str(&format!( + "Rust callback ingress Presence events: {ingress}\n" + )); + } + report + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum SubscribePresenceResult { + Accepted = 0, + NoPrivacyToken = 1, + Rejected = 2, +} + +pub fn subscribe_presence(jid: &JID) -> SubscribePresenceResult { + let jid = CString::new(jid.0.as_ref()).unwrap(); + match unsafe { C_SubscribePresence(jid.as_ptr()) } { + 0 => SubscribePresenceResult::Accepted, + 1 => SubscribePresenceResult::NoPrivacyToken, + _ => SubscribePresenceResult::Rejected, + } +} + +#[cfg(test)] +mod tests { + use std::{ + ffi::{CString, c_char}, + sync::atomic::{AtomicBool, Ordering}, + }; + + use super::take_owned_c_string; + + static FREED: AtomicBool = AtomicBool::new(false); + + unsafe extern "C" fn free_test_string(value: *mut c_char) { + drop(unsafe { CString::from_raw(value) }); + FREED.store(true, Ordering::SeqCst); + } + + #[test] + fn owned_diagnostic_string_is_copied_and_freed_once() { + FREED.store(false, Ordering::SeqCst); + let value = CString::new("raw presence events received: 0\n") + .unwrap() + .into_raw(); + let report = unsafe { take_owned_c_string(value, free_test_string) }; + assert_eq!(report.as_deref(), Some("raw presence events received: 0\n")); + assert!(FREED.load(Ordering::SeqCst)); + assert!(unsafe { take_owned_c_string(std::ptr::null_mut(), free_test_string) }.is_none()); + } +} diff --git a/whatsrust/src/registrations.rs b/whatsrust/src/registrations.rs index e706918..c31ec77 100644 --- a/whatsrust/src/registrations.rs +++ b/whatsrust/src/registrations.rs @@ -24,7 +24,7 @@ where { setup_presence_handler(move |from, unavailable, last_seen| { if std::env::var("WPTUI_PRESENCE_DEBUG").as_deref() == Ok("1") { - PRESENCE_CALLBACK_INGRESS.fetch_add(1, Ordering::Relaxed); + super::presence::record_callback_ingress(); } callback(PresenceUpdate { from,