Refactor the imp/gui bondary to use real traits #18
+8
-5
@@ -7,7 +7,10 @@ use std::cell::RefCell;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::imp;
|
use crate::imp::{CurrentPlatform, PlatformRuntime, SpawnHandleTrait};
|
||||||
|
|
||||||
|
/// The spawn handle type for the current platform.
|
||||||
|
type SpawnHandle = <CurrentPlatform as PlatformRuntime>::SpawnHandle;
|
||||||
|
|
||||||
static DF_MODEL: Asset = asset!("/assets/DeepFilterNet3_ll_onnx.tar.gz");
|
static DF_MODEL: Asset = asset!("/assets/DeepFilterNet3_ll_onnx.tar.gz");
|
||||||
// TODO: make this user configurable.
|
// TODO: make this user configurable.
|
||||||
@@ -33,7 +36,7 @@ enum DenoisingModelState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn with_denoising_model<O>(
|
fn with_denoising_model<O>(
|
||||||
spawn: &imp::SpawnHandle,
|
spawn: &SpawnHandle,
|
||||||
func: impl FnOnce(&mut DfTract) -> O,
|
func: impl FnOnce(&mut DfTract) -> O,
|
||||||
) -> Option<O> {
|
) -> Option<O> {
|
||||||
// Using a thread local is super gross, but DfTract is not Send (so it can never leave the current
|
// Using a thread local is super gross, but DfTract is not Send (so it can never leave the current
|
||||||
@@ -89,7 +92,7 @@ fn with_denoising_model<O>(
|
|||||||
|
|
||||||
pub struct AudioProcessor {
|
pub struct AudioProcessor {
|
||||||
denoise: bool,
|
denoise: bool,
|
||||||
spawn: imp::SpawnHandle,
|
spawn: SpawnHandle,
|
||||||
buffer: Vec<f32>,
|
buffer: Vec<f32>,
|
||||||
noise_floor: f32,
|
noise_floor: f32,
|
||||||
/// Whether we were transmitting in the previous frame
|
/// Whether we were transmitting in the previous frame
|
||||||
@@ -102,7 +105,7 @@ impl AudioProcessor {
|
|||||||
pub fn new_plain() -> Self {
|
pub fn new_plain() -> Self {
|
||||||
AudioProcessor {
|
AudioProcessor {
|
||||||
denoise: false,
|
denoise: false,
|
||||||
spawn: imp::SpawnHandle::current(),
|
spawn: SpawnHandle::current(),
|
||||||
buffer: Vec::new(),
|
buffer: Vec::new(),
|
||||||
noise_floor: DEFAULT_NOISE_FLOOR,
|
noise_floor: DEFAULT_NOISE_FLOOR,
|
||||||
was_transmitting: false,
|
was_transmitting: false,
|
||||||
@@ -113,7 +116,7 @@ impl AudioProcessor {
|
|||||||
pub fn new_denoising() -> Self {
|
pub fn new_denoising() -> Self {
|
||||||
AudioProcessor {
|
AudioProcessor {
|
||||||
denoise: true,
|
denoise: true,
|
||||||
spawn: imp::SpawnHandle::current(),
|
spawn: SpawnHandle::current(),
|
||||||
buffer: Vec::new(),
|
buffer: Vec::new(),
|
||||||
noise_floor: DEFAULT_NOISE_FLOOR,
|
noise_floor: DEFAULT_NOISE_FLOOR,
|
||||||
was_transmitting: false,
|
was_transmitting: false,
|
||||||
|
|||||||
+105
-4
@@ -1,13 +1,114 @@
|
|||||||
|
use crate::app::Command;
|
||||||
|
|
|||||||
|
use color_eyre::eyre::Error;
|
||||||
|
use dioxus::hooks::UnboundedReceiver;
|
||||||
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
||||||
use mumble_web2_common::ClientConfig;
|
use mumble_web2_common::{ClientConfig, ServerStatus};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
pub use tokio::runtime::Handle as SpawnHandle;
|
use std::future::Future;
|
||||||
pub use tokio::task::spawn;
|
use std::time::Duration;
|
||||||
pub use tokio::time::sleep;
|
|
||||||
|
liamwarfield marked this conversation as resolved
Outdated
liamwarfield
commented
Delete this bar Delete this bar
|
|||||||
|
use super::{Platform, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
|
||||||
|
|
||||||
pub use super::connect::*;
|
pub use super::connect::*;
|
||||||
pub use super::native_audio::*;
|
pub use super::native_audio::*;
|
||||||
|
|
||||||
|
pub use tokio::task::spawn;
|
||||||
|
pub use tokio::time::sleep;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Platform Struct
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Desktop platform implementation using Tokio and native audio.
|
||||||
|
pub struct DesktopPlatform;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SpawnHandle
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
pub type SpawnHandle = tokio::runtime::Handle;
|
||||||
|
|
||||||
|
impl SpawnHandleTrait for SpawnHandle {
|
||||||
|
fn spawn<F>(&self, future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + Send + 'static,
|
||||||
|
{
|
||||||
|
let _ = tokio::runtime::Handle::spawn(self, future);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current() -> Self {
|
||||||
|
tokio::runtime::Handle::current()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Trait Implementations
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
impl PlatformRuntime for DesktopPlatform {
|
||||||
|
type SpawnHandle = SpawnHandle;
|
||||||
|
|
||||||
|
fn spawn<F>(future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + Send + 'static,
|
||||||
|
{
|
||||||
|
let _ = tokio::task::spawn(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sleep(duration: Duration) {
|
||||||
|
tokio::time::sleep(duration).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformConfig for DesktopPlatform {
|
||||||
|
async fn load_config() -> color_eyre::Result<ClientConfig> {
|
||||||
|
load_config().await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_username() -> Option<String> {
|
||||||
|
load_username()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_server_url() -> Option<String> {
|
||||||
|
load_server_url()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default_username(username: &str) -> Option<()> {
|
||||||
|
set_default_username(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default_server(server: &str) -> Option<()> {
|
||||||
|
set_default_server(server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformNetwork for DesktopPlatform {
|
||||||
|
async fn network_connect(
|
||||||
|
address: String,
|
||||||
|
username: String,
|
||||||
|
event_rx: &mut UnboundedReceiver<Command>,
|
||||||
|
gui_config: &ClientConfig,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
network_connect(address, username, event_rx, gui_config).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_status(client: &reqwest::Client) -> color_eyre::Result<ServerStatus> {
|
||||||
|
get_status(client).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformInit for DesktopPlatform {
|
||||||
|
fn init_logging() {
|
||||||
|
init_logging();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_permissions() {
|
||||||
|
// No-op on desktop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Platform for DesktopPlatform {}
|
||||||
|
|
||||||
fn get_config_path() -> std::path::PathBuf {
|
fn get_config_path() -> std::path::PathBuf {
|
||||||
let strategy = choose_app_strategy(AppStrategyArgs {
|
let strategy = choose_app_strategy(AppStrategyArgs {
|
||||||
top_level_domain: "com".to_string(),
|
top_level_domain: "com".to_string(),
|
||||||
|
|||||||
+105
-6
@@ -1,16 +1,115 @@
|
|||||||
use android_permissions::{PermissionManager, RECORD_AUDIO};
|
use android_permissions::{PermissionManager, RECORD_AUDIO};
|
||||||
|
use crate::app::Command;
|
||||||
|
use color_eyre::eyre::Error;
|
||||||
|
use dioxus::hooks::UnboundedReceiver;
|
||||||
use jni::{objects::JObject, JavaVM};
|
use jni::{objects::JObject, JavaVM};
|
||||||
use mumble_web2_common::ClientConfig;
|
use mumble_web2_common::{ClientConfig, ServerStatus};
|
||||||
|
use std::future::Future;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use super::{Platform, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
|
||||||
pub use tokio::runtime::Handle as SpawnHandle;
|
|
||||||
pub use tokio::task::spawn;
|
|
||||||
pub use tokio::time::sleep;
|
|
||||||
|
|
||||||
pub use super::connect::*;
|
pub use super::connect::*;
|
||||||
pub use super::native_audio::*;
|
pub use super::native_audio::*;
|
||||||
|
|
||||||
pub fn set_default_username(username: &str) -> Option<()> {
|
pub use tokio::task::spawn;
|
||||||
|
pub use tokio::time::sleep;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Platform Struct
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Mobile platform implementation using Tokio, native audio, and Android permissions.
|
||||||
|
pub struct MobilePlatform;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SpawnHandle
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
pub type SpawnHandle = tokio::runtime::Handle;
|
||||||
|
|
||||||
|
impl SpawnHandleTrait for SpawnHandle {
|
||||||
|
fn spawn<F>(&self, future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + Send + 'static,
|
||||||
|
{
|
||||||
|
let _ = tokio::runtime::Handle::spawn(self, future);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current() -> Self {
|
||||||
|
tokio::runtime::Handle::current()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Trait Implementations
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
impl PlatformRuntime for MobilePlatform {
|
||||||
|
type SpawnHandle = SpawnHandle;
|
||||||
|
|
||||||
|
fn spawn<F>(future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + Send + 'static,
|
||||||
|
{
|
||||||
|
let _ = tokio::task::spawn(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sleep(duration: Duration) {
|
||||||
|
tokio::time::sleep(duration).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformConfig for MobilePlatform {
|
||||||
|
async fn load_config() -> color_eyre::Result<ClientConfig> {
|
||||||
|
load_config().await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_username() -> Option<String> {
|
||||||
|
load_username()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_server_url() -> Option<String> {
|
||||||
|
load_server_url()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default_username(username: &str) -> Option<()> {
|
||||||
|
set_default_username(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default_server(server: &str) -> Option<()> {
|
||||||
|
set_default_server(server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformNetwork for MobilePlatform {
|
||||||
|
async fn network_connect(
|
||||||
|
address: String,
|
||||||
|
username: String,
|
||||||
|
event_rx: &mut UnboundedReceiver<Command>,
|
||||||
|
gui_config: &ClientConfig,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
network_connect(address, username, event_rx, gui_config).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_status(client: &reqwest::Client) -> color_eyre::Result<ServerStatus> {
|
||||||
|
get_status(client).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformInit for MobilePlatform {
|
||||||
|
fn init_logging() {
|
||||||
|
init_logging();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_permissions() {
|
||||||
|
request_recording_permission();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Platform for MobilePlatform {}
|
||||||
|
|
||||||
|
pub fn set_default_username(_username: &str) -> Option<()> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+147
-3
@@ -1,3 +1,126 @@
|
|||||||
|
//! 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.
|
||||||
|
|
||||||
|
use crate::app::Command;
|
||||||
|
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
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Trait for spawn handles that can be stored and used to spawn tasks later.
|
||||||
|
#[cfg(feature = "web")]
|
||||||
|
liamwarfield marked this conversation as resolved
Outdated
liamwarfield
commented
We need to add a doc comment for the overall trait here. We need to add a doc comment for the overall trait here.
liamwarfield
commented
Done Done
|
|||||||
|
pub trait SpawnHandleTrait: Clone + 'static {
|
||||||
|
/// Spawn an async task using this handle.
|
||||||
|
fn spawn<F>(&self, future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + 'static;
|
||||||
|
|
||||||
|
/// Get a spawn handle for the current context.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runtime primitives: task spawning and async sleep.
|
||||||
|
#[cfg(feature = "web")]
|
||||||
|
pub trait PlatformRuntime {
|
||||||
|
/// The spawn handle type for this platform.
|
||||||
|
type SpawnHandle: SpawnHandleTrait;
|
||||||
|
|
||||||
|
/// 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 = ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// Spawn an async task.
|
||||||
|
fn spawn<F>(future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + Send + 'static;
|
||||||
|
|
||||||
|
/// Async sleep for the given duration.
|
||||||
|
fn sleep(duration: Duration) -> impl Future<Output = ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration persistence: loading and saving user preferences.
|
||||||
|
pub trait PlatformConfig {
|
||||||
|
/// Load the client configuration (proxy URL, cert hash, etc.).
|
||||||
|
fn load_config() -> impl Future<Output = color_eyre::Result<ClientConfig>>;
|
||||||
|
|
||||||
|
/// Load saved username.
|
||||||
|
fn load_username() -> Option<String>;
|
||||||
|
|
||||||
|
/// Load saved server URL.
|
||||||
|
fn load_server_url() -> Option<String>;
|
||||||
|
|
||||||
|
/// Save the default username.
|
||||||
|
fn set_default_username(username: &str) -> Option<()>;
|
||||||
|
|
||||||
|
/// Save the default server URL.
|
||||||
|
fn set_default_server(server: &str) -> Option<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
#[cfg(feature = "web")]
|
#[cfg(feature = "web")]
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
@@ -11,6 +134,30 @@ mod desktop;
|
|||||||
#[cfg(feature = "mobile")]
|
#[cfg(feature = "mobile")]
|
||||||
mod mobile;
|
mod mobile;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Platform Type Alias
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// The current platform type, selected at compile time based on features.
|
||||||
|
#[cfg(feature = "web")]
|
||||||
|
pub type CurrentPlatform = web::WebPlatform;
|
||||||
|
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
pub type CurrentPlatform = desktop::DesktopPlatform;
|
||||||
|
|
||||||
|
#[cfg(feature = "mobile")]
|
||||||
|
pub type CurrentPlatform = mobile::MobilePlatform;
|
||||||
|
|
||||||
|
/// Compile-time assertion that CurrentPlatform implements Platform.
|
||||||
|
const _: () = {
|
||||||
|
fn assert_platform<T: Platform>() {}
|
||||||
|
let _ = assert_platform::<CurrentPlatform>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Platform Re-exports
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
pub use desktop::*;
|
pub use desktop::*;
|
||||||
#[cfg(feature = "mobile")]
|
#[cfg(feature = "mobile")]
|
||||||
@@ -24,6 +171,3 @@ pub fn request_permissions() {}
|
|||||||
|
|
||||||
#[cfg(all(feature = "web", not(any(feature = "desktop", feature = "mobile"))))]
|
#[cfg(all(feature = "web", not(any(feature = "desktop", feature = "mobile"))))]
|
||||||
pub use web::*;
|
pub use web::*;
|
||||||
|
|
||||||
#[cfg(any(feature = "desktop"))]
|
|
||||||
pub use desktop::*;
|
|
||||||
|
|||||||
+91
-1
@@ -29,7 +29,6 @@ use web_sys::AudioWorkletNode;
|
|||||||
use web_sys::EncodedAudioChunk;
|
use web_sys::EncodedAudioChunk;
|
||||||
use web_sys::EncodedAudioChunkInit;
|
use web_sys::EncodedAudioChunkInit;
|
||||||
use web_sys::EncodedAudioChunkType;
|
use web_sys::EncodedAudioChunkType;
|
||||||
use web_sys::MediaStream;
|
|
||||||
use web_sys::MediaStreamConstraints;
|
use web_sys::MediaStreamConstraints;
|
||||||
use web_sys::MessageEvent;
|
use web_sys::MessageEvent;
|
||||||
use web_sys::WebTransport;
|
use web_sys::WebTransport;
|
||||||
@@ -39,6 +38,8 @@ use web_sys::WorkletOptions;
|
|||||||
use web_sys::{console, window};
|
use web_sys::{console, window};
|
||||||
use web_sys::{AudioContext, AudioDataCopyToOptions};
|
use web_sys::{AudioContext, AudioDataCopyToOptions};
|
||||||
|
|
||||||
|
use super::{Platform, PlatformConfig, PlatformInit, PlatformNetwork, PlatformRuntime, SpawnHandleTrait};
|
||||||
|
|
||||||
pub use wasm_bindgen_futures::spawn_local as spawn;
|
pub use wasm_bindgen_futures::spawn_local as spawn;
|
||||||
|
|
||||||
pub trait ImpRead: AsyncRead + Unpin + 'static {}
|
pub trait ImpRead: AsyncRead + Unpin + 'static {}
|
||||||
@@ -47,10 +48,98 @@ impl<T: AsyncRead + Unpin + 'static> ImpRead for T {}
|
|||||||
pub trait ImpWrite: AsyncWrite + Unpin + 'static {}
|
pub trait ImpWrite: AsyncWrite + Unpin + 'static {}
|
||||||
impl<T: AsyncWrite + Unpin + 'static> ImpWrite for T {}
|
impl<T: AsyncWrite + Unpin + 'static> ImpWrite for T {}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Platform Struct
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Web platform implementation using WebTransport and Web Audio API.
|
||||||
|
pub struct WebPlatform;
|
||||||
|
|
||||||
pub async fn sleep(d: Duration) {
|
pub async fn sleep(d: Duration) {
|
||||||
TimeoutFuture::new(d.as_millis() as u32).await
|
TimeoutFuture::new(d.as_millis() as u32).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Trait Implementations
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
impl SpawnHandleTrait for SpawnHandle {
|
||||||
|
fn spawn<F>(&self, future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + 'static,
|
||||||
|
{
|
||||||
|
wasm_bindgen_futures::spawn_local(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current() -> Self {
|
||||||
|
SpawnHandle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformRuntime for WebPlatform {
|
||||||
|
type SpawnHandle = SpawnHandle;
|
||||||
|
|
||||||
|
fn spawn<F>(future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + 'static,
|
||||||
|
{
|
||||||
|
wasm_bindgen_futures::spawn_local(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sleep(duration: Duration) {
|
||||||
|
TimeoutFuture::new(duration.as_millis() as u32).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformConfig for WebPlatform {
|
||||||
|
async fn load_config() -> color_eyre::Result<ClientConfig> {
|
||||||
|
load_config().await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_username() -> Option<String> {
|
||||||
|
load_username()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_server_url() -> Option<String> {
|
||||||
|
load_server_url()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default_username(username: &str) -> Option<()> {
|
||||||
|
set_default_username(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_default_server(server: &str) -> Option<()> {
|
||||||
|
set_default_server(server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformNetwork for WebPlatform {
|
||||||
|
async fn network_connect(
|
||||||
|
address: String,
|
||||||
|
username: String,
|
||||||
|
event_rx: &mut UnboundedReceiver<Command>,
|
||||||
|
gui_config: &ClientConfig,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
network_connect(address, username, event_rx, gui_config).await
|
||||||
|
}
|
||||||
|
|
||||||
|
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 request_permissions() {
|
||||||
|
// No-op on web
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Platform for WebPlatform {}
|
||||||
|
|
||||||
trait ResultExt<T> {
|
trait ResultExt<T> {
|
||||||
fn ey(self) -> Result<T, Error>;
|
fn ey(self) -> Result<T, Error>;
|
||||||
}
|
}
|
||||||
@@ -495,6 +584,7 @@ pub fn init_logging() {
|
|||||||
info!("logging initiated");
|
info!("logging initiated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SpawnHandle;
|
pub struct SpawnHandle;
|
||||||
|
|
||||||
impl SpawnHandle {
|
impl SpawnHandle {
|
||||||
|
|||||||
+3
-3
@@ -27,7 +27,7 @@ use tracing::error;
|
|||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::effects::AudioProcessor;
|
use crate::effects::AudioProcessor;
|
||||||
use crate::imp::AudioSystem;
|
use crate::imp::{AudioSystem, CurrentPlatform, Platform, PlatformNetwork, PlatformRuntime};
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
mod effects;
|
mod effects;
|
||||||
@@ -47,7 +47,7 @@ pub async fn network_entrypoint(mut event_rx: UnboundedReceiver<Command>) {
|
|||||||
|
|
||||||
*STATE.server.write() = Default::default();
|
*STATE.server.write() = Default::default();
|
||||||
*STATE.status.write() = ConnectionState::Connecting;
|
*STATE.status.write() = ConnectionState::Connecting;
|
||||||
if let Err(error) = imp::network_connect(address, username, &mut event_rx, &config).await {
|
if let Err(error) = CurrentPlatform::network_connect(address, username, &mut event_rx, &config).await {
|
||||||
error!("could not connect {:?}", error);
|
error!("could not connect {:?}", error);
|
||||||
*STATE.status.write() = ConnectionState::Failed(error.to_string());
|
*STATE.status.write() = ConnectionState::Failed(error.to_string());
|
||||||
} else {
|
} else {
|
||||||
@@ -105,7 +105,7 @@ pub async fn network_loop<R: imp::ImpRead, W: imp::ImpWrite>(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
imp::sleep(Duration::from_millis(3000)).await;
|
CurrentPlatform::sleep(Duration::from_millis(3000)).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user
Add a doc comment here for what this file is, here and elsewhere in imp
I'm not sure if this is actually needed.