wip improved trait shit

This commit is contained in:
2026-01-24 20:16:02 -07:00
committed by Liam Warfield
parent ff14f577fe
commit 411d923c2a
9 changed files with 118 additions and 181 deletions
+5 -2
View File
@@ -7,7 +7,10 @@ use std::collections::HashMap;
use std::future::Future;
use std::time::Duration;
use super::{Platform, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
use super::{
PlatformConfig, PlatformInit, PlatformInterface, PlatformNetwork, PlatformRuntime,
SpawnHandleTrait,
};
pub use super::connect::*;
pub use super::native_audio::*;
@@ -107,7 +110,7 @@ impl PlatformInit for DesktopPlatform {
}
}
impl Platform for DesktopPlatform {}
impl PlatformInterface for DesktopPlatform {}
fn get_config_path() -> std::path::PathBuf {
let strategy = choose_app_strategy(AppStrategyArgs {
+2 -2
View File
@@ -7,7 +7,7 @@ use mumble_web2_common::{ClientConfig, ServerStatus};
use std::future::Future;
use std::time::Duration;
use super::{Platform, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
use super::{PlatformInterface, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
pub use super::connect::*;
pub use super::native_audio::*;
@@ -107,7 +107,7 @@ impl PlatformInit for MobilePlatform {
}
}
impl Platform for MobilePlatform {}
impl PlatformInterface for MobilePlatform {}
pub fn set_default_username(_username: &str) -> Option<()> {
None
+45 -96
View File
@@ -15,8 +15,7 @@ use std::time::Duration;
// ============================================================================
/// Trait for spawn handles that can be stored and used to spawn tasks later.
#[cfg(feature = "web")]
pub trait SpawnHandleTrait: Clone + 'static {
pub trait SpawnHandleInterface: Clone + 'static {
/// Spawn an async task using this handle.
fn spawn<F>(&self, future: F)
where
@@ -26,51 +25,39 @@ pub trait SpawnHandleTrait: Clone + 'static {
fn current() -> Self;
}
/// Trait for spawn handles that can be stored and used to spawn tasks later.
#[cfg(any(feature = "desktop", feature = "mobile"))]
pub trait SpawnHandleTrait: Clone + 'static {
/// Spawn an async task using this handle.
fn spawn<F>(&self, future: F)
where
F: Future<Output = ()> + Send + 'static;
/// Get a spawn handle for the current context.
fn current() -> Self;
pub trait AudioSystemInterface {
type AudioPlayer: AudioPlayerInterface;
}
/// Runtime primitives: task spawning and async sleep.
#[cfg(feature = "web")]
pub trait PlatformRuntime {
/// The spawn handle type for this platform.
type SpawnHandle: SpawnHandleTrait;
pub trait AudioPlayerInterface {}
/// Spawn an async task.
fn spawn<F>(future: F)
where
F: Future<Output = ()> + 'static;
/// 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;
type SpawnHandle: SpawnHandleInterface;
/// Async sleep for the given duration.
fn sleep(duration: Duration) -> impl Future<Output = ()>;
}
/// Initialize logging for the platform.
fn init_logging();
/// Runtime primitives: task spawning and async sleep.
#[cfg(any(feature = "desktop", feature = "mobile"))]
pub trait PlatformRuntime {
/// The spawn handle type for this platform.
type SpawnHandle: SpawnHandleTrait;
/// Request runtime permissions (Android audio recording, etc.).
fn request_permissions();
/// Spawn an async task.
fn spawn<F>(future: F)
where
F: Future<Output = ()> + Send + 'static;
/// Establish a connection to the Mumble server and run the network loop.
fn network_connect(
address: String,
username: String,
event_rx: &mut UnboundedReceiver<Command>,
gui_config: &ClientConfig,
) -> impl Future<Output = Result<(), Error>>;
/// Async sleep for the given duration.
fn sleep(duration: Duration) -> impl Future<Output = ()>;
}
/// Get server status (user count, version, etc.).
fn get_status(
client: &reqwest::Client,
) -> impl Future<Output = color_eyre::Result<ServerStatus>>;
/// Configuration persistence: loading and saving user preferences.
pub trait PlatformConfig {
/// Load the client configuration (proxy URL, cert hash, etc.).
/// Load the proxy overrides (proxy URL, cert hash, etc.).
fn load_config() -> impl Future<Output = color_eyre::Result<ClientConfig>>;
/// Load saved username.
@@ -84,39 +71,16 @@ pub trait PlatformConfig {
/// Save the default server URL.
fn set_default_server(server: &str) -> Option<()>;
/// Spawn an async task.
fn spawn<F>(future: F)
where
F: Future<Output = ()> + 'static;
/// Async sleep for the given duration.
fn sleep(duration: Duration) -> impl Future<Output = ()>;
}
/// Network operations: connecting to servers.
pub trait PlatformNetwork {
/// Establish a connection to the Mumble server and run the network loop.
fn network_connect(
address: String,
username: String,
event_rx: &mut UnboundedReceiver<Command>,
gui_config: &ClientConfig,
) -> impl Future<Output = Result<(), Error>>;
/// Get server status (user count, version, etc.).
fn get_status(client: &reqwest::Client)
-> impl Future<Output = color_eyre::Result<ServerStatus>>;
}
/// Platform initialization.
pub trait PlatformInit {
/// Initialize logging for the platform.
fn init_logging();
/// Request runtime permissions (Android audio recording, etc.).
fn request_permissions();
}
/// Combined platform trait.
///
/// 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 Platform: PlatformRuntime + PlatformConfig + PlatformNetwork + PlatformInit {}
// ============================================================================
// Platform Modules
// ============================================================================
@@ -138,36 +102,21 @@ mod mobile;
// Platform Type Alias
// ============================================================================
/// The current platform type, selected at compile time based on features.
#[cfg(feature = "web")]
pub type CurrentPlatform = web::WebPlatform;
pub type Platform = web::WebPlatform;
#[cfg(feature = "desktop")]
pub type CurrentPlatform = desktop::DesktopPlatform;
#[cfg(all(feature = "desktop", not(feature = "web")))]
pub type Platform = desktop::DesktopPlatform;
#[cfg(feature = "mobile")]
pub type CurrentPlatform = mobile::MobilePlatform;
#[cfg(all(feature = "mobile", not(feature = "web"), not(feature = "desktop")))]
pub type Platform = mobile::MobilePlatform;
pub type AudioSystem = <Platform as PlatformInterface>::AudioSystem;
pub type AudioPlayer = <AudioSystem as AudioSystemInterface>::AudioPlayer;
pub type SpawnHandle = <Platform as PlatformInterface>::SpawnHandle;
/// Compile-time assertion that CurrentPlatform implements Platform.
const _: () = {
fn assert_platform<T: Platform>() {}
let _ = assert_platform::<CurrentPlatform>;
fn assert_platform<T: PlatformInterface>() {}
let _ = assert_platform::<Platform>;
};
// ============================================================================
// Platform Re-exports
// ============================================================================
#[cfg(feature = "desktop")]
pub use desktop::*;
#[cfg(feature = "mobile")]
pub use mobile::*;
#[cfg(feature = "mobile")]
pub use mobile::request_permissions;
#[cfg(any(feature = "desktop", feature = "web"))]
pub fn request_permissions() {}
#[cfg(all(feature = "web", not(any(feature = "desktop", feature = "mobile"))))]
pub use web::*;
+2 -1
View File
@@ -124,7 +124,8 @@ impl AudioSystem {
if let Some(new_processor) = processors.take() {
current_processor = new_processor;
}
let state = current_processor.process(frame, config.channels as usize, &mut output_buffer);
let state =
current_processor.process(frame, config.channels as usize, &mut output_buffer);
encode_and_send(state, &mut output_buffer, &mut encoder, &mut each);
};
+32 -52
View File
@@ -38,8 +38,6 @@ use web_sys::WorkletOptions;
use web_sys::{console, window};
use web_sys::{AudioContext, AudioDataCopyToOptions};
use super::{Platform, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
pub use wasm_bindgen_futures::spawn_local as spawn;
pub trait ImpRead: AsyncRead + Unpin + 'static {}
@@ -55,15 +53,14 @@ impl<T: AsyncWrite + Unpin + 'static> ImpWrite for T {}
/// Web platform implementation using WebTransport and Web Audio API.
pub struct WebPlatform;
pub async fn sleep(d: Duration) {
TimeoutFuture::new(d.as_millis() as u32).await
}
// ============================================================================
// Trait Implementations
// ============================================================================
impl SpawnHandleTrait for SpawnHandle {
#[derive(Clone)]
pub struct WebSpawnHandle;
impl super::SpawnHandleInterface for WebSpawnHandle {
fn spawn<F>(&self, future: F)
where
F: Future<Output = ()> + 'static,
@@ -72,26 +69,22 @@ impl SpawnHandleTrait for SpawnHandle {
}
fn current() -> Self {
SpawnHandle
WebSpawnHandle
}
}
impl PlatformRuntime for WebPlatform {
type SpawnHandle = SpawnHandle;
impl super::PlatformInterface for WebPlatform {
type AudioSystem = WebAudioSystem;
type SpawnHandle = WebSpawnHandle;
fn spawn<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
wasm_bindgen_futures::spawn_local(future);
fn init_logging() {
init_logging();
}
async fn sleep(duration: Duration) {
TimeoutFuture::new(duration.as_millis() as u32).await;
fn request_permissions() {
// No-op on web
}
}
impl PlatformConfig for WebPlatform {
async fn load_config() -> color_eyre::Result<ClientConfig> {
load_config().await
}
@@ -111,9 +104,7 @@ impl PlatformConfig for WebPlatform {
fn set_default_server(server: &str) -> Option<()> {
set_default_server(server)
}
}
impl PlatformNetwork for WebPlatform {
async fn network_connect(
address: String,
username: String,
@@ -126,20 +117,19 @@ impl PlatformNetwork for WebPlatform {
async fn get_status(client: &reqwest::Client) -> color_eyre::Result<ServerStatus> {
get_status(client).await
}
}
impl PlatformInit for WebPlatform {
fn init_logging() {
init_logging();
fn spawn<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
wasm_bindgen_futures::spawn_local(future);
}
fn request_permissions() {
// No-op on web
async fn sleep(duration: Duration) {
TimeoutFuture::new(duration.as_millis() as u32).await;
}
}
impl Platform for WebPlatform {}
trait ResultExt<T> {
fn ey(self) -> Result<T, Error>;
}
@@ -162,7 +152,7 @@ impl<T> ResultExt<T> for Result<T, JsError> {
}
}
pub struct AudioSystem {
pub struct WebAudioSystem {
webctx: AudioContext,
processors: AudioProcessorSender,
}
@@ -193,7 +183,11 @@ async fn attach_worklet(audio_context: &AudioContext) -> Result<(), Error> {
Ok(())
}
impl AudioSystem {
impl super::AudioSystemInterface for WebAudioSystem {
type AudioPlayer = WebAudioPlayer;
}
impl WebAudioSystem {
pub async fn new() -> Result<Self, Error> {
// Create MediaStreams to playback decoded audio
// The audio context is used to reproduce audio.
@@ -202,7 +196,7 @@ impl AudioSystem {
let processors = AudioProcessorSender::default();
Ok(AudioSystem { webctx, processors })
Ok(WebAudioSystem { webctx, processors })
}
pub fn set_processor(&self, processor: AudioProcessor) {
@@ -224,7 +218,7 @@ impl AudioSystem {
Ok(())
}
pub fn create_player(&mut self) -> Result<AudioPlayer, Error> {
pub fn create_player(&mut self) -> Result<WebAudioPlayer, Error> {
let sink_node = AudioWorkletNode::new(&self.webctx, "rust_speaker_worklet").ey()?;
// Connect worklet to destination
@@ -277,13 +271,15 @@ impl AudioSystem {
decoder_error.forget();
output.forget();
Ok(AudioPlayer(audio_decoder))
Ok(WebAudioPlayer(audio_decoder))
}
}
pub struct AudioPlayer(AudioDecoder);
pub struct WebAudioPlayer(AudioDecoder);
impl AudioPlayer {
impl super::AudioPlayerInterface for WebAudioPlayer {}
impl WebAudioPlayer {
pub fn play_opus(&mut self, payload: &[u8]) {
let js_audio_payload = Uint8Array::from(payload);
let _ = self.0.decode(
@@ -583,19 +579,3 @@ pub fn init_logging() {
info!("logging initiated");
}
#[derive(Clone)]
pub struct SpawnHandle;
impl SpawnHandle {
pub fn current() -> Self {
SpawnHandle
}
pub fn spawn<F>(&self, future: F)
where
F: Future<Output = ()> + 'static,
{
spawn(future);
}
}