//! Platform abstraction layer //! //! This module defines traits that each platform (web, desktop, mobile) must implement. //! The traits make the platform boundary explicit and provide compile-time verification. #![allow(async_fn_in_trait)] use crate::{app::Command, effects::AudioProcessor}; use color_eyre::eyre::Error; use dioxus::hooks::UnboundedReceiver; use mumble_web2_common::{ClientConfig, ServerStatus}; use std::future::Future; use std::time::Duration; // ============================================================================ // Trait Definitions // ============================================================================ pub trait AudioSystemInterface: Sized { type AudioPlayer: AudioPlayerInterface; /// Initialize the audio system, including relevant state async fn new() -> Result; /// Set the processor for the microphone input, mainly noise cancellation settings. fn set_processor(&self, processor: AudioProcessor); /// Begin listening to microphone input, calling the `each` function with /// encoded opus frames. fn start_recording( &mut self, each: impl FnMut(Vec, bool) + Send + 'static, ) -> Result<(), Error>; /// Begin playback of an audio stream, returning an object that can be passed opus frames. fn create_player(&mut self) -> Result; } pub trait AudioPlayerInterface { /// Playback an opus frame. fn play_opus(&mut self, payload: &[u8]); } /// This is the main trait that each platform must implement. It combines all /// platform-specific functionality into a single interface, providing compile-time /// verification that all platforms implement the required functionality. pub trait PlatformInterface { type AudioSystem: AudioSystemInterface; /// Initialize logging for the platform. fn init_logging(); /// Request runtime permissions (Android audio recording, etc.). fn request_permissions(); /// Establish a connection to the Mumble server and run the network loop. fn network_connect( address: String, username: String, event_rx: &mut UnboundedReceiver, gui_config: &ClientConfig, ) -> impl Future>; /// Get server status (user count, version, etc.). fn get_status( client: &reqwest::Client, ) -> impl Future>; /// Load the proxy overrides (proxy URL, cert hash, etc.). fn load_config() -> impl Future>; /// Load saved username. fn load_username() -> Option; /// Load saved server URL. fn load_server_url() -> Option; /// Save the default username. fn set_default_username(username: &str) -> Option<()>; /// Save the default server URL. fn set_default_server(server: &str) -> Option<()>; /// Async sleep for the given duration. fn sleep(duration: Duration) -> impl Future; } // ============================================================================ // Platform Modules // ============================================================================ #[cfg(any(feature = "desktop", feature = "mobile"))] mod connect; #[cfg(feature = "desktop")] mod desktop; #[cfg(feature = "mobile")] mod mobile; #[cfg(any(feature = "desktop", feature = "mobile"))] mod native_audio; mod stub; #[cfg(feature = "web")] mod web; // ============================================================================ // Platform Type Alias // ============================================================================ #[cfg(feature = "web")] pub type Platform = web::WebPlatform; #[cfg(all(feature = "desktop", not(feature = "web")))] pub type Platform = desktop::DesktopPlatform; #[cfg(all(feature = "mobile", not(feature = "web"), not(feature = "desktop")))] pub type Platform = mobile::MobilePlatform; #[cfg(all( not(feature = "mobile"), not(feature = "web"), not(feature = "desktop") ))] pub type Platform = stub::StubPlatform; pub type AudioSystem = ::AudioSystem; pub type AudioPlayer = ::AudioPlayer; // ======================== // Platform Async Runtime // ======================== // Note: these can not be part of the Platform because they differ in Send requiremets #[cfg(all(any(feature = "desktop", feature = "mobile"), not(feature = "web")))] pub use connect::{spawn, SpawnHandle}; #[cfg(all( not(feature = "desktop"), not(feature = "mobile"), not(feature = "web") ))] pub use stub::{spawn, SpawnHandle}; #[cfg(feature = "web")] pub use web::{spawn, SpawnHandle}; // ======================= // Compile-time Assertions // ======================= const _: () = { fn assert_platform() {} // Check each implementation, and prevent warnings that the implementations are unused. #[cfg(feature = "web")] let _ = assert_platform::; #[cfg(feature = "desktop")] let _ = assert_platform::; #[cfg(feature = "mobile")] let _ = assert_platform::; let _ = assert_platform::; };